Merge "Document WAN cache FLD_* constants"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 12 May 2016 19:44:17 +0000 (19:44 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 12 May 2016 19:44:17 +0000 (19:44 +0000)
48 files changed:
docs/extension.schema.json
includes/GlobalFunctions.php
includes/MediaWikiServices.php
includes/ServiceWiring.php
includes/Title.php
includes/WatchedItem.php
includes/api/ApiBase.php
includes/collation/IcuCollation.php
includes/libs/objectcache/WANObjectCache.php
includes/linker/LinkTarget.php
includes/objectcache/RedisBagOStuff.php
includes/registration/ExtensionProcessor.php
includes/resourceloader/ResourceLoaderContext.php
includes/resourceloader/ResourceLoaderUserGroupsModule.php
includes/resourceloader/ResourceLoaderUserModule.php
includes/title/MediaWikiTitleCodec.php
includes/title/TitleFormatter.php
includes/title/TitleValue.php
languages/i18n/ar.json
languages/i18n/be-tarask.json
languages/i18n/bg.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/el.json
languages/i18n/eo.json
languages/i18n/es.json
languages/i18n/fr.json
languages/i18n/gl.json
languages/i18n/he.json
languages/i18n/it.json
languages/i18n/ksh.json
languages/i18n/or.json
languages/i18n/pl.json
languages/i18n/qqq.json
languages/i18n/vi.json
languages/i18n/zh-hans.json
maintenance/updateCollation.php
resources/src/mediawiki.action/mediawiki.action.view.metadata.js
resources/src/mediawiki.widgets/mw.widgets.CategoryCapsuleItemWidget.js
tests/parser/parserTest.inc
tests/phpunit/includes/LinkerTest.php
tests/phpunit/includes/MediaWikiServicesTest.php
tests/phpunit/includes/TitleTest.php
tests/phpunit/includes/WatchedItemIntegrationTest.php
tests/phpunit/includes/parser/NewParserTest.php
tests/phpunit/includes/title/MediaWikiTitleCodecTest.php
tests/phpunit/includes/title/TitleValueTest.php

index ed3eaa9..158cb6e 100644 (file)
                        "type": "object",
                        "description": "Registry of factory functions to create Config objects"
                },
+               "SessionProviders": {
+                       "type": "object",
+                       "description": "Session providers"
+               },
                "CentralIdLookupProviders": {
                        "type": "object",
                        "description": "Central ID lookup providers"
index 537bdef..618fa4c 100644 (file)
@@ -2134,6 +2134,24 @@ function wfTempDir() {
                        return $tmp;
                }
        }
+
+       /**
+        * PHP on Windows will detect C:\Windows\Temp as not writable even though PHP can write to it
+        * so create a directory within that called 'mwtmp' with a suffix of the user running the
+        * current process.
+        * The user is included as if various scripts are run by different users they will likely
+        * not be able to access each others temporary files.
+        */
+       if ( wfIsWindows() ) {
+               $tmp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mwtmp' . '-' . get_current_user();
+               if ( !file_exists( $tmp ) ) {
+                       mkdir( $tmp );
+               }
+               if ( file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) {
+                       return $tmp;
+               }
+       }
+
        throw new MWException( 'No writable temporary directory could be found. ' .
                'Please set $wgTmpDirectory to a writable directory.' );
 }
index 9e18fd1..5bb5597 100644 (file)
@@ -20,6 +20,8 @@ use SiteLookup;
 use SiteStore;
 use WatchedItemStore;
 use SkinFactory;
+use TitleFormatter;
+use TitleParser;
 
 /**
  * Service locator for MediaWiki core services.
@@ -458,6 +460,21 @@ class MediaWikiServices extends ServiceContainer {
        public function getGenderCache() {
                return $this->getService( 'GenderCache' );
        }
+       /**
+        * @since 1.28
+        * @return TitleFormatter
+        */
+       public function getTitleFormatter() {
+               return $this->getService( 'TitleFormatter' );
+       }
+
+       /**
+        * @since 1.28
+        * @return TitleParser
+        */
+       public function getTitleParser() {
+               return $this->getService( 'TitleParser' );
+       }
 
        ///////////////////////////////////////////////////////////////////////////
        // NOTE: When adding a service getter here, don't forget to add a test
index e282bda..aa99a71 100644 (file)
@@ -143,6 +143,24 @@ return [
                return new GenderCache();
        },
 
+       '_MediaWikiTitleCodec' => function( MediaWikiServices $services ) {
+               global $wgContLang;
+
+               return new MediaWikiTitleCodec(
+                       $wgContLang,
+                       $services->getGenderCache(),
+                       $services->getMainConfig()->get( 'LocalInterwikis' )
+               );
+       },
+
+       'TitleFormatter' => function( MediaWikiServices $services ) {
+               return $services->getService( '_MediaWikiTitleCodec' );
+       },
+
+       'TitleParser' => function( MediaWikiServices $services ) {
+               return $services->getService( '_MediaWikiTitleCodec' );
+       },
+
        ///////////////////////////////////////////////////////////////////////////
        // NOTE: When adding a service here, don't forget to add a getter function
        // in the MediaWikiServices class. The convenience getter should just call
index 65b2d3a..876afe6 100644 (file)
@@ -22,7 +22,6 @@
  * @file
  */
 use MediaWiki\Linker\LinkTarget;
-
 use MediaWiki\MediaWikiServices;
 
 /**
@@ -159,42 +158,6 @@ class Title implements LinkTarget {
        private $mIsBigDeletion = null;
        // @}
 
-       /**
-        * B/C kludge: provide a TitleParser for use by Title.
-        * Ideally, Title would have no methods that need this.
-        * Avoid usage of this singleton by using TitleValue
-        * and the associated services when possible.
-        *
-        * @return MediaWikiTitleCodec
-        */
-       private static function getMediaWikiTitleCodec() {
-               global $wgContLang, $wgLocalInterwikis;
-
-               static $titleCodec = null;
-               static $titleCodecFingerprint = null;
-
-               // $wgContLang and $wgLocalInterwikis may change (especially while testing),
-               // make sure we are using the right one. To detect changes over the course
-               // of a request, we remember a fingerprint of the config used to create the
-               // codec singleton, and re-create it if the fingerprint doesn't match.
-               $fingerprint = spl_object_hash( $wgContLang ) . '|' . implode( '+', $wgLocalInterwikis );
-
-               if ( $fingerprint !== $titleCodecFingerprint ) {
-                       $titleCodec = null;
-               }
-
-               if ( !$titleCodec ) {
-                       $titleCodec = new MediaWikiTitleCodec(
-                               $wgContLang,
-                               GenderCache::singleton(),
-                               $wgLocalInterwikis
-                       );
-                       $titleCodecFingerprint = $fingerprint;
-               }
-
-               return $titleCodec;
-       }
-
        /**
         * B/C kludge: provide a TitleParser for use by Title.
         * Ideally, Title would have no methods that need this.
@@ -204,11 +167,12 @@ class Title implements LinkTarget {
         * @return TitleFormatter
         */
        private static function getTitleFormatter() {
-               // NOTE: we know that getMediaWikiTitleCodec() returns a MediaWikiTitleCodec,
-               //      which implements TitleFormatter.
-               return self::getMediaWikiTitleCodec();
+               return MediaWikiServices::getInstance()->getTitleFormatter();
        }
 
+       /**
+        * @access protected
+        */
        function __construct() {
        }
 
@@ -3334,9 +3298,11 @@ class Title implements LinkTarget {
                // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
                //        the parsing code with Title, while avoiding massive refactoring.
                // @todo: get rid of secureAndSplit, refactor parsing code.
-               $titleParser = self::getMediaWikiTitleCodec();
+               // @note: getTitleParser() returns a TitleParser implementation which does not have a
+               //        splitTitleString method, but the only implementation (MediaWikiTitleCodec) does
+               $titleCodec = MediaWikiServices::getInstance()->getTitleParser();
                // MalformedTitleException can be thrown here
-               $parts = $titleParser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
+               $parts = $titleCodec->splitTitleString( $dbkey, $this->getDefaultNamespace() );
 
                # Fill fields
                $this->setFragment( '#' . $parts['fragment'] );
index 50c79dc..b070e1e 100644 (file)
@@ -152,7 +152,7 @@ class WatchedItem {
         *             or WatchedItemStore::loadWatchedItem()
         */
        public static function fromUserTitle( $user, $title, $checkRights = User::CHECK_USER_RIGHTS ) {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                return new self( $user, $title, self::DEPRECATED_USAGE_TIMESTAMP, (bool)$checkRights );
        }
 
@@ -160,7 +160,7 @@ class WatchedItem {
         * @deprecated since 1.27 Use WatchedItemStore::resetNotificationTimestamp()
         */
        public function resetNotificationTimestamp( $force = '', $oldid = 0 ) {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                if ( $this->checkRights && !$this->user->isAllowed( 'editmywatchlist' ) ) {
                        return;
                }
@@ -176,7 +176,7 @@ class WatchedItem {
         * @deprecated since 1.27 Use WatchedItemStore::addWatchBatch()
         */
        public static function batchAddWatch( array $items ) {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                if ( !$items ) {
                        return false;
                }
@@ -209,7 +209,7 @@ class WatchedItem {
         * @return bool
         */
        public function addWatch() {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                $this->user->addWatch( $this->getTitle(), $this->checkRights );
                return true;
        }
@@ -219,7 +219,7 @@ class WatchedItem {
         * @return bool
         */
        public function removeWatch() {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                if ( $this->checkRights && !$this->user->isAllowed( 'editmywatchlist' ) ) {
                        return false;
                }
@@ -232,7 +232,7 @@ class WatchedItem {
         * @return bool
         */
        public function isWatched() {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                return $this->user->isWatched( $this->getTitle(), $this->checkRights );
        }
 
@@ -240,7 +240,7 @@ class WatchedItem {
         * @deprecated since 1.27 Use WatchedItemStore::duplicateAllAssociatedEntries()
         */
        public static function duplicateEntries( Title $oldTitle, Title $newTitle ) {
-               // wfDeprecated( __METHOD__, '1.27' );
+               wfDeprecated( __METHOD__, '1.27' );
                $store = MediaWikiServices::getInstance()->getWatchedItemStore();
                $store->duplicateAllAssociatedEntries( $oldTitle, $newTitle );
        }
index da64c03..7d0ae32 100644 (file)
@@ -1522,20 +1522,20 @@ abstract class ApiBase extends ContextSource {
                        throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
                }
 
-               $errors = $status->getErrorsArray();
+               $errors = $status->getErrorsByType( 'error' );
                if ( !$errors ) {
                        // No errors? Assume the warnings should be treated as errors
-                       $errors = $status->getWarningsArray();
+                       $errors = $status->getErrorsByType( 'warning' );
                }
                if ( !$errors ) {
                        // Still no errors? Punt
-                       $errors = [ [ 'unknownerror-nocode' ] ];
+                       $errors = [ [ 'message' => 'unknownerror-nocode', 'params' => [] ] ];
                }
 
                // Cannot use dieUsageMsg() because extensions might return custom
                // error messages.
-               if ( $errors[0] instanceof Message ) {
-                       $msg = $errors[0];
+               if ( $errors[0]['message'] instanceof Message ) {
+                       $msg = $errors[0]['message'];
                        if ( $msg instanceof IApiMessage ) {
                                $extraData = $msg->getApiData();
                                $code = $msg->getApiCode();
@@ -1543,8 +1543,8 @@ abstract class ApiBase extends ContextSource {
                                $code = $msg->getKey();
                        }
                } else {
-                       $code = array_shift( $errors[0] );
-                       $msg = wfMessage( $code, $errors[0] );
+                       $code = $errors[0]['message'];
+                       $msg = wfMessage( $code, $errors[0]['params'] );
                }
                if ( isset( ApiBase::$messageMap[$code] ) ) {
                        // Translate message to code, for backwards compatibility
index 942036b..a374b13 100644 (file)
@@ -234,32 +234,33 @@ class IcuCollation extends Collation {
 
        /**
         * @since 1.16.3
+        * @return array
         */
        public function getFirstLetterData() {
-               if ( $this->firstLetterData !== null ) {
-                       return $this->firstLetterData;
-               }
-
-               $cache = ObjectCache::getLocalServerInstance( CACHE_ANYTHING );
-               $cacheKey = $cache->makeKey(
-                       'first-letters',
-                       $this->locale,
-                       $this->digitTransformLanguage->getCode(),
-                       self::getICUVersion()
-               );
-               $cacheEntry = $cache->get( $cacheKey );
-
-               if ( $cacheEntry && isset( $cacheEntry['version'] )
-                       && $cacheEntry['version'] == self::FIRST_LETTER_VERSION
-               ) {
-                       $this->firstLetterData = $cacheEntry;
-                       return $this->firstLetterData;
+               if ( $this->firstLetterData === null ) {
+                       $cache = ObjectCache::getLocalServerInstance( CACHE_ANYTHING );
+                       $cacheKey = $cache->makeKey(
+                               'first-letters',
+                               $this->locale,
+                               $this->digitTransformLanguage->getCode(),
+                               self::getICUVersion(),
+                               self::FIRST_LETTER_VERSION
+                       );
+                       $this->firstLetterData = $cache->getWithSetCallback( $cacheKey, $cache::TTL_WEEK, function () {
+                               return $this->fetchFirstLetterData();
+                       } );
                }
+               return $this->firstLetterData;
+       }
 
+       /**
+        * @return array
+        * @throws MWException
+        */
+       private function fetchFirstLetterData() {
                // Generate data from serialized data file
-
                if ( isset( self::$tailoringFirstLetters[$this->locale] ) ) {
-                       $letters = wfGetPrecompiledData( "first-letters-root.ser" );
+                       $letters = wfGetPrecompiledData( 'first-letters-root.ser' );
                        // Append additional characters
                        $letters = array_merge( $letters, self::$tailoringFirstLetters[$this->locale] );
                        // Remove unnecessary ones, if any
@@ -374,15 +375,11 @@ class IcuCollation extends Collation {
                $data = [
                        'chars' => array_values( $letterMap ),
                        'keys' => array_keys( $letterMap ),
-                       'version' => self::FIRST_LETTER_VERSION,
                ];
 
                // Reduce memory usage before caching
                unset( $letterMap );
 
-               // Save to cache
-               $this->firstLetterData = $data;
-               $cache->set( $cacheKey, $data, $cache::TTL_WEEK );
                return $data;
        }
 
@@ -390,30 +387,21 @@ class IcuCollation extends Collation {
         * @since 1.16.3
         */
        public function getLetterByIndex( $index ) {
-               if ( $this->firstLetterData === null ) {
-                       $this->getFirstLetterData();
-               }
-               return $this->firstLetterData['chars'][$index];
+               return $this->getFirstLetterData()['chars'][$index];
        }
 
        /**
         * @since 1.16.3
         */
        public function getSortKeyByLetterIndex( $index ) {
-               if ( $this->firstLetterData === null ) {
-                       $this->getFirstLetterData();
-               }
-               return $this->firstLetterData['keys'][$index];
+               return $this->getFirstLetterData()['keys'][$index];
        }
 
        /**
         * @since 1.16.3
         */
        public function getFirstLetterCount() {
-               if ( $this->firstLetterData === null ) {
-                       $this->getFirstLetterData();
-               }
-               return count( $this->firstLetterData['chars'] );
+               return count( $this->getFirstLetterData()['chars'] );
        }
 
        /**
index bc3687b..470a38c 100644 (file)
@@ -377,8 +377,9 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @return bool Success
         */
        final public function set( $key, $value, $ttl = 0, array $opts = [] ) {
+               $now = microtime( true );
                $lockTSE = isset( $opts['lockTSE'] ) ? $opts['lockTSE'] : self::TSE_NONE;
-               $age = isset( $opts['since'] ) ? max( 0, microtime( true ) - $opts['since'] ) : 0;
+               $age = isset( $opts['since'] ) ? max( 0, $now - $opts['since'] ) : 0;
                $lag = isset( $opts['lag'] ) ? $opts['lag'] : 0;
 
                // Do not cache potentially uncommitted data as it might get rolled back
@@ -413,7 +414,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
                }
 
                // Wrap that value with time/TTL/version metadata
-               $wrapped = $this->wrap( $value, $ttl ) + $wrapExtra;
+               $wrapped = $this->wrap( $value, $ttl, $now ) + $wrapExtra;
 
                $func = function ( $cache, $key, $cWrapped ) use ( $wrapped ) {
                        return ( is_string( $cWrapped ) )
@@ -1009,14 +1010,15 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *
         * @param mixed $value
         * @param integer $ttl [0=forever]
+        * @param float $now Unix Current timestamp just before calling set()
         * @return array
         */
-       protected function wrap( $value, $ttl ) {
+       protected function wrap( $value, $ttl, $now ) {
                return [
                        self::FLD_VERSION => self::VERSION,
                        self::FLD_VALUE => $value,
                        self::FLD_TTL => $ttl,
-                       self::FLD_TIME => microtime( true )
+                       self::FLD_TIME => $now
                ];
        }
 
@@ -1024,7 +1026,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * Do not use this method outside WANObjectCache
         *
         * @param array|string|bool $wrapped
-        * @param float $now Unix Current timestamp (preferrable pre-query)
+        * @param float $now Unix Current timestamp (preferrably pre-query)
         * @return array (mixed; false if absent/invalid, current time left)
         */
        protected function unwrap( $wrapped, $now ) {
index 7b59751..da48e00 100644 (file)
@@ -33,6 +33,14 @@ interface LinkTarget {
         */
        public function getNamespace();
 
+       /**
+        * Convenience function to test if it is in the namespace
+        *
+        * @param int $ns
+        * @return bool
+        */
+       public function inNamespace( $ns );
+
        /**
         * Get the link fragment (i.e. the bit after the #) in text form.
         *
index 61e6926..90508da 100644 (file)
@@ -310,7 +310,8 @@ class RedisBagOStuff extends BagOStuff {
         * @return mixed
         */
        protected function unserialize( $data ) {
-               return ctype_digit( $data ) ? intval( $data ) : unserialize( $data );
+               $int = intval( $data );
+               return $data === (string)$int ? $int : unserialize( $data );
        }
 
        /**
index 415e664..26058c9 100644 (file)
@@ -23,6 +23,7 @@ class ExtensionProcessor implements Processor {
                'AvailableRights',
                'ContentHandlers',
                'ConfigRegistry',
+               'SessionProviders',
                'CentralIdLookupProviders',
                'RateLimits',
                'RecentChangesFlags',
index 8e0239a..85fc53d 100644 (file)
@@ -227,15 +227,17 @@ class ResourceLoaderContext {
         * Get the possibly-cached User object for the specified username
         *
         * @since 1.25
-        * @return User|bool false if a valid object cannot be created
+        * @return User
         */
        public function getUserObj() {
                if ( $this->userObj === null ) {
                        $username = $this->getUser();
                        if ( $username ) {
-                               $this->userObj = User::newFromName( $username );
+                               // Use provided username if valid, fallback to anonymous user
+                               $this->userObj = User::newFromName( $username ) ?: new User;
                        } else {
-                               $this->userObj = new User; // Anonymous user
+                               // Anonymous user
+                               $this->userObj = new User;
                        }
                }
 
index e2a8e41..b225185 100644 (file)
@@ -40,7 +40,7 @@ class ResourceLoaderUserGroupsModule extends ResourceLoaderWikiModule {
                }
 
                $user = $context->getUserObj();
-               if ( !$user || $user->isAnon() ) {
+               if ( $user->isAnon() ) {
                        return [];
                }
 
index d584165..c38f8d8 100644 (file)
@@ -43,7 +43,7 @@ class ResourceLoaderUserModule extends ResourceLoaderWikiModule {
                }
 
                $user = $context->getUserObj();
-               if ( !$user || $user->isAnon() ) {
+               if ( $user->isAnon() ) {
                        return [];
                }
 
index 0e291ed..5c504f3 100644 (file)
@@ -181,6 +181,31 @@ class MediaWikiTitleCodec implements TitleFormatter, TitleParser {
                );
        }
 
+       /**
+        * @since 1.27
+        * @see TitleFormatter::getPrefixedDBkey()
+        * @param LinkTarget $target
+        * @return string
+        */
+       public function getPrefixedDBkey( LinkTarget $target ) {
+               $key = '';
+               if ( $target->isExternal() ) {
+                       $key .= $target->getInterwiki() . ':';
+               }
+               $nsName = $this->getNamespaceName(
+                       $target->getNamespace(),
+                       $target->getText()
+               );
+
+               if ( $nsName !== '' ) {
+                       $key .= $nsName . ':';
+               }
+
+               $key .= $target->getText();
+
+               return strtr( $key, ' ', '_' );
+       }
+
        /**
         * @see TitleFormatter::getText()
         *
index c081129..5177606 100644 (file)
@@ -68,6 +68,18 @@ interface TitleFormatter {
         */
        public function getPrefixedText( LinkTarget $title );
 
+       /**
+        * Return the title in prefixed database key form, with interwiki
+        * and namespace.
+        *
+        * @since 1.27
+        *
+        * @param LinkTarget $target
+        *
+        * @return string
+        */
+       public function getPrefixedDBkey( LinkTarget $target );
+
        /**
         * Returns the title formatted for display, with namespace and fragment.
         *
index 63c075f..597bf2f 100644 (file)
@@ -94,6 +94,15 @@ class TitleValue implements LinkTarget {
                return $this->namespace;
        }
 
+       /**
+        * @since 1.27
+        * @param int $ns
+        * @return bool
+        */
+       public function inNamespace( $ns ) {
+               return $this->namespace == $ns;
+       }
+
        /**
         * @return string
         */
index ab9421f..0f46528 100644 (file)
        "newarticle": "(جديد)",
        "newarticletext": "لقد تبعت وصلة لصفحة لم يتم إنشائها بعد.\nلإنشاء هذه الصفحة ابدأ الكتابة في الصندوق بالأسفل (انظر في [$1 صفحة المساعدة] للمزيد من المعلومات).\nإذا كانت زيارتك لهذه الصفحة بالخطأ، اضغط على زر ''رجوع'' في متصفح الإنترنت لديك.",
        "anontalkpagetext": "----''هذه صفحة نقاش لمستخدم مجهول لم يقم بإنشاء حساب بعد أو لا يستعمل ذلك الحساب.\nلذا فيجب علينا استعمال رقم الأيبي للتعرف عليه/عليها.\nمثل هذا العنوان يمكن أن يشترك فيه عدة مستخدمين.\nلو كنت مستخدما مجهولا وتشعر بأن تعليقات لا تخصك تم توجيهها إليك، من فضلك [[Special:UserLogin/signup|أنشئ حسابا]] أو [[Special:UserLogin|سجل الدخول]] لتجنب الارتباك المستقبلي مع مستخدمين مجهولين آخرين.''",
-       "noarticletext": "لا يوجد حاليا أي نص في هذه الصفحة.\nيمكنك [[Special:Search/{{PAGENAME}}|البحث عن عنوان هذه الصفحة]] في الصفحات الأخرى،\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} البحث في السجلات المتعلقة]،\nأو [{{fullurl:{{FULLPAGENAME}}|action=edit}} تعديل هذه الصفحة]</span>.",
+       "noarticletext": "الصفحة خالية. يمكنك [[Special:Search/{{PAGENAME}}|البحث عن عنوانها]] في الصفحات الأخرى أو\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} البحث في السجلات] (لتعرف إذا حذفت)،\nأو '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} إنشاؤها بنفسك]'''</span>.",
        "noarticletext-nopermission": "لا يوجد حاليا أي نص في هذه الصفحة.\nيمكنك [[Special:Search/{{PAGENAME}}|البحث عن عنوان هذه الصفحة]] في الصفحات الأخرى، أو <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} البحث في السجلات المتعلقة بها]</span>، لكنك لست مخولاً لإنشاء هذه الصفحة.",
        "missing-revision": "المراجعة #$1 من الصفحة المسماة \"{{FULLPAGENAME}}\" غير موجودة.\n\nهذا يحدث عادة عن طريق اتباع وصلة تاريخ قديمة لصفحة تم حذفها.\nالتفاصيل يمكن إيجادها في [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سجل الحذف].",
        "userpage-userdoesnotexist": "حساب المستخدم \"<nowiki>$1</nowiki>\" غير مسجل.\nمن فضلك تأكد أنك تريد إنشاء/تعديل هذه الصفحة.",
index 58ec3a8..c132dca 100644 (file)
        "apisandbox-request-url-label": "URL-адрас запыту:",
        "apisandbox-request-time": "Час запыту: {{PLURAL:$1|$1 мс}}",
        "apisandbox-results-fixtoken": "Выпраўце токен і паўтарыце адпраўку",
+       "apisandbox-results-fixtoken-fail": "Памылка пры атрыманьні токену «$1».",
        "apisandbox-alert-page": "Палі на гэтай старонцы няслушныя.",
        "apisandbox-alert-field": "Значэньне гэтага поля зьяўляецца няслушным.",
        "booksources": "Крыніцы кніг",
        "whatlinkshere-prev": "{{PLURAL:$1|папярэдняя|папярэднія}} $1",
        "whatlinkshere-next": "{{PLURAL:$1|наступная|наступныя}} $1",
        "whatlinkshere-links": "← спасылкі",
-       "whatlinkshere-hideredirs": "$1 перанакіраваньні",
+       "whatlinkshere-hideredirs": "Схаваць перанакіраваньні",
        "whatlinkshere-hidetrans": "$1 уключэньні",
        "whatlinkshere-hidelinks": "$1 спасылкі",
        "whatlinkshere-hideimages": "$1 спасылкі на выявы",
index eb1e5ae..828033c 100644 (file)
        "revdelete-show-file-submit": "Да",
        "revdelete-selected-text": "{{PLURAL:$1|Избрана версия|Избрани версии}} от [[:$2]]:",
        "logdelete-selected": "{{PLURAL:$1|Избрано събитие|Избрани събития}}:",
+       "revdelete-text-text": "Изтритите редакции ще продължат да се виждат в историята на страницата, но части от съдържанието ще бъдат публично недостъпни.",
+       "revdelete-text-others": "Другите администратори ще продължат да имат достъп до скритото съдържание и могат да го възстановят, освен ако не бъдат наложени допълнителни ограничения.",
        "revdelete-confirm": "Необходимо е да потвърдите, че желаете да извършите действието, разбирате последствията и го правите според [[{{MediaWiki:Policy-url}}|политиката]].",
        "revdelete-suppress-text": "Премахването трябва да се използва '''само''' при следните случаи:\n* Потенциално уязвима в правно отношение информация\n* Неподходяща лична информация\n*: ''домашни адреси и телефонни номера, номера за социално осигуряване и др.''",
        "revdelete-legend": "Задаване на ограничения:",
        "logempty": "Дневникът не съдържа записи, отговарящи на избрания критерий.",
        "log-title-wildcard": "Търсене на заглавия, започващи със",
        "showhideselectedlogentries": "Промяна на видимостта на избраните записи",
-       "checkbox-all": "Всички",
-       "checkbox-none": "Никои",
+       "checkbox-select": "Избери: $1",
+       "checkbox-all": "всички",
+       "checkbox-none": "никои",
+       "checkbox-invert": "обърни избора",
        "allpages": "Всички страници",
        "nextpage": "Следваща страница ($1)",
        "prevpage": "Предходна страница ($1)",
        "sqlite-no-fts": "$1 без поддръжка на пълнотекстово търсене",
        "logentry-delete-delete": "$1 {{GENDER:$2|изтри}} страницата $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|възстанови}} страницата $3",
+       "logentry-delete-revision": "$1 {{GENDER:$2|промени}} видимостта на {{PLURAL:$5|една редакция|$5 редакции}} в страница $3: $4",
+       "logentry-delete-event-legacy": "$1 {{GENDER:$2|промени}} видимостта на събитията от дневниците за страница $3",
        "logentry-delete-revision-legacy": "$1 {{GENDER:$2|промени}} видимостта на версиите на страница $3",
        "logentry-suppress-revision": "$1 тайно {{GENDER:$2|промени}} видимостта на {{PLURAL:$5|една версия|$5 версии}} на страницата $3: $4",
        "logentry-suppress-revision-legacy": "$1 тайно {{GENDER:$2|промени}} видимостта на версиите на страница $3",
index 8c40276..05c4418 100644 (file)
        "revdelete-restricted": "tilføjede begrænsninger for administratorer",
        "revdelete-unrestricted": "fjernede begrænsninger for administratorer",
        "logentry-block-block": "$1 {{GENDER:$2|blokerede}} {{GENDER:$4|$3}} med en udløbstid på $5 $6",
+       "logentry-block-unblock": "$1 {{GENDER:$2|ophævede blokering af}} {{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1 {{GENDER:$2|ændrede}} blokeringsindstillinger for {{GENDER:$4|$3}} med en udløbstid på $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|ændrede}} blokeringsindstillinger for {{GENDER:$4|$3}} med en udløbstid på $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|flyttede}} siden $3 til $4",
index 05a1720..be7ed04 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|vorheriger|vorherige $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|nächster|nächste $1}}",
        "whatlinkshere-links": "← Links",
-       "whatlinkshere-hideredirs": "Weiterleitungen $1",
-       "whatlinkshere-hidetrans": "Vorlageneinbindungen $1",
-       "whatlinkshere-hidelinks": "Links $1",
-       "whatlinkshere-hideimages": "Dateilinks $1",
+       "whatlinkshere-hideredirs": "Weiterleitungen ausblenden",
+       "whatlinkshere-hidetrans": "Vorlageneinbindungen ausblenden",
+       "whatlinkshere-hidelinks": "Links ausblenden",
+       "whatlinkshere-hideimages": "Dateilinks ausblenden",
        "whatlinkshere-filters": "Filter",
        "whatlinkshere-submit": "Los",
        "autoblockid": "Automatische Sperrung #$1",
index ba4d82b..3578658 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|veror|veror $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|verni|verni $1}}",
        "whatlinkshere-links": "← gırey",
-       "whatlinkshere-hideredirs": "Hetenayışê $1",
-       "whatlinkshere-hidetrans": "Açarnayışê $1",
-       "whatlinkshere-hidelinks": "Greyê $1",
+       "whatlinkshere-hideredirs": "Hetenayışan bınımne",
+       "whatlinkshere-hidetrans": "Gırêyanê açarnayışan bınımne",
+       "whatlinkshere-hidelinks": "Gırêyan bınımne",
        "whatlinkshere-hideimages": "Gıreyê dosya $1",
        "whatlinkshere-filters": "Avrêci",
        "autoblockid": "Otomatik vındarnayış #$1",
index 379bbec..6d198c2 100644 (file)
        "apisandbox-dynamic-parameters-add-placeholder": "Ονομασία παραμέτρου",
        "apisandbox-dynamic-error-exists": "Η παράμετρος με την ονομασία \"$1\" υπάρχει ήδη",
        "apisandbox-submit-invalid-fields-title": "Κάποια από τα πεδία δεν είναι έγκυρα",
-       "apisandbox-submit-invalid-fields-message": "ΠαÏ\81ακαλÏ\8e Î´Î¹Î¿Ï\81θÏ\8eÏ\83Ï\84ε Ï\84α Ï\83ημειÏ\89μένα Ï\80εδία ÎºÎ±Î¹ Ï\80Ï\81οÏ\83Ï\80αθείστε ξανά.",
+       "apisandbox-submit-invalid-fields-message": "ΠαÏ\81ακαλÏ\8e Î´Î¹Î¿Ï\81θÏ\8eÏ\83Ï\84ε Ï\84α Ï\83ημειÏ\89μένα Ï\80εδία ÎºÎ±Î¹ Ï\80Ï\81οÏ\83Ï\80αθήστε ξανά.",
        "apisandbox-results": "Αποτελέσματα",
        "apisandbox-sending-request": "Αποστολή αιτήματος API...",
        "apisandbox-loading-results": "Λήψη αποτελεσμάτων API...",
index 6168265..52e1cd1 100644 (file)
        "upload-form-label-not-own-work-local-local": "Vi eble ŝatu egale pravi [[Special:Upload|la defaŭltan paĝon]].",
        "upload-form-label-own-work-message-default": "Mi komprenas ke mi alŝutas tiun dosieron al komunigita deponejo. Mi konfirmas ke mi faras tiun respektante de la uzadtermoj kaj de la permisilopolitikoj tie.",
        "upload-form-label-not-own-work-message-default": "Se vi ne eblas alŝuti tiun dosieron respektante de politikoj de komuna deponejo, bonvolu fermi tiun dialogon kaj provi denove kun alia metodo.",
+       "upload-form-label-not-own-work-local-default": "Vi ankaŭ eble dezirus provi per uzi [[Special:Upload|la alŝutan paĝon sur {{SITENAME}}]], se ĉi tiu dosiero povas esti alŝutita tie respektante de iliaj politikoj.",
+       "upload-form-label-own-work-message-shared": "Mi atestas ke mi posedas la kopirajton sur ĉi tiu dosiero kaj konsenti al neeksvalidiĝebla liberigo de ĉi tiu dosiero al Vikimedia Komunejo sub la [https://creativecommons.Org/licencoj/de-sa/4.0/ Krea Komunaĵo Atribuite Samkondiĉe 4.0] permisilo kaj mi konsentas al la [https://wikimediafoundation.Org/vikiaj/Terminoj_de_Uzaj kondiĉoj de uzo].",
+       "upload-form-label-not-own-work-message-shared": "Se vi ne posedas la kopirajton sur ĉi tiu dosiero aŭ vi deziras liberigi ĝin sub malsama licenco, konsideru uzi la [https://commons.Wikimedia.Org/vikia/Specialaĵo:UploadWizard asistanto de  alŝutado al komunejo].",
+       "upload-form-label-not-own-work-local-shared": "Vi ankaŭ eble dezirus provi per uzi [[Special:Upload|la paĝon de alŝutado sur {{SITENAME}}]], se ĉi tiu dosiero povas esti alŝutita tie respektante de iliaj politikoj.",
        "backend-fail-stream": "Ne povis fluigi dosieron $1.",
        "backend-fail-backup": "Ne povis enarkivigi dosieron $1.",
        "backend-fail-notexists": "La dosiero $1 ne ekzistas.",
        "apihelp": "Helpo pri API",
        "apihelp-no-such-module": "Modulo \"$1\" ne estis trovita.",
        "apisandbox": "API testejo",
+       "apisandbox-jsonly": "JavaScript estas postulita por uzi la API provejon.",
        "apisandbox-api-disabled": "API estas malŝalta en ĉi tiu retejo.",
        "apisandbox-intro": "Uzu tiun ĉi paĝon por eksperimenti kun '''MediaWiki API'''.\nVidu [//www.mediawiki.org/wiki/API:Main_page la API-dokumentadon] por pli da detaloj pri la uzo de API. Ekz-e: [//www.mediawiki.org/wiki/API#A_simple_example atingi la enhavon de la Ĉefpaĝo]. Elektu agon por vidi pliajn ekzemplojn.\n\nNotu ke, kvankam ĉi tiu estas provejo, agoj kiun vi faros en ĉi tiu paĝo povas modifi la vikion.",
        "apisandbox-unfullscreen": "Montri paĝon",
index 296b22e..0337736 100644 (file)
                        "Eloy",
                        "Lemondoge",
                        "Jdforrester",
-                       "Indiralena"
+                       "Indiralena",
+                       "Rubentl134"
                ]
        },
        "tog-underline": "Subrayar los enlaces:",
        "changecontentmodel-success-text": "Se ha cambiado el tipo de contenido de [[:$1]].",
        "changecontentmodel-cannot-convert": "El contenido de [[:$1]] no se puede convertir a un tipo de $2.",
        "changecontentmodel-nodirectediting": "El modelo de contenido $1 no admite la edición directa",
+       "changecontentmodel-emptymodels-title": "No hay modelos de contenido disponibles",
        "log-name-contentmodel": "Registro de cambios del modelo de contenido",
        "log-description-contentmodel": "Eventos relacionados con los modelos de contenido de una página",
        "logentry-contentmodel-new": "$1 {{GENDER:$2|creó}} la página $3 usando un modelo de contenido no predeterminado \"$5\"",
        "whatlinkshere-hideredirs": "$1 redirecciones",
        "whatlinkshere-hidetrans": "$1 inclusiones",
        "whatlinkshere-hidelinks": "$1 enlaces",
-       "whatlinkshere-hideimages": "$1 enlaces a archivos",
+       "whatlinkshere-hideimages": "Ocultar los vínculos de archivo",
        "whatlinkshere-filters": "Filtros",
        "whatlinkshere-submit": "Ir",
        "autoblockid": "Bloqueo automático #$1",
        "lockdbsuccesstext": "La base de datos de {{SITENAME}} ha sido bloqueada.\n<br />Recuerde retirar el bloqueo después de completar las tareas de mantenimiento.",
        "unlockdbsuccesstext": "La base de datos de {{SITENAME}} ha sido desbloqueada.",
        "lockfilenotwritable": "El archivo-cerrojo de la base de datos no tiene permiso de escritura. Para bloquear o desbloquear la base de datos, este archivo tiene que ser escribible por el servidor web.",
+       "databaselocked": "La base de datos ya está bloqueada.",
        "databasenotlocked": "La base de datos no está bloqueada.",
        "lockedbyandtime": "(por {{GENDER:$1|$1}} el $2 a las $3)",
        "move-page": "Trasladar $1",
index 9788eb5..672b071 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|précédente|$1 précédentes}}",
        "whatlinkshere-next": "{{PLURAL:$1|suivante|$1 suivantes}}",
        "whatlinkshere-links": "← liens",
-       "whatlinkshere-hideredirs": "$1 les redirections",
-       "whatlinkshere-hidetrans": "$1 les inclusions",
-       "whatlinkshere-hidelinks": "$1 les liens",
-       "whatlinkshere-hideimages": "$1 les liens vers le fichier",
+       "whatlinkshere-hideredirs": "Masquer les redirections",
+       "whatlinkshere-hidetrans": "Masquer les inclusions",
+       "whatlinkshere-hidelinks": "Masquer les liens",
+       "whatlinkshere-hideimages": "Masquer les liens vers le fichier",
        "whatlinkshere-filters": "Filtres",
        "whatlinkshere-submit": "Lister",
        "autoblockid": "Blocage automatique #$1",
index 6499e8e..ba7e524 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|anterior|$1 anteriores}}",
        "whatlinkshere-next": "{{PLURAL:$1|seguinte|$1 seguintes}}",
        "whatlinkshere-links": "← ligazóns",
-       "whatlinkshere-hideredirs": "$1 as redireccións",
-       "whatlinkshere-hidetrans": "$1 as inclusións",
-       "whatlinkshere-hidelinks": "$1 as ligazóns",
-       "whatlinkshere-hideimages": "$1 as ligazóns ao ficheiro",
+       "whatlinkshere-hideredirs": "Ocultar as redireccións",
+       "whatlinkshere-hidetrans": "Ocultar as inclusións",
+       "whatlinkshere-hidelinks": "Ocultar as ligazóns",
+       "whatlinkshere-hideimages": "Ocultar as ligazóns ao ficheiro",
        "whatlinkshere-filters": "Filtros",
        "whatlinkshere-submit": "Ir",
        "autoblockid": "Bloqueo automático nº$1",
index 1abe8a9..0d61e70 100644 (file)
        "category_header": "דפים בקטגוריה \"$1\"",
        "subcategories": "קטגוריות משנה",
        "category-media-header": "קובצי מדיה בקטגוריה \"$1\"",
-       "category-empty": "'''קטגוריה זו אינה כוללת דפים או קובצי מדיה.'''",
+       "category-empty": "<em>קטגוריה זו אינה כוללת דפים או קובצי מדיה.</em>",
        "hidden-categories": "{{PLURAL:$1|קטגוריה מוסתרת|קטגוריות מוסתרות}}",
        "hidden-category-category": "קטגוריות מוסתרות",
        "category-subcat-count": "{{PLURAL:$2|קטגוריה זו כוללת את קטגוריית המשנה הבאה בלבד.|קטגוריה זו כוללת את {{PLURAL:$1|קטגוריית המשנה המוצגת להלן|$1 קטגוריות המשנה המוצגות להלן}}, וכוללת בסך־הכול $2 קטגוריות משנה.}}",
        "redirectedfrom": "(הופנה מהדף $1)",
        "redirectpagesub": "דף הפניה",
        "redirectto": "הפניה ל:",
-       "lastmodifiedat": "×\93×£ ×\96×\94 ×©×\81×\95Ö¼× ×\94 ×\9c×\90×\97ר×\95× ×\94 ×\91Ö¾$1, ×\91שע×\94 $2.",
+       "lastmodifiedat": "דף זה שוּנה לאחרונה ב־$1, בשעה $2.",
        "viewcount": "דף זה נצפה {{PLURAL:$1|פעם אחת|פעמיים|$1 פעמים}}.",
        "protectedpage": "דף מוגן",
        "jumpto": "קפיצה אל:",
        "perfcachedts": "המידע הבא הוא עותק שמור בזיכרון המטמון של המידע, שעודכן לאחרונה ב־$1. לכל היותר {{PLURAL:$4|תוצאה אחת נשמרת|$4 תוצאות נשמרות}} בזיכרון המטמון.",
        "querypage-no-updates": "העדכונים לדף זה כרגע מופסקים, והמידע לא יעודכן באופן שוטף.",
        "viewsource": "הצגת מקור",
-       "viewsource-title": "הצגת המקור של $1",
+       "viewsource-title": "הצגת המקור של הדף \"$1\"",
        "actionthrottled": "הפעולה הוגבלה",
        "actionthrottledtext": "כאמצעי נגד שימוש לרעה, קיימת מגבלה על ביצוע פעולה זו פעמים רבות מדי בזמן קצר, וחרגת מהמגבלה הזאת.\nנא לנסות שוב בעוד מספר דקות.",
        "protectedpagetext": "דף זה מוגן כדי למנוע עריכה ופעולות אחרות.",
        "subject": "נושא:",
        "minoredit": "זוהי עריכה משנית",
        "watchthis": "מעקב אחרי דף זה",
-       "savearticle": "ש×\9e×\99ר×\94",
+       "savearticle": "ש×\9e×\99רת ×\94×\93×£",
        "publishpage": "פרסום הדף",
        "preview": "תצוגה מקדימה",
        "showpreview": "תצוגה מקדימה",
        "revdelete-unsuppress": "הסרת הגבלות בגרסאות המשוחזרות",
        "revdelete-log": "סיבה:",
        "revdelete-submit": "ביצוע על {{PLURAL:$1|הגרסה שנבחרה|הגרסאות שנבחרו}}",
-       "revdelete-success": "×\9eצ×\91 ×\94תצ×\95×\92×\94 ×©×\9c ×\94×\92רס×\94 ×©×\81×\95Ö¼× ×\94.",
+       "revdelete-success": "מצב התצוגה של הגרסה שוּנה.",
        "revdelete-failure": "לא ניתן היה לשנות את מצב התצוגה של הגרסה:\n$1",
-       "logdelete-success": "×\9eצ×\91 ×\94תצ×\95×\92×\94 ×©×\9c ×¤×¢×\95×\9cת ×\94×\99×\95×\9e×\9f ×©×\81×\95Ö¼× ×\94.",
+       "logdelete-success": "מצב התצוגה של פעולת היומן שוּנה.",
        "logdelete-failure": "לא ניתן היה לשנות את מצב התצוגה של היומן:\n$1",
        "revdel-restore": "שינוי מצב התצוגה",
        "pagehist": "היסטוריית הדף",
        "recentchangeslinked": "שינויים בדפים המקושרים",
        "recentchangeslinked-feed": "שינויים בדפים המקושרים",
        "recentchangeslinked-toolbox": "שינויים בדפים המקושרים",
-       "recentchangeslinked-title": "שינויים בדפים המקושרים מהדף $1",
+       "recentchangeslinked-title": "שינויים בדפים המקושרים מהדף \"$1\"",
        "recentchangeslinked-summary": "בדף מיוחד זה רשומים השינויים האחרונים בדפים המקושרים מתוך הדף (או בדפים הכלולים בקטגוריה).\nדפים ב[[Special:Watchlist|רשימת המעקב שלכם]] מוצגים ב'''הדגשה'''.",
        "recentchangeslinked-page": "שם הדף:",
        "recentchangeslinked-to": "הצגת השינויים בדפים המקשרים לדף הנתון במקום זאת",
        "recentchanges-page-removed-from-category": "הדף [[:$1]] הוסר מקטגוריה",
        "recentchanges-page-removed-from-category-bundled": "הדף [[:$1]] הוסר מקטגוריה, [[Special:WhatLinksHere/$1|והוא מוכלל בדפים אחרים]]",
        "autochange-username": "שינוי אוטומטי של מדיה־ויקי",
-       "upload": "העלאת קובץ לשרת",
-       "uploadbtn": "×\94×¢×\9c×\90×\94",
+       "upload": "העלאת קובץ",
+       "uploadbtn": "×\94×¢×\9c×\90ת ×\94ק×\95×\91×¥",
        "reuploaddesc": "ביטול ההעלאה וחזרה לטופס העלאת קבצים לשרת",
        "upload-tryagain": "שליחת התיאור החדש של הקובץ",
        "uploadnologin": "לא נכנסת לחשבון",
        "excontent": "התוכן היה: \"$1\"",
        "excontentauthor": "התוכן היה: \"$1\", {{GENDER:$2|והתורם היחיד היה|והתורמת היחידה הייתה}} \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|שיחה]])",
        "exbeforeblank": "התוכן לפני שרוקן היה: \"$1\"",
-       "delete-confirm": "מחיקת \"$1\"",
+       "delete-confirm": "מחיקת הדף \"$1\"",
        "delete-legend": "מחיקה",
        "historywarning": "<strong>אזהרה:</strong> לדף שאתם עומדים למחוק יש היסטוריית שינויים של {{PLURAL:$1|גרסה אחת|$1 גרסאות}}:",
        "historyaction-submit": "הצגה",
        "sp-contributions-newonly": "הצגת עריכות שהן יצירות של דפים בלבד",
        "sp-contributions-submit": "חיפוש",
        "whatlinkshere": "דפים המקושרים לכאן",
-       "whatlinkshere-title": "דפים המקשרים לדף $1",
+       "whatlinkshere-title": "דפים המקשרים לדף \"$1\"",
        "whatlinkshere-page": "דף:",
        "linkshere": "הדפים שלהלן מקושרים לדף '''[[:$1]]''':",
        "nolinkshere": "אין דפים המקושרים לדף '''[[:$1]]'''.",
        "whatlinkshere-prev": "{{PLURAL:$1|הקודם|$1 הקודמים}}",
        "whatlinkshere-next": "{{PLURAL:$1|הבא|$1 הבאים}}",
        "whatlinkshere-links": "→ קישורים",
-       "whatlinkshere-hideredirs": "$1 הפניות",
-       "whatlinkshere-hidetrans": "$1 הכללות",
-       "whatlinkshere-hidelinks": "$1 קישורים",
-       "whatlinkshere-hideimages": "$1 קישורים לקובץ",
+       "whatlinkshere-hideredirs": "הסתרת הפניות",
+       "whatlinkshere-hidetrans": "הסתרת הכללות",
+       "whatlinkshere-hidelinks": "הסתרת קישורים",
+       "whatlinkshere-hideimages": "הסתרת קישורי קבצים",
        "whatlinkshere-filters": "מסננים",
        "whatlinkshere-submit": "הצגה",
        "autoblockid": "חסימה אוטומטית #$1",
        "databaselocked": "בסיס הנתונים כבר נעול.",
        "databasenotlocked": "בסיס הנתונים אינו נעול.",
        "lockedbyandtime": "(על־ידי $1 ב־$3, $2)",
-       "move-page": "העברת $1",
+       "move-page": "העברת הדף \"$1\"",
        "move-page-legend": "העברת דף",
        "movepagetext": "שימוש בטופס שלהלן ישנה את שמו של דף, ויעביר את כל ההיסטוריה שלו לשם חדש.\nהשם הישן יהפוך לדף הפניה אל הדף עם השם החדש.\nבאפשרותכם לעדכן אוטומטית דפי הפניה לכותרת המקורית.\nאם תבחרו לא לעשות זאת, אנא ודאו שאין [[Special:DoubleRedirects|הפניות כפולות]] או [[Special:BrokenRedirects|שבורות]].\nאתם אחראים לוודא שכל הקישורים ימשיכו להצביע למקום שאליו הם אמורים להצביע.\n\nשימו לב: הדף <strong>לא</strong> יועבר אם כבר יש דף תחת השם החדש, אלא אם הדף השני הוא הפניה ואין לו היסטוריית עריכות קודמות.\nפירוש הדבר שאפשר לשנות חזרה את שמו של דף לשם המקורי אם נעשתה טעות, ושלא ניתן לדרוס דף קיים.\n\n<strong>לתשומת לבכם:</strong>\nשינוי זה עשוי להיות שינוי דרסטי ובלתי צפוי לדף פופולרי;\nאנא ודאו שאתם מבינים את השלכות המעשה לפני שאתם ממשיכים.",
        "movepagetext-noredirectfixer": "שימוש בטופס שלהלן ישנה את שמו של דף, ויעביר את כל ההיסטוריה שלו לשם חדש.\nהשם הישן יהפוך לדף הפניה אל הדף עם השם החדש.\nאנא ודאו שאין [[Special:DoubleRedirects|הפניות כפולות]] או [[Special:BrokenRedirects|שבורות]].\nאתם אחראים לוודא שכל הקישורים ימשיכו להצביע למקום שאליו הם אמורים להצביע.\n\nשימו לב: הדף <strong>לא</strong> יועבר אם כבר יש דף תחת השם החדש, אלא אם הדף הזה הוא הפניה ואין לו היסטוריית עריכות קודמות.\nפירוש הדבר שאפשר לשנות חזרה את שמו של דף לשם המקורי אם נעשתה טעות, ושלא ניתן לדרוס דף קיים.\n\n<strong>לתשומת לבכם:</strong>\nשינוי זה עשוי להיות שינוי דרסטי ובלתי צפוי לדף פופולרי;\nאנא ודאו שאתם מבינים את השלכות המעשה לפני שאתם ממשיכים.",
        "tooltip-pt-mytalk": "דף השיחה שלך",
        "tooltip-pt-anontalk": "שיחה על תרומות המשתמש האנונימי",
        "tooltip-pt-preferences": "ההעדפות שלך",
-       "tooltip-pt-watchlist": "רשימת הדפים שאתם עוקבים אחרי השינויים בהם",
+       "tooltip-pt-watchlist": "רשימת הדפים ש{{GENDER:|אתה עוקב|את עוקבת}} אחרי השינויים בהם",
        "tooltip-pt-mycontris": "רשימת התרומות שלך",
        "tooltip-pt-anoncontribs": "רשימת העריכות שנעשו מכתובת ה־IP הזאת",
        "tooltip-pt-login": "מומלץ להיכנס לחשבון; עם זאת, אין חובה לעשות זאת",
        "tooltip-save": "שמירת השינויים שלך",
        "tooltip-publish": "פרסום השינויים שלך",
        "tooltip-preview": "תצוגה מקדימה של השינויים שלך. נא להשתמש באפשרות זו לפני השמירה.",
-       "tooltip-diff": "צפ×\99×\99×\94 ×\91ש×\99× ×\95×\99×\99×\9d ×©×¢×¨×\9bת×\9d בטקסט",
+       "tooltip-diff": "×\94צ×\92ת ×\94ש×\99× ×\95×\99×\99×\9d ×©×\91×\99צעת בטקסט",
        "tooltip-compareselectedversions": "צפייה בהשוואת שתי גרסאות של דף זה",
        "tooltip-watch": "הוספת דף זה לרשימת המעקב שלך",
        "tooltip-watchlistedit-normal-submit": "הסרת הדפים",
        "anonymous": "{{PLURAL:$1|משתמש אנונימי|משתמשים אנונימיים}} של {{SITENAME}}",
        "siteuser": "משתמש {{SITENAME}} $1",
        "anonuser": "משתמש אנונימי של {{SITENAME}} $1",
-       "lastmodifiedatby": "דף זה שונה לאחרונה ב־$2, $1 על־ידי $3.",
+       "lastmodifiedatby": "דף זה שוּנה לאחרונה ב־$2, $1 על־ידי $3.",
        "othercontribs": "מבוסס על העבודה של $1.",
        "others": "אחרים",
        "siteusers": "{{PLURAL:$2|{{GENDER:$1|משתמש}}|משתמשי}} {{SITENAME}} $1",
        "spam_blanking": "כל הגרסאות כוללות קישורים ל־$1, מרוקן את הדף",
        "spam_deleting": "כל הגרסאות כוללות קישורים ל־$1, מוחק את הדף",
        "simpleantispam-label": "בדיקת אנטי־ספאם.\n<strong>אל</strong> תמלאו שדה זה!",
-       "pageinfo-title": "מידע על \"$1\"",
+       "pageinfo-title": "מידע על הדף \"$1\"",
        "pageinfo-not-current": "מצטערים, לא ניתן להציג את המידע הזה לגרסאות ישנות.",
        "pageinfo-header-basic": "מידע בסיסי",
        "pageinfo-header-edits": "היסטוריית עריכות",
index fe2cd10..14da229 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|precedente|precedenti $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|successivo|successivi $1}}",
        "whatlinkshere-links": "← collegamenti",
-       "whatlinkshere-hideredirs": "$1 redirect",
-       "whatlinkshere-hidetrans": "$1 inclusioni",
-       "whatlinkshere-hidelinks": "$1 collegamenti",
-       "whatlinkshere-hideimages": "$1 link da file",
+       "whatlinkshere-hideredirs": "Nascondi redirect",
+       "whatlinkshere-hidetrans": "Nascondi inclusioni",
+       "whatlinkshere-hidelinks": "Nascondi collegamenti",
+       "whatlinkshere-hideimages": "Nascondi collegamenti da file",
        "whatlinkshere-filters": "Filtri",
        "whatlinkshere-submit": "Vai",
        "autoblockid": "Autoblocco #$1",
        "autoredircomment": "Redirect alla pagina [[$1]]",
        "autosumm-new": "Creata pagina con \"$1\"",
        "autosumm-newblank": "Creata pagina vuota",
-       "size-bytes": "$1 byte",
+       "size-bytes": "$1 {{PLURAL:$1|byte}}",
        "lag-warn-normal": "Le modifiche apportate {{PLURAL:$1|nell'ultimo secondo|negli ultimi $1 secondi}} potrebbero non apparire in questa lista.",
        "lag-warn-high": "A causa di un eccessivo ritardo nell'aggiornamento del server di database, le modifiche apportate {{PLURAL:$1|nell'ultimo secondo|negli ultimi $1 secondi}} potrebbero non apparire in questa lista.",
        "watchlistedit-normal-title": "Modifica osservati speciali",
index 7e4cc1a..ca96110 100644 (file)
        "whatlinkshere-prev": "de vörijje {{PLURAL:$1||$1|noll}} zeije",
        "whatlinkshere-next": "de nächste {{PLURAL:$1||$1|noll}} zeije",
        "whatlinkshere-links": "← Links",
-       "whatlinkshere-hideredirs": "de Ömleijdonge $1",
+       "whatlinkshere-hideredirs": "De Ömleijdonge verschteijsche",
        "whatlinkshere-hidetrans": "de Oproofe $1",
-       "whatlinkshere-hidelinks": "de nommahle Lengks $1",
+       "whatlinkshere-hidelinks": "De nommahle Lengks verschteijsche",
        "whatlinkshere-hideimages": "$1 de Lengks op Datteihje",
        "whatlinkshere-filters": "Ußsööke",
        "whatlinkshere-submit": "Lohß jonn!",
index d711ecd..1449441 100644 (file)
        "preferences": "ପସନ୍ଦ",
        "mypreferences": "ପସନ୍ଦ",
        "prefs-edits": "ସମ୍ପାଦନା ସଂଖ୍ୟା:",
-       "prefsnologintext2": "ନିà¬\9cର à¬ªà¬¸à¬¨à­\8dଦ à¬¬à¬¦à¬²ାଇବା ପାଇଁ ଲଗ ଇନ କରନ୍ତୁ ।",
+       "prefsnologintext2": "ନିà¬\9cର à¬ªà¬¸à¬¨à­\8dଦ à¬¬à¬¦à¬³ାଇବା ପାଇଁ ଲଗ ଇନ କରନ୍ତୁ ।",
        "prefs-skin": "ବହିରାବରଣ",
        "skin-preview": "ସାଇତା ଆଗରୁ ଦେଖଣା",
        "datedefault": "କୌଣସି ପସନ୍ଦ ନାହିଁ",
index dc4f956..43656ec 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|poprzednie|poprzednie $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|następne|następne $1}}",
        "whatlinkshere-links": "← linkujące",
-       "whatlinkshere-hideredirs": "$1 przekierowania",
-       "whatlinkshere-hidetrans": "$1 dołączenia",
-       "whatlinkshere-hidelinks": "$1 linki",
-       "whatlinkshere-hideimages": "$1 linki z plików",
+       "whatlinkshere-hideredirs": "Ukryj przekierowania",
+       "whatlinkshere-hidetrans": "Ukryj dołączenia",
+       "whatlinkshere-hidelinks": "Ukryj linki",
+       "whatlinkshere-hideimages": "Ukryj linki z plików",
        "whatlinkshere-filters": "Filtry",
        "whatlinkshere-submit": "Dalej",
        "autoblockid": "automatyczna blokada nr $1",
index 2faea55..751bb82 100644 (file)
        "whatlinkshere-prev": "This is part of the navigation message on the top and bottom of Whatlinkshere pages, where it is used as the first argument of {{msg-mw|Viewprevnext}}.\n\nParameters:\n* $1 - the number of items shown per page. It is not used when $1 is zero; not sure what happens when $1 is one.\nSpecial pages use {{msg-mw|Prevn}} instead (still as an argument to {{msg-mw|Viewprevnext}}).\n\nSee also:\n* {{msg-mw|Whatlinkshere-next}}\n{{Identical|Previous}}",
        "whatlinkshere-next": "This is part of the navigation message on the top and bottom of Whatlinkshere pages, where it is used as the second argument of {{msg-mw|Viewprevnext}}.\n\nParameters:\n* $1 - the number of items shown per page. It is not used when $1 is zero; not sure what happens when $1 is one.\nSpecial pages use {{msg-mw|Nextn}} instead (still as an argument to {{msg-mw|Viewprevnext}}).\n\nSee also:\n* {{msg-mw|Whatlinkshere-prev}}\n{{Identical|Next}}",
        "whatlinkshere-links": "Used on [[Special:WhatLinksHere]]. It is a link to the WhatLinksHere page of that page.\n\nExample line:\n* [[Main Page]] ([[Special:WhatLinksHere/Main Page|{{int:whatlinkshere-links}}]])\n{{Identical|Link}}",
-       "whatlinkshere-hideredirs": "Filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}\n{{Identical|Redirect}}",
-       "whatlinkshere-hidetrans": "First filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}\n{{Identical|Transclusion}}",
-       "whatlinkshere-hidelinks": "Filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}\n{{Identical|Link}}",
+       "whatlinkshere-hideredirs": "Filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}",
+       "whatlinkshere-hidetrans": "First filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}",
+       "whatlinkshere-hidelinks": "Filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 is the {{msg-mw|hide}} or {{msg-mw|show}}",
        "whatlinkshere-hideimages": "Filter option in [[Special:WhatLinksHere]]. Parameters:\n* $1 - the {{msg-mw|Hide}} or {{msg-mw|Show}}\n\nSee also:\n*{{msg-mw|Isimage}}\n*{{msg-mw|Media tip}}",
        "whatlinkshere-filters": "{{Identical|Filter}}",
        "whatlinkshere-submit": "Label for submit button in [[Special:WhatLinksHere]]\n{{Identical|Go}}",
index 2589d77..66a40c6 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|kết quả trước|$1 kết quả trước}}",
        "whatlinkshere-next": "{{PLURAL:$1|kết quả sau|$1 kết quả sau}}",
        "whatlinkshere-links": "← liên kết",
-       "whatlinkshere-hideredirs": "$1 trang đổi hướng",
+       "whatlinkshere-hideredirs": "Ẩn trang đổi hướng",
        "whatlinkshere-hidetrans": "$1 trang nhúng",
-       "whatlinkshere-hidelinks": "$1 liên kết",
-       "whatlinkshere-hideimages": "$1 liên kết tập tin",
+       "whatlinkshere-hidelinks": "Ẩn liên kết",
+       "whatlinkshere-hideimages": "Ẩn liên kết tập tin",
        "whatlinkshere-filters": "Bộ lọc",
        "whatlinkshere-submit": "Xem",
        "autoblockid": "Cấm tự động #$1",
index 68b3945..f261a47 100644 (file)
        "whatlinkshere-prev": "{{PLURAL:$1|前|前$1个}}",
        "whatlinkshere-next": "{{PLURAL:$1|后|后$1个}}",
        "whatlinkshere-links": "←链接",
-       "whatlinkshere-hideredirs": "$1重定向",
-       "whatlinkshere-hidetrans": "$1嵌入",
-       "whatlinkshere-hidelinks": "$1链接",
-       "whatlinkshere-hideimages": "$1文件链接",
+       "whatlinkshere-hideredirs": "隐藏重定向",
+       "whatlinkshere-hidetrans": "隐藏嵌入",
+       "whatlinkshere-hidelinks": "隐藏链接",
+       "whatlinkshere-hideimages": "隐藏文件链接",
        "whatlinkshere-filters": "过滤器",
        "whatlinkshere-submit": "提交",
        "autoblockid": "自动封禁#$1",
index 6d9a616..922cc87 100644 (file)
@@ -34,7 +34,7 @@ require_once __DIR__ . '/Maintenance.php';
  */
 class UpdateCollation extends Maintenance {
        const BATCH_SIZE = 100; // Number of rows to process in one batch
-       const SYNC_INTERVAL = 20; // Wait for slaves after this many batches
+       const SYNC_INTERVAL = 5; // Wait for slaves after this many batches
 
        public $sizeHistogram = [];
 
@@ -70,6 +70,7 @@ TEXT
                global $wgCategoryCollation;
 
                $dbw = $this->getDB( DB_MASTER );
+               $dbr = $this->getDB( DB_SLAVE );
                $force = $this->getOption( 'force' );
                $dryRun = $this->getOption( 'dry-run' );
                $verboseStats = $this->getOption( 'verbose-stats' );
@@ -97,6 +98,7 @@ TEXT
                $options = [
                        'LIMIT' => self::BATCH_SIZE,
                        'ORDER BY' => $orderBy,
+                       'STRAIGHT_JOIN' // per T58041
                ];
 
                if ( $force || $dryRun ) {
@@ -110,7 +112,7 @@ TEXT
                                ];
                        }
 
-                       $count = $dbw->estimateRowCount(
+                       $count = $dbr->estimateRowCount(
                                'categorylinks',
                                '*',
                                $collationConds,
@@ -118,7 +120,7 @@ TEXT
                        );
                        // Improve estimate if feasible
                        if ( $count < 1000000 ) {
-                               $count = $dbw->selectField(
+                               $count = $dbr->selectField(
                                        'categorylinks',
                                        'COUNT(*)',
                                        $collationConds,
@@ -131,6 +133,7 @@ TEXT
                                return;
                        }
                        $this->output( "Fixing collation for $count rows.\n" );
+                       wfWaitForSlaves();
                }
                $count = 0;
                $batchCount = 0;
index b1a63b0..4c75e33 100644 (file)
@@ -14,7 +14,7 @@
                        $table = $( '#mw_metadata' ),
                        $tbody = $table.find( 'tbody' );
 
-               if ( !$tbody.length || !$tbody.find( '.collapsable' ).length ) {
+               if ( !$tbody.find( '.collapsable' ).length ) {
                        return;
                }
 
index 58115c3..2eb84e6 100644 (file)
         * @constructor
         * @inheritdoc
         */
-       function ForeignTitle() {
-               ForeignTitle.parent.apply( this, arguments );
+       function ForeignTitle( title, namespace ) {
+               // We only need to handle categories here... but we don't know the target language.
+               // So assume that any namespace-like prefix is the 'Category' namespace...
+               title = title.replace( /^(.+?)_*:_*(.*)$/, 'Category:$2' ); // HACK
+               ForeignTitle.parent.call( this, title, namespace );
        }
        OO.inheritClass( ForeignTitle, mw.Title );
        ForeignTitle.prototype.getNamespacePrefix = function () {
index 78e5f6f..ccb86d0 100644 (file)
@@ -27,6 +27,7 @@
  * @file
  * @ingroup Testing
  */
+use MediaWiki\MediaWikiServices;
 
 /**
  * @ingroup Testing
@@ -331,6 +332,18 @@ class ParserTest {
                Hooks::clear( 'InterwikiLoadPrefix' );
        }
 
+       /**
+        * Reset the Title-related services that need resetting
+        * for each test
+        */
+       public static function resetTitleServices() {
+               $services = MediaWikiServices::getInstance();
+               $services->resetServiceForTesting( 'TitleFormatter' );
+               $services->resetServiceForTesting( 'TitleParser' );
+               $services->resetServiceForTesting( '_MediaWikiTitleCodec' );
+
+       }
+
        public function setupRecorder( $options ) {
                if ( isset( $options['record'] ) ) {
                        $this->recorder = new DbTestRecorder( $this );
@@ -958,6 +971,8 @@ class ParserTest {
                MWTidy::destroySingleton();
                RepoGroup::destroySingleton();
 
+               self::resetTitleServices();
+
                return $context;
        }
 
index e50b4f1..d701a81 100644 (file)
@@ -309,4 +309,118 @@ class LinkerTest extends MediaWikiLangTestCase {
                ];
                // @codingStandardsIgnoreEnd
        }
+
+       public static function provideLinkBeginHook() {
+               // @codingStandardsIgnoreStart Generic.Files.LineLength
+               return [
+                       // Modify $html
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $html = 'foobar';
+                               },
+                               '<a href="/wiki/Special:BlankPage" title="Special:BlankPage">foobar</a>'
+                       ],
+                       // Modify $attribs
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $attribs['bar'] = 'baz';
+                               },
+                               '<a href="/wiki/Special:BlankPage" title="Special:BlankPage" bar="baz">Special:BlankPage</a>'
+                       ],
+                       // Modify $query
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $query['bar'] = 'baz';
+                               },
+                               '<a href="/w/index.php?title=Special:BlankPage&amp;bar=baz" title="Special:BlankPage">Special:BlankPage</a>'
+                       ],
+                       // Force HTTP $options
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $options = [ 'http' ];
+                               },
+                               '<a href="http://example.org/wiki/Special:BlankPage" title="Special:BlankPage">Special:BlankPage</a>'
+                       ],
+                       // Force 'forcearticlepath' in $options
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $options = [ 'forcearticlepath' ];
+                                       $query['foo'] = 'bar';
+                               },
+                               '<a href="/wiki/Special:BlankPage?foo=bar" title="Special:BlankPage">Special:BlankPage</a>'
+                       ],
+                       // Abort early
+                       [
+                               function( $dummy, $title, &$html, &$attribs, &$query, &$options, &$ret ) {
+                                       $ret = 'foobar';
+                                       return false;
+                               },
+                               'foobar'
+                       ],
+               ];
+               // @codingStandardsIgnoreEnd
+       }
+
+       /**
+        * @dataProvider provideLinkBeginHook
+        */
+       public function testLinkBeginHook( $callback, $expected ) {
+               $this->setMwGlobals( [
+                       'wgArticlePath' => '/wiki/$1',
+                       'wgWellFormedXml' => true,
+                       'wgServer' => '//example.org',
+                       'wgCanonicalServer' => 'http://example.org',
+                       'wgScriptPath' => '/w',
+                       'wgScript' => '/w/index.php',
+               ] );
+
+               $this->setMwGlobals( 'wgHooks', [ 'LinkBegin' => [ $callback ] ] );
+               $title = SpecialPage::getTitleFor( 'Blankpage' );
+               $out = Linker::link( $title );
+               $this->assertEquals( $expected, $out );
+       }
+
+       public static function provideLinkEndHook() {
+               return [
+                       // Override $html
+                       [
+                               function( $dummy, $title, $options, &$html, &$attribs, &$ret ) {
+                                       $html = 'foobar';
+                               },
+                               '<a href="/wiki/Special:BlankPage" title="Special:BlankPage">foobar</a>'
+                       ],
+                       // Modify $attribs
+                       [
+                               function( $dummy, $title, $options, &$html, &$attribs, &$ret ) {
+                                       $attribs['bar'] = 'baz';
+                               },
+                               '<a href="/wiki/Special:BlankPage" title="Special:BlankPage" bar="baz">Special:BlankPage</a>'
+                       ],
+                       // Fully override return value and abort hook
+                       [
+                               function( $dummy, $title, $options, &$html, &$attribs, &$ret ) {
+                                       $ret = 'blahblahblah';
+                                       return false;
+                               },
+                               'blahblahblah'
+                       ],
+
+               ];
+       }
+
+       /**
+        * @dataProvider provideLinkEndHook
+        */
+       public function testLinkEndHook( $callback, $expected ) {
+               $this->setMwGlobals( [
+                       'wgArticlePath' => '/wiki/$1',
+                       'wgWellFormedXml' => true,
+               ] );
+
+               $this->setMwGlobals( 'wgHooks', [ 'LinkEnd' => [ $callback ] ] );
+
+               $title = SpecialPage::getTitleFor( 'Blankpage' );
+               $out = Linker::link( $title );
+               $this->assertEquals( $expected, $out );
+       }
 }
index 4f917a5..467a2ad 100644 (file)
@@ -200,6 +200,10 @@ class MediaWikiServicesTest extends PHPUnit_Framework_TestCase {
 
                // All getters should be named just like the service, with "get" added.
                foreach ( $getServiceCases as $name => $case ) {
+                       if ( $name[0] === '_' ) {
+                               // Internal service, no getter
+                               continue;
+                       }
                        list( $service, $class ) = $case;
                        $getterCases[$name] = [
                                'get' . $service,
@@ -239,6 +243,9 @@ class MediaWikiServicesTest extends PHPUnit_Framework_TestCase {
                        'DBLoadBalancer' => [ 'DBLoadBalancer', 'LoadBalancer' ],
                        'WatchedItemStore' => [ 'WatchedItemStore', WatchedItemStore::class ],
                        'GenderCache' => [ 'GenderCache', GenderCache::class ],
+                       '_MediaWikiTitleCodec' => [ '_MediaWikiTitleCodec', MediaWikiTitleCodec::class ],
+                       'TitleFormatter' => [ 'TitleFormatter', TitleFormatter::class ],
+                       'TitleParser' => [ 'TitleParser', TitleParser::class ],
                ];
        }
 
index 7d025d2..7850f24 100644 (file)
@@ -144,6 +144,13 @@ class TitleTest extends MediaWikiTestCase {
                                ]
                        ]
                ] );
+
+               // Reset TitleParser since we modified $wgLocalInterwikis
+               $this->setService( 'TitleParser', new MediaWikiTitleCodec(
+                               Language::factory( 'en' ),
+                               new GenderCache(),
+                               [ 'localtestiw' ]
+               ) );
        }
 
        /**
@@ -702,4 +709,42 @@ class TitleTest extends MediaWikiTestCase {
                $this->assertEquals( $title->getInterwiki(), $fragmentTitle->getInterwiki() );
                $this->assertEquals( $fragment, $fragmentTitle->getFragment() );
        }
+
+       public function provideGetPrefixedText() {
+               return [
+                       // ns = 0
+                       [
+                               Title::makeTitle( NS_MAIN, 'Foobar' ),
+                               'Foobar'
+                       ],
+                       // ns = 2
+                       [
+                               Title::makeTitle( NS_USER, 'Foobar' ),
+                               'User:Foobar'
+                       ],
+                       // fragment not included
+                       [
+                               Title::makeTitle( NS_MAIN, 'Foobar', 'fragment' ),
+                               'Foobar'
+                       ],
+                       // ns = -2
+                       [
+                               Title::makeTitle( NS_MEDIA, 'Foobar' ),
+                               'Media:Foobar'
+                       ],
+                       // non-existent namespace
+                       [
+                               Title::makeTitle( 100000, 'Foobar' ),
+                               ':Foobar'
+                       ],
+               ];
+       }
+
+       /**
+        * @covers Title::getPrefixedText
+        * @dataProvider provideGetPrefixedText
+        */
+       public function testGetPrefixedText( Title $title, $expected ) {
+               $this->assertEquals( $expected, $title->getPrefixedText() );
+       }
 }
index e536205..be22260 100644 (file)
@@ -13,6 +13,14 @@ class WatchedItemIntegrationTest extends MediaWikiTestCase {
                parent::setUp();
                self::$users['WatchedItemIntegrationTestUser']
                        = new TestUser( 'WatchedItemIntegrationTestUser' );
+
+               $this->hideDeprecated( 'WatchedItem::fromUserTitle' );
+               $this->hideDeprecated( 'WatchedItem::addWatch' );
+               $this->hideDeprecated( 'WatchedItem::removeWatch' );
+               $this->hideDeprecated( 'WatchedItem::isWatched' );
+               $this->hideDeprecated( 'WatchedItem::resetNotificationTimestamp' );
+               $this->hideDeprecated( 'WatchedItem::duplicateEntries' );
+               $this->hideDeprecated( 'WatchedItem::batchAddWatch' );
        }
 
        private function getUser() {
@@ -20,6 +28,7 @@ class WatchedItemIntegrationTest extends MediaWikiTestCase {
        }
 
        public function testWatchAndUnWatchItem() {
+
                $user = $this->getUser();
                $title = Title::newFromText( 'WatchedItemIntegrationTestPage' );
                // Cleanup after previous tests
index 22bb237..4c973e5 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 /**
  * Although marked as a stub, can work independently.
  *
@@ -179,6 +178,7 @@ class NewParserTest extends MediaWikiTestCase {
 
                MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
                $wgContLang->resetNamespaces(); # reset namespace cache
+               ParserTest::resetTitleServices();
        }
 
        protected function tearDown() {
index e321bdb..40065f5 100644 (file)
@@ -179,6 +179,36 @@ class MediaWikiTitleCodecTest extends MediaWikiTestCase {
                $this->assertEquals( $expected, $actual );
        }
 
+       public static function provideGetPrefixedDBkey() {
+               return [
+                       [ NS_MAIN, 'Foo_Bar', '', '', 'en', 'Foo_Bar' ],
+                       [ NS_USER, 'Hansi_Maier', 'stuff_and_so_on', '', 'en', 'User:Hansi_Maier' ],
+
+                       // No capitalization or normalization is applied while formatting!
+                       [ NS_USER_TALK, 'hansi__maier', '', '', 'en', 'User_talk:hansi__maier' ],
+
+                       // getGenderCache() provides a mock that considers first
+                       // names ending in "a" to be female.
+                       [ NS_USER, 'Lisa_Müller', '', '', 'de', 'Benutzerin:Lisa_Müller' ],
+
+                       [ NS_MAIN, 'Remote_page', '', 'remotetestiw', 'en', 'remotetestiw:Remote_page' ]
+               ];
+       }
+
+       /**
+        * @dataProvider provideGetPrefixedDBkey
+        */
+       public function testGetPrefixedDBkey( $namespace, $dbkey, $fragment,
+               $interwiki, $lang, $expected
+       ) {
+               $codec = $this->makeCodec( $lang );
+               $title = new TitleValue( $namespace, $dbkey, $fragment, $interwiki );
+
+               $actual = $codec->getPrefixedDBkey( $title );
+
+               $this->assertEquals( $expected, $actual );
+       }
+
        public static function provideGetFullText() {
                return [
                        [ NS_MAIN, 'Foo_Bar', '', 'en', 'Foo Bar' ],
index 7922553..4dbda74 100644 (file)
@@ -42,6 +42,7 @@ class TitleValueTest extends MediaWikiTestCase {
                $title = new TitleValue( $ns, $text, $fragment, $interwiki );
 
                $this->assertEquals( $ns, $title->getNamespace() );
+               $this->assertTrue( $title->inNamespace( $ns ) );
                $this->assertEquals( $text, $title->getText() );
                $this->assertEquals( $fragment, $title->getFragment() );
                $this->assertEquals( $hasFragment, $title->hasFragment() );