Merge "Avoid arithmetics on localized number string ("0,04") in SpecialWatchlist"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 9 Jul 2018 23:53:13 +0000 (23:53 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 9 Jul 2018 23:53:13 +0000 (23:53 +0000)
338 files changed:
.eslintrc.json
.phpcs.xml
.travis.yml
RELEASE-NOTES-1.32
includes/Block.php
includes/Category.php
includes/CommentStore.php
includes/DefaultSettings.php
includes/Defines.php
includes/EditPage.php
includes/FileDeleteForm.php
includes/FormOptions.php
includes/GlobalFunctions.php
includes/Html.php
includes/Linker.php
includes/MWNamespace.php
includes/MWTimestamp.php
includes/MagicWord.php
includes/MediaWiki.php
includes/MediaWikiServices.php
includes/Message.php
includes/OutputPage.php
includes/Pingback.php
includes/ProtectionForm.php
includes/Revision.php
includes/ServiceWiring.php
includes/SiteConfiguration.php
includes/Status.php
includes/Storage/DerivedPageDataUpdater.php
includes/Storage/NameTableStore.php
includes/Storage/PageUpdater.php
includes/Storage/RevisionFactory.php
includes/Storage/RevisionLookup.php
includes/Storage/RevisionStore.php
includes/Storage/RevisionStoreFactory.php [deleted file]
includes/Storage/SqlBlobStore.php
includes/StubObject.php
includes/TemplateParser.php
includes/Title.php
includes/WebRequest.php
includes/WikiMap.php
includes/Xml.php
includes/api/ApiBase.php
includes/api/ApiDelete.php
includes/api/ApiMain.php
includes/api/ApiModuleManager.php
includes/api/ApiPageSet.php
includes/api/ApiQueryAllDeletedRevisions.php
includes/api/ApiQueryAllRevisions.php
includes/api/ApiQueryBacklinksprop.php
includes/api/ApiQueryBase.php
includes/api/ApiQueryIWBacklinks.php
includes/api/ApiQueryImageInfo.php
includes/api/ApiQueryLangBacklinks.php
includes/api/ApiQueryQueryPage.php
includes/api/ApiQueryRecentChanges.php
includes/api/ApiQueryRevisionsBase.php
includes/api/ApiResult.php
includes/api/i18n/mk.json
includes/api/i18n/nl.json
includes/api/i18n/uk.json
includes/auth/AbstractPasswordPrimaryAuthenticationProvider.php
includes/auth/AuthManager.php
includes/auth/AuthenticationResponse.php
includes/auth/Throttler.php
includes/cache/LinkCache.php
includes/cache/MessageBlobStore.php
includes/cache/MessageCache.php
includes/changes/CategoryMembershipChange.php
includes/changes/ChangesList.php
includes/changes/EnhancedChangesList.php
includes/changes/OldChangesList.php
includes/changes/RecentChange.php
includes/changetags/ChangeTags.php
includes/content/AbstractContent.php
includes/content/CodeContentHandler.php
includes/content/Content.php
includes/content/ContentHandler.php
includes/content/MessageContent.php
includes/content/TextContent.php
includes/content/TextContentHandler.php
includes/db/CloneDatabase.php
includes/db/PatchFileLocation.php
includes/debug/logger/monolog/LineFormatter.php
includes/deferred/SearchUpdate.php
includes/diff/DairikiDiff.php
includes/diff/DifferenceEngine.php
includes/exception/LocalizedException.php
includes/exception/MWExceptionHandler.php
includes/export/DumpPipeOutput.php
includes/externalstore/ExternalStoreDB.php
includes/filerepo/file/ArchivedFile.php
includes/filerepo/file/LocalFile.php
includes/filerepo/file/OldLocalFile.php
includes/gallery/TraditionalImageGallery.php
includes/htmlform/HTMLForm.php
includes/htmlform/fields/HTMLUsersMultiselectField.php
includes/http/MWHttpRequest.php
includes/installer/CliInstaller.php
includes/installer/DatabaseUpdater.php
includes/installer/Installer.php
includes/installer/WebInstaller.php
includes/installer/WebInstallerOptions.php
includes/installer/WebInstallerPage.php
includes/installer/i18n/uk.json
includes/interwiki/Interwiki.php
includes/jobqueue/JobRunner.php
includes/jobqueue/JobSpecification.php
includes/libs/Cookie.php
includes/libs/MapCacheLRU.php
includes/libs/ProcessCacheLRU.php
includes/libs/StatusValue.php
includes/libs/Timing.php
includes/libs/filebackend/FileBackend.php
includes/libs/filebackend/FileBackendStore.php
includes/libs/filebackend/SwiftFileBackend.php
includes/libs/filebackend/filejournal/FileJournal.php
includes/libs/lockmanager/ScopedLock.php
includes/libs/mime/MimeAnalyzer.php
includes/libs/mime/XmlTypeCheck.php
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/RESTBagOStuff.php
includes/libs/objectcache/WANObjectCache.php
includes/libs/rdbms/database/DBConnRef.php
includes/libs/rdbms/database/Database.php
includes/libs/rdbms/database/DatabaseMysqlBase.php
includes/libs/rdbms/database/DatabaseMysqli.php
includes/libs/rdbms/database/DatabaseSqlite.php
includes/libs/rdbms/database/IDatabase.php
includes/libs/rdbms/database/IMaintainableDatabase.php
includes/libs/rdbms/exception/DBConnectionError.php
includes/libs/rdbms/exception/DBError.php
includes/libs/rdbms/exception/DBQueryError.php
includes/libs/rdbms/lbfactory/ILBFactory.php
includes/libs/rdbms/lbfactory/LBFactory.php
includes/libs/rdbms/loadbalancer/ILoadBalancer.php
includes/libs/rdbms/loadbalancer/LoadBalancer.php
includes/libs/redis/RedisConnRef.php
includes/libs/redis/RedisConnectionPool.php
includes/logging/LogEntry.php
includes/logging/LogEventsList.php
includes/logging/LogFormatter.php
includes/logging/PatrolLog.php
includes/mail/MailAddress.php
includes/media/BmpHandler.php
includes/media/MediaHandler.php
includes/media/MediaTransformOutput.php
includes/media/TiffHandler.php
includes/media/XCF.php
includes/page/Article.php
includes/page/PageArchive.php
includes/page/WikiPage.php
includes/pager/IndexPager.php
includes/parser/CoreParserFunctions.php
includes/parser/Parser.php
includes/parser/ParserOptions.php
includes/parser/ParserOutput.php
includes/parser/Sanitizer.php
includes/poolcounter/PoolWorkArticleView.php
includes/preferences/DefaultPreferencesFactory.php
includes/profiler/Profiler.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderClientHtml.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderImageModule.php
includes/resourceloader/ResourceLoaderLanguageDataModule.php
includes/resourceloader/ResourceLoaderLanguageNamesModule.php
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderSpecialCharacterDataModule.php
includes/resourceloader/ResourceLoaderUserOptionsModule.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/search/SearchDatabase.php
includes/search/SearchEngine.php
includes/search/SearchEngineFactory.php
includes/search/SearchHighlighter.php
includes/search/SearchMssql.php
includes/search/SearchResult.php
includes/session/CookieSessionProvider.php
includes/session/Session.php
includes/session/SessionBackend.php
includes/session/SessionProviderInterface.php
includes/shell/Shell.php
includes/site/SiteSQLStore.php
includes/skins/BaseTemplate.php
includes/skins/QuickTemplate.php
includes/skins/Skin.php
includes/skins/SkinTemplate.php
includes/specialpage/AuthManagerSpecialPage.php
includes/specialpage/LoginSignupSpecialPage.php
includes/specialpage/SpecialPage.php
includes/specialpage/SpecialPageFactory.php
includes/specials/SpecialBlock.php
includes/specials/SpecialChangeEmail.php
includes/specials/SpecialEmailuser.php
includes/specials/SpecialPrefixindex.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUpload.php
includes/specials/forms/PreferencesFormLegacy.php
includes/specials/forms/PreferencesFormOOUI.php
includes/specials/pagers/ActiveUsersPager.php
includes/specials/pagers/DeletedContribsPager.php
includes/specials/pagers/UsersPager.php
includes/tidy/RaggettBase.php
includes/tidy/RaggettExternal.php
includes/tidy/RaggettInternalHHVM.php
includes/tidy/RaggettInternalPHP.php
includes/title/MalformedTitleException.php
includes/upload/UploadBase.php
includes/upload/UploadStash.php
includes/user/PasswordReset.php
includes/user/User.php
includes/user/UserGroupMembership.php
includes/utils/MWRestrictions.php
includes/utils/ZipDirectoryReader.php
includes/watcheditem/WatchedItemStoreInterface.php
includes/widget/UsersMultiselectWidget.php
languages/ConverterRule.php
languages/Language.php
languages/LanguageConverter.php
languages/classes/LanguageFi.php
languages/i18n/ace.json
languages/i18n/ary.json
languages/i18n/ast.json
languages/i18n/bcc.json
languages/i18n/be-tarask.json
languages/i18n/bg.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/ckb.json
languages/i18n/dsb.json
languages/i18n/et.json
languages/i18n/fa.json
languages/i18n/frr.json
languages/i18n/fy.json
languages/i18n/he.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/ia.json
languages/i18n/ilo.json
languages/i18n/inh.json
languages/i18n/io.json
languages/i18n/is.json
languages/i18n/ja.json
languages/i18n/lki.json
languages/i18n/mg.json
languages/i18n/mni.json
languages/i18n/my.json
languages/i18n/nb.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/pl.json
languages/i18n/ps.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/ru.json
languages/i18n/rue.json
languages/i18n/sat.json
languages/i18n/sd.json
languages/i18n/sk.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/szl.json
languages/i18n/te.json
languages/i18n/tr.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
maintenance/Maintenance.php
maintenance/backup.inc
maintenance/categoryChangesAsRdf.php
maintenance/dumpTextPass.php
maintenance/populateChangeTagDef.php
maintenance/populateContentTables.php
maintenance/storage/recompressTracked.php
package.json
resources/Resources.php
resources/src/jquery.tablesorter/jquery.tablesorter.js
resources/src/mediawiki.Title/Title.js
resources/src/mediawiki.Upload.BookletLayout/BookletLayout.css
resources/src/mediawiki.Uri/Uri.js
resources/src/mediawiki.api/upload.js
resources/src/mediawiki.debug/debug.js
resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js
resources/src/mediawiki.language/mediawiki.language.numbers.js
resources/src/mediawiki.page.watch.ajax.js
resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ItemModel.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.FilterTagMultiselectWidget.js
resources/src/mediawiki.rcfilters/ui/mw.rcfilters.ui.MenuSelectWidget.js
resources/src/mediawiki.special.apisandbox/apisandbox.js
resources/src/mediawiki.special.preferences.styles.ooui.css [deleted file]
resources/src/mediawiki.special.preferences.styles.ooui.less [new file with mode: 0644]
resources/src/mediawiki.special.upload/upload.js
resources/src/mediawiki.visibleTimeout.js
resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js
tests/common/TestsAutoLoader.php
tests/integration/includes/shell/FirejailCommandTest.php
tests/parser/ParserTestRunner.php
tests/parser/parserTests.txt
tests/phan/config.php
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/includes/ActorMigrationTest.php
tests/phpunit/includes/CommentStoreTest.php
tests/phpunit/includes/MediaWikiServicesTest.php
tests/phpunit/includes/PageArchiveTest.php
tests/phpunit/includes/RevisionDbTestBase.php
tests/phpunit/includes/RevisionMcrReadNewDbTest.php [new file with mode: 0644]
tests/phpunit/includes/RevisionTest.php
tests/phpunit/includes/Storage/McrReadNewRevisionStoreDbTest.php [new file with mode: 0644]
tests/phpunit/includes/Storage/McrReadNewSchemaOverride.php [new file with mode: 0644]
tests/phpunit/includes/Storage/McrRevisionStoreDbTest.php
tests/phpunit/includes/Storage/McrWriteBothRevisionStoreDbTest.php
tests/phpunit/includes/Storage/McrWriteBothSchemaOverride.php
tests/phpunit/includes/Storage/NoContentModelRevisionStoreDbTest.php
tests/phpunit/includes/Storage/PreMcrRevisionStoreDbTest.php
tests/phpunit/includes/Storage/RevisionStoreFactoryTest.php [deleted file]
tests/phpunit/includes/Storage/RevisionStoreTest.php
tests/phpunit/includes/TestLogger.php
tests/phpunit/includes/api/RandomImageGenerator.php
tests/phpunit/includes/api/query/ApiQueryTestBase.php
tests/phpunit/includes/db/LBFactoryTest.php
tests/phpunit/includes/db/LoadBalancerTest.php
tests/phpunit/includes/htmlform/HTMLFormTest.php
tests/phpunit/includes/libs/rdbms/database/DBConnRefTest.php
tests/phpunit/includes/libs/rdbms/database/DatabaseTest.php
tests/phpunit/includes/media/MediaWikiMediaTestCase.php
tests/phpunit/includes/page/WikiPageDbTestBase.php
tests/phpunit/includes/page/WikiPageMcrReadNewDbTest.php [new file with mode: 0644]
tests/phpunit/includes/resourceloader/ResourceLoaderStartUpModuleTest.php
tests/phpunit/includes/session/TestUtils.php
tests/phpunit/mocks/content/DummyContentForTesting.php
tests/phpunit/mocks/content/DummyContentHandlerForTesting.php
tests/phpunit/mocks/content/DummyNonTextContent.php
tests/phpunit/mocks/content/DummyNonTextContentHandler.php
tests/phpunit/mocks/content/DummySerializeErrorContentHandler.php
tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js
thumb.php

index 64b5ea7..844895d 100644 (file)
@@ -11,6 +11,7 @@
                "OO": false
        },
        "rules": {
-               "dot-notation": 0
+               "dot-notation": 0,
+               "max-len": 0
        }
 }
index e15eca0..8d33f69 100644 (file)
@@ -7,7 +7,6 @@
                <exclude name="MediaWiki.Commenting.FunctionComment.MissingParamTag" />
                <exclude name="MediaWiki.Commenting.FunctionComment.MissingReturn" />
                <exclude name="MediaWiki.Commenting.FunctionComment.ExtraParamComment" />
-               <exclude name="MediaWiki.Commenting.FunctionComment.DefaultNullTypeParam" />
                <exclude name="MediaWiki.Commenting.FunctionComment.WrongStyle" />
                <exclude name="MediaWiki.Commenting.PhpunitAnnotations.NotClassTrait" />
                <exclude name="MediaWiki.Commenting.IllegalSingleLineComment.IllegalSingleLineCommentStart" />
index e15fc55..2fc6d64 100644 (file)
@@ -38,8 +38,11 @@ matrix:
     - env: dbtype=mysql dbuser=root
       php: hhvm-3.18
   allow_failures:
-    - php: hhvm-3.24
+    - php: 7.2
+    - env: dbtype=postgres dbuser=travis
+    - php: hhvm-3.18
     - php: hhvm-3.21
+    - php: hhvm-3.24
 
 services:
   - mysql
@@ -80,7 +83,8 @@ notifications:
   email: false
   irc:
     channels:
-      - "chat.freenode.net#mediawiki-feed"
+      - "chat.freenode.net#wikimedia-dev"
+    template:
+      - "%{repository}#%{build_number} (%{branch} - %{commit} %{author}): %{message} - %{build_url}"
     on_success: change
-    on_failure: change
-    skip_join: true
+    on_failure: always
index d0d5eb2..c41e5b5 100644 (file)
@@ -10,6 +10,7 @@ production.
   have been removed.
 * The $wgUseAjax setting, deprecated in 1.31, is now ignored.
 * The $wgSiteSupportPage setting, unused since 1.5, was removed.
+* The $wgBrowserBlacklist setting, deprecated in 1.30, was removed.
 * The default quality of JPEG thumbnails generated by GD was reduced from 95 to
   80. The quality of JPEG thumbnails is now configurable through the new setting
   $wgJpegQuality (default 80). This aligns the quality to what ImageMagick uses.
@@ -171,6 +172,9 @@ because of Phabricator reports.
   use 'EditPageGetCheckboxesDefinition' instead.
 * Linker::getLinkColour() and DummyLinker::getLinkColour(), deprecated since
   1.28, were removed. LinkRenderer::getLinkClasses() should be used instead.
+* Wikimedia\Rdbms\LoadBalancer::getLaggedSlaveMode(), deprecated in 1.28, has
+  been removed. Use Wikimedia\Rdbms\LoadBalancer::getLaggedReplicaMode()
+  instead.
 * mw.widgets.CategoryMultiselectWidget now uses TagMultiselectWidget instead of
   CapsuleMultiselectWidget. The following methods may no longer be used:
   * setItemsFromData: Use setValue instead
@@ -227,6 +231,7 @@ because of Phabricator reports.
 * The 'jquery.hidpi' module (polyfill for IMG srcset) is deprecated.
 * ResourceLoaderStartUpModule::getStartupModules() and ::getLegacyModules()
   are deprecated. These concepts are obsolete and have no replacement.
+* String type for $lang of DifferenceEngine::setTextLanguage is deprecated.
 
 === Other changes in 1.32 ===
 * …
index 9567b06..1d2e341 100644 (file)
@@ -224,7 +224,7 @@ class Block {
                        'ipb_address',
                        'ipb_by',
                        'ipb_by_text',
-                       'ipb_by_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'ipb_by_actor' : null,
+                       'ipb_by_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'ipb_by_actor' : 'NULL',
                        'ipb_timestamp',
                        'ipb_auto',
                        'ipb_anon_only',
@@ -299,7 +299,7 @@ class Block {
         *     1) A block directly on the given user or IP
         *     2) A rangeblock encompassing the given IP (smallest first)
         *     3) An autoblock on the given IP
-        * @param User|string $vagueTarget Also search for blocks affecting this target.  Doesn't
+        * @param User|string|null $vagueTarget Also search for blocks affecting this target.  Doesn't
         *     make any sense to use TYPE_AUTO / TYPE_ID here. Leave blank to skip IP lookups.
         * @throws MWException
         * @return bool Whether a relevant block was found
@@ -405,7 +405,7 @@ class Block {
        /**
         * Get a set of SQL conditions which will select rangeblocks encompassing a given range
         * @param string $start Hexadecimal IP representation
-        * @param string $end Hexadecimal IP representation, or null to use $start = $end
+        * @param string|null $end Hexadecimal IP representation, or null to use $start = $end
         * @return string
         */
        public static function getRangeCond( $start, $end = null ) {
@@ -519,7 +519,7 @@ class Block {
         * Insert a block into the block table. Will fail if there is a conflicting
         * block (same name and options) already in the database.
         *
-        * @param IDatabase $dbw If you have one available
+        * @param IDatabase|null $dbw If you have one available
         * @return bool|array False on failure, assoc array on success:
         *      ('id' => block ID, 'autoIds' => array of autoblock IDs)
         */
@@ -1162,7 +1162,7 @@ class Block {
         *     Calling this with a user, IP address or range will not select autoblocks, and will
         *     only select a block where the targets match exactly (so looking for blocks on
         *     1.2.3.4 will not select 1.2.0.0/16 or even 1.2.3.4/32)
-        * @param string|User|int $vagueTarget As above, but we will search for *any* block which
+        * @param string|User|int|null $vagueTarget As above, but we will search for *any* block which
         *     affects that target (so for an IP address, get ranges containing that IP; and also
         *     get any relevant autoblocks). Leave empty or blank to skip IP-based lookups.
         * @param bool $fromMaster Whether to use the DB_MASTER database
index 41ecc65..fe3dd0d 100644 (file)
@@ -171,7 +171,7 @@ class Category {
         *   fields are null, the resulting Category object will represent an empty
         *   category if a title object was given. If the fields are null and no
         *   title was given, this method fails and returns false.
-        * @param Title $title Optional title object for the category represented by
+        * @param Title|null $title Optional title object for the category represented by
         *   the given row. May be provided if it is already known, to avoid having
         *   to re-create a title object later.
         * @return Category|false
index 6b94d58..484b846 100644 (file)
@@ -158,7 +158,7 @@ class CommentStore {
         *
         * @since 1.30
         * @since 1.31 Method signature changed, $key parameter added (with deprecated back compat)
-        * @param string $key A key such as "rev_comment" identifying the comment
+        * @param string|null $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
         * @return string[] to include in the `$vars` to `IDatabase->select()`. All
         *  fields are aliased, so `+` is safe to use.
@@ -191,7 +191,7 @@ class CommentStore {
         *
         * @since 1.30
         * @since 1.31 Method signature changed, $key parameter added (with deprecated back compat)
-        * @param string $key A key such as "rev_comment" identifying the comment
+        * @param string|null $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
         * @return array With three keys:
         *   - tables: (string[]) to include in the `$table` to `IDatabase->select()`
@@ -381,7 +381,7 @@ class CommentStore {
         * @since 1.31 Method signature changed, $key parameter added (with deprecated back compat)
         * @param string $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
-        * @param object|array $row Result row.
+        * @param object|array|null $row Result row.
         * @param bool $fallback If true, fall back as well as possible instead of throwing an exception.
         * @return CommentStoreComment
         */
@@ -415,7 +415,7 @@ class CommentStore {
         * @param IDatabase $db Database handle to use for lookup
         * @param string $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
-        * @param object|array $row Result row.
+        * @param object|array|null $row Result row.
         * @param bool $fallback If true, fall back as well as possible instead of throwing an exception.
         * @return CommentStoreComment
         */
@@ -458,16 +458,7 @@ class CommentStore {
                $comment = CommentStoreComment::newUnsavedComment( $comment, $data );
 
                # Truncate comment in a Unicode-sensitive manner
-               $comment->text = $this->lang->truncate( $comment->text, self::MAX_COMMENT_LENGTH );
-               if ( mb_strlen( $comment->text, 'UTF-8' ) > self::COMMENT_CHARACTER_LIMIT ) {
-                       $ellipsis = wfMessage( 'ellipsis' )->inLanguage( $this->lang )->escaped();
-                       if ( mb_strlen( $ellipsis ) >= self::COMMENT_CHARACTER_LIMIT ) {
-                               // WTF?
-                               $ellipsis = '...';
-                       }
-                       $maxLength = self::COMMENT_CHARACTER_LIMIT - mb_strlen( $ellipsis, 'UTF-8' );
-                       $comment->text = mb_substr( $comment->text, 0, $maxLength, 'UTF-8' ) . $ellipsis;
-               }
+               $comment->text = $this->lang->truncateForVisual( $comment->text, self::COMMENT_CHARACTER_LIMIT );
 
                if ( $this->stage > MIGRATION_OLD && !$comment->id ) {
                        $dbData = $comment->data;
@@ -530,7 +521,7 @@ class CommentStore {
                $comment = $this->createComment( $dbw, $comment, $data );
 
                if ( $this->stage <= MIGRATION_WRITE_BOTH ) {
-                       $fields[$key] = $this->lang->truncate( $comment->text, 255 );
+                       $fields[$key] = $this->lang->truncateForDatabase( $comment->text, 255 );
                }
 
                if ( $this->stage >= MIGRATION_WRITE_BOTH ) {
@@ -567,7 +558,7 @@ class CommentStore {
         * @param IDatabase $dbw Database handle to insert on
         * @param string $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
-        * @param string|Message|CommentStoreComment $comment As for `self::createComment()`
+        * @param string|Message|CommentStoreComment|null $comment As for `self::createComment()`
         * @param array|null $data As for `self::createComment()`
         * @return array Fields for the insert or update
         */
@@ -606,7 +597,7 @@ class CommentStore {
         * @param IDatabase $dbw Database handle to insert on
         * @param string $key A key such as "rev_comment" identifying the comment
         *  field being fetched.
-        * @param string|Message|CommentStoreComment $comment As for `self::createComment()`
+        * @param string|Message|CommentStoreComment|null $comment As for `self::createComment()`
         * @param array|null $data As for `self::createComment()`
         * @return array Two values:
         *  - array Fields for the insert or update
index 3771df1..f3bc9cc 100644 (file)
@@ -3003,11 +3003,6 @@ $wgAllUnicodeFixes = false;
  */
 $wgLegacyEncoding = false;
 
-/**
- * @deprecated since 1.30, does nothing
- */
-$wgBrowserBlackList = [];
-
 /**
  * If set to true, the MediaWiki 1.4 to 1.5 schema conversion will
  * create stub reference rows in the text table instead of copying
@@ -7892,7 +7887,7 @@ $wgNewUserLog = true;
  * Maintain a log of page creations at Special:Log/create?
  * @since 1.32
  */
-$wgPageCreationLog = false;
+$wgPageCreationLog = true;
 
 /** @} */ # end logging }
 
@@ -8897,13 +8892,23 @@ $wgInterwikiPrefixDisplayTypes = [];
 $wgCommentTableSchemaMigrationStage = MIGRATION_OLD;
 
 /**
- * RevisionStore table schema migration stage (content, slots, content_models & slot_roles tables)
+ * RevisionStore table schema migration stage (content, slots, content_models & slot_roles tables).
+ * Use the SCHEMA_COMPAT_XXX flags. Supported values:
+ *
+ * - SCHEMA_COMPAT_OLD
+ * - SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
+ * - SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW
+ * - SCHEMA_COMPAT_OLD
+ *
+ * Note that reading the old and new schema at the same time is not supported.
+ * Attempting to set both read bits in $wgMultiContentRevisionSchemaMigrationStage
+ * will result in an InvalidArgumentException.
  *
  * @see Task: https://phabricator.wikimedia.org/T174028
  * @see Commit: https://gerrit.wikimedia.org/r/#/c/378724/
  *
  * @since 1.32
- * @var int One of the MIGRATION_* constants
+ * @var int An appropriate combination of SCHEMA_COMPAT_XXX flags.
  */
 $wgMultiContentRevisionSchemaMigrationStage = MIGRATION_OLD;
 
index e5261e7..72cddd2 100644 (file)
@@ -270,11 +270,34 @@ define( 'CONTENT_FORMAT_XML', 'application/xml' );
 define( 'SHELL_MAX_ARG_STRLEN', '100000' );
 /**@}*/
 
+/**@{
+ * Schema compatibility flags.
+ *
+ * Used as flags in a bit field that indicates whether the old or new schema (or both)
+ * are read or written.
+ *
+ * - SCHEMA_COMPAT_WRITE_OLD: Whether information is written to the old schema.
+ * - SCHEMA_COMPAT_READ_OLD: Whether information stored in the old schema is read.
+ * - SCHEMA_COMPAT_WRITE_NEW: Whether information is written to the new schema.
+ * - SCHEMA_COMPAT_READ_NEW: Whether information stored in the new schema is read.
+ */
+define( 'SCHEMA_COMPAT_WRITE_OLD', 0x01 );
+define( 'SCHEMA_COMPAT_READ_OLD', 0x02 );
+define( 'SCHEMA_COMPAT_WRITE_NEW', 0x10 );
+define( 'SCHEMA_COMPAT_READ_NEW', 0x20 );
+define( 'SCHEMA_COMPAT_WRITE_BOTH', SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_WRITE_NEW );
+define( 'SCHEMA_COMPAT_READ_BOTH', SCHEMA_COMPAT_READ_OLD | SCHEMA_COMPAT_READ_NEW );
+define( 'SCHEMA_COMPAT_OLD', SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_OLD );
+define( 'SCHEMA_COMPAT_NEW', SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_NEW );
+/**@}*/
+
 /**@{
  * Schema change migration flags.
  *
  * Used as values of a feature flag for an orderly transition from an old
- * schema to a new schema.
+ * schema to a new schema. The numeric values of these constants are compatible with the
+ * SCHEMA_COMPAT_XXX bitfield semantics. High bits are used to ensure that the numeric
+ * ordering follows the order in which the migration stages should be used.
  *
  * - MIGRATION_OLD: Only read and write the old schema. The new schema need not
  *   even exist. This is used from when the patch is merged until the schema
@@ -289,8 +312,8 @@ define( 'SHELL_MAX_ARG_STRLEN', '100000' );
  * - MIGRATION_NEW: Only read and write the new schema. The old schema (and the
  *   feature flag) may now be removed.
  */
-define( 'MIGRATION_OLD', 0 );
-define( 'MIGRATION_WRITE_BOTH', 1 );
-define( 'MIGRATION_WRITE_NEW', 2 );
-define( 'MIGRATION_NEW', 3 );
+define( 'MIGRATION_OLD', 0x00000000 | SCHEMA_COMPAT_OLD );
+define( 'MIGRATION_WRITE_BOTH', 0x10000000 | SCHEMA_COMPAT_READ_BOTH | SCHEMA_COMPAT_WRITE_BOTH );
+define( 'MIGRATION_WRITE_NEW', 0x20000000 | SCHEMA_COMPAT_READ_BOTH | SCHEMA_COMPAT_WRITE_NEW );
+define( 'MIGRATION_NEW', 0x30000000 | SCHEMA_COMPAT_NEW );
 /**@}*/
index 9a8a4a6..dfb2b43 100644 (file)
@@ -948,12 +948,7 @@ class EditPage {
                        } else {
                                // If we receive the last parameter of the request, we can fairly
                                // claim the POST request has not been truncated.
-
-                               // TODO: softened the check for cutover.  Once we determine
-                               // that it is safe, we should complete the transition by
-                               // removing the "edittime" clause.
-                               $this->incompleteForm = ( !$request->getVal( 'wpUltimateParam' )
-                                       && is_null( $this->edittime ) );
+                               $this->incompleteForm = !$request->getVal( 'wpUltimateParam' );
                        }
                        if ( $this->incompleteForm ) {
                                # If the form is incomplete, force to preview.
@@ -3198,8 +3193,8 @@ ERROR;
         * Builds a standard summary input with a label.
         *
         * @param string $summary The value of the summary input
-        * @param string $labelText The html to place inside the label
-        * @param array $inputAttrs Array of attrs to use on the input
+        * @param string|null $labelText The html to place inside the label
+        * @param array|null $inputAttrs Array of attrs to use on the input
         *
         * @return OOUI\FieldLayout OOUI FieldLayout with Label and Input
         */
@@ -3332,8 +3327,8 @@ ERROR;
         * The $textoverride method can be used by subclasses overriding showContentForm
         * to pass back to this method.
         *
-        * @param array $customAttribs Array of html attributes to use in the textarea
-        * @param string $textoverride Optional text to override $this->textarea1 with
+        * @param array|null $customAttribs Array of html attributes to use in the textarea
+        * @param string|null $textoverride Optional text to override $this->textarea1 with
         */
        protected function showTextbox1( $customAttribs = null, $textoverride = null ) {
                if ( $this->wasDeletedSinceLastEdit() && $this->formtype == 'save' ) {
@@ -4031,7 +4026,7 @@ ERROR;
         * Shows a bulletin board style toolbar for common editing functions.
         * It can be disabled in the user preferences.
         *
-        * @param Title $title Title object for the page being edited (optional)
+        * @param Title|null $title Title object for the page being edited (optional)
         * @return string
         */
        public static function getEditToolbar( $title = null ) {
index 898005e..ce75037 100644 (file)
@@ -148,7 +148,7 @@ class FileDeleteForm {
         * @param string &$oldimage Archive name
         * @param string $reason Reason of the deletion
         * @param bool $suppress Whether to mark all deleted versions as restricted
-        * @param User $user User object performing the request
+        * @param User|null $user User object performing the request
         * @param array $tags Tags to apply to the deletion action
         * @throws MWException
         * @return Status
index 53c8d3b..53ddac9 100644 (file)
@@ -336,7 +336,7 @@ class FormOptions implements ArrayAccess {
         * available for accessing with getValue() or consumeValue() etc.
         *
         * @param WebRequest $r The request to fetch values from
-        * @param array $optionKeys Which options to fetch the values for (default:
+        * @param array|null $optionKeys Which options to fetch the values for (default:
         *     all of them). Note that passing an empty array will also result in
         *     values for all keys being fetched.
         * @throws MWException If the type of any option is invalid
index d9996f4..543978b 100644 (file)
@@ -31,6 +31,7 @@ use MediaWiki\MediaWikiServices;
 use MediaWiki\Shell\Shell;
 use Wikimedia\ScopedCallback;
 use Wikimedia\Rdbms\DBReplicationWaitError;
+use Wikimedia\WrappedString;
 
 /**
  * Load an extension
@@ -1442,7 +1443,7 @@ function wfHostname() {
  * If $wgShowHostnames is true, the script will also set 'wgHostname' to the
  * hostname of the server handling the request.
  *
- * @param string $nonce Value from OutputPage::getCSPNonce
+ * @param string|null $nonce Value from OutputPage::getCSPNonce
  * @return string|WrappedString HTML
  */
 function wfReportTime( $nonce = null ) {
@@ -1979,7 +1980,7 @@ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
  * For dealing with nullable timestamp columns in the database.
  *
  * @param int $outputtype
- * @param string $ts
+ * @param string|null $ts
  * @return string
  */
 function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
@@ -2057,8 +2058,8 @@ function wfTempDir() {
  * Make directory, and make all parent directories if they don't exist
  *
  * @param string $dir Full path to directory to create
- * @param int $mode Chmod value to use, default is $wgDirectoryMode
- * @param string $caller Optional caller param for debugging.
+ * @param int|null $mode Chmod value to use, default is $wgDirectoryMode
+ * @param string|null $caller Optional caller param for debugging.
  * @throws MWException
  * @return bool
  */
@@ -2316,7 +2317,7 @@ function wfShellWikiCmd( $script, array $parameters = [], array $options = [] )
  * @param string $mine
  * @param string $yours
  * @param string &$result
- * @param string &$mergeAttemptResult
+ * @param string|null &$mergeAttemptResult
  * @return bool
  */
 function wfMerge( $old, $mine, $yours, &$result, &$mergeAttemptResult = null ) {
@@ -3103,6 +3104,7 @@ function wfShorthandToInteger( $string = '', $default = -1 ) {
  * @return string The language code which complying with BCP 47 standards.
  */
 function wfBCP47( $code ) {
+       wfDeprecated( __METHOD__, '1.31' );
        return LanguageCode::bcp47( $code );
 }
 
@@ -3198,7 +3200,7 @@ function wfUnpack( $format, $data, $length = false ) {
  *
  * @param string $name The image name to check
  * @param Title|bool $contextTitle The page on which the image occurs, if known
- * @param string $blacklist Wikitext of a file blacklist
+ * @param string|null $blacklist Wikitext of a file blacklist
  * @return bool
  */
 function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
index 0016426..3dd21c1 100644 (file)
@@ -557,7 +557,7 @@ class Html {
         * literal "</script>" or (for XML) literal "]]>".
         *
         * @param string $contents JavaScript
-        * @param string $nonce Nonce for CSP header, from OutputPage::getCSPNonce()
+        * @param string|null $nonce Nonce for CSP header, from OutputPage::getCSPNonce()
         * @return string Raw HTML
         */
        public static function inlineScript( $contents, $nonce = null ) {
@@ -582,7 +582,7 @@ class Html {
         * "<script src=foo.js></script>".
         *
         * @param string $url
-        * @param string $nonce Nonce for CSP header, from OutputPage::getCSPNonce()
+        * @param string|null $nonce Nonce for CSP header, from OutputPage::getCSPNonce()
         * @return string Raw HTML
         */
        public static function linkedScript( $url, $nonce = null ) {
index 89a6a10..b807ecc 100644 (file)
@@ -904,7 +904,7 @@ class Linker {
         *   red if the user has no edits?
         * @param int $flags Customisation flags (e.g. Linker::TOOL_LINKS_NOBLOCK
         *   and Linker::TOOL_LINKS_EMAIL).
-        * @param int $edits User edit count (optional, for performance)
+        * @param int|null $edits User edit count (optional, for performance)
         * @return string HTML fragment
         */
        public static function userToolLinks(
@@ -966,7 +966,7 @@ class Linker {
         * @since 1.16.3
         * @param int $userId User identifier
         * @param string $userText User name or IP address
-        * @param int $edits User edit count (optional, for performance)
+        * @param int|null $edits User edit count (optional, for performance)
         * @return string
         */
        public static function userToolLinksRedContribs( $userId, $userText, $edits = null ) {
@@ -1678,7 +1678,7 @@ class Linker {
         * @since 1.16.3. $context added in 1.20. $options added in 1.21
         *
         * @param Revision $rev
-        * @param IContextSource $context Context to use or null for the main context.
+        * @param IContextSource|null $context Context to use or null for the main context.
         * @param array $options
         * @return string
         */
index 1df5d51..e03a29b 100644 (file)
@@ -477,7 +477,7 @@ class MWNamespace {
         *
         * @since 1.23
         * @param int $index Index to check
-        * @param User $user User to check
+        * @param User|null $user User to check
         * @return array
         */
        public static function getRestrictionLevels( $index, User $user = null ) {
index 7f3649e..d28169f 100644 (file)
@@ -138,9 +138,9 @@ class MWTimestamp extends ConvertibleTimestamp {
         * Generate a purely relative timestamp, i.e., represent the time elapsed between
         * the given base timestamp and this object.
         *
-        * @param MWTimestamp $relativeTo Relative base timestamp (defaults to now)
-        * @param User $user Use to use offset for
-        * @param Language $lang Language to use
+        * @param MWTimestamp|null $relativeTo Relative base timestamp (defaults to now)
+        * @param User|null $user Use to use offset for
+        * @param Language|null $lang Language to use
         * @param array $chosenIntervals Intervals to use to represent it
         * @return string Relative timestamp
         */
index 17a4a0f..9cef700 100644 (file)
@@ -257,7 +257,7 @@ class MagicWord {
         *
         * Use factory instead: MagicWord::get
         *
-        * @param string $id The internal name of the magic word
+        * @param string|null $id The internal name of the magic word
         * @param string[]|string $syn synonyms for the magic word
         * @param bool $cs If magic word is case sensitive
         */
index bcc3633..b11239a 100644 (file)
@@ -568,7 +568,7 @@ class MediaWiki {
 
        /**
         * @see MediaWiki::preOutputCommit()
-        * @param callable $postCommitWork [default: null]
+        * @param callable|null $postCommitWork [default: null]
         * @since 1.26
         */
        public function doPreOutputCommit( callable $postCommitWork = null ) {
@@ -580,7 +580,7 @@ class MediaWiki {
         * the user can receive a response (in case commit fails)
         *
         * @param IContextSource $context
-        * @param callable $postCommitWork [default: null]
+        * @param callable|null $postCommitWork [default: null]
         * @since 1.27
         */
        public static function preOutputCommit(
index caaa6b3..a756d50 100644 (file)
@@ -21,7 +21,6 @@ use MediaWiki\Storage\NameTableStore;
 use MediaWiki\Storage\RevisionFactory;
 use MediaWiki\Storage\RevisionLookup;
 use MediaWiki\Storage\RevisionStore;
-use MediaWiki\Storage\RevisionStoreFactory;
 use OldRevisionImporter;
 use UploadRevisionImporter;
 use Wikimedia\Rdbms\LBFactory;
@@ -467,6 +466,14 @@ class MediaWikiServices extends ServiceContainer {
                return $this->getService( 'StatsdDataFactory' );
        }
 
+       /**
+        * @since 1.32
+        * @return IBufferingStatsdDataFactory
+        */
+       public function getPerDbNameStatsdDataFactory() {
+               return $this->getService( 'PerDbNameStatsdDataFactory' );
+       }
+
        /**
         * @since 1.27
         * @return EventRelayerGroup
@@ -760,14 +767,6 @@ class MediaWikiServices extends ServiceContainer {
                return $this->getService( 'RevisionStore' );
        }
 
-       /**
-        * @since 1.32
-        * @return RevisionStoreFactory
-        */
-       public function getRevisionStoreFactory() {
-               return $this->getService( 'RevisionStoreFactory' );
-       }
-
        /**
         * @since 1.31
         * @return RevisionLookup
index 84ab7ca..a499035 100644 (file)
@@ -242,7 +242,7 @@ class Message implements MessageSpecifier, Serializable {
         * message keys to try and use the first non-empty message for, or a
         * MessageSpecifier to copy from.
         * @param array $params Message parameters.
-        * @param Language $language [optional] Language to use (defaults to current user language).
+        * @param Language|null $language [optional] Language to use (defaults to current user language).
         * @throws InvalidArgumentException
         */
        public function __construct( $key, $params = [], Language $language = null ) {
index 0b2ba40..3b4b14a 100644 (file)
@@ -24,6 +24,7 @@ use MediaWiki\Linker\LinkTarget;
 use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Session\SessionManager;
+use Wikimedia\Rdbms\IResultWrapper;
 use Wikimedia\RelPath;
 use Wikimedia\WrappedString;
 use Wikimedia\WrappedStringList;
@@ -468,7 +469,7 @@ class OutputPage extends ContextSource {
         * Internal use only. Use OutputPage::addModules() if possible.
         *
         * @param string $file URL to file (absolute path, protocol-relative, or full url)
-        * @param string $unused Previously used to change the cache-busting query parameter
+        * @param string|null $unused Previously used to change the cache-busting query parameter
         */
        public function addScriptFile( $file, $unused = null ) {
                if ( substr( $file, 0, 1 ) !== '/' && !preg_match( '#^[a-z]*://#i', $file ) ) {
@@ -1337,7 +1338,7 @@ class OutputPage extends ContextSource {
 
        /**
         * @param array $categories
-        * @return bool|ResultWrapper
+        * @return bool|IResultWrapper
         */
        protected function addCategoryLinksToLBAndGetResult( array $categories ) {
                # Add the links to a LinkBatch
@@ -1928,7 +1929,7 @@ class OutputPage extends ContextSource {
         * @param bool $interface Use interface language ($wgLang instead of
         *   $wgContLang) while parsing language sensitive magic words like GRAMMAR and PLURAL.
         *   This also disables LanguageConverter.
-        * @param Language $language Target language object, will override $interface
+        * @param Language|null $language Target language object, will override $interface
         * @throws MWException
         * @return string HTML
         */
@@ -2532,7 +2533,7 @@ class OutputPage extends ContextSource {
         * Output a standard permission error page
         *
         * @param array $errors Error message keys or [key, param...] arrays
-        * @param string $action Action that was denied or null if unknown
+        * @param string|null $action Action that was denied or null if unknown
         */
        public function showPermissionsErrorPage( array $errors, $action = null ) {
                foreach ( $errors as $key => $error ) {
@@ -2622,7 +2623,7 @@ class OutputPage extends ContextSource {
         * Format a list of error messages
         *
         * @param array $errors Array of arrays returned by Title::getUserPermissionsErrors
-        * @param string $action Action that was denied or null if unknown
+        * @param string|null $action Action that was denied or null if unknown
         * @return string The wikitext error-messages, formatted into a list.
         */
        public function formatPermissionsErrorMessage( array $errors, $action = null ) {
@@ -2707,7 +2708,7 @@ class OutputPage extends ContextSource {
         *
         * @param Title $title Title to link
         * @param array $query Query string parameters
-        * @param string $text Text of the link (input is not escaped)
+        * @param string|null $text Text of the link (input is not escaped)
         * @param array $options Options array to pass to Linker
         */
        public function addReturnTo( $title, array $query = [], $text = null, $options = [] ) {
@@ -2722,9 +2723,9 @@ class OutputPage extends ContextSource {
         * Add a "return to" link pointing to a specified title,
         * or the title indicated in the request, or else the main page
         *
-        * @param mixed $unused
-        * @param Title|string $returnto Title or String to return to
-        * @param string $returntoquery Query string for the return to link
+        * @param mixed|null $unused
+        * @param Title|string|null $returnto Title or String to return to
+        * @param string|null $returntoquery Query string for the return to link
         */
        public function returnToMain( $unused = null, $returnto = null, $returntoquery = null ) {
                if ( $returnto == null ) {
@@ -3054,7 +3055,7 @@ class OutputPage extends ContextSource {
         * Add one or more variables to be set in mw.config in JavaScript
         *
         * @param string|array $keys Key or array of key/value pairs
-        * @param mixed $value [optional] Value of the configuration variable
+        * @param mixed|null $value [optional] Value of the configuration variable
         */
        public function addJsConfigVars( $keys, $value = null ) {
                if ( is_array( $keys ) ) {
index 64b54f1..ee608c2 100644 (file)
@@ -50,8 +50,8 @@ class Pingback {
        protected $id;
 
        /**
-        * @param Config $config
-        * @param LoggerInterface $logger
+        * @param Config|null $config
+        * @param LoggerInterface|null $logger
         */
        public function __construct( Config $config = null, LoggerInterface $logger = null ) {
                $this->config = $config ?: RequestContext::getMain()->getConfig();
index 51c2923..1f3de50 100644 (file)
@@ -196,7 +196,7 @@ class ProtectionForm {
        /**
         * Show the input form with optional error message
         *
-        * @param string $err Error message or null if there's no error
+        * @param string|null $err Error message or null if there's no error
         */
        function show( $err = null ) {
                $out = $this->mContext->getOutput();
index dc15f97..d6ff384 100644 (file)
@@ -887,7 +887,7 @@ class Revision implements IDBAccessObject {
         *   Revision::FOR_PUBLIC       to be displayed to all users
         *   Revision::FOR_THIS_USER    to be displayed to $user
         *   Revision::RAW              get the text regardless of permissions
-        * @param User $user User object to check for, only if FOR_THIS_USER is passed
+        * @param User|null $user User object to check for, only if FOR_THIS_USER is passed
         *   to the $audience parameter
         * @since 1.21
         * @return Content|null
index ba576d5..6da537d 100644 (file)
@@ -46,7 +46,7 @@ use MediaWiki\Preferences\DefaultPreferencesFactory;
 use MediaWiki\Shell\CommandFactory;
 use MediaWiki\Storage\BlobStoreFactory;
 use MediaWiki\Storage\NameTableStore;
-use MediaWiki\Storage\RevisionStoreFactory;
+use MediaWiki\Storage\RevisionStore;
 use MediaWiki\Storage\SqlBlobStore;
 use Wikimedia\ObjectFactory;
 
@@ -128,6 +128,14 @@ return [
                );
        },
 
+       'PerDbNameStatsdDataFactory' => function ( MediaWikiServices $services ) {
+               $config = $services->getMainConfig();
+               $wiki = $config->get( 'DBname' );
+               return new BufferingStatsdDataFactory(
+                       rtrim( $services->getMainConfig()->get( 'StatsdMetricPrefix' ), '.' ) . $wiki
+               );
+       },
+
        'EventRelayerGroup' => function ( MediaWikiServices $services ) {
                return new EventRelayerGroup( $services->getMainConfig()->get( 'EventRelayerConfig' ) );
        },
@@ -463,15 +471,10 @@ return [
        },
 
        'RevisionStore' => function ( MediaWikiServices $services ) {
-               return $services->getRevisionStoreFactory()->getRevisionStore();
-       },
-
-       'RevisionStoreFactory' => function ( MediaWikiServices $services ) {
                /** @var SqlBlobStore $blobStore */
                $blobStore = $services->getService( '_SqlBlobStore' );
-               $config = $services->getMainConfig();
 
-               $store = new RevisionStoreFactory(
+               $store = new RevisionStore(
                        $services->getDBLoadBalancer(),
                        $blobStore,
                        $services->getMainWANObjectCache(),
@@ -479,11 +482,14 @@ return [
                        $services->getContentModelStore(),
                        $services->getSlotRoleStore(),
                        $services->getMainConfig()->get( 'MultiContentRevisionSchemaMigrationStage' ),
-                       $services->getActorMigration(),
-                       LoggerFactory::getInstance( 'RevisionStore' ),
-                       $config->get( 'ContentHandlerUseDB' )
+                       $services->getActorMigration()
                );
 
+               $store->setLogger( LoggerFactory::getInstance( 'RevisionStore' ) );
+
+               $config = $services->getMainConfig();
+               $store->setContentHandlerUseDB( $config->get( 'ContentHandlerUseDB' ) );
+
                return $store;
        },
 
index 8e77956..1eaedc2 100644 (file)
@@ -180,7 +180,7 @@ class SiteConfiguration {
         * Retrieves a configuration setting for a given wiki.
         * @param string $settingName ID of the setting name to retrieve
         * @param string $wiki Wiki ID of the wiki in question.
-        * @param string $suffix The suffix of the wiki in question.
+        * @param string|null $suffix The suffix of the wiki in question.
         * @param array $params List of parameters. $.'key' is replaced by $value in all returned data.
         * @param array $wikiTags The tags assigned to the wiki.
         * @return mixed The value of the setting requested.
@@ -294,7 +294,7 @@ class SiteConfiguration {
        /**
         * Gets all settings for a wiki
         * @param string $wiki Wiki ID of the wiki in question.
-        * @param string $suffix The suffix of the wiki in question.
+        * @param string|null $suffix The suffix of the wiki in question.
         * @param array $params List of parameters. $.'key' is replaced by $value in all returned data.
         * @param array $wikiTags The tags assigned to the wiki.
         * @return array Array of settings requested.
@@ -325,7 +325,7 @@ class SiteConfiguration {
         * Retrieves a configuration setting for a given wiki, forced to a boolean.
         * @param string $setting ID of the setting name to retrieve
         * @param string $wiki Wiki ID of the wiki in question.
-        * @param string $suffix The suffix of the wiki in question.
+        * @param string|null $suffix The suffix of the wiki in question.
         * @param array $wikiTags The tags assigned to the wiki.
         * @return bool The value of the setting requested.
         */
@@ -364,7 +364,7 @@ class SiteConfiguration {
         * Retrieves the value of a given setting, and places it in its corresponding global variable.
         * @param string $setting ID of the setting name to retrieve
         * @param string $wiki Wiki ID of the wiki in question.
-        * @param string $suffix The suffix of the wiki in question.
+        * @param string|null $suffix The suffix of the wiki in question.
         * @param array $params List of parameters. $.'key' is replaced by $value in all returned data.
         * @param array $wikiTags The tags assigned to the wiki.
         */
@@ -399,7 +399,7 @@ class SiteConfiguration {
        /**
         * Retrieves the values of all settings, and places them in their corresponding global variables.
         * @param string $wiki Wiki ID of the wiki in question.
-        * @param string $suffix The suffix of the wiki in question.
+        * @param string|null $suffix The suffix of the wiki in question.
         * @param array $params List of parameters. $.'key' is replaced by $value in all returned data.
         * @param array $wikiTags The tags assigned to the wiki.
         */
index f17f173..8cd72a5 100644 (file)
@@ -171,7 +171,7 @@ class Status extends StatusValue {
         * @param string|bool $shortContext A short enclosing context message name, to
         *        be used when there is a single error
         * @param string|bool $longContext A long enclosing context message name, for a list
-        * @param string|Language $lang Language to use for processing messages
+        * @param string|Language|null $lang Language to use for processing messages
         * @return string
         */
        public function getWikiText( $shortContext = false, $longContext = false, $lang = null ) {
@@ -227,7 +227,7 @@ class Status extends StatusValue {
         *
         * @param string|string[]|bool $shortContext A message name or an array of message names.
         * @param string|string[]|bool $longContext A message name or an array of message names.
-        * @param string|Language $lang Language to use for processing messages
+        * @param string|Language|null $lang Language to use for processing messages
         * @return Message
         */
        public function getMessage( $shortContext = false, $longContext = false, $lang = null ) {
@@ -279,7 +279,7 @@ class Status extends StatusValue {
         * If $error is an array, the "params" field is a list of parameters for the message.
         *
         * @param array|string $error Code string or (key: code string, params: string[]) map
-        * @param string|Language $lang Language to use for processing messages
+        * @param string|Language|null $lang Language to use for processing messages
         * @return Message
         */
        protected function getErrorMessage( $error, $lang = null ) {
@@ -324,7 +324,7 @@ class Status extends StatusValue {
        /**
         * Return an array with a Message object for each error.
         * @param array $errors
-        * @param string|Language $lang Language to use for processing messages
+        * @param string|Language|null $lang Language to use for processing messages
         * @return Message[]
         */
        protected function getErrorMessageArray( $errors, $lang = null ) {
index cc72754..73463b5 100644 (file)
@@ -241,7 +241,7 @@ class DerivedPageDataUpdater implements IDBAccessObject {
         * @param JobQueueGroup $jobQueueGroup
         * @param MessageCache $messageCache
         * @param Language $contentLanguage
-        * @param LoggerInterface $saveParseLogger
+        * @param LoggerInterface|null $saveParseLogger
         */
        public function __construct(
                WikiPage $wikiPage,
@@ -1310,7 +1310,7 @@ class DerivedPageDataUpdater implements IDBAccessObject {
                                        // stale data from REPEATABLE-READ snapshots.
                                        // HACK: But don't use a fresh connection in unit tests, since it would not have
                                        // the fake tables. This should be handled by the LoadBalancer!
-                                       $flags = defined( 'MW_PHPUNIT_TEST' ) ? 0 : $lb::CONN_TRX_AUTO;
+                                       $flags = defined( 'MW_PHPUNIT_TEST' ) ? 0 : $lb::CONN_TRX_AUTOCOMMIT;
                                        $db = $lb->getConnectionRef( $dbIndex, [], $this->getWikiId(), $flags );
 
                                        return 1 + (int)$db->selectField(
index 1982d02..3516ffe 100644 (file)
@@ -70,10 +70,10 @@ class NameTableStore {
         * @param string $table
         * @param string $idField
         * @param string $nameField
-        * @param callable $normalizationCallback Normalization to be applied to names before being
+        * @param callable|null $normalizationCallback Normalization to be applied to names before being
         * saved or queried. This should be a callback that accepts and returns a single string.
         * @param bool|string $wikiId The ID of the target wiki database. Use false for the local wiki.
-        * @param callable $insertCallback Callback to change insert fields accordingly.
+        * @param callable|null $insertCallback Callback to change insert fields accordingly.
         * This parameter was introduced in 1.32
         */
        public function __construct(
@@ -109,8 +109,20 @@ class NameTableStore {
                return $this->loadBalancer->getConnection( $index, [], $this->wikiId, $flags );
        }
 
+       /**
+        * Gets the cache key for names.
+        *
+        * The cache key is constructed based on the wiki ID passed to the constructor, and allows
+        * sharing of name tables cached for a specific database between wikis.
+        *
+        * @return string
+        */
        private function getCacheKey() {
-               return $this->cache->makeKey( 'NameTableSqlStore', $this->table, $this->wikiId );
+               return $this->cache->makeGlobalKey(
+                       'NameTableSqlStore',
+                       $this->table,
+                       $this->loadBalancer->resolveDomainID( $this->wikiId )
+               );
        }
 
        /**
index 2376d16..67928f9 100644 (file)
@@ -344,6 +344,10 @@ class PageUpdater {
                // TODO: MCR: check the role and the content's model against the list of supported
                // roles, see T194046.
 
+               if ( $role !== 'main' ) {
+                       throw new InvalidArgumentException( 'Only the main slot is presently supported' );
+               }
+
                $this->slotsUpdate->modifyContent( $role, $content );
        }
 
index 86e8c06..2c45468 100644 (file)
@@ -77,7 +77,7 @@ interface RevisionFactory {
         *        Use RevisionStore::getArchiveQueryInfo() to build a query that yields the
         *        required fields.
         * @param int $queryFlags Flags for lazy loading behavior, see IDBAccessObject::READ_XXX.
-        * @param Title $title
+        * @param Title|null $title
         * @param array $overrides An associative array that allows fields in $row to be overwritten.
         *        Keys in this array correspond to field names in $row without the "ar_" prefix, so
         *        $overrides['user'] will override $row->ar_user, etc.
index 45cd184..a6e2930 100644 (file)
@@ -84,7 +84,7 @@ interface RevisionLookup extends IDBAccessObject {
         * MCR migration note: this replaces Revision::getPrevious
         *
         * @param RevisionRecord $rev
-        * @param Title $title if known (optional)
+        * @param Title|null $title if known (optional)
         *
         * @return RevisionRecord|null
         */
@@ -96,7 +96,7 @@ interface RevisionLookup extends IDBAccessObject {
         * MCR migration note: this replaces Revision::getNext
         *
         * @param RevisionRecord $rev
-        * @param Title $title if known (optional)
+        * @param Title|null $title if known (optional)
         *
         * @return RevisionRecord|null
         */
index 12bee1e..b01bdd8 100644 (file)
@@ -122,7 +122,7 @@ class RevisionStore
         */
        private $slotRoleStore;
 
-       /** @var int One of the MIGRATION_* constants */
+       /** @var int An appropriate combination of SCHEMA_COMPAT_XXX flags. */
        private $mcrMigrationStage;
 
        /**
@@ -134,9 +134,11 @@ class RevisionStore
         * @param CommentStore $commentStore
         * @param NameTableStore $contentModelStore
         * @param NameTableStore $slotRoleStore
-        * @param int $migrationStage
+        * @param int $mcrMigrationStage An appropriate combination of SCHEMA_COMPAT_XXX flags
         * @param ActorMigration $actorMigration
         * @param bool|string $wikiId
+        *
+        * @throws MWException if $mcrMigrationStage or $wikiId is invalid.
         */
        public function __construct(
                LoadBalancer $loadBalancer,
@@ -145,12 +147,39 @@ class RevisionStore
                CommentStore $commentStore,
                NameTableStore $contentModelStore,
                NameTableStore $slotRoleStore,
-               $migrationStage,
+               $mcrMigrationStage,
                ActorMigration $actorMigration,
                $wikiId = false
        ) {
                Assert::parameterType( 'string|boolean', $wikiId, '$wikiId' );
-               Assert::parameterType( 'integer', $migrationStage, '$migrationStage' );
+               Assert::parameterType( 'integer', $mcrMigrationStage, '$mcrMigrationStage' );
+               Assert::parameter(
+                       ( $mcrMigrationStage & SCHEMA_COMPAT_READ_BOTH ) !== SCHEMA_COMPAT_READ_BOTH,
+                       '$mcrMigrationStage',
+                       'Reading from the old and the new schema at the same time is not supported.'
+               );
+               Assert::parameter(
+                       ( $mcrMigrationStage & SCHEMA_COMPAT_READ_BOTH ) !== 0,
+                       '$mcrMigrationStage',
+                       'Reading needs to be enabled for the old or the new schema.'
+               );
+               Assert::parameter(
+                       ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_BOTH ) !== 0,
+                       '$mcrMigrationStage',
+                       'Writing needs to be enabled for the old or the new schema.'
+               );
+               Assert::parameter(
+                       ( $mcrMigrationStage & SCHEMA_COMPAT_READ_OLD ) === 0
+                       || ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) !== 0,
+                       '$mcrMigrationStage',
+                       'Cannot read the old schema when not also writing it.'
+               );
+               Assert::parameter(
+                       ( $mcrMigrationStage & SCHEMA_COMPAT_READ_NEW ) === 0
+                       || ( $mcrMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) !== 0,
+                       '$mcrMigrationStage',
+                       'Cannot read the new schema when not also writing it.'
+               );
 
                $this->loadBalancer = $loadBalancer;
                $this->blobStore = $blobStore;
@@ -158,12 +187,21 @@ class RevisionStore
                $this->commentStore = $commentStore;
                $this->contentModelStore = $contentModelStore;
                $this->slotRoleStore = $slotRoleStore;
-               $this->mcrMigrationStage = $migrationStage;
+               $this->mcrMigrationStage = $mcrMigrationStage;
                $this->actorMigration = $actorMigration;
                $this->wikiId = $wikiId;
                $this->logger = new NullLogger();
        }
 
+       /**
+        * @param int $flags A combination of SCHEMA_COMPAT_XXX flags.
+        * @return bool True if all the given flags were set in the $mcrMigrationStage
+        *         parameter passed to the constructor.
+        */
+       private function hasMcrSchemaFlags( $flags ) {
+               return ( $this->mcrMigrationStage & $flags ) === $flags;
+       }
+
        public function setLogger( LoggerInterface $logger ) {
                $this->logger = $logger;
        }
@@ -188,10 +226,14 @@ class RevisionStore
         * @throws MWException
         */
        public function setContentHandlerUseDB( $contentHandlerUseDB ) {
-               if ( !$contentHandlerUseDB && $this->mcrMigrationStage > MIGRATION_OLD ) {
-                       throw new MWException(
-                               'Content model must be stored in the database for multi content revision migration.'
-                       );
+               if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_NEW )
+                       || $this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_NEW )
+               ) {
+                       if ( !$contentHandlerUseDB ) {
+                               throw new MWException(
+                                       'Content model must be stored in the database for multi content revision migration.'
+                               );
+                       }
                }
                $this->contentHandlerUseDB = $contentHandlerUseDB;
        }
@@ -372,11 +414,17 @@ class RevisionStore
                        );
                }
 
-               // While inserting into the old schema make sure only the main slot is allowed.
-               // TODO: support extra slots in MIGRATION_WRITE_BOTH mode!
-               if ( $this->mcrMigrationStage <= MIGRATION_WRITE_BOTH && $slotRoles !== [ 'main' ] ) {
+               // If we are not writing into the new schema, we can't support extra slots.
+               if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_NEW ) && $slotRoles !== [ 'main' ] ) {
                        throw new InvalidArgumentException(
-                               'Only the main slot is supported with MCR migration mode <= MIGRATION_WRITE_BOTH!'
+                               'Only the main slot is supported when not writing to the MCR enabled schema!'
+                       );
+               }
+
+               // As long as we are not reading from the new schema, we don't want to write extra slots.
+               if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_NEW ) && $slotRoles !== [ 'main' ] ) {
+                       throw new InvalidArgumentException(
+                               'Only the main slot is supported when not reading from the MCR enabled schema!'
                        );
                }
 
@@ -433,7 +481,7 @@ class RevisionStore
                );
 
                // Trigger exception if the main slot is missing.
-               // Technically, this could go away with MIGRATION_NEW: while
+               // Technically, this could go away after MCR migration: while
                // calling code may require a main slot to exist, RevisionStore
                // really should not know or care about that requirement.
                $rev->getSlot( 'main', RevisionRecord::RAW );
@@ -506,7 +554,9 @@ class RevisionStore
                                $newSlots[$role] = $slot;
 
                                // Write the main slot's text ID to the revision table for backwards compatibility
-                               if ( $slot->getRole() === 'main' && $this->mcrMigrationStage <= MIGRATION_WRITE_BOTH ) {
+                               if ( $slot->getRole() === 'main'
+                                       && $this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_OLD )
+                               ) {
                                        $blobAddress = $slot->getAddress();
                                        $this->updateRevisionTextId( $dbw, $revisionId, $blobAddress );
                                }
@@ -576,11 +626,13 @@ class RevisionStore
                }
 
                // Write the main slot's text ID to the revision table for backwards compatibility
-               if ( $protoSlot->getRole() === 'main' && $this->mcrMigrationStage <= MIGRATION_WRITE_BOTH ) {
+               if ( $protoSlot->getRole() === 'main'
+                       && $this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_OLD )
+               ) {
                        $this->updateRevisionTextId( $dbw, $revisionId, $blobAddress );
                }
 
-               if ( $this->mcrMigrationStage >= MIGRATION_WRITE_BOTH ) {
+               if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_NEW ) ) {
                        if ( $protoSlot->hasContentId() ) {
                                $contentId = $protoSlot->getContentId();
                        } else {
@@ -705,8 +757,8 @@ class RevisionStore
                        $revisionRow['rev_id'] = $rev->getId();
                }
 
-               if ( $this->mcrMigrationStage <= MIGRATION_WRITE_BOTH ) {
-                       // In non MCR more this IF section will relate to the main slot
+               if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_OLD ) ) {
+                       // In non MCR mode this IF section will relate to the main slot
                        $mainSlot = $rev->getSlot( 'main' );
                        $model = $mainSlot->getModel();
                        $format = $mainSlot->getFormat();
@@ -1042,7 +1094,7 @@ class RevisionStore
                $blobFlags = null;
 
                if ( is_object( $row ) ) {
-                       if ( $this->mcrMigrationStage >= MIGRATION_NEW ) {
+                       if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_NEW ) ) {
                                // Don't emulate from a row when using the new schema.
                                // Emulating from an array is still OK.
                                throw new LogicException( 'Can\'t emulate the main slot when using MCR schema.' );
@@ -1425,10 +1477,7 @@ class RevisionStore
                $queryFlags,
                Title $title
        ) {
-               if ( $this->mcrMigrationStage < MIGRATION_NEW ) {
-                       // TODO: in MIGRATION_WRITE_BOTH, we could use the old and the new method:
-                       // e.g. call emulateMainSlot_1_29() if loadSlotRecords() fails.
-
+               if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_NEW ) ) {
                        $mainSlot = $this->emulateMainSlot_1_29( $revisionRow, $queryFlags, $title );
                        $slots = new RevisionSlots( [ 'main' => $mainSlot ] );
                } else {
@@ -1610,8 +1659,8 @@ class RevisionStore
                }
 
                if ( !empty( $fields['text_id'] ) ) {
-                       if ( $this->mcrMigrationStage >= MIGRATION_NEW ) {
-                               throw new MWException( "Cannot use text_id field with MCR schema" );
+                       if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_OLD ) ) {
+                               throw new MWException( "The text_id field is only available in the pre-MCR schema" );
                        }
 
                        if ( !empty( $fields['content'] ) ) {
@@ -1843,7 +1892,7 @@ class RevisionStore
         *
         * @param array $conditions
         * @param int $flags (optional)
-        * @param Title $title
+        * @param Title|null $title
         *
         * @return RevisionRecord|null
         */
@@ -1879,7 +1928,7 @@ class RevisionStore
         * @param IDatabase $db
         * @param array $conditions
         * @param int $flags (optional)
-        * @param Title $title
+        * @param Title|null $title
         *
         * @return RevisionRecord|null
         */
@@ -1967,7 +2016,8 @@ class RevisionStore
        /**
         * Finds the ID of a content row for a given revision and slot role.
         * This can be used to re-use content rows even while the content ID
-        * is still missing from SlotRecords, in MIGRATION_WRITE_BOTH mode.
+        * is still missing from SlotRecords, when writing to both the old and
+        * the new schema during MCR schema migration.
         *
         * @todo remove after MCR schema migration is complete.
         *
@@ -1978,7 +2028,7 @@ class RevisionStore
         * @return int|null
         */
        private function findSlotContentId( IDatabase $db, $revId, $role ) {
-               if ( $this->mcrMigrationStage < MIGRATION_WRITE_BOTH ) {
+               if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_NEW ) ) {
                        return null;
                }
 
@@ -2014,8 +2064,8 @@ class RevisionStore
         *  - 'page': Join with the page table, and select fields to identify the page
         *  - 'user': Join with the user table, and select the user name
         *  - 'text': Join with the text table, and select fields to load page text. This
-        *    option is deprecated in MW 1.32 with MCR migration stage MIGRATION_WRITE_BOTH,
-        *    and disallowed with MIGRATION_MEW.
+        *    option is deprecated in MW 1.32 when the MCR migration flag SCHEMA_COMPAT_WRITE_NEW
+        *    is set, and disallowed when SCHEMA_COMPAT_READ_OLD is not set.
         *
         * @return array With three keys:
         *  - tables: (string[]) to include in the `$table` to `IDatabase->select()`
@@ -2051,7 +2101,7 @@ class RevisionStore
                $ret['fields'] = array_merge( $ret['fields'], $actorQuery['fields'] );
                $ret['joins'] = array_merge( $ret['joins'], $actorQuery['joins'] );
 
-               if ( $this->mcrMigrationStage < MIGRATION_NEW ) {
+               if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_OLD ) ) {
                        $ret['fields'][] = 'rev_text_id';
 
                        if ( $this->contentHandlerUseDB ) {
@@ -2083,9 +2133,12 @@ class RevisionStore
                }
 
                if ( in_array( 'text', $options, true ) ) {
-                       if ( $this->mcrMigrationStage === MIGRATION_NEW ) {
+                       if ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_WRITE_OLD ) ) {
                                throw new InvalidArgumentException( 'text table can no longer be joined directly' );
-                       } elseif ( $this->mcrMigrationStage >= MIGRATION_WRITE_BOTH ) {
+                       } elseif ( !$this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_OLD ) ) {
+                               // NOTE: even when this class is set to not read from the old schema, callers
+                               // should still be able to join against the text table, as long as we are still
+                               // writing the old schema for compatibility.
                                wfDeprecated( __METHOD__ . ' with `text` option', '1.32' );
                        }
 
@@ -2121,7 +2174,7 @@ class RevisionStore
                        'joins'  => [],
                ];
 
-               if ( $this->mcrMigrationStage < MIGRATION_NEW ) {
+               if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_OLD ) ) {
                        $db = $this->getDBConnectionRef( DB_REPLICA );
                        $ret['tables']['slots'] = 'revision';
 
@@ -2142,10 +2195,6 @@ class RevisionStore
                                        $ret['fields']['model_name'] = 'NULL';
                                }
                        }
-
-                       // XXX: in MIGRATION_WRITE_BOTH mode, emulate *and* select - using a UNION?
-                       // See Anomie's idea at <https://gerrit.wikimedia.org/r/c/416465/
-                       // 8..10/includes/Storage/RevisionStore.php#2113>
                } else {
                        $ret['tables'][] = 'slots';
                        $ret['tables'][] = 'slot_roles';
@@ -2208,7 +2257,7 @@ class RevisionStore
                        'joins' => $commentQuery['joins'] + $actorQuery['joins'],
                ];
 
-               if ( $this->mcrMigrationStage < MIGRATION_NEW ) {
+               if ( $this->hasMcrSchemaFlags( SCHEMA_COMPAT_READ_OLD ) ) {
                        $ret['fields'][] = 'ar_text_id';
 
                        if ( $this->contentHandlerUseDB ) {
@@ -2273,7 +2322,7 @@ class RevisionStore
         * MCR migration note: this replaces Revision::getPrevious
         *
         * @param RevisionRecord $rev
-        * @param Title $title if known (optional)
+        * @param Title|null $title if known (optional)
         *
         * @return RevisionRecord|null
         */
@@ -2294,7 +2343,7 @@ class RevisionStore
         * MCR migration note: this replaces Revision::getNext
         *
         * @param RevisionRecord $rev
-        * @param Title $title if known (optional)
+        * @param Title|null $title if known (optional)
         *
         * @return RevisionRecord|null
         */
diff --git a/includes/Storage/RevisionStoreFactory.php b/includes/Storage/RevisionStoreFactory.php
deleted file mode 100644 (file)
index 6f8bd99..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-<?php
-
-/**
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * Attribution notice: when this file was created, much of its content was taken
- * from the Revision.php file as present in release 1.30. Refer to the history
- * of that file for original authorship.
- *
- * @file
- */
-
-namespace MediaWiki\Storage;
-
-use ActorMigration;
-use CommentStore;
-use Psr\Log\LoggerInterface;
-use WANObjectCache;
-use Wikimedia\Assert\Assert;
-use Wikimedia\Rdbms\LoadBalancer;
-
-/**
- * @since 1.32
- */
-class RevisionStoreFactory {
-
-       /** @var SqlBlobStore */
-       private $blobStore;
-
-       /** @var LoadBalancer */
-       private $loadBalancer;
-
-       /** @var WANObjectCache */
-       private $cache;
-
-       /** @var CommentStore */
-       private $commentStore;
-
-       /** @var ActorMigration */
-       private $actorMigration;
-
-       /** @var NameTableStore */
-       private $contentModelStore;
-
-       /** @var NameTableStore */
-       private $slotRoleStore;
-
-       /** @var int One of the MIGRATION_* constants */
-       private $mcrMigrationStage;
-
-       /**
-        * @var bool
-        * @see $wgContentHandlerUseDB
-        */
-       private $contentHandlerUseDB;
-
-       /** @var LoggerInterface */
-       private $logger;
-
-       /**
-        * @todo $blobStore should be allowed to be any BlobStore!
-        *
-        * @param LoadBalancer $loadBalancer
-        * @param SqlBlobStore $blobStore
-        * @param WANObjectCache $cache
-        * @param CommentStore $commentStore
-        * @param NameTableStore $contentModelStore
-        * @param NameTableStore $slotRoleStore
-        * @param int $migrationStage
-        * @param ActorMigration $actorMigration
-        * @param LoggerInterface $logger
-        * @param bool $contentHandlerUseDB see {@link $wgContentHandlerUseDB}
-        */
-       public function __construct(
-               LoadBalancer $loadBalancer,
-               SqlBlobStore $blobStore,
-               WANObjectCache $cache,
-               CommentStore $commentStore,
-               NameTableStore $contentModelStore,
-               NameTableStore $slotRoleStore,
-               $migrationStage,
-               ActorMigration $actorMigration,
-               LoggerInterface $logger,
-               $contentHandlerUseDB
-       ) {
-               Assert::parameterType( 'integer', $migrationStage, '$migrationStage' );
-
-               $this->loadBalancer = $loadBalancer;
-               $this->blobStore = $blobStore;
-               $this->cache = $cache;
-               $this->commentStore = $commentStore;
-               $this->contentModelStore = $contentModelStore;
-               $this->slotRoleStore = $slotRoleStore;
-               $this->mcrMigrationStage = $migrationStage;
-               $this->actorMigration = $actorMigration;
-               $this->logger = $logger;
-               $this->contentHandlerUseDB = $contentHandlerUseDB;
-       }
-
-       /**
-        * @since 1.32
-        *
-        * @param bool|string $wikiId false for the current domain / wikid
-        *
-        * @return RevisionStore for the given wikiId with all necessary services and a logger
-        */
-       public function getRevisionStore( $wikiId = false ) {
-               Assert::parameterType( 'string|boolean', $wikiId, '$wikiId' );
-
-               $store = new RevisionStore(
-                       $this->loadBalancer,
-                       $this->blobStore,
-                       $this->cache,
-                       $this->commentStore,
-                       $this->contentModelStore,
-                       $this->slotRoleStore,
-                       $this->mcrMigrationStage,
-                       $this->actorMigration,
-                       $wikiId
-               );
-
-               $store->setLogger( $this->logger );
-               $store->setContentHandlerUseDB( $this->contentHandlerUseDB );
-
-               return $store;
-       }
-
-}
index fb3ef94..48ffe2c 100644 (file)
@@ -267,8 +267,7 @@ class SqlBlobStore implements IDBAccessObject, BlobStore {
 
                // No negative caching; negative hits on text rows may be due to corrupted replica DBs
                $blob = $this->cache->getWithSetCallback(
-                       // TODO: change key, since this is not necessarily revision text!
-                       $this->cache->makeKey( 'revisiontext', 'textid', $blobAddress ),
+                       $this->getCacheKey( $blobAddress ),
                        $this->getCacheTTL(),
                        function ( $unused, &$ttl, &$setOpts ) use ( $blobAddress, $queryFlags ) {
                                list( $index ) = DBAccessObjectUtils::getDBOptions( $queryFlags );
@@ -356,6 +355,25 @@ class SqlBlobStore implements IDBAccessObject, BlobStore {
                return $blob;
        }
 
+       /**
+        * Get a cache key for a given Blob address.
+        *
+        * The cache key is constructed in a way that allows cached blobs from the same database
+        * to be re-used between wikis. For example, enwiki and frwiki will use the same cache keys
+        * for blobs from the wikidatawiki database.
+        *
+        * @param string $blobAddress
+        * @return string
+        */
+       private function getCacheKey( $blobAddress ) {
+               return $this->cache->makeGlobalKey(
+                       'BlobStore',
+                       'address',
+                       $this->dbLoadBalancer->resolveDomainID( $this->wikiId ),
+                       $blobAddress
+               );
+       }
+
        /**
         * Expand a raw data blob according to the flags given.
         *
@@ -370,7 +388,8 @@ class SqlBlobStore implements IDBAccessObject, BlobStore {
         * @param string|string[] $flags Blob flags, such as 'external' or 'gzip'.
         *   Note that not including 'utf-8' in $flags will cause the data to be decoded
         *   according to the legacy encoding specified via setLegacyEncoding.
-        * @param string|null $cacheKey May be used for caching if given
+        * @param string|null $cacheKey A blob address for use in the cache key. If not given,
+        *   caching is disabled.
         *
         * @return false|string The expanded blob or false on failure
         */
@@ -387,13 +406,10 @@ class SqlBlobStore implements IDBAccessObject, BlobStore {
                                return false;
                        }
 
-                       if ( $cacheKey && $this->wikiId === false ) {
-                               // Make use of the wiki-local revision text cache.
+                       if ( $cacheKey ) {
                                // The cached value should be decompressed, so handle that and return here.
-                               // NOTE: we rely on $this->cache being the right cache for $this->wikiId!
                                return $this->cache->getWithSetCallback(
-                                       // TODO: change key, since this is not necessarily revision text!
-                                       $this->cache->makeKey( 'revisiontext', 'textid', $cacheKey ),
+                                       $this->getCacheKey( $cacheKey ),
                                        $this->getCacheTTL(),
                                        function () use ( $url, $flags ) {
                                                // No negative caching per BlobStore::getBlob()
index 067f11f..9213076 100644 (file)
@@ -56,8 +56,8 @@ class StubObject {
        protected $params;
 
        /**
-        * @param string $global Name of the global variable.
-        * @param string|callable $class Name of the class of the real object
+        * @param string|null $global Name of the global variable.
+        * @param string|callable|null $class Name of the class of the real object
         *                               or a factory function to call
         * @param array $params Parameters to pass to constructor of the real object.
         */
index 2293dab..9271e3f 100644 (file)
@@ -46,7 +46,7 @@ class TemplateParser {
        protected $compileFlags = LightnCandy::FLAG_ERROR_EXCEPTION;
 
        /**
-        * @param string $templateDir
+        * @param string|null $templateDir
         * @param bool $forceRecompile
         */
        public function __construct( $templateDir = null, $forceRecompile = false ) {
index f6e5912..8586ad7 100644 (file)
@@ -2086,7 +2086,7 @@ class Title implements LinkTarget {
         * May provide false positives, but should never provide a false negative.
         *
         * @param string $action Action that permission needs to be checked for
-        * @param User $user User to check (since 1.19); $wgUser will be used if not provided.
+        * @param User|null $user User to check (since 1.19); $wgUser will be used if not provided.
         * @return bool
         */
        public function quickUserCan( $action, $user = null ) {
@@ -2097,7 +2097,7 @@ class Title implements LinkTarget {
         * Can $user perform $action on this page?
         *
         * @param string $action Action that permission needs to be checked for
-        * @param User $user User to check (since 1.19); $wgUser will be used if not
+        * @param User|null $user User to check (since 1.19); $wgUser will be used if not
         *   provided.
         * @param string $rigor Same format as Title::getUserPermissionsErrors()
         * @return bool
@@ -3139,7 +3139,7 @@ class Title implements LinkTarget {
         * Public for usage by LiquidThreads.
         *
         * @param array $rows Array of db result objects
-        * @param string $oldFashionedRestrictions Comma-separated set of permission keys
+        * @param string|null $oldFashionedRestrictions Comma-separated set of permission keys
         * indicating who can move or edit the page from the page table, (pre 1.10) rows.
         * Edit and move sections are separated by a colon
         * Example: "edit=autoconfirmed,sysop:move=sysop"
@@ -3211,7 +3211,7 @@ class Title implements LinkTarget {
        /**
         * Load restrictions from the page_restrictions table
         *
-        * @param string $oldFashionedRestrictions Comma-separated set of permission keys
+        * @param string|null $oldFashionedRestrictions Comma-separated set of permission keys
         * indicating who can move or edit the page from the page table, (pre 1.10) rows.
         * Edit and move sections are separated by a colon
         * Example: "edit=autoconfirmed,sysop:move=sysop"
@@ -4681,7 +4681,7 @@ class Title implements LinkTarget {
        /**
         * Updates page_touched for this page; called from LinksUpdate.php
         *
-        * @param string $purgeTime [optional] TS_MW timestamp
+        * @param string|null $purgeTime [optional] TS_MW timestamp
         * @return bool True if the update succeeded
         */
        public function invalidateCache( $purgeTime = null ) {
@@ -4752,7 +4752,7 @@ class Title implements LinkTarget {
        /**
         * Get the timestamp when this page was updated since the user last saw it.
         *
-        * @param User $user
+        * @param User|null $user
         * @return string|null
         */
        public function getNotificationTimestamp( $user = null ) {
index b499d86..f3edebc 100644 (file)
@@ -484,7 +484,7 @@ class WebRequest {
         * If no source and no default, returns null.
         *
         * @param string $name
-        * @param array $default Optional default (or null)
+        * @param array|null $default Optional default (or null)
         * @return array|null
         */
        public function getArray( $name, $default = null ) {
@@ -503,7 +503,7 @@ class WebRequest {
         * If an array is returned, contents are guaranteed to be integers.
         *
         * @param string $name
-        * @param array $default Option default (or null)
+        * @param array|null $default Option default (or null)
         * @return array Array of ints
         */
        public function getIntArray( $name, $default = null ) {
@@ -776,8 +776,8 @@ class WebRequest {
         * Get a cookie from the $_COOKIE jar
         *
         * @param string $key The name of the cookie
-        * @param string $prefix A prefix to use for the cookie name, if not $wgCookiePrefix
-        * @param mixed $default What to return if the value isn't found
+        * @param string|null $prefix A prefix to use for the cookie name, if not $wgCookiePrefix
+        * @param mixed|null $default What to return if the value isn't found
         * @return mixed Cookie value or $default if the cookie not set
         */
        public function getCookie( $key, $prefix = null, $default = null ) {
index 90ebc4b..9dc3bfe 100644 (file)
@@ -131,7 +131,7 @@ class WikiMap {
         *
         * @param string $wikiID Wiki'd id (generally database name)
         * @param string $user User name (must be normalised before calling this function!)
-        * @param string $text Link's text; optional, default to "User:$user"
+        * @param string|null $text Link's text; optional, default to "User:$user"
         * @return string HTML link or false if the wiki was not found
         */
        public static function foreignUserLink( $wikiID, $user, $text = null ) {
@@ -143,7 +143,7 @@ class WikiMap {
         *
         * @param string $wikiID Wiki'd id (generally database name)
         * @param string $page Page name (must be normalised before calling this function!)
-        * @param string $text Link's text; optional, default to $page
+        * @param string|null $text Link's text; optional, default to $page
         * @return string|false HTML link or false if the wiki was not found
         */
        public static function makeForeignLink( $wikiID, $page, $text = null ) {
index 40be80b..8c65461 100644 (file)
@@ -31,7 +31,7 @@ class Xml {
         * characters (<, >, &) are escaped but illegals are not touched.
         *
         * @param string $element Element name
-        * @param array $attribs Name=>value pairs. Values will be escaped.
+        * @param array|null $attribs Name=>value pairs. Values will be escaped.
         * @param string $contents Null to make an open tag only; '' for a contentless closed tag (default)
         * @param bool $allowShortTag Whether '' in $contents will result in a contentless closed tag
         * @return string
@@ -103,7 +103,7 @@ class Xml {
         * This opens an XML element
         *
         * @param string $element Name of the element
-        * @param array $attribs Array of attributes, see Xml::expandAttributes()
+        * @param array|null $attribs Array of attributes, see Xml::expandAttributes()
         * @return string
         */
        public static function openElement( $element, $attribs = null ) {
@@ -136,7 +136,7 @@ class Xml {
         * Create a date selector
         *
         * @param string $selected The month which should be selected, default ''.
-        * @param string $allmonths Value of a special item denoting all month.
+        * @param string|null $allmonths Value of a special item denoting all month.
         *   Null to not include (default).
         * @param string $id Element identifier
         * @return string Html string containing the month selector
@@ -197,7 +197,7 @@ class Xml {
         *
         * @param string $selected The language code of the selected language
         * @param bool $customisedOnly If true only languages which have some content are listed
-        * @param string $inLanguage The ISO code of the language to display the select list in (optional)
+        * @param string|null $inLanguage The ISO code of the language to display the select list in
         * @param array $overrideAttrs Override the attributes of the select tag (since 1.20)
         * @param Message|null $msg Label message key (since 1.20)
         * @return array Array containing 2 items: label HTML and select list HTML
@@ -477,7 +477,7 @@ class Xml {
        /**
         * Convenience function to build an HTML drop-down list item.
         * @param string $text Text for this item. Will be HTML escaped
-        * @param string $value Form submission value; if empty, use text
+        * @param string|null $value Form submission value; if empty, use text
         * @param bool $selected If true, will be the default selected item
         * @param array $attribs Optional additional HTML attributes
         * @return string HTML
@@ -503,7 +503,7 @@ class Xml {
         * @param string $other Text for the "Other reasons" option
         * @param string $selected Option which should be pre-selected
         * @param string $class CSS classes for the drop-down
-        * @param int $tabindex Value of the tabindex attribute
+        * @param int|null $tabindex Value of the tabindex attribute
         * @return string
         */
        public static function listDropDown( $name = '', $list = '', $other = '',
@@ -758,7 +758,7 @@ class Xml {
         * @param array $fields Associative array, key is the name of a message that
         *   contains a description for the field, value is an HTML string
         *   containing the appropriate input.
-        * @param string $submitLabel The name of a message containing a label for
+        * @param string|null $submitLabel The name of a message containing a label for
         *   the submit button.
         * @param array $submitAttribs The attributes to add to the submit button
         * @return string HTML form.
@@ -799,7 +799,7 @@ class Xml {
         * Build a table of data
         * @param array $rows An array of arrays of strings, each to be a row in a table
         * @param array $attribs An array of attributes to apply to the table tag [optional]
-        * @param array $headers An array of strings to use as table headers [optional]
+        * @param array|null $headers An array of strings to use as table headers [optional]
         * @return string
         */
        public static function buildTable( $rows, $attribs = [], $headers = null ) {
index 943ee22..be4eeef 100644 (file)
@@ -1100,7 +1100,7 @@ abstract class ApiBase extends ContextSource {
         * Return true if we're to watch the page, false if not, null if no change.
         * @param string $watchlist Valid values: 'watch', 'unwatch', 'preferences', 'nochange'
         * @param Title $titleObj The page under consideration
-        * @param string $userOption The user option to consider when $watchlist=preferences.
+        * @param string|null $userOption The user option to consider when $watchlist=preferences.
         *    If not set will use watchdefault always and watchcreations if $titleObj doesn't exist.
         * @return bool
         */
@@ -1574,7 +1574,7 @@ abstract class ApiBase extends ContextSource {
         * @param int &$value Parameter value
         * @param int|null $min Minimum value
         * @param int|null $max Maximum value for users
-        * @param int $botMax Maximum value for sysops/bots
+        * @param int|null $botMax Maximum value for sysops/bots
         * @param bool $enforceLimits Whether to enforce (die) if value is outside limits
         */
        protected function validateLimit( $paramName, &$value, $min, $max, $botMax = null,
@@ -1744,7 +1744,7 @@ abstract class ApiBase extends ContextSource {
         * Set a watch (or unwatch) based the based on a watchlist parameter.
         * @param string $watch Valid values: 'watch', 'unwatch', 'preferences', 'nochange'
         * @param Title $titleObj The article's title to change
-        * @param string $userOption The user option to consider when $watch=preferences
+        * @param string|null $userOption The user option to consider when $watch=preferences
         */
        protected function setWatch( $watch, $titleObj, $userOption = null ) {
                $value = $this->getWatchlistValue( $watch, $titleObj, $userOption );
@@ -1812,7 +1812,7 @@ abstract class ApiBase extends ContextSource {
         * @since 1.25
         * @param string|array|Message $msg
         * @param IContextSource $context
-        * @param array $params
+        * @param array|null $params
         * @return Message|null
         */
        public static function makeMessage( $msg, IContextSource $context, array $params = null ) {
index a63dee6..ec857b7 100644 (file)
@@ -139,7 +139,7 @@ class ApiDelete extends ApiBase {
         * @param Page $page Object to work on
         * @param User $user User doing the action
         * @param string $oldimage Archive name
-        * @param string &$reason Reason for the deletion. Autogenerated if null.
+        * @param string|null &$reason Reason for the deletion. Autogenerated if null.
         * @param bool $suppress Whether to mark all deleted versions as restricted
         * @param array $tags Tags to tag the deletion with
         * @return Status
index f324eff..610ecf5 100644 (file)
@@ -165,7 +165,7 @@ class ApiMain extends ApiBase {
        /**
         * Constructs an instance of ApiMain that utilizes the module and format specified by $request.
         *
-        * @param IContextSource|WebRequest $context If this is an instance of
+        * @param IContextSource|WebRequest|null $context If this is an instance of
         *    FauxRequest, errors are thrown and no printing occurs
         * @param bool $enableWrite Should be set to true if the api may modify data
         */
@@ -1616,7 +1616,7 @@ class ApiMain extends ApiBase {
        /**
         * Log the preceding request
         * @param float $time Time in seconds
-        * @param Exception $e Exception caught while processing the request
+        * @param Exception|null $e Exception caught while processing the request
         */
        protected function logRequest( $time, $e = null ) {
                $request = $this->getRequest();
index fec55aa..d2df013 100644 (file)
@@ -143,7 +143,7 @@ class ApiModuleManager extends ContextSource {
         * Get module instance by name, or instantiate it if it does not exist
         *
         * @param string $moduleName Module name
-        * @param string $group Optionally validate that the module is in a specific group
+        * @param string|null $group Optionally validate that the module is in a specific group
         * @param bool $ignoreCache If true, force-creates a new instance and does not cache it
         *
         * @return ApiBase|null The new module instance, or null if failed
@@ -205,7 +205,7 @@ class ApiModuleManager extends ContextSource {
 
        /**
         * Get an array of modules in a specific group or all if no group is set.
-        * @param string $group Optional group filter
+        * @param string|null $group Optional group filter
         * @return array List of module names
         */
        public function getNames( $group = null ) {
@@ -224,7 +224,7 @@ class ApiModuleManager extends ContextSource {
 
        /**
         * Create an array of (moduleName => moduleClass) for a specific group or for all.
-        * @param string $group Name of the group to get or null for all
+        * @param string|null $group Name of the group to get or null for all
         * @return array Name=>class map
         */
        public function getNamesWithClasses( $group = null ) {
@@ -256,7 +256,7 @@ class ApiModuleManager extends ContextSource {
        /**
         * Returns true if the specific module is defined at all or in a specific group.
         * @param string $moduleName Module name
-        * @param string $group Group name to check against, or null to check all groups,
+        * @param string|null $group Group name to check against, or null to check all groups,
         * @return bool True if defined
         */
        public function isDefined( $moduleName, $group = null ) {
index 515ebc5..a32b288 100644 (file)
@@ -460,7 +460,7 @@ class ApiPageSet extends ApiBase {
        /**
         * Get a list of redirect resolutions - maps a title to its redirect
         * target. Includes generator data for redirect source when available.
-        * @param ApiResult $result
+        * @param ApiResult|null $result
         * @return array Array of prefixed_title (string) => Title object
         * @since 1.21
         */
@@ -507,7 +507,7 @@ class ApiPageSet extends ApiBase {
        /**
         * Get a list of title normalizations - maps a title to its normalized
         * version in the form of result array.
-        * @param ApiResult $result
+        * @param ApiResult|null $result
         * @return array Array of raw_prefixed_title (string) => prefixed_title (string)
         * @since 1.21
         */
@@ -542,7 +542,7 @@ class ApiPageSet extends ApiBase {
        /**
         * Get a list of title conversions - maps a title to its converted
         * version as a result array.
-        * @param ApiResult $result
+        * @param ApiResult|null $result
         * @return array Array of (from, to) strings
         * @since 1.21
         */
@@ -573,7 +573,7 @@ class ApiPageSet extends ApiBase {
        /**
         * Get a list of interwiki titles - maps a title to its interwiki
         * prefix as result.
-        * @param ApiResult $result
+        * @param ApiResult|null $result
         * @param bool $iwUrl
         * @return array Array of raw_prefixed_title (string) => interwiki_prefix (string)
         * @since 1.21
@@ -692,7 +692,7 @@ class ApiPageSet extends ApiBase {
 
        /**
         * Revision IDs that were not found in the database as result array.
-        * @param ApiResult $result
+        * @param ApiResult|null $result
         * @return array Array of revision IDs
         * @since 1.21
         */
index be12977..ee13f3d 100644 (file)
@@ -35,7 +35,7 @@ class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
        }
 
        /**
-        * @param ApiPageSet $resultPageSet
+        * @param ApiPageSet|null $resultPageSet
         * @return void
         */
        protected function run( ApiPageSet $resultPageSet = null ) {
index 3af2459..a0e71a5 100644 (file)
@@ -33,7 +33,7 @@ class ApiQueryAllRevisions extends ApiQueryRevisionsBase {
        }
 
        /**
-        * @param ApiPageSet $resultPageSet
+        * @param ApiPageSet|null $resultPageSet
         * @return void
         */
        protected function run( ApiPageSet $resultPageSet = null ) {
index b338ed6..bfacc51 100644 (file)
@@ -88,7 +88,7 @@ class ApiQueryBacklinksprop extends ApiQueryGeneratorBase {
        }
 
        /**
-        * @param ApiPageSet $resultPageSet
+        * @param ApiPageSet|null $resultPageSet
         */
        private function run( ApiPageSet $resultPageSet = null ) {
                $settings = self::$settings[$this->getModuleName()];
index 3ad45bb..b9ed9f2 100644 (file)
@@ -320,7 +320,7 @@ abstract class ApiQueryBase extends ApiBase {
         * Add an option such as LIMIT or USE INDEX. If an option was set
         * before, the old value will be overwritten
         * @param string $name Option name
-        * @param string|string[] $value Option value
+        * @param string|string[]|null $value Option value
         */
        protected function addOption( $name, $value = null ) {
                if ( is_null( $value ) ) {
@@ -402,8 +402,8 @@ abstract class ApiQueryBase extends ApiBase {
        }
 
        /**
-        * @param string $query
-        * @param string $protocol
+        * @param string|null $query
+        * @param string|null $protocol
         * @return null|string
         */
        public function prepareUrlQuerySearchString( $query = null, $protocol = null ) {
@@ -503,7 +503,7 @@ abstract class ApiQueryBase extends ApiBase {
         * Same as addPageSubItems(), but one element of $data at a time
         * @param int $pageId Page ID
         * @param array $item Data array à la ApiResult
-        * @param string $elemname XML element name. If null, getModuleName()
+        * @param string|null $elemname XML element name. If null, getModuleName()
         *  is used
         * @return bool Whether the element fit in the result
         */
index 90aed6e..7193eeb 100644 (file)
@@ -42,7 +42,7 @@ class ApiQueryIWBacklinks extends ApiQueryGeneratorBase {
        }
 
        /**
-        * @param ApiPageSet $resultPageSet
+        * @param ApiPageSet|null $resultPageSet
         * @return void
         */
        public function run( $resultPageSet = null ) {
index e447f4f..177b248 100644 (file)
@@ -359,7 +359,7 @@ class ApiQueryImageInfo extends ApiQueryBase {
         * @param File $file
         * @param array $prop Array of properties to get (in the keys)
         * @param ApiResult $result
-        * @param array $thumbParams Containing 'width' and 'height' items, or null
+        * @param array|null $thumbParams Containing 'width' and 'height' items, or null
         * @param array|bool|string $opts Options for data fetching.
         *   This is an array consisting of the keys:
         *    'version': The metadata version for the metadata option
index 9b99312..7145d4d 100644 (file)
@@ -42,7 +42,7 @@ class ApiQueryLangBacklinks extends ApiQueryGeneratorBase {
        }
 
        /**
-        * @param ApiPageSet $resultPageSet
+        * @param ApiPageSet|null $resultPageSet
         * @return void
         */
        public function run( $resultPageSet = null ) {
index a828e11..8758d9c 100644 (file)
@@ -49,7 +49,7 @@ class ApiQueryQueryPage extends ApiQueryGeneratorBase {
        }
 
        /**
-        * @param ApiPageSet $resultPageSet
+        * @param ApiPageSet|null $resultPageSet
         */
        public function run( $resultPageSet = null ) {
                $params = $this->extractRequestParams();
index f3af226..f870d45 100644 (file)
@@ -136,7 +136,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
        /**
         * Generates and outputs the result of this query based upon the provided parameters.
         *
-        * @param ApiPageSet $resultPageSet
+        * @param ApiPageSet|null $resultPageSet
         */
        public function run( $resultPageSet = null ) {
                $user = $this->getUser();
index f888434..87c6f9d 100644 (file)
@@ -44,7 +44,7 @@ abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
        }
 
        /**
-        * @param ApiPageSet $resultPageSet
+        * @param ApiPageSet|null $resultPageSet
         * @return void
         */
        abstract protected function run( ApiPageSet $resultPageSet = null );
index df71830..3b7c181 100644 (file)
@@ -723,7 +723,7 @@ class ApiResult implements ApiSerializable {
         * @since 1.25
         * @param array &$arr
         * @param string $type See ApiResult::META_TYPE
-        * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
+        * @param string|null $kvpKeyName See ApiResult::META_KVP_KEY_NAME
         */
        public static function setArrayType( array &$arr, $type, $kvpKeyName = null ) {
                if ( !in_array( $type, [
@@ -742,7 +742,7 @@ class ApiResult implements ApiSerializable {
         * @since 1.25
         * @param array|string|null $path See ApiResult::addValue()
         * @param string $tag See ApiResult::META_TYPE
-        * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
+        * @param string|null $kvpKeyName See ApiResult::META_KVP_KEY_NAME
         */
        public function addArrayType( $path, $tag, $kvpKeyName = null ) {
                $arr = &$this->path( $path );
@@ -754,7 +754,7 @@ class ApiResult implements ApiSerializable {
         * @since 1.25
         * @param array &$arr
         * @param string $type See ApiResult::META_TYPE
-        * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
+        * @param string|null $kvpKeyName See ApiResult::META_KVP_KEY_NAME
         */
        public static function setArrayTypeRecursive( array &$arr, $type, $kvpKeyName = null ) {
                self::setArrayType( $arr, $type, $kvpKeyName );
@@ -770,7 +770,7 @@ class ApiResult implements ApiSerializable {
         * @since 1.25
         * @param array|string|null $path See ApiResult::addValue()
         * @param string $tag See ApiResult::META_TYPE
-        * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
+        * @param string|null $kvpKeyName See ApiResult::META_KVP_KEY_NAME
         */
        public function addArrayTypeRecursive( $path, $tag, $kvpKeyName = null ) {
                $arr = &$this->path( $path );
@@ -1050,7 +1050,7 @@ class ApiResult implements ApiSerializable {
         *
         * @since 1.25
         * @param array|object $data
-        * @param array &$metadata Store metadata here, if provided
+        * @param array|null &$metadata Store metadata here, if provided
         * @return array|object
         */
        public static function stripMetadataNonRecursive( $data, &$metadata = null ) {
index 3cec5d4..47ab23f 100644 (file)
        "apihelp-options-summary": "Смени ги нагодувањата на тековниот корисник.",
        "apihelp-options-extended-description": "Можат да се зададат само можностите заведени во јадрото или во едно од воспоставените додатоци, или пак можности со клуч кој ја има претставката <code>userjs-</code> (предвиден за употреба од кориснички скрипти).",
        "apihelp-options-param-reset": "Ги враќа поставките по основно.",
-       "apihelp-options-param-resetkinds": "Ð\9fисок на типови можности за повраток кога е зададена можноста <var>$1reset</var>.",
+       "apihelp-options-param-resetkinds": "Сисок на типови можности за повраток кога е зададена можноста <var>$1reset</var>.",
        "apihelp-options-param-change": "Список на промени во форматот name=value (на пр. skin=vector). Вредностите не треба да содржат исправени црти. Ако не зададете вредност (дури ни знак за равенство), на пр., можност|другаможност|..., ќе биде зададена вредноста на можноста по основно.",
        "apihelp-options-param-optionname": "Назив на можноста што треба да ѝ се зададе на вредноста дадена од <var>$1optionvalue</var>.",
        "apihelp-options-param-optionvalue": "Вредноста на можноста укажана од <var>$1optionname</var>. Може да содржи исправени црти.",
index 271e36c..d7735f6 100644 (file)
        "apihelp-protect-example-protect": "Een pagina beveiligen.",
        "apihelp-purge-param-forcelinkupdate": "Werk de koppelingstabellen bij.",
        "apihelp-purge-param-forcerecursivelinkupdate": "Werk de koppelingentabel bij, en werk de koppelingstabellen bij voor alle pagina's die deze pagina als sjabloon gebruiken.",
+       "apihelp-query+allcategories-summary": "Alle categorieën doorlopen.",
        "apihelp-query+allcategories-param-dir": "Richting om in te sorteren.",
        "apihelp-query+allcategories-param-limit": "Hoeveel categorieën te tonen.",
        "apihelp-query+allcategories-paramvalue-prop-size": "Voegt het aantal pagina's in de categorie toe.",
        "apihelp-query+allpages-param-limit": "Het totaal aantal pagina's dat getoont moeten worden.",
        "apihelp-query+allredirects-summary": "Toon alle doorverwijzingen naar een naamruimte.",
        "apihelp-query+allredirects-paramvalue-prop-title": "Voegt de titel van de doorverwijzing toe.",
+       "apihelp-query+allredirects-param-limit": "Hoeveel items er in totaal moeten worden getoond.",
        "apihelp-query+allrevisions-summary": "Toon alle versies.",
+       "apihelp-query+allrevisions-param-namespace": "Alleen pagina's in deze naamruimte weergeven.",
        "apihelp-query+allrevisions-example-user": "Toon de laatste 50 bijdragen van gebruiker <kbd>Example</kbd>.",
        "apihelp-query+mystashedfiles-paramvalue-prop-type": "Vraag het MIME- en mediatype van het bestand op.",
        "apihelp-query+mystashedfiles-param-limit": "Hoeveel bestanden te tonen.",
        "apihelp-query+alltransclusions-param-namespace": "De door te lopen naamruimte.",
+       "apihelp-query+alltransclusions-param-limit": "Hoeveel items er in totaal moeten worden getoond.",
        "apihelp-query+allusers-param-dir": "Richting om in te sorteren.",
        "apihelp-query+allusers-param-excludegroup": "Sluit gebruikers in de gegeven groepen uit.",
        "apihelp-query+allusers-paramvalue-prop-blockinfo": "Voegt informatie over een actuele blokkade van de gebruiker toe.",
        "apihelp-query+categorymembers-param-dir": "Richting om in te sorteren.",
        "apihelp-query+categorymembers-example-simple": "Toon de eerste 10 pagina's in <kbd>Category:Physics</kbd>.",
        "apihelp-query+deletedrevisions-param-tag": "Alleen revisies met dit label weergeven.",
+       "apihelp-query+deletedrevs-paraminfo-modes": "{{PLURAL:$1|Modus|Modi}}: $2",
        "apihelp-query+deletedrevs-param-tag": "Alleen revisies met dit label weergeven.",
        "apihelp-query+embeddedin-param-namespace": "De door te lopen naamruimte.",
+       "apihelp-query+filearchive-example-simple": "Toon een lijst met alle verwijderde bestanden.",
        "apihelp-query+fileusage-paramvalue-prop-pageid": "Pagina-ID van elke pagina.",
        "apihelp-query+fileusage-paramvalue-prop-title": "Titel van elke pagina.",
+       "apihelp-query+imageinfo-paramvalue-prop-mediatype": "Voegt het mediatype van het bestand toe.",
        "apihelp-query+imageusage-param-namespace": "De door te lopen naamruimte.",
        "apihelp-query+imageusage-example-simple": "Toon pagina's die [[:File:Albert Einstein Head.jpg]] gebruiken.",
        "apihelp-query+imageusage-example-generator": "Toon informatie over pagina's die [[:File:Albert Einstein Head.jpg]] gebruiken.",
        "apihelp-query+iwbacklinks-param-prefix": "Voorvoegsel voor de interwiki.",
+       "apihelp-query+iwlinks-paramvalue-prop-url": "Voegt de volledige URL toe.",
        "apihelp-query+langbacklinks-example-simple": "Toon de pagina's die verwijzen naar [[:fr:Test]].",
+       "apihelp-query+langlinks-paramvalue-prop-url": "Voegt de volledige URL toe.",
        "apihelp-query+linkshere-paramvalue-prop-pageid": "Pagina-ID van elke pagina.",
        "apihelp-query+linkshere-paramvalue-prop-title": "Titel van elke pagina.",
        "apihelp-query+linkshere-param-namespace": "Toon alleen pagina's in deze naamruimten.",
index 59616c0..3039ef4 100644 (file)
@@ -16,7 +16,7 @@
                        "Choomaq"
                ]
        },
-       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Документація]]\n* [[mw:Special:MyLanguage/API:FAQ|ЧаПи]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Список розсилки]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Оголошення API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Баґи і запити]\n</div>\n<strong>Статус:</strong> Усі функції, вказані на цій сторінці, мають працювати, але API далі перебуває в активній розробці і може змінитися у будь-який момент. Підпишіться на [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ список розсилки mediawiki-api-announce], щоб помічати оновлення.\n\n<strong>Хибні запити:</strong> Коли до API надсилаються хибні запити, буде відіслано HTTP-шапку з ключем «MediaWiki-API-Error», а тоді і значення шапки, і код помилки, надіслані назад, будуть встановлені з тим же значенням. Більше інформації див. на [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Errors and warnings]].\n\n<strong>Тестування:</strong> Для зручності тестування запитів API, див. [[Special:ApiSandbox]].",
+       "apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Документація]]\n* [[mw:Special:MyLanguage/API:FAQ|ЧаПи]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Список розсилки]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Оголошення API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Баґи і запити]\n</div>\n<strong>Статус:</strong> Усі функції, вказані на цій сторінці, мають працювати, але API далі перебуває в активній розробці і може змінитися у будь-який момент. Підпишіться на [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ список розсилки mediawiki-api-announce], щоб помічати оновлення.\n\n<strong>Хибні запити:</strong> Коли до API надсилаються хибні запити, буде відіслано HTTP-шапку з ключем «MediaWiki-API-Error», а тоді і значення шапки, і код помилки, надіслані назад, будуть встановлені з тим же значенням. Більше інформації див. на сторінці [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Помилки й попередження]].\n\n<p class=\"mw-apisandbox-link\"><strong>Тестування:</strong> Для зручності тестування запитів API, див. [[Special:ApiSandbox]].</p>",
        "apihelp-main-param-action": "Яку дію виконати.",
        "apihelp-main-param-format": "Формат виводу.",
        "apihelp-main-param-maxlag": "Максимальна затримка може використовуватися, коли MediaWiki інстальовано на реплікований кластер бази даних. Щоб зберегти дії, які спричиняють більшу затримку реплікації, цей параметр може змусити клієнт почекати, поки затримка реплікації не буде меншою за вказане значення. У випадку непомірної затримки, видається код помилки <samp>maxlag</samp> з повідомленням на зразок <samp>Очікування на $host: $lag секунд(и) затримки</samp>.<br />Див. [[mw:Special:MyLanguage/Manual:Maxlag_parameter|Manual: Maxlag parameter]] для детальнішої інформації.",
@@ -67,6 +67,7 @@
        "apihelp-compare-param-fromid": "Перший ID сторінки для порівняння.",
        "apihelp-compare-param-fromrev": "Перша версія для порівняння.",
        "apihelp-compare-param-fromtext": "Використати цей текст замість контенту версії, вказаної через <var>fromtitle</var>, <var>fromid</var> або <var>fromrev</var>.",
+       "apihelp-compare-param-fromsection": "Використовувати лише вказану секцію із заданого вмісту «from».",
        "apihelp-compare-param-frompst": "Зробити трансформацію перед збереженням на <var>fromtext</var>.",
        "apihelp-compare-param-fromcontentmodel": "Контентна модель <var>fromtext</var>. Якщо не вказано, буде використано припущення на основі інших параметрів.",
        "apihelp-compare-param-fromcontentformat": "Формат серіалізації контенту <var>fromtext</var>.",
@@ -75,6 +76,7 @@
        "apihelp-compare-param-torev": "Друга версія для порівняння.",
        "apihelp-compare-param-torelative": "Використати версію, яка стосується версії, визначеної через <var>fromtitle</var>, <var>fromid</var> або <var>fromrev</var>. Усі інші опції 'to' буде проігноровано.",
        "apihelp-compare-param-totext": "Використати цей текст замість контенту версії, вказаної через <var>totitle</var>, <var>toid</var> або <var>torev</var>.",
+       "apihelp-compare-param-tosection": "Використовувати лише вказану секцію із заданого вмісту «to».",
        "apihelp-compare-param-topst": "Виконати трансформацію перед збереженням на <var>totext</var>.",
        "apihelp-compare-param-tocontentmodel": "Контентна модель <var>totext</var>. Якщо не вказано, буде використано припущення на основі інших параметрів.",
        "apihelp-compare-param-tocontentformat": "Формат серіалізації контенту <var>totext</var>.",
        "apihelp-import-extended-description": "Зважте, що HTTP POST має бути виконано як завантаження файлу (тобто з використанням даних різних частин/форм) під час надсилання файлу для параметра <var>xml</var>.",
        "apihelp-import-param-summary": "Підсумок імпорту записів журналу.",
        "apihelp-import-param-xml": "Завантажено XML-файл.",
+       "apihelp-import-param-interwikiprefix": "Для завантажених імпортів: префікс інтервікі, який слід додавати до невідомих імен користувачів (а також відомих користувачів, якщо задано <var>$1assignknownusers</var>).",
+       "apihelp-import-param-assignknownusers": "Прив'язувати редагування до локальних користувачів, якщо користувач із таким іменем існує локально.",
        "apihelp-import-param-interwikisource": "Для інтервікі-імпорту: вікі, з якої імпортувати.",
        "apihelp-import-param-interwikipage": "Для інтервікі-імпорту: сторінки для імпорту.",
        "apihelp-import-param-fullhistory": "Для інтервікі-імпорту: імпортувати повну історію, не лише поточну версію.",
        "apihelp-opensearch-summary": "Шукати у вікі з використанням протоколу OpenSearch.",
        "apihelp-opensearch-param-search": "Рядок пошуку.",
        "apihelp-opensearch-param-limit": "Максимальна кількість результатів для виведення.",
-       "apihelp-opensearch-param-namespace": "Простори назв, у яких шукати.",
+       "apihelp-opensearch-param-namespace": "Простори назв, у яких шукати. Ігнорується, якщо <var>$1search</var> починається з валідного префікса простору назв.",
        "apihelp-opensearch-param-suggest": "Нічого не робити, якщо <var>[[mw:Special:MyLanguage/Manual:$wgEnableOpenSearchSuggest|$wgEnableOpenSearchSuggest]]</var> хибний.",
        "apihelp-opensearch-param-redirects": "Як обробляти перенаправлення:\n;return:Видати саме перенаправлення.\n;resolve:Видати цільову сторінку. Може видати менше, ніж $1limit результат{{PLURAL:$1limit||и|ів}}.\nЗ історичних причин, за замовчуванням стоїть «return» для $1format=json і «resolve» — для інших форматів.",
        "apihelp-opensearch-param-format": "Формат виводу.",
        "apihelp-parse-param-disablepp": "Використати натомість <var>$1disablelimitreport</var>.",
        "apihelp-parse-param-disableeditsection": "Пропустити посилання на редагування розділів на виході аналізу.",
        "apihelp-parse-param-disabletidy": "Не запускайте очищення HTML (e.g. tidy) на виході аналізу.",
+       "apihelp-parse-param-disablestylededuplication": "Не усувайте дублікати вбудованих таблиць стилів на виході парсера.",
        "apihelp-parse-param-generatexml": "Генерувати синтаксичне дерево XML (передбачає модель вмісту <code>$1</code>; замінено на <kbd>$2prop=parsetree</kbd>).",
        "apihelp-parse-param-preview": "Аналізувати у режимі попереднього перегляду.",
        "apihelp-parse-param-sectionpreview": "Аналізувати у режимі попереднього перегляду розділу (також вмикає попередній перегляд).",
        "apihelp-query+filearchive-param-dir": "Напрямок, у якому перелічити.",
        "apihelp-query+filearchive-param-sha1": "SHA1-хеш зображення. Перевизначає $1sha1base36.",
        "apihelp-query+filearchive-param-sha1base36": "SHA1-хеш зображення у base 36 (використано в MediaWiki).",
+       "apihelp-query+filearchive-param-prop": "Інформацію про яке зображення слід отримати:",
        "apihelp-query+filearchive-paramvalue-prop-sha1": "Додає хеш SHA-1 до зображення.",
        "apihelp-query+filearchive-paramvalue-prop-timestamp": "Додає часову мітку завантаженої версії.",
        "apihelp-query+filearchive-paramvalue-prop-user": "Додає користувача, який завантажив версію зображення.",
        "apihelp-query+filearchive-paramvalue-prop-archivename": "Додає до імені версію архіву для неостаточного варіанту файлу.",
        "apihelp-query+filearchive-example-simple": "Показати список усіх вилучених файлів.",
        "apihelp-query+filerepoinfo-summary": "Видати мета-інформацію про репозиторії зображень, налаштовані на вікі.",
-       "apihelp-query+filerepoinfo-param-prop": "Які властивості репозиторію отримати (на деяких вікі може бути більше):\n;apiurl:URL до репозиторію API — корисне для отримання інформації про зображення з хосту.\n;name:Ключ репозиторію — використано в e.g. <var>[[mw:Special:MyLanguage/Manual:$wgForeignFileRepos|$wgForeignFileRepos]]</var> і значення [[Special:ApiHelp/query+imageinfo|imageinfo]].\n;displayname:Людиночита назва репозиторію вікі.\n;rooturl:Корінний URL для шляху зображення.\n;local:Чи репозиторій локальний, чи ні.",
+       "apihelp-query+filerepoinfo-param-prop": "Які властивості репозиторію отримати (доступні властивості можуть варіюватися в інших вікі).",
+       "apihelp-query+filerepoinfo-paramvalue-prop-apiurl": "URL-адреса API репозиторію — корисно для отримання інформації про зображення від хоста.",
+       "apihelp-query+filerepoinfo-paramvalue-prop-articlepath": "<var>[[mw:Special:MyLanguage/Manual:$wgArticlePath|$wgArticlePath]]</var> вікі-сайту репозиторію, або еквівалент.",
+       "apihelp-query+filerepoinfo-paramvalue-prop-canUpload": "Чи можна завантажувати файли в цей репозиторій, напр., через CORS та спільну автентифікацію.",
+       "apihelp-query+filerepoinfo-paramvalue-prop-displayname": "Назва вікі-сайту репозиторію, читабельна для людини.",
        "apihelp-query+filerepoinfo-example-simple": "Отримати інформацію про репозиторії файлів.",
        "apihelp-query+fileusage-summary": "Знайти всі сторінки, що використовують дані файли.",
        "apihelp-query+fileusage-param-prop": "Які властивості отримати:",
index f5bfc2a..3d26767 100644 (file)
@@ -113,7 +113,7 @@ abstract class AbstractPasswordPrimaryAuthenticationProvider
         *
         * @param string $username
         * @param Status $status From $this->checkPasswordValidity()
-        * @param mixed $data Passed through to $this->getPasswordResetData()
+        * @param mixed|null $data Passed through to $this->getPasswordResetData()
         */
        protected function setPasswordResetFlag( $username, Status $status, $data = null ) {
                $reset = $this->getPasswordResetData( $username, $data );
index 3260ce4..ca59ec7 100644 (file)
@@ -2249,7 +2249,7 @@ class AuthManager implements LoggerAwareInterface {
         * Fetch authentication data from the current session
         * @protected For use by AuthenticationProviders
         * @param string $key
-        * @param mixed $default
+        * @param mixed|null $default
         * @return mixed
         */
        public function getAuthenticationSessionData( $key, $default = null ) {
index 956c985..8cb33aa 100644 (file)
@@ -199,7 +199,7 @@ class AuthenticationResponse {
        /**
         * @param AuthenticationRequest[] $reqs AuthenticationRequests needed to continue
         * @param string $redirectTarget URL
-        * @param mixed $redirectApiData Data suitable for adding to an ApiResult
+        * @param mixed|null $redirectApiData Data suitable for adding to an ApiResult
         * @return AuthenticationResponse
         * @see AuthenticationResponse::REDIRECT
         */
index 717b592..e6d9bd8 100644 (file)
@@ -52,7 +52,7 @@ class Throttler implements LoggerAwareInterface {
        protected $warningLimit;
 
        /**
-        * @param array $conditions An array of arrays describing throttling conditions.
+        * @param array|null $conditions An array of arrays describing throttling conditions.
         *     Defaults to $wgPasswordAttemptThrottle. See documentation of that variable for format.
         * @param array $params Parameters (all optional):
         *   - type: throttle type, used as a namespace for counters,
index 7cbb86f..58ed771 100644 (file)
@@ -75,7 +75,7 @@ class LinkCache {
         * in order to avoid link table inconsistency), which was later removed
         * for performance on wikis with a high edit rate.
         *
-        * @param bool $update
+        * @param bool|null $update
         * @return bool
         */
        public function forUpdate( $update = null ) {
@@ -125,7 +125,7 @@ class LinkCache {
         * @param int $id Page's ID
         * @param LinkTarget $target
         * @param int $len Text's length
-        * @param int $redir Whether the page is a redirect
+        * @param int|null $redir Whether the page is a redirect
         * @param int $revision Latest revision's ID
         * @param string|null $model Latest revision's content model ID
         * @param string|null $lang Language code of the page, if not the content language
index b262eab..65e659d 100644 (file)
@@ -50,8 +50,8 @@ class MessageBlobStore implements LoggerAwareInterface {
        protected $wanCache;
 
        /**
-        * @param ResourceLoader $rl
-        * @param LoggerInterface $logger
+        * @param ResourceLoader|null $rl
+        * @param LoggerInterface|null $logger
         */
        public function __construct( ResourceLoader $rl = null, LoggerInterface $logger = null ) {
                $this->resourceloader = $rl;
index 0854a43..78fb24a 100644 (file)
@@ -46,14 +46,11 @@ class MessageCache {
        const LOCK_TTL = 30;
 
        /**
-        * Process local cache of loaded messages that are defined in
-        * MediaWiki namespace. First array level is a language code,
-        * second level is message key and the values are either message
-        * content prefixed with space, or !NONEXISTENT for negative
-        * caching.
-        * @var array $mCache
+        * Process cache of loaded messages that are defined in MediaWiki namespace
+        *
+        * @var MapCacheLRU Map of (language code => key => " <MESSAGE>" or "!TOO BIG")
         */
-       protected $mCache;
+       protected $cache;
 
        /**
         * @var bool[] Map of (language code => boolean)
@@ -80,12 +77,6 @@ class MessageCache {
        /** @var Parser */
        protected $mParser;
 
-       /**
-        * Variable for tracking which variables are already loaded
-        * @var array $mLoadedLanguages
-        */
-       protected $mLoadedLanguages = [];
-
        /**
         * @var bool $mInParser
         */
@@ -174,6 +165,8 @@ class MessageCache {
                $this->clusterCache = $clusterCache;
                $this->srvCache = $serverCache;
 
+               $this->cache = new MapCacheLRU( 5 ); // limit size for sanity
+
                $this->mDisable = !$useDB;
                $this->mExpiry = $expiry;
        }
@@ -246,8 +239,8 @@ class MessageCache {
         * is disabled.
         *
         * @param string $code Language to which load messages
-        * @param int $mode Use MessageCache::FOR_UPDATE to skip process cache [optional]
-        * @throws MWException
+        * @param int|null $mode Use MessageCache::FOR_UPDATE to skip process cache [optional]
+        * @throws InvalidArgumentException
         * @return bool
         */
        protected function load( $code, $mode = null ) {
@@ -256,7 +249,7 @@ class MessageCache {
                }
 
                # Don't do double loading...
-               if ( isset( $this->mLoadedLanguages[$code] ) && $mode != self::FOR_UPDATE ) {
+               if ( $this->cache->has( $code ) && $mode != self::FOR_UPDATE ) {
                        return true;
                }
 
@@ -296,8 +289,8 @@ class MessageCache {
                        $staleCache = $cache;
                } else {
                        $where[] = 'got from local cache';
+                       $this->cache->set( $code, $cache );
                        $success = true;
-                       $this->mCache[$code] = $cache;
                }
 
                if ( !$success ) {
@@ -326,7 +319,7 @@ class MessageCache {
                                                $staleCache = $cache;
                                        } else {
                                                $where[] = 'got from global cache';
-                                               $this->mCache[$code] = $cache;
+                                               $this->cache->set( $code, $cache );
                                                $this->saveToCaches( $cache, 'local-only', $code );
                                                $success = true;
                                        }
@@ -347,7 +340,7 @@ class MessageCache {
                                } elseif ( $staleCache ) {
                                        # Use the stale cache while some other thread constructs the new one
                                        $where[] = 'using stale cache';
-                                       $this->mCache[$code] = $staleCache;
+                                       $this->cache->set( $code, $staleCache );
                                        $success = true;
                                        break;
                                } elseif ( $failedAttempts > 0 ) {
@@ -371,13 +364,14 @@ class MessageCache {
                if ( !$success ) {
                        $where[] = 'loading FAILED - cache is disabled';
                        $this->mDisable = true;
-                       $this->mCache = false;
+                       $this->cache->set( $code, null );
                        wfDebugLog( 'MessageCacheError', __METHOD__ . ": Failed to load $code\n" );
                        # This used to throw an exception, but that led to nasty side effects like
                        # the whole wiki being instantly down if the memcached server died
-               } else {
-                       # All good, just record the success
-                       $this->mLoadedLanguages[$code] = true;
+               }
+
+               if ( !$this->cache->has( $code ) ) { // sanity
+                       throw new LogicException( "Process cache for '$code' should be set by now." );
                }
 
                $info = implode( ', ', $where );
@@ -389,7 +383,7 @@ class MessageCache {
        /**
         * @param string $code
         * @param array &$where List of wfDebug() comments
-        * @param int $mode Use MessageCache::FOR_UPDATE to use DB_MASTER
+        * @param int|null $mode Use MessageCache::FOR_UPDATE to use DB_MASTER
         * @return bool|string True on success or one of ("cantacquire", "disabled")
         */
        protected function loadFromDBWithLock( $code, array &$where, $mode = null ) {
@@ -417,7 +411,7 @@ class MessageCache {
                }
 
                $cache = $this->loadFromDB( $code, $mode );
-               $this->mCache[$code] = $cache;
+               $this->cache->set( $code, $cache );
                $saveSuccess = $this->saveToCaches( $cache, 'all', $code );
 
                if ( !$saveSuccess ) {
@@ -451,7 +445,7 @@ class MessageCache {
         * on-demand from the database later.
         *
         * @param string $code Language code
-        * @param int $mode Use MessageCache::FOR_UPDATE to skip process cache
+        * @param int|null $mode Use MessageCache::FOR_UPDATE to skip process cache
         * @return array Loaded messages for storing in caches
         */
        protected function loadFromDB( $code, $mode = null ) {
@@ -473,10 +467,10 @@ class MessageCache {
 
                $mostused = [];
                if ( $wgAdaptiveMessageCache && $code !== $wgLanguageCode ) {
-                       if ( !isset( $this->mCache[$wgLanguageCode] ) ) {
+                       if ( !$this->cache->has( $wgLanguageCode ) ) {
                                $this->load( $wgLanguageCode );
                        }
-                       $mostused = array_keys( $this->mCache[$wgLanguageCode] );
+                       $mostused = array_keys( $this->cache->get( $wgLanguageCode ) );
                        foreach ( $mostused as $key => $value ) {
                                $mostused[$key] = "$value/$code";
                        }
@@ -578,10 +572,10 @@ class MessageCache {
                // (a) Update the process cache with the new message text
                if ( $text === false ) {
                        // Page deleted
-                       $this->mCache[$code][$title] = '!NONEXISTENT';
+                       $this->cache->setField( $code, $title, '!NONEXISTENT' );
                } else {
                        // Ignore $wgMaxMsgCacheEntrySize so the process cache is up to date
-                       $this->mCache[$code][$title] = ' ' . $text;
+                       $this->cache->setField( $code, $title, ' ' . $text );
                }
 
                // (b) Update the shared caches in a deferred update with a fresh DB snapshot
@@ -600,24 +594,27 @@ class MessageCache {
                                }
                                // Load the messages from the master DB to avoid race conditions
                                $cache = $this->loadFromDB( $code, self::FOR_UPDATE );
-                               $this->mCache[$code] = $cache;
-                               // Load the process cache values and set the per-title cache keys
+                               // Check if an individual cache key should exist and update cache accordingly
                                $page = WikiPage::factory( Title::makeTitle( NS_MEDIAWIKI, $title ) );
                                $page->loadPageData( $page::READ_LATEST );
                                $text = $this->getMessageTextFromContent( $page->getContent() );
-                               // Check if an individual cache key should exist and update cache accordingly
                                if ( is_string( $text ) && strlen( $text ) > $wgMaxMsgCacheEntrySize ) {
-                                       $titleKey = $this->bigMessageCacheKey( $this->mCache[$code]['HASH'], $title );
-                                       $this->wanCache->set( $titleKey, ' ' . $text, $this->mExpiry );
+                                       $this->wanCache->set(
+                                               $this->bigMessageCacheKey( $cache['HASH'], $title ),
+                                               ' ' . $text,
+                                               $this->mExpiry
+                                       );
                                }
                                // Mark this cache as definitely being "latest" (non-volatile) so
-                               // load() calls do try to refresh the cache with replica DB data
-                               $this->mCache[$code]['LATEST'] = time();
+                               // load() calls do not try to refresh the cache with replica DB data
+                               $cache['LATEST'] = time();
+                               // Update the process cache
+                               $this->cache->set( $code, $cache );
                                // Pre-emptively update the local datacenter cache so things like edit filter and
                                // blacklist changes are reflected immediately; these often use MediaWiki: pages.
                                // The datacenter handling replace() calls should be the same one handling edits
                                // as they require HTTP POST.
-                               $this->saveToCaches( $this->mCache[$code], 'all', $code );
+                               $this->saveToCaches( $cache, 'all', $code );
                                // Release the lock now that the cache is saved
                                ScopedCallback::consume( $scopedLock );
 
@@ -971,8 +968,8 @@ class MessageCache {
        public function getMsgFromNamespace( $title, $code ) {
                $this->load( $code );
 
-               if ( isset( $this->mCache[$code][$title] ) ) {
-                       $entry = $this->mCache[$code][$title];
+               if ( $this->cache->hasField( $code, $title ) ) {
+                       $entry = $this->cache->getField( $code, $title );
                        if ( substr( $entry, 0, 1 ) === ' ' ) {
                                // The message exists and is not '!TOO BIG'
                                return (string)substr( $entry, 1 );
@@ -984,40 +981,40 @@ class MessageCache {
                        $message = false;
                        Hooks::run( 'MessagesPreLoad', [ $title, &$message, $code ] );
                        if ( $message !== false ) {
-                               $this->mCache[$code][$title] = ' ' . $message;
+                               $this->cache->setField( $code, $title, ' ' . $message );
                        } else {
-                               $this->mCache[$code][$title] = '!NONEXISTENT';
+                               $this->cache->setField( $code, $title, '!NONEXISTENT' );
                        }
 
                        return $message;
                }
 
                // Individual message cache key
-               $titleKey = $this->bigMessageCacheKey( $this->mCache[$code]['HASH'], $title );
+               $bigKey = $this->bigMessageCacheKey( $this->cache->getField( $code, 'HASH' ), $title );
 
                if ( $this->mCacheVolatile[$code] ) {
                        $entry = false;
                        // Make sure that individual keys respect the WAN cache holdoff period too
                        LoggerFactory::getInstance( 'MessageCache' )->debug(
                                __METHOD__ . ': loading volatile key \'{titleKey}\'',
-                               [ 'titleKey' => $titleKey, 'code' => $code ] );
+                               [ 'titleKey' => $bigKey, 'code' => $code ] );
                } else {
                        // Try the individual message cache
-                       $entry = $this->wanCache->get( $titleKey );
+                       $entry = $this->wanCache->get( $bigKey );
                }
 
                if ( $entry !== false ) {
                        if ( substr( $entry, 0, 1 ) === ' ' ) {
-                               $this->mCache[$code][$title] = $entry;
+                               $this->cache->setField( $code, $title, $entry );
                                // The message exists, so make sure a string is returned
                                return (string)substr( $entry, 1 );
                        } elseif ( $entry === '!NONEXISTENT' ) {
-                               $this->mCache[$code][$title] = '!NONEXISTENT';
+                               $this->cache->setField( $code, $title, '!NONEXISTENT' );
 
                                return false;
                        } else {
                                // Corrupt/obsolete entry, delete it
-                               $this->wanCache->delete( $titleKey );
+                               $this->wanCache->delete( $bigKey );
                        }
                }
 
@@ -1040,14 +1037,14 @@ class MessageCache {
                        if ( $content ) {
                                $message = $this->getMessageTextFromContent( $content );
                                if ( is_string( $message ) ) {
-                                       $this->mCache[$code][$title] = ' ' . $message;
-                                       $this->wanCache->set( $titleKey, ' ' . $message, $this->mExpiry, $cacheOpts );
+                                       $this->cache->setField( $code, $title, ' ' . $message );
+                                       $this->wanCache->set( $bigKey, ' ' . $message, $this->mExpiry, $cacheOpts );
                                }
                        } else {
                                // A possibly temporary loading failure
                                LoggerFactory::getInstance( 'MessageCache' )->warning(
                                        __METHOD__ . ': failed to load message page text for \'{titleKey}\'',
-                                       [ 'titleKey' => $titleKey, 'code' => $code ] );
+                                       [ 'titleKey' => $bigKey, 'code' => $code ] );
                                $message = null; // no negative caching
                        }
                } else {
@@ -1056,8 +1053,8 @@ class MessageCache {
 
                if ( $message === false ) {
                        // Negative caching in case a "too big" message is no longer available (deleted)
-                       $this->mCache[$code][$title] = '!NONEXISTENT';
-                       $this->wanCache->set( $titleKey, '!NONEXISTENT', $this->mExpiry, $cacheOpts );
+                       $this->cache->setField( $code, $title, '!NONEXISTENT' );
+                       $this->wanCache->set( $bigKey, '!NONEXISTENT', $this->mExpiry, $cacheOpts );
                }
 
                return $message;
@@ -1066,8 +1063,8 @@ class MessageCache {
        /**
         * @param string $message
         * @param bool $interface
-        * @param Language $language
-        * @param Title $title
+        * @param Language|null $language
+        * @param Title|null $title
         * @return string
         */
        public function transform( $message, $interface = false, $language = null, $title = null ) {
@@ -1120,10 +1117,10 @@ class MessageCache {
 
        /**
         * @param string $text
-        * @param Title $title
+        * @param Title|null $title
         * @param bool $linestart Whether or not this is at the start of a line
         * @param bool $interface Whether this is an interface message
-        * @param Language|string $language Language code
+        * @param Language|string|null $language Language code
         * @return ParserOutput|string
         */
        public function parse( $text, $title = null, $linestart = true,
@@ -1192,13 +1189,12 @@ class MessageCache {
         *
         * Mainly used after a mass rebuild
         */
-       function clear() {
+       public function clear() {
                $langs = Language::fetchLanguageNames( null, 'mw' );
                foreach ( array_keys( $langs ) as $code ) {
                        $this->wanCache->touchCheckKey( $this->getCheckKey( $code ) );
                }
-
-               $this->mLoadedLanguages = [];
+               $this->cache->clear();
        }
 
        /**
@@ -1235,12 +1231,12 @@ class MessageCache {
                global $wgContLang;
 
                $this->load( $code );
-               if ( !isset( $this->mCache[$code] ) ) {
+               if ( !$this->cache->has( $code ) ) {
                        // Apparently load() failed
                        return null;
                }
                // Remove administrative keys
-               $cache = $this->mCache[$code];
+               $cache = $this->cache->get( $code );
                unset( $cache['VERSION'] );
                unset( $cache['EXPIRY'] );
                unset( $cache['EXCESSIVE'] );
index a49fb4c..e745203 100644 (file)
@@ -59,7 +59,7 @@ class CategoryMembershipChange {
 
        /**
         * @param Title $pageTitle Title instance of the categorized page
-        * @param Revision $revision Latest Revision instance of the categorized page
+        * @param Revision|null $revision Latest Revision instance of the categorized page
         *
         * @throws MWException
         */
@@ -142,7 +142,7 @@ class CategoryMembershipChange {
        /**
         * @param string $timestamp Timestamp of the recent change to occur in TS_MW format
         * @param Title $categoryTitle Title of the category a page is being added to or removed from
-        * @param User $user User object of the user that made the change
+        * @param User|null $user User object of the user that made the change
         * @param string $comment Change summary
         * @param Title $pageTitle Title of the page that is being added or removed
         * @param string $lastTimestamp Parent revision timestamp of this change in TS_MW format
index 57c4026..c0822c3 100644 (file)
@@ -107,7 +107,7 @@ class ChangesList extends ContextSource {
         *
         * @param RecentChange &$rc Passed by reference
         * @param bool $watched (default false)
-        * @param int $linenumber (default null)
+        * @param int|null $linenumber (default null)
         *
         * @return string|bool
         */
@@ -249,7 +249,7 @@ class ChangesList extends ContextSource {
         * bot edit, or unpatrolled edit. In English it typically contains "N", "m", "b", or "!".
         *
         * @param string $flag One key of $wgRecentChangesFlags
-        * @param IContextSource $context
+        * @param IContextSource|null $context
         * @return string HTML
         */
        public static function flag( $flag, IContextSource $context = null ) {
@@ -310,7 +310,7 @@ class ChangesList extends ContextSource {
         *
         * @param int $old Number of bytes
         * @param int $new Number of bytes
-        * @param IContextSource $context
+        * @param IContextSource|null $context
         * @return string
         */
        public static function showCharacterDifference( $old, $new, IContextSource $context = null ) {
@@ -363,7 +363,7 @@ class ChangesList extends ContextSource {
         * Format the character difference of one or several changes.
         *
         * @param RecentChange $old
-        * @param RecentChange $new Last change to use, if not provided, $old will be used
+        * @param RecentChange|null $new Last change to use, if not provided, $old will be used
         * @return string HTML fragment
         */
        public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) {
@@ -628,7 +628,7 @@ class ChangesList extends ContextSource {
         * field of this revision, if it's marked as deleted.
         * @param RCCacheEntry|RecentChange $rc
         * @param int $field
-        * @param User $user User object to check, or null to use $wgUser
+        * @param User|null $user User object to check, or null to use $wgUser
         * @return bool
         */
        public static function userCan( $rc, $field, User $user = null ) {
@@ -652,7 +652,8 @@ class ChangesList extends ContextSource {
                }
        }
 
-       /** Inserts a rollback link
+       /**
+        * Insert a rollback link
         *
         * @param string &$s
         * @param RecentChange &$rc
@@ -661,15 +662,14 @@ class ChangesList extends ContextSource {
                if ( $rc->mAttribs['rc_type'] == RC_EDIT
                        && $rc->mAttribs['rc_this_oldid']
                        && $rc->mAttribs['rc_cur_id']
+                       && $rc->getAttribute( 'page_latest' ) == $rc->mAttribs['rc_this_oldid']
                ) {
-                       $page = $rc->getTitle();
-                       /** Check for rollback and edit permissions, disallow special pages, and only
+                       $title = $rc->getTitle();
+                       /** Check for rollback permissions, disallow special pages, and only
                         * show a link on the top-most revision */
-                       if ( $this->getUser()->isAllowed( 'rollback' )
-                               && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid']
-                       ) {
+                       if ( $title->quickUserCan( 'rollback', $this->getUser() ) ) {
                                $rev = new Revision( [
-                                       'title' => $page,
+                                       'title' => $title,
                                        'id' => $rc->mAttribs['rc_this_oldid'],
                                        'user' => $rc->mAttribs['rc_user'],
                                        'user_text' => $rc->mAttribs['rc_user_text'],
index ada02ce..a593d27 100644 (file)
@@ -93,7 +93,7 @@ class EnhancedChangesList extends ChangesList {
         *
         * @param RecentChange &$rc
         * @param bool $watched
-        * @param int $linenumber (default null)
+        * @param int|null $linenumber (default null)
         *
         * @return string
         */
index 51ee481..7ba12fb 100644 (file)
@@ -27,7 +27,7 @@ class OldChangesList extends ChangesList {
         *
         * @param RecentChange &$rc Passed by reference
         * @param bool $watched (default false)
-        * @param int $linenumber (default null)
+        * @param int|null $linenumber (default null)
         *
         * @return string|bool
         */
index 904090f..819f170 100644 (file)
@@ -490,7 +490,7 @@ class RecentChange {
 
        /**
         * Notify all the feeds about the change.
-        * @param array $feeds Optional feeds to send to, defaults to $wgRCFeeds
+        * @param array|null $feeds Optional feeds to send to, defaults to $wgRCFeeds
         */
        public function notifyRCFeeds( array $feeds = null ) {
                global $wgRCFeeds;
@@ -556,7 +556,7 @@ class RecentChange {
         *
         * @param RecentChange|int $change RecentChange or corresponding rc_id
         * @param bool $auto For automatic patrol
-        * @param string|string[] $tags Change tags to add to the patrol log entry
+        * @param string|string[]|null $tags Change tags to add to the patrol log entry
         *   ($user should be able to add the specified tags before this is called)
         * @return array See doMarkPatrolled(), or null if $change is not an existing rc_id
         */
@@ -581,7 +581,7 @@ class RecentChange {
         * 'markedaspatrollederror-noautopatrol' as errors
         * @param User $user User object doing the action
         * @param bool $auto For automatic patrol
-        * @param string|string[] $tags Change tags to add to the patrol log entry
+        * @param string|string[]|null $tags Change tags to add to the patrol log entry
         *   ($user should be able to add the specified tags before this is called)
         * @return array Array of permissions errors, see Title::getUserPermissionsErrors()
         */
@@ -932,7 +932,7 @@ class RecentChange {
         *
         * @param string $timestamp Timestamp of the recent change to occur
         * @param Title $categoryTitle Title of the category a page is being added to or removed from
-        * @param User $user User object of the user that made the change
+        * @param User|null $user User object of the user that made the change
         * @param string $comment Change summary
         * @param Title $pageTitle Title of the page that is being added or removed
         * @param int $oldRevId Parent revision ID of this change
@@ -941,7 +941,7 @@ class RecentChange {
         * @param bool $bot true, if the change was made by a bot
         * @param string $ip IP address of the user, if the change was made anonymously
         * @param int $deleted Indicates whether the change has been deleted
-        * @param bool $added true, if the category was added, false for removed
+        * @param bool|null $added true, if the category was added, false for removed
         *
         * @return RecentChange
         */
index 97f124b..7874640 100644 (file)
@@ -213,7 +213,7 @@ class ChangeTags {
         * @param int|null $rc_id The rc_id of the change to add the tags to
         * @param int|null $rev_id The rev_id of the change to add the tags to
         * @param int|null $log_id The log_id of the change to add the tags to
-        * @param string $params Params to put in the ct_params field of table 'change_tag'
+        * @param string|null $params Params to put in the ct_params field of table 'change_tag'
         * @param RecentChange|null $rc Recent change, in case the tagging accompanies the action
         * (this should normally be the case)
         *
@@ -244,7 +244,7 @@ class ChangeTags {
         * Pass a variable whose value is null if the rev_id is not relevant or unknown.
         * @param int|null &$log_id The log_id of the change to add the tags to.
         * Pass a variable whose value is null if the log_id is not relevant or unknown.
-        * @param string $params Params to put in the ct_params field of table
+        * @param string|null $params Params to put in the ct_params field of table
         * 'change_tag' when adding tags
         * @param RecentChange|null $rc Recent change being tagged, in case the tagging accompanies
         * the action
@@ -941,7 +941,7 @@ class ChangeTags {
         * @param string $tag
         * @param string $reason
         * @param User $user Who to attribute the action to
-        * @param int $tagCount For deletion only, how many usages the tag had before
+        * @param int|null $tagCount For deletion only, how many usages the tag had before
         * it was deleted.
         * @param array $logEntryTags Change tags to apply to the entry
         * that will be created in the tag management log
index b21eadc..284cab2 100644 (file)
@@ -43,7 +43,7 @@ abstract class AbstractContent implements Content {
        protected $model_id;
 
        /**
-        * @param string $modelId
+        * @param string|null $modelId
         *
         * @since 1.21
         */
@@ -145,7 +145,7 @@ abstract class AbstractContent implements Content {
        /**
         * @since 1.21
         *
-        * @param string $format
+        * @param string|null $format
         *
         * @return string
         *
@@ -182,7 +182,7 @@ abstract class AbstractContent implements Content {
        /**
         * @since 1.21
         *
-        * @param Content $that
+        * @param Content|null $that
         *
         * @return bool
         *
@@ -219,9 +219,9 @@ abstract class AbstractContent implements Content {
         * @since 1.21
         *
         * @param Title $title
-        * @param Content $old
+        * @param Content|null $old
         * @param bool $recursive
-        * @param ParserOutput $parserOutput
+        * @param ParserOutput|null $parserOutput
         *
         * @return DataUpdate[]
         *
index dfd46c8..e350fb3 100644 (file)
@@ -32,7 +32,7 @@ abstract class CodeContentHandler extends TextContentHandler {
         * Returns the English language, because code is English, and should be handled as such.
         *
         * @param Title $title
-        * @param Content $content
+        * @param Content|null $content
         *
         * @return Language
         *
@@ -46,7 +46,7 @@ abstract class CodeContentHandler extends TextContentHandler {
         * Returns the English language, because code is English, and should be handled as such.
         *
         * @param Title $title
-        * @param Content $content
+        * @param Content|null $content
         *
         * @return Language
         *
index 3e58782..000bff2 100644 (file)
@@ -162,7 +162,7 @@ interface Content {
         *
         * @since 1.21
         *
-        * @param string $format The desired serialization format, or null for the default format.
+        * @param string|null $format The desired serialization format, or null for the default format.
         *
         * @return string Serialized form of this Content object.
         */
@@ -210,7 +210,7 @@ interface Content {
         *
         * @since 1.21
         *
-        * @param Content $that The Content object to compare to.
+        * @param Content|null $that The Content object to compare to.
         *
         * @return bool True if this Content object is equal to $that, false otherwise.
         */
@@ -262,8 +262,8 @@ interface Content {
         *       may call ParserOutput::recordOption() on the output object.
         *
         * @param Title $title The page title to use as a context for rendering.
-        * @param int $revId Optional revision ID being rendered.
-        * @param ParserOptions $options Any parser options.
+        * @param int|null $revId Optional revision ID being rendered.
+        * @param ParserOptions|null $options Any parser options.
         * @param bool $generateHtml Whether to generate HTML (default: true). If false,
         *        the result of calling getText() on the ParserOutput object returned by
         *        this method is undefined.
@@ -296,12 +296,12 @@ interface Content {
         *   AbstractContent does.
         *
         * @param Title $title The context for determining the necessary updates
-        * @param Content $old An optional Content object representing the
+        * @param Content|null $old An optional Content object representing the
         *    previous content, i.e. the content being replaced by this Content
         *    object.
         * @param bool $recursive Whether to include recursive updates (default:
         *    false).
-        * @param ParserOutput $parserOutput Optional ParserOutput object.
+        * @param ParserOutput|null $parserOutput Optional ParserOutput object.
         *    Provide if you have one handy, to avoid re-parsing of the content.
         *
         * @return DataUpdate[] A list of DataUpdate objects for putting information
index 3b54494..3c5ee26 100644 (file)
@@ -70,7 +70,7 @@ abstract class ContentHandler {
         *
         * @since 1.21
         *
-        * @param Content $content
+        * @param Content|null $content
         *
         * @throws MWException If the content is not an instance of TextContent and
         * wgContentHandlerTextFallback was set to 'fail'.
@@ -115,11 +115,11 @@ abstract class ContentHandler {
         *
         * @param string $text The textual representation, will be
         *    unserialized to create the Content object
-        * @param Title $title The title of the page this text belongs to.
+        * @param Title|null $title The title of the page this text belongs to.
         *    Required if $modelId is not provided.
-        * @param string $modelId The model to deserialize to. If not provided,
+        * @param string|null $modelId The model to deserialize to. If not provided,
         *    $title->getContentModel() is used.
-        * @param string $format The format to use for deserialization. If not
+        * @param string|null $format The format to use for deserialization. If not
         *    given, the model's default format is used.
         *
         * @throws MWException If model ID or format is not supported or if the text can not be
@@ -420,7 +420,7 @@ abstract class ContentHandler {
         * @since 1.21
         *
         * @param Content $content The Content object to serialize
-        * @param string $format The desired serialization format
+        * @param string|null $format The desired serialization format
         *
         * @return string Serialized form of the content
         */
@@ -446,7 +446,7 @@ abstract class ContentHandler {
         * @since 1.21
         *
         * @param string $blob Serialized form of the content
-        * @param string $format The format used for serialization
+        * @param string|null $format The format used for serialization
         *
         * @return Content The Content object created by deserializing $blob
         */
@@ -654,7 +654,7 @@ abstract class ContentHandler {
         * @since 1.21
         *
         * @param Title $title The page to determine the language for.
-        * @param Content $content The page's content, if you have it handy, to avoid reloading it.
+        * @param Content|null $content The page's content, if you have it handy, to avoid reloading it.
         *
         * @return Language The page's language
         */
@@ -689,7 +689,7 @@ abstract class ContentHandler {
         * @since 1.21
         *
         * @param Title $title The page to determine the language for.
-        * @param Content $content The page's content, if you have it handy, to avoid reloading it.
+        * @param Content|null $content The page's content, if you have it handy, to avoid reloading it.
         *
         * @return Language The page's language for viewing
         */
@@ -1310,7 +1310,7 @@ abstract class ContentHandler {
         * Specific content handlers may override it if they need different content handling.
         *
         * @param WikiPage $page
-        * @param ParserCache $cache
+        * @param ParserCache|null $cache
         * @return ParserOutput
         */
        public function getParserOutputForIndexing( WikiPage $page, ParserCache $cache = null ) {
index 4b58989..b21c6f4 100644 (file)
@@ -42,7 +42,7 @@ class MessageContent extends AbstractContent {
 
        /**
         * @param Message|string $msg A Message object, or a message key.
-        * @param string[] $params An optional array of message parameters.
+        * @param string[]|null $params An optional array of message parameters.
         */
        public function __construct( $msg, $params = null ) {
                # XXX: messages may be wikitext, html or plain text! and maybe even something else entirely.
@@ -148,8 +148,8 @@ class MessageContent extends AbstractContent {
 
        /**
         * @param Title $title Unused.
-        * @param int $revId Unused.
-        * @param ParserOptions $options Unused.
+        * @param int|null $revId Unused.
+        * @param ParserOptions|null $options Unused.
         * @param bool $generateHtml Whether to generate HTML (default: true).
         *
         * @return ParserOutput
index 71f65b3..c058296 100644 (file)
@@ -75,7 +75,7 @@ class TextContent extends AbstractContent {
 
                $text = $this->getNativeData();
 
-               $truncatedtext = $wgContLang->truncate(
+               $truncatedtext = $wgContLang->truncateForDatabase(
                        preg_replace( "/[\n\r]/", ' ', $text ),
                        max( 0, $maxlength ) );
 
@@ -194,7 +194,7 @@ class TextContent extends AbstractContent {
         * @since 1.21
         *
         * @param Content $that The other content object to compare this content object to.
-        * @param Language $lang The language object to use for text segmentation.
+        * @param Language|null $lang The language object to use for text segmentation.
         *    If not given, $wgContentLang is used.
         *
         * @return Diff A diff representing the changes that would have to be
index 4a7944c..0978ffc 100644 (file)
@@ -38,7 +38,7 @@ class TextContentHandler extends ContentHandler {
         * Returns the content's text as-is.
         *
         * @param Content $content
-        * @param string $format The serialization format to check
+        * @param string|null $format The serialization format to check
         *
         * @return mixed
         */
@@ -108,7 +108,7 @@ class TextContentHandler extends ContentHandler {
         * @since 1.21
         *
         * @param string $text Serialized form of the content
-        * @param string $format The format used for serialization
+        * @param string|null $format The format used for serialization
         *
         * @return Content The TextContent object wrapping $text
         */
index edb54ae..20b08ed 100644 (file)
@@ -46,7 +46,7 @@ class CloneDatabase {
         * @param IMaintainableDatabase $db A database subclass
         * @param array $tablesToClone An array of tables to clone, unprefixed
         * @param string $newTablePrefix Prefix to assign to the tables
-        * @param string $oldTablePrefix Prefix on current tables, if not $wgDBprefix
+        * @param string|null $oldTablePrefix Prefix on current tables, if not $wgDBprefix
         * @param bool $dropCurrentTables
         */
        public function __construct( IMaintainableDatabase $db, array $tablesToClone,
@@ -82,10 +82,10 @@ class CloneDatabase {
                        # works correctly across DB engines, we need to change the pre-
                        # fix back and forth so tableName() works right.
 
-                       self::changePrefix( $this->oldTablePrefix );
+                       $this->db->tablePrefix( $this->oldTablePrefix );
                        $oldTableName = $this->db->tableName( $tbl, 'raw' );
 
-                       self::changePrefix( $this->newTablePrefix );
+                       $this->db->tablePrefix( $this->newTablePrefix );
                        $newTableName = $this->db->tableName( $tbl, 'raw' );
 
                        // Postgres: Temp tables are automatically deleted upon end of session
@@ -116,12 +116,12 @@ class CloneDatabase {
         */
        public function destroy( $dropTables = false ) {
                if ( $dropTables ) {
-                       self::changePrefix( $this->newTablePrefix );
+                       $this->db->tablePrefix( $this->newTablePrefix );
                        foreach ( $this->tablesToClone as $tbl ) {
                                $this->db->dropTable( $tbl );
                        }
                }
-               self::changePrefix( $this->oldTablePrefix );
+               $this->db->tablePrefix( $this->oldTablePrefix );
        }
 
        /**
index 013724c..d2787fa 100644 (file)
@@ -45,7 +45,7 @@ trait PatchFileLocation {
         *
         * @param IDatabase $db
         * @param string $name The script name (relative to $patchDir, without the '.sql' suffix)
-        * @param string $patchDir The directory to find the script in. Use __DIR__ to search in the
+        * @param string|null $patchDir The directory to find the script in. Use __DIR__ to search in the
         *        directory the calling code is located in. If omitted, the "maintenance"
         *        directory will be used, where the scripts used by the updater are located.
         *
index 62d45d4..8537d71 100644 (file)
@@ -42,8 +42,8 @@ use MWExceptionHandler;
 class LineFormatter extends MonologLineFormatter {
 
        /**
-        * @param string $format The format of the message
-        * @param string $dateFormat The format of the timestamp: one supported by DateTime::format
+        * @param string|null $format The format of the message
+        * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format
         * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries
         * @param bool $ignoreEmptyContextAndExtra
         * @param bool $includeStacktraces
index 2766bcb..0d1de1a 100644 (file)
@@ -113,7 +113,7 @@ class SearchUpdate implements DeferrableUpdate {
         * If you're using a real search engine, you'll probably want to override
         * this behavior and do something nicer with the original wikitext.
         * @param string $text
-        * @param SearchEngine $se Search engine
+        * @param SearchEngine|null $se Search engine
         * @return string
         */
        public function updateText( $text, SearchEngine $se = null ) {
index d76af31..063f826 100644 (file)
@@ -66,7 +66,7 @@ abstract class DiffOp {
        }
 
        /**
-        * @param int $i
+        * @param int|null $i
         * @return string[]|string|null
         */
        public function getClosing( $i = null ) {
index ff07fe7..1d9ad05 100644 (file)
@@ -102,7 +102,7 @@ class DifferenceEngine extends ContextSource {
        /**#@-*/
 
        /**
-        * @param IContextSource $context Context to use, anything else will be ignored
+        * @param IContextSource|null $context Context to use, anything else will be ignored
         * @param int $old Old ID we want to show and diff with.
         * @param string|int $new Either revision ID or 'prev' or 'next'. Default: 0.
         * @param int $rcid Deprecated, no longer used!
@@ -132,6 +132,8 @@ class DifferenceEngine extends ContextSource {
        }
 
        /**
+        * Get the language of the difference engine, defaults to page content language
+        *
         * @return Language
         */
        public function getDiffLang() {
@@ -1343,11 +1345,14 @@ class DifferenceEngine extends ContextSource {
 
        /**
         * Set the language in which the diff text is written
-        * (Defaults to page content language).
-        * @param Language|string $lang
+        *
+        * @param Language $lang
         * @since 1.19
         */
        public function setTextLanguage( $lang ) {
+               if ( !$lang instanceof Language ) {
+                       wfDeprecated( __METHOD__ . ' with other type than Language for $lang', '1.32' );
+               }
                $this->mDiffLang = wfGetLangObj( $lang );
        }
 
index f5f8c84..c0182d5 100644 (file)
@@ -46,7 +46,8 @@ class LocalizedException extends Exception implements ILocalizedException {
        /**
         * @param string|array|MessageSpecifier $messageSpec See Message::newFromSpecifier
         * @param int $code
-        * @param Exception|Throwable $previous The previous exception used for the exception chaining.
+        * @param Exception|Throwable|null $previous The previous exception used for the exception
+        *  chaining.
         */
        public function __construct( $messageSpec, $code = 0, $previous = null ) {
                $this->messageSpec = $messageSpec;
index 9039cfc..00dca9d 100644 (file)
@@ -161,8 +161,8 @@ class MWExceptionHandler {
         *
         * @param int $level Error level raised
         * @param string $message
-        * @param string $file
-        * @param int $line
+        * @param string|null $file
+        * @param int|null $line
         * @return bool
         *
         * @see logError()
@@ -231,12 +231,12 @@ class MWExceptionHandler {
         *
         * @since 1.25
         *
-        * @param int $level Error level raised
-        * @param string $message Error message
-        * @param string $file File that error was raised in
-        * @param int $line Line number error was raised at
-        * @param array $context Active symbol table point of error
-        * @param array $trace Backtrace at point of error (undocumented HHVM
+        * @param int|null $level Error level raised
+        * @param string|null $message Error message
+        * @param string|null $file File that error was raised in
+        * @param int|null $line Line number error was raised at
+        * @param array|null $context Active symbol table point of error
+        * @param array|null $trace Backtrace at point of error (undocumented HHVM
         *     feature)
         * @return bool Always returns false
         */
index ce899ed..26010da 100644 (file)
@@ -34,7 +34,7 @@ class DumpPipeOutput extends DumpFileOutput {
 
        /**
         * @param string $command
-        * @param string $file
+        * @param string|null $file
         */
        function __construct( $command, $file = null ) {
                if ( !is_null( $file ) ) {
index 45a6baf..422e1fb 100644 (file)
@@ -194,6 +194,10 @@ class ExternalStoreDB extends ExternalStoreMedium {
                static $externalBlobCache = [];
 
                $cacheID = ( $itemID === false ) ? "$cluster/$id" : "$cluster/$id/";
+
+               $wiki = $this->params['wiki'] ?? false;
+               $cacheID = ( $wiki === false ) ? $cacheID : "$cacheID@$wiki";
+
                if ( isset( $externalBlobCache[$cacheID] ) ) {
                        wfDebugLog( 'ExternalStoreDB-cache',
                                "ExternalStoreDB::fetchBlob cache hit on $cacheID" );
index 65e4345..5589c68 100644 (file)
@@ -248,7 +248,7 @@ class ArchivedFile {
                        'fa_minor_mime',
                        'fa_user',
                        'fa_user_text',
-                       'fa_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'fa_actor' : null,
+                       'fa_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'fa_actor' : 'NULL',
                        'fa_timestamp',
                        'fa_deleted',
                        'fa_deleted_timestamp', /* Used by LocalFileRestoreBatch */
index e86f292..7206dce 100644 (file)
@@ -223,7 +223,7 @@ class LocalFile extends File {
                        'img_minor_mime',
                        'img_user',
                        'img_user_text',
-                       'img_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'img_actor' : null,
+                       'img_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'img_actor' : 'NULL',
                        'img_timestamp',
                        'img_sha1',
                ] + MediaWikiServices::getInstance()->getCommentStore()->getFields( 'img_description' );
index aa434d0..9759b79 100644 (file)
@@ -41,7 +41,7 @@ class OldLocalFile extends LocalFile {
        /**
         * @param Title $title
         * @param FileRepo $repo
-        * @param string|int $time
+        * @param string|int|null $time
         * @return self
         * @throws MWException
         */
@@ -138,7 +138,7 @@ class OldLocalFile extends LocalFile {
                        'oi_minor_mime',
                        'oi_user',
                        'oi_user_text',
-                       'oi_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'oi_actor' : null,
+                       'oi_actor' => $wgActorTableSchemaMigrationStage > MIGRATION_OLD ? 'oi_actor' : 'NULL',
                        'oi_timestamp',
                        'oi_deleted',
                        'oi_sha1',
index a87322d..479ca5d 100644 (file)
@@ -226,7 +226,7 @@ class TraditionalImageGallery extends ImageGalleryBase {
                        $nt,
                        htmlspecialchars(
                                is_int( $this->getCaptionLength() ) ?
-                                       $lang->truncate( $nt->getText(), $this->getCaptionLength() ) :
+                                       $lang->truncateForVisual( $nt->getText(), $this->getCaptionLength() ) :
                                        $nt->getText()
                        ),
                        [
@@ -286,7 +286,7 @@ class TraditionalImageGallery extends ImageGalleryBase {
        }
 
        /**
-        * Length to truncate filename to in caption when using "showfilename" (if int).
+        * Length (in characters) to truncate filename to in caption when using "showfilename" (if int).
         * A value of 'true' will truncate the filename to one line using CSS, while
         * 'false' will disable truncating.
         *
index b5fb87f..442a7cf 100644 (file)
@@ -301,7 +301,7 @@ class HTMLForm extends ContextSource {
         * Build a new HTMLForm from an array of field attributes
         *
         * @param array $descriptor Array of Field constructs, as described above
-        * @param IContextSource $context Available since 1.18, will become compulsory in 1.18.
+        * @param IContextSource|null $context Available since 1.18, will become compulsory in 1.18.
         *     Obviates the need to call $form->setTitle()
         * @param string $messagePrefix A prefix to go in front of default messages
         */
@@ -752,6 +752,17 @@ class HTMLForm extends ContextSource {
                return $this;
        }
 
+       /**
+        * Get the introductory message HTML.
+        *
+        * @since 1.32
+        *
+        * @return string
+        */
+       public function getPreText() {
+               return $this->mPre;
+       }
+
        /**
         * Add HTML to the header, inside the form.
         *
index 46cc6d3..cda69a8 100644 (file)
@@ -3,7 +3,7 @@
 use MediaWiki\Widget\UsersMultiselectWidget;
 
 /**
- * Implements a capsule multiselect input field for user names.
+ * Implements a tag multiselect input field for user names.
  *
  * Besides the parameters recognized by HTMLUserTextField, additional recognized
  * parameters are:
index 70691b4..71e08a8 100644 (file)
@@ -87,7 +87,7 @@ abstract class MWHttpRequest implements LoggerAwareInterface {
         * @param string $url Url to use. If protocol-relative, will be expanded to an http:// URL
         * @param array $options (optional) extra params to pass (see Http::request())
         * @param string $caller The method making this request, for profiling
-        * @param Profiler $profiler An instance of the profiler for profiling, or null
+        * @param Profiler|null $profiler An instance of the profiler for profiling, or null
         */
        public function __construct(
                $url, array $options = [], $caller = __METHOD__, $profiler = null
index 845408a..4f531da 100644 (file)
@@ -47,7 +47,7 @@ class CliInstaller extends Installer {
 
        /**
         * @param string $siteName
-        * @param string $admin
+        * @param string|null $admin
         * @param array $option
         */
        function __construct( $siteName, $admin = null, array $option = [] ) {
index 1cd76b9..939301d 100644 (file)
@@ -109,7 +109,7 @@ abstract class DatabaseUpdater {
        /**
         * @param Database &$db To perform updates on
         * @param bool $shared Whether to perform updates on shared tables
-        * @param Maintenance $maintenance Maintenance object which created us
+        * @param Maintenance|null $maintenance Maintenance object which created us
         */
        protected function __construct( Database &$db, $shared, Maintenance $maintenance = null ) {
                $this->db = $db;
@@ -516,7 +516,7 @@ abstract class DatabaseUpdater {
         * Obviously, only use this for updates that occur after the updatelog table was
         * created!
         * @param string $key Name of key to insert
-        * @param string $val [optional] Value to insert along with the key
+        * @param string|null $val [optional] Value to insert along with the key
         */
        public function insertUpdateRow( $key, $val = null ) {
                $this->db->clearFlag( DBO_DDLMODE );
@@ -659,7 +659,7 @@ abstract class DatabaseUpdater {
         *
         * @param string $path Path to the patch file
         * @param bool $isFullPath Whether to treat $path as a relative or not
-        * @param string $msg Description of the patch
+        * @param string|null $msg Description of the patch
         * @return bool False if patch is skipped.
         */
        protected function applyPatch( $path, $isFullPath = false, $msg = null ) {
index 0890bc4..3905ba0 100644 (file)
@@ -531,7 +531,7 @@ abstract class Installer {
         * Installer variables are typically prefixed by an underscore.
         *
         * @param string $name
-        * @param mixed $default
+        * @param mixed|null $default
         *
         * @return mixed
         */
index 4142e6f..2ba221f 100644 (file)
@@ -472,7 +472,7 @@ class WebInstaller extends Installer {
         * Get a session variable.
         *
         * @param string $name
-        * @param array $default
+        * @param array|null $default
         *
         * @return array
         */
@@ -758,7 +758,7 @@ class WebInstaller extends Installer {
         */
        public function label( $msg, $forId, $contents, $helpData = "" ) {
                if ( strval( $msg ) == '' ) {
-                       $labelText = '\u{00A0}';
+                       $labelText = "\u{00A0}";
                } else {
                        $labelText = wfMessage( $msg )->escaped();
                }
@@ -1047,7 +1047,7 @@ class WebInstaller extends Installer {
 
                        $items[$value] =
                                Xml::radio( $params['controlName'], $value, $checked, $itemAttribs ) .
-                               '\u{00A0}' .
+                               "\u{00A0}" .
                                Xml::tags( 'label', [ 'for' => $id ], $this->parse(
                                        isset( $params['itemLabels'] ) ?
                                                wfMessage( $params['itemLabels'][$value] )->plain() :
index 7f0d27d..382ed3b 100644 (file)
@@ -412,7 +412,7 @@ class WebInstallerOptions extends WebInstallerPage {
 
                return '<p>' .
                        Html::element( 'img', [ 'src' => $this->getVar( 'wgRightsIcon' ) ] ) .
-                       '\u{00A0}\u{00A0}' .
+                       "\u{00A0}\u{00A0}" .
                        htmlspecialchars( $this->getVar( 'wgRightsText' ) ) .
                        "</p>\n" .
                        "<p style=\"text-align: center;\">" .
index 3aad6f8..1c5fa0e 100644 (file)
@@ -145,7 +145,7 @@ abstract class WebInstallerPage {
 
        /**
         * @param string $var
-        * @param mixed $default
+        * @param mixed|null $default
         *
         * @return mixed
         */
index a40bf1b..df5aad0 100644 (file)
        "config-license-help": "Чимало загальнодоступних вікі публікують увесь свій вміст під [https://freedomdefined.org/Definition вільною ліцензією]. Це розвиває відчуття спільної власності і заохочує довготривалу участь. У загальному випадку для приватної чи корпоративної вікі у цьому немає необхідності.\n\nЯкщо Ви хочете мати змогу використовувати текст з Вікіпедії і дати Вікіпедії змогу використовувати текст, скопійований з Вашої вікі, вам необхідно обрати <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nРаніше Вікіпедія використовувала GNU Free Documentation License.\nGFDL — допустима ліцензія, але у ній важко розібратися, а контент під GFDL важко використовувати повторно.",
        "config-email-settings": "Налаштування електронної пошти",
        "config-enable-email": "Увімкнути вихідну електронну пошту",
-       "config-enable-email-help": "Якщо Ви хочете, що електронна пошта працювала, необхідно виставити коректні [Config-dbsupport-oracle/manual/en/mail.configuration.php налаштування пошти у PHP].\nЯкщо Вам не потрібні жодні можливості електронної пошти у вікі, можете тут їх відключити.",
+       "config-enable-email-help": "Якщо Ви хочете, що електронна пошта працювала, необхідно виставити коректні [Config-dbsupport-oracle/manual/en/mail.configuration.php налаштування пошти у PHP].\nЯкщо Вам не потрібні жодні можливості електронної пошти у вікі, можете тут їх відімкнути.",
        "config-email-user": "Увімкнути електронну пошту користувач-користувачеві",
        "config-email-user-help": "Дозволити усім користувачам надсилати один одному електронну пошту, якщо вони увімкнули цю можливість у своїх налаштуваннях.",
        "config-email-usertalk": "Увімкнути сповіщення про повідомлення на сторінці обговорення користувача",
        "config-nofile": "Файл \"$1\" не знайдено. Його видалено?",
        "config-extension-link": "Чи знаєте ви, що ваше вікі підтримує [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions розширення]?\n\nВи можете переглядати [https://www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category розширення по категорії] або в [https://www.mediawiki.org/wiki/Extension_Matrix матрицю розширень] щоб побачити повний список розширень.",
        "config-skins-screenshots": "$1 (скріншоти: $2)",
+       "config-extensions-requires": "$1 (вимагає $2)",
        "config-screenshot": "скріншот",
        "mainpagetext": "<strong>Програмне забезпечення «MediaWiki» встановлено.</strong>",
        "mainpagedocfooter": "Інформацію про роботу з цією вікі можна знайти на сторінці [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Довідка:Вміст].\n\n== Деякі корисні ресурси ==\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Список налаштувань];\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Часті питання з приводу MediaWiki];\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Розсилка повідомлень про появу нових версій MediaWiki];\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Локалізувати MediaWiki своєю мовою]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Дізнатися, як боротися зі спамом у своїй вікі]"
index e6a943d..832883b 100644 (file)
@@ -111,7 +111,7 @@ class Interwiki {
        /**
         * Get the URL for a particular title (or with $1 if no title given)
         *
-        * @param string $title What text to put for the article name
+        * @param string|null $title What text to put for the article name
         * @return string The URL
         * @note Prior to 1.19 The getURL with an argument was broken.
         *       If you if you use this arg in an extension that supports MW earlier
index a85613a..7c5d0ae 100644 (file)
@@ -69,7 +69,7 @@ class JobRunner implements LoggerAwareInterface {
        }
 
        /**
-        * @param LoggerInterface $logger
+        * @param LoggerInterface|null $logger
         */
        public function __construct( LoggerInterface $logger = null ) {
                if ( $logger === null ) {
index af5646a..d060c1c 100644 (file)
@@ -117,7 +117,7 @@ class JobSpecification implements IJobSpecification {
         * @param string $type
         * @param array $params Map of key/values
         * @param array $opts Map of key/values; includes 'removeDuplicates'
-        * @param Title $title Optional descriptive title
+        * @param Title|null $title Optional descriptive title
         */
        public function __construct(
                $type, array $params, array $opts = [], Title $title = null
index a67b919..c0ab0a0 100644 (file)
@@ -86,7 +86,7 @@ class Cookie {
         * not a big problem in practice, but there are test cases)
         *
         * @param string $domain The domain to validate
-        * @param string $originDomain (optional) the domain the cookie originates from
+        * @param string|null $originDomain (optional) the domain the cookie originates from
         * @return bool
         */
        public static function validateCookieDomain( $domain, $originDomain = null ) {
index bca8c05..595dc1a 100644 (file)
@@ -247,7 +247,7 @@ class MapCacheLRU implements IExpiringStore, Serializable {
        /**
         * Clear one or several cache entries, or all cache entries
         *
-        * @param string|array $keys
+        * @param string|array|null $keys
         * @return void
         */
        public function clear( $keys = null ) {
index eb32d98..f0cd83e 100644 (file)
@@ -83,7 +83,7 @@ class ProcessCacheLRU {
        /**
         * Clear one or several cache entries, or all cache entries.
         *
-        * @param string|array $keys
+        * @param string|array|null $keys
         * @return void
         */
        public function clear( $keys = null ) {
index 3bdafe1..16cb1ed 100644 (file)
@@ -75,7 +75,7 @@ class StatusValue {
        /**
         * Factory function for good results
         *
-        * @param mixed $value
+        * @param mixed|null $value
         * @return static
         */
        public static function newGood( $value = null ) {
@@ -162,7 +162,7 @@ class StatusValue {
         * Change operation result
         *
         * @param bool $ok Whether the operation completed
-        * @param mixed $value
+        * @param mixed|null $value
         */
        public function setResult( $ok, $value = null ) {
                $this->ok = (bool)$ok;
index 04a13e2..65a9247 100644 (file)
@@ -83,7 +83,7 @@ class Timing implements LoggerAwareInterface {
        }
 
        /**
-        * @param string $markName The name of the mark that should
+        * @param string|null $markName The name of the mark that should
         *  be cleared. If not specified, all marks will be cleared.
         */
        public function clearMarks( $markName = null ) {
@@ -117,7 +117,7 @@ class Timing implements LoggerAwareInterface {
         *
         * @param string $measureName
         * @param string $startMark
-        * @param string $endMark
+        * @param string|null $endMark
         * @return array|bool The measure that has been created, or false if either
         *  the start mark or the end mark do not exist.
         */
index 785cb72..6d5efa2 100644 (file)
@@ -1267,7 +1267,7 @@ abstract class FileBackend implements LoggerAwareInterface {
         *
         * @see FileBackend::getFileStat()
         *
-        * @param array $paths Storage paths (optional)
+        * @param array|null $paths Storage paths (optional)
         */
        abstract public function clearCache( array $paths = null );
 
index 6c1dc53..118464c 100644 (file)
@@ -1299,7 +1299,7 @@ abstract class FileBackendStore extends FileBackend {
         *
         * @see FileBackend::clearCache()
         *
-        * @param array $paths Storage paths (optional)
+        * @param array|null $paths Storage paths (optional)
         */
        protected function doClearCache( array $paths = null ) {
        }
index 2f7bc1e..143f854 100644 (file)
@@ -1737,8 +1737,8 @@ class SwiftFileBackend extends FileBackendStore {
 
        /**
         * @param array $creds From getAuthentication()
-        * @param string $container
-        * @param string $object
+        * @param string|null $container
+        * @param string|null $object
         * @return string
         */
        protected function storageUrl( array $creds, $container = null, $object = null ) {
index 9343fcc..47be4eb 100644 (file)
@@ -149,9 +149,9 @@ abstract class FileJournal {
         * Get an array of file change log entries.
         * A starting change ID and/or limit can be specified.
         *
-        * @param int $start Starting change ID or null
+        * @param int|null $start Starting change ID or null
         * @param int $limit Maximum number of items to return
-        * @param string &$next Updated to the ID of the next entry.
+        * @param string|null &$next Updated to the ID of the next entry.
         * @return array List of associative arrays, each having:
         *     id         : unique, monotonic, ID for this change
         *     batch_uuid : UUID for an operation batch
index 6261335..d877db0 100644 (file)
@@ -85,7 +85,7 @@ class ScopedLock {
         * This is useful for early release of locks before function scope is destroyed.
         * This is the same as setting the lock object to null.
         *
-        * @param ScopedLock &$lock
+        * @param ScopedLock|null &$lock
         * @since 1.21
         */
        public static function release( ScopedLock &$lock = null ) {
index 4a16679..77c377b 100644 (file)
@@ -1029,9 +1029,9 @@ EOT;
         * @todo analyse file if need be
         * @todo look at multiple extension, separately and together.
         *
-        * @param string $path Full path to the image file, in case we have to look at the contents
+        * @param string|null $path Full path to the image file, in case we have to look at the contents
         *        (if null, only the MIME type is used to determine the media type code).
-        * @param string $mime MIME type. If null it will be guessed using guessMimeType.
+        * @param string|null $mime MIME type. If null it will be guessed using guessMimeType.
         *
         * @return string A value to be used with the MEDIATYPE_xxx constants.
         */
index e231113..9cfaf67 100644 (file)
@@ -91,7 +91,7 @@ class XmlTypeCheck {
         *  result stored in $this->filterMatchType.
         *
         * @param string $input a filename or string containing the XML element
-        * @param callable $filterCallback (optional)
+        * @param callable|null $filterCallback (optional)
         *        Function to call to do additional custom validity checks from the
         *        SAX element handler event. This gives you access to the element
         *        namespace, name, attributes, and text contents.
@@ -114,7 +114,7 @@ class XmlTypeCheck {
         * Alternative constructor: from filename
         *
         * @param string $fname the filename of an XML document
-        * @param callable $filterCallback (optional)
+        * @param callable|null $filterCallback (optional)
         *        Function to call to do additional custom validity checks from the
         *        SAX element handler event. This gives you access to the element
         *        namespace, name, and attributes, but not to text contents.
@@ -129,7 +129,7 @@ class XmlTypeCheck {
         * Alternative constructor: from string
         *
         * @param string $string a string containing an XML element
-        * @param callable $filterCallback (optional)
+        * @param callable|null $filterCallback (optional)
         *        Function to call to do additional custom validity checks from the
         *        SAX element handler event. This gives you access to the element
         *        namespace, name, and attributes, but not to text contents.
index e58a0bb..0100fb2 100644 (file)
@@ -185,7 +185,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         *
         * @param string $key
         * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
-        * @param int $oldFlags [unused]
+        * @param int|null $oldFlags [unused]
         * @return mixed Returns false on failure and if the item does not exist
         */
        public function get( $key, $flags = 0, $oldFlags = null ) {
@@ -774,7 +774,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         *
         * @since 1.27
         * @param string $class Key class
-        * @param string $component [optional] Key component (starting with a key collection name)
+        * @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
         */
        public function makeGlobalKey( $class, $component = null ) {
@@ -786,7 +786,7 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
         *
         * @since 1.27
         * @param string $class Key class
-        * @param string $component [optional] Key component (starting with a key collection name)
+        * @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
         */
        public function makeKey( $class, $component = null ) {
index d3aa9f5..fc3d575 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use Psr\Log\LoggerInterface;
+
 /**
  * Interface to key-value storage behind an HTTP server.
  *
  * @endcode
  */
 class RESTBagOStuff extends BagOStuff {
+       /**
+        * Default connection timeout in seconds. The kernel retransmits the SYN
+        * packet after 1 second, so 1.2 seconds allows for 1 retransmit without
+        * permanent failure.
+        */
+       const DEFAULT_CONN_TIMEOUT = 1.2;
+
+       /**
+        * Default request timeout
+        */
+       const DEFAULT_REQ_TIMEOUT = 3.0;
 
        /**
         * @var MultiHttpClient
@@ -43,18 +56,34 @@ class RESTBagOStuff extends BagOStuff {
                if ( empty( $params['url'] ) ) {
                        throw new InvalidArgumentException( 'URL parameter is required' );
                }
-               parent::__construct( $params );
                if ( empty( $params['client'] ) ) {
-                       $this->client = new MultiHttpClient( [] );
+                       // Pass through some params to the HTTP client.
+                       $clientParams = [
+                               'connTimeout' => $params['connTimeout'] ?? self::DEFAULT_CONN_TIMEOUT,
+                               'reqTimeout' => $params['reqTimeout'] ?? self::DEFAULT_REQ_TIMEOUT,
+                       ];
+                       foreach ( [ 'caBundlePath', 'proxy' ] as $key ) {
+                               if ( isset( $params[$key] ) ) {
+                                       $clientParams[$key] = $params[$key];
+                               }
+                       }
+                       $this->client = new MultiHttpClient( $clientParams );
                } else {
                        $this->client = $params['client'];
                }
+               // The parent constructor calls setLogger() which sets the logger in $this->client
+               parent::__construct( $params );
                // Make sure URL ends with /
                $this->url = rtrim( $params['url'], '/' ) . '/';
                // Default config, R+W > N; no locks on reads though; writes go straight to state-machine
                $this->attrMap[self::ATTR_SYNCWRITES] = self::QOS_SYNCWRITES_QC;
        }
 
+       public function setLogger( LoggerInterface $logger ) {
+               parent::setLogger( $logger );
+               $this->client->setLogger( $logger );
+       }
+
        /**
         * @param string $key
         * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
@@ -111,7 +140,7 @@ class RESTBagOStuff extends BagOStuff {
                        'body' => serialize( $value )
                ];
                list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->client->run( $req );
-               if ( $rcode === 200 || $rcode === 201 ) {
+               if ( $rcode === 200 || $rcode === 201 || $rcode === 204 ) {
                        return true;
                }
                return $this->handleError( "Failed to store $key", $rcode, $rerr );
@@ -129,7 +158,7 @@ class RESTBagOStuff extends BagOStuff {
                        'url' => $this->url . rawurlencode( $key ),
                ];
                list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->client->run( $req );
-               if ( $rcode === 200 || $rcode === 204 || $rcode === 205 ) {
+               if ( in_array( $rcode, [ 200, 204, 205, 404, 410 ] ) ) {
                        return true;
                }
                return $this->handleError( "Failed to delete $key", $rcode, $rerr );
index 0913322..e30e061 100644 (file)
@@ -291,9 +291,9 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * That method has cache slam avoiding features for hot/expensive keys.
         *
         * @param string $key Cache key made from makeKey() or makeGlobalKey()
-        * @param mixed &$curTTL Approximate TTL left on the key if present/tombstoned [returned]
+        * @param mixed|null &$curTTL Approximate TTL left on the key if present/tombstoned [returned]
         * @param array $checkKeys List of "check" keys
-        * @param float &$asOf UNIX timestamp of cached value; null on failure [returned]
+        * @param float|null &$asOf UNIX timestamp of cached value; null on failure [returned]
         * @return mixed Value of cache key or false on failure
         */
        final public function get( $key, &$curTTL = null, array $checkKeys = [], &$asOf = null ) {
@@ -1133,7 +1133,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         * @param int $ttl
         * @param callback $callback
         * @param array $opts Options map for getWithSetCallback()
-        * @param float &$asOf Cache generation timestamp of returned value [returned]
+        * @param float|null &$asOf Cache generation timestamp of returned value [returned]
         * @return mixed
         * @note Callable type hints are not used to avoid class-autoloading
         */
@@ -1600,7 +1600,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        /**
         * @see BagOStuff::makeKey()
         * @param string $class Key class
-        * @param string $component [optional] Key component (starting with a key collection name)
+        * @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
         * @since 1.27
         */
@@ -1611,7 +1611,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        /**
         * @see BagOStuff::makeGlobalKey()
         * @param string $class Key class
-        * @param string $component [optional] Key component (starting with a key collection name)
+        * @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
         * @since 1.27
         */
index b414a2a..50fdc27 100644 (file)
@@ -167,6 +167,9 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
+       /**
+        * @codeCoverageIgnore
+        */
        public function getWikiID() {
                return $this->getDomainID();
        }
index 57e5907..8d1e25f 100644 (file)
@@ -1925,18 +1925,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
                }
        }
 
-       public function tableExists( $table, $fname = __METHOD__ ) {
-               $tableRaw = $this->tableName( $table, 'raw' );
-               if ( isset( $this->sessionTempTables[$tableRaw] ) ) {
-                       return true; // already known to exist
-               }
-
-               $table = $this->tableName( $table );
-               $ignoreErrors = true;
-               $res = $this->query( "SELECT 1 FROM $table LIMIT 1", $fname, $ignoreErrors );
-
-               return (bool)$res;
-       }
+       abstract public function tableExists( $table, $fname = __METHOD__ );
 
        public function indexUnique( $table, $index ) {
                $indexInfo = $this->indexInfo( $table, $index );
@@ -4124,7 +4113,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
         * @see WANObjectCache::getWithSetCallback()
         *
         * @param IDatabase $db1
-        * @param IDatabase $db2 [optional]
+        * @param IDatabase|null $db2 [optional]
         * @return array Map of values:
         *   - lag: highest lag of any of the DBs or false on error (e.g. replication stopped)
         *   - since: oldest UNIX timestamp of any of the DB lag estimates
index bbc656d..3ecfca9 100644 (file)
@@ -498,7 +498,7 @@ abstract class DatabaseMysqlBase extends Database {
        /**
         * Returns the text of the error message from previous MySQL operation
         *
-        * @param resource $conn Raw connection
+        * @param resource|null $conn Raw connection
         * @return string
         */
        abstract protected function mysqlError( $conn = null );
@@ -1457,7 +1457,7 @@ abstract class DatabaseMysqlBase extends Database {
        /**
         * List all tables on the database
         *
-        * @param string $prefix Only show tables with this prefix, e.g. mw_
+        * @param string|null $prefix Only show tables with this prefix, e.g. mw_
         * @param string $fname Calling function name
         * @return array
         */
@@ -1511,7 +1511,7 @@ abstract class DatabaseMysqlBase extends Database {
        /**
         * Lists VIEWs in the database
         *
-        * @param string $prefix Only show VIEWs with this prefix, eg.
+        * @param string|null $prefix Only show VIEWs with this prefix, eg.
         * unit_test_, or $wgDBprefix. Default: null, would return all views.
         * @param string $fname Name of calling function
         * @return array
@@ -1547,7 +1547,7 @@ abstract class DatabaseMysqlBase extends Database {
         * Differentiates between a TABLE and a VIEW.
         *
         * @param string $name Name of the TABLE/VIEW to test
-        * @param string $prefix
+        * @param string|null $prefix
         * @return bool
         * @since 1.22
         */
index ec69b9d..6d9dabd 100644 (file)
@@ -296,7 +296,7 @@ class DatabaseMysqli extends DatabaseMysqlBase {
        }
 
        /**
-        * @param mysqli $conn Optional connection object
+        * @param mysqli|null $conn Optional connection object
         * @return string
         */
        protected function mysqlError( $conn = null ) {
index 4b925e6..ca44572 100644 (file)
@@ -519,6 +519,19 @@ class DatabaseSqlite extends Database {
                return $this->lastAffectedRowCount;
        }
 
+       function tableExists( $table, $fname = __METHOD__ ) {
+               $tableRaw = $this->tableName( $table, 'raw' );
+               if ( isset( $this->sessionTempTables[$tableRaw] ) ) {
+                       return true; // already known to exist
+               }
+
+               $encTable = $this->addQuotes( $tableRaw );
+               $res = $this->query(
+                       "SELECT 1 FROM sqlite_master WHERE type='table' AND name=$encTable" );
+
+               return $res->numRows() ? true : false;
+       }
+
        /**
         * Returns information about an index
         * Returns false if the index does not exist
@@ -1023,7 +1036,7 @@ class DatabaseSqlite extends Database {
        /**
         * List all tables on the database
         *
-        * @param string $prefix Only show tables with this prefix, e.g. mw_
+        * @param string|null $prefix Only show tables with this prefix, e.g. mw_
         * @param string $fname Calling function name
         *
         * @return array
index 2145129..723051c 100644 (file)
@@ -166,14 +166,14 @@ interface IDatabase {
 
        /**
         * Get/set the table prefix.
-        * @param string $prefix The table prefix to set, or omitted to leave it unchanged.
+        * @param string|null $prefix The table prefix to set, or omitted to leave it unchanged.
         * @return string The previous table prefix.
         */
        public function tablePrefix( $prefix = null );
 
        /**
         * Get/set the db schema.
-        * @param string $schema The database schema to set, or omitted to leave it unchanged.
+        * @param string|null $schema The database schema to set, or omitted to leave it unchanged.
         * @return string The previous db schema.
         */
        public function dbSchema( $schema = null );
@@ -182,7 +182,7 @@ interface IDatabase {
         * Get properties passed down from the server info array of the load
         * balancer.
         *
-        * @param string $name The entry of the info array to get, or null to get the
+        * @param string|null $name The entry of the info array to get, or null to get the
         *   whole array
         *
         * @return array|mixed|null
@@ -195,7 +195,7 @@ interface IDatabase {
         * parameters, the member with the given name is set to the given value.
         *
         * @param string $name
-        * @param array $value
+        * @param array|null $value
         */
        public function setLBInfo( $name, $value = null );
 
@@ -1698,7 +1698,7 @@ interface IDatabase {
         * @since 1.31
         * @see IDatabase::startAtomic
         * @param string $fname
-        * @param AtomicSectionIdentifier $sectionId Section ID from startAtomic();
+        * @param AtomicSectionIdentifier|null $sectionId Section ID from startAtomic();
         *   passing this enables cancellation of unclosed nested sections [optional]
         * @throws DBError
         */
@@ -1882,7 +1882,7 @@ interface IDatabase {
         * The result is unquoted, and needs to be passed through addQuotes()
         * before it can be included in raw SQL.
         *
-        * @param string|int $ts
+        * @param string|int|null $ts
         *
         * @return string
         */
index 18e3cbb..5706435 100644 (file)
@@ -183,7 +183,7 @@ interface IMaintainableDatabase extends IDatabase {
        /**
         * Lists all the VIEWs in the database
         *
-        * @param string $prefix Only show VIEWs with this prefix, eg. unit_test_
+        * @param string|null $prefix Only show VIEWs with this prefix, eg. unit_test_
         * @param string $fname Name of calling function
         * @throws RuntimeException
         * @return array
@@ -279,7 +279,7 @@ interface IMaintainableDatabase extends IDatabase {
        /**
         * List all tables on the database
         *
-        * @param string $prefix Only show tables with this prefix, e.g. mw_
+        * @param string|null $prefix Only show tables with this prefix, e.g. mw_
         * @param string $fname Calling function name
         * @throws DBError
         * @return array
index 4c5bc9a..4ed2b31 100644 (file)
@@ -25,7 +25,7 @@ namespace Wikimedia\Rdbms;
  */
 class DBConnectionError extends DBExpectedError {
        /**
-        * @param IDatabase $db Object throwing the error
+        * @param IDatabase|null $db Object throwing the error
         * @param string $error Error text
         */
        public function __construct( IDatabase $db = null, $error = 'unknown error' ) {
index 1a5f4a3..6b67b45 100644 (file)
@@ -33,7 +33,7 @@ class DBError extends RuntimeException {
 
        /**
         * Construct a database error
-        * @param IDatabase $db Object which threw the error
+        * @param IDatabase|null $db Object which threw the error
         * @param string $error A simple error message to be used for debugging
         * @param \Exception|\Throwable|null $prev Previous exception
         */
index 0be08cd..4bdd8f6 100644 (file)
@@ -40,7 +40,7 @@ class DBQueryError extends DBExpectedError {
         * @param int|string $errno
         * @param string $sql
         * @param string $fname
-        * @param string $message Optional message, intended for subclases (optional)
+        * @param string|null $message Optional message, intended for subclases (optional)
         */
        public function __construct( IDatabase $db, $error, $errno, $sql, $fname, $message = null ) {
                if ( $message === null ) {
index 85ab115..222d7d1 100644 (file)
@@ -67,6 +67,22 @@ interface ILBFactory {
         */
        public function destroy();
 
+       /**
+        * Get the local (and default) database domain ID of connection handles
+        *
+        * @see DatabaseDomain
+        * @return string Database domain ID; this specifies DB name, schema, and table prefix
+        * @since 1.32
+        */
+       public function getLocalDomainID();
+
+       /**
+        * @param DatabaseDomain|string|bool $domain Database domain
+        * @return string Value of $domain if provided or the local domain otherwise
+        * @since 1.32
+        */
+       public function resolveDomainID( $domain );
+
        /**
         * Create a new load balancer object. The resulting object will be untracked,
         * not chronology-protected, and the caller is responsible for cleaning it up.
@@ -229,7 +245,7 @@ interface ILBFactory {
 
        /**
         * Determine if any master connection has pending/written changes from this request
-        * @param float $age How many seconds ago is "recent" [defaults to LB lag wait timeout]
+        * @param float|null $age How many seconds ago is "recent" [defaults to LB lag wait timeout]
         * @return bool
         */
        public function hasOrMadeRecentMasterChanges( $age = null );
index 856bd32..e47e75e 100644 (file)
@@ -152,6 +152,14 @@ abstract class LBFactory implements ILBFactory {
                $this->forEachLBCallMethod( 'disable' );
        }
 
+       public function getLocalDomainID() {
+               return $this->localDomain->getId();
+       }
+
+       public function resolveDomainID( $domain ) {
+               return ( $domain !== false ) ? (string)$domain : $this->getLocalDomainID();
+       }
+
        public function shutdown(
                $mode = self::SHUTDOWN_CHRONPROT_SYNC,
                callable $workCallback = null,
index 04a553c..0ff6a32 100644 (file)
@@ -86,8 +86,6 @@ interface ILoadBalancer {
 
        /** @var int DB handle should have DBO_TRX disabled and the caller will leave it as such */
        const CONN_TRX_AUTOCOMMIT = 1;
-       /** @var int Alias for CONN_TRX_AUTOCOMMIT for b/c; deprecated since 1.31 */
-       const CONN_TRX_AUTO = 1;
 
        /** @var string Manager of ILoadBalancer instances is running post-commit callbacks */
        const STAGE_POSTCOMMIT_CALLBACKS = 'stage-postcommit-callbacks';
@@ -122,6 +120,22 @@ interface ILoadBalancer {
         */
        public function __construct( array $params );
 
+       /**
+        * Get the local (and default) database domain ID of connection handles
+        *
+        * @see DatabaseDomain
+        * @return string Database domain ID; this specifies DB name, schema, and table prefix
+        * @since 1.31
+        */
+       public function getLocalDomainID();
+
+       /**
+        * @param DatabaseDomain|string|bool $domain Database domain
+        * @return string Value of $domain if provided or the local domain otherwise
+        * @since 1.32
+        */
+       public function resolveDomainID( $domain );
+
        /**
         * Get the index of the reader connection, which may be a replica DB
         *
@@ -156,7 +170,7 @@ interface ILoadBalancer {
         * This can be used a faster proxy for waitForAll()
         *
         * @param DBMasterPos|bool $pos Master position or false
-        * @param int $timeout Max seconds to wait; default is mWaitTimeout
+        * @param int|null $timeout Max seconds to wait; default is mWaitTimeout
         * @return bool Success (able to connect and no timeouts reached)
         */
        public function waitForOne( $pos, $timeout = null );
@@ -165,7 +179,7 @@ interface ILoadBalancer {
         * Set the master wait position and wait for ALL replica DBs to catch up to it
         *
         * @param DBMasterPos|bool $pos Master position or false
-        * @param int $timeout Max seconds to wait; default is mWaitTimeout
+        * @param int|null $timeout Max seconds to wait; default is mWaitTimeout
         * @return bool Success (able to connect and no timeouts reached)
         */
        public function waitForAll( $pos, $timeout = null );
@@ -488,7 +502,7 @@ interface ILoadBalancer {
         * Check if this load balancer object had any recent or still
         * pending writes issued against it by this PHP thread
         *
-        * @param float $age How many seconds ago is "recent" [defaults to mWaitTimeout]
+        * @param float|null $age How many seconds ago is "recent" [defaults to mWaitTimeout]
         * @return bool
         */
        public function hasOrMadeRecentMasterChanges( $age = null );
index fe0c622..f2f9eb0 100644 (file)
@@ -269,17 +269,14 @@ class LoadBalancer implements ILoadBalancer {
                $this->defaultGroup = $params['defaultGroup'] ?? null;
        }
 
-       /**
-        * Get the local (and default) database domain ID of connection handles
-        *
-        * @see DatabaseDomain
-        * @return string Database domain ID; this specifies DB name, schema, and table prefix
-        * @since 1.31
-        */
        public function getLocalDomainID() {
                return $this->localDomain->getId();
        }
 
+       public function resolveDomainID( $domain ) {
+               return ( $domain !== false ) ? (string)$domain : $this->getLocalDomainID();
+       }
+
        /**
         * Get a LoadMonitor instance
         *
@@ -606,7 +603,7 @@ class LoadBalancer implements ILoadBalancer {
         * Wait for a given replica DB to catch up to the master pos stored in $this
         * @param int $index Server index
         * @param bool $open Check the server even if a new connection has to be made
-        * @param int $timeout Max seconds to wait; default is "waitTimeout" given to __construct()
+        * @param int|null $timeout Max seconds to wait; default is "waitTimeout" given to __construct()
         * @return bool
         */
        protected function doWait( $index, $open = false, $timeout = null ) {
@@ -848,19 +845,19 @@ class LoadBalancer implements ILoadBalancer {
        }
 
        public function getConnectionRef( $db, $groups = [], $domain = false, $flags = 0 ) {
-               $domain = ( $domain !== false ) ? $domain : $this->localDomain;
+               $domain = $this->resolveDomainID( $domain );
 
                return new DBConnRef( $this, $this->getConnection( $db, $groups, $domain, $flags ) );
        }
 
        public function getLazyConnectionRef( $db, $groups = [], $domain = false, $flags = 0 ) {
-               $domain = ( $domain !== false ) ? $domain : $this->localDomain;
+               $domain = $this->resolveDomainID( $domain );
 
                return new DBConnRef( $this, [ $db, $groups, $domain, $flags ] );
        }
 
        public function getMaintenanceConnectionRef( $db, $groups = [], $domain = false, $flags = 0 ) {
-               $domain = ( $domain !== false ) ? $domain : $this->localDomain;
+               $domain = $this->resolveDomainID( $domain );
 
                return new MaintainableDBConnRef(
                        $this, $this->getConnection( $db, $groups, $domain, $flags ) );
index bbcb267..a73fac1 100644 (file)
@@ -154,8 +154,8 @@ class RedisConnRef implements LoggerAwareInterface {
         * See: https://github.com/phpredis/phpredis#scan
         *
         * @param int &$iterator
-        * @param string $pattern
-        * @param int $count
+        * @param string|null $pattern
+        * @param int|null $count
         * @return array $res
         */
        public function scan( &$iterator, $pattern = null, $count = null ) {
@@ -169,8 +169,8 @@ class RedisConnRef implements LoggerAwareInterface {
         *
         * @param string $key
         * @param int &$iterator
-        * @param string $pattern
-        * @param int $count
+        * @param string|null $pattern
+        * @param int|null $count
         * @return array $res
         */
        public function sScan( $key, &$iterator, $pattern = null, $count = null ) {
@@ -184,8 +184,8 @@ class RedisConnRef implements LoggerAwareInterface {
         *
         * @param string $key
         * @param int &$iterator
-        * @param string $pattern
-        * @param int $count
+        * @param string|null $pattern
+        * @param int|null $count
         * @return array $res
         */
        public function hScan( $key, &$iterator, $pattern = null, $count = null ) {
@@ -199,8 +199,8 @@ class RedisConnRef implements LoggerAwareInterface {
         *
         * @param string $key
         * @param int &$iterator
-        * @param string $pattern
-        * @param int $count
+        * @param string|null $pattern
+        * @param int|null $count
         * @return array $res
         */
        public function zScan( $key, &$iterator, $pattern = null, $count = null ) {
index 82cc233..aa60242 100644 (file)
@@ -168,7 +168,7 @@ class RedisConnectionPool implements LoggerAwareInterface {
         *
         * @param string $server A hostname/port combination or the absolute path of a UNIX socket.
         *                       If a hostname is specified but no port, port 6379 will be used.
-        * @param LoggerInterface $logger PSR-3 logger intance. [optional]
+        * @param LoggerInterface|null $logger PSR-3 logger intance. [optional]
         * @return RedisConnRef|bool Returns false on failure
         * @throws MWException
         */
@@ -382,7 +382,7 @@ class RedisConnectionPool implements LoggerAwareInterface {
         * Adjust or reset the connection handle read timeout value
         *
         * @param Redis $conn
-        * @param int $timeout Optional
+        * @param int|null $timeout Optional
         */
        public function resetTimeout( Redis $conn, $timeout = null ) {
                $conn->setOption( Redis::OPT_READ_TIMEOUT, $timeout ?: $this->readTimeout );
index e9e338d..39e679b 100644 (file)
@@ -625,7 +625,7 @@ class ManualLogEntry extends LogEntryBase {
        /**
         * Insert the entry into the `logging` table.
         *
-        * @param IDatabase $dbw
+        * @param IDatabase|null $dbw
         * @return int ID of the log entry
         * @throws MWException
         */
index 7b8ffef..17d15d6 100644 (file)
@@ -101,9 +101,9 @@ class LogEventsList extends ContextSource {
         * @param int|string $year Use 0 to start with no year preselected.
         * @param int|string $month A month in the 1..12 range. Use 0 to start with no month
         *  preselected.
-        * @param array $filter
+        * @param array|null $filter
         * @param string $tagFilter Tag to select by default
-        * @param string $action
+        * @param string|null $action
         */
        public function showOptions( $types = [], $user = '', $page = '', $pattern = false, $year = 0,
                $month = 0, $filter = null, $tagFilter = '', $action = null
@@ -524,7 +524,7 @@ class LogEventsList extends ContextSource {
         *
         * @param stdClass $row
         * @param int $field
-        * @param User $user User to check, or null to use $wgUser
+        * @param User|null $user User to check, or null to use $wgUser
         * @return bool
         */
        public static function userCan( $row, $field, User $user = null ) {
@@ -537,7 +537,7 @@ class LogEventsList extends ContextSource {
         *
         * @param int $bitfield Current field
         * @param int $field
-        * @param User $user User to check, or null to use $wgUser
+        * @param User|null $user User to check, or null to use $wgUser
         * @return bool
         */
        public static function userCanBitfield( $bitfield, $field, User $user = null ) {
@@ -745,7 +745,7 @@ class LogEventsList extends ContextSource {
         *
         * @param IDatabase $db
         * @param string $audience Public/user
-        * @param User $user User to check, or null to use $wgUser
+        * @param User|null $user User to check, or null to use $wgUser
         * @return string|bool String on success, false on failure.
         */
        public static function getExcludeClause( $db, $audience = 'public', User $user = null ) {
index 54f2d58..d59c6aa 100644 (file)
@@ -639,7 +639,7 @@ class LogFormatter {
        /**
         * Helper to make a link to the page, taking the plaintext
         * value in consideration.
-        * @param Title $title The page
+        * @param Title|null $title The page
         * @param array $parameters Query parameters
         * @param string|null $html Linktext of the link as raw html
         * @throws MWException
index 9b2e098..d737a4b 100644 (file)
@@ -33,8 +33,8 @@ class PatrolLog {
         *
         * @param int|RecentChange $rc Change identifier or RecentChange object
         * @param bool $auto Was this patrol event automatic?
-        * @param User $user User performing the action or null to use $wgUser
-        * @param string|string[] $tags Change tags to add to the patrol log entry
+        * @param User|null $user User performing the action or null to use $wgUser
+        * @param string|string[]|null $tags Change tags to add to the patrol log entry
         *   ($user should be able to add the specified tags before this is called)
         *
         * @return bool
index b9d9414..1b66c38 100644 (file)
@@ -47,8 +47,8 @@ class MailAddress {
 
        /**
         * @param string $address String with an email address, or a User object
-        * @param string $name Human-readable name if a string address is given
-        * @param string $realName Human-readable real name if a string address is given
+        * @param string|null $name Human-readable name if a string address is given
+        * @param string|null $realName Human-readable real name if a string address is given
         */
        function __construct( $address, $name = null, $realName = null ) {
                if ( is_object( $address ) && $address instanceof User ) {
index 0229ac1..ed6e76f 100644 (file)
@@ -41,7 +41,7 @@ class BmpHandler extends BitmapHandler {
         *
         * @param string $text
         * @param string $mime
-        * @param array $params
+        * @param array|null $params
         * @return array
         */
        function getThumbType( $text, $mime, $params = null ) {
index c76930c..4583632 100644 (file)
@@ -284,7 +284,7 @@ abstract class MediaHandler {
         *
         * @param string $ext Extension of original file
         * @param string $mime MIME type of original file
-        * @param array $params Handler specific rendering parameters
+        * @param array|null $params Handler specific rendering parameters
         * @return array Thumbnail extension and MIME type
         */
        function getThumbType( $ext, $mime, $params = null ) {
index 4aeaac4..dba1b60 100644 (file)
@@ -236,7 +236,7 @@ abstract class MediaTransformOutput {
        }
 
        /**
-        * @param string $title
+        * @param string|null $title
         * @param string|array $params Query parameters to add
         * @return array
         */
index f0f4cda..441513e 100644 (file)
@@ -61,7 +61,7 @@ class TiffHandler extends ExifBitmapHandler {
        /**
         * @param string $ext
         * @param string $mime
-        * @param array $params
+        * @param array|null $params
         * @return bool
         */
        function getThumbType( $ext, $mime, $params = null ) {
index 491fef2..0cb618f 100644 (file)
@@ -46,7 +46,7 @@ class XCFHandler extends BitmapHandler {
         *
         * @param string $ext
         * @param string $mime
-        * @param array $params
+        * @param array|null $params
         * @return array
         */
        function getThumbType( $ext, $mime, $params = null ) {
index 51136ff..abf32d7 100644 (file)
@@ -85,7 +85,7 @@ class Article implements Page {
        /**
         * Constructor and clear the article
         * @param Title $title Reference to a Title object.
-        * @param int $oldId Revision ID, null to fetch from request, zero for current
+        * @param int|null $oldId Revision ID, null to fetch from request, zero for current
         */
        public function __construct( Title $title, $oldId = null ) {
                $this->mOldId = $oldId;
@@ -1962,7 +1962,7 @@ class Article implements Page {
         * @since 1.16 (r52326) for LiquidThreads
         *
         * @param int|null $oldid Revision ID or null
-        * @param User $user The relevant user
+        * @param User|null $user The relevant user
         * @return ParserOutput|bool ParserOutput or false if the given revision ID is not found
         */
        public function getParserOutput( $oldid = null, User $user = null ) {
@@ -2615,8 +2615,8 @@ class Article implements Page {
        /**
         * @param string $reason
         * @param bool $suppress
-        * @param int $u1 Unused
-        * @param bool $u2 Unused
+        * @param int|null $u1 Unused
+        * @param bool|null $u2 Unused
         * @param string &$error
         * @return bool
         */
index 8b42020..9681ece 100644 (file)
@@ -387,8 +387,8 @@ class PageArchive {
         * @param string $comment
         * @param array $fileVersions
         * @param bool $unsuppress
-        * @param User $user User performing the action, or null to use $wgUser
-        * @param string|string[] $tags Change tags to add to log entry
+        * @param User|null $user User performing the action, or null to use $wgUser
+        * @param string|string[]|null $tags Change tags to add to log entry
         *   ($user should be able to add the specified tags before this is called)
         * @return array|bool array(number of file revisions restored, number of image revisions
         *   restored, log message) on success, false on failure.
index c25ff35..0b28074 100644 (file)
@@ -768,7 +768,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   Revision::FOR_PUBLIC       to be displayed to all users
         *   Revision::FOR_THIS_USER    to be displayed to $wgUser
         *   Revision::RAW              get the text regardless of permissions
-        * @param User $user User object to check for, only if FOR_THIS_USER is passed
+        * @param User|null $user User object to check for, only if FOR_THIS_USER is passed
         *   to the $audience parameter
         * @return Content|null The content of the current revision
         *
@@ -808,7 +808,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   Revision::FOR_PUBLIC       to be displayed to all users
         *   Revision::FOR_THIS_USER    to be displayed to the given user
         *   Revision::RAW              get the text regardless of permissions
-        * @param User $user User object to check for, only if FOR_THIS_USER is passed
+        * @param User|null $user User object to check for, only if FOR_THIS_USER is passed
         *   to the $audience parameter
         * @return int User ID for the user that made the last article revision
         */
@@ -827,7 +827,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   Revision::FOR_PUBLIC       to be displayed to all users
         *   Revision::FOR_THIS_USER    to be displayed to the given user
         *   Revision::RAW              get the text regardless of permissions
-        * @param User $user User object to check for, only if FOR_THIS_USER is passed
+        * @param User|null $user User object to check for, only if FOR_THIS_USER is passed
         *   to the $audience parameter
         * @return User|null
         */
@@ -846,7 +846,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   Revision::FOR_PUBLIC       to be displayed to all users
         *   Revision::FOR_THIS_USER    to be displayed to the given user
         *   Revision::RAW              get the text regardless of permissions
-        * @param User $user User object to check for, only if FOR_THIS_USER is passed
+        * @param User|null $user User object to check for, only if FOR_THIS_USER is passed
         *   to the $audience parameter
         * @return string Username of the user that made the last article revision
         */
@@ -864,7 +864,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   Revision::FOR_PUBLIC       to be displayed to all users
         *   Revision::FOR_THIS_USER    to be displayed to the given user
         *   Revision::RAW              get the text regardless of permissions
-        * @param User $user User object to check for, only if FOR_THIS_USER is passed
+        * @param User|null $user User object to check for, only if FOR_THIS_USER is passed
         *   to the $audience parameter
         * @return string Comment stored for the last article revision
         */
@@ -1306,10 +1306,10 @@ class WikiPage implements Page, IDBAccessObject {
         * @param IDatabase $dbw
         * @param Revision $revision For ID number, and text used to set
         *   length and redirect status fields
-        * @param int $lastRevision If given, will not overwrite the page field
+        * @param int|null $lastRevision If given, will not overwrite the page field
         *   when different from the currently set value.
         *   Giving 0 indicates the new page flag should be set on.
-        * @param bool $lastRevIsRedirect If given, will optimize adding and
+        * @param bool|null $lastRevIsRedirect If given, will optimize adding and
         *   removing rows in redirect table.
         * @return bool Success; false if the page row was missing or page_latest changed
         */
@@ -1664,13 +1664,17 @@ class WikiPage implements Page, IDBAccessObject {
         *        to perform updates, if the edit was already saved.
         * @param RevisionSlotsUpdate|null $forUpdate The new content to be saved by the edit (pre PST),
         *        if the edit was not yet saved.
+        * @param bool $forEdit Only re-use if the cached DerivedPageDataUpdater has the current
+        *       revision as the edit's parent revision. This ensures that the same
+        *       DerivedPageDataUpdater cannot be re-used for two consecutive edits.
         *
         * @return DerivedPageDataUpdater
         */
        private function getDerivedDataUpdater(
                User $forUser = null,
                RevisionRecord $forRevision = null,
-               RevisionSlotsUpdate $forUpdate = null
+               RevisionSlotsUpdate $forUpdate = null,
+               $forEdit = false
        ) {
                if ( !$forRevision && !$forUpdate ) {
                        // NOTE: can't re-use an existing derivedDataUpdater if we don't know what the caller is
@@ -1693,7 +1697,8 @@ class WikiPage implements Page, IDBAccessObject {
                        && !$this->derivedDataUpdater->isReusableFor(
                                $forUser,
                                $forRevision,
-                               $forUpdate
+                               $forUpdate,
+                               $forEdit ? $this->getLatest() : null
                        )
                ) {
                        $this->derivedDataUpdater = null;
@@ -1716,16 +1721,18 @@ class WikiPage implements Page, IDBAccessObject {
         * @since 1.32
         *
         * @param User $user
+        * @param RevisionSlotsUpdate|null $forUpdate If given, allows any cached ParserOutput
+        *        that may already have been returned via getDerivedDataUpdater to be re-used.
         *
         * @return PageUpdater
         */
-       public function newPageUpdater( User $user ) {
+       public function newPageUpdater( User $user, RevisionSlotsUpdate $forUpdate = null ) {
                global $wgAjaxEditStash, $wgUseAutomaticEditSummaries, $wgPageCreationLog;
 
                $pageUpdater = new PageUpdater(
                        $user,
                        $this, // NOTE: eventually, PageUpdater should not know about WikiPage
-                       $this->getDerivedDataUpdater( $user ),
+                       $this->getDerivedDataUpdater( $user, null, $forUpdate, true ),
                        $this->getDBLoadBalancer(),
                        $this->getRevisionStore()
                );
@@ -1774,8 +1781,8 @@ class WikiPage implements Page, IDBAccessObject {
         * restores or repeats. The new revision is expected to have the exact same content as
         * the given original revision. This is used with rollbacks and with dummy "null" revisions
         * which are created to record things like page moves.
-        * @param User $user The user doing the edit
-        * @param string $serialFormat IGNORED.
+        * @param User|null $user The user doing the edit
+        * @param string|null $serialFormat IGNORED.
         * @param array|null $tags Change tags to apply to this edit
         * Callers are responsible for permission checks
         * (with ChangeTags::canAddTagsAccompanyingChange)
@@ -1820,10 +1827,13 @@ class WikiPage implements Page, IDBAccessObject {
                        $flags = ( $flags & ~EDIT_MINOR );
                }
 
+               $slotsUpdate = new RevisionSlotsUpdate();
+               $slotsUpdate->modifyContent( 'main', $content );
+
                // NOTE: while doEditContent() executes, callbacks to getDerivedDataUpdater and
                // prepareContentForEdit will generally use the DerivedPageDataUpdater that is also
                // used by this PageUpdater. However, there is no guarantee for this.
-               $updater = $this->newPageUpdater( $user );
+               $updater = $this->newPageUpdater( $user, $slotsUpdate );
                $updater->setContent( 'main', $content );
                $updater->setOriginalRevisionId( $originalRevId );
                $updater->setUndidRevisionId( $undidRevId );
@@ -1935,7 +1945,7 @@ class WikiPage implements Page, IDBAccessObject {
                $updater = $this->getDerivedDataUpdater( $user, $revision, $slots );
 
                if ( !$updater->isUpdatePrepared() ) {
-                       $updater->prepareContent( $user, $slots, [], $useCache );
+                       $updater->prepareContent( $user, $slots, $useCache );
 
                        if ( $revision ) {
                                $updater->prepareUpdate( $revision );
@@ -1987,7 +1997,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @param int &$cascade Set to false if cascading protection isn't allowed.
         * @param string $reason
         * @param User $user The user updating the restrictions
-        * @param string|string[] $tags Change tags to add to the pages and protection log entries
+        * @param string|string[]|null $tags Change tags to add to the pages and protection log entries
         *   ($user should be able to add the specified tags before this is called)
         * @return Status Status object; if action is taken, $status->value is the log_id of the
         *   protection log entry.
@@ -2403,10 +2413,10 @@ class WikiPage implements Page, IDBAccessObject {
         * @param string $reason Delete reason for deletion log
         * @param bool $suppress Suppress all revisions and log the deletion in
         *        the suppression log instead of the deletion log
-        * @param int $u1 Unused
-        * @param bool $u2 Unused
+        * @param int|null $u1 Unused
+        * @param bool|null $u2 Unused
         * @param array|string &$error Array of errors to append to
-        * @param User $user The deleting user
+        * @param User|null $user The deleting user
         * @return bool True if successful
         */
        public function doDeleteArticle(
@@ -2425,10 +2435,10 @@ class WikiPage implements Page, IDBAccessObject {
         * @param string $reason Delete reason for deletion log
         * @param bool $suppress Suppress all revisions and log the deletion in
         *   the suppression log instead of the deletion log
-        * @param int $u1 Unused
-        * @param bool $u2 Unused
+        * @param int|null $u1 Unused
+        * @param bool|null $u2 Unused
         * @param array|string &$error Array of errors to append to
-        * @param User $deleter The deleting user
+        * @param User|null $deleter The deleting user
         * @param array $tags Tags to apply to the deletion action
         * @param string $logsubtype
         * @return Status Status object; if successful, $status->value is the log_id of the
@@ -2542,7 +2552,19 @@ class WikiPage implements Page, IDBAccessObject {
                        // Fetch all rows in case the DB needs that to properly lock them.
                }
 
-               // Get all of the page revisions
+               // If SCHEMA_COMPAT_WRITE_OLD is set, also select all extra fields we still write,
+               // so we can copy it to the archive table.
+               // We know the fields exist, otherwise SCHEMA_COMPAT_WRITE_OLD could not function.
+               if ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
+                       $revQuery['fields'][] = 'rev_text_id';
+
+                       if ( $wgContentHandlerUseDB ) {
+                               $revQuery['fields'][] = 'rev_content_model';
+                               $revQuery['fields'][] = 'rev_content_format';
+                       }
+               }
+
+                       // Get all of the page revisions
                $res = $dbw->select(
                        $revQuery['tables'],
                        $revQuery['fields'],
@@ -2584,17 +2606,15 @@ class WikiPage implements Page, IDBAccessObject {
                        ] + $commentStore->insert( $dbw, 'ar_comment', $comment )
                                + $actorMigration->getInsertValues( $dbw, 'ar_user', $user );
 
-                       if ( $wgMultiContentRevisionSchemaMigrationStage < MIGRATION_NEW ) {
+                       if ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_OLD ) {
                                $rowInsert['ar_text_id'] = $row->rev_text_id;
-                       }
 
-                       if (
-                               $wgContentHandlerUseDB &&
-                               $wgMultiContentRevisionSchemaMigrationStage <= MIGRATION_WRITE_BOTH
-                       ) {
-                               $rowInsert['ar_content_model'] = $row->rev_content_model;
-                               $rowInsert['ar_content_format'] = $row->rev_content_format;
+                               if ( $wgContentHandlerUseDB ) {
+                                       $rowInsert['ar_content_model'] = $row->rev_content_model;
+                                       $rowInsert['ar_content_format'] = $row->rev_content_format;
+                               }
                        }
+
                        $rowsInsert[] = $rowInsert;
                        $revids[] = $row->rev_id;
 
index 2323142..b00ec3a 100644 (file)
@@ -456,8 +456,8 @@ abstract class IndexPager extends ContextSource implements Pager {
         * Make a self-link
         *
         * @param string $text Text displayed on the link
-        * @param array $query Associative array of parameter to be in the query string
-        * @param string $type Link type used to create additional attributes, like "rel", "class" or
+        * @param array|null $query Associative array of parameter to be in the query string
+        * @param string|null $type Link type used to create additional attributes, like "rel", "class" or
         *  "title". Valid values (non-exhaustive list): 'first', 'last', 'prev', 'next', 'asc', 'desc'.
         * @return string HTML fragment
         */
index 99ca07a..10da0b0 100644 (file)
@@ -112,7 +112,7 @@ class CoreParserFunctions {
        /**
         * @param Parser $parser
         * @param string $date
-        * @param string $defaultPref
+        * @param string|null $defaultPref
         *
         * @return string
         */
@@ -165,7 +165,7 @@ class CoreParserFunctions {
         *
         * @param Parser $parser
         * @param string $s The text to encode.
-        * @param string $arg (optional): The type of encoding.
+        * @param string|null $arg (optional): The type of encoding.
         * @return string
         */
        public static function urlencode( $parser, $s = '', $arg = null ) {
@@ -292,7 +292,7 @@ class CoreParserFunctions {
        /**
         * @param Parser $parser
         * @param string $num
-        * @param string $arg
+        * @param string|null $arg
         * @return string
         */
        public static function formatnum( $parser, $num = '', $arg = null ) {
@@ -556,7 +556,7 @@ class CoreParserFunctions {
         * Note: function name changed to "mwnamespace" rather than "namespace"
         * to not break PHP 5.3
         * @param Parser $parser
-        * @param string $title
+        * @param string|null $title
         * @return mixed|string
         */
        public static function mwnamespace( $parser, $title = null ) {
@@ -613,7 +613,7 @@ class CoreParserFunctions {
         * Functions to get and normalize pagenames, corresponding to the magic words
         * of the same names
         * @param Parser $parser
-        * @param string $title
+        * @param string|null $title
         * @return string
         */
        public static function pagename( $parser, $title = null ) {
@@ -721,8 +721,8 @@ class CoreParserFunctions {
         * can't be called too many times per page.
         * @param Parser $parser
         * @param string $name
-        * @param string $arg1
-        * @param string $arg2
+        * @param string|null $arg1
+        * @param string|null $arg2
         * @return string
         */
        public static function pagesincategory( $parser, $name = '', $arg1 = null, $arg2 = null ) {
@@ -789,7 +789,7 @@ class CoreParserFunctions {
         *
         * @param Parser $parser
         * @param string $page Name of page to check (Default: empty string)
-        * @param string $raw Should number be human readable with commas or just number
+        * @param string|null $raw Should number be human readable with commas or just number
         * @return string
         */
        public static function pagesize( $parser, $page = '', $raw = null ) {
@@ -1141,7 +1141,7 @@ class CoreParserFunctions {
        /**
         * Get the pageid of a specified page
         * @param Parser $parser
-        * @param string $title Title to get the pageid from
+        * @param string|null $title Title to get the pageid from
         * @return int|null|string
         * @since 1.23
         */
@@ -1186,7 +1186,7 @@ class CoreParserFunctions {
        /**
         * Get the id from the last revision of a specified page.
         * @param Parser $parser
-        * @param string $title Title to get the id from
+        * @param string|null $title Title to get the id from
         * @return int|null|string
         * @since 1.23
         */
@@ -1203,7 +1203,7 @@ class CoreParserFunctions {
        /**
         * Get the day from the last revision of a specified page.
         * @param Parser $parser
-        * @param string $title Title to get the day from
+        * @param string|null $title Title to get the day from
         * @return string
         * @since 1.23
         */
@@ -1220,7 +1220,7 @@ class CoreParserFunctions {
        /**
         * Get the day with leading zeros from the last revision of a specified page.
         * @param Parser $parser
-        * @param string $title Title to get the day from
+        * @param string|null $title Title to get the day from
         * @return string
         * @since 1.23
         */
@@ -1237,7 +1237,7 @@ class CoreParserFunctions {
        /**
         * Get the month with leading zeros from the last revision of a specified page.
         * @param Parser $parser
-        * @param string $title Title to get the month from
+        * @param string|null $title Title to get the month from
         * @return string
         * @since 1.23
         */
@@ -1254,7 +1254,7 @@ class CoreParserFunctions {
        /**
         * Get the month from the last revision of a specified page.
         * @param Parser $parser
-        * @param string $title Title to get the month from
+        * @param string|null $title Title to get the month from
         * @return string
         * @since 1.23
         */
@@ -1271,7 +1271,7 @@ class CoreParserFunctions {
        /**
         * Get the year from the last revision of a specified page.
         * @param Parser $parser
-        * @param string $title Title to get the year from
+        * @param string|null $title Title to get the year from
         * @return string
         * @since 1.23
         */
@@ -1288,7 +1288,7 @@ class CoreParserFunctions {
        /**
         * Get the timestamp from the last revision of a specified page.
         * @param Parser $parser
-        * @param string $title Title to get the timestamp from
+        * @param string|null $title Title to get the timestamp from
         * @return string
         * @since 1.23
         */
@@ -1305,7 +1305,7 @@ class CoreParserFunctions {
        /**
         * Get the user from the last revision of a specified page.
         * @param Parser $parser
-        * @param string $title Title to get the user from
+        * @param string|null $title Title to get the user from
         * @return string
         * @since 1.23
         */
index 0270828..fd5ccb8 100644 (file)
@@ -401,7 +401,7 @@ class Parser {
         * @param ParserOptions $options
         * @param bool $linestart
         * @param bool $clearState
-        * @param int $revid Number to pass in {{REVISIONID}}
+        * @param int|null $revid Number to pass in {{REVISIONID}}
         * @return ParserOutput A ParserOutput
         */
        public function parse(
@@ -683,7 +683,7 @@ class Parser {
         * Also removes comments.
         * Do not call this function recursively.
         * @param string $text
-        * @param Title $title
+        * @param Title|null $title
         * @param ParserOptions $options
         * @param int|null $revid
         * @param bool|PPFrame $frame
@@ -789,7 +789,7 @@ class Parser {
        /**
         * Accessor/mutator for the Title object
         *
-        * @param Title $x Title object or null to just get the current one
+        * @param Title|null $x Title object or null to just get the current one
         * @return Title
         */
        public function Title( $x = null ) {
@@ -843,7 +843,7 @@ class Parser {
        /**
         * Accessor/mutator for the ParserOptions object
         *
-        * @param ParserOptions $x New value or null to just get the current one
+        * @param ParserOptions|null $x New value or null to just get the current one
         * @return ParserOptions Current ParserOptions object
         */
        public function Options( $x = null ) {
@@ -1914,7 +1914,7 @@ class Parser {
         * @since 1.21
         * @param string|bool $url Optional URL, to extract the domain from for rel =>
         *   nofollow if appropriate
-        * @param Title $title Optional Title, for wgNoFollowNsExceptions lookups
+        * @param Title|null $title Optional Title, for wgNoFollowNsExceptions lookups
         * @return string|null Rel attribute for $url
         */
        public static function getExternalLinkRel( $url = false, $title = null ) {
index 5959281..3a7a1d6 100644 (file)
@@ -932,8 +932,8 @@ class ParserOptions {
         * @warning For interaction with the parser cache, use
         *  WikiPage::makeParserOptions(), ContentHandler::makeParserOptions(), or
         *  ParserOptions::newCanonical() instead.
-        * @param User $user
-        * @param Language $lang
+        * @param User|null $user
+        * @param Language|null $lang
         */
        public function __construct( $user = null, $lang = null ) {
                if ( $user === null ) {
@@ -1271,7 +1271,7 @@ class ParserOptions {
         *
         * @since 1.17
         * @param string[] $forOptions
-        * @param Title $title Used to get the content language of the page (since r97636)
+        * @param Title|null $title Used to get the content language of the page (since r97636)
         * @return string Page rendering hash
         */
        public function optionsHash( $forOptions, $title = null ) {
index 265d151..182648a 100644 (file)
@@ -770,7 +770,7 @@ class ParserOutput extends CacheTime {
         * Add one or more variables to be set in mw.config in JavaScript.
         *
         * @param string|array $keys Key or array of key/value pairs.
-        * @param mixed $value [optional] Value of the configuration variable.
+        * @param mixed|null $value [optional] Value of the configuration variable.
         * @since 1.23
         */
        public function addJsConfigVars( $keys, $value = null ) {
index 21498f8..71b71e0 100644 (file)
@@ -463,12 +463,12 @@ class Sanitizer {
         * Cleans up HTML, removes dangerous tags and attributes, and
         * removes HTML comments
         * @param string $text
-        * @param callable $processCallback Callback to do any variable or parameter
+        * @param callable|null $processCallback Callback to do any variable or parameter
         *   replacements in HTML attribute values
         * @param array|bool $args Arguments for the processing callback
         * @param array $extratags For any extra tags to include
         * @param array $removetags For any tags (default or extra) to exclude
-        * @param callable $warnCallback (Deprecated) Callback allowing the
+        * @param callable|null $warnCallback (Deprecated) Callback allowing the
         *   addition of a tracking category when bad input is encountered.
         *   DO NOT ADD NEW PARAMETERS AFTER $warnCallback, since it will be
         *   removed shortly.
index 7b888ab..4af86ae 100644 (file)
@@ -53,7 +53,7 @@ class PoolWorkArticleView extends PoolCounterWork {
         * @param int $revid ID of the revision being parsed.
         * @param bool $useParserCache Whether to use the parser cache.
         *   operation.
-        * @param Content|string $content Content to parse or null to load it; may
+        * @param Content|string|null $content Content to parse or null to load it; may
         *   also be given as a wikitext string, for BC.
         */
        public function __construct( WikiPage $page, ParserOptions $parserOptions,
index 03e4bdb..bcd15b3 100644 (file)
@@ -1080,7 +1080,7 @@ class DefaultPreferencesFactory implements PreferencesFactory {
                if ( $this->config->get( 'StructuredChangeFiltersShowPreference' ) ) {
                        $defaultPreferences['rcenhancedfilters-disable'] = [
                                'type' => 'toggle',
-                               'section' => 'rc/opt-out',
+                               'section' => 'rc/optoutrc',
                                'label-message' => 'rcfilters-preference-label',
                                'help-message' => 'rcfilters-preference-help',
                        ];
@@ -1283,7 +1283,7 @@ class DefaultPreferencesFactory implements PreferencesFactory {
                if ( $this->config->get( 'StructuredChangeFiltersShowWatchlistPreference' ) ) {
                        $defaultPreferences['wlenhancedfilters-disable'] = [
                                'type' => 'toggle',
-                               'section' => 'watchlist/opt-out',
+                               'section' => 'watchlist/optoutwatchlist',
                                'label-message' => 'rcfilters-watchlist-preference-label',
                                'help-message' => 'rcfilters-watchlist-preference-help',
                        ];
index d02011f..bf4ddc7 100644 (file)
@@ -165,7 +165,7 @@ abstract class Profiler {
        abstract public function scopedProfileIn( $section );
 
        /**
-        * @param SectionProfileCallback &$section
+        * @param SectionProfileCallback|null &$section
         */
        public function scopedProfileOut( SectionProfileCallback &$section = null ) {
                $section = null;
index 43f294b..aa632c3 100644 (file)
@@ -238,8 +238,8 @@ class ResourceLoader implements LoggerAwareInterface {
 
        /**
         * Register core modules and runs registration hooks.
-        * @param Config $config [optional]
-        * @param LoggerInterface $logger [optional]
+        * @param Config|null $config [optional]
+        * @param LoggerInterface|null $logger [optional]
         */
        public function __construct( Config $config = null, LoggerInterface $logger = null ) {
                global $IP;
@@ -317,7 +317,7 @@ class ResourceLoader implements LoggerAwareInterface {
         * Register a module with the ResourceLoader system.
         *
         * @param mixed $name Name of module as a string or List of name/object pairs as an array
-        * @param array $info Module info array. For backwards compatibility with 1.17alpha,
+        * @param array|null $info Module info array. For backwards compatibility with 1.17alpha,
         *   this may also be a ResourceLoaderModule object. Optional when using
         *   multiple-registration calling style.
         * @throws MWException If a duplicate module registration is attempted
@@ -446,7 +446,7 @@ class ResourceLoader implements LoggerAwareInterface {
         * Source IDs are typically the same as the Wiki ID or database name (e.g. lowercase a-z).
         *
         * @param array|string $id Source ID (string), or [ id1 => loadUrl, id2 => loadUrl, ... ]
-        * @param string|array $loadUrl load.php url (string), or array with loadUrl key for
+        * @param string|array|null $loadUrl load.php url (string), or array with loadUrl key for
         *  backwards-compatibility.
         * @throws MWException
         */
@@ -570,8 +570,7 @@ class ResourceLoader implements LoggerAwareInterface {
        }
 
        /**
-        * Return whether the definition of a module corresponds to a simple ResourceLoaderFileModule
-        * or one of its subclasses.
+        * Whether the module is a ResourceLoaderFileModule (including subclasses).
         *
         * @param string $name Module name
         * @return bool
@@ -584,14 +583,13 @@ class ResourceLoader implements LoggerAwareInterface {
                if ( isset( $info['object'] ) ) {
                        return false;
                }
-               if (
-                       isset( $info['class'] ) &&
-                       $info['class'] !== ResourceLoaderFileModule::class &&
-                       !is_subclass_of( $info['class'], ResourceLoaderFileModule::class )
-               ) {
-                       return false;
-               }
-               return true;
+               return (
+                       // The implied default for 'class' is ResourceLoaderFileModule
+                       !isset( $info['class'] ) ||
+                       // Explicit default
+                       $info['class'] === ResourceLoaderFileModule::class ||
+                       is_subclass_of( $info['class'], ResourceLoaderFileModule::class )
+               );
        }
 
        /**
@@ -1310,7 +1308,7 @@ MESSAGE;
         *         Set the state of modules with the given names to the given states
         *
         * @param string $name
-        * @param string $state
+        * @param string|null $state
         * @return string JavaScript code
         */
        public static function makeLoaderStateScript( $name, $state = null ) {
@@ -1408,11 +1406,11 @@ MESSAGE;
         *        Registers modules with the given names and parameters.
         *
         * @param string $name Module name
-        * @param string $version Module version hash
-        * @param array $dependencies List of module names on which this module depends
-        * @param string $group Group which the module is in
-        * @param string $source Source of the module, or 'local' if not foreign
-        * @param string $skip Script body of the skip function
+        * @param string|null $version Module version hash
+        * @param array|null $dependencies List of module names on which this module depends
+        * @param string|null $group Group which the module is in
+        * @param string|null $source Source of the module, or 'local' if not foreign
+        * @param string|null $skip Script body of the skip function
         * @return string JavaScript code
         */
        public static function makeLoaderRegisterScript( $name, $version = null,
@@ -1466,7 +1464,7 @@ MESSAGE;
         *       Register sources with the given IDs and properties.
         *
         * @param string $id Source ID
-        * @param string $loadUrl load.php url
+        * @param string|null $loadUrl load.php url
         * @return string JavaScript code
         */
        public static function makeLoaderSourcesScript( $id, $loadUrl = null ) {
@@ -1503,7 +1501,8 @@ MESSAGE;
         * startup module if the client has adequate support for MediaWiki JavaScript code.
         *
         * @param string $script JavaScript code
-        * @param string $nonce [optional] Content-Security-Policy nonce (from OutputPage::getCSPNonce)
+        * @param string|null $nonce [optional] Content-Security-Policy nonce
+        *  (from OutputPage::getCSPNonce)
         * @return string|WrappedString HTML
         */
        public static function makeInlineScript( $script, $nonce = null ) {
@@ -1645,10 +1644,10 @@ MESSAGE;
         * @param array $modules
         * @param string $lang
         * @param string $skin
-        * @param string $user
-        * @param string $version
+        * @param string|null $user
+        * @param string|null $version
         * @param bool $debug
-        * @param string $only
+        * @param string|null $only
         * @param bool $printable
         * @param bool $handheld
         * @param array $extraQuery
index 20faf8b..8d08366 100644 (file)
@@ -405,7 +405,8 @@ class ResourceLoaderClientHtml {
         * @param array $modules One or more module names
         * @param string $only ResourceLoaderModule TYPE_ class constant
         * @param array $extraQuery [optional] Array with extra query parameters for the request
-        * @param string $nonce [optional] Content-Security-Policy nonce (from OutputPage::getCSPNonce)
+        * @param string|null $nonce [optional] Content-Security-Policy nonce
+        *  (from OutputPage::getCSPNonce)
         * @return string|WrappedStringList HTML
         */
        public static function makeLoad( ResourceLoaderContext $mainContext, array $modules, $only,
index 68ea0c0..b2ca291 100644 (file)
@@ -153,10 +153,10 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         *
         * @param array $options List of options; if not given or empty, an empty module will be
         *     constructed
-        * @param string $localBasePath Base path to prepend to all local paths in $options. Defaults
-        *     to $IP
-        * @param string $remoteBasePath Base path to prepend to all remote paths in $options. Defaults
-        *     to $wgResourceBasePath
+        * @param string|null $localBasePath Base path to prepend to all local paths in $options.
+        *     Defaults to $IP
+        * @param string|null $remoteBasePath Base path to prepend to all remote paths in $options.
+        *     Defaults to $wgResourceBasePath
         *
         * Below is a description for the $options array:
         * @throws InvalidArgumentException
@@ -298,9 +298,9 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * Implementation note: the amount of global state used in this function is staggering.
         *
         * @param array $options Module definition
-        * @param string $localBasePath Path to use if not provided in module definition. Defaults
+        * @param string|null $localBasePath Path to use if not provided in module definition. Defaults
         *     to $IP
-        * @param string $remoteBasePath Path to use if not provided in module definition. Defaults
+        * @param string|null $remoteBasePath Path to use if not provided in module definition. Defaults
         *     to $wgResourceBasePath
         * @return array Array( localBasePath, remoteBasePath )
         */
@@ -653,7 +653,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         *
         * @param array $list List of lists to select from
         * @param string $key Key to look for in $map
-        * @param string $fallback Key to look for in $list if $key doesn't exist
+        * @param string|null $fallback Key to look for in $list if $key doesn't exist
         * @return array List of elements from $map which matched $key or $fallback,
         *  or an empty list in case of no match
         */
@@ -826,7 +826,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @private
         * @param array $styles Map of media type to file paths to read, remap, and concatenate
         * @param bool $flip
-        * @param ResourceLoaderContext $context
+        * @param ResourceLoaderContext|null $context
         * @return array List of concatenated and remapped CSS data from $styles,
         *     keyed by media type
         * @throws MWException
index c779240..b711cbd 100644 (file)
@@ -40,6 +40,7 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
 
        protected $images = [];
        protected $defaultColor = null;
+       protected $useDataURI = true;
        protected $variants = [];
        protected $prefix = null;
        protected $selectorWithoutVariant = '.{prefix}-{name}';
@@ -51,7 +52,7 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
         *
         * @param array $options List of options; if not given or empty, an empty module will be
         *     constructed
-        * @param string $localBasePath Base path to prepend to all local paths in $options. Defaults
+        * @param string|null $localBasePath Base path to prepend to all local paths in $options. Defaults
         *     to $IP
         *
         * Below is a description for the $options array:
@@ -183,6 +184,9 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
                                        $this->{$member} = $option;
                                        break;
 
+                               case 'useDataURI':
+                                       $this->{$member} = (bool)$option;
+                                       break;
                                case 'defaultColor':
                                case 'prefix':
                                case 'selectorWithoutVariant':
@@ -358,7 +362,7 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
                $script,
                $variant = null
        ) {
-               $imageDataUri = $image->getDataUri( $context, $variant, 'original' );
+               $imageDataUri = $this->useDataURI ? $image->getDataUri( $context, $variant, 'original' ) : false;
                $primaryUrl = $imageDataUri ?: $image->getUrl( $context, $script, $variant, 'original' );
                $declarations = $this->getCssDeclarations(
                        $primaryUrl,
@@ -443,7 +447,7 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
         * Extract a local base path from module definition information.
         *
         * @param array $options Module definition
-        * @param string $localBasePath Path to use if not provided in module definition. Defaults
+        * @param string|null $localBasePath Path to use if not provided in module definition. Defaults
         *     to $IP
         * @return string Local base path
         */
index e78484a..f6716e7 100644 (file)
@@ -72,7 +72,7 @@ class ResourceLoaderLanguageDataModule extends ResourceLoaderModule {
        }
 
        /**
-        * @param ResourceLoaderContext $context
+        * @param ResourceLoaderContext|null $context
         * @return array
         */
        public function getDependencies( ResourceLoaderContext $context = null ) {
index 57260ba..72ccf66 100644 (file)
@@ -60,7 +60,7 @@ class ResourceLoaderLanguageNamesModule extends ResourceLoaderModule {
        }
 
        /**
-        * @param ResourceLoaderContext $context
+        * @param ResourceLoaderContext|null $context
         * @return array
         */
        public function getDependencies( ResourceLoaderContext $context = null ) {
index 609abb8..7018f16 100644 (file)
@@ -347,7 +347,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
         * Note: It is expected that $context will be made non-optional in the near
         * future.
         *
-        * @param ResourceLoaderContext $context
+        * @param ResourceLoaderContext|null $context
         * @return array List of module names as strings
         */
        public function getDependencies( ResourceLoaderContext $context = null ) {
index a0061e3..0ad7fe4 100644 (file)
@@ -61,7 +61,7 @@ class ResourceLoaderSpecialCharacterDataModule extends ResourceLoaderModule {
        }
 
        /**
-        * @param ResourceLoaderContext $context
+        * @param ResourceLoaderContext|null $context
         * @return array
         */
        public function getDependencies( ResourceLoaderContext $context = null ) {
index ffa55c0..0d40ad7 100644 (file)
@@ -32,7 +32,7 @@ class ResourceLoaderUserOptionsModule extends ResourceLoaderModule {
        protected $targets = [ 'desktop', 'mobile' ];
 
        /**
-        * @param ResourceLoaderContext $context
+        * @param ResourceLoaderContext|null $context
         * @return array List of module names as strings
         */
        public function getDependencies( ResourceLoaderContext $context = null ) {
index ff5de0d..b699b36 100644 (file)
@@ -76,7 +76,8 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
        protected $group;
 
        /**
-        * @param array $options For back-compat, this can be omitted in favour of overwriting getPages.
+        * @param array|null $options For back-compat, this can be omitted in favour of overwriting
+        *  getPages.
         */
        public function __construct( array $options = null ) {
                if ( is_null( $options ) ) {
index 643c2c1..f2929a3 100644 (file)
@@ -35,7 +35,7 @@ class SearchDatabase extends SearchEngine {
        protected $db;
 
        /**
-        * @param IDatabase $db The database to search from
+        * @param IDatabase|null $db The database to search from
         */
        public function __construct( IDatabase $db = null ) {
                if ( $db ) {
index b2bacda..63c4610 100644 (file)
@@ -505,7 +505,7 @@ abstract class SearchEngine {
         *
         * @todo This isn't ideal, we'd really like to have content-specific handling here
         * @param Title $t Title we're indexing
-        * @param Content $c Content of the page to index
+        * @param Content|null $c Content of the page to index
         * @return string
         */
        public function getTextFromContent( Title $t, Content $c = null ) {
index 8cdca57..ecb6f43 100644 (file)
@@ -19,7 +19,7 @@ class SearchEngineFactory {
 
        /**
         * Create SearchEngine of the given type.
-        * @param string $type
+        * @param string|null $type
         * @return SearchEngine
         */
        public function create( $type = null ) {
index 20462cf..2cec241 100644 (file)
@@ -327,8 +327,8 @@ class SearchHighlighter {
         * @param string $text
         * @param int $start
         * @param int $end
-        * @param int &$posStart (out) actual start position
-        * @param int &$posEnd (out) actual end position
+        * @param int|null &$posStart (out) actual start position
+        * @param int|null &$posEnd (out) actual end position
         * @return string
         */
        function extract( $text, $start, $end, &$posStart = null, &$posEnd = null ) {
@@ -525,12 +525,12 @@ class SearchHighlighter {
                        }
                        --$contextlines;
                        // truncate function changes ... to relevant i18n message.
-                       $pre = $wgContLang->truncate( $m[1], - $contextchars, '...', false );
+                       $pre = $wgContLang->truncateForVisual( $m[1], - $contextchars, '...', false );
 
                        if ( count( $m ) < 3 ) {
                                $post = '';
                        } else {
-                               $post = $wgContLang->truncate( $m[3], $contextchars, '...', false );
+                               $post = $wgContLang->truncateForVisual( $m[3], $contextchars, '...', false );
                        }
 
                        $found = $m[2];
index 43bd3be..30ac92d 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup Search
  */
 
+use Wikimedia\Rdbms\IResultWrapper;
+
 /**
  * Search engine hook base class for Mssql (ConText).
  * @ingroup Search
@@ -168,7 +170,7 @@ class SearchMssql extends SearchDatabase {
         * @param int $id
         * @param string $title
         * @param string $text
-        * @return bool|ResultWrapper
+        * @return bool|IResultWrapper
         */
        function update( $id, $title, $text ) {
                // We store the column data as UTF-8 byte order marked binary stream
@@ -191,7 +193,7 @@ class SearchMssql extends SearchDatabase {
         *
         * @param int $id
         * @param string $title
-        * @return bool|ResultWrapper
+        * @return bool|IResultWrapper
         */
        function updateTitle( $id, $title ) {
                $table = $this->db->tableName( 'searchindex' );
index 2f20d9d..bd19a84 100644 (file)
@@ -66,7 +66,7 @@ class SearchResult {
         * Return a new SearchResult and initializes it with a title.
         *
         * @param Title $title
-        * @param SearchResultSet $parentSet
+        * @param SearchResultSet|null $parentSet
         * @return SearchResult
         */
        public static function newFromTitle( $title, SearchResultSet $parentSet = null ) {
index 74925bd..aaa522a 100644 (file)
@@ -339,7 +339,7 @@ class CookieSessionProvider extends SessionProvider {
         * @param \WebRequest $request
         * @param string $key
         * @param string $prefix
-        * @param mixed $default
+        * @param mixed|null $default
         * @return mixed
         */
        protected function getCookie( $request, $key, $prefix, $default = null ) {
index e9a03f2..3dc8299 100644 (file)
@@ -291,7 +291,7 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
        /**
         * Fetch a value from the session
         * @param string|int $key
-        * @param mixed $default Returned if $this->exists( $key ) would be false
+        * @param mixed|null $default Returned if $this->exists( $key ) would be false
         * @return mixed
         */
        public function get( $key, $default = null ) {
@@ -522,7 +522,7 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
        /**
         * Fetch a value from the session that was set with self::setSecret()
         * @param string|int $key
-        * @param mixed $default Returned if $this->exists( $key ) would be false or decryption fails
+        * @param mixed|null $default Returned if $this->exists( $key ) would be false or decryption fails
         * @return mixed
         */
        public function getSecret( $key, $default = null ) {
index a376037..58163c6 100644 (file)
@@ -457,7 +457,7 @@ final class SessionBackend {
 
        /**
         * Set the "logged out" timestamp
-        * @param int $ts
+        * @param int|null $ts
         */
        public function setLoggedOutTimestamp( $ts = null ) {
                $ts = (int)$ts;
index 02ae23d..896e62f 100644 (file)
@@ -24,6 +24,7 @@
 namespace MediaWiki\Session;
 
 use Language;
+use Message;
 
 /**
  * This exists to make IDEs happy, so they don't see the
index ee9f1eb..0ddc443 100644 (file)
@@ -110,6 +110,7 @@ class Shell {
        /**
         * Returns a new instance of Command class
         *
+        * @note You should check Shell::isDisabled() before calling this
         * @param string|string[] ...$commands String or array of strings representing the command to
         * be executed, each value will be escaped.
         *   Example:   [ 'convert', '-font', 'font name' ] would produce "'convert' '-font' 'font name'"
@@ -223,6 +224,7 @@ class Shell {
         * Note that $parameters should be a flat array and an option with an argument
         * should consist of two consecutive items in the array (do not use "--option value").
         *
+        * @note You should check Shell::isDisabled() before calling this
         * @param string $script MediaWiki CLI script with full path
         * @param string[] $parameters Arguments and options to the script
         * @param array $options Associative array of options:
index e0b8d27..91d9ef7 100644 (file)
@@ -39,7 +39,7 @@ class SiteSQLStore {
         *             or MediaWikiServices::getSiteLookup() instead.
         *
         * @param null $sitesTable IGNORED
-        * @param null $cache IGNORED
+        * @param BagOStuff|null $cache IGNORED
         *
         * @return SiteStore
         */
index c1c856d..5c4d812 100644 (file)
@@ -575,7 +575,7 @@ abstract class BaseTemplate extends QuickTemplate {
         * If you pass "flat" as an option then the returned array will be a flat array
         * of footer icons instead of a key/value array of footerlinks arrays broken
         * up into categories.
-        * @param string $option
+        * @param string|null $option
         * @return array|mixed
         */
        function getFooterLinks( $option = null ) {
@@ -612,7 +612,7 @@ abstract class BaseTemplate extends QuickTemplate {
         * in the list of footer icons. This is mostly useful for skins which only
         * display the text from footericons instead of the images and don't want a
         * duplicate copyright statement because footerlinks already rendered one.
-        * @param string $option
+        * @param string|null $option
         * @return array
         */
        function getFooterIcons( $option = null ) {
index 296c133..969ac51 100644 (file)
@@ -35,7 +35,7 @@ abstract class QuickTemplate {
        protected $config;
 
        /**
-        * @param Config $config
+        * @param Config|null $config
         */
        function __construct( Config $config = null ) {
                $this->data = [];
@@ -73,7 +73,7 @@ abstract class QuickTemplate {
         * Gets the template data requested
         * @since 1.22
         * @param string $name Key for the data
-        * @param mixed $default Optional default (or null)
+        * @param mixed|null $default Optional default (or null)
         * @return mixed The value of the data requested or the deafult
         */
        public function get( $name, $default = null ) {
index 51be3b6..dde60f5 100644 (file)
@@ -391,13 +391,13 @@ abstract class Skin extends ContextSource {
 
        /**
         * Outputs the HTML generated by other functions.
-        * @param OutputPage $out
+        * @param OutputPage|null $out
         */
        abstract function outputPage( OutputPage $out = null );
 
        /**
         * @param array $data
-        * @param string $nonce OutputPage::getCSPNonce()
+        * @param string|null $nonce OutputPage::getCSPNonce()
         * @return string|WrappedString HTML
         */
        static function makeVariablesScript( $data, $nonce = null ) {
@@ -739,7 +739,7 @@ abstract class Skin extends ContextSource {
        }
 
        /**
-        * @param OutputPage $out Defaults to $this->getOutput() if left as null
+        * @param OutputPage|null $out Defaults to $this->getOutput() if left as null
         * @return string
         */
        function subPageSubtitle( $out = null ) {
@@ -1605,7 +1605,7 @@ abstract class Skin extends ContextSource {
         *   the current page, if the section is included from a template)
         * @param string $section The designation of the section being pointed to,
         *   to be included in the link, like "&section=$section"
-        * @param string $tooltip The tooltip to use for the link: will be escaped
+        * @param string|null $tooltip The tooltip to use for the link: will be escaped
         *   and wrapped in the 'editsectionhint' message
         * @param string $lang Language code
         * @return string HTML to use for edit link
index 507688d..7fc0c3a 100644 (file)
@@ -206,7 +206,7 @@ class SkinTemplate extends Skin {
        /**
         * initialize various variables and generate the template
         *
-        * @param OutputPage $out
+        * @param OutputPage|null $out
         */
        function outputPage( OutputPage $out = null ) {
                Profiler::instance()->setTemplated( true );
@@ -508,7 +508,7 @@ class SkinTemplate extends Skin {
         *
         * @since 1.31
         *
-        * @param array $personalTools
+        * @param array|null $personalTools
         * @param array $options
         * @return string
         */
index 557bd9c..1476e85 100644 (file)
@@ -70,7 +70,7 @@ abstract class AuthManagerSpecialPage extends SpecialPage {
         * Used to preserve POST data over a HTTP redirect.
         *
         * @param array $data
-        * @param bool $wasPosted
+        * @param bool|null $wasPosted
         */
        protected function setRequest( array $data, $wasPosted = null ) {
                $request = $this->getContext()->getRequest();
@@ -223,7 +223,7 @@ abstract class AuthManagerSpecialPage extends SpecialPage {
         * Load or initialize $authAction, $authRequests and $subPage.
         * Subclasses should call this from execute() or otherwise ensure the variables are initialized.
         * @param string $subPage Subpage of the special page.
-        * @param string $authAction Override auth action specified in request (this is useful
+        * @param string|null $authAction Override auth action specified in request (this is useful
         *    when the form needs to be changed from <action> to <action>_CONTINUE after a successful
         *    authentication step)
         * @param bool $reset Regenerate the requests even if a cached version is available
@@ -730,7 +730,7 @@ abstract class AuthManagerSpecialPage extends SpecialPage {
         * Get an array value, or a default if it does not exist.
         * @param array $array
         * @param string $fieldName
-        * @param mixed $default
+        * @param mixed|null $default
         * @return mixed
         */
        protected static function getField( array $array, $fieldName, $default = null ) {
index 3082101..d5c889a 100644 (file)
@@ -1491,7 +1491,7 @@ class LoginForm extends SpecialPage {
        ];
 
        /**
-        * @param WebRequest $request
+        * @param WebRequest|null $request
         */
        public function __construct( $request = null ) {
                wfDeprecated( 'LoginForm', '1.27' );
index 5db8066..7a39b3c 100644 (file)
@@ -182,7 +182,7 @@ class SpecialPage implements MessageLocalizer {
        /**
         * Get or set whether this special page is listed in Special:SpecialPages
         * @since 1.6
-        * @param bool $x
+        * @param bool|null $x
         * @return bool
         */
        function listed( $x = null ) {
@@ -220,7 +220,7 @@ class SpecialPage implements MessageLocalizer {
 
        /**
         * Whether the special page is being evaluated via transclusion
-        * @param bool $x
+        * @param bool|null $x
         * @return bool
         */
        function including( $x = null ) {
@@ -388,7 +388,8 @@ class SpecialPage implements MessageLocalizer {
         * Note that this does not in any way check that the user is authorized to use this special page
         * (use checkPermissions() for that).
         *
-        * @param string $level A security level. Can be an arbitrary string, defaults to the page name.
+        * @param string|null $level A security level. Can be an arbitrary string, defaults to the page
+        *   name.
         * @return bool False means a redirect to the reauthentication page has been set and processing
         *   of the special page should be aborted.
         * @throws ErrorPageError If the security level cannot be met, even with reauthentication.
index b3cb806..b3b63b0 100644 (file)
@@ -415,7 +415,7 @@ class SpecialPageFactory {
         * Return categorised listable special pages which are available
         * for the current user, and everyone.
         *
-        * @param User $user User object to check permissions, $wgUser will be used
+        * @param User|null $user User object to check permissions, $wgUser will be used
         *        if not provided
         * @return array ( string => Specialpage )
         */
index bc632b1..92c6f50 100644 (file)
@@ -494,7 +494,7 @@ class SpecialBlock extends FormSpecialPage {
         * @todo Should be in Block.php?
         * @param string $par Subpage parameter passed to setup, or data value from
         *     the HTMLForm
-        * @param WebRequest $request Optionally try and get data from a request too
+        * @param WebRequest|null $request Optionally try and get data from a request too
         * @return array [ User|string|null, Block::TYPE_ constant|null ]
         */
        public static function getTargetAndType( $par, WebRequest $request = null ) {
@@ -1005,7 +1005,7 @@ class SpecialBlock extends FormSpecialPage {
        /**
         * Process the form on POST submission.
         * @param array $data
-        * @param HTMLForm $form
+        * @param HTMLForm|null $form
         * @return bool|array True for success, false for didn't-try, array of errors on failure
         */
        public function onSubmit( array $data, HTMLForm $form = null ) {
index c514300..4f97ba2 100644 (file)
@@ -151,8 +151,6 @@ class SpecialChangeEmail extends FormSpecialPage {
         * @return Status
         */
        private function attemptChange( User $user, $newaddr ) {
-               $authManager = AuthManager::singleton();
-
                if ( $newaddr != '' && !Sanitizer::validateEmail( $newaddr ) ) {
                        return Status::newFatal( 'invalidemailaddress' );
                }
index 4a939d4..d7ce414 100644 (file)
@@ -267,7 +267,7 @@ class SpecialEmailUser extends UnlistedSpecialPage {
         *
         * @param User $user
         * @param string $editToken Edit token
-        * @param Config $config optional for backwards compatibility
+        * @param Config|null $config optional for backwards compatibility
         * @return string|null Null on success or string on error
         */
        public static function getPermissionsError( $user, $editToken, Config $config = null ) {
index 6848d2c..2a8a09d 100644 (file)
@@ -140,7 +140,7 @@ class SpecialPrefixindex extends SpecialAllPages {
        /**
         * @param int $namespace
         * @param string $prefix
-        * @param string $from List all pages from this name (default false)
+        * @param string|null $from List all pages from this name (default false)
         */
        protected function showPrefixChunk( $namespace, $prefix, $from = null ) {
                global $wgContLang;
index 540dbc6..3069bd8 100644 (file)
@@ -129,7 +129,7 @@ class SpecialUndelete extends SpecialPage {
         * specific title if one is set.
         *
         * @param string $permission
-        * @param User $user
+        * @param User|null $user
         * @return bool
         */
        protected function isAllowed( $permission, User $user = null ) {
index 2eeafe6..e52945a 100644 (file)
@@ -33,7 +33,7 @@ use MediaWiki\MediaWikiServices;
 class SpecialUpload extends SpecialPage {
        /**
         * Get data POSTed through the form and assign them to the object
-        * @param WebRequest $request Data posted.
+        * @param WebRequest|null $request Data posted.
         */
        public function __construct( $request = null ) {
                parent::__construct( 'Upload', 'upload' );
@@ -587,7 +587,7 @@ class SpecialUpload extends SpecialPage {
         * @param string $license
         * @param string $copyStatus
         * @param string $source
-        * @param Config $config Configuration object to load data from
+        * @param Config|null $config Configuration object to load data from
         * @return string
         */
        public static function getInitialPageText( $comment = '', $license = '',
index cae895f..c1f1026 100644 (file)
@@ -130,7 +130,8 @@ class PreferencesFormLegacy extends HTMLForm {
         * @return string
         */
        function getLegend( $key ) {
-               $legend = parent::getLegend( $key );
+               $aliasKey = ( $key === 'optoutwatchlist' || $key === 'optoutrc' ) ? 'opt-out' : $key;
+               $legend = parent::getLegend( $aliasKey );
                Hooks::run( 'PreferencesGetLegend', [ $this, $key, &$legend ] );
                return $legend;
        }
index 423e2bb..cfa8f8f 100644 (file)
@@ -227,7 +227,8 @@ class PreferencesFormOOUI extends OOUIHTMLForm {
         * @return string
         */
        function getLegend( $key ) {
-               $legend = parent::getLegend( $key );
+               $aliasKey = ( $key === 'optoutwatchlist' || $key === 'optoutrc' ) ? 'opt-out' : $key;
+               $legend = parent::getLegend( $aliasKey );
                Hooks::run( 'PreferencesGetLegend', [ $this, $key, &$legend ] );
                return $legend;
        }
index 26ed499..87c849a 100644 (file)
@@ -44,7 +44,7 @@ class ActiveUsersPager extends UsersPager {
        private $blockStatusByUid;
 
        /**
-        * @param IContextSource $context
+        * @param IContextSource|null $context
         * @param FormOptions $opts
         */
        function __construct( IContextSource $context = null, FormOptions $opts ) {
index b5ced13..f261b72 100644 (file)
@@ -23,6 +23,7 @@
  * @ingroup Pager
  */
 use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\IResultWrapper;
 use Wikimedia\Rdbms\FakeResultWrapper;
 
index 23f9f83..aa757e6 100644 (file)
@@ -38,9 +38,9 @@ class UsersPager extends AlphabeticPager {
        protected $userGroupCache;
 
        /**
-        * @param IContextSource $context
-        * @param array $par (Default null)
-        * @param bool $including Whether this page is being transcluded in
+        * @param IContextSource|null $context
+        * @param array|null $par (Default null)
+        * @param bool|null $including Whether this page is being transcluded in
         * another page
         */
        function __construct( IContextSource $context = null, $par = null, $including = null ) {
index 245982e..ccef56b 100644 (file)
@@ -41,7 +41,7 @@ abstract class RaggettBase extends TidyDriverBase {
         * Perform a clean/repair operation
         * @param string $text HTML to check
         * @param bool $stderr Whether to read result from STDERR rather than STDOUT
-        * @param int &$retval Exit code (-1 on internal error)
+        * @param int|null &$retval Exit code (-1 on internal error)
         * @return null|string
         * @throws MWException
         */
index b59423a..2809ea4 100644 (file)
@@ -9,7 +9,7 @@ class RaggettExternal extends RaggettBase {
         *
         * @param string $text HTML to check
         * @param bool $stderr Whether to read result from STDERR rather than STDOUT
-        * @param int &$retval Exit code (-1 on internal error)
+        * @param int|null &$retval Exit code (-1 on internal error)
         * @return string|null
         */
        protected function cleanWrapped( $text, $stderr = false, &$retval = null ) {
index bb83d6a..65d37f4 100644 (file)
@@ -9,7 +9,7 @@ class RaggettInternalHHVM extends RaggettBase {
         *
         * @param string $text HTML to check
         * @param bool $stderr Whether to read result from error status instead of output
-        * @param int &$retval Exit code (-1 on internal error)
+        * @param int|null &$retval Exit code (-1 on internal error)
         * @return string|null
         */
        protected function cleanWrapped( $text, $stderr = false, &$retval = null ) {
index e5642d9..e7724b0 100644 (file)
@@ -9,7 +9,7 @@ class RaggettInternalPHP extends RaggettBase {
         *
         * @param string $text HTML to check
         * @param bool $stderr Whether to read result from error status instead of output
-        * @param int &$retval Exit code (-1 on internal error)
+        * @param int|null &$retval Exit code (-1 on internal error)
         * @return string|null
         */
        protected function cleanWrapped( $text, $stderr = false, &$retval = null ) {
index 213343f..47660e3 100644 (file)
@@ -29,7 +29,7 @@ class MalformedTitleException extends Exception implements ILocalizedException {
 
        /**
         * @param string $errorMessage Localisation message describing the error (since MW 1.26)
-        * @param string $titleText The invalid title text (since MW 1.26)
+        * @param string|null $titleText The invalid title text (since MW 1.26)
         * @param string[] $errorMessageParameters Additional parameters for the error message.
         * $titleText will be appended if it's not null. (since MW 1.26)
         */
index 87b96ac..dcebb60 100644 (file)
@@ -247,7 +247,7 @@ abstract class UploadBase {
 
        /**
         * @param string $tempPath File system path to temporary file containing the upload
-        * @param int $fileSize
+        * @param int|null $fileSize
         */
        protected function setTempFile( $tempPath, $fileSize = null ) {
                $this->mTempPath = $tempPath;
@@ -1103,7 +1103,7 @@ abstract class UploadBase {
         * file again.
         *
         * @deprecated since 1.28 Use tryStashFile() instead
-        * @param User $user
+        * @param User|null $user
         * @return UploadStashFile Stashed file
         * @throws UploadStashBadPathException
         * @throws UploadStashFileException
@@ -1116,7 +1116,7 @@ abstract class UploadBase {
        /**
         * Implementation for stashFile() and tryStashFile().
         *
-        * @param User $user
+        * @param User|null $user
         * @return UploadStashFile Stashed file
         */
        protected function doStashFile( User $user = null ) {
@@ -1529,7 +1529,7 @@ abstract class UploadBase {
         * @todo Replace this with a whitelist filter!
         * @param string $element
         * @param array $attribs
-        * @param array $data
+        * @param array|null $data
         * @return bool
         */
        public function checkSvgScriptCallback( $element, $attribs, $data = null ) {
index e55ab1f..4e40dce 100644 (file)
@@ -81,7 +81,7 @@ class UploadStash {
         * (should replace it eventually).
         *
         * @param FileRepo $repo
-        * @param User $user (default null)
+        * @param User|null $user
         */
        public function __construct( FileRepo $repo, $user = null ) {
                // this might change based on wiki's configuration.
@@ -202,7 +202,7 @@ class UploadStash {
         * database, along with other metadata.
         *
         * @param string $path Path to file you want stashed
-        * @param string $sourceType The type of upload that generated this file
+        * @param string|null $sourceType The type of upload that generated this file
         *   (currently, I believe, 'file' or null)
         * @throws UploadStashBadPathException
         * @throws UploadStashFileException
@@ -777,7 +777,8 @@ class UploadStashException extends MWException implements ILocalizedException {
        /**
         * @param string|array|MessageSpecifier $messageSpec See Message::newFromSpecifier
         * @param int $code Exception code
-        * @param Exception|Throwable $previous The previous exception used for the exception chaining.
+        * @param Exception|Throwable|null $previous The previous exception used for the exception
+        *  chaining.
         */
        public function __construct( $messageSpec, $code = 0, $previous = null ) {
                $this->messageSpec = $messageSpec;
index faf09ee..e7825e4 100644 (file)
@@ -122,8 +122,8 @@ class PasswordReset implements LoggerAwareInterface {
         *
         * @since 1.29 Fourth argument for displayPassword removed.
         * @param User $performingUser The user that does the password reset
-        * @param string $username The user whose password is reset
-        * @param string $email Alternative way to specify the user
+        * @param string|null $username The user whose password is reset
+        * @param string|null $email Alternative way to specify the user
         * @return StatusValue Will contain the passwords as a username => password array if the
         *   $displayPassword flag was set
         * @throws LogicException When the user is not allowed to perform the action
index 4e6e83a..ea8cd57 100644 (file)
@@ -759,7 +759,8 @@ class User implements IDBAccessObject, UserIdentity {
         * will be loaded once more from the database when accessing them.
         *
         * @param stdClass $row A row from the user table
-        * @param array $data Further data to load into the object (see User::loadFromRow for valid keys)
+        * @param array|null $data Further data to load into the object
+        *  (see User::loadFromRow for valid keys)
         * @return User
         */
        public static function newFromRow( $row, $data = null ) {
@@ -1062,7 +1063,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @param string|array $groups A single group name or an array of group names
         * @param int $limit Max number of users to return. The actual limit will never exceed 5000
         *   records; larger values are ignored.
-        * @param int $after ID the user to start after
+        * @param int|null $after ID the user to start after
         * @return UserArrayFromResult
         */
        public static function findUsersByGroup( $groups, $limit = 5000, $after = null ) {
@@ -1465,7 +1466,7 @@ class User implements IDBAccessObject, UserIdentity {
         * Initialize this object from a row from the user table.
         *
         * @param stdClass $row Row from the user table to load.
-        * @param array $data Further user data to load into the object
+        * @param array|null $data Further user data to load into the object
         *
         *  user_groups   Array of arrays or stdClass result rows out of the user_groups
         *                table. Previously you were supposed to pass an array of strings
@@ -2692,7 +2693,7 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * Update the 'You have new messages!' status.
         * @param bool $val Whether the user has new messages
-        * @param Revision $curRev New, as yet unseen revision of the user talk
+        * @param Revision|null $curRev New, as yet unseen revision of the user talk
         *   page. Ignored if null or !$val.
         */
        public function setNewtalk( $val, $curRev = null ) {
@@ -3133,7 +3134,7 @@ class User implements IDBAccessObject, UserIdentity {
         * Get the user's current setting for a given option.
         *
         * @param string $oname The option to check
-        * @param string|array $defaultOverride A default value returned if the option does not exist
+        * @param string|array|null $defaultOverride A default value returned if the option does not exist
         * @param bool $ignoreHidden Whether to ignore the effects of $wgHiddenPrefs
         * @return string|array|int|null User's current value for the option
         * @see getBoolOption()
@@ -3328,7 +3329,7 @@ class User implements IDBAccessObject, UserIdentity {
         *
         * @see User::listOptionKinds
         * @param IContextSource $context
-        * @param array $options Assoc. array with options keys to check as keys.
+        * @param array|null $options Assoc. array with options keys to check as keys.
         *   Defaults to $this->mOptions.
         * @return array The key => kind mapping data
         */
@@ -3690,7 +3691,7 @@ class User implements IDBAccessObject, UserIdentity {
         * never expire.)
         *
         * @param string $group Name of the group to add
-        * @param string $expiry Optional expiry timestamp in any format acceptable to
+        * @param string|null $expiry Optional expiry timestamp in any format acceptable to
         *   wfTimestamp(), or null if the group assignment should not expire
         * @return bool
         */
@@ -4068,7 +4069,7 @@ class User implements IDBAccessObject, UserIdentity {
         *
         * @param WebRequest|null $request WebRequest object to use; $wgRequest will be used if null
         *        is passed.
-        * @param bool $secure Whether to force secure/insecure cookies or use default
+        * @param bool|null $secure Whether to force secure/insecure cookies or use default
         * @param bool $rememberMe Whether to add a Token cookie for elongated sessions
         */
        public function setCookies( $request = null, $secure = null, $rememberMe = false ) {
@@ -4617,7 +4618,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @param string $val Input value to compare
         * @param string|array $salt Optional function-specific data for hashing
         * @param WebRequest|null $request Object to use or null to use $wgRequest
-        * @param int $maxage Fail tokens older than this, in seconds
+        * @param int|null $maxage Fail tokens older than this, in seconds
         * @return bool Whether the token matches
         */
        public function matchEditToken( $val, $salt = '', $request = null, $maxage = null ) {
@@ -4631,7 +4632,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @param string $val Input value to compare
         * @param string|array $salt Optional function-specific data for hashing
         * @param WebRequest|null $request Object to use or null to use $wgRequest
-        * @param int $maxage Fail tokens older than this, in seconds
+        * @param int|null $maxage Fail tokens older than this, in seconds
         * @return bool Whether the token matches
         */
        public function matchEditTokenNoSuffix( $val, $salt = '', $request = null, $maxage = null ) {
@@ -4682,7 +4683,7 @@ class User implements IDBAccessObject, UserIdentity {
         * @param string $body Message body
         * @param User|null $from Optional sending user; if unspecified, default
         *   $wgPasswordSender will be used.
-        * @param string $replyto Reply-To address
+        * @param string|null $replyto Reply-To address
         * @return Status
         */
        public function sendMail( $subject, $body, $from = null, $replyto = null ) {
@@ -5446,7 +5447,7 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * Load the user options either from cache, the database or an array
         *
-        * @param array $data Rows for the current user out of the user_properties table
+        * @param array|null $data Rows for the current user out of the user_properties table
         */
        protected function loadOptions( $data = null ) {
                global $wgContLang;
index 89cdc5f..cf985cb 100644 (file)
@@ -46,7 +46,7 @@ class UserGroupMembership {
 
        /**
         * @param int $userId The ID of the user who belongs to the group
-        * @param string $group The internal group name
+        * @param string|null $group The internal group name
         * @param string|null $expiry Timestamp of expiry in TS_MW format, or null if no expiry
         */
        public function __construct( $userId = 0, $group = null, $expiry = null ) {
index caf88a1..d4f1a92 100644 (file)
@@ -26,7 +26,7 @@ class MWRestrictions {
        private $ipAddresses = [ '0.0.0.0/0', '::/0' ];
 
        /**
-        * @param array $restrictions
+        * @param array|null $restrictions
         * @throws InvalidArgumentException
         */
        protected function __construct( array $restrictions = null ) {
index 20bad13..46f3bbc 100644 (file)
@@ -511,7 +511,7 @@ class ZipDirectoryReader {
         * in the file to satisfy the request, an exception will be thrown.
         *
         * @param int $start The byte offset of the start of the block.
-        * @param int $length The number of bytes to return. If omitted, the remainder
+        * @param int|null $length The number of bytes to return. If omitted, the remainder
         *    of the file will be returned.
         *
         * @return string
index 30d1cbb..99a051d 100644 (file)
@@ -264,7 +264,7 @@ interface WatchedItemStoreInterface {
         * @since 1.31
         *
         * @param User $user
-        * @param int $unreadLimit
+        * @param int|null $unreadLimit
         *
         * @return int|bool The number of unread notifications
         *                  true if greater than or equal to $unreadLimit
index 68cdad6..aaa46ae 100644 (file)
@@ -39,7 +39,7 @@ class UsersMultiselectWidget extends \OOUI\Widget {
                $textarea = new MultilineTextInputWidget( [
                        'name' => $this->inputName,
                        'value' => implode( "\n", $this->usersArray ),
-                       'rows' => 25,
+                       'rows' => 10,
                ] );
                $this->prependContent( $textarea );
        }
index 8dfe00f..dc61519 100644 (file)
@@ -330,7 +330,7 @@ class ConverterRule {
 
        /**
         * Parse rules and flags.
-        * @param string $variant Variant language code
+        * @param string|null $variant Variant language code
         */
        public function parse( $variant = null ) {
                if ( !$variant ) {
index 1623c36..3253957 100644 (file)
@@ -220,7 +220,8 @@ class Language {
 
                // Check if there is a language class for the code
                $class = self::classFromCode( $code, $fallback );
-               if ( class_exists( $class ) ) {
+               // LanguageCode does not inherit Language
+               if ( class_exists( $class ) && is_a( $class, 'Language', true ) ) {
                        $lang = new $class;
                        return $lang;
                }
@@ -1089,7 +1090,7 @@ class Language {
         * @param string $ts 14-character timestamp
         *      YYYYMMDDHHMMSS
         *      01234567890123
-        * @param DateTimeZone $zone Timezone of $ts
+        * @param DateTimeZone|null $zone Timezone of $ts
         * @param int &$ttl The amount of time (in seconds) the output may be cached for.
         * Only makes sense if $ts is the current time.
         * @todo handling of "o" format character for Iranian, Hebrew, Hijri & Thai?
@@ -4075,7 +4076,7 @@ class Language {
         * match up with it.
         *
         * @param string $str The validated block duration in English
-        * @param User $user User object to use timezone from or null for $wgUser
+        * @param User|null $user User object to use timezone from or null for $wgUser
         * @param int $now Current timestamp, for formatting relative block durations
         * @return string Somehow translated block duration
         * @see LanguageFi.php for example implementation
index ce35717..dcc2cf3 100644 (file)
@@ -211,7 +211,7 @@ class LanguageConverter {
 
        /**
         * Validate the variant
-        * @param string $variant The variant to validate
+        * @param string|null $variant The variant to validate
         * @return mixed Returns the variant if it is valid, null otherwise
         */
        public function validateVariant( $variant = null ) {
index 54ff421..b2bae18 100644 (file)
@@ -84,7 +84,7 @@ class LanguageFi extends Language {
 
        /**
         * @param string $str
-        * @param User $user User object to use timezone from or null for $wgUser
+        * @param User|null $user User object to use timezone from or null for $wgUser
         * @param int $now Current timestamp, for formatting relative block durations
         * @return string
         */
index 605025c..5cdcfaf 100644 (file)
        "tooltip-ca-nstab-special": "Nyoë nakeuh miëng kusuih, ngön h’an jeuët geuandam.",
        "tooltip-ca-nstab-project": "Eu laman buët",
        "tooltip-ca-nstab-image": "Eu miëng beureukaih",
+       "tooltip-ca-nstab-mediawiki": "Eu peusan sistem",
        "tooltip-ca-nstab-template": "Eu seunaleuëk",
        "tooltip-ca-nstab-help": "Eu laman beunantu",
        "tooltip-ca-nstab-category": "Eu miëng kawan",
        "logentry-delete-delete": "$1 {{GENDER:$2|geusampôh}} miëng $3",
        "logentry-move-move": "$1 {{GENDER:$2|geupinah}} mieng $3 u $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|geupinah}} mieng $3 u $4 hana geubôh peuninah",
+       "logentry-move-move_redir": "$1 {{GENDER:$2|geupinah}} mieng $3 u $4 ateueh mieng peuninah",
        "logentry-patrol-patrol-auto": "$1 otomatis {{GENDER:$2|geutanda}} revisi $4 nibak mieng $3 nyang geukawai",
        "logentry-newusers-create": "$1 {{GENDER:$2|geupeugöt}} akun ureuëng ngui",
        "logentry-upload-upload": "$1 {{GENDER:$2|geupasoe}} $3",
index 005737d..fb7eec7 100644 (file)
@@ -11,7 +11,8 @@
                        "Macofe",
                        "MtDu",
                        "Abdeaitali",
-                       "Reda Benkhadra"
+                       "Reda Benkhadra",
+                       "Anass Sedrati"
                ]
        },
        "tog-underline": "Ṣetter l-lyanaṫ:",
        "exif-giffilecomment": "ṫ-ṫĝliq dial milef GIF",
        "exif-intellectualgenre": "noĝ l-madda",
        "exif-scenecode": "cod mċhd IPTC",
+       "exif-orientation-1": "عادي",
        "exif-orientation-4": "mḍwwer ofoqiyyen",
        "exif-orientation-8": "mḍwwer 90° <abbr title=\"Ĝks (ṫ-ṫijah) Ĝaqarib s-Saĝa\">ĜĜS</abbr>",
        "exif-componentsconfiguration-0": "ma kayn-ċ",
index 4f4aa01..8f248ce 100644 (file)
        "welcomecreation-msg": "Creóse la to cuenta.\nNun t'escaezas de camudar les tos [[Special:Preferences|preferencies de {{SITENAME}}]].",
        "yourname": "Nome d'usuariu:",
        "userlogin-yourname": "Nome d'usuariu",
-       "userlogin-yourname-ph": "Escriba'l so nome d'usuariu",
-       "createacct-another-username-ph": "Escriba'l nome d'usuariu",
+       "userlogin-yourname-ph": "Escribe'l to nome d'usuariu",
+       "createacct-another-username-ph": "Escribe'l nome d'usuariu",
        "yourpassword": "Contraseña:",
        "userlogin-yourpassword": "Contraseña",
-       "userlogin-yourpassword-ph": "Escriba la so contraseña",
-       "createacct-yourpassword-ph": "Escriba una contraseña",
+       "userlogin-yourpassword-ph": "Escribe la contraseña",
+       "createacct-yourpassword-ph": "Escribe una contraseña",
        "yourpasswordagain": "Escribi otra vuelta la contraseña:",
        "createacct-yourpasswordagain": "Confirmar la contraseña",
-       "createacct-yourpasswordagain-ph": "Escriba nuevamente la contraseña",
+       "createacct-yourpasswordagain-ph": "Escribe nuevamente la contraseña",
        "userlogin-remembermypassword": "Caltener abierta la sesión",
        "userlogin-signwithsecure": "Usar una conexón segura",
        "cannotlogin-title": "Nun pudo aniciase sesión",
        "logout": "Salir",
        "userlogout": "Salir",
        "notloggedin": "Nun aniciasti sesión",
-       "userlogin-noaccount": "¿Nun tien una cuenta?",
+       "userlogin-noaccount": "¿Nun tienes una cuenta?",
        "userlogin-joinproject": "Xunise a {{SITENAME}}",
        "createaccount": "Crear una cuenta",
-       "userlogin-resetpassword-link": "¿Escaeció la contraseña?",
+       "userlogin-resetpassword-link": "¿Escaecisti la contraseña?",
        "userlogin-helplink2": "Ayuda del aniciu de sesión",
        "userlogin-loggedin": "Yá anició sesión como {{GENDER:$1|$1}}.\nUtilice'l formulariu de más abaxo p'aniciar sesión como otru usuariu.",
        "userlogin-reauth": "Tienes d'aniciar sesión de nueves pa comprobar que yes {{GENDER:$1|$1}}.",
        "userlogin-createanother": "Crear otra cuenta",
        "createacct-emailrequired": "Direición de corréu electrónicu",
        "createacct-emailoptional": "Direición de corréu electrónicu (opcional)",
-       "createacct-email-ph": "Escriba la so direición de corréu electrónicu",
-       "createacct-another-email-ph": "Escriba la direición de corréu electrónicu",
+       "createacct-email-ph": "Escribe la to direición de corréu electrónicu",
+       "createacct-another-email-ph": "Escribe la direición de corréu electrónicu",
        "createaccountmail": "Usar una contraseña al debalu temporal y unviala a la direición de corréu electrónicu conseñada",
        "createaccountmail-help": "Pue usase pa crear una cuenta pa otra persona ensin saber la contraseña.",
        "createacct-realname": "Nome real (opcional)",
        "createacct-reason": "Motivu",
-       "createacct-reason-ph": "Por qué quier crear otra cuenta",
+       "createacct-reason-ph": "Por qué vas crear otra cuenta",
        "createacct-reason-help": "Mensaxe que s'amuesa nel rexistru de creación de cuentes",
        "createacct-submit": "Crear la cuenta",
        "createacct-another-submit": "Crear una cuenta",
        "createacct-continue-submit": "Siguir cola creación de la cuenta",
        "createacct-another-continue-submit": "Siguir cola creación de la cuenta",
-       "createacct-benefit-heading": "{{SITENAME}} failu xente como vusté.",
+       "createacct-benefit-heading": "Persones como tu son les que construyen {{SITENAME}}.",
        "createacct-benefit-body1": "{{PLURAL:$1|edición|ediciones}}",
        "createacct-benefit-body2": "{{PLURAL:$1|páxina|páxines}}",
        "createacct-benefit-body3": "{{PLURAL:$1|collaborador|collaboradores}} de recién",
        "userexists": "El nome d'usuariu conseñáu yá ta usándose.\nPor favor escueyi un nome diferente.",
        "loginerror": "Error d'aniciu de sesión",
        "createacct-error": "Error de creación de cuenta",
-       "createaccounterror": "Nun se pudo crear la cuenta: $1",
+       "createaccounterror": "Nun pudo crease la cuenta: $1",
        "nocookiesnew": "La cuenta d'usuariu ta creada, pero nun aniciasti sesión.\n{{SITENAME}} usa «cookies» pa identificar a los usuarios.\nTienes les «cookies» desactivaes.\nPor favor actívales y anicia sesión col nuevu nome d'usuariu y contraseña.",
-       "nocookieslogin": "{{SITENAME}} usa «cookies» pa identificar a los usuarios.\nTien les «cookies» desactivaes.\nPor favor activeles y vuelva a intentalo.",
-       "nocookiesfornew": "La cuenta nun se creó porque nun pudimos confirmar l'orixe.\nComprueba que tienes activaes les «cookies», recarga esta páxina y vuelvi a intentalo.",
+       "nocookieslogin": "{{SITENAME}} usa «cookies» pa identificar a los usuarios.\nTien les «cookies» desactivaes.\nPor favor actívales y tenta otra vuelta.",
+       "nocookiesfornew": "La cuenta nun se creó porque nun pudimos confirmar l'orixe.\nComprueba que tienes activaes les «cookies», recarga esta páxina y tenta otra vuelta.",
        "createacct-loginerror": "La cuenta creóse correchamente, pero nun pudo aniciase sesión automáticamente. Sigui col [[Special:UserLogin|accesu manual]].",
        "noname": "Nun conseñasti un nome d'usuariu válidu.",
        "loginsuccesstitle": "Identificáu",
        "nouserspecified": "Has d'especificar un nome d'usuariu.",
        "login-userblocked": "Esti usuariu ta bloquiáu. Nun se permite l'aniciu de sesión.",
        "wrongpassword": "Escribisti un nome d'usuariu o contraseña incorreutu.\nTenta otra vuelta.",
-       "wrongpasswordempty": "La contraseña taba en blanco.\nVuelvi a intentalo.",
+       "wrongpasswordempty": "La contraseña taba en blancu.\nTenta otra vuelta.",
        "passwordtooshort": "Les contraseñes han de tener polo menos {{PLURAL:$1|1 caráuter|$1 caráuteres}}.",
        "passwordtoolong": "Les contraseñes nun puen ser mayores de {{PLURAL:$1|1 caráuter|$1 caráuteres}}.",
        "passwordtoopopular": "Les contraseñes más escoyíes de vezu nun pueden usase. Escueye una contraseña más difícil d'aldovinar.",
        "throttled-mailpassword": "Yá s'unvió un corréu de reaniciu la clave {{PLURAL:$1|na postrer hora|nes postreres $1 hores}}.\nPa evitar abusos, namái s'unviará un corréu de reaniciu cada {{PLURAL:$1|hora|$1 hores}}.",
        "mailerror": "Fallu al unviar el corréu: $1",
        "acct_creation_throttle_hit": "Los visitantes d'esta wiki qu'usen la to direición IP yá crearon {{PLURAL:$1|1 cuenta|$1 cuentes}} nel periodu de $2, que ye'l máximu almitíu nesi tiempu.\nPoro, los visitantes qu'usen esta direición IP nun pueden crear más cuentes pol momentu.",
-       "emailauthenticated": "La so direición de corréu electrónicu confirmóse'l $2 a les $3.",
-       "emailnotauthenticated": "La so direición de corréu electrónicu inda nun se confirmó.\nNun s'unviará corréu pa nenguna de les funciones siguientes.",
+       "emailauthenticated": "La direición de corréu electrónicu confirmóse'l $2 a les $3.",
+       "emailnotauthenticated": "La direición de corréu electrónicu inda nun se confirmó.\nNun s'unviará corréu pa nenguna de les funciones siguientes.",
        "noemailprefs": "Conseña una direición de corréu electrónicu nes tos preferencies pa que funcionen eses carauterístiques.",
        "emailconfirmlink": "Confirmar la direición de corréu electrónicu",
        "invalidemailaddress": "La direición de corréu electrónicu nun pue aceutase yá que paez tener un formatu inválidu.\nPor favor conseña una direición con formatu afayadizu o dexa baleru'l campu.",
        "pt-createaccount": "Crear una cuenta",
        "pt-userlogout": "Salir",
        "php-mail-error-unknown": "Fallu desconocíu na función mail() de PHP.",
-       "user-mail-no-addy": "Intentasti unviar un corréu electrónicu ensin direición de corréu.",
-       "user-mail-no-body": "Trató d'unviar un corréu electrónicu con un cuerpu baleru o curtiu enforma.",
+       "user-mail-no-addy": "Tentasti unviar un corréu electrónicu ensin direición de corréu.",
+       "user-mail-no-body": "Tentasti unviar un corréu electrónicu col cuerpu vaciu o curtiu enforma.",
        "changepassword": "Camudar la contraseña",
-       "resetpass_announce": "P'acabar d'aniciar sesión, tien de definir equí una contraseña nueva.",
+       "resetpass_announce": "P'acabar d'aniciar sesión, tienes de configurar una contraseña nueva.",
        "resetpass_text": "<!-- Amestar testu equí -->",
        "resetpass_header": "Camudar la contraseña de la cuenta",
        "oldpassword": "Contraseña antigua:",
        "retypenew": "Vuelvi a escribir la contraseña nueva:",
        "resetpass_submit": "Configurar la contraseña y aniciar sesión",
        "changepassword-success": "Camudóse la contraseña.",
-       "changepassword-throttled": "Ficisti demasiaos intentos d'aniciu de sesión recientes.\nPor favor espera $1 enantes d'intentalo otra vuelta.",
+       "changepassword-throttled": "Ficisti demasiaos intentos d'aniciar sesión de recien.\nPor favor espera $1 enantes de tentar otra vuelta.",
        "botpasswords": "Contraseñes de bots",
        "botpasswords-summary": "Les <em>contraseñes de bot</em> permiten l'accesu a una cuenta d'usuariu por aciu de la API sin usar les credenciales d'accesu de la cuenta principal. Los permisos d'usuariu disponibles al aniciar sesión con una contraseña de bot puen tar torgaos.\n\nSi nun sabes pa qué val esto, probablemente nun tendríes d'usalo. Naide tendría de pidite nunca que xeneres una d'estes y que-y la deas.",
        "botpasswords-disabled": "Les contraseñes de bot tán desactivaes.",
        "botpasswords-label-delete": "Desaniciar",
        "botpasswords-label-resetpassword": "Reestablecer la contraseña",
        "botpasswords-label-grants": "Permisos aplicables:",
-       "botpasswords-help-grants": "Los permisos dan accesu a los permisos d'usuariu que yá tenga la cuenta. Activar un permisu equí nun da accesu a nengún permisu que la to cuenta nun tenga d'otra miente. Mira la [[Special:ListGrants|tabla de permisos]] pa más información.",
+       "botpasswords-help-grants": "Los permisos dan accesu a los permisos d'usuariu que yá tengas na cuenta. Activar un permisu equí nun da accesu a nengún permisu que la to cuenta nun tenga d'otra miente. Mira la [[Special:ListGrants|tabla de permisos]] pa más información.",
        "botpasswords-label-grants-column": "Permitío",
        "botpasswords-bad-appid": "El nome del bot \"$1\" nun ye válidu.",
        "botpasswords-insert-failed": "Nun pudo amestase'l nome de bot «$1». ¿Taba añadíu yá?",
        "resetpass-temp-emailed": "Anició sesión con un códigu temporal unviáu per corréu electrónicu.\nPa completar l'aniciu de sesión, tien de definir una nueva contraseña equí:",
        "resetpass-temp-password": "Contraseña temporal:",
        "resetpass-abort-generic": "Una estensión encaboxó'l cambiu de la contraseña.",
-       "resetpass-expired": "La so contraseña caducó. Defina una nueva contraseña p'aniciar sesión.",
+       "resetpass-expired": "La to contraseña caducó. Configura una nueva contraseña p'aniciar sesión.",
        "resetpass-expired-soft": "La contraseña caducó y precisa cambiase. Escueye agora una contraseña nueva, o pulsia «{{int:authprovider-resetpass-skip-label}}» pa cambiala sero.",
        "resetpass-validity-soft": "La contraseña nun ye válida: $1\n\nEscueye agora una contraseña nueva, o pulsia «{{int:authprovider-resetpass-skip-label}}» pa cambiala sero.",
        "passwordreset": "Reaniciar contraseña",
-       "passwordreset-text-one": "Complete esti formulariu pa reaniciar la contraseña.",
-       "passwordreset-text-many": "{{PLURAL:$1|Rellene unu de los campos pa recibir una contraseña temporal per corréu.}}",
+       "passwordreset-text-one": "Completa esti formulariu pa recibir per corréu una contraseña temporal.",
+       "passwordreset-text-many": "{{PLURAL:$1|Rellena unu de los campos pa recibir una contraseña temporal per corréu.}}",
        "passwordreset-disabled": "Los reanicios de contraseña tán desactivaos nesta wiki.",
        "passwordreset-emaildisabled": "Les funciones de corréu electrónicu tan desactivaes nesta wiki.",
        "passwordreset-username": "Nome d'usuariu:",
        "passwordreset-domain": "Dominiu:",
        "passwordreset-email": "Direición de corréu electrónicu:",
        "passwordreset-emailtitle": "Detalles de la cuenta en {{SITENAME}}",
-       "passwordreset-emailtext-ip": "Dalguién (seique vusté, dende la direición IP $1)solicitó'l reaniciu de la so contraseña de {{SITENAME}} ($4).\n{{PLURAL:$3|La cuenta d'usuariu siguiente ta asociada|Les cuentes d'usuariu siguientes tán asociaes}}\na esta direición de corréu electrónicu:\n\n$2\n\n{{PLURAL:$3|Esta contraseña provisional caduca|Estes contraseñes provisionales caduquen}} {{PLURAL:$5|nun día|en $5 díes}}.\nTendría d'aniciar sesión y escoyer una contraseña nueva agora. Si esta solicitú la fizo otra persona,\no si recordó la clave orixinal y yá nun quier camudala, pue escaecer esti mensaxe y siguir\nusando la contraseña antigua.",
-       "passwordreset-emailtext-user": "L'usuariu $1 de {{SITENAME}} solicitó un reaniciu de la so contraseña de {{SITENAME}} ($4). {{PLURAL:$3|La cuenta d'usuariu siguiente ta asociada|Les cuentes d'usuariu siguientes tán asociaes}} con esta direición de corréu electrónicu:\n\n$2\n\n{{PLURAL:$3|Esta contraseña provisional caduca|Estes contraseñes provisionales caduquen}} {{PLURAL:$5|nun día|en $5 díes}}.\nTendría d'aniciar sesión y escoyer una contraseña nueva agora. Si esta solicitú la fizo otra persona, o si recordó la clave orixinal y yá nun quier camudala, pue escaecer esti mensaxe y siguir usando la contraseña antigua.",
+       "passwordreset-emailtext-ip": "Dalguién (seique tu, dende la direición IP $1)solicitó'l reaniciu de la contraseña de {{SITENAME}} ($4).\n{{PLURAL:$3|La cuenta d'usuariu siguiente ta asociada|Les cuentes d'usuariu siguientes tán asociaes}}\na esta direición de corréu electrónicu:\n\n$2\n\n{{PLURAL:$3|Esta contraseña provisional caduca|Estes contraseñes provisionales caduquen}} {{PLURAL:$5|nun día|en $5 díes}}.\nTendríes d'aniciar sesión y escoyer una contraseña nueva agora. Si esta solicitú ye d'otra persona,\no si recordasti la clave orixinal y yá nun quies camudala, inora esti mensaxe y sigue\nusando la contraseña antigua.",
+       "passwordreset-emailtext-user": "L'usuariu $1 de {{SITENAME}} solicitó un reaniciu de la contraseña de {{SITENAME}} ($4). {{PLURAL:$3|La cuenta d'usuariu siguiente ta asociada|Les cuentes d'usuariu siguientes tán asociaes}} con esta direición de corréu electrónicu:\n\n$2\n\n{{PLURAL:$3|Esta contraseña provisional caduca|Estes contraseñes provisionales caduquen}} {{PLURAL:$5|nun día|en $5 díes}}.\nTendríes d'aniciar sesión y escoyer una contraseña nueva agora. Si esta solicitú ye d'otra persona, o si recordasti la clave orixinal y yá nun quies camudala, pues inorar esti mensaxe y siguir usando la contraseña antigua.",
        "passwordreset-emailelement": "Nome d'usuariu: \n$1\n\nContraseña temporal: \n$2",
        "passwordreset-emailsentemail": "Si esta direición de corréu electrónicu ta asociada cola to cuenta, unviaráse un corréu pa reaniciar la contraseña.",
        "passwordreset-emailsentusername": "Si hai una direición de corréu electrónicu asociada con esti nome d'usuariu, unviaráse un corréu electrónicu pa reaniciar la contraseña.",
index 06e1c76..bd8bac4 100644 (file)
@@ -12,7 +12,8 @@
                        "Oldstoneage",
                        "Baloch Afghanistan",
                        "Mjbmr",
-                       "Macofe"
+                       "Macofe",
+                       "Matěj Suchánek"
                ]
        },
        "tog-underline": ":لینکانآ خط کش",
        "yourdiff": "تفاوتان",
        "copyrightwarning": "لطفا توجه بیت که کل نوشته یات ته {{SITENAME}}  تحت $2 نشر بنت.(بچار په جزیات$1).\nاگه شما لوٹیت شمی نوشتانک اصلاح و دگه چهاپ مبنت، اچه آیانا ادان مهلیت.\nشما ما را قول دهیت که وتی چیزا بنویسیت یا چه یک دامین عمومی کپی کتگیت.\n''' نوشتانکی که کپی رایت دارند بی اجازه ادا هور مکنیت'''",
        "copyrightwarning2": "لطفا توجه کنیت که کل مشارکاتن ته {{SITENAME}} شاید اصلاح, عوض و یا توسط دگه شرکت کننده آن حذف بنت.\nاگر شما نه لوٹیت شمی نوشتاک گون بی رحمی اصلاح مه بنت، اچه شما آیء ادان دیم مه دهیت.<br />\nشما هنچوش ما را قول دهیت که شما شی وت نوشتت یا ایء چه یک دامین عمومی یا هنچوشین آزاتین منبع کپی کتیت.(بچار $1 په جزییات).\n''' نوشتاکی که حق کپی دارنت بی اجازت دیم مه دهیت!'''",
-       "longpageerror": "'''ارور: ائ سیاهگء که راهیگ کتگ ات {{PULAR:$1|یک کیلوبایت|$1 کیلوبایت}} اندازگ داریت. ائ اندازگ چه ماکسیممء {{PLURAL:$2|یک کیلوبایت|$2 کیلوبایت}} گیشتر انت.'''\nنبیت که آئرا ذخیرگ بکن ات.",
+       "longpageerror": "'''ارور: ائ سیاهگء که راهیگ کتگ ات {{PLURAL:$1|یک کیلوبایت|$1 کیلوبایت}} اندازگ داریت. ائ اندازگ چه ماکسیممء {{PLURAL:$2|یک کیلوبایت|$2 کیلوبایت}} گیشتر انت.'''\nنبیت که آئرا ذخیرگ بکن ات.",
        "readonlywarning": "'''هژاری: دیتابیس پر جوڑ کتنء واسته کبل بیتگ انت، پمیشکا انیگء نبیت که وتی ادیتانء ذخیرگ بکن ات.'''\nاگان لوٹ ات وتی سیاهگء مان یک تکست فایلء کپی و پر آیوکء ذخیرگ بکن ات.\n\nهما ماسٹر که آئرا کبل کتگ ائ توضیحء درشان کتگ انت: $1",
        "protectedpagewarning": "'''هوژاری: ائ تاک پراتکت بیتگ انت، گڑا تهنا کاربرانء گون ماسٹری حالتء توان انت که آئرا ادیت بکن انت'''\nآهرین سیاهگ که گون ائ بابتء همگرنچ انت جهلیگء آتکگ انت:",
        "semiprotectedpagewarning": "'''بزان:''' ائ پیج کبل بیتگ تانکه تهنا رجستر کتگین کاربران بتوان انت آئرا ادیت بکن انت.\nآهرین سیاهگ که گون ائ بابتء همگرنچ انت جهلیگء آتکگ:",
index b79cbe4..597499a 100644 (file)
        "filepageexists": "Старонка апісаньня для гэтага файлу ўжо існуе як <strong>[[:$1]]</strong>, але файлу з такой назвай няма.\nАпісаньне якое вы дадалі ня зьявіцца на старонцы апісаньня.\nКаб яно там зьявілася, вам трэба рэдагаваць яе самастойна.\n[[$1|thumb]]",
        "fileexists-extension": "Файл з падобнай назвай ужо існуе: [[$2|thumb]]\n* Назва загружанага файла: <strong>[[:$1]]</strong>\n* Назва існуючага файла: <strong>[[:$2]]</strong>\nМагчыма, вы жадаеце выкарыстаць адрозную назву?",
        "fileexists-thumbnail-yes": "Верагодна, файл зьяўляецца паменшанай копіяй (<em>мініятурай</em>).\n[[$1|thumb]]\nКалі ласка, праверце файл <strong>[[:$1]]</strong>.\nКалі правераны файл зьяўляецца той жа выявай, то загрузка дадатковай мініятуры ня мае сэнсу.",
-       "file-thumbnail-no": "Назва файла пачынаецца з <strong>$1</strong>.\nВерагодна гэта паменшаная копія выявы ''(мініятура)''.\nКалі Вы маеце гэтую выяву ў поўным памеры, загрузіце яе, альбо зьмяніце назву файла.",
+       "file-thumbnail-no": "Назва файлу пачынаецца з <strong>$1</strong>.\nВерагодна, гэта паменшаная копія выявы (<em>мініятура</em>).\nКалі вы маеце гэтую выяву ў поўным памеры, загрузіце яе, альбо зьмяніце назву файлу.",
        "fileexists-forbidden": "Файл з такой назвай ужо існуе і ня можа быць перапісаны.\nКалі ласка, вярніцеся назад і загрузіце гэты файл з новай назвай. [[File:$1|thumb|center|$1]]",
-       "fileexists-shared-forbidden": "Файл Ð· Ñ\82акой Ð½Ð°Ð·Ð²Ð°Ð¹ Ñ\83жо Ñ\96Ñ\81нÑ\83е Ñ\9e Ð°Ð³Ñ\83лÑ\8cнÑ\8bм Ñ\81Ñ\85овÑ\96Ñ\88Ñ\87Ñ\8b Ñ\84айлаÑ\9e.\nÐ\9aалÑ\96 Ð\92Ñ\8b Ð¶Ð°Ð´Ð°ÐµÑ\86е Ð·Ð°Ð³Ñ\80Ñ\83зÑ\96Ñ\86Ñ\8c Ð\92аÑ\88 Ñ\84айл, Ð²Ñ\8fÑ\80нÑ\96Ñ\86еÑ\81Ñ\8f Ð½Ð°Ð·Ð°Ð´ Ñ\96 Ð·Ð°Ð³Ñ\80Ñ\83зÑ\96Ñ\86е Ð³Ñ\8dÑ\82Ñ\8b Ñ\84айл Ð· Ð½Ð¾Ð²Ð°Ð¹ Ð½Ð°Ð·Ð²Ð°Ð¹. [[File:$1|thumb|center|$1]]",
+       "fileexists-shared-forbidden": "Файл Ð· Ñ\82акой Ð½Ð°Ð·Ð²Ð°Ð¹ Ñ\83жо Ñ\96Ñ\81нÑ\83е Ñ\9e Ð°Ð³Ñ\83лÑ\8cнÑ\8bм Ñ\81Ñ\85овÑ\96Ñ\88Ñ\87Ñ\8b Ñ\84айлаÑ\9e.\nÐ\9aалÑ\96 Ð²Ñ\8b Ð¶Ð°Ð´Ð°ÐµÑ\86е Ð·Ð°Ð³Ñ\80Ñ\83зÑ\96Ñ\86Ñ\8c Ð²Ð°Ñ\88 Ñ\84айл, Ð²Ñ\8fÑ\80нÑ\96Ñ\86еÑ\81Ñ\8f Ð½Ð°Ð·Ð°Ð´ Ñ\96 Ð·Ð°Ð³Ñ\80Ñ\83зÑ\96Ñ\86е Ð³Ñ\8dÑ\82Ñ\8b Ñ\84айл Ð· Ð½Ð¾Ð²Ð°Ð¹ Ð½Ð°Ð·Ð²Ð°Ð¹.\n[[File:$1|thumb|center|$1]]",
        "fileexists-no-change": "Гэтая загрузка зьяўляецца дакладнай копіяй цяперашняй вэрсіі <strong>[[:$1]]</strong>.",
        "fileexists-duplicate-version": "Гэтая загрузка зьяўляецца дакладнай копіяй {{PLURAL:$2|1=старой вэрсіі|старых вэрсіяў}} файлу <strong>[[:$1]]</strong>.",
        "file-exists-duplicate": "Гэты файл дублюе {{PLURAL:$1|1=наступны файл|наступныя файлы}}:",
-       "file-deleted-duplicate": "Ð\9fадобнÑ\8b Ñ\84айл ([[:$1]]) Ñ\83жо Ð²Ñ\8bдалÑ\8fÑ\9eÑ\81Ñ\8f. Ð\9aалÑ\96 Ð»Ð°Ñ\81ка, Ð¿Ð°Ð³Ð»Ñ\8fдзÑ\96Ñ\86е Ð³Ñ\96Ñ\81Ñ\82оÑ\80Ñ\8bÑ\8e Ð²Ñ\8bдаленÑ\8cнÑ\8fÑ\9e Ð³Ñ\8dÑ\82ага Ñ\84айла перад яго паўторнай загрузкай.",
+       "file-deleted-duplicate": "Ð\86дÑ\8dнÑ\82Ñ\8bÑ\87нÑ\8b Ñ\84айл ([[:$1]]) Ñ\83жо Ñ\80аней Ð²Ñ\8bдалÑ\8fÑ\9eÑ\81Ñ\8f. Ð\92ам Ñ\82Ñ\80Ñ\8dба Ð¿Ð°Ð³Ð»Ñ\8fдзеÑ\86Ñ\8c Ð³Ñ\96Ñ\81Ñ\82оÑ\80Ñ\8bÑ\8e Ð²Ñ\8bдаленÑ\8cнÑ\8fÑ\9e Ð³Ñ\8dÑ\82ага Ñ\84айлÑ\83 перад яго паўторнай загрузкай.",
        "file-deleted-duplicate-notitle": "Файл, ідэнтычны гэтаму файлу, раней ужо быў выдалены, а назва файла была забароненая.\nВам трэба зьвярнуцца да некага з правамі прагляду зьвестак забароненых файлаў, каб прааналізаваць сытуацыю перад тым, як загружаць файл ізноў.",
        "uploadwarning": "Папярэджаньне",
-       "uploadwarning-text": "Калі ласка, зьмяніце апісаньне файла ніжэй і паспрабуйце ізноў.",
+       "uploadwarning-text": "Калі ласка, зьмяніце апісаньне файлу ніжэй і паспрабуйце ізноў.",
        "uploadwarning-text-nostash": "Калі ласка, загрузіце файл ізноў, зьмніце апісаньне ніжэй і паспрабуйце яшчэ раз.",
        "savefile": "Захаваць файл",
-       "uploaddisabled": "Загрузка файлаў забароненая",
+       "uploaddisabled": "Загрузка файлаў забароненая.",
        "copyuploaddisabled": "Загрузка праз URL-адрас адключаная.",
        "uploaddisabledtext": "Загрузка файлаў забароненая.",
        "php-uploaddisabledtext": "Загрузка файлаў была адключаная ў парамэтрах канфігурацыі PHP. Калі ласка, праверце значэньне парамэтра «file_uploads».",
index ade421b..a781dbc 100644 (file)
@@ -42,7 +42,8 @@
                        "Irus",
                        "Kareyac",
                        "ShockD",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Vlad5250"
                ]
        },
        "tog-underline": "Подчертаване на препратките:",
        "right-editcontentmodel": "Редактиране на модела на съдържанието на страница",
        "right-editinterface": "Редактиране на потребителския интерфейс",
        "right-editusercss": "Редактиране на CSS файловете на други потребители",
+       "right-edituserjson": "Редактиране на JSON файловете на други потребители",
        "right-edituserjs": "Редактиране на JS файловете на други потребители",
        "right-editmyusercss": "Редактиране на собствените потребителски CSS файлове",
+       "right-editmyuserjson": "Редактиране на собствените потребителски JSON файлове",
        "right-editmyuserjs": "Редактиране на собствените потребителски JavaScript файлове",
        "right-viewmywatchlist": "Преглеждане на собствения списък за наблюдение",
        "right-editmywatchlist": "Редактиране на собствения списък за наблюдение. Забележете, че някои действия все пак ще добавят страници, дори и без текущото право.",
index 2ea950d..ee7435f 100644 (file)
        "confirm-unwatch-top": "Voleu treure aquesta pàgina de la llista de seguiment?",
        "confirm-rollback-button": "D'acord",
        "confirm-rollback-top": "Voleu revertir les modificacions a la pàgina?",
+       "colon-separator": ":&#32;",
        "quotation-marks": "«$1»",
        "imgmultipageprev": "← pàgina anterior",
        "imgmultipagenext": "pàgina següent →",
index 720e420..ec13f39 100644 (file)
        "timezoneregion-indian": "Индин океан",
        "timezoneregion-pacific": "Тийна океан",
        "allowemail": "Магийта декъашхошна хьайга электронан поштехула кехат кхехьийта",
+       "email-allow-new-users-label": "Магийта керла декъашхошна хьайга электронан поштехула кехат кхехьийта",
        "email-blacklist-label": "ХӀокху декъашхошна электронан пошт соьга яийта бехкам-бе:",
        "prefs-searchoptions": "Лахар",
        "prefs-namespaces": "ЦӀерийн меттигаш",
        "credentialsform-provider": "ДӀаяздарийн тайпа:",
        "credentialsform-account": "Декъашхочун цӀе:",
        "userjsispublic": "Тергам бе: JavaScript бухара агӀонаш чохь къайлаха хаамаш хийла ца беза, уьш массо декъашхойн тӀекхочуш йолу дера.",
-       "usercssispublic": "Тергам бе: CSS бухара агӀонаш чохь къайлаха хаамаш хийла ца беза, уьш массо декъашхойн тӀекхочуш йолу дера."
+       "usercssispublic": "Тергам бе: CSS бухара агӀонаш чохь къайлаха хаамаш хийла ца беза, уьш массо декъашхойн тӀекхочуш йолу дера.",
+       "passwordpolicies": "Паролийн политика",
+       "passwordpolicies-summary": "ХӀокху агӀонгахь гойту декъашхойн паролашна болу бехкам. Декъашхойн юкъабогӀуш болу бакъонийн тобанашка хьажжина бу бехкам.",
+       "passwordpolicies-policy-minimalpasswordlength": "Пароль $1 {{PLURAL:$1|символал}} еха хила еза",
+       "passwordpolicies-policy-passwordcannotmatchusername": "Пароль декъашхочун цӀерах тера хила ца еза",
+       "passwordpolicies-policy-passwordcannotmatchblacklist": "Пароль Ӏаьржачу могӀаман юкъахь йолучарех тера хила ца еза",
+       "passwordpolicies-policy-maximalpasswordlength": "Пароль $1 {{PLURAL:$1|символал}} йоца хила ца еза",
+       "passwordpolicies-policy-passwordcannotbepopular": "Пароль дукха {{PLURAL:$1|лелош йолчарех хила ца еза}}"
 }
index 061f7bc..53337d1 100644 (file)
        "rcfilters-view-tags-help-icon-tooltip": "زیاتر بزانە لەسەر دەستکارییە تاگکراوەکان",
        "rcfilters-liveupdates-button": "نوێکردنەوەی زیندوو",
        "rcnotefrom": "ژێرەوە {{PLURAL:$5|گۆڕانکارییەکەیە|گۆڕانکارییەکانە}} لە <strong>$3، $4</strong>ەوە (ھەتا <strong>$1</strong> نیشان دراوە).",
+       "rclistfromreset": "گەڕاندنەوەی ھەڵبژاردەی بەروار",
        "rclistfrom": "گۆڕانکارییە نوێکان نیشان بدە بە دەستپێکردن لە $3 $2",
        "rcshowhideminor": "دەستکارییە بچووکەکان $1",
        "rcshowhideminor-show": "نیشان بدە",
index 8e23bfb..1bd76b6 100644 (file)
@@ -20,7 +20,8 @@
                        "TMg",
                        "Macofe",
                        "Matma Rex",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Vlad5250"
                ]
        },
        "tog-underline": "Wótkaze pódšmarnuś:",
        "right-editsemiprotected": "Boki wobźěłaś, kótarež su pśez \"{{int:protect-level-autoconfirmed}}\" šćitane",
        "right-editinterface": "Wužywański pówjerch wobźěłaś",
        "right-editusercss": "Dataje CSS drugich wužywarjow wobźěłaś",
+       "right-edituserjson": "Dataje JSON drugich wužywarjow wobźěłaś",
        "right-edituserjs": "Dataje JS drugich wužywarjow wobźěłaś",
        "right-editmyusercss": "Twóje swójske wužywarske CSS-dataje wobźěłaś",
+       "right-editmyuserjson": "Twóje swójske wužywarske JSON-dataje wobźěłaś",
        "right-editmyuserjs": "Twóje swójske wužywarske JavaScript-dataje wobźěłaś",
        "right-viewmywatchlist": "Se swójske wobglědowańki woglědaś",
        "right-editmywatchlist": "Swóje wobglědowańki wobźěłaś. Wobglěduj, až někotare akcije hyšći pśidawaju boki bźez toś togo pšawa.",
index 2e8bf97..ca35efc 100644 (file)
        "rcfilters-other-review-tools": "Muud ülevaatusriistad",
        "rcfilters-group-results-by-page": "Rühmita tulemused lehekülje kaupa",
        "rcfilters-activefilters": "Aktiivsed filtrid",
+       "rcfilters-activefilters-hide": "Peida",
+       "rcfilters-activefilters-show": "Näita",
+       "rcfilters-activefilters-hide-tooltip": "Peida aktiivsete filtrite ala",
+       "rcfilters-activefilters-show-tooltip": "Kuva aktiivsete filtrite ala",
        "rcfilters-advancedfilters": "Täpsemad filtrid",
        "rcfilters-limit-title": "Näita nii mitut tulemust",
        "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|muudatus|muudatust}}, $2",
        "rcfilters-highlighted-filters-list": "Esile tõstetud: $1",
        "rcfilters-quickfilters": "Salvestatud filtrid",
        "rcfilters-quickfilters-placeholder-title": "Filtreid pole veel salvestatud",
-       "rcfilters-quickfilters-placeholder-description": "Et filtri sätted salvestada ja et neid hiljem uuesti kasutada, klõpsa alloleva aktiivsete filtrite loendi juures järjehoidjaikooni.",
+       "rcfilters-quickfilters-placeholder-description": "Filtri sätete salvestamiseks (et neid hiljem uuesti kasutada) klõpsa alloleva aktiivsete filtrite loendi juures järjehoidjaikooni.",
        "rcfilters-savedqueries-defaultlabel": "Salvestatud filtrid",
        "rcfilters-savedqueries-rename": "Nimeta ümber",
        "rcfilters-savedqueries-setdefault": "Määra vaikefiltriteks",
        "rcfilters-restore-default-filters": "Taasta vaikefiltrid",
        "rcfilters-clear-all-filters": "Eemalda kõik filtrid",
        "rcfilters-show-new-changes": "Vaata uusimaid muudatusi",
-       "rcfilters-search-placeholder": "Filtri muudatusi (kasuta menüüd või otsi filtri nime)",
+       "rcfilters-search-placeholder": "Filtreeri muudatusi (kasuta menüüd või tipi filtri nimi)",
        "rcfilters-invalid-filter": "Vigane filter",
        "rcfilters-empty-filter": "Aktiivsed filtrid puuduvad. Näidatakse kogu kaastööd.",
        "rcfilters-filterlist-title": "Filtrid",
index 0d44d82..3eb42f9 100644 (file)
@@ -65,7 +65,9 @@
                        "Alp Er Tunqa",
                        "Baloch Khan",
                        "Fitoschido",
-                       "Alireza Ivaz"
+                       "Alireza Ivaz",
+                       "Iriman",
+                       "Matěj Suchánek"
                ]
        },
        "tog-underline": "خط کشیدن زیر پیوندها:",
        "copyrightwarning": "لطفاً توجه داشته‌باشید که همهٔ مشارکت‌ها در {{SITENAME}} منتشرشده تحت $2 در نظر گرفته‌می‌شوند (برای جزئیات بیش‌تر $1 را ببینید).\nاگر نمی‌خواهید نوشته‌هایتان بی‌رحمانه ویرایش و توزیع شوند؛ بنابراین، آنها را اینجا ارائه نکنید.<br />\nشما همچنین به ما تعهد می‌کنید که خودتان این را نوشته‌اید یا آن را از یک منبع با مالکیت عمومی یا مشابه آزاد آن برداشته‌اید (برای جزئیات بیش‌تر $1 را ببینید).\n<strong>کارهای دارای حق تکثیر را بدون اجازه ارائه نکنید!</strong>",
        "copyrightwarning2": "لطفاً توجه داشته‌باشید که همهٔ مشارکت‌ها در {{SITENAME}} ممکن است توسط دیگر مشارکت‌کنندگان تغییر یابند، ویرایش یا حذف شوند.\nاگر نمی‌خواهید نوشته‌هایتان بی‌رحمانه ویرایش شوند؛ بنابراین، آنها را اینجا ارائه نکنید.<br />\nشما همچنین به ما تعهد می‌کنید که خودتان این را نوشته‌اید یا آن را از یک منبع با مالکیت عمومی یا مشابه آزاد آن برداشته‌اید ($1 را برای جزئیات بیشتر ببینید).\n<strong>کارهای دارای حق تکثیر را بدون اجازه ارائه نکنید!</strong>",
        "editpage-cannot-use-custom-model": "مدل محتوای این صفحه نمی‌تواند عوض شود.",
-       "longpageerror": "'''خطا: متنی که ارسال کرده‌اید {{PULAR:$1|یک کیلوبایت|$1 کیلوبایت}} طول دارد. این مقدار از مقدار بیشینهٔ {{PLURAL:$2|یک کیلوبایت|$2 کیلوبایت}} بیشتر است.'''\nنمی‌توان آن را ذخیره کرد.",
+       "longpageerror": "'''خطا: متنی که ارسال کرده‌اید {{PLURAL:$1|یک کیلوبایت|$1 کیلوبایت}} طول دارد. این مقدار از مقدار بیشینهٔ {{PLURAL:$2|یک کیلوبایت|$2 کیلوبایت}} بیشتر است.'''\nنمی‌توان آن را ذخیره کرد.",
        "readonlywarning": "<strong>هشدار: پایگاه داده برای نگهداری قفل شده‌است، به همین علت هم‌اکنون نمی‌توانید ویرایش‌هایتان را ذخیره کنید.</strong>\nاگر می‌خواهید متن را در یک پروندهٔ متنی کپی کنید و برای آینده ذخیره‌اش کنید.\n\nمدیری که آن را قفل کرده این توضیح را ارائه کرده‌است: $1",
        "protectedpagewarning": "'''هشدار: این صفحه قفل شده‌است تا فقط کاربران با دسترسی مدیریت بتوانند ویرایشش کنند.'''\nآخرین موارد سیاهه در زیر آمده‌است:",
        "semiprotectedpagewarning": "'''توجه:''' این صفحه قفل شده‌است تا تنها کاربران ثبت‌نام‌کرده قادر به ویرایش آن باشند.\nآخرین موارد سیاهه در زیر آمده‌است:",
        "recentchanges-label-minor": "این یک ویرایش جزئی است",
        "recentchanges-label-bot": "این ویرایش را یک ربات انجام داده است",
        "recentchanges-label-unpatrolled": "این ویرایش هنوز گشت‌زنی نشده است",
-       "recentchanges-label-plusminus": "حجم صفحه به اندازه این مقدار بایت تغییر یافته است",
+       "recentchanges-label-plusminus": "حجم صفحه به اندازه این مقدار بایت تغییر یافته است.",
        "recentchanges-legend-heading": "<strong>اختصارها:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (همچنین به [[Special:NewPages|فهرست صفحات تازه]] نگاه کنید)",
        "recentchanges-legend-plusminus": "(<em>±۱۲۳</em>)",
        "filehist-dimensions": "ابعاد",
        "filehist-filesize": "اندازهٔ پرونده",
        "filehist-comment": "توضیح",
-       "imagelinks": "به‌کاررفتن پرونده",
+       "imagelinks": "کاربرد پرونده",
        "linkstoimage": "{{PLURAL:$1|صفحهٔ|صفحه‌های}} زیر به این تصویر پیوند {{PLURAL:$1|دارد|دارند}}:",
        "linkstoimage-more": "بیش از $1 صفحه به این پرونده پیوند {{PLURAL:$1|دارد|دارند}}.\nفهرست زیر تنها {{PLURAL:$1|اولین پیوند|اولین $1 پیوند}} به این صفحه را نشان می‌دهد.\n[[Special:WhatLinksHere/$2|فهرست کامل]] نیز موجود است.",
        "nolinkstoimage": "این پرونده در هیچ صفحه‌ای به کار نرفته‌است.",
index 2d03e12..b69d49c 100644 (file)
@@ -12,7 +12,8 @@
                        "Macofe",
                        "Matma Rex",
                        "Nemo bis",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Tacsipacsi"
                ]
        },
        "tog-underline": "Ferwisangen onerstrik:",
        "category_header": "Sidjen uun kategorii \"$1\"",
        "subcategories": "Onerkategoriin",
        "category-media-header": "Meedien uun kategorii \"$1\"",
-       "category-empty": "\"Uun detdiar kategorii san uun uugenblak nian sidjen of meedien.\"",
+       "category-empty": "<em>Uun detdiar kategorii san uun uugenblak nian sidjen of meedien.</em>",
        "hidden-categories": "{{PLURAL:$1|Ferbürgen kategorii|Ferbürgen kategoriin}}",
        "hidden-category-category": "Ferbürgen kategoriin",
        "category-subcat-count": "{{PLURAL:$2|Detdiar kategorii hää ian onerkategorii.|Uun detdiar kategorii {{PLURAL:$1|stäänt ian onerkategorii|stun $1 onerkategoriin}} faan $2 uun't gehial.}}",
index d63d371..79e62fe 100644 (file)
        "nav-login-createaccount": "Oanmelde",
        "logout": "Ofmelde",
        "userlogout": "Ofmelde",
-       "notloggedin": "Net oanmelde",
+       "notloggedin": "Net oanmeld",
        "userlogin-noaccount": "Hasto gjin akkount?",
        "userlogin-joinproject": "Meidwaan {{SITENAME}}",
        "createaccount": "Registrearje",
        "upload": "Bied triem oan",
        "uploadbtn": "Bied triem oan",
        "reuploaddesc": "Werom nei oanbied-side.",
-       "uploadnologin": "Net oanmelde",
+       "uploadnologin": "Net oanmeld",
        "uploadnologintext": "Jo moatte [[Special:UserLogin|oanmeld]] wêze om in triem oanbiede te kinnen.",
        "upload_directory_missing": "De heechlaadmap ($1) is der net en koe net oanmakke wurde troch de webserver.",
        "upload_directory_read_only": "De webserver kin net skriuwe yn de oanbiedpad ($1).",
index 8cd97f4..7c25725 100644 (file)
        "watchlisttools-raw": "עריכת רשימת המעקב הגולמית",
        "iranian-calendar-m1": "פרורדין",
        "iranian-calendar-m2": "ארדיבהשת",
-       "iranian-calendar-m3": "ח׳רדאד",
+       "iranian-calendar-m3": "ח'רדאד",
        "iranian-calendar-m4": "תיר",
        "iranian-calendar-m5": "מרדאד",
        "iranian-calendar-m6": "שהריור",
        "iranian-calendar-m7": "מהר",
        "iranian-calendar-m8": "אבאן",
-       "iranian-calendar-m9": "אד׳ר",
+       "iranian-calendar-m9": "אד'ר",
        "iranian-calendar-m10": "די",
        "iranian-calendar-m11": "בהמן",
        "iranian-calendar-m12": "אספנד",
index 55eba4d..cad811d 100644 (file)
@@ -39,7 +39,8 @@
                        "Wumbolo",
                        "Fitoschido",
                        "Hamster",
-                       "BadDog"
+                       "BadDog",
+                       "Vlad5250"
                ]
        },
        "tog-underline": "Podcrtavanje poveznica",
        "right-editcontentmodel": "Odredi modela sadržaja stranice",
        "right-editinterface": "Uređivanje suradničkog sučelja",
        "right-editusercss": "Uređivanje CSS stranica drugih suradnika",
+       "right-edituserjson": "Uređivanje JSON stranica drugih suradnika",
        "right-edituserjs": "Uređivanje JS stranica drugih suradnika",
        "right-editmyusercss": "Uređivanje vlastitih CSS stranica",
+       "right-editmyuserjson": "Uređivanje vlastitih JSON stranica",
        "right-editmyuserjs": "Uređivanje vlastitih JavaScript stranica",
        "right-viewmywatchlist": "Pregled svojeg popisa praćenih stranica",
        "right-editmywatchlist": "Uređivanje vlastitog popisa praćenja. Određenim djelovanjima moguće je dodavati stranice i bez ovoga dopuštenja.",
        "rcfilters-other-review-tools": "Ostali alati za pregledavanje:",
        "rcfilters-group-results-by-page": "Grupiranje rezultata po stranici",
        "rcfilters-activefilters": "Aktivni filtri",
+       "rcfilters-activefilters-hide": "Skrij",
+       "rcfilters-activefilters-show": "Pokaži",
        "rcfilters-advancedfilters": "Napredni filtri",
        "rcfilters-limit-title": "Rezultata za prikaz",
        "rcfilters-limit-and-date-label": "{{PLURAL:$1|$1 izmjena|$1 izmjene|$1 izmjena}}, $2",
        "rcfilters-watchlist-edit-watchlist-button": "Izmijeni popis praćenih stranica",
        "rcfilters-preference-label": "Skrij poboljšanu inačicu nedavnih promjena",
        "rcfilters-preference-help": "Vraća natrag stanje prije redizajna sučelja 2017., te svih oruđa dodanih tada i poslije toga.",
+       "rcfilters-watchlist-preference-label": "Sakrij poboljšanu inačicu popisa praćenja",
+       "rcfilters-watchlist-preference-help": "Vraća natrag stanje prije redizajna sučelja 2017., te svih oruđa dodanih tada i poslije toga.",
        "rcnotefrom": "Ispod {{PLURAL:$5|je izmjena|su izmjene}} od <strong>$3, $4</strong> (prikazano ih do <strong>$1</strong>).",
        "rclistfromreset": "Vrati na zadano izbor datuma",
        "rclistfrom": "Prikaži nove promjene počevši od $3 $2",
        "dellogpage": "Evidencija brisanja",
        "dellogpagetext": "Dolje je popis nedavnih brisanja.\nSva vremena su prema poslužiteljevom vremenu.",
        "deletionlog": "evidencija brisanja",
+       "logentry-create-create": "$1 {{GENDER:$2|stvorio|stvorila}} je stranicu $3",
        "reverted": "Vraćeno na prijašnju inačicu",
        "deletecomment": "Razlog:",
        "deleteotherreason": "Drugi/dodatni razlog:",
index 6bd9201..9264fbb 100644 (file)
        "rcfilters-activefilters": "Aktív szűrők",
        "rcfilters-activefilters-hide": "Elrejt",
        "rcfilters-activefilters-show": "Mutat",
+       "rcfilters-activefilters-hide-tooltip": "Aktív szűrők dobozának elrejtése",
+       "rcfilters-activefilters-show-tooltip": "Aktív szűrők dobozának megjelenítése",
        "rcfilters-advancedfilters": "Haladó szűrők",
        "rcfilters-limit-title": "Megjelenítendő találatok száma",
        "rcfilters-limit-and-date-label": "$1 változtatás, $2",
index f632abc..5c9ac57 100644 (file)
        "resetpass-submit-loggedin": "Cambiar contrasigno",
        "resetpass-submit-cancel": "Cancellar",
        "resetpass-wrong-oldpass": "Le contrasigno temporari o actual es invalide.\nEs possibile que tu ha ja cambiate tu contrasigno o requestate un nove contrasigno temporari.",
-       "resetpass-recycled": "Redefini tu contrasigno a un differente del actual, per favor.",
+       "resetpass-recycled": "Cambia tu contrasigno a un differente del actual, per favor.",
        "resetpass-temp-emailed": "Tu ha aperite session con un codice temporari que tu recipeva in e-mail.\nPro completar le accesso, tu debe definir un nove contrasigno hic:",
        "resetpass-temp-password": "Contrasigno temporari:",
        "resetpass-abort-generic": "Le cambio del contrasigno ha essite abortate per un extension.",
        "resetpass-expired": "Le contrasigno ha expirate. Per favor defini un nove contrasigno pro aperir session.",
-       "resetpass-expired-soft": "Le contrasigno ha expirate e debe esser redefinite. Per favor elige un nove contrasigno ora, o clicca sur \"{{int:authprovider-resetpass-skip-label}}\" pro redefinir lo plus tarde.",
-       "resetpass-validity-soft": "Le contrasigno non es valide: $1\n\nPer favor elige un nove contrasigno ora, o clicca sur \"{{int:authprovider-resetpass-skip-label}}\" pro redefinir lo plus tarde.",
+       "resetpass-expired-soft": "Le contrasigno ha expirate e debe esser cambiate. Per favor, elige un nove contrasigno ora, o clicca sur \"{{int:authprovider-resetpass-skip-label}}\" pro cambiar lo plus tarde.",
+       "resetpass-validity-soft": "Le contrasigno non es valide: $1\n\nPer favor, elige un nove contrasigno ora, o clicca sur \"{{int:authprovider-resetpass-skip-label}}\" pro cambiar lo plus tarde.",
        "passwordreset": "Reinitialisar contrasigno",
        "passwordreset-text-one": "Completa iste formulario pro reinitialisar tu contrasigno.",
        "passwordreset-text-many": "{{PLURAL:$1|Completa un de iste campos pro reciper un contrasigno temporari in e-mail.}}",
        "converter-manual-rule-error": "Error detegite in le regula manual de conversion de lingua",
        "undo-success": "Le modification pote esser disfacite.\nPer favor controla le comparation infra pro verificar que tu vole facer isto, e postea salveguarda le modificationes infra pro assi disfacer le modification.",
        "undo-failure": "Le modification non poteva esser annullate a causa de conflicto con modificationes intermedie.",
+       "undo-main-slot-only": "Le modification non poteva esser disfacite perque illo implica contento foras del cannellatura principal.",
        "undo-norev": "Impossibile annullar le modification proque illo non existe o esseva delite.",
        "undo-nochange": "Pare que iste modification ha jam essite disfacite.",
        "undo-summary": "Annullava le version $1 per [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussion]] | [[Special:Contributions/$2|{{MediaWiki:Contribslink}}]])",
        "rcfilters-other-review-tools": "Altere instrumentos de revision",
        "rcfilters-group-results-by-page": "Gruppar resultatos per pagina",
        "rcfilters-activefilters": "Filtros active",
+       "rcfilters-activefilters-hide": "Celar",
+       "rcfilters-activefilters-show": "Monstrar",
+       "rcfilters-activefilters-hide-tooltip": "Celar le area de filtros active",
+       "rcfilters-activefilters-show-tooltip": "Monstrar le area de filtros active",
        "rcfilters-advancedfilters": "Filtros avantiate",
        "rcfilters-limit-title": "Resultatos a monstrar",
        "rcfilters-limit-and-date-label": "$1 modification{{PLURAL:$1||es}}, $2",
        "rcfilters-savedqueries-rename": "Renominar",
        "rcfilters-savedqueries-setdefault": "Predefinir",
        "rcfilters-savedqueries-unsetdefault": "Remover predefinition",
-       "rcfilters-savedqueries-remove": "Remover",
+       "rcfilters-savedqueries-remove": "Deler",
        "rcfilters-savedqueries-new-name-label": "Nomine",
        "rcfilters-savedqueries-new-name-placeholder": "Describe le proposito del filtro",
        "rcfilters-savedqueries-apply-label": "Crear filtro",
        "rcfilters-empty-filter": "Nulle filtro active. Tote le contributiones es monstrate.",
        "rcfilters-filterlist-title": "Filtros",
        "rcfilters-filterlist-whatsthis": "Como functiona istes?",
-       "rcfilters-filterlist-feedbacklink": "Da nos tu opinion sur iste (nove) instrumentos de filtrage",
+       "rcfilters-filterlist-feedbacklink": "Da nos tu opinion sur iste instrumentos de filtrage",
        "rcfilters-highlightbutton-title": "Colorar le resultatos",
        "rcfilters-highlightmenu-title": "Selige un color",
        "rcfilters-highlightmenu-help": "Selige un color pro illuminar iste proprietate",
index 65e62e0..269e7a0 100644 (file)
@@ -9,7 +9,8 @@
                        "לערי ריינהארט",
                        "아라",
                        "Macofe",
-                       "Nemo bis"
+                       "Nemo bis",
+                       "Matěj Suchánek"
                ]
        },
        "tog-underline": "Pinag-ugisan ti silpo:",
        "deletereason-dropdown": "* Kadawyan a rasrason ti panagikkat\n** Spam\n** Bandalismo\n** Panaglabsing iti karbengan ti kopia\n** Kiddaw ti mannurat\n** Naputed a baw-ing",
        "delete-edit-reasonlist": "Urnosen dagiti rason ti panagikkat",
        "delete-toobig": "Daytoy a panid ket dakkel ti pakasaritaanna, sumurok a  $1 {{PLURAL:a panagbaliwan|dagiti panagbaliwan}}.\nTi panagikkat ti kastoy a pammpanid ket naparitan tapno mapawilan ti saan nga inkarkaro a pannakadadael ti {{SITENAME}}.",
-       "delete-warning-toobig": "Daytoy a panid ket adda ti dakkel unay a pakasaritaan ti panagurnos, ti kaadu nga $1 {{PLURAL:$1|panagbaliw|dagiti panagbaliw}}.\nTi panagikkat ket madisturbo ti panagpataray ti database ti {{SITNAME}};\nagal-aluadka a mangrugi.",
+       "delete-warning-toobig": "Daytoy a panid ket adda ti dakkel unay a pakasaritaan ti panagurnos, ti kaadu nga $1 {{PLURAL:$1|panagbaliw|dagiti panagbaliw}}.\nTi panagikkat ket madisturbo ti panagpataray ti database ti {{SITENAME}};\nagal-aluadka a mangrugi.",
        "deleteprotected": "Saanmo a maikkat daytoy a panid gapu ta nasalaknibanen.",
        "deleting-backlinks-warning": "<strong>Ballaag:</strong> [[Special:WhatLinksHere/{{FULLPAGENAME}}|Dagiti dadduma a panid]] ket naisilpo wenno nailak-am manipud iti panid a gangani nga ikkatem.",
        "deleting-subpages-warning": "<strong>Ballaag:</strong> Ti panid nga ik-ikkatem ket addaan [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|iti subpanid|kadagiti $1 a subpanid|51=kadagiti sumurok a 50 a subpanid}}]].",
        "ip_range_toolarge": "Dagiti serra a nasakop a dakdakkel ngem /$1 ket saan a maipalubos.",
        "proxyblocker": "Pannakbagi a panagserra",
        "proxyblockreason": "Ti IP a pagtaengam ket naserraan ngamin ket daytoy ket nakalukat a panakbagi.\nPangngaasi a kontakem ti agit-ited ti serbisio ti Internetmo wenno teknikal a suporta ti gunglom ken ibagam kaniada ti nakaro a parikut ti seguridad.",
-       "sorbsreason": "Ti IP a pagtaengam ket nailista a kasla \"nalukatan a pannakbagi\" iti DNSBL nga inusar babaen ti {{SITNAME}}.",
-       "sorbs_create_account_reason": "Ti IP a pagtaengam ket nailista a kasla \"nalukatan a pannakbagi\" iti DNSBL nga inusar babaen ti {{SITNAME}}.\nSaanka a makapartuat ti pakabilangan.",
+       "sorbsreason": "Ti IP a pagtaengam ket nailista a kasla \"nalukatan a pannakbagi\" iti DNSBL nga inusar babaen ti {{SITENAME}}.",
+       "sorbs_create_account_reason": "Ti IP a pagtaengam ket nailista a kasla \"nalukatan a pannakbagi\" iti DNSBL nga inusar babaen ti {{SITENAME}}.\nSaanka a makapartuat ti pakabilangan.",
        "softblockrangesreason": "Dagiti kontribusion ti di ammo ket saan a maipalubos manipud iti IP nga adresmo ($1). Pagngaasi a sumrek.",
        "xffblockreason": "Ti maysa nga adres ti IP nga adda iti paulo ti X-Forwarded-For, mabalin a kukuam wenno ti pannakbagi a server nga us-usarem, ket naserraan. Ti kasisigud a rason ti pannakaserra idi ket: $1",
        "cant-see-hidden-user": "Ti agar-aramat a kayatmo a serraan ket naserraanen ken nailemmeng.\nGapu ta awan met ti karbengam nga agilemming ti agar-aramat, saanmo a makita wenno maurnos ti serra ti agar-aramat.",
        "monthsall": "amin",
        "confirmemail": "Pasingkedan ti esurat a pagtaengam",
        "confirmemail_noemail": "Awan ti umisu nga e-surat a pagtaengam a naikabil idiay [[Special:Preferences|kaykayat ti agar-aramat]].",
-       "confirmemail_text": "Ti {{SITNAME}} ket masapulna a pasingkedam ti esurat a pagtaengam sakbay nga agusar ti esurat a langa.\nIpabalinmo dita baba a buton ti panagitulod ti pasingkedan a surat dita a pagtaengam.\nTi surat ket mangiraman ti panilpo nga aglaon ti maysa a kodigo;\nikabil ti panilpo dita pagbasabasam tapno mapasingkedam a ti esurat a pagtaengam ket umisu.",
+       "confirmemail_text": "Ti {{SITENAME}} ket masapulna a pasingkedam ti esurat a pagtaengam sakbay nga agusar ti esurat a langa.\nIpabalinmo dita baba a buton ti panagitulod ti pasingkedan a surat dita a pagtaengam.\nTi surat ket mangiraman ti panilpo nga aglaon ti maysa a kodigo;\nikabil ti panilpo dita pagbasabasam tapno mapasingkedam a ti esurat a pagtaengam ket umisu.",
        "confirmemail_pending": "Ti pasingkedan a kodigo ket naipatulod kenkan:\nno kadamdama ka a nangaramid ti pakabilangam, aguray ka pay ti mano a minutos a sumangpet sakbay ka nga agpadas ti agkiddaw ti baro a kodigo.",
        "confirmemail_send": "Agipatulod ti pasingkedan a kodigo",
        "confirmemail_sent": "Naipatuloden ti pammasingked nga esurat.",
index 370d9a4..01fc2fd 100644 (file)
        "excontentauthor": "чухьнахьадар: «$1», цу оагIон цаI марка воаца автор ва [[Special:Contributions/$2|$2]] ([[User talk:$2|дувцара оагIув]])",
        "delete-confirm": "$1 — дӀаяккхар",
        "delete-legend": "ДӀаяккхар",
-       "confirmdeletetext": "Оаш дIадийхад бIарчча дIадаккхар оагIон а (е сурта), цун деррига хувцара истори а. '''Дехар да''', бакъде шоай из бокъонца де безам болаш долга а, из дича хургдар кхеташ долга а, из дар укх [[{{MediaWiki:Policy-url}}|бокъонашца]] долга.",
+       "historywarning": "<strong>Теркам:</strong> Хьо дӀаяккха гӀертача оагӀон тоадара истори я $1 {{PLURAL:$1|эрш}} чулоацаш:",
+       "confirmdeletetext": "Оаш дIадийхад еррига оагIув (е сурт) дIаяккхар а, цун деррига хувцара истори дIаяккхар а. '''Дехар да''', бакъде шоай из бокъонца де безам болаш долга а, из дича хургдар кхеташ долга а, из хьадар укх [[{{MediaWiki:Policy-url}}|бокъонашца]] долга а.",
        "actioncomplete": "Кхоачашдаьд",
        "actionfailed": "Кхоачашдаьдац",
        "deletedtext": "«$1» дIаяьккхай.\nХьажа $2 тIехьара дIадаккхарий хьаязъяьрга бIаргтохаргболаш.",
        "deletecomment": "Бахьан:",
        "deleteotherreason": "Кхыдола бахьан/тIатохар:",
        "deletereasonotherlist": "Кхыдола бахьан",
+       "deleting-backlinks-warning": "'''ДӀахьалхадаккхар:''' Iа дӀайоаккхаш йолча оагIонна тӀатовжаш я [[Special:WhatLinksHere/{{FULLPAGENAME}}|кхыйола оагӀонаш]].",
        "rollbacklink": "юхаяккха",
        "rollbacklinkcount": "юхататта $1 {{PLURAL:$1|нийсдар}}",
        "rollback-success": "{{GENDER:$3|$1}} яха доакъашхочо тоадаьр юхататтад; юхайоалаяьй {{GENDER:$4|$2}} доакъашхочун эрш.",
index b095455..5f06f9b 100644 (file)
        "timezoneregion-europe": "Europa",
        "timezoneregion-indian": "Indiana Oceano",
        "timezoneregion-pacific": "Pacifico",
-       "allowemail": "Permisez e-posti de altra uzanti",
+       "allowemail": "Permisez e-posti de altra uzeri",
        "email-allow-new-users-label": "Permisez e-posti de la nova uzeri",
        "email-blacklist-label": "Impedez la sequanta uzeri sendar e-posto a me:",
        "prefs-namespaces": "Nomari",
        "prefs-help-email": "Informar vua adreso di e-posto esas fakultativa, tamen ol bezonesos se vu oblivior vua pasovorto e bezonor la sendo di nova pasovorto.",
        "prefs-help-email-others": "Vu povas anke selektar se permisos altri skribar a vu per e-posto, per ligilo en vua uzanto-pagino o diskuto-pagino.\nVua e-posto adreso ne revelesos, kande l'altri skribos a vu.",
        "prefs-help-email-required": "E-postala adreso es bezonata.",
-       "prefs-info": "Bazala informeso",
+       "prefs-info": "Bazala informo",
        "prefs-i18n": "Internacioniigo",
        "prefs-signature": "Signaturo",
        "prefs-timeoffset": "Registro dil tempo",
index 3aa9d6f..6d5ce82 100644 (file)
        "customcssprotected": "Þú hefur ekki leyfi að breyta þessari CSS-umbrotsíðu, því hún hefur notandastillingar annars notanda.",
        "customjsprotected": "Þú hefur ekki leyfi til að breyta þessari JavaScript síðu, því hún hefur notandastillingar annars notanda.",
        "mycustomcssprotected": "Þú hefur ekki leyfi til þess að breyta þessari CSS-síðu.",
+       "mycustomjsonprotected": "Þú hefur ekki heimild til að breyta þessari JSON-síðu.",
        "mycustomjsprotected": "Þú hefur ekki leyfi til þess að breyta þessari JavaScript-síðu.",
        "myprivateinfoprotected": "Þú ert ekki með réttindi til að breyta einkaupplýsingunum þínum.",
        "mypreferencesprotected": "Þú ert ekki með réttindi til að breyta kjörstillingunum þínum.",
        "wrongpasswordempty": "Lykilorðsreiturinn var auður. Reyndu aftur.",
        "passwordtooshort": "Lykilorð skal vera að minnsta kosti $1 {{PLURAL:$1|stafur|stafir}}.",
        "passwordtoolong": "Lykilorð geta ekki verið lengri en $1 {{PLURAL:$1|stafur|stafir}}.",
-       "passwordtoopopular": "Ekki má nota algeng lykilorð. Veldu eitthvað alveg sérstakt lykilorð.",
+       "passwordtoopopular": "Ekki má nota títt valin lykilorð. Vinsamlegast velja lykilorð sem erfiðara er að giska á.",
        "password-name-match": "Þarf að lykilorð þitt sé öðruvísi notandanafni þínu",
        "password-login-forbidden": "Notkun þessa notandanafns og lykilorðs er ekki leyfileg.",
        "mailmypassword": "Endurstilla lykilorð",
        "botpasswords-existing": "Fyrirliggjandi lykilorð vélmenna",
        "botpasswords-createnew": "Búa til nýtt lykilorð vélmennis",
        "botpasswords-editexisting": "Breyta fyrirliggjandi lykilorði vélmennis",
+       "botpasswords-label-needsreset": "(lykilorð þarf að endurstilla)",
        "botpasswords-label-appid": "Heiti vélmennis:",
        "botpasswords-label-create": "Búa til",
        "botpasswords-label-update": "Uppfæra",
        "savechanges": "Vista breytingar",
        "publishpage": "Gefa út síðu",
        "publishchanges": "Gefa út breytingar",
+       "savearticle-start": "Vista síðu…",
+       "savechanges-start": "Vista breytingar…",
+       "publishpage-start": "Birta síðu...",
+       "publishchanges-start": "Gefa út breytingar...",
        "preview": "Forskoða",
        "showpreview": "Forskoða",
        "showdiff": "Sýna breytingar",
        "anoneditwarning": "<strong>Viðvörun:</strong> Þú ert ekki innskráð(ur). Vistfang þitt verður sýnt opinberlega ef þú gerir einhverjar breytingar. Ef þú <strong>[$1 skráir þig inn]</strong> eða <strong>[$2 stofnar aðgang]</strong> munu breytingarnar þínar vera tengdar við notandanafn þitt, ásamt öðrum kostum.",
        "anonpreviewwarning": "<em>Þú ert ekki skráð(ur) inn. Vistfang þitt skráist í breytingaskrá síðunnar.</em>",
        "missingsummary": "'''Áminning:''' Þú hefur ekki skrifað breytingarágrip.\nEf þú smellir á Vista aftur, verður breyting þín vistuð án þess.",
+       "selfredirect": "<strong>Viðvörun:</strong> Þú ert að vísa þessa síðu á sjálfa sig.\nÞað kann að vera að þú hafir valið vitlausa marksíðu fyrir tilvísunina eða að þú sért að breyta vitlausri síðu.\nEf þú smellir aftur á „$1“ verður tilvísunin samt búin til.",
        "missingcommenttext": "Gerðu svo vel og skrifaðu athugasemd.",
        "missingcommentheader": "<strong>Áminning:</strong> Þú hefur ekki gefið upp umræðuefni.\nEf þú smellir á \"$1\" aftur, verður breyting þín vistuð án þess.",
        "summary-preview": "Forskoða breytingarágrip:",
        "subject-preview": "Forskoðun viðfangsefnis:",
        "previewerrortext": "Óvænt villa kom upp þegar reynt var að forskoða breytingarnar þínar.",
        "blockedtitle": "Notandi er bannaður",
-       "blockedtext": "'''Notandanafn þitt eða vistfang hefur verið bannað.'''\n\nBannið var sett af $1.\nÁstæðan er eftirfarandi: ''$2''.\n\n* Bannið hófst: $8\n* Banninu lýkur: $6\n* Sá sem banna átti: $7\n\nÞú getur haft samband við $1 eða annan [[{{MediaWiki:Grouppage-sysop}}|stjórnanda]] til að ræða bannið.\nÞú getur ekki notað „Senda þessum notanda tölvupóst“ aðgerðina nema gilt netfang sé skráð í [[Special:Preferences|kjörstillingum þínum]] og að þér hafi ekki verið óheimilað það.\nNúverandi vistfang þitt er $3, og bannnúmerið er #$5.\nHafðu með allar þessar upplýsingar hér fyrir ofan í fyrirspurnum þínum.",
-       "autoblockedtext": "Vistfang þitt hefur verið sjálfvirkt bannað því það var notað af öðrum notanda, sem var bannaður af $1.\nÁstæðan er eftirfarandi:\n\n:''$2''\n\n* Bannið hófst: $8\n* Banninu lýkur: $6\n* Sá sem banna átti: $7\n\nÞú getur haft samband við $1 eða annan [[{{MediaWiki:Grouppage-sysop}}|stjórnanda]] til að ræða bannið.\n\nAthugaðu að þú getur ekki notað „Senda þessum notanda tölvupóst“ aðgerðina nema gilt netfang sé skráð í [[Special:Preferences|kjörstillingum þínum]] og að þér hafi ekki verið óheimilað það.\n\nNúverandi vistfang þitt er $3, og bannnúmerið er #$5.\nHafðu með allar þessar upplýsingar hér fyrir ofan í fyrirspurnum þínum.",
+       "blockedtext": "<strong>Þitt notandanafn eða vistfang hefur verið bannað.</strong>\n\nBannið var sett af $1.\nÁstæðan er eftirfarandi: <em>$2</em>.\n\n* Bannið hófst: $8\n* Banninu lýkur: $6\n* Sá sem banna átti: $7\n\nÞú getur haft samband við $1 eða annan [[{{MediaWiki:Grouppage-sysop}}|stjórnanda]] til að ræða bannið.\nÞú getur ekki notað „Senda þessum notanda tölvupóst“ aðgerðina nema gilt netfang sé skráð í [[Special:Preferences|kjörstillingum þínum]] og að þér hafi ekki verið óheimilað það.\nNúverandi vistfang þitt er $3, og bannnúmerið er #$5.\nHafðu með allar þessar upplýsingar hér fyrir ofan í fyrirspurnum þínum.",
+       "autoblockedtext": "Vistfang þitt hefur verið sjálfvirkt bannað því það var notað af öðrum notanda, sem var bannaður af $1.\nÁstæðan er eftirfarandi:\n\n:<em>$2</em>\n\n* Bannið hófst: $8\n* Banninu lýkur: $6\n* Sá sem banna átti: $7\n\nÞú getur haft samband við $1 eða annan [[{{MediaWiki:Grouppage-sysop}}|stjórnanda]] til að ræða bannið.\n\nAthugaðu að þú getur ekki notað „Senda þessum notanda tölvupóst“ aðgerðina nema gilt netfang sé skráð í [[Special:Preferences|kjörstillingum þínum]] og að þér hafi ekki verið óheimilað það.\n\nNúverandi vistfang þitt er $3, og bannnúmerið er #$5.\nHafðu með allar þessar upplýsingar hér fyrir ofan í fyrirspurnum þínum.",
        "systemblockedtext": "Notandanafnið þitt eða IP-vistfangið hafa verið útilokuð sjálfvirkt af MediaWiki.\nUppgefin ástæða er:\n\n:<em>$2</em>\n\n* Upphaf útilokunar: $8\n* Útilokun rennur út: $6\n* Sá sem átti að útiloka: $7\n\nNúverandi IP-vistfang þitt er$3.\nHafðu allar þessar upplýsingar með í öllum þeim fyrirspurnum sem þú gætir gert vegna þessa.",
        "blockednoreason": "engin ástæða gefin",
        "whitelistedittext": "Þú þarft að $1 þig til að breyta síðum.",
        "blocked-notice-logextract": "Þessi notandi er í banni.\nSíðasta færsla notandans úr bannskrá er birt hér fyrir neðan til skýringar:",
        "clearyourcache": "<strong>Athugaðu:</strong> Eftir vistun kann að vera að þú þurfir að komast hjá skyndiminni vafrans þíns til að sjá breytingarnar.\n* <strong>Firefox / Safari:</strong> Haltu <em>Shift</em> samtímis og þú smellir á <em>Endurhlaða (Reload)</em>, eða ýttu á annaðhvort <em>Ctrl-F5</em> eða <em>Ctrl-R</em> (<em>⌘-R</em> á Mac)\n* <strong>Google Chrome:</strong> Ýttu á <em>Ctrl-Shift-R </em>(<em>⌘-Shift-R</em> á Mac)\n* <strong>Internet Explorer:</strong> Haltu <em>Ctrl</em> samtímis og þú smellir á <em>Endurnýja (Refresh)</em>, eða ýttu á <em>Ctrl-F5</em>\n* <strong>Opera:</strong> Farðu í <em>Verkfæri (Tools) → Stillingar (Preferences)</em> og svo <em>Friðhelgi og öryggi (Privacy & security) → Hreinsa vafragögn (Clear browsing data) → Myndir og skrár í skyndiminni (Chached images and files)</em>",
        "usercssyoucanpreview": "'''Ath:''' Hægt er að nota „{{int:showpreview}}“ hnappinn til að prófa CSS-kóða áður en hann er vistaður.",
+       "userjsonyoucanpreview": "<strong>Ath.:</strong> Hægt er að nota hnappinn „{{int:showpreview}}“ til að prófa JSON-kóða áður en hann er vistaður.",
        "userjsyoucanpreview": "'''Ath:''' Hægt er að nota \"{{int:showpreview}}\" hnappinn til að prófa JavaScript-kóða áður en hann er vistaður.",
        "usercsspreview": "'''Mundu að þú ert aðeins að forskoða CSS-kóðann þinn.'''\n'''Hann hefur ekki enn verið vistaður!'''",
        "userjspreview": "'''Mundu að þú ert aðeins að prófa/forskoða JavaScript-kóðann þinn.'''\n'''Hann hefur ekki enn verið vistaður!'''",
        "rcfilters-other-review-tools": "Önnur rýniverkfæri",
        "rcfilters-group-results-by-page": "Flokka niðurstöður eftir síðum",
        "rcfilters-activefilters": "Virkar síur",
+       "rcfilters-activefilters-hide": "Fela",
+       "rcfilters-activefilters-show": "Sýna",
        "rcfilters-advancedfilters": "Ítarlegar síur",
        "rcfilters-limit-title": "Breytingar sem á að birta",
        "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|breyting|breytingar}}, $2",
        "rcfilters-savedqueries-rename": "Endurnefna",
        "rcfilters-savedqueries-setdefault": "Setja sem sjálfgefið",
        "rcfilters-savedqueries-unsetdefault": "Fjarlægja sjálfgefið",
-       "rcfilters-savedqueries-remove": "Fjarlægja",
+       "rcfilters-savedqueries-remove": "Eyða",
        "rcfilters-savedqueries-new-name-label": "Heiti",
        "rcfilters-savedqueries-new-name-placeholder": "Lýstu markmiði síunnar",
        "rcfilters-savedqueries-apply-label": "Búa til síu",
        "deadendpages": "Botnlangar",
        "deadendpagestext": "Eftirfarandi síður tengjast ekki við aðrar síður á {{SITENAME}}.",
        "protectedpages": "Verndaðar síður",
+       "protectedpages-filters": "Síur:",
        "protectedpages-indef": "Aðeins óendanlegar verndanir",
        "protectedpages-summary": "Þessi skrá sýnir lista yfir síður sem eru til og eru verndaðar í auknablikinu. Til að sjá lista yfir titla sem eru verndaðir gegn stofnun þeirra, sjáðu [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]].",
        "protectedpages-cascade": "Keðjuverndun eingöngu",
        "apisandbox-dynamic-parameters-add-label": "Bæta við viðfangi:",
        "apisandbox-dynamic-parameters-add-placeholder": "Heiti viðfangs",
        "apisandbox-deprecated-parameters": "Úrelt viðföng",
+       "apisandbox-add-multi": "Bæta við",
        "apisandbox-submit-invalid-fields-title": "Sumir reitir eru ógildir",
        "apisandbox-results": "Niðurstöður",
+       "apisandbox-sending-request": "Sendir API-beiðni...",
        "apisandbox-request-url-label": "Slóð á beiðni:",
        "apisandbox-request-format-json-label": "JSON",
        "apisandbox-request-json-label": "JSON beiðni:",
        "revertpage": "Tók aftur breytingar [[Special:Contributions/$2|$2]] ([[User talk:$2|spjall]]), breytt til síðustu útgáfu [[User:$1|$1]]",
        "revertpage-nouser": "Tók aftur breytingar falins notanda til síðustu útgáfu {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Tók til baka breytingar eftir {{GENDER:$3|$1}};\nsetti yfir á síðustu útgáfu eftir {{GENDER:$4|$2}}.",
+       "rollback-success-notify": "Tók aftur breytingar $1;\nbreytti í síðustu útgafu $2. [$3 Sýna breytingar]",
        "sessionfailure-title": "Mistök í setu",
        "sessionfailure": "Líklega er vandamál með innskráningarsetuna þína;\nhætt hefur verið við þessa aðgerð sem vörn gegn mögulegu samskiptaráni setunar.\nReyndu að senda upplýsingarnar aftur inn.",
+       "changecontentmodel": "Breyta efnislíkani síðu",
+       "changecontentmodel-legend": "Breyta efnislíkani",
        "changecontentmodel-title-label": "Titill síðu",
+       "changecontentmodel-model-label": "Nýtt efnislíkan",
        "changecontentmodel-reason-label": "Ástæða:",
        "changecontentmodel-submit": "Breyta",
+       "changecontentmodel-success-title": "Efnislíkaninu var breytt",
        "logentry-contentmodel-change-revertlink": "taka aftur",
        "logentry-contentmodel-change-revert": "taka aftur",
        "protectlogpage": "Verndunarskrá",
        "unblocked-id": "Bann $1 hefur verið fjarlægt",
        "unblocked-ip": "[[Special:Contributions/$1|$1]] hefur verið afbannaður.",
        "blocklist": "Bannaðir notendur og vistföng",
+       "autoblocklist": "Sjálfvirk bönn",
        "autoblocklist-submit": "Leita",
+       "autoblocklist-legend": "Sýna sjálfvirk bönn",
+       "autoblocklist-total-autoblocks": "Samanlagður fjöldi sjálfvirkra banna: $1",
+       "autoblocklist-empty": "Listinn yfir sjálfvirk bönn er tómur.",
+       "autoblocklist-otherblocks": "{{PLURAL:$1|Annað sjálfvirkt bann|Önnur sjálfvirk bönn}}",
        "ipblocklist": "Bannaðir notendur og vistföng",
        "ipblocklist-legend": "Finna bannaðan notanda",
        "blocklist-userblocks": "Fela notendabönn",
        "tooltip-ca-nstab-help": "Sýna hjálparsíðuna",
        "tooltip-ca-nstab-category": "Sýna efnisflokkasíðuna",
        "tooltip-minoredit": "Merkja þessa breytingu sem minniháttar",
-       "tooltip-save": "Vista breytingarnar",
-       "tooltip-publish": "Gefa út breytingarnar þínar",
+       "tooltip-save": "Vista breytingar þínar",
+       "tooltip-publish": "Gefa út breytingar þínar",
        "tooltip-preview": "Forskoða breytingarnar þínar. Gerðu þetta áður en þú vistar.",
        "tooltip-diff": "Sýna hvaða breytingar þú gerðir á textanum.",
        "tooltip-compareselectedversions": "Sjá breytingarnar á þessari grein á milli útgáfanna sem þú valdir.",
        "version-specialpages": "Kerfissíður",
        "version-parserhooks": "Tengikrókar þáttara (parser hooks)",
        "version-variables": "Breytur",
+       "version-editors": "Ritstjórar",
        "version-antispam": "Varnir gegn amasendingum",
        "version-api": "API",
        "version-other": "Aðrar",
        "tag-mw-new-redirect": "Ný endurbeining",
        "tag-mw-removed-redirect": "Fjarlægði endurbeiningu",
        "tag-mw-replace": "Skipt út",
+       "tag-mw-rollback": "Afturköllun",
        "tag-mw-undo": "Afturkalla",
        "tags-title": "Merki",
        "tags-intro": "Þessi síða sýnir merkin sem hugbúnaðurinn gæti merkt breytingar með, og hvað þau þýða.",
index a40f475..082ed36 100644 (file)
        "revdelete-text-file": "削除されたファイルの版はファイルの履歴に表示されつづけますが、一般の利用者はその内容の一部を閲覧できなくなります。",
        "logdelete-text": "削除された記録項目は記録に表示され続けますが、一般の利用者はその内容の一部を閲覧できなくなります。",
        "revdelete-text-others": "追加の制限を設定しない限り、他の管理者は非表示コンテンツにまだアクセスすることも復元することもできます。",
-       "revdelete-confirm": "ã\81\93ã\81®æ\93\8dä½\9cã\82\92è¡\8cã\81\8aã\81\86ã\81¨ã\81\97ã\81¦ã\81\84ã\82\8bã\81\93ã\81¨ã\80\81ã\81\9dã\81®結果を理解していること、[[{{MediaWiki:Policy-url}}|方針]]に従っていること、を確認してください。",
+       "revdelete-confirm": "ã\81\93ã\81®æ\93\8dä½\9cã\81«ã\82\88ã\82\8aç\94\9fã\81\98ã\82\8b結果を理解していること、[[{{MediaWiki:Policy-url}}|方針]]に従っていること、を確認してください。",
        "revdelete-suppress-text": "秘匿は、<strong>以下の場合に限って</strong>使用すべきです:\n* 名誉毀損のおそれがある記述\n* 非公開個人情報\n*: <em>自宅の住所、電話番号、個人を識別できる公的な番号など</em>",
        "revdelete-legend": "閲覧レベル制限を設定",
        "revdelete-hide-text": "版の本文",
index 49b816b..e474285 100644 (file)
@@ -11,7 +11,8 @@
                        "Ebraminio",
                        "Process cq",
                        "Alirezaaa",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Matěj Suchánek"
                ]
        },
        "tog-underline": "خط کیشائن ژێر پیوندەل:",
        "copyrightwarning": "لطفاً توجه داشته‌باشید که همهٔ مشارکت‌ها در {{SITENAME}} منتشرشده تحت $2 در نظر گرفته‌می‌شوند (برای جزئیات بیش‌تر $1 را ببینید).\nاگر نمی‌خواهید نوشته‌هایتان بی‌رحمانه ویرایش و توزیع شوند؛ بنابراین، آنها را اینجا ارائه نکنید.<br />\nشما همچنین به ما تعهد می‌کنید که خودتان این را نوشته‌اید یا آن را از یک منبع با مالکیت عمومی یا مشابه آزاد آن برداشته‌اید (برای جزئیات بیش‌تر $1 را ببینید).\n<strong>کارهای دارای حق تکثیر را بدون اجازه ارائه نکنید!</strong>",
        "copyrightwarning2": "لطفاً توجه داشته‌باشید که همهٔ مشارکت‌ها در {{SITENAME}} ممکن است توسط دیگر مشارکت‌کنندگان تغییر یابند، ویرایش یا حذف شوند.\nاگر نمی‌خواهید نوشته‌هایتان بی‌رحمانه ویرایش شوند؛ بنابراین، آنها را اینجا ارائه نکنید.<br />\nشما همچنین به ما تعهد می‌کنید که خودتان این را نوشته‌اید یا آن را از یک منبع با مالکیت عمومی یا مشابه آزاد آن برداشته‌اید ($1 را برای جزئیات بیشتر ببینید).\n<strong>کارهای دارای حق تکثیر را بدون اجازه ارائه نکنید!</strong>",
        "editpage-cannot-use-custom-model": "مدل محتوای این صفحه نمی‌تواند عوض شود.",
-       "longpageerror": "'''خطا: متنی که ارسال کرده‌اید {{PULAR:$1|یک کیلوبایت|$1 کیلوبایت}} طول دارد. این مقدار از مقدار بیشینهٔ {{PLURAL:$2|یک کیلوبایت|$2 کیلوبایت}} بیشتر است.'''\nنمی‌توان آن را ذخیره کرد.",
+       "longpageerror": "'''خطا: متنی که ارسال کرده‌اید {{PLURAL:$1|یک کیلوبایت|$1 کیلوبایت}} طول دارد. این مقدار از مقدار بیشینهٔ {{PLURAL:$2|یک کیلوبایت|$2 کیلوبایت}} بیشتر است.'''\nنمی‌توان آن را ذخیره کرد.",
        "readonlywarning": "<strong>هشدار: پایگاه داده برای نگهداری قفل شده‌است، به همین علت هم‌اکنون نمی‌توانید ویرایش‌هایتان را ذخیره کنید.</strong>\nاگر می‌خواهید متن را در یک پروندهٔ متنی کپی کنید و برای آینده ذخیره‌اش کنید.\n\nمدیری که آن را قفل کرده این توضیح را ارائه کرده‌است: $1",
        "protectedpagewarning": "'''هشدار: این صفحه قفل شده‌است تا فقط کاربران با دسترسی مدیریت بتوانند ویرایشش کنند.'''\nآخرین موارد سیاهه در زیر آمده‌است:",
        "semiprotectedpagewarning": "'''توجه:''' این صفحه قفل شده‌است تا تنها کاربران ثبت‌نام‌کرده قادر به ویرایش آن باشند.\nآخرین موارد سیاهه در زیر آمده‌است:",
index 6337ea4..95adaf8 100644 (file)
        "cascadeprotected": "Ankehitriny dia voaaro ity pejy ity satria misy pejy voaaro {{PLURAL:$1|iray|$1}} mampiasa ity pejy ity. Io pejy io dia mampiasa ny fiarovana \"mirihana\":\n\n$2",
        "namespaceprotected": "Tsy manana alalàna manova ny toeran'anarana « '''$1''' » ianao.",
        "customcssprotected": "Tsy afaka manova ity pejy CSS ity ianao satria misy ny safidy manokan'ny mpikambana hafa.",
+       "customjsonprotected": "Tsy manana alalalana manova ity pejy JSON ity ianao satria misy ny safidin'olon-kafa izy ity.",
        "customjsprotected": "Tsy afaka manova ity pejy JavaScript ity inaao satria misy ny safidin'ny mpikambana hafa.",
        "mycustomcssprotected": "Tsy manana ny alalana ahafahana manova ity pejy CSS ity ianao.",
+       "mycustomjsonprotected": "Tsy manana alalana manova ity pejy JSON ity ianao.",
        "mycustomjsprotected": "Tsy manana ny alalana ahafahana manova ity pejy JavaScript ity ianao.",
        "myprivateinfoprotected": "Tsy manana alalana ahafahana manova ny fampahalalana tsy sarababem-bahoakanao ianao.",
        "mypreferencesprotected": "Tsy manana alalana ahafahana manova ny safidinao ianao.",
        "changepassword-success": "Voaova soa aman-tsara ny tenimiafinao!",
        "changepassword-throttled": "Betsaka loatra ny andram-pidirana nataonao.\nAndraso $1 aloha ny mamerina.",
        "botpasswords": "Tenimiafin-drôbô",
+       "botpasswords-summary": "Ny <em>Tenimiafina rôbô</em> dia manome alalana ny mampiasa ny kaontim-pikambana iray amin'ny alalana API ka tsy mila mampiasa ny tenimiafin'ny mpikambana. Ny zom-pikambana ananana rehefa tafiditra amin'ny alalana tenimiafina rôbô dia mety ho voafetra.\n\nRaha tsy fantatrao ny antony hanaovanao izany dia tsy tokony hanao izany ianao. Tsy tokony hisy ny olona hangataka anao hamoaka iray amin'itony ary hanome azy.",
        "botpasswords-disabled": "Tsy ampiasaina ny tenimiafin-drôbô.",
+       "botpasswords-no-central-id": "Raha hampiasa tenimiafina rôbô dia tsy maintsy tafiditra anaty kaonty iombonan-tsehatra ianao.",
        "botpasswords-existing": "Tenimiafin-drôbô efa misy",
        "botpasswords-createnew": "Hamorona tenimiafina rôbô vaovao",
        "botpasswords-editexisting": "Hanova tenimiafina rôbô efa misy",
+       "botpasswords-label-needsreset": "(mila famerenana ny tenimiafina)",
        "botpasswords-label-appid": "Anarana rôbô:",
        "botpasswords-label-create": "Foronina",
        "botpasswords-label-update": "Vaozina",
        "savechanges": "Hitahiry ny fiovana",
        "publishpage": "Hamoaka pejy",
        "publishchanges": "Hamoaka ny fiovana",
+       "savearticle-start": "Hitahiry pejy...",
+       "savechanges-start": "Mitahiry ny fiovana...",
+       "publishpage-start": "Hamoaka ny pejy...",
+       "publishchanges-start": "Hamoaka ny fiovana...",
        "preview": "Topi-maso",
        "showpreview": "Asehoy aloha",
        "showdiff": "Asehoy ny fiovana",
        "anoneditwarning": "<strong>Fampitandremana :</strong> Tsy niditra tamin'ny kaontinao ianao. Ho hitan'ny vahoaka ny adiresy IP-nao raha manova inona na inona ianao. Raha <strong>[$1 miditra amin'ny kaontinao]</strong> ianao dia ho anisan'ny tombontsoa anananao ny fanaovana ny fiovana amin'ny solonanaranao.",
        "anonpreviewwarning": "''Tsy niditra ianao. Hampitahiry ny adiresy IP anao ao amin'ny tantaram-panovan'ity pejy ity ny fitehirizana ny fanovana.''",
        "missingsummary": "'''Hafatra fampantsiahivana''' : tsy mbola nanome ny ambangovangom-panovanao ianao.\nRaha mbola tsindriano fanindroany eo amin'ny bokotra $1, ho voatahiry tsy fanambarana ny fanovanao.",
-       "missingcommenttext": "Ampidiro ny ambangovangony azafady.",
+       "missingcommenttext": "Mampidira ambangovangony azafady.",
        "missingcommentheader": "<strong>Fampatsiahivana: </strong> Tsy nampiditra lohahevitra ho an'ity hafatra ity ianao. Raha tsindrianao fanindroany \"$1\" dia ho tehirizina tsy misy lohahevitra ny hafatrao.",
        "summary-preview": "Topi-mason'ny ambangovangom-panovana :",
        "subject-preview": "Topi-mason-dohahevitra:",
index 9f50960..81cfbe9 100644 (file)
        "badtitletext": "The requested page title was invalid, empty, or an incorrectly linked inter-language or inter-wiki title.\nIt may contain one or more characters that cannot be used in titles.",
        "title-invalid-empty": "The requested page title is empty or contains only the name of a namespace.",
        "title-invalid-utf8": "The requested page title contains an invalid UTF-8 sequence.",
+       "title-invalid-interwiki": "ꯍꯪꯒꯠꯆꯔꯤꯕꯥ ꯂꯥꯃꯥꯏꯁꯤꯒꯤ ꯃꯃꯤꯡ ꯁꯤ ꯋꯤꯀꯤ ꯒꯥ ꯃꯔꯤ ꯂꯩꯅꯕꯥ ꯁꯝꯅꯐꯝ ꯌꯥꯑꯣ ꯏ ꯃꯗꯨꯗꯤ ꯃꯃꯤꯡ ꯑꯣꯏꯅꯥ ꯁꯤꯖꯤꯟꯅꯕꯥ ꯌꯥꯗꯕꯥ",
        "viewsource": "ꯍꯧꯔꯛꯐꯝ ꯎꯨꯇꯂꯨ",
        "viewsource-title": "$1 ꯒꯤ ꯍꯧꯔꯛꯐꯝ ꯎꯨꯠꯂꯨ",
+       "viewsourcetext": "ꯅꯪꯅꯥ ꯌꯦꯡꯕꯥ ꯌꯥꯒꯅꯤ ꯑꯃꯗꯤ ꯃꯁꯤꯒꯤ ꯂꯥꯃꯥꯏꯁꯤꯒꯤ ꯍꯧꯔꯛꯐꯝ ꯁꯤꯟꯇꯣꯛ ꯎ",
+       "mycustomjsonprotected": "ꯅꯪꯅꯥ ꯃꯁꯤ json ꯂꯥꯃꯥꯏꯁꯤ ꯁꯦꯝꯒꯠꯅꯕꯥ ꯑꯌꯥꯕꯥ ꯄꯤꯗꯦ",
+       "mycustomjsprotected": "JavaScript ꯂꯥꯃꯥꯏꯁꯤ ꯅꯪꯅꯥ ꯁꯦꯝꯒꯠꯅꯕꯒꯤ ꯑꯌꯥꯕꯥ ꯄꯤꯗꯦ",
+       "myprivateinfoprotected": "ꯅꯪꯅꯥ ꯅꯪꯒꯤ ꯑꯔꯣꯟꯕꯥ ꯑꯀꯨꯞꯄ ꯋꯥꯔꯣꯜ ꯂꯧꯐꯝ ꯁꯤ ꯁꯦꯝꯒꯠꯅꯕꯒꯤ ꯑꯌꯥꯕꯥ ꯄꯤꯗꯦ",
+       "mypreferencesprotected": "ꯅꯪꯅꯥ ꯅꯪꯒꯤ ꯄꯔꯤꯐꯔꯦꯟꯁ ꯁꯤ ꯁꯦꯝꯒꯠꯅꯕꯒꯤ ꯑꯌꯥꯕꯥ ꯄꯤꯗꯦ",
+       "ns-specialprotected": "ꯑꯈꯟꯅꯕꯥ ꯂꯥꯃꯥꯏꯅꯤ ꯁꯦꯝꯗꯣꯛꯄꯥ ꯌꯥꯗꯦ",
+       "titleprotected": "ꯃꯁꯤꯒꯤ ꯃꯃꯤꯡ ꯁꯤ ꯉꯥꯛꯊꯣꯛꯂꯦ ꯁꯥꯒꯠꯂꯛꯄꯗꯥ [[User:$1|$1]].\nThe reason given is <em>$2</em>.",
+       "filereadonlyerror": "Unable to modify the file \"$1\" because the file repository \"$2\" is in read-only mode.\n\nThe system administrator who locked it offered this explanation: \"$3\".",
+       "invalidtitle-knownnamespace": "Invalid title with namespace \"$2\" and text \"$3\"",
+       "invalidtitle-unknownnamespace": "Invalid title with unknown namespace number $1 and text \"$2\"",
+       "exception-nologin": "ꯂꯦꯒ ꯏꯟ ꯇꯧꯗꯦ",
+       "exception-nologin-text": "ꯆꯥꯟꯕꯤꯗꯨꯅꯥ ꯂꯣꯒ ꯏꯟ ꯇꯧꯁꯤꯟꯂꯨ ꯃꯁꯤꯒꯤ ꯂꯥꯃꯥꯏꯁꯤꯗꯥ ꯆꯪꯁꯤꯟꯅꯕꯥ ꯅꯠꯇꯔꯒꯥ ꯃꯊꯧ ꯄꯥꯡꯊꯣꯛꯅꯕꯥ",
+       "exception-nologin-text-manual": "Please $1 to be able to access this page or action.",
+       "virus-badscanner": "ꯑꯐꯠꯇꯕꯥ ꯀꯟꯐꯤꯒꯨꯔꯦꯁꯟ: Unknown virus scanner: <em>$1</em>",
+       "virus-scanfailed": "ꯁꯦꯡꯗꯣꯛꯄꯥ ꯃꯥꯏꯄꯥꯛꯇꯔꯦ (code $1)",
+       "virus-unknownscanner": "ꯁꯛꯈꯪꯗꯕꯥ ꯑꯦꯟꯇꯤ ꯕꯥꯏꯔꯨꯁ",
+       "logouttext": "<strong>You are now logged out.</strong>\n\nNote that some pages may continue to be displayed as if you were still logged in, until you clear your browser cache.",
+       "cannotlogoutnow-title": "ꯍꯧꯖꯤꯛ ꯂꯣꯒ out ꯇꯧꯕꯥ ꯌꯥꯗꯦ",
+       "cannotlogoutnow-text": "$1 ꯁꯤꯖꯤꯟꯅꯔꯤꯉꯩꯗꯥ ꯂꯣꯒꯒꯤꯡ out ꯁꯤ ꯑꯣꯏꯊꯣꯛꯇꯦ",
+       "welcomeuser": "$1ꯂꯦꯡꯁꯤꯟꯕꯤꯔꯛꯁꯤ",
+       "welcomecreation-msg": "ꯅꯪꯒꯤ ꯑꯦꯀꯥꯎꯟꯇ ꯁꯤ ꯁꯥꯈꯔꯦ\nꯅꯪꯒꯤ ꯑꯄꯥꯝꯕꯒꯤ ꯃꯇꯨꯡ ꯏꯟꯅꯥ ꯍꯣꯡꯗꯣꯛꯄꯥ ꯌꯥꯔꯦ ꯅꯪꯅꯥ {{SITENAME}} [[Special:Preferences|preferences]] ꯅꯪꯒꯤ ꯑꯄꯥꯝꯕꯒꯤ ꯃꯇꯨꯡꯏꯟꯅꯥ.",
+       "yourname": "ꯁꯤꯖꯤꯟꯅꯔꯤꯕꯥ ꯃꯃꯤꯡ",
        "userlogin-yourname": "Username",
        "userlogin-yourname-ph": "Enter your username",
+       "createacct-another-username-ph": "ꯁꯤꯖꯤꯟꯅꯔꯤꯕꯥ ꯃꯃꯤꯡ ꯗꯨ ꯏꯁꯤꯟꯂꯣ",
+       "yourpassword": "ꯆꯪꯁꯤꯟꯅꯕꯥ ꯋꯥꯍꯩ",
        "userlogin-yourpassword": "ꯆꯪꯁꯤꯟꯅꯕꯥ ꯋꯥꯍꯩ",
        "userlogin-yourpassword-ph": "ꯄꯥꯁꯋ꯭ꯇ ꯏꯔꯛ ꯎ",
        "createacct-yourpassword-ph": "ꯄꯥꯁꯋ꯭ꯇ ꯏꯔꯛ ꯎ",
+       "yourpasswordagain": "ꯑꯃꯨꯛꯍꯟꯅꯥ ꯄꯥꯁꯋꯔꯇ ꯅꯝꯃꯨ",
        "createacct-yourpasswordagain": "Confirm password",
        "createacct-yourpasswordagain-ph": "ꯑꯃꯨꯛ ꯍꯟꯅꯥ ꯄꯥꯁꯋ꯭ꯇ ꯏꯌꯨ",
        "userlogin-remembermypassword": "ꯑꯩꯕꯨ ꯑꯗꯨꯝ ꯂꯣꯒ ꯏꯟ ꯇꯧꯍꯟꯂꯨ",
+       "userlogin-signwithsecure": "ꯁꯣꯏꯔꯣꯏꯗꯕꯥ ꯁꯝꯅꯕꯥ  ꯁꯤꯖꯤꯟ ꯅꯧ",
+       "cannotlogin-title": "ꯂꯣꯒ ꯏꯟ ꯌꯥꯗꯦ",
+       "cannotlogin-text": "ꯂꯣꯒ ꯏꯟ ꯁꯤ ꯑꯣꯏꯊꯣꯛꯇꯦ",
+       "cannotloginnow-title": "ꯍꯧꯖꯤꯛ ꯂꯣꯒ ꯏꯟ ꯇꯧꯕꯥ ꯌꯥꯗꯦ",
+       "cannotloginnow-text": "$1 ꯁꯤꯖꯤꯟꯅꯔꯤꯉꯩꯗꯥ ꯂꯣꯒꯒꯤꯡ ꯏꯟ ꯁꯤ ꯑꯣꯏꯊꯣꯛꯇꯦ",
+       "cannotcreateaccount-title": "ꯑꯦꯀꯥꯎꯟ ꯁꯥꯕꯥ ꯌꯥꯗꯦ",
+       "cannotcreateaccount-text": "ꯍꯦꯛꯇꯥ ꯑꯣꯀꯥꯎꯅ ꯁꯥꯕꯥ ꯁꯤ ꯃꯁꯤꯒꯤ ꯋꯤꯀꯤ ꯁꯤ ꯗ ꯌꯥꯍꯟꯗꯦ",
+       "yourdomainname": "ꯅꯪꯒꯤ ꯗꯣꯃꯦꯟ",
+       "password-change-forbidden": "ꯅꯪꯅꯥ ꯃꯁꯤꯒꯤ ꯋꯤꯀꯤ ꯁꯤꯗꯥ ꯄꯥꯁꯋꯔꯇ ꯍꯣꯡꯕꯥ ꯌꯥꯔꯣꯏ",
+       "externaldberror": "There was either an authentication database error or you are not allowed to update your external account.",
        "login": "Chang Sinba",
+       "login-security": "ꯅꯪꯒꯤ ꯁꯛꯑꯣꯡ ꯈꯟꯗꯣꯛꯄꯥ",
+       "nav-login-createaccount": "ꯂꯣꯒ ꯏꯟ/ꯑꯦꯀꯥꯎꯟ ꯁꯥꯕꯥ",
        "logout": "Log out",
        "userlogout": "ꯂꯣꯒ ꯑꯧꯇ",
+       "notloggedin": "ꯂꯦꯒ ꯏꯟ ꯇꯧꯗꯦ",
        "userlogin-noaccount": "ꯑꯦꯀꯥꯎꯟ ꯂꯩꯇꯕꯔꯥ",
        "userlogin-joinproject": "ꯌꯥꯎꯕꯥ {{ꯃꯃꯤꯡ ꯏꯐꯝ}}",
        "createaccount": "ꯑꯩꯒꯤ ꯑꯣꯏꯕꯥ ꯑꯃꯥ ꯁꯦꯝꯕꯥ",
        "userlogin-resetpassword-link": "ꯄꯥꯁꯋꯔꯇ ꯀꯥꯎꯈꯔꯕꯥ",
        "userlogin-helplink2": "ꯂꯣꯒꯒꯤꯡ ꯇꯧꯗꯨꯅꯥ ꯃꯇꯦꯡ ꯄꯥꯡ ꯎ",
+       "userlogin-loggedin": "You are already logged in as {{GENDER:$1|$1}}.\nUse the form below to log in as another user.",
+       "userlogin-reauth": "You must log in again to verify that you are {{GENDER:$1|$1}}.",
+       "userlogin-createanother": "ꯅꯪꯒꯤ ꯑꯇꯣꯞꯄꯥ ꯑꯦꯀꯥꯎꯟꯇ ꯁꯦꯝꯕꯥ",
+       "createacct-emailrequired": "ꯏꯃꯦꯜ ꯂꯥꯐꯝ",
        "createacct-emailoptional": "Email address (ꯑꯇꯣꯞꯄꯥ ꯱)",
        "createacct-email-ph": "Enter your email address",
+       "createacct-another-email-ph": "Enter your email address",
+       "createaccountmail": "Use a temporary random password and send it to the specified email address",
+       "createaccountmail-help": "ꯄꯥꯁꯋꯔꯇ ꯈꯪꯗꯅꯥ ꯑꯇꯣꯞꯄꯒꯤ ꯑꯦꯀꯥꯎꯟ ꯁꯥꯕꯗꯥ ꯁꯤꯖꯤꯟꯅꯕꯥ ꯌꯥꯏ",
+       "createacct-realname": "ꯑꯆꯨꯝꯕꯥ ꯃꯃꯤꯡ(ꯑꯇꯣꯞꯄꯥ ꯱)",
        "createacct-reason": "ꯃꯔꯝ",
+       "createacct-reason-ph": "ꯅꯪꯅꯥ ꯀꯔꯤꯒꯤ ꯑꯇꯣꯞꯄꯥ ꯑꯦꯀꯥꯎꯟ ꯁꯥꯔꯤꯅꯣ",
+       "createacct-reason-help": "ꯄꯥꯎꯖꯦꯜꯁꯤ ꯑꯦꯀꯥꯎꯟ ꯁꯦꯝꯕꯥ ꯂꯣꯒ ꯇꯥ ꯎꯨꯠꯂꯦ",
        "createacct-submit": "ꯅꯪꯒꯤ ꯑꯦꯀꯥꯎꯟꯇ ꯁꯦꯝꯕꯥ",
+       "createacct-another-submit": "ꯑꯩꯒꯤ ꯑꯣꯏꯕꯥ ꯑꯃꯥ ꯁꯦꯝꯕꯥ",
+       "createacct-continue-submit": "ꯃꯈꯥ ꯆꯠꯊꯧ ꯑꯦꯀꯥꯎꯟ ꯁꯦꯝꯕꯗꯨ",
+       "createacct-another-continue-submit": "ꯃꯈꯥ ꯆꯠꯊꯧ ꯑꯦꯀꯥꯎꯟ ꯁꯦꯝꯕꯗꯨ",
        "createacct-benefit-heading": "{{SITENAME}} ꯁꯤ ꯅꯪꯒꯥ ꯃꯥꯟꯅꯕꯥ ꯃꯤꯑꯣꯏꯁꯤꯡꯅꯥ ꯁꯦꯝꯕꯅꯤ",
        "createacct-benefit-body1": "{{PLURAL:$1|edit|edits}}",
        "createacct-benefit-body2": "{{PLURAL:$1|page|pages}}",
        "createacct-benefit-body3": "ꯍꯧꯖꯤꯛꯀꯤ {{PLURAL:$1|contributor|contributors}}",
+       "badretype": "ꯅꯪꯅꯥ ꯏꯔꯤꯕꯥ ꯄꯥꯡꯋꯔꯇ ꯁꯤ ꯆꯥꯟꯅꯗꯔꯦ",
+       "usernameinprogress": "ꯉꯍꯥꯛꯇꯪ ꯉꯥꯏꯕꯤꯈꯣ ꯃꯁꯤꯒꯤ ꯁꯤꯖꯤꯟꯅꯔꯤꯕꯥ ꯃꯃꯤꯡ ꯁꯤ ꯒꯤ ꯑꯦꯀꯥꯎꯟ ꯁꯦꯝꯕꯗꯥ ꯈꯨꯃꯥꯡ ꯆꯥꯎꯁꯤꯟꯂꯦ",
+       "createacct-error": "ꯑꯀꯥꯎꯟ ꯁꯦꯝꯕꯗꯥ ꯑꯁꯣꯏꯕꯥ ꯂꯩꯔꯦ",
+       "createaccounterror": "$1 ꯑꯦꯀꯥꯎꯟ ꯁꯥꯕꯥ ꯌꯥꯗꯔꯦ",
+       "nocookiesnew": "The user account was created, but you are not logged in.\n{{SITENAME}} uses cookies to log in users.\nYou have cookies disabled.\nPlease enable them, then log in with your new username and password.",
+       "nocookieslogin": "{{SITENAME}} uses cookies to log in users.\nYou have cookies disabled.\nPlease enable them and try again.",
+       "loginsuccesstitle": "ꯂꯣꯒ ꯏꯟ",
+       "login-userblocked": "ꯃꯁꯤꯒꯤ ꯁꯤꯖꯤꯟꯅꯔꯤꯕꯥꯁꯤ ꯊꯤꯡꯖꯤꯟꯈꯔꯦ? ꯂꯣꯒ ꯏꯟ ꯌꯥꯔꯥꯔꯣꯏ",
+       "passwordtooshort": "ꯄꯥꯁꯋꯔꯇ ꯁꯤ ꯌꯥꯝꯗꯔꯕꯗꯥ ꯃꯁꯤ ꯈꯔꯥꯁꯤ ꯌꯥꯎꯒꯗꯕꯅꯤ {{PLURAL:$1|1 character|$1 characters}}.",
+       "mailmypassword": "ꯄꯥꯁꯋ꯭ꯇ ꯁꯦꯝꯗꯣꯛꯄꯥ",
+       "passwordremindertitle": " {{SITENAME}} ꯁꯤꯒꯤ ꯑꯅꯧꯕꯥ ꯉꯥꯏꯍꯥꯛꯀꯤ ꯑꯣꯏꯕꯥ ꯄꯥꯁꯋꯔꯇ",
+       "passwordremindertext": "Someone (from IP address $1) requested a new\npassword for {{SITENAME}} ($4). A temporary password for user\n\"$2\" has been created and was set to \"$3\". If this was your\nintent, you will need to log in and choose a new password now.\nYour temporary password will expire in {{PLURAL:$5|one day|$5 days}}.\n\nIf someone else made this request, or if you have remembered your password,\nand you no longer wish to change it, you may ignore this message and\ncontinue using your old password.",
+       "emailconfirmlink": "ꯅꯪꯒꯤ ꯏꯃꯦꯜ ꯑꯦꯗꯔꯦꯁ ꯌꯥꯕꯔꯥ ꯌꯦꯡ ꯎ",
+       "accountcreated": "ꯑꯦꯀꯥꯎꯟ ꯁꯥꯈꯔꯦ",
        "loginlanguagelabel": "$1 ꯂꯣꯟ",
        "pt-login": "Chang Sinba",
        "pt-login-button": "Chang Sinba",
+       "pt-login-continue-button": "ꯂꯣꯘ ꯏꯟ ꯃꯈꯥ ꯆꯠꯊꯧ",
        "pt-createaccount": "ꯑꯩꯒꯤ ꯑꯣꯏꯕꯥ ꯑꯃꯥ ꯁꯦꯝꯕꯥ",
        "pt-userlogout": "Log out",
+       "changepassword": "ꯄꯥꯁꯋ꯭ꯔꯇ ꯍꯣꯡꯗꯣꯛꯄꯥ",
+       "oldpassword": "ꯑꯔꯤꯕꯥ ꯄꯥꯁꯋꯔꯇ",
+       "newpassword": "ꯑꯅꯧꯕꯥ ꯄꯥꯁꯋꯔꯇ",
+       "retypenew": "ꯑꯃꯨꯛꯍꯟꯅꯥ ꯑꯅꯧꯕꯥ  ꯄꯥꯁꯋꯔꯇ ꯅꯝꯃꯨ",
+       "resetpass_submit": "ꯄꯥꯁꯋꯔꯇ ꯁꯦꯝꯃꯨ ꯱ꯗꯤ ꯂꯣꯒ ꯏꯟ",
+       "changepassword-success": "ꯅꯪꯒꯤ ꯄꯥꯁꯋꯔꯇ ꯁꯤ ꯍꯣꯡꯗꯣꯛꯈꯔꯦ",
+       "changepassword-throttled": "You have made too many recent login attempts.\nPlease wait $1 before trying again.",
+       "botpasswords": "ꯕꯣꯇ ꯄꯥꯁꯋꯔꯇ",
+       "botpasswords-summary": "<em>Bot passwords</em> allow access to a user account via the API without using the account's main login credentials. The user rights available when logged in with a bot password may be restricted.\n\nIf you don't know why you might want to do this, you should probably not do it. No one should ever ask you to generate one of these and give it to them.",
+       "botpasswords-disabled": "ꯕꯣꯇ ꯄꯥꯁꯋꯔꯇ ꯌꯥꯉꯟꯗꯕꯥ",
+       "botpasswords-label-create": "ꯁꯥꯕꯥ",
+       "botpasswords-label-update": "ꯅꯧꯊꯣꯛꯍꯟꯕꯥ",
+       "botpasswords-label-cancel": "ꯀꯛꯊꯠꯄꯥ",
+       "botpasswords-label-delete": "ꯃꯥꯡꯍꯟꯕꯥ",
+       "botpasswords-label-resetpassword": "ꯄꯥꯁꯋ꯭ꯇ ꯑꯃꯨꯛ ꯍꯟꯅꯥ ꯁꯦꯝꯗꯣꯛꯄꯥ",
+       "botpasswords-label-grants": "ꯆꯥꯟꯅꯕꯥ ꯌꯥꯕꯥ ꯑꯌꯥꯕ",
+       "botpasswords-help-grants": "Grants allow access to rights already held by your user account. Enabling a grant here does not provide access to any rights that your user account would not otherwise have. See the [[Special:ListGrants|table of grants]] for more information.",
+       "botpasswords-created-title": "ꯕꯣꯠ ꯄꯥꯁꯋ꯭ꯔꯇ ꯁꯥꯈꯔꯦ",
+       "botpasswords-deleted-title": "ꯕꯣꯠ ꯄꯥꯁꯋ꯭ꯔꯇ ꯀꯛꯊꯠꯈꯔꯦ",
+       "resetpass_forbidden": "ꯄꯥꯁꯋ꯭ꯔꯇ ꯍꯣꯡꯗꯣꯧꯄꯥ ꯌꯥꯔꯣꯏ",
+       "resetpass_forbidden-reason": "$1 ꯄꯥꯁꯋ꯭ꯔꯇ ꯍꯣꯡꯗꯣꯧꯄꯥ ꯌꯥꯔꯣꯏ",
+       "resetpass-no-info": "ꯃꯁꯤꯒꯤ ꯂꯥꯃꯥꯏꯁꯤ ꯍꯦꯧꯇꯥ ꯌꯧꯅꯕꯥ ꯅꯪ ꯁꯣꯏꯗꯅꯥ ꯂꯣꯒ ꯏꯟ ꯇꯧꯒꯗꯕꯅꯤ",
+       "resetpass-submit-loggedin": "ꯄꯥꯁꯋ꯭ꯔꯇ ꯍꯣꯡꯗꯣꯛꯄꯥ",
+       "resetpass-submit-cancel": "ꯀꯛꯊꯠꯄꯥ",
+       "resetpass-wrong-oldpass": "ꯃꯇꯝ ꯈꯔꯥꯒꯤ ꯑꯣꯏꯅꯥ ꯀꯔꯤꯝꯇꯥ ꯌꯥꯎꯗꯦ  ꯅꯠꯇꯔꯒꯥ ꯍꯧꯖꯤꯧꯀꯤ ꯄꯥꯁꯋ꯭ꯔꯇ꯫\nꯅꯪꯅꯥ ꯄꯥꯁꯋꯑꯔꯇ ꯍꯥꯟꯅꯗꯒꯤ ꯍꯣꯡꯂꯝꯂꯅꯤ ꯅꯠꯇꯔꯒꯥ ꯍꯪꯒꯠꯂꯨ ꯉꯥꯏꯍꯥꯛꯀꯤ ꯑꯣꯏꯕꯥ ꯄꯥꯁꯋ꯭ꯔꯇ",
+       "resetpass-temp-password": "ꯉꯩꯍꯥꯛꯀꯤ ꯑꯣꯏꯕꯥ ꯄꯥꯁꯋ꯭ꯔꯇ",
        "passwordreset": "ꯄꯥꯁꯋ꯭ꯇ ꯁꯦꯝꯗꯣꯛꯄꯥ",
+       "passwordreset-username": "ꯁꯤꯖꯤꯟꯅꯔꯤꯕꯥ ꯃꯃꯤꯡ",
+       "passwordreset-domain": "ꯗꯣꯃꯦꯟ",
+       "passwordreset-email": "ꯏꯃꯦꯜ ꯂꯥꯐꯝ",
+       "passwordreset-emailtitle": "{{SITENAME}} ꯑꯀꯨꯞꯄꯥ ꯃꯔꯣꯜ",
+       "passwordreset-emailtext-ip": "Someone (probably you, from IP address $1) requested a reset of your\npassword for {{SITENAME}} ($4). The following user {{PLURAL:$3|account is|accounts are}}\nassociated with this email address:\n\n$2\n\n{{PLURAL:$3|This temporary password|These temporary passwords}} will expire in {{PLURAL:$5|one day|$5 days}}.\nYou should log in and choose a new password now. If someone else made this\nrequest, or if you have remembered your original password, and you no longer\nwish to change it, you may ignore this message and continue using your old\npassword.",
+       "passwordreset-emailtext-user": "User $1 on {{SITENAME}} requested a reset of your password for {{SITENAME}}\n($4). The following user {{PLURAL:$3|account is|accounts are}} associated with this email address:\n\n$2\n\n{{PLURAL:$3|This temporary password|These temporary passwords}} will expire in {{PLURAL:$5|one day|$5 days}}.\nYou should log in and choose a new password now. If someone else made this\nrequest, or if you have remembered your original password, and you no longer\nwish to change it, you may ignore this message and continue using your old\npassword.",
+       "passwordreset-emailelement": "$1 ꯁꯤꯖꯤꯟꯅꯔꯤꯕꯥ\n$2 ꯉꯩꯍꯥꯛꯀꯤ ꯑꯣꯏꯕꯥ ꯄꯥꯁꯋꯔꯇ",
+       "changeemail-none": "ꯑꯃꯥꯇꯥ ꯅꯠꯇꯦ",
+       "changeemail-password": "ꯅꯪꯒꯤ {{SITENAME}} ꯄꯥꯁꯋ꯭ꯔꯇ:",
+       "changeemail-submit": "ꯏ-ꯃꯦꯜ ꯍꯣꯡꯕꯥ",
        "bold_sample": "ꯆꯥꯎꯅꯥ ꯏꯕꯥ",
        "bold_tip": "ꯆꯥꯎꯅꯥ ꯏꯕꯥ",
        "italic_sample": "ꯋꯥꯔꯦꯡ ꯐꯆꯅꯥ ꯏꯕꯥ",
        "sig_tip": "ꯃꯇꯝꯒꯤ ꯏꯁꯇꯥꯝꯒꯥ ꯂꯣꯏꯟꯅꯥ ꯅꯪꯒꯤ ꯈꯨꯇꯌꯦꯛ",
        "hr_tip": "ꯐꯩꯅꯥ ꯆꯤꯡꯕꯥ ꯂꯥ ꯏ (ꯃꯔꯤꯛ ꯃꯔꯤꯛ ꯑꯣꯏꯅꯥ ꯁꯤꯖꯤꯟꯅꯧ)",
        "summary": "ꯑꯇꯦꯟꯕꯥ ꯁꯟꯗꯣꯛꯅꯥ ꯇꯥꯛꯄꯥ",
+       "subject": "ꯍꯤꯔꯝ",
        "minoredit": "ꯃꯁꯤ ꯑꯄꯤꯛꯄꯥ ꯁꯦꯝꯒꯠꯄꯅꯤ",
        "watchthis": "ꯃꯁꯤꯒꯤ ꯂꯥꯃꯥꯏꯁꯤ ꯌꯦꯡꯉꯨ",
        "savearticle": "ꯂꯥꯃꯥꯏ ꯇꯨꯪꯁꯤꯟꯕꯥ",
+       "savechanges": "ꯑꯍꯣꯡꯕꯗꯨ ꯇꯨꯪꯁꯤꯟꯂꯨ",
+       "publishpage": "ꯂꯥꯃꯥꯏ ꯐꯣꯡꯗꯣꯛ ꯎ",
+       "publishchanges": "ꯑꯍꯣꯡꯕꯁꯤꯡꯗꯨ ꯐꯣꯡꯗꯣꯛ ꯎ",
+       "savearticle-start": "ꯂꯥꯃꯥꯏ ꯁꯤ ꯇꯨꯡꯁꯤꯜꯂꯨ",
+       "savechanges-start": "ꯑꯍꯣꯡꯕꯁꯤꯡꯗꯨ ꯇꯨꯪꯁꯤꯟꯂꯨ",
+       "publishpage-start": "ꯂꯥꯃꯥꯏ ꯐꯣꯡꯗꯣꯛ ꯎ",
+       "publishchanges-start": "ꯑꯍꯣꯡꯕꯁꯤꯡꯗꯨ ꯐꯣꯡꯗꯣꯛ ꯎ",
+       "preview": "ꯃꯥꯃꯥꯡꯒꯤ",
        "showpreview": "ꯍꯥꯟꯅꯒꯤꯗꯨ ꯎꯨꯇꯂꯨ",
        "showdiff": "ꯑꯍꯣꯡꯕꯗꯨ ꯎꯨꯇꯂꯨ",
+       "blankarticle": "<strong>Warning:</strong> The page you are creating is blank.\nIf you click \"$1\" again, the page will be created without any content.",
        "anoneditwarning": "<strong>Warning:</strong> You are not logged in. Your IP address will be publicly visible if you make any edits. If you <strong>[$1 log in]</strong> or <strong>[$2 create an account]</strong>, your edits will be attributed to your username, along with other benefits.",
        "loginreqlink": "Chang Sinba",
+       "accmailtitle": "ꯄꯥꯁꯋ꯭ꯔꯇ ꯊꯥꯕ",
+       "newarticle": "ꯑꯅꯧꯕꯥ",
        "newarticletext": "You have followed a link to a page that does not exist yet.\nTo create the page, start typing in the box below (see the [$1 help page] for more info).\nIf you are here by mistake, click your browser's <strong>back</strong> button.",
+       "anontalkpagetext": "----\n<em>This is the discussion page for an anonymous user who has not created an account yet, or who does not use it.</em>\nWe therefore have to use the numerical IP address to identify him/her.\nSuch an IP address can be shared by several users.\nIf you are an anonymous user and feel that irrelevant comments have been directed at you, please [[Special:CreateAccount|create an account]] or [[Special:UserLogin|log in]] to avoid future confusion with other anonymous users.",
        "noarticletext": "There is currently no text in this page.\nYou can [[Special:Search/{{PAGENAME}}|search for this page title]] in other pages,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs],\nor [{{fullurl:{{FULLPAGENAME}}|action=edit}} create this page]</span>.",
        "noarticletext-nopermission": "There is currently no text in this page.\nYou can [[Special:Search/{{PAGENAME}}|search for this page title]] in other pages, or <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs]</span>, but you do not have permission to create this page.",
+       "missing-revision": "The revision #$1 of the page named \"{{FULLPAGENAME}}\" does not exist.\n\nThis is usually caused by following an outdated history link to a page that has been deleted.\nDetails can be found in the [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} deletion log].",
+       "continue-editing": "ꯁꯦꯝꯒꯠꯄꯒꯤ ꯃꯐꯝꯗꯨꯗꯥ ꯆꯠꯂꯨ",
        "editing": "$1 ꯁꯦꯝꯒꯠꯂꯤ",
        "creating": "Creating $1",
        "editingsection": "Editing $1 (section)",
+       "yourtext": "ꯅꯪꯒꯤ ꯇꯦꯀꯁ",
+       "yourdiff": "ꯈꯦꯠꯅꯕꯥ ꯁꯤꯡ",
+       "copyrightwarning": "Please note that all contributions to {{SITENAME}} are considered to be released under the $2 (see $1 for details).\nIf you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.<br />\nYou are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource.\n<strong>Do not submit copyrighted work without permission!</strong>",
        "templatesused": "{{PLURAL:$1|Template|Templates}} used on this page:",
        "template-protected": "ꯉꯥꯛꯊꯣꯛꯂꯕꯥ",
        "template-semiprotected": "ꯇꯪꯈꯥꯏ ꯉꯥꯛꯊꯣꯛꯂꯕꯥ",
        "hiddencategories": "This page is a member of {{PLURAL:$1|1 hidden category|$1 hidden categories}}:",
+       "permissionserrors": "ꯑꯌꯥꯕꯥꯗꯨ ꯁꯣꯏꯔꯦ",
        "permissionserrorstext-withaction": "You do not have permission to $2, for the following {{PLURAL:$1|reason|reasons}}:",
        "moveddeleted-notice": "ꯃꯁꯤꯒꯤ ꯂꯥꯃꯥꯏꯁꯤ ꯀꯛꯊꯠꯈꯔꯦ. \nꯀꯛꯊꯠꯄꯥ, ꯉꯥꯛꯊꯣꯛꯄꯥ ꯑꯃꯗꯤ ꯂꯣꯒ ꯂꯦꯡꯍꯟꯕꯥ ꯂꯥꯃꯥꯏꯒꯤꯗꯃꯛ ꯇꯨ ꯃꯈꯥꯒꯤ ꯁꯤꯗꯥ ꯔꯤꯐꯔꯦꯟꯁ ꯎꯨꯠꯂꯦ",
+       "edit-conflict": "ꯁ‍ꯦꯝꯒꯠꯐꯝꯒꯤ ꯈꯠꯅ ꯆꯩꯅꯕꯥ",
+       "content-json-empty-object": "ꯑꯍꯥꯡꯕꯥ ꯄꯣꯠꯁꯛ",
        "viewpagelogs": "ꯃꯁꯤꯒꯤ ꯂꯥꯃꯥꯏꯁꯤꯒꯤ ꯅꯧꯅ ꯆꯪꯉꯨ",
        "currentrev-asof": "$1 ꯒꯤ ꯅꯧꯅꯥ ꯑꯃꯨꯛꯍꯟꯅꯥ ꯌꯦꯡꯕꯥ ꯃꯤꯠꯌꯦꯡ",
        "revisionasof": "Revision as of $1",
        "currentrevisionlink": "ꯈꯋꯥꯏꯗꯒꯤ ꯅꯧꯅꯥ ꯑꯃꯨꯛ ꯌꯦꯡꯕꯥ",
        "cur": "ꯍꯧ",
        "last": "ꯃꯥꯃꯥꯡꯒꯤ",
+       "page_first": "ꯑꯍꯥꯟꯕ",
+       "page_last": "ꯑꯔꯣꯏꯕ",
+       "histlegend": "Diff selection: Mark the radio boxes of the revisions to compare and hit enter or the button at the bottom.<br />\nLegend: <strong>({{int:cur}})</strong> = difference with latest revision, <strong>({{int:last}})</strong> = difference with preceding revision, <strong>{{int:minoreditletter}}</strong> = minor edit.",
+       "history-fieldset-title": "ꯊꯤꯋꯨ ꯑꯃꯨꯛ ꯍꯝꯁꯟꯅꯥ ꯌꯦꯡꯅꯕꯥ",
+       "histfirst": "ꯈꯋꯥꯏꯗꯒꯤ ꯑꯔꯤꯕꯥ",
+       "histlast": "ꯑꯅꯧꯕꯥ",
        "rev-delundel": "ꯑꯍꯣꯡꯕꯥ ꯎꯍꯟꯂꯤꯕꯥ",
+       "revdelete-show-file-submit": "ꯍꯣꯏ",
+       "revdelete-radio-unset": "ꯎꯍꯟꯕ",
        "history-title": "Revision history of \"$1\"",
        "difference-title": "$1 ꯒꯤ ꯑꯃꯨꯛꯍꯟꯕꯥ ꯈꯦꯠꯅꯕꯥꯒꯤ ꯃꯔꯛ",
        "lineno": "ꯂꯥ ꯏ $1",
        "nmembers": "$1 {{PLURAL:$1|member|members}}",
        "newpages": "ꯑꯅꯧꯕꯥ ꯂꯥꯃꯥꯏꯁꯤꯡ",
        "move": "ꯂꯦꯡꯍꯟꯕꯥ",
+       "pager-newer-n": "{{PLURAL:$1|newer 1|newer $1}}",
        "pager-older-n": "{{PLURAL:$1|older 1|older $1}}",
        "booksources": "ꯂꯥꯏꯔꯤꯛꯀꯤ ꯍꯧꯔꯛꯐꯝ",
        "booksources-search-legend": "ꯂꯥꯏꯔꯤꯛꯀꯤ ꯍꯧꯔꯛꯐꯝ ꯊꯤꯕꯥ",
        "tooltip-save": "ꯅꯪꯒꯤ ꯑꯍꯣꯡꯕꯗꯨ ꯇꯨꯡꯁꯤꯟꯂꯨ",
        "tooltip-preview": "ꯅꯪꯒꯤ ꯑꯍꯣꯡꯕꯗꯨ ꯑꯃꯨꯛ ꯍꯟꯅꯥ ꯎꯠꯂꯨ. ꯆꯥꯟꯕꯤꯗꯨꯅꯥ ꯃꯁꯤ ꯍꯥꯟꯅꯥ ꯁꯤꯖꯤꯅꯧ ꯇꯪꯁꯤꯟꯗ꯭ꯔꯤꯉꯧꯗꯥ",
        "tooltip-diff": "ꯅꯪꯅꯥ ꯏꯔꯤꯕꯥ ꯄꯥꯔꯦꯡꯗꯨꯗꯥ ꯑꯍꯣꯡꯕꯥ ꯎꯠꯂꯨ",
+       "tooltip-compareselectedversions": "See the differences between the two selected revisions of this page",
        "tooltip-rollback": "ꯑꯔꯣꯏꯕꯥ ꯈꯣꯝꯒꯠꯛꯂꯤꯕꯥꯁꯤꯡꯒꯤ ꯁꯦꯝꯒꯠꯄꯁꯤꯡ ꯗꯨꯒꯤ ꯂꯥꯃꯥꯏ ꯑꯃꯨꯛ ꯅꯝꯕꯗꯥ ꯂꯥꯛꯍꯟꯂꯨ ꯍꯥꯟꯅꯒꯤ ꯃꯐꯝꯗꯨꯗꯥ",
        "tooltip-undo": "\"Undo\" reverts this edit and opens the edit form in preview mode. It allows adding a reason in the summary.",
        "tooltip-summary": "ꯑꯇꯦꯟꯕꯥ ꯀꯨꯞꯅꯥ ꯁꯟꯗꯣꯛꯅꯩ ꯇꯥꯛꯄꯥ ꯏꯌꯨ",
        "simpleantispam-label": "Anti-spam check.\nDo <strong>not</strong> fill this in!",
+       "pageinfo-header-edits": "ꯄꯨꯋꯥꯔꯤ ꯁꯦꯝꯒꯠꯄ",
        "pageinfo-header-restrictions": "ꯉꯥꯛꯊꯣꯛꯂꯕꯥ ꯂꯥꯃꯥꯏ",
        "pageinfo-robot-noindex": "ꯌꯥꯍꯟꯗꯕꯥ",
        "pageinfo-subpages-name": "ꯂꯥꯃꯥꯏꯁꯤ ꯒꯤ ꯃꯅꯨꯡ ꯆꯟꯕꯥ ꯀꯨꯞꯊꯕꯥ ꯂꯥꯃꯥꯏꯁꯤꯡ",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|redirect|redirects}}; $3 {{PLURAL:$3|non-redirect|non-redirects}})",
+       "pageinfo-firstuser": "ꯂꯥꯃꯥꯏ ꯁꯥꯔꯤꯕ ꯃꯤꯑꯣꯏꯁꯤꯡ",
+       "pageinfo-firsttime": "ꯂꯥꯃꯥꯏ ꯁꯥꯈꯤꯕꯒꯤ ꯆꯩꯆꯠ",
+       "pageinfo-lastuser": "ꯈꯋꯥꯏꯗꯒꯤ ꯅꯧꯕ ꯁꯦꯝꯒꯠꯂꯛꯂꯤꯕꯁꯤꯡ",
+       "pageinfo-lasttime": "ꯅꯧꯔꯤꯕ ꯁꯦꯝꯒꯠꯄꯒꯤ ꯆꯩꯆꯠ",
+       "pageinfo-edits": "ꯑꯄꯨꯟꯕ ꯁꯦꯝꯒꯠꯄꯒꯤ ꯃꯁꯤꯡ",
+       "pageinfo-authors": "ꯑꯄꯨꯟꯕ ꯑꯈꯟꯅꯕ ꯑꯌꯤꯕꯁꯤꯡꯒꯤ ꯃꯁꯤꯡ",
        "pageinfo-magic-words": "Magic {{PLURAL:$1|word|words}} ($1)",
        "pageinfo-toolboxlink": "ꯂꯥꯃꯥꯏꯒꯤ ꯃꯇꯥꯡꯗꯥ",
        "pageinfo-contentpage-yes": "ꯍꯣꯏ",
index c3fc175..e73006f 100644 (file)
        "rev-deleted-user": "(အသုံးပြုသူအမည် ဖယ်ရှားပြီး)",
        "rev-deleted-event": "(အသေးစိတ် မှတ်တမ်း ဖယ်ရှားပြီးပြီ)",
        "rev-deleted-text-permission": "ဤစာမျက်နှာ ရာဇဝင်များသည် <strong>ဖျက်ထားပြီး</strong>ဖြစ်သည်။\nအသေးစိတ်များကို [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ဖျက်ထားသည့် မှတ်တမ်း]တွင် တွေ့ရှိနိုင်ပါသည်။",
+       "rev-deleted-unhide-diff": "ဤမူကွဲ၏ တည်းဖြတ်မူတစ်ခုအား <strong>ဖျက်ထားသည်</strong>။\nအသေးစိတ်ကို [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ဖျက်ထားသည့် မှတ်တမ်း]တွင် တွေ့ရှိနိုင်သည်။\nသင့်အနေဖြင့် ဆန္ဒရှိပါက [$1 ဤမူကွဲ]ကို ဆက်လက် ကြည့်ရှုနိုင်သည်။",
+       "rev-deleted-diff-view": "ဤမူကွဲ၏ တည်းဖြတ်မူတစ်ခုအား <strong>ဖျက်ထားသည်</strong>။\nဤမူကွဲကို ကြည့်ရှုနိုင်ပြီး အသေးစိတ်ကို [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ဖျက်ထားသည့် မှတ်တမ်း]တွင် တွေ့ရှိနိုင်သည်။",
        "rev-delundel": "အမြင်ပုံစံ ပြောင်းလဲရန်",
        "rev-showdeleted": "ပြ",
        "revisiondelete": "မူများကို ဖျက်ရန်/မဖျက်တော့ရန်",
index 80d38b6..17886a4 100644 (file)
        "resetpass-submit-loggedin": "Endre passord",
        "resetpass-submit-cancel": "Avbryt",
        "resetpass-wrong-oldpass": "Ugyldig midlertidig eller aktivt passord.\nDet kan tenkes at allerede har gjennomført et vellykket bytte av passord, eller bedt om et nytt midlertidig passord.",
-       "resetpass-recycled": "Vær vennlig å endre passordet til noe annen enn gjeldende passord.",
+       "resetpass-recycled": "Endre passordet ditt til noe annet enn det nåværende passordet.",
        "resetpass-temp-emailed": "Du logget inn med en midlertidig kode sendt på e-post.\nFor å avslutte innloggingen må du angi et nytt passord her:",
        "resetpass-temp-password": "Midlertidig passord:",
        "resetpass-abort-generic": "Endring av passord har blitt avbrutt av en utvidelse.",
        "resetpass-expired": "Passordet ditt har utløpt. Vær vennlig å angi et nytt passord for å logge inn.",
        "resetpass-expired-soft": "Passordet ditt har utløpt og må endres. Vær vennlig å angi et nytt passord, eller klikk \"{{int:authprovider-resetpass-skip-label}}\" for å endre det senere.",
-       "resetpass-validity-soft": "Ditt passord er ikke gyldig: $1",
+       "resetpass-validity-soft": "Passordet ditt er ikke gyldig: $1\n\nVelg et nytt passord nå, eller klikk på «{{int:authprovider-resetpass-skip-label}}» for å endre det seinere.",
        "passwordreset": "Tilbakestilling av passord",
        "passwordreset-text-one": "Fyll ut skjemaet for å tilbakestille passordet",
        "passwordreset-text-many": "{{PLURAL:$1|Fyll inn ett av datafeltene for å tilbakestille passordet ditt via epost.}}",
        "previewerrortext": "En feil oppsto mens dine endringer skulle forhåndsvises.",
        "blockedtitle": "Brukeren er blokkert",
        "blockedtext": "<strong>Ditt brukernavn eller din IP-adresse har blitt blokkert.</strong>\n\nBlokkeringen ble utført av $1. Grunnen som ble oppgitt var <em>$2</em>.\n\n* Blokkeringen begynte:  $8\n* Blokkeringen opphører: $6\n* Blokkeringen ment for: $7\n\nDu kan kontakte $1 eller en annen [[{{MediaWiki:Grouppage-sysop}}|administrator]] for å diskutere blokkeringen.\nDu kan ikke bruke \"{{int:emailuser}}\"-funksjonen med mindre du har oppgitt en gyldig e-postadresse i [[Special:Preferences|innstillingene dine]] og du ikke har blitt blokkert fra å sende e-post.\nDin nåværende IP-adresse er $3, og blokkerings-ID-en er #$5.\nVennligst ta med all denne informasjonen ved henvendelser.",
-       "autoblockedtext": "Din IP-adresse har blitt automatisk blokkert fordi den ble brukt av en annen bruker som ble blokkert av $1.\nDen oppgitte grunnen var:\n\n:'''$2'''\n\n* Blokkeringen begynte: $8\n* Blokkeringen utgår: $6\n* Blokkeringen er ment for: $7\n\nDu kan kontakte $1 eller en av de andre [[{{MediaWiki:Grouppage-sysop}}|administratorene]] for å diskutere blokkeringen.\n\nMerk at du ikke kan bruke «E-post til denne brukeren»-funksjonen med mindre du har registrert en gyldig e-postadresse i [[Special:Preferences|innstillingene dine]].\n\nDin IP-adresse er $3, og blokkerings-ID-en er #$5.\nVennligst ta med all denne informasjonen ved henvendelser.",
+       "autoblockedtext": "Din IP-adresse har blitt automatisk blokkert fordi den ble brukt av en annen bruker som ble blokkert av $1.\nDen oppgitte grunnen var:\n\n:'''$2'''\n\n* Blokkeringen begynte: $8\n* Blokkeringen utgår: $6\n* Blokkeringen er ment for: $7\n\nDu kan kontakte $1 eller en av de andre [[{{MediaWiki:Grouppage-sysop}}|administratorene]] for å diskutere blokkeringen.\n\nMerk at du ikke kan bruke «{{int:emailuser}}»-funksjonen med mindre du har registrert en gyldig e-postadresse i [[Special:Preferences|innstillingene dine]].\n\nDin IP-adresse er $3, og blokkerings-ID-en er #$5.\nVennligst ta med all denne informasjonen ved henvendelser.",
        "systemblockedtext": "Ditt brukernavn eller IP-adresse har blitt blokkert automatisk av MediaWiki.\n\nBlokkeringen grunnes:\n\n:<em>$2</em>\n\n* Blokkeringen startet: $8\n* Blokkeringen gjelder til: $6\n* Blokkeringen er ment for: $7\n\nDin nåværende IP-adresse er $3.\nVennligst inkluder informasjonen over i alle spørsmål du spør angående dette.",
        "blockednoreason": "ingen grunn gitt",
        "whitelistedittext": "Du må $1 for å redigere artikler.",
        "converter-manual-rule-error": "En feil ble oppdaget i en manuell språkkonverteringsregel",
        "undo-success": "Redigeringen kan omgjøres. Sjekk sammenligningen under for å bekrefte at du vil gjøre dette, og lagre endringene for å fullføre omgjøringen.",
        "undo-failure": "Redigeringen kunne ikke omgjøres på grunn av konflikterende etterfølgende redigeringer.",
+       "undo-main-slot-only": "Redigeringen kunne ikke omgjøres fordi den involverer innhold utenfor hovedspalten.",
        "undo-norev": "Redigeringen kunne ikke fjernes fordi den ikke eksisterer eller ble slettet",
        "undo-nochange": "Det ser ut til at redigeringen allerede er tilbakestilt.",
        "undo-summary": "Fjerner revisjon $1 av [[Special:Contributions/$2|$2]] ([[User talk:$2|diskusjon]])",
        "rcfilters-other-review-tools": "Andre gjennomgangsverktøy",
        "rcfilters-group-results-by-page": "Grupper resultater etter side",
        "rcfilters-activefilters": "Aktive filtre",
+       "rcfilters-activefilters-hide": "Skjul",
+       "rcfilters-activefilters-show": "Vis",
+       "rcfilters-activefilters-hide-tooltip": "Skjul området for aktive filtre",
+       "rcfilters-activefilters-show-tooltip": "Vis området for aktive filtre",
        "rcfilters-advancedfilters": "Avanserte filtre",
        "rcfilters-limit-title": "Antall resultater som skal vises",
        "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|endring|endringer}}, $2",
        "rcfilters-savedqueries-rename": "Gi nytt navn",
        "rcfilters-savedqueries-setdefault": "Sett som standard",
        "rcfilters-savedqueries-unsetdefault": "Fjern som standard",
-       "rcfilters-savedqueries-remove": "Fjern",
+       "rcfilters-savedqueries-remove": "Slett",
        "rcfilters-savedqueries-new-name-label": "Navn",
        "rcfilters-savedqueries-new-name-placeholder": "Beskriv formålet til filteret",
        "rcfilters-savedqueries-apply-label": "Opprett filter",
        "rcfilters-empty-filter": "Ingen aktive filtre. Alle bidrag vises.",
        "rcfilters-filterlist-title": "Filtre",
        "rcfilters-filterlist-whatsthis": "Hvordan virker dette?",
-       "rcfilters-filterlist-feedbacklink": "Gi tilbakemelding på disse (nye) filterverktøyene",
+       "rcfilters-filterlist-feedbacklink": "Gi tilbakemelding på disse filterverktøyene",
        "rcfilters-highlightbutton-title": "Marker resultater",
        "rcfilters-highlightmenu-title": "Velg en farge",
        "rcfilters-highlightmenu-help": "Velg en farge for å merke denne egenskapen",
index ff9175b..6adf737 100644 (file)
        "expansion-depth-exceeded-warning": "De pagina bevat te veel sjablonen",
        "parser-unstrip-loop-warning": "Er is een \"unstrip\"-lus gedetecteerd",
        "unstrip-depth-warning": "De recursielimiet ($1) voor \"unstrip\" is overschreden",
+       "unstrip-depth-category": "Pagina's waar de \"unstrip\" dieptelimiet is overschreden.",
+       "unstrip-size-warning": "De groottelimiet ($1) voor \"unstrip\" is overschreden",
+       "unstrip-size-category": "Pagina's waar de \"unstrip\" groottelimiet is overschreden",
        "converter-manual-rule-error": "Er is een fout gedetecteerd in een handmatig toegevoegde taalconversieregel.",
        "undo-success": "Deze bewerking kan ongedaan gemaakt worden.\nHieronder staat de tekst waarin de wijziging ongedaan is gemaakt.\nControleer voor het opslaan of het resultaat gewenst is.",
        "undo-failure": "De wijziging kan niet ongedaan gemaakt worden vanwege andere strijdige wijzigingen.",
        "limitreport-cputime-value": "$1 {{PLURAL:$1|seconde|seconden}}",
        "limitreport-walltime": "Werkelijk tijdsgebruik",
        "limitreport-walltime-value": "$1 {{PLURAL:$1|seconde|seconden}}",
-       "limitreport-ppvisitednodes": "Aantal nodes bekeken tijdens de voorverwerking:",
-       "limitreport-ppgeneratednodes": "Aantal nodes aangemaakt tijdens de voorverwerking:",
+       "limitreport-ppvisitednodes": "Aantal nodes bekeken tijdens de voorverwerking",
+       "limitreport-ppgeneratednodes": "Aantal nodes aangemaakt tijdens de voorverwerking",
        "limitreport-postexpandincludesize": "Inclusiegrootte na uitbreiden",
        "limitreport-postexpandincludesize-value": "$1/$2 {{PLURAL:$2|byte|bytes}}",
        "limitreport-templateargumentsize": "Grootte sjabloonparameters",
        "limitreport-templateargumentsize-value": "$1 / $2 {{PLURAL:$2|byte|bytes}}",
        "limitreport-expansiondepth": "Hoogste uitbreidingsdiepte",
        "limitreport-expensivefunctioncount": "Aantal kostbare parserfuncties",
+       "limitreport-unstrip-depth": "\"Unstrip\" recursiediepte",
+       "limitreport-unstrip-size": "\"Unstrip\" grootte na uitbreiden",
        "limitreport-unstrip-size-value": "$1/$2 {{PLURAL:$2|byte|bytes}}",
        "expandtemplates": "Sjablonen substitueren",
        "expand_templates_intro": "Deze speciale pagina leest de opgegeven wikitekst in en substitueert recursief alle sjablonen in de wikitekst.\nHet substitueert ook alle parserfuncties zoals\n<code><nowiki>{{</nowiki>#language:…}}</code> en\nvariabelen als <code><nowiki>{{</nowiki>CURRENTDAY}}</code>.\nVrijwel alles tussen dubbele accolades wordt gesubstitueerd.",
index 395b665..be98c41 100644 (file)
        "prefs-diffs": "Skilnader",
        "prefs-help-prefershttps": "Denne innstillinga vil verta verksam neste gongen du loggar inn.",
        "userrights": "Administrering av brukartilgang",
-       "userrights-lookup-user": "Administrer brukargrupper",
+       "userrights-lookup-user": "Vel ein brukar",
        "userrights-user-editname": "Skriv inn brukarnamn:",
-       "editusergroup": "Endre brukargrupper",
+       "editusergroup": "Last inn brukargrupper",
        "editinguser": "Endrar brukarrettane til brukaren '''[[User:$1|$1]]''' $2",
+       "viewinguserrights": "Viser brukarrettane til {{GENDER:$1|brukaren}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Endre brukargrupper",
+       "userrights-viewusergroup": "Sjå {{GENDER:$1|brukargrupper}}",
        "saveusergroups": "Lagre brukargrupper",
        "userrights-groupsmember": "Medlem av:",
        "userrights-groupsmember-auto": "Implisitt medlem av:",
        "right-siteadmin": "Låse og låse opp databasen",
        "right-override-export-depth": "Eksporter sider inkludert lenkte sider til ei djupn på 5",
        "right-sendemail": "Senda e-post til andre brukarar",
+       "grant-group-page-interaction": "Verka inn på sider",
        "grant-group-high-volume": "Utføra høgvolumaktivitet",
+       "grant-group-administration": "Utføra administrative handlingar",
        "grant-blockusers": "Blokkera og avblokkera brukarar",
        "grant-createaccount": "Oppretta kontoar",
        "grant-createeditmovepage": "Oppretta, endra og flytta sider",
        "grant-delete": "Sletta sider, versjonar og loggoppføringar",
        "grant-editinterface": "Gjera endringar i MediaWiki-namnerommet og i CSS/JavaScript for brukarkontoen",
+       "grant-editpage": "Endra oppretta sider",
        "grant-highvolume": "Høgvolumendring",
        "grant-protect": "Verna og avverna sider",
+       "grant-rollback": "Rulla attende endringar på sider",
        "grant-sendemail": "Senda e-post til andre brukarar",
        "grant-uploadeditmovefile": "Lasta opp, byta ut og flytta filer",
        "grant-uploadfile": "Lasta opp nye filer",
        "booksources-search": "Søk",
        "booksources-text": "Nedanfor finn du ei liste over lenkjer til andre nettstader som sel nye og brukte bøker, og desse kan ha meir informasjon om bøker du leitar etter:",
        "booksources-invalid-isbn": "Det oppgjevne ISBN-nummeret er ugyldig; sjekk med kjelda di om du har oppgjeve det rett.",
+       "magiclink-tracking-rfc": "Sider som nyttar magiske RFC-lenkjer",
+       "magiclink-tracking-pmid": "Sider som nyttar magiske PMID-lenkjer‎",
+       "magiclink-tracking-isbn": "Sider som nyttar magiske ISBN-lenkjer",
        "specialloguserlabel": "Utøvar:",
        "speciallogtitlelabel": "Mål (tittel eller {{ns:user}}:brukarnamn for brukar):",
        "log": "Loggar",
        "trackingcategories-msg": "Sporingskategori",
        "trackingcategories-name": "Meldingsnamn",
        "trackingcategories-desc": "Inkluderingsgrunnlag",
+       "restricted-displaytitle-ignored": "Sider med ignorerte visingstitlar",
+       "restricted-displaytitle-ignored-desc": "Ein <code><nowiki>{{DISPLAYTITLE}}</nowiki></code>-funksjon på denne sida  er sett bort ifrå av di argumentet ikkje svarar til den faktiske tittelen til sida.",
        "noindex-category-desc": "Sida vert ikkje indeksert av robotar av di ho inneheld trylleordet <code><nowiki>__NOINDEX__</nowiki></code> og er i eit namnerom der dette flagget er tillate.",
        "trackingcategories-nodesc": "Inga skilding er tilgjengeleg.",
        "trackingcategories-disabled": "Kategorien er avslegen",
        "editcomment": "Samandraget for endringa var: <em>$1</em>.",
        "revertpage": "Attenderulla endring gjord av [[Special:Contributions/$2|$2]] ([[User talk:$2|diskusjon]]) til siste versjonen av [[User:$1|$1]]",
        "revertpage-nouser": "Attenderulla endring gjord av ein løynd brukar til siste versjonen av {{GENDER:$1|[[User:$1|$1]]}}",
-       "rollback-success": "Rulla attende endringane av $1, attende til siste versjonen av $2.",
+       "rollback-success": "Rulla attende endringane av {{GENDER:$3|$1}}, attende til siste versjonen av {{GENDER:$4|$2}}.",
        "sessionfailure-title": "Feil med omgangen.",
        "sessionfailure": "Det ser ut til å vera eit problem med innloggingsøkta di. Handlinga er vorten avbroten for å vera føre var mot kidnapping av økta. Bruk attendeknappen i nettlesaren din og prøv om att.",
        "changecontentmodel": "Endra innhaldsmodellen til ei side",
        "watchlisttools-raw": "Endre på overvakingslista i råformat",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskusjon]])",
        "duplicate-defaultsort": "Åtvaring: Standardsorteringa «$2» tar over for den tidlegare sorteringa «$1».",
+       "restricted-displaytitle": "<strong>Åtvaring:</strong> Visingstittelen «$1» vart sett bort frå sidan han ikkje svarar til den faktiske tittelen til sida.",
        "version": "Versjon",
        "version-extensions": "Installerte utvidingar",
        "version-skins": "Installerte drakter",
index 60c36a9..e630c19 100644 (file)
@@ -98,7 +98,8 @@
                        "Anwar2",
                        "Acamicamacaraca",
                        "DeRudySoulStorm",
-                       "Railfail536"
+                       "Railfail536",
+                       "Vlad5250"
                ]
        },
        "tog-underline": "Podkreślenie linków:",
        "right-browsearchive": "Przeszukiwanie usuniętych stron",
        "right-undelete": "Odtwarzanie usuniętych stron",
        "right-suppressrevision": "Podgląd, ukrywanie i odkrywanie wersji ukrytych przed wszystkimi",
-       "right-viewsuppressed": "Umożliwia zobaczenie wersji ukrytych przed każdym użytkownikiem",
+       "right-viewsuppressed": "Podgląd wersji ukrytych przed każdym użytkownikiem",
        "right-suppressionlog": "Podgląd rejestru ukrywania",
        "right-block": "Blokowanie użytkownikom możliwości edycji",
        "right-blockemail": "Blokowanie użytkownikom możliwości wysyłania wiadomości",
index a88101d..ad46b4d 100644 (file)
        "rcfilters-savedqueries-apply-and-setdefault-label": "د فرض په ډول د فيلټر جوړول",
        "rcfilters-savedqueries-cancel-label": "ناگارل",
        "rcfilters-savedqueries-add-new-title": "د امستنې اوسنۍ فيلټر خوندي کړي",
+       "rcfilters-restore-default-filters": "د ډيفاولټ فلټرونه بیا تازه کول",
        "rcfilters-search-placeholder": "د فلټر بدلونونه (د مینو کارول یا د فلټر نوم لټونه)",
        "rcfilters-invalid-filter": "غلط فلټر",
        "rcfilters-empty-filter": "هيڅ فعال فلټر نشته. ټولي سمونې ښکاره شوي.",
index 120b542..23b2f66 100644 (file)
        "removedwatchtext-short": "A página \"$1\" foi removida de sua lista de páginas vigiadas.",
        "watch": "Vigiar",
        "watchthispage": "Vigiar esta página",
-       "unwatch": "Desinteressar-se",
+       "unwatch": "Deixar de vigiar",
        "unwatchthispage": "Parar de vigiar esta página",
        "notanarticle": "Não é uma página de conteúdo",
        "notvisiblerev": "Edição eliminada",
        "sp-contributions-logs": "registros",
        "sp-contributions-talk": "discussão",
        "sp-contributions-userrights": "{{GENDER:$1|gestão}} dos privilégios",
-       "sp-contributions-blocked-notice": "Este usuário atualmente está bloqueado. O registro de bloqueio mais recente é fornecido abaixo para referência:",
+       "sp-contributions-blocked-notice": "{{GENDER:$1|Este usuário atualmente está bloqueado|Esta usuária atualmente está bloqueada|Este(a) usuário(a) atualmente está bloqueado(a)}}. O registro de bloqueio mais recente é fornecido abaixo para referência:",
        "sp-contributions-blocked-notice-anon": "Este endereço IP encontra-se bloqueado.\nSegue, para referência, a entrada mais recente no registro de bloqueios:",
        "sp-contributions-search": "Navegar pelas contribuições",
        "sp-contributions-username": "Endereço de IP ou usuário(a):",
index db51aa6..26ec459 100644 (file)
@@ -77,7 +77,8 @@
                        "RadiX",
                        "MokaAkashiyaPT",
                        "Athena in Wonderland",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Ldacosta"
                ]
        },
        "tog-underline": "Sublinhar hiperligações:",
        "mostinterwikis": "Páginas com mais hiperligações interwikis",
        "mostrevisions": "Páginas com mais revisões",
        "prefixindex": "Todas as páginas iniciadas por",
-       "prefixindex-namespace": "Todas as páginas com prefixo (domínio $1)",
+       "prefixindex-namespace": "Todas as páginas com prefixo (espaço nominal $1)",
        "prefixindex-submit": "Mostrar",
        "prefixindex-strip": "Remover prefixo",
        "shortpages": "Páginas curtas",
        "removedwatchtext-short": "A página \"$1\" foi removida das suas páginas vigiadas.",
        "watch": "Vigiar",
        "watchthispage": "Vigiar esta página",
-       "unwatch": "Desinteressar-se",
+       "unwatch": "Deixar de vigiar",
        "unwatchthispage": "Parar de vigiar esta página",
        "notanarticle": "Não é uma página de conteúdo",
        "notvisiblerev": "A última revisão feita por um utilizador diferente foi eliminada",
        "sp-contributions-logs": "registos",
        "sp-contributions-talk": "discussão",
        "sp-contributions-userrights": "{{GENDER:$1|gestão}} dos privilégios",
-       "sp-contributions-blocked-notice": "Este utilizador está bloqueado neste momento.\nPara referência é apresentado abaixo o último registo de bloqueio:",
+       "sp-contributions-blocked-notice": "{{GENDER:$1|Este utilizador está bloqueado|Esta utilizadora está bloqueada}} neste momento.\nPara referência é apresentado abaixo o último registo de bloqueio:",
        "sp-contributions-blocked-notice-anon": "Este endereço IP está bloqueado neste momento.\nPara referência é apresentado abaixo o último registo de bloqueio:",
        "sp-contributions-search": "Pesquisar contribuições",
        "sp-contributions-username": "Endereço IP ou nome de utilizador:",
index 082230f..d9fef83 100644 (file)
        "prefs-editor": "Used in [[Special:Preferences]], tab \"Editing\" ({{int:prefs-editing}}).\n\n{{Identical|Editor}}",
        "prefs-preview": "Used in [[Special:Preferences]], tab \"Editing\".\n{{Identical|Preview}}",
        "prefs-advancedrc": "Used in [[Special:Preferences]], tab \"Recent changes\".\n{{Identical|Advanced options}}",
-       "prefs-opt-out": "Used in [[Special:Preferences]], tab \"Recent changes\".",
+       "prefs-opt-out": "Used in [[Special:Preferences]], tabs \"Recent changes\" and \"Watchlist\".",
        "prefs-advancedrendering": "Used in [[Special:Preferences]], tab \"Appearence\".\n{{Identical|Advanced options}}",
        "prefs-advancedsearchoptions": "Used in [[Special:Preferences]], tab \"Search options\".\n{{Identical|Advanced options}}",
        "prefs-advancedwatchlist": "Used in [[Special:Preferences]], tab \"Watchlist\".\n{{Identical|Advanced options}}",
index e52721f..3f61034 100644 (file)
                        "Happy13241",
                        "Vcohen",
                        "AttemptToCallNil",
-                       "Stjn"
+                       "Stjn",
+                       "Vlad5250"
                ]
        },
        "tog-underline": "Подчёркивание ссылок:",
        "pagetitle": "$1 — {{SITENAME}}",
        "pagetitle-view-mainpage": "{{SITENAME}}",
        "backlinksubtitle": "← $1",
-       "retrievedfrom": "Источник — «$1»",
+       "retrievedfrom": "Источник — $1",
        "youhavenewmessages": "{{PLURAL:$3|Вы получили}} $1 ($2).",
        "youhavenewmessagesfromusers": "{{PLURAL:$4|Вы получили}} $1 от {{PLURAL:$3|$3 участника|$3 участников|1=другого участника}} ($2).",
        "youhavenewmessagesmanyusers": "Вы получили $1 от множества пользователей ($2).",
        "mergelog": "Журнал объединений",
        "revertmerge": "Разделить",
        "mergelogpagetext": "Ниже приведён список последних объединений историй страниц.",
-       "history-title": "$1 — история изменений",
-       "difference-title": "$1 — различия между версиями",
+       "history-title": "$1: история изменений",
+       "difference-title": "$1: различия между версиями",
        "difference-title-multipage": "Разница между страницами «$1» и «$2»",
        "difference-multipage": "(Различия между страницами)",
        "lineno": "Строка $1:",
        "userrights-irreversible-marker": "$1*",
        "userrights-no-shorten-expiry-marker": "$1#",
        "userrights-expiry-current": "Истекает $1",
-       "userrights-expiry-none": "Ð\9dикогда",
+       "userrights-expiry-none": "Ð\91еÑ\81Ñ\81Ñ\80оÑ\87но",
        "userrights-expiry": "Права истекают:",
        "userrights-expiry-existing": "$2, $3",
        "userrights-expiry-othertime": "Другое время:",
        "right-movefile": "переименование файлов",
        "right-suppressredirect": "подавление перенаправлений при переименовании страниц",
        "right-upload": "загрузка файлов",
-       "right-reupload": "запиÑ\81Ñ\8c Ñ\84айлов Ð¿Ð¾Ð²ÐµÑ\80Ñ\85 Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вÑ\83Ñ\8eÑ\89иÑ\85",
+       "right-reupload": "пеÑ\80езапиÑ\81Ñ\8c Ñ\81Ñ\83Ñ\89еÑ\81Ñ\82вÑ\83Ñ\8eÑ\89иÑ\85 Ñ\84айлов",
        "right-reupload-own": "перезапись файлов тем же участником",
        "right-reupload-shared": "подмена файлов из общих хранилищ локальными",
        "right-upload_by_url": "загрузка файлов с адреса URL",
        "right-purge": "очистка кэша страниц без подтверждения",
        "right-autoconfirmed": "обход ограничений скорости на IP-адрес",
        "right-bot": "автоматический процесс",
-       "right-nominornewtalk": "малые правки на СО участников не создают для них уведомление о новом сообщении",
+       "right-nominornewtalk": "малые правки на страницах обсуждений участников не создают для них уведомление о новом сообщении",
        "right-apihighlimits": "уменьшение ограничений на выполнение API-запросов",
        "right-writeapi": "использование API для записи",
        "right-delete": "удаление страниц",
        "filerevert-success": "'''[[Media:$1|$1]]''' был возвращён к [$4 версии от $3, $2].",
        "filerevert-badversion": "Не существует предыдущей локальной версии этого файла с указанной меткой времени.",
        "filerevert-identical": "Текущая версия файла уже идентична выбранной.",
-       "filedelete": "$1 — удаление",
+       "filedelete": "$1: удаление",
        "filedelete-legend": "Удалить файл",
        "filedelete-intro": "Вы собираетесь удалить файл '''[[Media:$1|$1]]''' со всей его историей.",
        "filedelete-intro-old": "Вы удаляете версию <strong>[[Media:$1|$1]]</strong> от [$4 $3, $2].",
        "excontent": "содержимое: «$1»",
        "excontentauthor": "содержимое: «$1», единственным автором которого был [[Special:Contributions/$2|$2]] ([[User talk:$2|обсуждение]])",
        "exbeforeblank": "содержимое до очистки: «$1»",
-       "delete-confirm": "$1 — удаление",
+       "delete-confirm": "$1: удаление",
        "delete-legend": "Удаление",
        "historywarning": "<strong>Внимание:</strong> Вы собираетесь удалить страницу, у которой есть история правок, содержащая $1 {{PLURAL:$1|версию|версии|версий}}:",
        "historyaction-submit": "Показать",
        "anoncontribs": "Вклад",
        "contribsub2": "Вклад {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "Не зарегистрировано учётной записи «$1».",
-       "nocontribs": "Ð\98зменений, Ñ\81ооÑ\82веÑ\82Ñ\81Ñ\82вÑ\83Ñ\8eÑ\89иÑ\85 Ð·Ð°Ð´Ð°Ð½Ð½Ñ\8bм Ñ\83Ñ\81ловиÑ\8fм, Ð½Ð°Ð¹Ð´ÐµÐ½Ð¾ Ð½Ðµ Ð±Ñ\8bло.",
+       "nocontribs": "Ð\98зменений, Ñ\81ооÑ\82веÑ\82Ñ\81Ñ\82вÑ\83Ñ\8eÑ\89иÑ\85 Ð·Ð°Ð´Ð°Ð½Ð½Ñ\8bм Ñ\83Ñ\81ловиÑ\8fм, Ð½Ðµ Ð½Ð°Ð¹Ð´ÐµÐ½о.",
        "uctop": "(текущая)",
        "month": "С месяца (и ранее):",
        "year": "С года (и ранее):",
index b4de155..070f620 100644 (file)
@@ -14,7 +14,8 @@
                        "Matma Rex",
                        "KHMELNYTSKYIA",
                        "Igor Kercsa",
-                       "Fitoschido"
+                       "Fitoschido",
+                       "Vlad5250"
                ]
        },
        "tog-underline": "Підкреслёвати одказы:",
        "right-editsemiprotected": "Едітованя сторінок замкнутых на „{{int:protect-level-autoconfirmed}}“",
        "right-editinterface": "Едітованя інтерфейсу хоснователя",
        "right-editusercss": "Едітованя CSS файлів іншых хоснователїв",
+       "right-edituserjson": "Едітованя JSON файлів іншых хоснователїв",
        "right-edituserjs": "Едітованя JS файлів іншых хоснователїв",
        "right-editmyusercss": "Едітовати вашы властны хосновательскы CSS файлы.",
+       "right-editmyuserjson": "Едітовати вашы властны хосновательскы JSON файлы",
        "right-editmyuserjs": "Едітовати вашы властны хосновательскы JavaScript файлы",
        "right-viewmywatchlist": "Перезераня властного списку слїдованых сторінок",
        "right-editmywatchlist": "Едітованя властного списку слїдованых сторінок. Усвідомте собі, же дакотры дїї будуть до нёго придавати сторінкы і без такого права.",
index 6af24e5..3d12f42 100644 (file)
        "talk": "ᱜᱟᱞᱢᱟᱨᱟᱣ",
        "views": "ᱧᱮᱞᱚᱜᱚᱜ",
        "toolbox": "ᱦᱟᱹᱛᱭᱟᱹᱨ",
+       "tool-link-userrights": "{{GENDER:$1|ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ}} ᱜᱟᱫᱮᱞ ᱵᱚᱫᱚᱞ",
+       "tool-link-userrights-readonly": "{{GENDER:$1|ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ}} ᱜᱟᱫᱮᱞ ᱧᱮᱞ",
        "tool-link-emailuser": "ᱤᱢᱮᱞᱟᱭᱢᱮ ᱱᱩᱭ {{GENDER:$1|ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ}}",
        "imagepage": "ᱨᱮᱫ ᱥᱟᱦᱴᱟ ᱧᱮᱞᱢᱮ",
        "mediawikipage": "ᱠᱷᱚᱵᱚᱨ ᱥᱟᱦᱴᱟ ᱧᱮᱞᱢᱮ",
        "jumptonavigation": "ᱟᱹᱪᱩᱨᱵᱟᱲᱟ",
        "jumptosearch": "ᱥᱮᱸᱫᱽᱨᱟ",
        "view-pool-error": "Ikạkańmẽ, sarvarre nitoḱ do aḍi cap menaḱa.\nẠḍi aema beoharko noa sakam ńel lạgit́ko kurumuṭueda.\nNãwate noa sakam ńel kurumuṭuy lạgit́te dayakate mit́ghạṛi tạṅgiymẽ.\n$1",
+       "generic-pool-error": "ᱤᱠᱟᱹᱠᱟᱹᱧᱢᱮ, ᱱᱚᱶᱟ ᱥᱚᱨᱵᱷᱚᱨ ᱨᱮ ᱱᱤᱛ ᱟᱹᱰᱤᱜᱟᱱ ᱪᱟᱯ ᱢᱮᱱᱟᱜᱼᱟ ᱾\nᱟᱹᱰᱤ ᱟᱭᱢᱟ ᱵᱮᱵᱷᱟᱨᱤᱭᱟᱹ ᱱᱚᱶᱟ ᱥᱟᱠᱟᱢ ᱧᱮᱞ ᱞᱟᱹᱜᱤᱫ ᱠᱚ ᱨᱤᱠᱟᱹᱭᱮᱫᱼᱟ ᱾\nᱱᱚᱶᱟ ᱥᱟᱠᱟᱢ ᱧᱮᱞ ᱞᱟᱹᱜᱤᱰ ᱱᱟᱥᱮ ᱛᱟᱺᱜᱤ ᱢᱮ ᱾",
        "pool-timeout": "Somoy paromena cạbi lạgit́te tạṅgi hoyoḱkana",
        "pool-queuefull": "Pool queue is full",
        "pool-errorunknown": "ᱵᱟᱝ ᱵᱟᱰᱟᱭ ᱦᱩᱲᱟᱹᱜ",
        "filerenameerror": "\"$1\" ᱨᱮᱫ ᱨᱮᱭᱟᱜ ᱧᱩᱛᱩᱢ ᱵᱚᱫᱚᱞ ᱠᱟᱛᱮ \"$2\" ᱮᱢ ᱵᱟᱝ ᱜᱟᱱᱚᱜ ᱠᱟᱱᱟ᱾",
        "filedeleteerror": "$1 ᱨᱮᱫ ᱫᱚ ᱵᱟᱝ ᱜᱮᱫᱽ ᱜᱤᱰᱤ ᱞᱮᱱᱟ",
        "directorycreateerror": "\"$1\" ᱰᱟᱭᱨᱮᱠᱴᱳᱨᱤ ᱫᱚ ᱵᱟᱝ ᱛᱮᱭᱟᱨᱞᱮᱱᱟ᱾",
+       "directorynotreadableerror": "ᱯᱟᱠᱷᱟ \"$1\" ᱫᱚ ᱯᱟᱲᱦᱟᱣ ᱵᱟᱭ ᱜᱟᱱᱚᱜᱼᱟ ᱾",
        "filenotfound": "\"$1\" ᱨᱮᱫ ᱫᱚ ᱵᱟᱝ ᱧᱟᱢᱞᱮᱱᱟ᱾",
        "unexpected": "ᱵᱟᱝ ᱟᱥᱟᱜ ᱠᱟᱱ ᱢᱟᱹᱱ: \"$1\"=\"$2\".",
        "formerror": "ᱦᱩᱲᱟᱹᱜ: ᱯᱷᱚᱨᱚᱢ ᱫᱚ ᱵᱟᱝ ᱡᱤᱢᱟᱹᱞᱮᱱᱟ᱾",
        "rcfilters-days-show-days": "$1 {{PLURAL:$1|ᱢᱟᱦᱟ|ᱢᱟᱦᱟᱸ}}",
        "rcfilters-days-show-hours": "$1 {{PLURAL:$1|ᱴᱟᱲᱟᱝ|ᱴᱟᱬᱟᱝ}}",
        "rcfilters-savedqueries-rename": "ᱧᱩᱢ-ᱟᱹᱨᱩ",
-       "rcfilters-savedqueries-remove": "á±\9aᱪá±\9aá±\9c",
+       "rcfilters-savedqueries-remove": "á±\9cᱮᱫ á±\9cᱤᱰᱤ",
        "rcfilters-savedqueries-new-name-label": "ᱧᱩᱛᱩᱢ",
        "rcfilters-savedqueries-cancel-label": "ᱵᱟᱹᱜᱤ",
        "rcnotefrom": "ᱞᱟᱛᱟᱨ {{PLURAL:$5|ᱵᱚᱫᱚᱞ|ᱵᱚᱫᱚᱞ ᱠᱚ}} <strong>$3, $4</strong> ᱠᱷᱚᱱ (<strong>$1</strong> ᱦᱟᱹᱵᱤᱡ ᱩᱫᱩᱜ-ᱮᱱᱟ)",
index 5dae666..62ae7d5 100644 (file)
        "tag-filter": "[[Special:Tags|ٽيگ]] ڇاڻي:",
        "tag-filter-submit": "ڇاڻي",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ٽيگ|ٽيگز}}]]: $2)",
+       "tag-mw-new-redirect": "نئون چوريل",
        "tag-mw-rollback-description": "واپس-ورايو ڳنڍڻي کي استعمال ڪندي پوين ترميمن کي واپس ورائيندڙ ترميمون",
        "tags-title": "ٽيگس",
        "tags-tag": "ٽيگ نانءُ",
index f1ff7f0..db457ae 100644 (file)
@@ -38,7 +38,8 @@
                        "Xð",
                        "Pmikolas44",
                        "Fitoschido",
-                       "Matěj Suchánek"
+                       "Matěj Suchánek",
+                       "Vlad5250"
                ]
        },
        "tog-underline": "Podčiarkovať odkazy:",
        "timezoneregion-indian": "Indický oceán",
        "timezoneregion-pacific": "Tichý oceán",
        "allowemail": "Povoliť prijímanie e-mailov od iných používateľov",
+       "email-allow-new-users-label": "Povoliť e-maily od úplne nových používateľov",
        "email-blacklist-label": "Znemožniť týmto používateľom posielať mi e-maily:",
        "prefs-searchoptions": "Vyhľadávanie",
        "prefs-namespaces": "Menné priestory",
        "prefs-dateformat": "Formát dátumu",
        "prefs-timeoffset": "Časový posun",
        "prefs-advancedediting": "Všeobecné možnosti",
+       "prefs-developertools": "Vývojárske nástroje",
        "prefs-editor": "Používateľ",
        "prefs-preview": "Náhľad",
        "prefs-advancedrc": "Rozšírené možnosti",
        "right-editcontentmodel": "Upravovať model obsahu stránky",
        "right-editinterface": "Upravovať správy používateľského rozhrania",
        "right-editusercss": "Upravovať CSS súbory ostatných používateľov",
+       "right-edituserjson": "Upravovať JSON súbory ostatných používateľov",
        "right-edituserjs": "Upravovať JS súbory ostatných používateľov",
        "right-editmyusercss": "Upraviť svoje vlastné používateľské súbory CSS",
+       "right-editmyuserjson": "Upraviť svoje vlastné používateľské súbory JSON",
        "right-editmyuserjs": "Upraviť svoje vlastné používateľské súbory JavaScript",
        "right-viewmywatchlist": "Zobraziť vlastný zoznam sledovaných stránok",
        "right-editmywatchlist": "Upraviť vlastný zoznam sledovaných stránok. Všimnite si, že niektoré operácie budú môcť pridať stránky aj bez tohto oprávnenia.",
        "rcfilters-advancedfilters": "Pokročilé filtre",
        "rcfilters-limit-title": "Zobraziť zmeny",
        "rcfilters-limit-and-date-label": "{{PLURAL:$1|Jedna úprava|$1 úpravy|$1 úprav}}, $2",
+       "rcfilters-date-popup-title": "Hľadané časové obdobie",
        "rcfilters-days-title": "Posledné dni",
        "rcfilters-hours-title": "Posledné hodiny",
        "rcfilters-days-show-days": "$1 {{PLURAL:$1|deň|dni|dní}}",
        "table_pager_limit_submit": "Spustiť",
        "table_pager_empty": "Bez výsledkov",
        "autosumm-blank": "Odstránený obsah stránky",
-       "autosumm-replace": "Nahrádzam stránku textom „$1“",
+       "autosumm-replace": "Obsah stránky nahradený textom „$1“",
        "autoredircomment": "Presmerovanie na [[$1]]",
+       "autosumm-removed-redirect": "Zrušené presmerovanie na [[$1]]",
+       "autosumm-changed-redirect-target": "Cieľ presmerovania zmenený z [[$1]] na [[$2]]",
        "autosumm-new": "Vytvorená stránka „$1“",
        "autosumm-newblank": "Vytvorená prázdna stránka",
        "lag-warn-normal": "Úpravy za {{PLURAL:$1|poslednú sekundu|posledné $1 sekundy|posledných $1 sekúnd}} nemusia byť v tomto zozname zobrazené.",
        "specialpages-group-wiki": "Údaje a nástroje",
        "specialpages-group-redirects": "Špeciálne stránky, ktoré sú presmerovania",
        "specialpages-group-spam": "Nástroje proti spamu",
-       "specialpages-group-developer": "Nástroje vývojárov",
+       "specialpages-group-developer": "Vývojárske nástroje",
        "blankpage": "Prázdna stránka",
        "intentionallyblankpage": "Táto stránka je zámerne prázdna. Používa sa na meranie výkonnosti atď.",
        "external_image_whitelist": "  #Nechajte tento riadok presne tak, ako je<pre>\n#Časti regulárnych výrazov (tie, ktoré sa píšu medzi //) napíšte dolu\n#Budú porovnané s URL externých obrázkov\n#Tie, ktoré budú zodpovedať reg. výrazu sa zobrazia ako obrázky, inak sa zobrazí iba odkaz na obrázok\n#Riadky, ktoré začínajú znakom # sa považujú za komentáre\n#Na veľkosti písmen nezáleží\n\n#Napíšte všetky časti reg. výrazov nad tento riadok. Nechajte tento riadok presne tak, ako je</pre>",
index 3c3e629..9f8a351 100644 (file)
        "december-date": "$1. децембар",
        "period-am": "преподне",
        "period-pm": "поподне",
-       "pagecategories": "{{PLURAL:$1|Категорија|Категорије|Категорија}}",
+       "pagecategories": "{{PLURAL:$1|Категорија|Категорије}}",
        "category_header": "Странице у категорији „$1“",
        "subcategories": "Поткатегорије",
        "category-media-header": "Датотеке у категорији „$1“",
        "permalink": "Трајна веза",
        "print": "Штампај",
        "view": "Погледај",
-       "view-foreign": "Ð\92иди на пројекту $1",
+       "view-foreign": "Ð\9fогледаÑ\98 на пројекту $1",
        "edit": "Уреди",
        "edit-local": "Уреди локални опис",
        "create": "Направи",
        "pagetitle-view-mainpage": "{{SITENAME}}",
        "backlinksubtitle": "← $1",
        "retrievedfrom": "Преузето из „$1“",
-       "youhavenewmessages": "Имате $1 ($2).",
+       "youhavenewmessages": "{{PLURAL:$3|Имате}} $1 ($2).",
        "youhavenewmessagesfromusers": "{{PLURAL:$4|Имате}} $1 од {{PLURAL:$3|другог корисника|$3 корисника}} ($2).",
        "youhavenewmessagesmanyusers": "Имате $1 од много корисника ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|нову поруку|нове поруке|нових порука}}",
        "sort-descending": "Поређај опадајуће",
        "sort-ascending": "Поређај растуће",
        "nstab-main": "Страница",
-       "nstab-user": "{{GENDER:{{BASEPAGENAME}}|Корисник|Корисница|Корисник}}",
+       "nstab-user": "Корисничка страница",
        "nstab-media": "Медији",
-       "nstab-special": "Ð\9fоÑ\81ебно",
-       "nstab-project": "Ð\9fÑ\80оÑ\98екаÑ\82",
+       "nstab-special": "Ð\9fоÑ\81ебна Ñ\81Ñ\82Ñ\80аниÑ\86а",
+       "nstab-project": "СÑ\82Ñ\80аниÑ\86а Ð¿Ñ\80оÑ\98екÑ\82а",
        "nstab-image": "Датотека",
        "nstab-mediawiki": "Порука",
        "nstab-template": "Шаблон",
        "userlogin-yourpassword-ph": "Унесите своју лозинку",
        "createacct-yourpassword-ph": "Унесите лозинку",
        "yourpasswordagain": "Поново унеси лозинку:",
-       "createacct-yourpasswordagain": "Потврди лозинку",
+       "createacct-yourpasswordagain": "Потврдите лозинку",
        "createacct-yourpasswordagain-ph": "Унесите лозинку поново",
        "userlogin-remembermypassword": "Остави ме пријављеног/у",
        "userlogin-signwithsecure": "Користите сигурну конекцију",
        "showpreview": "Прикажи претпреглед",
        "showdiff": "Прикажи измене",
        "blankarticle": "<strong>Упозорење:</strong> Страница коју правите је празна.\nАко још једном притиснете „$1”, страница ће бити направљена без икаквог садржаја.",
-       "anoneditwarning": "<strong>УпозоÑ\80еÑ\9aе:</strong> Ð\9dиÑ\81Ñ\82е Ð¿Ñ\80иÑ\98авÑ\99ени. Ð\90ко Ð¾Ð±Ñ\98авиÑ\82е Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83, Ð\92аÑ\88а IP Ð°Ð´Ñ\80еÑ\81а Ñ\9bе Ð±Ð¸Ñ\82и Ñ\98авно Ð²Ð¸Ð´Ñ\99ива Ñ\83 Ñ\9aеноÑ\98 Ð¸Ñ\81Ñ\82оÑ\80иÑ\98и Ð¸Ð·Ð¼ÐµÐ½Ð° Ð¸ Ð´Ñ\80Ñ\83где. Ð\90ко Ñ\81е <strong>[$1 Ð¿Ñ\80иÑ\98авиÑ\82е]</strong> Ð¸Ð»Ð¸ <strong>[$2 Ð¾Ñ\82воÑ\80иÑ\82е Ð½Ð°Ð»Ð¾Ð³]</strong>, Ð¿Ð¾Ñ\80ед Ð¾Ñ\81Ñ\82алиÑ\85 Ð¿Ð¾Ð³Ð¾Ð´Ð½Ð¾Ñ\81Ñ\82и ÐºÐ¾Ñ\98е Ð´Ð¾Ð±Ð¸Ñ\98аÑ\82е Ð\92аÑ\88е Ð¸Ð·Ð¼ÐµÐ½Ðµ Ñ\9bе Ð±Ð¸Ñ\82и Ð¿Ñ\80ипиÑ\81иване Ð\92аÑ\88ем ÐºÐ¾Ñ\80иÑ\81ниÑ\87ком Ð¸Ð¼ÐµÐ½Ñ\83.",
+       "anoneditwarning": "<strong>УпозоÑ\80еÑ\9aе:</strong> Ð\9dиÑ\81Ñ\82е Ð¿Ñ\80иÑ\98авÑ\99ени. Ð\92аÑ\88а IP Ð°Ð´Ñ\80еÑ\81а Ñ\9bе Ð±Ð¸Ñ\82и Ñ\98авно Ð²Ð¸Ð´Ñ\99ива Ð°ÐºÐ¾ Ð½Ð°Ð¿Ñ\80авиÑ\82е Ð½ÐµÐºÑ\83 Ð¸Ð·Ð¼ÐµÐ½Ñ\83. Ð\90ко Ñ\81е <strong>[$1 Ð¿Ñ\80иÑ\98авиÑ\82е]</strong> Ð¸Ð»Ð¸ <strong>[$2 Ð¾Ñ\82воÑ\80иÑ\82е Ð½Ð°Ð»Ð¾Ð³]</strong>, Ð\92аÑ\88е Ð¸Ð·Ð¼ÐµÐ½Ðµ Ñ\9bе Ð±Ð¸Ñ\82и Ð¿Ñ\80ипиÑ\81иване Ð\92аÑ\88ем ÐºÐ¾Ñ\80иÑ\81ниÑ\87ком Ð¸Ð¼ÐµÐ½Ñ\83, Ð¿Ð¾Ñ\80ед Ð´Ñ\80Ñ\83гиÑ\85 Ð¿Ð¾Ð³Ð¾Ð´Ð½Ð¾Ñ\81Ñ\82и.",
        "anonpreviewwarning": "<em>Нисте пријављени. Ако објавите страницу, Ваша IP адреса ће бити јавно видљива у њеној историји измена и другде.</em>",
        "missingsummary": "<strong>Подсетник:</strong> Нисте унели опис измене.\nАко поново кликнете на „$1”, Ваша измена ће бити сачувана без описа.",
        "selfredirect": "<strong>Упозорење:</strong> Преусмеравате ову страницу на њу саму.\nМожда вам је одредишна страница за преусмерење погрешна или уређујете погрешну страницу.\nАко још једном притиснете „$1”, преусмерење ће свеједно бити направљено.",
        "newarticle": "(нови)",
        "newarticletext": "Дошли сте на страницу која још не постоји.\nДа бисте је направили, почните да куцате у прозор испод овог текста (погледајте [$1 страницу за помоћ]).\nАко сте овде дошли грешком, вратите се на претходну страницу.",
        "anontalkpagetext": "----\n<em>Ово је страница за разговор с анонимним корисником који још нема налог или га не користи.</em>\nЗбог тога морамо да користимо бројчану IP адресу како бисмо га препознали.\nТакву адресу може делити више корисника.\nАко сте анонимни корисник и мислите да су вам упућене примедбе, [[Special:CreateAccount|отворите налог]] или се [[Special:UserLogin|пријавите]] да бисте избегли будућу забуну с осталим анонимним корисницима.",
-       "noarticletext": "Ð\9dа Ð¾Ð²Ð¾Ñ\98 Ñ\81Ñ\82Ñ\80аниÑ\86и Ñ\82Ñ\80енÑ\83Ñ\82но Ð½ÐµÐ¼Ð° Ñ\81адÑ\80жаÑ\98а.\nМожете [[Special:Search/{{PAGENAME}}|потражити овај наслов]] на другим страницама,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} претражити сродне извештаје] или [{{fullurl:{{FULLPAGENAME}}|action=edit}} направити ову страницу]</span>.",
-       "noarticletext-nopermission": "Ð\9dа Ð¾Ð²Ð¾Ñ\98 Ñ\81Ñ\82Ñ\80аниÑ\86и Ñ\82Ñ\80енÑ\83Ñ\82но Ð½ÐµÐ¼Ð° Ñ\81адÑ\80жаÑ\98а.\nМожете [[Special:Search/{{PAGENAME}}|потражити овај наслов]] на другим страницама или <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} претражити сродне дневнике]</span>, али немате дозволу да направите ову страницу.",
+       "noarticletext": "Ð\9dа Ð¾Ð²Ð¾Ñ\98 Ñ\81Ñ\82Ñ\80аниÑ\86и Ñ\82Ñ\80енÑ\83Ñ\82но Ð½ÐµÐ¼Ð° Ñ\82екÑ\81Ñ\82а.\nМожете [[Special:Search/{{PAGENAME}}|потражити овај наслов]] на другим страницама,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} претражити сродне извештаје] или [{{fullurl:{{FULLPAGENAME}}|action=edit}} направити ову страницу]</span>.",
+       "noarticletext-nopermission": "Ð\9dа Ð¾Ð²Ð¾Ñ\98 Ñ\81Ñ\82Ñ\80аниÑ\86и Ñ\82Ñ\80енÑ\83Ñ\82но Ð½ÐµÐ¼Ð° Ñ\82екÑ\81Ñ\82а.\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Размислите да ли заиста желите да направите/уредите ову страницу.",
        "userpage-userdoesnotexist-view": "Кориснички налог „$1“ није отворен.",
        "viewpagelogs": "Дневници ове странице",
        "nohistory": "Не постоји историја измена ове странице.",
        "currentrev": "Тренутна измена",
-       "currentrev-asof": "Ð\9fоÑ\81ледÑ\9aа измена на датум $2 у $3",
+       "currentrev-asof": "ТÑ\80енÑ\83Ñ\82на измена на датум $2 у $3",
        "revisionasof": "Измена на датум $2 у $3",
        "revision-info": "Измена од $1 од стране {{GENDER:$6|корисника $2|кориснице $2}}$7",
        "previousrevision": "← Старија измена",
        "next-page": "следећа страница",
        "prevn-title": "$1 {{PLURAL:$1|претходни  резултат|претходна резултата|претходних резултата}}",
        "nextn-title": "$1 {{PLURAL:$1|следећи резултат|следећа резултата|следећих резултата}}",
-       "shown-title": "Прикажи $1 {{PLURAL:$1|резултат|резултата|резултата}} по страници",
+       "shown-title": "Прикажи $1 {{PLURAL:$1|резултат|резултата}} по страници",
        "viewprevnext": "Погледај ($1 {{int:pipe-separator}} $2) ($3).",
        "searchmenu-exists": "<strong>Постоји страница под називом „[[:$1]]”!</strong> {{PLURAL:$2|0=|Такође погледајте друге пронађене резултате претраге.}}",
-       "searchmenu-new": "<strong>Направите страницу „[[:$1]]”!</strong> {{PLURAL:$2|0=|Такође погледајте резултат претраге.|Такође погледајте резултате претраге.}}",
+       "searchmenu-new": "<strong>Направите страницу „[[:$1]]” на овом викију!</strong> {{PLURAL:$2|0=|Такође погледајте страницу пронађену претрагом.|Такође погледајте резултате претраге.}}",
        "searchprofile-articles": "Странице са садржајем",
        "searchprofile-images": "Датотеке",
        "searchprofile-everything": "Све",
        "searchprofile-images-tooltip": "Претражите датотеке",
        "searchprofile-everything-tooltip": "Претражите сав садржај (укључујући странице за разговор)",
        "searchprofile-advanced-tooltip": "Претражите прилагођене именске просторе",
-       "search-result-size": "$1 ({{PLURAL:$2|1 реч|$2 речи|$2 речи}})",
+       "search-result-size": "$1 ({{PLURAL:$2|1 реч|$2 речи}})",
        "search-result-category-size": "{{PLURAL:$1|1 члан|$1 члана|$1 чланова}}, ({{PLURAL:$2|1 поткатегорија|$2 поткатегорије|$2 поткатегорија}}, {{PLURAL:$3|1 датотека|$3 датотеке|$3 датотека}})",
-       "search-redirect": "(преусмерено са $1)",
+       "search-redirect": "(преусмерење са $1)",
        "search-section": "(одељак $1)",
        "search-category": "(категорија $1)",
        "search-file-match": "(подудара се садржај датотеке)",
-       "search-suggest": "Да ли сте мислили на: $1",
+       "search-suggest": "Да ли сте мислили: $1",
        "search-rewritten": "Приказани резултати за $1. Ипак претражи $2.",
        "search-interwiki-caption": "Резултати са сестринских пројеката",
        "search-interwiki-default": "Резултати са $1:",
        "showingresults": "Испод {{PLURAL:$1|је приказан <strong>1</strong> резултат|су приказана <strong>$1</strong> резултата|је приказано <strong>$1</strong> резултата}}, почев од броја <strong>$2</strong>.",
        "showingresultsinrange": "Испод {{PLURAL:$1|је приказан <strong>1</strong> резултат|су приказана <strong>$1</strong> резултата|је приказано <strong>$1</strong> резултата}}, у распону од <strong>$2</strong> до <strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Резултат <strong>$1</strong> од <strong>$3</strong>|Резултати <strong>$1—$2</strong> од <strong>$3</strong>}}",
-       "search-nonefound": "Ð\9dема Ð¿Ð¾ÐºÐ»Ð°Ð¿Ð°Ñ\9aа.",
+       "search-nonefound": "Ð\9dиÑ\81Ñ\83 Ð¿Ñ\80онаÑ\92ени Ñ\80езÑ\83лÑ\82аÑ\82и ÐºÐ¾Ñ\98и Ð¾Ð´Ð³Ð¾Ð²Ð°Ñ\80аÑ\98Ñ\83 Ñ\83пиÑ\82Ñ\83.",
        "search-nonefound-thiswiki": "Нема резултата на овом сајту који се поклапају са термином претраге.",
        "powersearch-legend": "Напредна претрага",
        "powersearch-ns": "Претрага по именским просторима:",
        "right-bot": "сматрање измена као аутоматски процес",
        "right-nominornewtalk": "непоседовање мањих измена на страницама за разговор отвара прозор за нове поруке",
        "right-apihighlimits": "коришћење виших граница за упите из API-ја",
-       "right-writeapi": "пиÑ\81аÑ\9aе API-ја",
+       "right-writeapi": "могÑ\83Ñ\9bноÑ\81Ñ\82 Ð¿Ð¸Ñ\81аÑ\9aа API-ја",
        "right-delete": "брисање страница",
        "right-bigdelete": "брисање страница с великом историјом",
        "right-deletelogentry": "брисање и враћање одређених ставки у дневнику",
        "recentchanges-label-plusminus": "Промена величине странице у бајтовима",
        "recentchanges-legend-heading": "<strong>Легенда:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (такође погледајте [[Special:NewPages|списак нових страница]])",
+       "recentchanges-legend-plusminus": "(<em>±123</em>)",
        "recentchanges-submit": "Прикажи",
        "rcfilters-tag-remove": "Уклоните филтер „$1“",
        "rcfilters-legend-heading": "<strong>Списак скраћеница:</strong>",
        "rcfilters-target-page-placeholder": "Унесите име странице (или категорије)",
        "rcnotefrom": "Испод {{PLURAL:$5|је измена|су измене}} од <strong>$3, $4</strong> (до <strong>$1</strong> приказано).",
        "rclistfromreset": "Ресетуј одабир датума",
-       "rclistfrom": "Прикажи нове измене почев од $2, $3",
+       "rclistfrom": "Прикажи нове измене почев од $3 у $2",
        "rcshowhideminor": "$1 мање измене",
        "rcshowhideminor-show": "Прикажи",
        "rcshowhideminor-hide": "Сакриј",
        "recentchangeslinked-feed": "Сродне измене",
        "recentchangeslinked-toolbox": "Сродне измене",
        "recentchangeslinked-title": "Сродне измене са „$1“",
-       "recentchangeslinked-summary": "УнеÑ\81иÑ\82е Ð¸Ð¼Ðµ Ñ\81Ñ\82Ñ\80аниÑ\86е Ð´Ð° Ð±Ð¸Ñ\81Ñ\82е Ð²Ð¸Ð´ÐµÐ»Ð¸ Ð¿Ñ\80омене Ð½Ð° Ñ\81Ñ\82Ñ\80аниÑ\86ама ÐºÐ¾Ñ\98е Ñ\81Ñ\83 Ð¿Ð¾Ð²ÐµÐ·Ð°Ð½Ðµ Ñ\81а Ð¸Ð»Ð¸ Ñ\81а Ñ\82е Ñ\81Ñ\82Ñ\80аниÑ\86е. (Ð\94а Ð±Ð¸Ñ\81Ñ\82е Ð²Ð¸Ð´ÐµÐ»Ð¸ Ñ\87ланове ÐºÐ°Ñ\82егоÑ\80иÑ\98е, Ñ\83неÑ\81иÑ\82е {{ns:category}}:Ð\98ме ÐºÐ°Ñ\82егоÑ\80иÑ\98е). Ð\9fÑ\80омене Ð½Ð° Ñ\81Ñ\82Ñ\80аниÑ\86ама ÐºÐ¾Ñ\98е Ñ\81Ñ\83 на [[Special:Watchlist|Вашем списку надгледања]] су <strong>подебљане</strong>.",
+       "recentchangeslinked-summary": "УнеÑ\81иÑ\82е Ð¸Ð¼Ðµ Ñ\81Ñ\82Ñ\80аниÑ\86е Ð´Ð° Ð±Ð¸Ñ\81Ñ\82е Ð²Ð¸Ð´ÐµÐ»Ð¸ Ð¸Ð·Ð¼ÐµÐ½Ðµ Ñ\81Ñ\82Ñ\80аниÑ\86а ÐºÐ¾Ñ\98е Ñ\81Ñ\83 Ð¿Ð¾Ð²ÐµÐ·Ð°Ð½Ðµ Ñ\81а Ð¸Ð»Ð¸ Ñ\81а Ñ\82е Ñ\81Ñ\82Ñ\80аниÑ\86е. (Ð\94а Ð±Ð¸Ñ\81Ñ\82е Ð²Ð¸Ð´ÐµÐ»Ð¸ Ñ\87ланове ÐºÐ°Ñ\82егоÑ\80иÑ\98е, Ñ\83неÑ\81иÑ\82е {{ns:category}}:Ð\98ме ÐºÐ°Ñ\82егоÑ\80иÑ\98е). Ð\98змене Ñ\81Ñ\82Ñ\80аниÑ\86а на [[Special:Watchlist|Вашем списку надгледања]] су <strong>подебљане</strong>.",
        "recentchangeslinked-page": "Назив странице:",
        "recentchangeslinked-to": "Прикажи измене страница које су повезане с датом страницом",
        "recentchanges-page-added-to-category": "[[:$1]] је додата у категорију",
        "upload-curl-error28": "Отпремање је истекло",
        "upload-curl-error28-text": "Сервер не одговара на упит.\nПроверите да ли сајт ради, мало осачекајте и покушајте поново.\nПробајте касније када буде мање оптерећење.",
        "license": "Лиценца:",
-       "license-header": "Ð\9bиÑ\86енÑ\86а:",
+       "license-header": "Ð\9bиÑ\86енÑ\86иÑ\80аÑ\9aе",
        "nolicense": "Није изабрано",
        "licenses-edit": "Уреди избор лиценци",
        "license-nopreview": "(преглед није доступан)",
        "apisandbox-multivalue-all-namespaces": "$1 (сви именски простори)",
        "apisandbox-multivalue-all-values": "$1 (све вредности)",
        "booksources": "Штампани извори",
-       "booksources-search-legend": "ТÑ\80ажи ÐºÑ\9aижевне изворе",
+       "booksources-search-legend": "Ð\9fÑ\80еÑ\82Ñ\80ажи Ñ\88Ñ\82ампане изворе",
        "booksources-isbn": "ISBN:",
        "booksources-search": "Претражи",
        "booksources-text": "Испод се налази списак веза ка сајтовима који се баве продајом нових и половних књига, а који би могли имати додатне податке о књигама које тражите:",
        "watchnologin": "Нисте пријављени",
        "addwatch": "Додај на списак надгледања",
        "addedwatchtext": "Страница „[[:$1]]“ и њена страница за разговор је додата на Ваш [[Special:Watchlist|списак надгледања]].",
+       "addedwatchtext-talk": "Страница „[[:$1]]” и њена придружена страница је додата на Ваш [[Special:Watchlist|списак надгледања]]",
        "addedwatchtext-short": "Страница „$1“ је додата на Ваш списак надгледања.",
        "removewatch": "Уклони са списка надгледања",
-       "removedwatchtext": "Страница „[[:$1]]“ и њена страница за разговор је уклоњена с вашег [[Special:Watchlist|списка надгледања]].",
+       "removedwatchtext": "Страница „[[:$1]]“ и њена страница за разговор је уклоњена са Вашег [[Special:Watchlist|списка надгледања]].",
        "removedwatchtext-short": "Страница „$1“ је уклоњена с вашег списка надгледања.",
        "watch": "Надгледај",
        "watchthispage": "Надгледај ову страницу",
        "wlshowhidemine": "моје измене",
        "wlshowhidecategorization": "категоризацију страница",
        "watchlist-options": "Опције списка надгледања",
-       "watching": "Надгледање…",
-       "unwatching": "УклаÑ\9aаÑ\9aе Ñ\81а Ñ\81пиÑ\81ка Ð½Ð°Ð´Ð³Ð»ÐµÐ´Ð°Ñ\9aа...",
+       "watching": "Надгледам…",
+       "unwatching": "Ð\9fÑ\80еÑ\81Ñ\82аÑ\98ем Ð´Ð° Ð½Ð°Ð´Ð³Ð»ÐµÐ´Ð°Ð¼...",
        "watcherrortext": "Дошло је до грешке при промени поставки вашег списка надгледања за „$1“.",
        "enotif_reset": "Означи све странице као посећене",
        "enotif_impersonal_salutation": "{{SITENAME}} корисник",
        "dellogpage": "Дневник брисања",
        "dellogpagetext": "Испод је списак последњих брисања.",
        "deletionlog": "дневник брисања",
+       "logentry-create-create": "$1 је {{GENDER:$2|направио|направила}} страницу $3",
        "reverted": "Враћено на ранију измену",
        "deletecomment": "Разлог:",
        "deleteotherreason": "Други/додатни разлог:",
        "logentry-contentmodel-change-revert": "врати",
        "protectlogpage": "Дневник заштите",
        "protectlogtext": "Испод је списак заштићених страница.\nПогледајте [[Special:ProtectedPages|списак заштићених страница]] за више детаља.",
-       "protectedarticle": "је заштитио „[[$1]]“",
-       "modifiedarticleprotection": "промењен степен заштите за „[[$1]]“",
+       "protectedarticle": "је {{GENDER:|заштитио|заштитила}} страницу „[[$1]]“",
+       "modifiedarticleprotection": "је {{GENDER:|променио|променила}} степен заштите странице „[[$1]]“",
        "unprotectedarticle": "је скинуо заштиту са странице „[[$1]]“",
        "movedarticleprotection": "је преместио подешавања заштите са „[[$2]]“ на „[[$1]]“",
        "protectedarticle-comment": "{{GENDER:$2|Заштићена}} страница [[$1]]",
        "contribsub2": "За {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "Кориснички налог „$1“ није отворен.",
        "nocontribs": "Нема измена које одговарају наведеним критеријумима.",
-       "uctop": "(последња)",
+       "uctop": "(тренутна)",
        "month": "од месеца (и раније):",
        "year": "од године (и раније):",
        "sp-contributions-newbies": "Прикажи само доприносе нових корисника",
        "blocklogpage": "Дневник блокирања",
        "blocklog-showlog": "{{GENDER:$1|Овај корисник је раније блокиран|Ова корисница је раније блокирана}}.\nИсторија блокирања се налази испод:",
        "blocklog-showsuppresslog": "{{GENDER:$1|Овај корисник је раније блокиран и сакривен|Ова корисница је раније блокирана и сакривена}}.\nИсторија сакривања се налази испод:",
-       "blocklogentry": "је блокирао [[$1]] с роком истицања од $2 $3",
-       "reblock-logentry": "{{GENDER:|је променио|је променила|је променио}} подешавања за блокирање {{GENDER:$1|корисника|кориснице|корисника}} [[$1]] с роком истека од $2 ($3)",
+       "blocklogentry": "је блокирао [[$1]] са временом истицања од $2 $3",
+       "reblock-logentry": "{{GENDER:|је променио|је променила}} подешавања за блокирање {{GENDER:$1|корисника|кориснице}} [[$1]] са временом истека од $2 ($3)",
        "blocklogtext": "Ово је дневник блокирања и деблокирања корисника.\nАутоматски блокиране ИП адресе нису наведене.\nТекуће забране и блокирања можете наћи [[Special:BlockList|овде]].",
        "unblocklogentry": "је деблокирао $1",
        "block-log-flags-anononly": "само анонимни корисници",
        "tooltip-pt-login-private": "Морате да се пријавите да бисте користили овај Вики",
        "tooltip-pt-logout": "Одјавите се",
        "tooltip-pt-createaccount": "Предлажемо Вам да отворите налог и пријавите се, иако то није обавезно",
-       "tooltip-ca-talk": "Разговор о садржају",
+       "tooltip-ca-talk": "Разговор о страници са садржајем",
        "tooltip-ca-edit": "Уредите ову страницу",
        "tooltip-ca-addsection": "Започните нови одељак",
        "tooltip-ca-viewsource": "Ова страница је закључана. \nМожете да погледате њен изворни кôд",
        "tooltip-ca-delete": "Обришите ову страницу",
        "tooltip-ca-undelete": "Врати измене направљене на овој страници пре него што буде обрисана",
        "tooltip-ca-move": "Премести ову страницу",
-       "tooltip-ca-watch": "Додајте ову страницу на списак надгледања",
+       "tooltip-ca-watch": "Ð\94одаÑ\98Ñ\82е Ð¾Ð²Ñ\83 Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 Ð½Ð° Ñ\81воÑ\98 Ñ\81пиÑ\81ак Ð½Ð°Ð´Ð³Ð»ÐµÐ´Ð°Ñ\9aа",
        "tooltip-ca-unwatch": "Уклоните ову страницу са списка надгледања",
        "tooltip-search": "Претражите пројекат {{SITENAME}}",
        "tooltip-search-go": "Идите на страницу са тачно овим именом ако постоји",
        "tooltip-n-mainpage": "Посетите главну страну",
        "tooltip-n-mainpage-description": "Посетите главну страну",
        "tooltip-n-portal": "О пројекту, шта можете да радите и где да пронађете ствари",
-       "tooltip-n-currentevents": "Пронађите додатне информације о тренутним догађајима",
+       "tooltip-n-currentevents": "Пронађите додатне информације о актуелностима",
        "tooltip-n-recentchanges": "Списак скорашњих измена на викију",
        "tooltip-n-randompage": "Учитајте случајну страницу",
        "tooltip-n-help": "Место где можете да научите нешто",
        "tooltip-t-permalink": "Трајна веза ка овој измени странице",
        "tooltip-ca-nstab-main": "Погледајте страницу са садржајем",
        "tooltip-ca-nstab-user": "Погледајте корисничку страницу",
-       "tooltip-ca-nstab-media": "Погледајте мултимедијалну датотеку",
+       "tooltip-ca-nstab-media": "Погледајте медијску страницу",
        "tooltip-ca-nstab-special": "Ово је посебна страница. Не можете је мењати.",
        "tooltip-ca-nstab-project": "Погледајте страницу пројекта",
-       "tooltip-ca-nstab-image": "Прикажи страницу датотеке",
+       "tooltip-ca-nstab-image": "Погледајте страницу датотеке",
        "tooltip-ca-nstab-mediawiki": "Погледајте системску поруку",
        "tooltip-ca-nstab-template": "Погледајте шаблон",
        "tooltip-ca-nstab-help": "Погледајте страницу за помоћ",
-       "tooltip-ca-nstab-category": "Ð\9fогледаÑ\98Ñ\82е Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 ÐºÐ°Ñ\82егоÑ\80иÑ\98а",
+       "tooltip-ca-nstab-category": "Ð\9fогледаÑ\98Ñ\82е Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83 ÐºÐ°Ñ\82егоÑ\80иÑ\98е",
        "tooltip-minoredit": "Означите као мању измену",
        "tooltip-save": "Сачувајте своје измене",
        "tooltip-publish": "Објавите своје измене",
        "tooltip-preview": "Прегледајте своје измене. Користите ово дугме пре чувања.",
-       "tooltip-diff": "Погледајте које измене сте направили на тексту",
+       "tooltip-diff": "Погледајте које измене сте направили у тексту",
        "tooltip-compareselectedversions": "Погледаjте разлике између две изабране измене ове странице.",
        "tooltip-watch": "Додајте ову страницу на свој списак надгледања",
        "tooltip-watchlistedit-normal-submit": "Уклоните наслове",
        "tooltip-watchlistedit-raw-submit": "Ажурирај списак",
        "tooltip-recreate": "Поново направите страницу иако је обрисана",
        "tooltip-upload": "Започните отпремање",
-       "tooltip-rollback": "â\80\9eÐ\92Ñ\80аÑ\82иâ\80\9c Ð²Ñ\80аÑ\9bа Ð¸Ð·Ð¼ÐµÐ½Ðµ Ð¿Ð¾Ñ\81ледÑ\9aег ÐºÐ¾Ñ\80иÑ\81ника једним кликом",
-       "tooltip-undo": "Опција „поништи” враћа ову измену и отвара образац за уређивање у претпрегледном моду. Омогућава додавање разлога у опису измене.",
+       "tooltip-rollback": "â\80\9eÐ\92Ñ\80аÑ\82иâ\80\9c Ð²Ñ\80аÑ\9bа Ð¸Ð·Ð¼ÐµÐ½Ðµ Ð¿Ð¾Ñ\81ледÑ\9aег Ð´Ð¾Ð¿Ñ\80иноÑ\81иоÑ\86а Ð¾Ð²Ðµ Ñ\81Ñ\82Ñ\80аниÑ\86е једним кликом",
+       "tooltip-undo": "„Поништи” враћа ову измену и отвара образац за уређивање у претпрегледном моду. Дозвољава додавање разлога у опису измене.",
        "tooltip-preferences-save": "Сачувај подешавања",
        "tooltip-summary": "Унесите кратак опис",
        "interlanguage-link-title": "$1 — $2",
        "spam_blanking": "Све измене садрже везе до $1. Чистим",
        "spam_deleting": "Све измене садрже везе до $1. Бришем",
        "simpleantispam-label": "Анти-спам провера. \n<strong>Не</strong> попуњавај ово унутра!",
-       "pageinfo-title": "Ð\9fодаÑ\86и Ð¾ „$1“",
+       "pageinfo-title": "Ð\98нÑ\84оÑ\80маÑ\86иÑ\98е Ð·Ð° „$1“",
        "pageinfo-not-current": "Нажалост, немогуће је прибавити ове податке за старије измене.",
-       "pageinfo-header-basic": "Ð\9eÑ\81новни Ð¿Ð¾Ð´Ð°Ñ\86и",
+       "pageinfo-header-basic": "Ð\9eÑ\81новне Ð¸Ð½Ñ\84оÑ\80маÑ\86иÑ\98е",
        "pageinfo-header-edits": "Историја измена",
        "pageinfo-header-restrictions": "Заштита странице",
        "pageinfo-header-properties": "Својства странице",
        "pageinfo-robot-noindex": "Није дозвољено",
        "pageinfo-watchers": "Број надгледача странице",
        "pageinfo-visiting-watchers": "Број надгледача странице који су посетили скорашње измене",
-       "pageinfo-few-watchers": "Ð\9cаÑ\9aе Ð¾Ð´ $1 {{PLURAL:$1|пÑ\80аÑ\82иоÑ\86а|пÑ\80аÑ\82иоÑ\86а|пÑ\80аÑ\82илаÑ\86а}}",
+       "pageinfo-few-watchers": "Ð\9cаÑ\9aе Ð¾Ð´ $1 {{PLURAL:$1|надгледаÑ\87а}}",
        "pageinfo-redirects-name": "Број преусмерења на ову страницу",
        "pageinfo-redirects-value": "$1",
-       "pageinfo-subpages-name": "Ð\9fодÑ\81Ñ\82Ñ\80аниÑ\86е ове странице",
+       "pageinfo-subpages-name": "Ð\91Ñ\80оÑ\98 Ð¿Ð¾Ð´Ñ\81Ñ\82Ñ\80аниÑ\86а ове странице",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|преусмерење|преусмерења|преусмерења}}; $3 {{PLURAL:$3|непреусмерење|непреусмерења|непреусмерења}})",
        "pageinfo-firstuser": "Аутор странице",
        "pageinfo-firsttime": "Датум стварања странице",
        "svg-long-desc": "SVG датотека, номинално $1 × $2 пиксела, величина: $3",
        "svg-long-desc-animated": "Анимирана SVG датотека, номинално: $1 × $2 пиксела, величина: $3",
        "svg-long-error": "Неисправна SVG датотека: $1",
-       "show-big-image": "Ð\9fÑ\83на Ð²ÐµÐ»Ð¸Ñ\87ина",
+       "show-big-image": "Ð\98звоÑ\80на Ð´Ð°Ñ\82оÑ\82ека",
        "show-big-image-preview": "Величина овог приказа: $1.",
        "show-big-image-preview-differ": "Величина $3 прегледа за ову $2 датотеку је $1.",
        "show-big-image-other": "$2 {{PLURAL:$2|друга резолуција|друге резолуције|других резолуција}}: $1.",
        "htmlform-title-not-exists": "$1 не постоји.",
        "htmlform-user-not-exists": "<strong>$1</strong> не постоји.",
        "htmlform-user-not-valid": "<strong>$1</strong> није исправно корисничко име.",
-       "logentry-delete-delete": "$1 је {{GENDER:$2|обрисао|обрисала|обрисао}} страницу $3",
+       "logentry-delete-delete": "$1 је {{GENDER:$2|обрисао|обрисала}} страницу $3",
        "logentry-delete-delete_redir": "$1 је {{GENDER:$2|обрисао|обрисала}} преусмерење $3 преписивањем",
        "logentry-delete-restore": "$1 је {{GENDER:$2|вратио|вратила}} страницу $3 ($4)",
        "logentry-delete-restore-nocount": "$1 је {{GENDER:$2|вратио|вратила}} страницу $3",
index b1c1bc3..65da0bd 100644 (file)
@@ -31,7 +31,8 @@
                        "Bugoslav",
                        "Acamicamacaraca",
                        "Fitoschido",
-                       "BadDog"
+                       "BadDog",
+                       "Vlad5250"
                ]
        },
        "tog-underline": "Podvlačenje veza:",
        "right-editsemiprotected": "uređivanje stranica pod zaštitom „{{int:protect-level-autoconfirmed}}“",
        "right-editinterface": "uređivanje korisničkog okruženja",
        "right-editusercss": "uređivanje tuđih CSS datoteka",
+       "right-edituserjson": "uređivanje tuđih JSON datoteka",
        "right-edituserjs": "uređivanje tuđih JavaScript datoteka",
        "right-editmyusercss": "uređivanje sopstvenih CSS datoteka",
+       "right-editmyuserjson": "uređivanje sopstvenih JSON datoteka",
        "right-editmyuserjs": "uređivanje sopstvenih JavaScript datoteka",
        "right-viewmywatchlist": "vidi sopstveni spisak nadgledanja",
        "right-editmywatchlist": "uređivanje sopstvenog spiska nadgledanja; neke preduzete radnje će svejedno dodati stranice na spisak i bez ovog prava",
        "dellogpage": "Dnevnik brisanja",
        "dellogpagetext": "Ispod je spisak poslednjih brisanja.",
        "deletionlog": "dnevnik brisanja",
+       "logentry-create-create": "$1 {{GENDER:$2|kreirao je|kreirala je}} stranicu $3",
        "reverted": "Vraćeno na raniju izmenu",
        "deletecomment": "Razlog:",
        "deleteotherreason": "Drugi/dodatni razlog:",
index 9e5717c..d48650d 100644 (file)
@@ -21,7 +21,8 @@
                        "Uostofchuodnego",
                        "Przem(1)s",
                        "Fitoschido",
-                       "Matěj Suchánek"
+                       "Matěj Suchánek",
+                       "Vlad5250"
                ]
        },
        "tog-underline": "Podsztrychniyniy linkōw:",
        "right-editprotected": "Sprowjej zawarte zajty (ze zawarćym kaskadowym)",
        "right-editinterface": "Sprowjej interfejs użytkowńika",
        "right-editusercss": "Sprowjej pliki CSS inkszych użytkowńikůw",
+       "right-edituserjson": "Sprowjej pliki JSON inkšych užytkowńikůw",
        "right-edituserjs": "Sprowjej pliki JS inkšych užytkowńikůw",
        "right-rollback": "Rewert drap sprawjyńo uostatńygo użytkowńika kery sprowjoł dano zajta",
        "right-markbotedits": "Uoznocz rewertowane sprawjyńo kej sprawjyńo botůw",
index efd014d..9d131fd 100644 (file)
        "recentchangesdays": "ఇటీవలి మార్పులు లో చూపించవలసిన రోజులు:",
        "recentchangesdays-max": "గరిష్ఠంగా $1 {{PLURAL:$1|రోజు|రోజులు}}",
        "recentchangescount": "అప్రమేయంగా చూపించాల్సిన దిద్దుబాట్ల సంఖ్య:",
-       "prefs-help-recentchangescount": "à°\87ది à°\87à°\9fà±\80వలి à°®à°¾à°°à±\8dà°ªà±\81à°²à±\81, à°ªà±\87à°\9cà±\80 à°\9aà°°à°¿à°¤à±\8dà°°à°²à±\81, à°®à°°à°¿à°¯à±\81 à°\9aà°¿à°\9fà±\8dà°\9fాలà°\95à±\81 à°µà°°à±\8dతిసà±\8dà°¤à±\81à°\82ది.",
-       "prefs-help-watchlist-token2": "మీ వీక్షణజాబితా యొక్క జాలవడ్డింపుకు చెందిన రహస్య తాళమిది.\nఈ తాళం తెలిసిన ఎవరైనా మీ వీక్షణజాబితాను చదవగలుగుతారు. అందుచేత దీన్ని ఎవరికీ ఇవ్వకండి.\n[[Special:ResetTokens|దాన్ని మార్చాలంటే ఇక్కడ నొక్కండి]].",
+       "prefs-help-recentchangescount": "à°\97à°°à°¿à°·à±\8dà°  à°¸à°\82à°\96à±\8dà°¯: 1000",
+       "prefs-help-watchlist-token2": "మీ వీక్షణజాబితా యొక్క జాలవడ్డింపుకు చెందిన రహస్య తాళమిది.\nఈ తాళం తెలిసిన ఎవరైనా మీ వీక్షణజాబితాను చదవగలుగుతారు. అందుచేత దీన్ని ఎవరికీ ఇవ్వకండి.\nఅవసరమైతే [[Special:ResetTokens|దాన్ని మార్చుకోవచ్చు]].",
        "savedprefs": "మీ అభిరుచులను భద్రపరిచాం.",
        "savedrights": "{{GENDER:$1|$1}} వాడుకరి గుంపులు భద్రమయ్యాయి.",
        "timezonelegend": "కాల మండలం:",
        "right-siteadmin": "డేటాబేసును లాక్, అన్‌లాక్ చెయ్యి",
        "right-override-export-depth": "5 లింకుల లోతు వరకు ఉన్న పేజీలతో సహా, పేజీలను ఎగుమతి చెయ్యి",
        "right-sendemail": "ఇతర వాడుకరులకు ఈ-మెయిలు పంపించడం",
-       "right-managechangetags": "డేటాబేసులో [[Special:Tags|ట్యాగుల]]ను సృష్టించడం, తొలగించడం",
+       "right-managechangetags": "[[Special:Tags|ట్యాగుల]]ను సృష్టించడం, (అ)చేతనం చెయ్యడం",
        "right-applychangetags": "తన మార్పులతో [[Special:Tags|ట్యాగుల]]ను ఆపాదించడం",
        "right-changetags": "విడి కూర్పులకు, చిట్టా పద్దులకు ఏవైనా [[Special:Tags|ట్యాగుల]]ను చేర్చడం, తొలగించడం",
-       "right-deletechangetags": "[[ప్రత్యేక:Tags|ట్యాగులను]] డేటాబేసు నుండి తొలగించు",
+       "right-deletechangetags": "[[Special:Tags|ట్యాగులను]] డేటాబేసు నుండి తొలగించు",
        "grant-generic": "\"$1\" హక్కుల కట్ట",
        "grant-group-email": "ఈమెయిలు పంపించడం",
        "grant-group-administration": "నిర్వాహక చర్యలు చేపట్టడం",
        "pageswithprop-prophidden-binary": "binary లక్షణం విలువ దాచబడింది ($1)",
        "doubleredirects": "జంట దారిమార్పులు",
        "doubleredirectstext": "ఇతర దారిమార్పు పుటలకి తీసుకెళ్ళే దారిమార్పులని ఈ పుట చూపిస్తుంది.\nప్రతీ వరుసలో మొదటి మరియు రెండవ దారిమార్పులకు లంకెలు, ఆలానే రెండవ దారిమార్పు పుట యొక్క లక్ష్యం ఉన్నాయి. సాధారణంగా ఈ రెండవ దారిమార్పు యొక్క లక్ష్యమే \"అసలైనది\", అదే మొదటి దారిమార్పు యొక్క లక్ష్యంగా ఉండాలి.\n<del>కొట్టివేయబడిన</del> పద్దులు పరిష్కరించబడ్డవి.",
-       "double-redirect-fixed-move": "[[$1]]ని తరలించారు, అది ప్రస్తుతం [[$2]]కి దారిమార్పు.",
+       "double-redirect-fixed-move": "[[$1]]ని తరలించారు.\nదాన్ని ఆటోమేటిగ్గా తాజాకరించాం. ప్రస్తుతం అది [[$2]]కి దారిమార్పు చేస్తోంది.",
        "double-redirect-fixed-maintenance": "[[$1]] కు జమిలి దారిమార్పును [[$2]] కు అప్రమేయంగా సరిచేస్తున్నాం.",
        "double-redirect-fixer": "దారిమార్పు సరిద్దువారు",
        "brokenredirects": "తెగిపోయిన దారిమార్పులు",
        "dellogpage": "తొలగింపుల చిట్టా",
        "dellogpagetext": "ఇది ఇటీవలి తుడిచివేతల జాబితా.",
        "deletionlog": "తొలగింపుల చిట్టా",
+       "logentry-create-create": "$3 పేజీని $1 {{GENDER:$2|సృష్టించారు}}",
        "reverted": "పాత కూర్పుకు తీసుకువెళ్ళాం.",
        "deletecomment": "కారణం:",
        "deleteotherreason": "ఇతర/అదనపు కారణం:",
        "newimages-summary": "ఇటీవలే ఎగుమతైన ఫైళ్ళను ఈ ప్రత్యేక పేజీ చూపిస్తుంది.",
        "newimages-legend": "పడపోత",
        "newimages-label": "ఫైలుపేరు (లేదా దానిలోని భాగం):",
+       "newimages-user": "ఐపీ చిరునామా లేదా వాడుకరి పేరు",
+       "newimages-newbies": "కొత్త ఖాతాల రచనలని మాత్రమే చూపించు",
        "newimages-showbots": "బాట్లు చేసిన అప్లోడ్లు చూపించు",
        "newimages-mediatype": "మాధ్యమ రకం:",
        "noimages": "చూసేందుకు ఏమీ లేదు.",
        "compare-title-not-exists": "మీరు పేర్కొన్న శీర్షిక లేనే లేదు.",
        "compare-revision-not-exists": "మీరు పేర్కొన్న కూర్పు లేనే లేదు.",
        "diff-form": "తేడాలు",
+       "permanentlink": "స్థిర లంకె",
        "dberr-problems": "క్షమించండి! ఈ సైటు సాంకేతిక సమస్యలని ఎదుర్కొంటుంది.",
        "dberr-again": "కొన్ని నిమిషాలాగి మళ్ళీ ప్రయత్నించండి.",
        "dberr-info": "(డేటాబేసును చేరలేకున్నాం: $1)",
        "log-action-filter-managetags-delete": "ట్యాగు తొలగింపు",
        "log-action-filter-managetags-activate": "ట్యాగు చేతనం",
        "log-action-filter-managetags-deactivate": "ట్యాగు అచేతనం",
+       "log-action-filter-protect-protect": "సంరక్షణ",
+       "log-action-filter-upload-upload": "కొత్త ఎక్కింపు",
        "authmanager-userdoesnotexist": "వాడుకరి ఖాతా \"$1\" నమోదయి లేదు.",
        "authmanager-userlogin-remembermypassword-help": "సెషను ముగిసిన తరువాత కూడా సంకేతపదాన్ని గుర్తుంచుకోమంటారా",
        "authmanager-username-help": "ధ్రువీకరణ కోసం వాడుకరిపేరు.",
        "restrictionsfield-help": "వరుసకొక్క ఐపీ అడ్రసు లేదా CIDR శ్రేణి. ప్రతీ ఒక్కదాన్నీ చేతనం చేసేందుకు, వాడండి:<pre>0.0.0.0/0\n::/0</pre>",
        "revid": "కూర్పు $1",
        "pageid": "పేజీ ఐడీ $1",
+       "pagedata-bad-title": "చెల్లని శీర్షిక: $1.",
+       "passwordpolicies": "సంకేతపదపు విధానాలు",
+       "passwordpolicies-group": "సమూహం",
        "passwordpolicies-policies": "విధానాలు"
 }
index 45c0836..e5e01ba 100644 (file)
        "resetpass-temp-password": "Geçici parola:",
        "resetpass-abort-generic": "Parola değişikliği bir uzantı tarafından iptal edildi.",
        "resetpass-expired": "Parolanızın süresi bitti. Lütfen, giriş için yeni bir parola oluşturun.",
-       "resetpass-expired-soft": "Parolanızın süresi bitti ve değiştirilmesi gerekiyor. Lütfen, yeni bir parola seçin veya daha sonra oluşturmak için \"{{int:authprovider-resetpass-skip-label}}\" butonuna tıklayın.",
-       "resetpass-validity-soft": "Parolanız geçerli değil: $1\n\nLütfen yeni bir şifre belirleyin ya da daha sonra sıfırlamak için \"{{int:authprovider-resetpass-skip-label}}\" bağlantısını tıklayın.",
+       "resetpass-expired-soft": "Parolanızın süresi bitti ve değiştirilmesi gerekiyor. Lütfen, yeni bir parola seçin veya daha sonra değiştirmek için \"{{int:authprovider-resetpass-skip-label}}\" düğmesine tıklayın.",
+       "resetpass-validity-soft": "Parolanız geçerli değil: $1\n\nLütfen yeni bir şifre belirleyin ya da daha sonra değiştirmek için \"{{int:authprovider-resetpass-skip-label}}\" bağlantısını tıklayın.",
        "passwordreset": "Parola sıfırlama",
        "passwordreset-text-one": "Parolanızı sıfırlamak için bu formu doldurun.",
        "passwordreset-text-many": "{{PLURAL:$1|E-posta ile geçici bir parola almak için alanlardan birini doldurun.}}",
        "stub-threshold-disabled": "Devre dışı",
        "recentchangesdays": "Son değişikliklerde gösterilecek günler:",
        "recentchangesdays-max": "(en fazla $1 {{PLURAL:$1|gün|gün}})",
-       "recentchangescount": "Varsayılan olarak gösterilecek değişiklik sayısı:",
-       "prefs-help-recentchangescount": "Bu, son değişiklikleri, sayfa geçmişlerini ve günlükleri içerir.",
-       "prefs-help-watchlist-token2": "Bu izleme listenizin gizli anahtarıdır. Anahtarı bilen herkes izleme listenizi görebilir. Bu nedenle kimseyle paylaşmayın. [[Special:ResetTokens|Bu anahtarı sıfırlamak isterseniz buraya tıklayın]].",
+       "recentchangescount": "Son değişiliklerde, sayfa geçmişlerinde ve günlüklerde varsayılan olarak gösterilecek değişiklik sayısı:",
+       "prefs-help-recentchangescount": "Azami sayı: 1000",
+       "prefs-help-watchlist-token2": "Bu izleme listenizin gizli anahtarıdır.\nAnahtarı bilen herkes izleme listenizi görebilir, bu nedenle kimseyle paylaşmayın.\nEğer isterseniz, [[Special:ResetTokens|sıfırlayabilirsiniz]].",
        "savedprefs": "Tercihleriniz kaydedildi.",
        "savedrights": "{{GENDER:$1|$1}} için kullanıcı grupları kaydedildi.",
        "timezonelegend": "Zaman dilimi:",
        "prefs-files": "Dosyalar",
        "prefs-custom-css": "Özel CSS",
        "prefs-custom-js": "Özel JS",
-       "prefs-common-config": "Tüm temalar için paylaşılan CSS/JS:",
+       "prefs-common-config": "Tüm temalar için paylaşılan CSS/JSON/Javascript:",
        "prefs-reset-intro": "Bu sayfayı tercihlerinizi site varsayılanına döndürmek için kullanabilirsiniz. Bu geri alınamaz.",
        "prefs-emailconfirm-label": "E-posta doğrulaması:",
        "youremail": "E-posta:",
        "userrights-groupsmember": "İçinde olduğu gruplar:",
        "userrights-groupsmember-auto": "Saklı olarak içinde olduğu gruplar:",
        "userrights-groupsmember-type": "$1",
-       "userrights-groups-help": "Bu kullanıcının içinde olduğu grupları değiştirebilirsiniz:\n* Seçili bir kutu, kullanıcının o gruba dahil olduğunu anlatır.\n* Seçilmemiş bir kutu, kullanıcının o gruba dahil olmadığını anlatır.\n* *, grubu bir kez ekledikten sonra kaldıramayacağınızı veya tam tersi kaldırdıktan sonra ekleyemeyeceğiniz belirtir.\n* #, grubun sona erme zamanını yalnızca öne çekebileceğinizi, ileri alamayacağınızı belirtir.",
+       "userrights-groups-help": "Bu kullanıcının içinde olduğu grupları değiştirebilirsiniz:\n* Seçili bir kutu, kullanıcının o gruba dahil olduğunu anlatır.\n* Seçilmemiş bir kutu, kullanıcının o gruba dahil olmadığını anlatır.\n* *, grubu bir kez ekledikten sonra kaldıramayacağınızı veya tam tersi kaldırdıktan sonra ekleyemeyeceğiniz belirtir.\n* #, grup üyeliğinin sona erme zamanını yalnızca öne çekebileceğinizi, ileri alamayacağınızı belirtir.",
        "userrights-reason": "Neden:",
        "userrights-no-interwiki": "Diğer vikilerdeki kullanıcıların izinlerini değiştirmeye yetkiniz yok.",
        "userrights-nodatabase": "$1 veritabanı mevcut veya bölgesel değil",
        "right-siteadmin": "Veritabanını kilitle ve kilidi aç",
        "right-override-export-depth": "Sayfaları, derinlik 5'e kadar bağlantılı sayfalarla beraber, dışa aktar",
        "right-sendemail": "Diğer kullanıcılara e-posta gönder",
-       "right-managechangetags": "Veritabanında [[Special:Tags|etiket]] oluşturma veya silme",
+       "right-managechangetags": "[[Special:Tags|Etiket]] oluşturma ve (de)aktive etme",
        "right-applychangetags": "Değişiklikleriyle beraber [[Special:Tags|etiketleri]] uygula",
        "right-changetags": "Tekil sürümler ve günlük kayıtlarına rastgele [[Special:Tags|etiket]] ekleme veya çıkarma",
        "grant-group-email": "E-posta gönder",
        "grant-group-other": "Çeşitli aktivite",
        "grant-createeditmovepage": "Sayfaları oluşturma, düzenleme ve taşıma",
-       "grant-editmycssjs": "Kullanıcı CSS/JavaScript'ini düzenle",
+       "grant-editmycssjs": "Kullanıcı CSS/JSON/JavaScript'ini düzenle",
        "grant-editmyoptions": "Kullanıcı tercihlerini Düzenle",
        "grant-editmywatchlist": "İzleme listeni düzenle",
        "grant-editprotected": "Korumalı sayfaları Düzenle",
        "rcfilters-savedqueries-rename": "Yeniden adlandır",
        "rcfilters-savedqueries-setdefault": "Varsayılan olarak belirle",
        "rcfilters-savedqueries-unsetdefault": "Varsayılan olmaktan çıkar",
-       "rcfilters-savedqueries-remove": "Kaldır",
+       "rcfilters-savedqueries-remove": "Sil",
        "rcfilters-savedqueries-new-name-label": "Ad",
        "rcfilters-savedqueries-new-name-placeholder": "Süzgecin amacını tanımlayın",
        "rcfilters-savedqueries-apply-label": "Süzgeç oluştur",
        "rcfilters-empty-filter": "Etkin süzgeç bulunmuyor. Tüm katkıları gösteriliyor.",
        "rcfilters-filterlist-title": "Süzgeçler",
        "rcfilters-filterlist-whatsthis": "Bunlar nasıl çalışır?",
-       "rcfilters-filterlist-feedbacklink": "Bu (yeni) süzgeç araçları konusunda ne düşündüğünüzü bize bildirin",
+       "rcfilters-filterlist-feedbacklink": "Bu süzgeç araçları konusunda ne düşündüğünüzü bize bildirin",
        "rcfilters-highlightbutton-title": "Sonuçları vurgula",
        "rcfilters-highlightmenu-title": "Bir renk seçin",
        "rcfilters-highlightmenu-help": "Bu özelliği vurgulamak için bir renk seçin",
        "rcfilters-filterlist-noresults": "Süzgeç bulunamadı",
        "rcfilters-noresults-conflict": "Arama kriterleri çelişkili olduğu için hiçbir sonuç bulunamadı",
-       "rcfilters-filtergroup-authorship": "Düzenleme sahipliği",
+       "rcfilters-filtergroup-authorship": "Katkı sahipliği",
        "rcfilters-filter-editsbyself-label": "Senin değişiklikleriniz",
        "rcfilters-filter-editsbyself-description": "Kendi katkılarınız.",
        "rcfilters-filter-editsbyother-label": "Başkalarının düzenlemeleri",
        "rcfilters-filter-editsbyother-description": "Kendi düzenlemeleriniz hariç bütün düzenlemeler.",
-       "rcfilters-filtergroup-userExpLevel": "Deneyim düzeyi (yalnızca kayıtlı kullanıcılar için)",
+       "rcfilters-filtergroup-userExpLevel": "Kullanıcı kaydı ve deneyim",
        "rcfilters-filter-user-experience-level-registered-label": "Kayıtlı",
        "rcfilters-filter-user-experience-level-registered-description": "Oturum açmış editörler.",
        "rcfilters-filter-user-experience-level-unregistered-label": "Kayıtlı olmayan",
        "rcfilters-filter-user-experience-level-newcomer-label": "Yeni gelenler",
        "rcfilters-filter-user-experience-level-newcomer-description": "10'dan az düzenlemesi veya 4 günden az etkinliği olan kayıtlı kullanıcılar.",
        "rcfilters-filter-user-experience-level-learner-label": "Öğreniciler",
-       "rcfilters-filter-user-experience-level-learner-description": "\"Yeni gelenler\"den daha fazla gün boyunca etkinlik ve daha fazla düzenleme, ancak \"Deneyimli kullanıcılar\"dan daha az etkinlik ve düzenleme.",
+       "rcfilters-filter-user-experience-level-learner-description": "Deneyimi \"Yeni gelenler\" ve \"Deneyimli kullanıcılar\" arasındaki editörler.",
        "rcfilters-filter-user-experience-level-experienced-label": "Deneyimli kullanıcılar",
-       "rcfilters-filter-user-experience-level-experienced-description": "30'dan fazla gün etkinlik ve 500'den fazla düzenleme.",
+       "rcfilters-filter-user-experience-level-experienced-description": "30'dan fazla gün etkinliğe ve 500'den fazla düzenlemeye sahip kayıtlı editörler.",
        "rcfilters-filtergroup-automated": "Otomatikleştirilmiş katkılar",
        "rcfilters-filter-bots-label": "Bot",
        "rcfilters-filter-bots-description": "Otomatikleştirilmiş araçlar tarafından yapılan düzenlemeler.",
index 6be888c..867af79 100644 (file)
@@ -76,7 +76,8 @@
                        "Avatar6",
                        "Fitoschido",
                        "Movses",
-                       "Esk78"
+                       "Esk78",
+                       "Vlad5250"
                ]
        },
        "tog-underline": "Підкреслювання посилань:",
        "resetpass-submit-loggedin": "Змінити пароль",
        "resetpass-submit-cancel": "Скасувати",
        "resetpass-wrong-oldpass": "Неправильний тимчасовий або поточний пароль.\nМожливо, Ви вже змінили пароль або зробили запит на новий тимчасовий пароль.",
-       "resetpass-recycled": "Ð\97мÑ\96нÑ\96Ñ\82Ñ\8c Ñ\81вÑ\96й Ð¿Ð°Ñ\80олÑ\8c Ð½Ð° Ñ\89оÑ\81Ñ\8c Ñ\96нÑ\88е, Ð½Ñ\96ж Ð²аш поточний пароль.",
+       "resetpass-recycled": "Ð\97мÑ\96нÑ\96Ñ\82Ñ\8c Ñ\81вÑ\96й Ð¿Ð°Ñ\80олÑ\8c Ð½Ð° Ñ\89оÑ\81Ñ\8c Ñ\96нÑ\88е, Ð½Ñ\96ж Ð\92аш поточний пароль.",
        "resetpass-temp-emailed": "Ви ввійшли, використовуючи тимчасовий пароль, який отримали електронною поштою. \nДля завершення входу в систему необхідно задати новий пароль:",
        "resetpass-temp-password": "Тимчасовий пароль:",
        "resetpass-abort-generic": "Зміну пароля було перервано розширенням.",
        "resetpass-expired": "Термін дії вашого пароля закінчився. Будь ласка, встановіть новий пароль для входу в систему.",
-       "resetpass-expired-soft": "Термін дії вашого пароля закінчився, і тепер він повинен бути змінений. Будь ласка, виберіть новий пароль або натисніть \"{{int:authprovider-resetpass-skip-label}}\", щоб змінити його пізніше.",
-       "resetpass-validity-soft": "Ваш пароль є некоректним: $1\n\nБудь ласка, виберіть новий пароль або натисніть «{{int:authprovider-resetpass-skip-label}}», щоб скинути його пізніше.",
+       "resetpass-expired-soft": "Термін дії вашого пароля закінчився, і тепер він повинен бути змінений. Будь ласка, виберіть новий пароль або натисніть «{{int:authprovider-resetpass-skip-label}}», щоб змінити його пізніше.",
+       "resetpass-validity-soft": "Ваш пароль є некоректним: $1\n\nБудь ласка, виберіть новий пароль або натисніть «{{int:authprovider-resetpass-skip-label}}», щоб змінити його пізніше.",
        "passwordreset": "Скинути пароль",
        "passwordreset-text-one": "Заповніть цю форму, щоб отримати тимчасовий пароль електронною поштою.",
        "passwordreset-text-many": "{{PLURAL:$1|Заповніть одне з полів для отримання тимчасового пароля електронною поштою.}}",
        "converter-manual-rule-error": "Помилка в ручному правилі перетворення мови",
        "undo-success": "Редагування може бути скасовано.\nБудь ласка, перевірте порівняння нижче, щоб впевнитись, що це те, що ви хочете зробити, а потім збережіть зміни, щоб закінчити скасування редагування.",
        "undo-failure": "Неможливо скасувати редагування через несумісність проміжних змін.",
+       "undo-main-slot-only": "Редагування не може бути скасоване, бо воно включає вміст з-за меж основного слоту.",
        "undo-norev": "Редагування не може бути скасоване, бо його не існує або було вилучено.",
        "undo-nochange": "Схоже, редагування вже було скасовано.",
        "undo-summary": "Скасування редагування № $1 користувача [[Special:Contribs/$2|$2]] ([[User talk:$2|обговорення]])",
        "right-browsearchive": "пошук вилучених сторінок",
        "right-undelete": "відновлення сторінок",
        "right-suppressrevision": "перегляд, приховування та відновлення конкретних змін сторінок від будь-якого користувача",
-       "right-viewsuppressed": "Ð\9fеÑ\80еглÑ\8fд Ð·Ð¼Ñ\96н, Ð¿Ñ\80иÑ\85оваих від усіх користувачів",
+       "right-viewsuppressed": "пеÑ\80еглÑ\8fд Ð·Ð¼Ñ\96н, Ð¿Ñ\80иÑ\85ованих від усіх користувачів",
        "right-suppressionlog": "перегляд приватних журналів",
        "right-block": "заборона редагувань для інших дописувачів",
        "right-blockemail": "блокування користувача від надсилання електронної пошти",
        "right-protect": "зміна рівнів захисту та редагування каскадно захищених сторінок",
        "right-editprotected": "редагування сторінок з рівнем захисту «{{int:protect-level-sysop}}»",
        "right-editsemiprotected": "редагування сторінок з рівнем захисту «{{int:protect-level-autoconfirmed}}»",
-       "right-editcontentmodel": "Редагувати модель вмісту сторінки",
+       "right-editcontentmodel": "редагування моделі вмісту сторінки",
        "right-editinterface": "редагування інтерфейсу користувача",
        "right-editusercss": "редагування CSS-файлів інших користувачів",
        "right-edituserjson": "редагування JSON-файлів інших користувачів",
        "action-viewmywatchlist": "перегляд власного списку спостереження",
        "action-viewmyprivateinfo": "перегляд своєї приватної інформації",
        "action-editmyprivateinfo": "редагування своєї приватної інформації",
-       "action-editcontentmodel": "редагувати модель вмісту сторінки",
+       "action-editcontentmodel": "редагування моделі вмісту сторінки",
        "action-managechangetags": "створення та (де)активування міток",
        "action-applychangetags": "додавання міток разом зі змінами",
        "action-changetags": "додавання або вилучення будь-яких міток для певних версій сторінок або записів журналів",
        "rcfilters-other-review-tools": "Інші інструменти перевірки",
        "rcfilters-group-results-by-page": "Групувати результати за сторінкою",
        "rcfilters-activefilters": "Активні фільтри",
+       "rcfilters-activefilters-hide": "Приховати",
+       "rcfilters-activefilters-show": "Показати",
+       "rcfilters-activefilters-hide-tooltip": "Приховати область Активних фільтрів",
+       "rcfilters-activefilters-show-tooltip": "Показати область Активних фільтрів",
        "rcfilters-advancedfilters": "Розширені фільтри",
        "rcfilters-limit-title": "Результати, які треба показати",
        "rcfilters-limit-and-date-label": "$1 {{PLURAL:$1|зміна|зміни|змін}}, $2",
        "rcfilters-empty-filter": "Без фільтрів. Показано всі зміни.",
        "rcfilters-filterlist-title": "Фільтри",
        "rcfilters-filterlist-whatsthis": "Як це працює?",
-       "rcfilters-filterlist-feedbacklink": "Розкажіть нам, що Ви думаєте про ці (нові) інструменти фільтрування",
+       "rcfilters-filterlist-feedbacklink": "Розкажіть нам, що Ви думаєте про ці інструменти фільтрування",
        "rcfilters-highlightbutton-title": "Виділити результати",
        "rcfilters-highlightmenu-title": "Вибрати колір",
        "rcfilters-highlightmenu-help": "Вибрати колір, щоб виділити цю властивість",
        "rcfilters-watchlist-showupdated": "Зміни до сторінок, які Ви не відвідували з моменту здійснення змін, виділені <strong>жирним</strong>, із цілісними маркерами.",
        "rcfilters-preference-label": "Приховати покращену версію Нових редагувань",
        "rcfilters-preference-help": "Скасовує зміну дизайну 2017 року та всі інструменти, додані тоді й пізніше.",
+       "rcfilters-watchlist-preference-label": "Приховати покращену версію Списку спостереження",
+       "rcfilters-watchlist-preference-help": "Відкидає зміну дизайну інтерфейсу 2017 року та всі інструменти додані тоді й пізніше.",
        "rcfilters-filter-showlinkedfrom-label": "Показати зміни на сторінках, на які є посилання з",
        "rcfilters-filter-showlinkedfrom-option-label": "<strong>Сторінки, на які є посилання з</strong> обраної сторінки",
        "rcfilters-filter-showlinkedto-label": "Показати зміни на сторінках, що посилаються сюди",
        "dellogpage": "Журнал вилучень",
        "dellogpagetext": "Нижче наведений список останніх вилучень.",
        "deletionlog": "журнал вилучень",
+       "log-name-create": "Журнал створення сторінок",
+       "log-description-create": "Нижче наведений список останніх створень сторінок.",
+       "logentry-create-create": "$1 {{GENDER:$2|створив|створила}} сторінку $3",
        "reverted": "Повернуто до більш ранньої версії",
        "deletecomment": "Причина:",
        "deleteotherreason": "Інша/додаткова причина:",
        "anoncontribs": "Внесок",
        "contribsub2": "Для {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "Обліковий запис користувача  «$1» не зареєстровано.",
-       "nocontribs": "Редагувань, що задовольняють заданим умовам не знайдено.",
+       "nocontribs": "Редагувань, що задовольняють заданим умовам, не знайдено.",
        "uctop": "(поточна)",
        "month": "До місяця (включно):",
        "year": "До року (включно):",
        "semicolon-separator": ";&#32;",
        "comma-separator": ",&#32;",
        "colon-separator": ":&#32;",
-       "pipe-separator": "&#32;&#32;",
+       "pipe-separator": "&#32;|&#32;",
        "word-separator": "&#32;",
        "ellipsis": "...",
        "percent": "$1%",
index 45a3874..f84bf57 100644 (file)
        "listingcontinuesabbrev": "جاری۔",
        "index-category": "فہرست شدہ صفحات",
        "noindex-category": "غیر فہرست شدہ صفحات",
-       "broken-file-category": "فائل کے شکستہ روابط کے حامل صفحات",
+       "broken-file-category": "فائلوں کے شکستہ روابط پر مشتمل صفحات",
        "categoryviewer-pagedlinks": "($1) ($2)",
        "about": "تعارف",
        "article": "صفحہ مواد",
        "userinvalidconfigtitle": "<strong>انتباہ:</strong> یہاں «$1» نام سے کوئی پوشاک موجود نہیں۔ شخصی .css اور .js کے صفحات اپنے عنوان میں چھوٹے حروف استعمال کرتے ہیں، مثلاً {{ns:user}}:Foo/Vector.css کی بجائے {{ns:user}}:Foo/vector.css",
        "updated": "(اپ ڈیٹڈ)",
        "note": "'''نوٹ:'''",
-       "previewnote": "'''یاد رکھیں، یہ صرف نمائش ہے ۔آپ کی ترامیم ابھی محفوظ نہیں کی گئیں۔'''",
+       "previewnote": "<strong>یاد رکھیں، یہ صرف نمائش ہے۔</strong>\nآپ کی ترامیم ابھی محفوظ نہیں کی گئیں!",
        "continue-editing": "خانہ ترمیم میں جائیں",
        "previewconflict": "اس نمائش میں خانہ ترمیم کے اوپر موجود متن جس انداز میں ظاہر ہو رہا ہے، محفوظ کرنے کے بعد اسی طرح نظر آئے گا۔",
        "session_fail_preview": "معذرت! نشست کے مواد میں خامی کی وجہ سے آپ کی  ترمیم مکمل نہیں ہو سکی۔\n\nشاید آپ اپنے کھاتے سے خارج ہو گئے ہیں۔ <strong>براہ کرم اس بات کی تصدیق کر لیں کہ آپ داخل ہیں اور دوبارہ کوشش کریں۔</strong> اگر آپ کو پھر بھی مشکل پیش آرہی ہو تو ایک بار [[Special:UserLogout|خارج ہو کر]] واپس داخل ہو جائیں اور اپنے براؤزر کو جانچ لیں کہ آیا وہ اس سائٹ کی کوکیز اخذ کر رہا ہے یا نہیں۔",
        "recreate-moveddeleted-warn": "''' انتباہ: آپ ایک گزشتہ حذف شدہ صفحہ دوبارہ تخلیق کررہے ہیں. '''\n\nآپ کو اِس بات پر غور کرنا چاہئے کہ آیا اِس صفحہ کی تدوین جاری رکھنا موزوں ہے یا نہیں.\nصفحہ کا نوشتۂ حذف شدگی و منتقلی یہاں سہولت کی خاطر مہیّا کیا جارہا ہے:",
        "moveddeleted-notice": "اس صفحہ کو حذف کر دیا گیا ہے۔\nحوالہ کے لیے ذیل میں اس صفحہ کا نوشتہ حذف شدگی اور نوشتہ منتقلی درج ہے۔",
        "moveddeleted-notice-recent": "معذرت، اس صفحہ کو حال ہی میں حذف کیا گیا ہے (گزشتہ چوبیس گھنٹوں میں)۔\nحوالہ کے لیے ذیل میں اس صفحہ کا نوشتہ حذف اور نوشتہ منتقلی موجود ہے۔",
-       "log-fulllog": "پورا نوشتہ دیکھئے",
+       "log-fulllog": "پورا نوشتہ دیکھیے",
        "edit-hook-aborted": "کسی رکاوٹ کی وجہ سے ترمیم کاری منسوخ کر دی گئی ہے۔\nاور کوئی وضاحت نہیں دی گئی۔",
        "edit-gone-missing": "صفحہ تجدید نہیں کیا جاسکتا.\nلگتا ہے یہ حذف ہوچکا ہے.",
        "edit-conflict": "تنازعۂ تدوین.",
        "prefs-email": "برقی خط کے اختیارات",
        "prefs-rendering": "ظاہریت",
        "saveprefs": "محفوظ",
-       "restoreprefs": "تمام ابتدائی ترتیبات کو بحال کریں",
+       "restoreprefs": "تمام ابتدائی ترتیبات (تمام خانوں میں) کو بحال کریں",
        "prefs-editing": "ترمیم کاری",
        "searchresultshead": "تلاش",
        "stub-threshold": "نامکمل ربط کے فارمیٹ کی حد ($1):",
        "prefs-dateformat": "تاریخ کی ترتیب",
        "prefs-timeoffset": "وقت کی ترتیب",
        "prefs-advancedediting": "اعلی اختیارات",
+       "prefs-developertools": "آلات ترقی دہندہ",
        "prefs-editor": "خانہ ترمیم",
        "prefs-preview": "نمائش",
        "prefs-advancedrc": "اضافی اختیارات",
        "recentchangeslinked-feed": "متعلقہ تبدیلیاں",
        "recentchangeslinked-toolbox": "متعلقہ تبدیلیاں",
        "recentchangeslinked-title": "\"$1\" سے متعلقہ تبدیلیاں",
-       "recentchangeslinked-summary": "یہ ان تبدیلیوں کی فہرست ہے جو حال ہی میں کسی مخصوص صفحہ سے مربوط صفحات (یا مخصوص زمرہ کے اراکین) میں کی گئی ہیں۔\n\n[[Special:Watchlist|آپ کی زیر نظر فہرست]] میں یہ صفحات <strong>جلی</strong نظر آئیں گےـ",
+       "recentchangeslinked-summary": "یہ ان تبدیلیوں کی فہرست ہے جو حال ہی میں کسی مخصوص صفحہ سے مربوط صفحات (یا مخصوص زمرہ کے اراکین) میں کی گئی ہیں۔\n\n[[Special:Watchlist|آپ کی زیر نظر فہرست]] میں یہ صفحات <strong>جلی</strong> نظر آئیں گےـ",
        "recentchangeslinked-page": "صفحہ کا نام:",
        "recentchangeslinked-to": "اس کی بجائے درج کردہ صفحہ سے مربوط صفحات کی تبدیلیاں دکھائیں",
        "recentchanges-page-added-to-category": "[[:$1]] کو زمرہ میں شامل کیا گیا",
index fad61bf..30a5434 100644 (file)
        "nonwrite-api-promise-error": "“Promise-Non-Write-API-Action”HTTP报头已发送,但请求是一个API编写模块。",
        "internalerror": "内部错误",
        "internalerror_info": "内部错误:$1",
-       "internalerror-fatal-exception": "类型“$1”的致命错误",
+       "internalerror-fatal-exception": "类型“$1”的致命例外",
        "filecopyerror": "无法将文件“$1”复制到“$2”。",
        "filerenameerror": "无法将文件“$1”重命名为“$2”。",
        "filedeleteerror": "无法删除文件“$1”。",
        "resetpass-submit-loggedin": "更改密码",
        "resetpass-submit-cancel": "取消",
        "resetpass-wrong-oldpass": "临时密码或当前密码无效。您可能已经更改了您的密码,或者请求了新的临时密码。",
-       "resetpass-recycled": "请将您的密码更改为除当前密码以外的其他密码。",
+       "resetpass-recycled": "请将您的密码更改成与当前密码不同的密码。",
        "resetpass-temp-emailed": "您使用了通过电子邮件发送的临时密码登录。要完成登录,您必须在此设置一个新密码:",
        "resetpass-temp-password": "临时密码:",
        "resetpass-abort-generic": "密码更改已经被扩展程序中止。",
        "exif-photometricinterpretation-1": "黑白(黑为0)",
        "exif-photometricinterpretation-4": "透明遮罩",
        "exif-photometricinterpretation-5": "分隔(可能是CMYK)",
+       "exif-photometricinterpretation-9": "CIE L*a*b*(ICC编码)",
+       "exif-photometricinterpretation-10": "CIE L*a*b*(ITU编码)",
        "exif-photometricinterpretation-32803": "色彩滤镜矩阵",
+       "exif-photometricinterpretation-34892": "线性原始",
        "exif-unknowndate": "未知日期",
        "exif-orientation-1": "标准",
        "exif-orientation-2": "水平翻转",
index f98faa7..e34c4f4 100644 (file)
        "right-override-export-depth": "匯出頁面包含連結內容,深度上限為 5 層",
        "right-sendemail": "傳送電子郵件聯絡其他使用者",
        "right-managechangetags": "建立並自資料庫 (取消) 啟用 [[Special:Tags|標籤]]",
-       "right-applychangetags": "連同某個人的變更一起套用[[Special:Tags|標籤]]",
+       "right-applychangetags": "連同自己的變更一起套用[[Special:Tags|標籤]]",
        "right-changetags": "加入與移除任何於各別修訂與日誌項目的[[Special:Tags|標籤]]",
        "right-deletechangetags": "從資料庫刪除 [[Special:Tags|標籤]]",
        "grant-generic": "\"$1\" 權限組合",
index 245bb47..c4a7594 100644 (file)
@@ -203,7 +203,7 @@ abstract class Maintenance {
        /**
         * Do the actual work. All child classes will need to implement this
         *
-        * @return bool|null True for success, false for failure. Not returning
+        * @return bool|null|void True for success, false for failure. Not returning
         *   a value, or returning null, is also interpreted as success. Returning
         *   false for failure will cause doMaintenance.php to exit the process
         *   with a non-zero exit status.
@@ -253,7 +253,7 @@ abstract class Maintenance {
         * this will return an array.
         *
         * @param string $name The name of the param
-        * @param mixed $default Anything you want, default null
+        * @param mixed|null $default Anything you want, default null
         * @return mixed
         */
        protected function getOption( $name, $default = null ) {
@@ -309,7 +309,7 @@ abstract class Maintenance {
        /**
         * Get an argument.
         * @param int $argId The integer value (from zero) for the arg
-        * @param mixed $default The default if it doesn't exist
+        * @param mixed|null $default The default if it doesn't exist
         * @return mixed
         */
        protected function getArg( $argId = 0, $default = null ) {
@@ -359,7 +359,7 @@ abstract class Maintenance {
 
        /**
         * Return input from stdin.
-        * @param int $len The number of bytes to read. If null, just return the handle.
+        * @param int|null $len The number of bytes to read. If null, just return the handle.
         *   Maintenance::STDIN_ALL returns the full length
         * @return mixed
         */
@@ -388,7 +388,7 @@ abstract class Maintenance {
         * Throw some output to the user. Scripts can call this with no fears,
         * as we handle all --quiet stuff here
         * @param string $out The text to show to the user
-        * @param mixed $channel Unique identifier for the channel. See function outputChanneled.
+        * @param mixed|null $channel Unique identifier for the channel. See function outputChanneled.
         */
        protected function output( $out, $channel = null ) {
                // This is sometimes called very early, before Setup.php is included.
@@ -464,7 +464,7 @@ abstract class Maintenance {
         * same channel are concatenated, but any intervening messages in another
         * channel start a new line.
         * @param string $msg The message without trailing newline
-        * @param string $channel Channel identifier or null for no
+        * @param string|null $channel Channel identifier or null for no
         *     channel. Channel comparison uses ===.
         */
        public function outputChanneled( $msg, $channel = null ) {
@@ -661,7 +661,7 @@ abstract class Maintenance {
         * Run a child maintenance script. Pass all of the current arguments
         * to it.
         * @param string $maintClass A name of a child maintenance class
-        * @param string $classFile Full path of where the child is
+        * @param string|null $classFile Full path of where the child is
         * @return Maintenance
         */
        public function runChild( $maintClass, $classFile = null ) {
@@ -922,9 +922,9 @@ abstract class Maintenance {
         * $mOptions becomes an array with keys set to the option names
         * $mArgs becomes a zero-based array containing the non-option arguments
         *
-        * @param string $self The name of the script, if any
-        * @param array $opts An array of options, in form of key=>value
-        * @param array $args An array of command line arguments
+        * @param string|null $self The name of the script, if any
+        * @param array|null $opts An array of options, in form of key=>value
+        * @param array|null $args An array of command line arguments
         */
        public function loadParamsAndArgs( $self = null, $opts = null, $args = null ) {
                # If we were given opts or args, set those and return early
@@ -1606,6 +1606,9 @@ abstract class Maintenance {
                if ( wfIsWindows() ) {
                        return $default;
                }
+               if ( Shell::isDisabled() ) {
+                       return $default;
+               }
                // It's possible to get the screen size with VT-100 terminal escapes,
                // but reading the responses is not possible without setting raw mode
                // (unless you want to require the user to press enter), and that
index 21d9bb1..6eeb81b 100644 (file)
@@ -76,7 +76,7 @@ class BackupDumper extends Maintenance {
        private $stubText = false; // include rev_text_id instead of text; for 2-pass dump
 
        /**
-        * @param array $args For backward compatibility
+        * @param array|null $args For backward compatibility
         */
        function __construct( $args = null ) {
                parent::__construct();
index a12cda7..bf7a22d 100644 (file)
@@ -220,7 +220,7 @@ SPARQL;
        ) {
                $tables = [ 'recentchanges', 'page_props', 'category' ];
                if ( $extra_tables ) {
-                       $tables += $extra_tables;
+                       $tables = array_merge( $tables, $extra_tables );
                }
                $it = new BatchRowIterator( $dbr,
                        $tables,
index 59a6b51..95dd4da 100644 (file)
@@ -100,7 +100,7 @@ class TextPassDumper extends BackupDumper {
        protected $db;
 
        /**
-        * @param array $args For backward compatibility
+        * @param array|null $args For backward compatibility
         */
        function __construct( $args = null ) {
                parent::__construct();
index b2e976c..707eb29 100644 (file)
@@ -151,7 +151,7 @@ class PopulateChangeTagDef extends Maintenance {
                $dbw = $this->lbFactory->getMainLB()->getConnection( DB_MASTER );
                $sleep = (int)$this->getOption( 'sleep', 10 );
                $lastId = 0;
-               $this->output( "Starting to add ct_tag_id = {$tagId} for ct_tag = {$tagName}" );
+               $this->output( "Starting to add ct_tag_id = {$tagId} for ct_tag = {$tagName}\n" );
                while ( true ) {
                        // Given that indexes might not be there, it's better to use replica
                        $ids = $dbr->selectFieldValues(
@@ -173,7 +173,7 @@ class PopulateChangeTagDef extends Maintenance {
                                );
                                continue;
                        } else {
-                               $this->output( "Updating ct_tag_id = {$tagId} up to row ct_id = {$lastId}" );
+                               $this->output( "Updating ct_tag_id = {$tagId} up to row ct_id = {$lastId}\n" );
                        }
 
                        $dbw->update(
@@ -189,7 +189,7 @@ class PopulateChangeTagDef extends Maintenance {
                        }
                }
 
-               $this->output( "Finished adding ct_tag_id = {$tagId} for ct_tag = {$tagName}" );
+               $this->output( "Finished adding ct_tag_id = {$tagId} for ct_tag = {$tagName}\n" );
        }
 
 }
index eee534f..b550cc2 100644 (file)
@@ -72,9 +72,10 @@ class PopulateContentTables extends Maintenance {
 
                $t0 = microtime( true );
 
-               if ( $wgMultiContentRevisionSchemaMigrationStage < MIGRATION_WRITE_BOTH ) {
+               if ( ( $wgMultiContentRevisionSchemaMigrationStage & SCHEMA_COMPAT_WRITE_NEW ) === 0 ) {
                        $this->writeln(
-                               "...cannot update while \$wgMultiContentRevisionSchemaMigrationStage < MIGRATION_WRITE_BOTH"
+                               '...cannot update while \$wgMultiContentRevisionSchemaMigrationStage '
+                               . 'does not have the SCHEMA_COMPAT_WRITE_NEW bit set.'
                        );
                        return false;
                }
index 271cbf3..7f36442 100644 (file)
@@ -24,6 +24,7 @@
 
 use MediaWiki\Logger\LegacyLogger;
 use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\IDatabase;
 
 $optionsWithArgs = RecompressTracked::getOptionsWithArgs();
 require __DIR__ . '/../commandLine.inc';
@@ -640,7 +641,7 @@ class RecompressTracked {
        /**
         * Gets a DB master connection for the given external cluster name
         * @param string $cluster
-        * @return Database
+        * @return IDatabase
         */
        function getExtDB( $cluster ) {
                $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
index 45533aa..070fad8 100644 (file)
   },
   "devDependencies": {
     "deepmerge": "1.3.2",
-    "eslint": "4.9.0",
-    "eslint-config-wikimedia": "0.5.0",
+    "eslint": "5.0.1",
+    "eslint-config-wikimedia": "0.6.0",
     "eslint-plugin-qunit": "3.2.1",
     "grunt": "1.0.3",
     "grunt-banana-checker": "0.6.0",
     "grunt-contrib-copy": "1.0.0",
     "grunt-contrib-watch": "1.1.0",
-    "grunt-eslint": "20.1.0",
+    "grunt-eslint": "21.0.0",
     "grunt-jsonlint": "1.1.0",
     "grunt-karma": "2.0.0",
     "grunt-stylelint": "0.10.0",
index cafc1bc..c41fcd6 100644 (file)
@@ -84,8 +84,7 @@ return [
         *     The highest level, this stylesheet contains extra common styles for classes like
         *     .firstHeading, #contentSub, et cetera which are not outputted by MediaWiki but are common
         *     to skins like MonoBook, Vector, etc... Essentially this level is for styles that are
-        *     common to MonoBook clones. And since practically every skin that currently exists within
-        *     core is a MonoBook clone, all our core skins currently use this level.
+        *     common to MonoBook clones.
         *
         * These modules are typically loaded by addModuleStyles(), which has absolutely no concept of
         * dependency management. As a result they contain duplicate stylesheet references instead of
@@ -2184,7 +2183,7 @@ return [
        ],
        'mediawiki.special.preferences.styles.ooui' => [
                'targets' => [ 'desktop', 'mobile' ],
-               'styles' => 'resources/src/mediawiki.special.preferences.styles.ooui.css',
+               'styles' => 'resources/src/mediawiki.special.preferences.styles.ooui.less',
        ],
        'mediawiki.special.recentchanges' => [
                'scripts' => 'resources/src/mediawiki.special.recentchanges.js',
index 552c0c3..64d394c 100644 (file)
                if ( ts.collationTable ) {
                        // Build array of key names
                        for ( key in ts.collationTable ) {
-                               // Check hasOwn to be safe
-                               if ( ts.collationTable.hasOwnProperty( key ) ) {
-                                       keys.push( mw.RegExp.escape( key ) );
-                               }
+                               keys.push( mw.RegExp.escape( key ) );
                        }
                        if ( keys.length ) {
                                ts.collationRegex = new RegExp( keys.join( '|' ), 'ig' );
index 2b76187..c02af98 100644 (file)
                                tiff: 'tif',
                                ogv: 'ogg'
                        };
-               if ( squish.hasOwnProperty( lower ) ) {
+               if ( Object.prototype.hasOwnProperty.call( squish, lower ) ) {
                        return squish[ lower ];
                } else if ( /^[0-9a-z]+$/.test( lower ) ) {
                        return lower;
index 72ce9f0..cb9c286 100644 (file)
@@ -4,9 +4,11 @@
        background-color: #eaecf0;
        background-size: cover;
        background-position: center center;
-       padding: 1.5em;
-       margin: -1.5em;
-       margin-bottom: 1.5em;
+       /* Same as padding on `.oo-ui-bookletLayout-stackLayout > .oo-ui-panelLayout`,
+        * equals 20px at default font size */
+       padding: 1.42857143em;
+       margin: -1.42857143em;
+       margin-bottom: 1.42857143em;
        position: relative;
 }
 
index 7467d50..eb7cff0 100644 (file)
                                        // Copy data over from existing URI object
                                        for ( prop in uri ) {
                                                // Only copy direct properties, not inherited ones
-                                               if ( uri.hasOwnProperty( prop ) ) {
+                                               if ( Object.prototype.hasOwnProperty.call( uri, prop ) ) {
                                                        // Deep copy object properties
                                                        if ( Array.isArray( uri[ prop ] ) || $.isPlainObject( uri[ prop ] ) ) {
                                                                this[ prop ] = $.extend( true, {}, uri[ prop ] );
index 29bd59a..f343321 100644 (file)
@@ -40,9 +40,7 @@
        function getFirstKey( obj ) {
                var key;
                for ( key in obj ) {
-                       if ( obj.hasOwnProperty( key ) ) {
-                               return key;
-                       }
+                       return key;
                }
        }
 
index 3f5a5db..37c0fac 100644 (file)
                        };
 
                        for ( id in panes ) {
-                               if ( !panes.hasOwnProperty( id ) ) {
-                                       continue;
-                               }
-
                                $( '<div>' )
                                        .prop( {
                                                className: 'mw-debug-pane',
                                        .appendTo( $table );
 
                                for ( key in data ) {
-                                       if ( !data.hasOwnProperty( key ) ) {
-                                               continue;
-                                       }
-
                                        $( '<tr>' )
                                                .append( $( '<th>' ).text( key ) )
                                                .append( $( '<td>' ).text( data[ key ] ) )
index 4f9e43a..437800a 100644 (file)
                getAst: function ( key ) {
                        var wikiText;
 
-                       if ( !this.astCache.hasOwnProperty( key ) ) {
+                       if ( !Object.prototype.hasOwnProperty.call( this.astCache, key ) ) {
                                wikiText = this.settings.messages.get( key );
                                if ( typeof wikiText !== 'string' ) {
                                        wikiText = '⧼' + key + '⧽';
                        return oldParser.apply( this );
                }
 
-               if ( !this.map.hasOwnProperty( this.format ) ) {
+               if ( !Object.prototype.hasOwnProperty.call( this.map, this.format ) ) {
                        this.map[ this.format ] = mw.jqueryMsg.getMessageFunction( {
                                messages: this.map,
                                // For format 'escaped', escaping part is handled by mediawiki.js
index 267210c..f4194d3 100644 (file)
                for ( i = 0; i < arguments.length; i++ ) {
                        table = arguments[ i ];
                        for ( key in table ) {
-                               if ( table.hasOwnProperty( key ) ) {
-                                       // The thousand separator should be deleted
-                                       flipped[ table[ key ] ] = key === ',' ? '' : key;
-                               }
+                               // The thousand separator should be deleted
+                               flipped[ table[ key ] ] = key === ',' ? '' : key;
                        }
                }
 
                        if ( transformTable ) {
                                convertedNumber = '';
                                for ( i = 0; i < numberString.length; i++ ) {
-                                       if ( transformTable.hasOwnProperty( numberString[ i ] ) ) {
+                                       if ( Object.prototype.hasOwnProperty.call( transformTable, numberString[ i ] ) ) {
                                                convertedNumber += transformTable[ numberString[ i ] ];
                                        } else {
                                                convertedNumber += numberString[ i ];
index 5b41876..2002b9f 100644 (file)
 
                actionPaths = mw.config.get( 'wgActionPaths' );
                for ( key in actionPaths ) {
-                       if ( actionPaths.hasOwnProperty( key ) ) {
-                               parts = actionPaths[ key ].split( '$1' );
-                               parts = parts.map( mw.RegExp.escape );
-                               m = new RegExp( parts.join( '(.+)' ) ).exec( url );
-                               if ( m && m[ 1 ] ) {
-                                       return key;
-                               }
-
+                       parts = actionPaths[ key ].split( '$1' );
+                       parts = parts.map( mw.RegExp.escape );
+                       m = new RegExp( parts.join( '(.+)' ) ).exec( url );
+                       if ( m && m[ 1 ] ) {
+                               return key;
                        }
                }
 
index d1e40ca..fef5226 100644 (file)
                if ( !this.isHighlightSupported() ) {
                        return;
                }
+               // If the highlight color on the item and in the parameter is null/undefined, return early.
+               if ( !this.highlightColor && !highlightColor ) {
+                       return;
+               }
 
                if ( this.highlightColor !== highlightColor ) {
                        this.highlightColor = highlightColor;
index c7db7c6..6d36c71 100644 (file)
 
        /* Methods */
 
+       /**
+        * Override parent method to avoid unnecessary resize events.
+        */
+       mw.rcfilters.ui.FilterTagMultiselectWidget.prototype.updateIfHeightChanged = function () { };
+
        /**
         * Respond to view select widget choose event
         *
index 04b7709..40b2fcf 100644 (file)
         */
        mw.rcfilters.ui.MenuSelectWidget.prototype.onModelInitialize = function () {
                this.menuInitialized = false;
+               // Set timeout for the menu to lazy build.
+               setTimeout( this.lazyMenuCreation.bind( this ) );
        };
 
        /**
index f936658..7c0d232 100644 (file)
                        var apiPromise,
                                deferred = $.Deferred();
 
-                       if ( moduleInfoCache.hasOwnProperty( module ) ) {
+                       if ( Object.prototype.hasOwnProperty.call( moduleInfoCache, module ) ) {
                                return deferred
                                        .resolve( moduleInfoCache[ module ] )
                                        .promise( { abort: function () {} } );
 
                                subpages = page.getSubpages();
                                for ( i = 0; i < subpages.length; i++ ) {
-                                       if ( pages.hasOwnProperty( subpages[ i ].key ) ) {
+                                       if ( Object.prototype.hasOwnProperty.call( pages, subpages[ i ].key ) ) {
                                                checkPages.push( pages[ subpages[ i ].key ] );
                                        }
                                }
                                        if ( page.getSubpages ) {
                                                subpages = page.getSubpages();
                                                for ( j = 0; j < subpages.length; j++ ) {
-                                                       if ( !pages.hasOwnProperty( subpages[ j ].key ) ) {
+                                                       if ( !Object.prototype.hasOwnProperty.call( pages, subpages[ j ].key ) ) {
                                                                subpages[ j ].indentLevel = page.indentLevel + 1;
                                                                pages[ subpages[ j ].key ] = new ApiSandbox.PageLayout( subpages[ j ] );
                                                        }
                                page.getQueryParams( params, displayParams );
                                subpages = page.getSubpages();
                                for ( i = 0; i < subpages.length; i++ ) {
-                                       if ( pages.hasOwnProperty( subpages[ i ].key ) ) {
+                                       if ( Object.prototype.hasOwnProperty.call( pages, subpages[ i ].key ) ) {
                                                checkPages.push( pages[ subpages[ i ].key ] );
                                        }
                                }
 
                                // Force a 'fm' format with wrappedhtml=1, if available
                                if ( params.format !== undefined ) {
-                                       if ( availableFormats.hasOwnProperty( params.format + 'fm' ) ) {
+                                       if ( Object.prototype.hasOwnProperty.call( availableFormats, params.format + 'fm' ) ) {
                                                params.format = params.format + 'fm';
                                        }
                                        if ( params.format.substr( -2 ) === 'fm' ) {
 
                                subpages = page.getSubpages();
                                for ( i = 0; i < subpages.length; i++ ) {
-                                       if ( pages.hasOwnProperty( subpages[ i ].key ) ) {
+                                       if ( Object.prototype.hasOwnProperty.call( pages, subpages[ i ].key ) ) {
                                                checkPages.push( pages[ subpages[ i ].key ] );
                                        }
                                }
                                page.apiCheckValid();
                                subpages = page.getSubpages();
                                for ( i = 0; i < subpages.length; i++ ) {
-                                       if ( pages.hasOwnProperty( subpages[ i ].key ) ) {
+                                       if ( Object.prototype.hasOwnProperty.call( pages, subpages[ i ].key ) ) {
                                                checkPages.push( pages[ subpages[ i ].key ] );
                                        }
                                }
                                                }
                                        }
                                        if ( params ) {
-                                               tmp.widget.setApiValue( params.hasOwnProperty( name ) ? params[ name ] : undefined );
+                                               tmp.widget.setApiValue( Object.prototype.hasOwnProperty.call( params, name ) ? params[ name ] : undefined );
                                        }
                                } else {
                                        newVars = {};
                                        buttons = [],
                                        filterFmModules = function ( v ) {
                                                return v.substr( -2 ) !== 'fm' ||
-                                                       !availableFormats.hasOwnProperty( v.substr( 0, v.length - 2 ) );
+                                                       !Object.prototype.hasOwnProperty.call( availableFormats, v.substr( 0, v.length - 2 ) );
                                        };
 
                                // This is something of a hack. We always want the 'format' and
                        this.loadFromQueryParams = params;
                } else {
                        $.each( this.widgets, function ( name, widget ) {
-                               var v = params.hasOwnProperty( name ) ? params[ name ] : undefined;
+                               var v = Object.prototype.hasOwnProperty.call( params, name ) ? params[ name ] : undefined;
                                widget.setApiValue( v );
                        } );
                        this.updateTemplatedParameters( params );
diff --git a/resources/src/mediawiki.special.preferences.styles.ooui.css b/resources/src/mediawiki.special.preferences.styles.ooui.css
deleted file mode 100644 (file)
index 4c5d344..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/* Reuses colors from mediawiki.legacy/shared.css */
-.mw-email-not-authenticated .oo-ui-labelWidget,
-.mw-email-none .oo-ui-labelWidget {
-       border: 1px solid #fde29b;
-       background-color: #fdf1d1;
-       color: #000;
-       padding: 0.5em;
-}
-/* Authenticated email field has its own class too. Unstyled by default */
-/*
-.mw-email-authenticated .oo-ui-labelWidget { }
-*/
-
-/* This is needed because add extra buttons in a weird way */
-.mw-prefs-buttons .mw-htmlform-submit-buttons {
-       margin: 0;
-       display: inline;
-}
-
-.mw-prefs-buttons {
-       margin-top: 1em;
-}
-
-#prefcontrol {
-       margin-right: 0.5em;
-}
-
-/*
- * Hide, but keep accessible for screen-readers.
- */
-.client-js .mw-navigation-hint {
-       overflow: hidden;
-       height: 0;
-       zoom: 1;
-}
-
-/* Override OOUI styles so that dropdowns near the bottom of the form don't get clipped,
- * e.g.'Appearance' / 'Threshold for stub link formatting'. This is hacky and bad, it would be
- * better solved by setting overlays for the widgets, but we can't do it from PHP... */
-#preferences .oo-ui-panelLayout {
-       position: static;
-       overflow: visible;
-       -webkit-transform: none;
-       transform: none;
-}
-
-#preferences .oo-ui-menuLayout .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed {
-       border-width: 0;
-       border-radius: 0;
-       box-shadow: none;
-       padding-left: 0;
-       padding-right: 0;
-}
-
-.mw-prefs-faketabs > .oo-ui-menuLayout > .oo-ui-menuLayout-menu a {
-       color: inherit;
-       text-decoration: none;
-}
-
-/* Adjust the borders when JS is disabled: frame each prefsection instead of the
- * whole tabLayout wrapper */
-.client-nojs #preferences .oo-ui-menuLayout .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed {
-       border-color: #c8ccd1;
-       border-width: 1px 0 0;
-}
-
-.client-nojs .mw-prefs-faketabs {
-       border-width: 0;
-       border-radius: 0;
-       box-shadow: none;
-}
-
-.client-nojs .mw-prefs-faketabs > .oo-ui-menuLayout > .oo-ui-menuLayout-content > .oo-ui-stackLayout {
-       margin-bottom: 1em;
-}
-
-/* Hide the tab menu when JS is disabled as we can't use this feature */
-.client-nojs .mw-prefs-faketabs > .oo-ui-menuLayout > .oo-ui-menuLayout-menu {
-       display: none;
-}
-
-.client-nojs #preferences .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed:last-child {
-       padding-bottom: 0;
-       margin-bottom: 0;
-}
-
-/* Hide top level legends when JS is enabled, as they will not be visible
- * when the real tabLayout is built */
-.client-js #preferences .oo-ui-tabPanelLayout > fieldset > legend {
-       display: none;
-}
-
-.client-js #preferences .oo-ui-tabPanelLayout {
-       padding-top: 0.5em;
-}
-
-.client-js #preferences .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed {
-       margin-left: 0;
-       margin-bottom: 0;
-       padding: 0;
-       border-width: 0;
-       border-radius: 0;
-       box-shadow: none;
-}
-
-.client-js #preferences > .oo-ui-panelLayout > .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-header {
-       margin-bottom: 1em;
-}
-
-/* Make the "Basic information" section more compact */
-/* OOUI's `align: 'left'` for FieldLayouts sucks, so we do our own */
-#mw-htmlform-info > .oo-ui-fieldLayout-align-top > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-header {
-       width: 20%;
-       display: inline-block;
-       vertical-align: middle;
-       padding: 0;
-}
-
-#mw-htmlform-info > .oo-ui-fieldLayout-align-top .oo-ui-fieldLayout-help {
-       margin-right: 0;
-}
-
-#mw-htmlform-info > .oo-ui-fieldLayout.oo-ui-fieldLayout-align-top > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
-       width: 80%;
-       display: inline-block;
-       vertical-align: middle;
-}
-
-/* Expand the dropdown and textfield of "Time zone" field to the */
-/* usual maximum width and display them on separate lines. */
-#wpTimeCorrection .oo-ui-dropdownInputWidget,
-#wpTimeCorrection .oo-ui-textInputWidget {
-       display: block;
-       max-width: 50em;
-}
-
-#wpTimeCorrection .oo-ui-textInputWidget {
-       margin-top: 0.5em;
-}
-
-/* HACK: expand width of gadget descriptions.
- * This should be moved to the Gadgets extension */
-#mw-htmlform-gadgets .oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body {
-       max-width: none;
-}
diff --git a/resources/src/mediawiki.special.preferences.styles.ooui.less b/resources/src/mediawiki.special.preferences.styles.ooui.less
new file mode 100644 (file)
index 0000000..ecf6887
--- /dev/null
@@ -0,0 +1,159 @@
+@import 'mediawiki.mixins';
+
+/* Reuses colors from mediawiki.legacy/shared.css */
+.mw-email-not-authenticated .oo-ui-labelWidget,
+.mw-email-none .oo-ui-labelWidget {
+       background-color: #fdf1d1;
+       color: #000;
+       border: 1px solid #fde29b;
+       padding: 0.5em;
+}
+/* Authenticated email field has its own class too. Unstyled by default */
+/*
+.mw-email-authenticated .oo-ui-labelWidget { }
+*/
+
+/* This is needed because add extra buttons in a weird way */
+.mw-prefs-buttons .mw-htmlform-submit-buttons {
+       display: inline;
+       margin: 0;
+}
+
+.mw-prefs-buttons {
+       margin-top: 1em;
+}
+
+#prefcontrol {
+       margin-right: 0.5em;
+}
+
+/*
+ * Hide, but keep accessible for screen-readers.
+ */
+.client-js .mw-navigation-hint {
+       overflow: hidden;
+       height: 0;
+       zoom: 1;
+}
+
+/* Override OOUI styles so that dropdowns near the bottom of the form don't get clipped,
+ * e.g.'Appearance' / 'Threshold for stub link formatting'. This is hacky and bad, it would be
+ * better solved by setting overlays for the widgets, but we can't do it from PHP... */
+#preferences .oo-ui-panelLayout {
+       position: static;
+       overflow: visible;
+       .transform( none );
+}
+
+#preferences .oo-ui-menuLayout .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed {
+       border-width: 0;
+       border-radius: 0;
+       padding-left: 0;
+       padding-right: 0;
+       box-shadow: none;
+}
+
+.mw-prefs-faketabs > .oo-ui-menuLayout > .oo-ui-menuLayout-menu a {
+       color: inherit;
+       text-decoration: none;
+}
+
+/* Disabled JavaScript */
+.client-nojs {
+       /* Adjust the borders: frame each prefsection instead of the
+        * whole tabLayout wrapper */
+       #preferences .oo-ui-menuLayout .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed:first-child {
+               border-color: #c8ccd1;
+               border-width: 1px 0 0;
+       }
+
+       #preferences .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed:last-child {
+               padding-bottom: 0;
+               margin-bottom: 0;
+       }
+
+       /* Fake Tabs to address reflow */
+       .mw-prefs-faketabs {
+               border-width: 0;
+               border-radius: 0;
+               .box-shadow( none );
+
+               > .oo-ui-menuLayout > .oo-ui-menuLayout-content > .oo-ui-stackLayout {
+                       margin-bottom: 1em;
+               }
+
+               /* Hide the tab menu when JS is disabled as we can't use this feature */
+               > .oo-ui-menuLayout > .oo-ui-menuLayout-menu {
+                       display: none;
+               }
+       }
+}
+
+/* Enabled JavaScript
+ * Hide top level legends when JS is enabled, as they will not be visible
+ * when the real tabLayout is built */
+.client-js #preferences {
+       .oo-ui-tabPanelLayout {
+               padding-top: 0.5em;
+
+               & > fieldset > legend {
+                       display: none;
+               }
+       }
+
+       .oo-ui-panelLayout-framed .oo-ui-panelLayout-framed {
+               margin-top: 2.286em; /* equals `32px` at `font-size: 14px;` */
+               margin-bottom: 0;
+               border-width: 0;
+               border-radius: 0;
+               padding: 0;
+               box-shadow: none;
+
+               &:first-child {
+                       margin-top: 0.85714286em;
+               }
+       }
+
+       > .oo-ui-panelLayout > .oo-ui-fieldsetLayout > .oo-ui-fieldsetLayout-header {
+               margin-bottom: 1em;
+       }
+}
+
+/* Make the "Basic information" section more compact */
+/* OOUI's `align: 'left'` for FieldLayouts sucks, so we do our own */
+#mw-htmlform-info {
+       > .oo-ui-fieldLayout-align-top > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-header {
+               display: inline-block;
+               width: 20%;
+               padding: 0;
+               vertical-align: middle;
+       }
+
+       > .oo-ui-fieldLayout-align-top .oo-ui-fieldLayout-help {
+               margin-right: 0;
+       }
+
+       > .oo-ui-fieldLayout.oo-ui-fieldLayout-align-top > .oo-ui-fieldLayout-body > .oo-ui-fieldLayout-field {
+               display: inline-block;
+               width: 80%;
+               vertical-align: middle;
+       }
+}
+
+/* Expand the dropdown and textfield of "Time zone" field to the */
+/* usual maximum width and display them on separate lines. */
+#wpTimeCorrection .oo-ui-dropdownInputWidget,
+#wpTimeCorrection .oo-ui-textInputWidget {
+       display: block;
+       max-width: 50em;
+}
+
+#wpTimeCorrection .oo-ui-textInputWidget {
+       margin-top: 0.5em;
+}
+
+/* HACK: expand width of gadget descriptions.
+ * This should be moved to the Gadgets extension */
+#mw-htmlform-gadgets .oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline > .oo-ui-fieldLayout-body {
+       max-width: none;
+}
index 144659a..da54ad2 100644 (file)
@@ -37,7 +37,7 @@
                                clearTimeout( this.timeoutID );
                        }
                        // Check response cache
-                       if ( this.responseCache.hasOwnProperty( this.nameToCheck ) ) {
+                       if ( Object.prototype.hasOwnProperty.call( this.responseCache, this.nameToCheck ) ) {
                                this.setWarning( this.responseCache[ this.nameToCheck ] );
                                return;
                        }
                        var template = $element.val(),
                                $spinner;
 
-                       if ( this.responseCache.hasOwnProperty( template ) ) {
+                       if ( Object.prototype.hasOwnProperty.call( this.responseCache, template ) ) {
                                this.showPreview( this.responseCache[ template ], $previewContainer );
                                return;
                        }
index e2bbd68..bac0674 100644 (file)
                 *  corresponding call to set().
                 */
                clear: function ( visibleTimeoutId ) {
-                       if ( activeTimeouts.hasOwnProperty( visibleTimeoutId ) ) {
+                       if ( Object.prototype.hasOwnProperty.call( activeTimeouts, visibleTimeoutId ) ) {
                                activeTimeouts[ visibleTimeoutId ]();
                        }
                }
index 2bbeabf..6ee9595 100644 (file)
@@ -26,8 +26,8 @@
         * @cfg {boolean} [showMissing=true] Show missing pages
         * @cfg {boolean} [addQueryInput=true] Add exact user's input query to results
         * @cfg {boolean} [excludeCurrentPage] Exclude the current page from suggestions
-        * @cfg {boolean} [validateTitle=true] Whether the input must be a valid title (if set to true,
-        *  the widget will marks itself red for invalid inputs, including an empty query).
+        * @cfg {boolean} [validateTitle=true] Whether the input must be a valid title
+        * @cfg {boolean} [required=false] Whether the input must not be empty
         * @cfg {Object} [cache] Result cache which implements a 'set' method, taking keyed values as an argument
         * @cfg {mw.Api} [api] API object to use, creates a default mw.Api instance if not specified
         */
                var api = this.getApi(),
                        cache = this.constructor.static.interwikiPrefixesPromiseCache,
                        key = api.defaults.ajax.url;
-               if ( !cache.hasOwnProperty( key ) ) {
+               if ( !Object.prototype.hasOwnProperty.call( cache, key ) ) {
                        cache[ key ] = api.get( {
                                action: 'query',
                                meta: 'siteinfo',
         * @return {boolean} The query is valid
         */
        mw.widgets.TitleWidget.prototype.isQueryValid = function () {
-               return this.validateTitle ? !!this.getMWTitle() : true;
+               if ( !this.validateTitle ) {
+                       return true;
+               }
+               if ( !this.required && this.getQueryValue() === '' ) {
+                       return true;
+               }
+               return !!this.getMWTitle();
        };
 
 }( jQuery, mediaWiki ) );
index 88c541e..3911faa 100644 (file)
@@ -152,6 +152,7 @@ $wgAutoloadClasses += [
        'MediaWiki\Tests\Storage\McrSchemaDetection' => "$testDir/phpunit/includes/Storage/McrSchemaDetection.php",
        'MediaWiki\Tests\Storage\McrSchemaOverride' => "$testDir/phpunit/includes/Storage/McrSchemaOverride.php",
        'MediaWiki\Tests\Storage\McrWriteBothSchemaOverride' => "$testDir/phpunit/includes/Storage/McrWriteBothSchemaOverride.php",
+       'MediaWiki\Tests\Storage\McrReadNewSchemaOverride' => "$testDir/phpunit/includes/Storage/McrReadNewSchemaOverride.php",
        'MediaWiki\Tests\Storage\RevisionSlotsTest' => "$testDir/phpunit/includes/Storage/RevisionSlotsTest.php",
        'MediaWiki\Tests\Storage\RevisionRecordTests' => "$testDir/phpunit/includes/Storage/RevisionRecordTests.php",
        'MediaWiki\Tests\Storage\RevisionStoreDbTestBase' => "$testDir/phpunit/includes/Storage/RevisionStoreDbTestBase.php",
index 1e008ee..4730005 100644 (file)
@@ -16,7 +16,9 @@ class FirejailCommandIntegrationTest extends PHPUnit\Framework\TestCase {
 
        public function setUp() {
                parent::setUp();
-               if ( Shell::command( 'which', 'firejail' )->execute()->getExitCode() ) {
+               if ( Shell::isDisabled() ) {
+                       $this->markTestSkipped( 'shelling out is disabled' );
+               } elseif ( Shell::command( 'which', 'firejail' )->execute()->getExitCode() ) {
                        $this->markTestSkipped( 'firejail not installed' );
                } elseif ( wfIsWindows() ) {
                        $this->markTestSkipped( 'test supports POSIX environments only' );
index 60a5881..ceee432 100644 (file)
@@ -773,7 +773,7 @@ class ParserTestRunner {
        /**
         * Get a Parser object
         *
-        * @param string $preprocessor
+        * @param string|null $preprocessor
         * @return Parser
         */
        function getParser( $preprocessor = null ) {
@@ -1264,6 +1264,7 @@ class ParserTestRunner {
                $this->dbClone = new CloneDatabase( $this->db, $this->listTables(), $prefix );
                $this->dbClone->useTemporaryTables( $temporary );
                $this->dbClone->cloneTableStructure();
+               CloneDatabase::changePrefix( $prefix );
 
                if ( $dbType == 'oracle' ) {
                        $this->db->query( 'BEGIN FILL_WIKI_INFO; END;' );
index e06a732..0b12c84 100644 (file)
@@ -2272,6 +2272,83 @@ hi
 <link rel="mw:PageProp/Category" href="./Category:Ho" />
 !! end
 
+!! test
+Paragraph wrapping following unclosed table
+!! options
+parsoid=wt2html,html2html
+!! wikitext
+{|
+|-
+
+{|
+| x
+|}
+
+a
+
+b
+
+c
+!! html/php+tidy
+<table>
+
+
+</table><table>
+<tbody><tr>
+<td>x
+</td></tr></tbody></table>
+<p>a
+</p><p>b
+</p><p>c
+</p>
+!! html/parsoid
+<table data-parsoid='{"autoInsertedEnd":true}'>
+<tbody><tr class="mw-empty-elt" data-parsoid='{"startTagSrc":"|-"}'></tr>
+
+</tbody></table><table>
+<tbody><tr data-parsoid='{"autoInsertedStart":true}'><td>x</td></tr>
+</tbody></table>
+
+<p>a</p>
+
+<p>b</p>
+
+<p>c</p>
+!! end
+
+!! test
+Paragraph wrapping suppressed in html p
+!! options
+parsoid=wt2html,html2html
+!! wikitext
+<p>
+
+
+hi
+
+
+
+</p>
+!! html/php+tidy
+<p>
+
+
+hi
+
+
+
+</p>
+!! html/parsoid
+<p data-parsoid='{"stx":"html"}'>
+
+
+hi
+
+
+
+</p>
+!! end
+
 ###
 ### Preformatted text
 ###
 a
 <table></table>
 !! html/parsoid
-<p data-parsoid='{"fostered":true,"autoInsertedEnd":true}'>a</p><table>
-<tbody><tr data-parsoid='{"startTagSrc":"|-","autoInsertedEnd":true}'>
-
-</tr></tbody></table>
+<span data-parsoid='{"fostered":true,"autoInsertedEnd":true,"autoInsertedStart":true}'>
+a
+</span><table>
+<tbody><tr class="mw-empty-elt" data-parsoid='{"startTagSrc":"|-"}'></tr></tbody></table>
 !! end
 
 !! test
@@ -12786,16 +12863,22 @@ Templates: Block Tags: 2. Back-to-back template uses
 
 !!end
 
-# This is an edge case relating to paragraph wrapping.
-!!test
+## This is an edge case relating to paragraph wrapping.
+## Note that Parsoid fails to match Remex because it's using the closing tag
+## as a heuristic to determine if it's in a block, rather than SAX based events.
+!! test
 Templates: Correctly encapsulate templates producing </p> tag without a corresponding <p> tag
 !! wikitext
 {{echo|a
 b</p>}}
+!! html/php+tidy
+<p>a
+</p><p>
+b</p><p class="mw-empty-elt"></p>
 !! html/parsoid
-<p about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"a\nb&lt;/p>"}},"i":0}}]}'>a
-b</p>
-!!end
+<p about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"a\nb&lt;/p>"}},"i":0}}]}'>a</p><span about="#mwt1">
+</span><span about="#mwt1">b</span><p about="#mwt1" data-parsoid='{"autoInsertedStart":true,"stx":"html"}'></p>
+!! end
 
 !!test
 Templates: Links: 1. Simple example
@@ -13164,13 +13247,13 @@ parsoid=wt2html,wt2wt
 <link rel="mw:PageProp/Category" href="./Category:Bar1"><link rel="mw:PageProp/Category" href="./Category:Bar2"><table><tbody><tr><td>foo</td></tr></tbody></table>
 !!end
 
-!!test
+!! test
 Templates: Wiki Tables: 1a. Fostering of entire template content
 !! wikitext
 {|
 {{echo|a}}
 |}
-!! html
+!! html/php
 <table>
 a
 <tr><td></td></tr></table>
 a
 <table><tbody><tr><td></td></tr></tbody></table>
 !! html/parsoid
-<p about="#mwt2" typeof="mw:Transclusion" data-parsoid='{"fostered":true,"autoInsertedEnd":true,"firstWikitextNode":"TABLE","pi":[[{"k":"1"}]]}' data-mw='{"parts":["{|\n",{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"a"}},"i":0}},"\n|}"]}'>a</p><table about="#mwt2">
+<span about="#mwt2" typeof="mw:Transclusion" data-parsoid='{"fostered":true,"autoInsertedEnd":true,"autoInsertedStart":true,"firstWikitextNode":"TABLE","pi":[[{"k":"1"}]]}' data-mw='{"parts":["{|\n",{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"a"}},"i":0}},"\n|}"]}'>a</span><table about="#mwt2">
 
 </table>
 !! end
@@ -13216,14 +13299,14 @@ foo
 </table>
 !! end
 
-!!test
+!! test
 Templates: Wiki Tables: 2. Fostering of partial template content
 !! wikitext
 {|
 {{echo|a
 <div>b</div>}}
 |}
-!! html
+!! html/php
 <table>
 a
 <div>b</div>
 <div>b</div><table>
 <tbody><tr><td></td></tr></tbody></table>
 !! html/parsoid
-<p about="#mwt2" typeof="mw:Transclusion" data-parsoid='{"fostered":true,"autoInsertedEnd":true,"firstWikitextNode":"TABLE","pi":[[{"k":"1"}]]}' data-mw='{"parts":["{|\n",{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"a\n&lt;div>b&lt;/div>"}},"i":0}},"\n|}"]}'>a</p><div about="#mwt2">b</div><table about="#mwt2">
+<span about="#mwt2" typeof="mw:Transclusion" data-parsoid='{"fostered":true,"autoInsertedEnd":true,"autoInsertedStart":true,"firstWikitextNode":"TABLE","pi":[[{"k":"1"}]]}' data-mw='{"parts":["{|\n",{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"a\n&lt;div>b&lt;/div>"}},"i":0}},"\n|}"]}'>a</span><div about="#mwt2">b</div><table about="#mwt2">
 
 
 </table>
@@ -22099,11 +22182,11 @@ conversion:
 
 ==Latinski==
 !! html/php
-<h2><span id="-.7BNaslov.7D-"></span><span class="mw-headline" id="-{Naslov}-">Naslov</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Уреди одељак „Naslov“">уреди</a><span class="mw-editsection-bracket">]</span></span></h2>
+<h2><span id="-.7BNaslov.7D-"></span><span class="mw-headline" id="-{Naslov}-">Naslov</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=1" title="Уредите одељак „Naslov“">уреди</a><span class="mw-editsection-bracket">]</span></span></h2>
 <p>Ноте тхат евен ан унпротецтед хеадлине ИД ис нот аффецтед бy лангуаге
 цонверсион:
 </p>
-<h2><span class="mw-headline" id="Latinski">Латински</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Уреди одељак „Латински“">уреди</a><span class="mw-editsection-bracket">]</span></span></h2>
+<h2><span class="mw-headline" id="Latinski">Латински</span><span class="mw-editsection"><span class="mw-editsection-bracket">[</span><a href="/index.php?title=Parser_test&amp;action=edit&amp;section=2" title="Уредите одељак „Латински“">уреди</a><span class="mw-editsection-bracket">]</span></span></h2>
 
 !! html/parsoid
 <h2 id="-{Naslov}-"><span id="-.7BNaslov.7D-" typeof="mw:FallbackId"></span><span typeof="mw:LanguageVariant" data-mw-variant='{"disabled":{"t":"Naslov"}}'></span></h2>
@@ -22871,7 +22954,7 @@ language=sr
 !! wikitext
 [[Датотека:Foobar.jpg|thumb|-{R|caption:}-]]
 !! html/php
-<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/%D0%94%D0%B0%D1%82%D0%BE%D1%82%D0%B5%D0%BA%D0%B0:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" width="180" height="20" class="thumbimage" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/270px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/360px-Foobar.jpg 2x" /></a>  <div class="thumbcaption"><div class="magnify"><a href="/wiki/%D0%94%D0%B0%D1%82%D0%BE%D1%82%D0%B5%D0%BA%D0%B0:Foobar.jpg" class="internal" title="Повећај"></a></div>caption:</div></div></div>
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/%D0%94%D0%B0%D1%82%D0%BE%D1%82%D0%B5%D0%BA%D0%B0:Foobar.jpg" class="image"><img alt="" src="http://example.com/images/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg" width="180" height="20" class="thumbimage" srcset="http://example.com/images/thumb/3/3a/Foobar.jpg/270px-Foobar.jpg 1.5x, http://example.com/images/thumb/3/3a/Foobar.jpg/360px-Foobar.jpg 2x" /></a>  <div class="thumbcaption"><div class="magnify"><a href="/wiki/%D0%94%D0%B0%D1%82%D0%BE%D1%82%D0%B5%D0%BA%D0%B0:Foobar.jpg" class="internal" title="Повећајте"></a></div>caption:</div></div></div>
 
 !! html/parsoid
 <figure class="mw-default-size" typeof="mw:Image/Thumb" data-parsoid='{"optList":[{"ck":"thumbnail","ak":"thumb"}]}'><a href="./Датотека:Foobar.jpg"><img resource="./Датотека:Foobar.jpg" src="//example.com/images/thumb/3/3a/Foobar.jpg/220px-Foobar.jpg" data-file-width="1941" data-file-height="220" data-file-type="bitmap" height="25" width="220"/></a><figcaption><span typeof="mw:LanguageVariant" data-mw-variant='{"disabled":{"t":"caption:"}}' data-parsoid='{"fl":["R"]}'></span></figcaption></figure>
@@ -23415,7 +23498,12 @@ Line two
 
 !! end
 
-# FIXME: Why does/should the blockquote+div combo suppress p-wrapping here?
+## This is a corner case interaction between the paragraph wrapping in the
+## php parser's BlockLevelPass and Remex.  `doBlockLevels` has a notion of
+## some tags which close paragraphs (and thus prevent wrapping on their line),
+## of which "div" is one, but do p-wrapping inside them.  These are referred
+## to as "never suppressing".  Remex, for its part, doesn't traverse into
+## "div"s to p-wrap.  Hence, we only get this partial wrapping.
 !! test
 Paragraphs inside blockquotes/divs (no extra line breaks)
 !! wikitext
index 61d86bd..bcb3379 100644 (file)
@@ -327,10 +327,6 @@ return [
                "PhanTypeMismatchArgument",
                // approximate error count: 13
                "PhanTypeMismatchArgumentInternal",
-               // approximate error count: 6
-               "PhanTypeMismatchDeclaredParam",
-               // approximate error count: 111
-               "PhanTypeMismatchDeclaredParamNullable",
                // approximate error count: 5
                "PhanTypeMismatchDimAssignment",
                // approximate error count: 2
index f363d83..82739a7 100644 (file)
@@ -715,7 +715,7 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
         *
         * @param array|string $pairs Key to the global variable, or an array
         *  of key/value pairs.
-        * @param mixed $value Value to set the global to (ignored
+        * @param mixed|null $value Value to set the global to (ignored
         *  if an array is given as first argument).
         *
         * @note To allow changes to global variables to take effect on global service instances,
@@ -883,7 +883,8 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
         *
         * @since 1.27
         *
-        * @param Config $configOverrides Configuration overrides for the new MediaWikiServices instance.
+        * @param Config|null $configOverrides Configuration overrides for the new MediaWikiServices
+        *        instance.
         * @param callable[] $services An associative array of services to re-define. Keys are service
         *        names, values are callables.
         *
@@ -963,7 +964,7 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
         *   in which case the next two parameters are ignored; or a single string
         *   identifying a group, to use with the next two parameters.
         * @param string|null $newKey
-        * @param mixed $newValue
+        * @param mixed|null $newValue
         */
        public function setGroupPermissions( $newPerms, $newKey = null, $newValue = null ) {
                global $wgGroupPermissions;
@@ -1043,7 +1044,16 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
         * @since 1.18
         */
        public function dbPrefix() {
-               return $this->db->getType() == 'oracle' ? self::ORA_DB_PREFIX : self::DB_PREFIX;
+               return self::getTestPrefixFor( $this->db );
+       }
+
+       /**
+        * @param IDatabase $db
+        * @return string
+        * @since 1.32
+        */
+       public static function getTestPrefixFor( IDatabase $db ) {
+               return $db->getType() == 'oracle' ? self::ORA_DB_PREFIX : self::DB_PREFIX;
        }
 
        /**
@@ -1074,8 +1084,8 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
         * @since 1.25 ($namespace in 1.28)
         * @param string|Title $pageName Page name or title
         * @param string $text Page's content
-        * @param int $namespace Namespace id (name cannot already contain namespace)
-        * @param User $user If null, static::getTestSysop()->getUser() is used.
+        * @param int|null $namespace Namespace id (name cannot already contain namespace)
+        * @param User|null $user If null, static::getTestSysop()->getUser() is used.
         * @return array Title object and page id
         */
        protected function insertPage(
@@ -1226,33 +1236,95 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
        }
 
        /**
-        * Setups a database with the given prefix.
+        * Prepares the given database connection for usage in the context of usage tests.
+        * This sets up clones database tables and changes the table prefix as appropriate.
+        * If the database connection already has cloned tables, calling this method has no
+        * effect. The tables are not re-cloned or reset in that case.
+        *
+        * @param IMaintainableDatabase $db
+        */
+       protected function prepareConnectionForTesting( IMaintainableDatabase $db ) {
+               if ( !self::$dbSetup ) {
+                       throw new LogicException(
+                               'Cannot use prepareConnectionForTesting()'
+                               . ' if the test case is not defined to use the database!'
+                       );
+               }
+
+               if ( isset( $db->_originalTablePrefix ) ) {
+                       // The DB connection was already prepared for testing.
+                       return;
+               }
+
+               $testPrefix = self::getTestPrefixFor( $db );
+               $oldPrefix = $db->tablePrefix();
+
+               $tablesCloned = self::listTables( $db );
+
+               if ( $oldPrefix === $testPrefix ) {
+                       // The database connection already has the test prefix, but presumably not
+                       // the cloned tables. This is the typical case, since the LBFactory will
+                       // have the prefix set during testing, but LoadBalancers will still return
+                       // connections that don't have the cloned table structure.
+                       $oldPrefix = self::$oldTablePrefix;
+               }
+
+               $dbClone = new CloneDatabase( $db, $tablesCloned, $testPrefix, $oldPrefix );
+               $dbClone->useTemporaryTables( self::$useTemporaryTables );
+
+               $db->_originalTablePrefix = $oldPrefix;
+
+               if ( ( $db->getType() == 'oracle' || !self::$useTemporaryTables ) && self::$reuseDB ) {
+                       throw new LogicException( 'Cannot clone database tables' );
+               } else {
+                       $dbClone->cloneTableStructure();
+               }
+       }
+
+       /**
+        * Setups a database with cloned tables using the given prefix.
         *
         * If reuseDB is true and certain conditions apply, it will just change the prefix.
         * Otherwise, it will clone the tables and change the prefix.
         *
-        * Clones all tables in the given database (whatever database that connection has
-        * open), to versions with the test prefix.
-        *
         * @param IMaintainableDatabase $db Database to use
-        * @param string $prefix Prefix to use for test tables
+        * @param string|null $prefix Prefix to use for test tables. If not given, the prefix is determined
+        *        automatically for $db.
         * @return bool True if tables were cloned, false if only the prefix was changed
         */
-       protected static function setupDatabaseWithTestPrefix( IMaintainableDatabase $db, $prefix ) {
-               $tablesCloned = self::listTables( $db );
-               $dbClone = new CloneDatabase( $db, $tablesCloned, $prefix );
-               $dbClone->useTemporaryTables( self::$useTemporaryTables );
-
-               $db->_originalTablePrefix = $db->tablePrefix();
+       protected static function setupDatabaseWithTestPrefix(
+               IMaintainableDatabase $db,
+               $prefix = null
+       ) {
+               if ( $prefix === null ) {
+                       $prefix = self::getTestPrefixFor( $db );
+               }
 
                if ( ( $db->getType() == 'oracle' || !self::$useTemporaryTables ) && self::$reuseDB ) {
-                       CloneDatabase::changePrefix( $prefix );
-
+                       $db->tablePrefix( $prefix );
                        return false;
-               } else {
+               }
+
+               if ( !isset( $db->_originalTablePrefix ) ) {
+                       $oldPrefix = $db->tablePrefix();
+
+                       if ( $oldPrefix === $prefix ) {
+                               // table already has the correct prefix, but presumably no cloned tables
+                               $oldPrefix = self::$oldTablePrefix;
+                       }
+
+                       $db->tablePrefix( $oldPrefix );
+                       $tablesCloned = self::listTables( $db );
+                       $dbClone = new CloneDatabase( $db, $tablesCloned, $prefix, $oldPrefix );
+                       $dbClone->useTemporaryTables( self::$useTemporaryTables );
+
                        $dbClone->cloneTableStructure();
-                       return true;
+
+                       $db->tablePrefix( $prefix );
+                       $db->_originalTablePrefix = $oldPrefix;
                }
+
+               return true;
        }
 
        /**
@@ -1271,6 +1343,10 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
                if ( self::isUsingExternalStoreDB() ) {
                        self::setupExternalStoreTestDBs( $testPrefix );
                }
+
+               // NOTE: Change the prefix in the LBFactory and $wgDBprefix, to prevent
+               // *any* database connections to operate on live data.
+               CloneDatabase::changePrefix( $testPrefix );
        }
 
        /**
@@ -1325,19 +1401,12 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
        /**
         * Clones the External Store database(s) for testing
         *
-        * @param string $testPrefix Prefix for test tables
+        * @param string|null $testPrefix Prefix for test tables. Will be determined automatically
+        *        if not given.
         */
-       protected static function setupExternalStoreTestDBs( $testPrefix ) {
+       protected static function setupExternalStoreTestDBs( $testPrefix = null ) {
                $connections = self::getExternalStoreDatabaseConnections();
                foreach ( $connections as $dbw ) {
-                       // Hack: cloneTableStructure sets $wgDBprefix to the unit test
-                       // prefix,.  Even though listTables now uses tablePrefix, that
-                       // itself is populated from $wgDBprefix by default.
-
-                       // We have to set it back, or we won't find the original 'blobs'
-                       // table to copy.
-
-                       $dbw->tablePrefix( self::$oldTablePrefix );
                        self::setupDatabaseWithTestPrefix( $dbw, $testPrefix );
                }
        }
@@ -1716,6 +1785,29 @@ abstract class MediaWikiTestCase extends PHPUnit\Framework\TestCase {
                return $tables;
        }
 
+       /**
+        * Copy test data from one database connection to another.
+        *
+        * This should only be used for small data sets.
+        *
+        * @param IDatabase $source
+        * @param IDatabase $target
+        */
+       public function copyTestData( IDatabase $source, IDatabase $target ) {
+               $tables = self::listOriginalTables( $source, 'unprefixed' );
+
+               foreach ( $tables as $table ) {
+                       $res = $source->select( $table, '*', [], __METHOD__ );
+                       $allRows = [];
+
+                       foreach ( $res as $row ) {
+                               $allRows[] = (array)$row;
+                       }
+
+                       $target->insert( $table, $allRows, __METHOD__, [ 'IGNORE' ] );
+               }
+       }
+
        /**
         * @throws MWException
         * @since 1.18
index 1b0c848..d28be7a 100644 (file)
@@ -470,16 +470,17 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
                }
 
                $stages = [
-                       MIGRATION_OLD => [ MIGRATION_OLD, MIGRATION_WRITE_NEW ],
-                       MIGRATION_WRITE_BOTH => [ MIGRATION_OLD, MIGRATION_NEW ],
-                       MIGRATION_WRITE_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
-                       MIGRATION_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
+                       MIGRATION_OLD => [ MIGRATION_OLD, MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW ],
+                       MIGRATION_WRITE_BOTH => [ MIGRATION_OLD, MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW,
+                               MIGRATION_NEW ],
+                       MIGRATION_WRITE_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
+                       MIGRATION_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
                ];
 
                $nameKey = $key . '_text';
                $actorKey = $key === 'ipb_by' ? 'ipb_by_actor' : substr( $key, 0, -5 ) . '_actor';
 
-               foreach ( $stages as $writeStage => $readRange ) {
+               foreach ( $stages as $writeStage => $possibleReadStages ) {
                        if ( $key === 'ipb_by' ) {
                                $extraFields['ipb_address'] = __CLASS__ . "#$writeStage";
                        }
@@ -512,7 +513,7 @@ class ActorMigrationTest extends MediaWikiLangTestCase {
                                $callback( $id, $extraFields );
                        }
 
-                       for ( $readStage = $readRange[0]; $readStage <= $readRange[1]; $readStage++ ) {
+                       foreach ( $possibleReadStages as $readStage ) {
                                $r = $this->makeMigration( $readStage );
 
                                $queryInfo = $r->getJoin( $key );
index a510897..c41361c 100644 (file)
@@ -367,13 +367,14 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                ];
 
                $stages = [
-                       MIGRATION_OLD => [ MIGRATION_OLD, MIGRATION_WRITE_NEW ],
-                       MIGRATION_WRITE_BOTH => [ MIGRATION_OLD, MIGRATION_NEW ],
-                       MIGRATION_WRITE_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
-                       MIGRATION_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
+                       MIGRATION_OLD => [ MIGRATION_OLD, MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW ],
+                       MIGRATION_WRITE_BOTH => [ MIGRATION_OLD, MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW,
+                               MIGRATION_NEW ],
+                       MIGRATION_WRITE_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
+                       MIGRATION_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
                ];
 
-               foreach ( $stages as $writeStage => $readRange ) {
+               foreach ( $stages as $writeStage => $possibleReadStages ) {
                        if ( $key === 'ipb_reason' ) {
                                $extraFields['ipb_address'] = __CLASS__ . "#$writeStage";
                        }
@@ -406,7 +407,7 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                $callback( $id );
                        }
 
-                       for ( $readStage = $readRange[0]; $readStage <= $readRange[1]; $readStage++ ) {
+                       foreach ( $possibleReadStages as $readStage ) {
                                $rstore = $this->makeStore( $readStage );
 
                                $fieldRow = $this->db->selectRow(
@@ -460,13 +461,14 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                ];
 
                $stages = [
-                       MIGRATION_OLD => [ MIGRATION_OLD, MIGRATION_WRITE_NEW ],
-                       MIGRATION_WRITE_BOTH => [ MIGRATION_OLD, MIGRATION_NEW ],
-                       MIGRATION_WRITE_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
-                       MIGRATION_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_NEW ],
+                       MIGRATION_OLD => [ MIGRATION_OLD, MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW ],
+                       MIGRATION_WRITE_BOTH => [ MIGRATION_OLD, MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW,
+                               MIGRATION_NEW ],
+                       MIGRATION_WRITE_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
+                       MIGRATION_NEW => [ MIGRATION_WRITE_BOTH, MIGRATION_WRITE_NEW, MIGRATION_NEW ],
                ];
 
-               foreach ( $stages as $writeStage => $readRange ) {
+               foreach ( $stages as $writeStage => $possibleReadStages ) {
                        if ( $key === 'ipb_reason' ) {
                                $extraFields['ipb_address'] = __CLASS__ . "#$writeStage";
                        }
@@ -499,7 +501,7 @@ class CommentStoreTest extends MediaWikiLangTestCase {
                                $callback( $id );
                        }
 
-                       for ( $readStage = $readRange[0]; $readStage <= $readRange[1]; $readStage++ ) {
+                       foreach ( $possibleReadStages as $readStage ) {
                                $rstore = $this->makeStoreWithKey( $readStage, $key );
 
                                $fieldRow = $this->db->selectRow(
index cf48215..093cb07 100644 (file)
@@ -16,7 +16,6 @@ use MediaWiki\Storage\NameTableStore;
 use MediaWiki\Storage\RevisionFactory;
 use MediaWiki\Storage\RevisionLookup;
 use MediaWiki\Storage\RevisionStore;
-use MediaWiki\Storage\RevisionStoreFactory;
 use MediaWiki\Storage\SqlBlobStore;
 
 /**
@@ -315,6 +314,8 @@ class MediaWikiServicesTest extends MediaWikiTestCase {
                        'SiteStore' => [ 'SiteStore', SiteStore::class ],
                        'SiteLookup' => [ 'SiteLookup', SiteLookup::class ],
                        'StatsdDataFactory' => [ 'StatsdDataFactory', IBufferingStatsdDataFactory::class ],
+                       'PerDbNameStatsdDataFactory' =>
+                               [ 'PerDbNameStatsdDataFactory', IBufferingStatsdDataFactory::class ],
                        'InterwikiLookup' => [ 'InterwikiLookup', InterwikiLookup::class ],
                        'EventRelayerGroup' => [ 'EventRelayerGroup', EventRelayerGroup::class ],
                        'SearchEngineFactory' => [ 'SearchEngineFactory', SearchEngineFactory::class ],
@@ -347,7 +348,6 @@ class MediaWikiServicesTest extends MediaWikiTestCase {
                        'BlobStore' => [ 'BlobStore', BlobStore::class ],
                        '_SqlBlobStore' => [ '_SqlBlobStore', SqlBlobStore::class ],
                        'RevisionStore' => [ 'RevisionStore', RevisionStore::class ],
-                       'RevisionStoreFactory' => [ 'RevisionStoreFactory', RevisionStoreFactory::class ],
                        'RevisionLookup' => [ 'RevisionLookup', RevisionLookup::class ],
                        'RevisionFactory' => [ 'RevisionFactory', RevisionFactory::class ],
                        'ContentModelStore' => [ 'ContentModelStore', NameTableStore::class ],
index 623d4a6..15a991e 100644 (file)
@@ -52,6 +52,7 @@ class PageArchiveTest extends MediaWikiTestCase {
 
                $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
                $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', MIGRATION_OLD );
+               $this->setMwGlobals( 'wgMultiContentRevisionSchemaMigrationStage', SCHEMA_COMPAT_OLD );
                $this->overrideMwServices();
 
                // First create our dummy page
@@ -135,6 +136,7 @@ class PageArchiveTest extends MediaWikiTestCase {
         */
        public function testListRevisions() {
                $this->setMwGlobals( 'wgCommentTableSchemaMigrationStage', MIGRATION_OLD );
+               $this->setMwGlobals( 'wgMultiContentRevisionSchemaMigrationStage', SCHEMA_COMPAT_OLD );
                $this->overrideMwServices();
 
                $revisions = $this->archivedPage->listRevisions();
index 73050e0..e17f855 100644 (file)
@@ -1412,7 +1412,8 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
                $rev = $this->testPage->getRevision();
 
                // Clear any previous cache for the revision during creation
-               $key = $cache->makeGlobalKey( RevisionStore::ROW_CACHE_KEY,
+               $key = $cache->makeGlobalKey(
+                       RevisionStore::ROW_CACHE_KEY,
                        $db->getDomainID(),
                        $rev->getPage(),
                        $rev->getId()
diff --git a/tests/phpunit/includes/RevisionMcrReadNewDbTest.php b/tests/phpunit/includes/RevisionMcrReadNewDbTest.php
new file mode 100644 (file)
index 0000000..1054b7d
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+use MediaWiki\Tests\Storage\McrReadNewSchemaOverride;
+
+/**
+ * Tests Revision against the intermediate MCR DB schema for use during schema migration.
+ *
+ * @covers Revision
+ *
+ * @group Revision
+ * @group Storage
+ * @group ContentHandler
+ * @group Database
+ * @group medium
+ */
+class RevisionMcrReadNewDbTest extends RevisionDbTestBase {
+
+       use McrReadNewSchemaOverride;
+
+       protected function getContentHandlerUseDB() {
+               return true;
+       }
+
+}
index 6de37af..7ef1182 100644 (file)
@@ -895,7 +895,12 @@ class RevisionTest extends MediaWikiTestCase {
                        )
                );
 
-               $cacheKey = $cache->makeKey( 'revisiontext', 'textid', 'tt:7777' );
+               $cacheKey = $cache->makeGlobalKey(
+                       'BlobStore',
+                       'address',
+                       $lb->getLocalDomainID(),
+                       'tt:7777'
+               );
                $this->assertSame( 'AAAABBAAA', $cache->get( $cacheKey ) );
        }
 
diff --git a/tests/phpunit/includes/Storage/McrReadNewRevisionStoreDbTest.php b/tests/phpunit/includes/Storage/McrReadNewRevisionStoreDbTest.php
new file mode 100644 (file)
index 0000000..cbae4c7
--- /dev/null
@@ -0,0 +1,252 @@
+<?php
+namespace MediaWiki\Tests\Storage;
+
+use CommentStoreComment;
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
+use MediaWiki\Storage\SlotRecord;
+use TextContent;
+use Title;
+use WikitextContent;
+
+/**
+ * Tests RevisionStore against the intermediate MCR DB schema for use during schema migration.
+ *
+ * @covers \MediaWiki\Storage\RevisionStore
+ *
+ * @group RevisionStore
+ * @group Storage
+ * @group Database
+ * @group medium
+ */
+class McrReadNewRevisionStoreDbTest extends RevisionStoreDbTestBase {
+
+       use McrReadNewSchemaOverride;
+
+       protected function assertRevisionExistsInDatabase( RevisionRecord $rev ) {
+               $numberOfSlots = count( $rev->getSlotRoles() );
+
+               // new schema is written
+               $this->assertSelect(
+                       'slots',
+                       [ 'count(*)' ],
+                       [ 'slot_revision_id' => $rev->getId() ],
+                       [ [ (string)$numberOfSlots ] ]
+               );
+
+               $store = MediaWikiServices::getInstance()->getRevisionStore();
+               $revQuery = $store->getSlotsQueryInfo( [ 'content' ] );
+
+               $this->assertSelect(
+                       $revQuery['tables'],
+                       [ 'count(*)' ],
+                       [
+                               'slot_revision_id' => $rev->getId(),
+                       ],
+                       [ [ (string)$numberOfSlots ] ],
+                       [],
+                       $revQuery['joins']
+               );
+
+               // Legacy schema is still being written
+               $this->assertSelect(
+                       [ 'revision', 'text' ],
+                       [ 'count(*)' ],
+                       [ 'rev_id' => $rev->getId(), 'rev_text_id > 0' ],
+                       [ [ 1 ] ],
+                       [],
+                       [ 'text' => [ 'INNER JOIN', [ 'rev_text_id = old_id' ] ] ]
+               );
+
+               parent::assertRevisionExistsInDatabase( $rev );
+       }
+
+       /**
+        * @param SlotRecord $a
+        * @param SlotRecord $b
+        */
+       protected function assertSameSlotContent( SlotRecord $a, SlotRecord $b ) {
+               parent::assertSameSlotContent( $a, $b );
+
+               // Assert that the same content ID has been used
+               $this->assertSame( $a->getContentId(), $b->getContentId() );
+       }
+
+       public function provideInsertRevisionOn_successes() {
+               foreach ( parent::provideInsertRevisionOn_successes() as $case ) {
+                       yield $case;
+               }
+
+               yield 'Multi-slot revision insertion' => [
+                       [
+                               'content' => [
+                                       'main' => new WikitextContent( 'Chicken' ),
+                                       'aux' => new TextContent( 'Egg' ),
+                               ],
+                               'page' => true,
+                               'comment' => $this->getRandomCommentStoreComment(),
+                               'timestamp' => '20171117010101',
+                               'user' => true,
+                       ],
+               ];
+       }
+
+       public function provideNewNullRevision() {
+               foreach ( parent::provideNewNullRevision() as $case ) {
+                       yield $case;
+               }
+
+               yield [
+                       Title::newFromText( 'UTPage_notAutoCreated' ),
+                       [
+                               'content' => [
+                                       'main' => new WikitextContent( 'Chicken' ),
+                                       'aux' => new WikitextContent( 'Omelet' ),
+                               ],
+                       ],
+                       CommentStoreComment::newUnsavedComment( __METHOD__ . ' comment multi' ),
+               ];
+       }
+
+       public function testGetQueryInfo_NoSlotDataJoin() {
+               $store = MediaWikiServices::getInstance()->getRevisionStore();
+               $queryInfo = $store->getQueryInfo();
+
+               // with the new schema enabled, query info should not join the main slot info
+               $this->assertFalse( array_key_exists( 'a_slot_data', $queryInfo['tables'] ) );
+               $this->assertFalse( array_key_exists( 'a_slot_data', $queryInfo['joins'] ) );
+       }
+
+       public function provideGetArchiveQueryInfo() {
+               yield [
+                       [
+                               'tables' => [
+                                       'archive',
+                               ],
+                               'fields' => array_merge(
+                                       $this->getDefaultArchiveFields( false ),
+                                       [
+                                               'ar_comment_text' => 'ar_comment',
+                                               'ar_comment_data' => 'NULL',
+                                               'ar_comment_cid' => 'NULL',
+                                               'ar_user_text' => 'ar_user_text',
+                                               'ar_user' => 'ar_user',
+                                               'ar_actor' => 'NULL',
+                                       ]
+                               ),
+                               'joins' => [
+                               ],
+                       ]
+               ];
+       }
+
+       public function provideGetQueryInfo() {
+               // TODO: more option variations
+               yield [
+                       [ 'page', 'user' ],
+                       [
+                               'tables' => [
+                                       'revision',
+                                       'page',
+                                       'user',
+                               ],
+                               'fields' => array_merge(
+                                       $this->getDefaultQueryFields( false ),
+                                       $this->getCommentQueryFields(),
+                                       $this->getActorQueryFields(),
+                                       [
+                                               'page_namespace',
+                                               'page_title',
+                                               'page_id',
+                                               'page_latest',
+                                               'page_is_redirect',
+                                               'page_len',
+                                               'user_name',
+                                       ]
+                               ),
+                               'joins' => [
+                                       'page' => [ 'INNER JOIN', [ 'page_id = rev_page' ] ],
+                                       'user' => [ 'LEFT JOIN', [ 'rev_user != 0', 'user_id = rev_user' ] ],
+                               ],
+                       ]
+               ];
+       }
+
+       public function provideGetSlotsQueryInfo() {
+               yield [
+                       [],
+                       [
+                               'tables' => [
+                                       'slots',
+                                       'slot_roles',
+                               ],
+                               'fields' => array_merge(
+                                       [
+                                               'slot_revision_id',
+                                               'slot_content_id',
+                                               'slot_origin',
+                                               'role_name',
+                                       ]
+                               ),
+                               'joins' => [
+                                       'slot_roles' => [ 'INNER JOIN', [ 'slot_role_id = role_id' ] ],
+                               ],
+                       ]
+               ];
+               yield [
+                       [ 'content' ],
+                       [
+                               'tables' => [
+                                       'slots',
+                                       'slot_roles',
+                                       'content',
+                                       'content_models',
+                               ],
+                               'fields' => array_merge(
+                                       [
+                                               'slot_revision_id',
+                                               'slot_content_id',
+                                               'slot_origin',
+                                               'role_name',
+                                               'content_size',
+                                               'content_sha1',
+                                               'content_address',
+                                               'model_name',
+                                       ]
+                               ),
+                               'joins' => [
+                                       'slot_roles' => [ 'INNER JOIN', [ 'slot_role_id = role_id' ] ],
+                                       'content' => [ 'INNER JOIN', [ 'slot_content_id = content_id' ] ],
+                                       'content_models' => [ 'INNER JOIN', [ 'content_model = model_id' ] ],
+                               ],
+                       ]
+               ];
+       }
+
+       public function provideNewMutableRevisionFromArray() {
+               foreach ( parent::provideNewMutableRevisionFromArray() as $case ) {
+                       yield $case;
+               }
+
+               yield 'Basic array, multiple roles' => [
+                       [
+                               'id' => 2,
+                               'page' => 1,
+                               'timestamp' => '20171017114835',
+                               'user_text' => '111.0.1.2',
+                               'user' => 0,
+                               'minor_edit' => false,
+                               'deleted' => 0,
+                               'len' => 29,
+                               'parent_id' => 1,
+                               'sha1' => '89qs83keq9c9ccw9olvvm4oc9oq50ii',
+                               'comment' => 'Goat Comment!',
+                               'content' => [
+                                       'main' => new WikitextContent( 'Söme Cöntent' ),
+                                       'aux' => new TextContent( 'Öther Cöntent' ),
+                               ]
+                       ]
+               ];
+       }
+
+}
diff --git a/tests/phpunit/includes/Storage/McrReadNewSchemaOverride.php b/tests/phpunit/includes/Storage/McrReadNewSchemaOverride.php
new file mode 100644 (file)
index 0000000..1958333
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+namespace MediaWiki\Tests\Storage;
+
+use Wikimedia\Rdbms\IMaintainableDatabase;
+use MediaWiki\DB\PatchFileLocation;
+
+/**
+ * Trait providing schema overrides that allow tests to run against the intermediate MCR database
+ * schema for use during schema migration.
+ */
+trait McrReadNewSchemaOverride {
+
+       use PatchFileLocation;
+       use McrSchemaDetection;
+
+       /**
+        * @return int
+        */
+       protected function getMcrMigrationStage() {
+               return SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW;
+       }
+
+       /**
+        * @return string[]
+        */
+       protected function getMcrTablesToReset() {
+               return [ 'content', 'content_models', 'slots', 'slot_roles' ];
+       }
+
+       /**
+        * @override MediaWikiTestCase::getSchemaOverrides
+        * @return array[]
+        */
+       protected function getSchemaOverrides( IMaintainableDatabase $db ) {
+               $overrides = [
+                       'scripts' => [],
+                       'drop' => [],
+                       'create' => [],
+                       'alter' => [],
+               ];
+
+               if ( !$this->hasMcrTables( $db ) ) {
+                       $overrides['create'] = [ 'slots', 'content', 'slot_roles', 'content_models', ];
+                       $overrides['scripts'][] = $this->getSqlPatchPath( $db, 'patch-slot_roles' );
+                       $overrides['scripts'][] = $this->getSqlPatchPath( $db, 'patch-content_models' );
+                       $overrides['scripts'][] = $this->getSqlPatchPath( $db, 'patch-content' );
+                       $overrides['scripts'][] = $this->getSqlPatchPath( $db, 'patch-slots' );
+               }
+
+               if ( !$this->hasPreMcrFields( $db ) ) {
+                       $overrides['alter'][] = 'revision';
+                       $overrides['scripts'][] = $this->getSqlPatchPath( $db, 'create-pre-mcr-fields', __DIR__ );
+               }
+
+               return $overrides;
+       }
+
+}
index 5bf49d3..9a118d7 100644 (file)
@@ -26,6 +26,7 @@ class McrRevisionStoreDbTest extends RevisionStoreDbTestBase {
        protected function assertRevisionExistsInDatabase( RevisionRecord $rev ) {
                $numberOfSlots = count( $rev->getSlotRoles() );
 
+               // new schema is written
                $this->assertSelect(
                        'slots',
                        [ 'count(*)' ],
@@ -47,13 +48,6 @@ class McrRevisionStoreDbTest extends RevisionStoreDbTestBase {
                        $revQuery['joins']
                );
 
-               $this->assertSelect(
-                       'content',
-                       [ 'count(*)' ],
-                       [ 'content_address' => $rev->getSlot( 'main' )->getAddress() ],
-                       [ [ 1 ] ]
-               );
-
                parent::assertRevisionExistsInDatabase( $rev );
        }
 
index c984142..9d5dc29 100644 (file)
@@ -32,6 +32,7 @@ class McrWriteBothRevisionStoreDbTest extends RevisionStoreDbTestBase {
        }
 
        protected function assertRevisionExistsInDatabase( RevisionRecord $rev ) {
+               // New schema is being written
                $this->assertSelect(
                        'slots',
                        [ 'count(*)' ],
@@ -46,6 +47,16 @@ class McrWriteBothRevisionStoreDbTest extends RevisionStoreDbTestBase {
                        [ [ '1' ] ]
                );
 
+               // Legacy schema is still being written
+               $this->assertSelect(
+                       [ 'revision', 'text' ],
+                       [ 'count(*)' ],
+                       [ 'rev_id' => $rev->getId(), 'rev_text_id > 0' ],
+                       [ [ 1 ] ],
+                       [],
+                       [ 'text' => [ 'INNER JOIN', [ 'rev_text_id = old_id' ] ] ]
+               );
+
                parent::assertRevisionExistsInDatabase( $rev );
        }
 
@@ -159,8 +170,8 @@ class McrWriteBothRevisionStoreDbTest extends RevisionStoreDbTestBase {
                                                'role_name' => $db->addQuotes( 'main' ),
                                                'content_size' => 'slots.rev_len',
                                                'content_sha1' => 'slots.rev_sha1',
-                                               'content_address' =>
-                                                       'CONCAT(' . $db->addQuotes( 'tt:' ) . ',slots.rev_text_id)',
+                                               'content_address' => $db->buildConcat( [
+                                                       $db->addQuotes( 'tt:' ), 'slots.rev_text_id' ] ),
                                                'model_name' => 'slots.rev_content_model',
                                        ]
                                ),
index 2a54dbe..7275f90 100644 (file)
@@ -17,7 +17,7 @@ trait McrWriteBothSchemaOverride {
         * @return int
         */
        protected function getMcrMigrationStage() {
-               return MIGRATION_WRITE_BOTH;
+               return SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD;
        }
 
        /**
index 2337805..1d504de 100644 (file)
@@ -156,7 +156,7 @@ class NoContentModelRevisionStoreDbTest extends RevisionStoreDbTestBase {
                                                'content_size' => 'slots.rev_len',
                                                'content_sha1' => 'slots.rev_sha1',
                                                'content_address' =>
-                                                       'CONCAT(' . $db->addQuotes( 'tt:' ) . ',slots.rev_text_id)',
+                                                       $db->buildConcat( [ $db->addQuotes( 'tt:' ), 'slots.rev_text_id' ] ),
                                                'model_name' => 'NULL',
                                        ]
                                ),
index a27d2bb..cee5f96 100644 (file)
@@ -2,6 +2,7 @@
 namespace MediaWiki\Tests\Storage;
 
 use InvalidArgumentException;
+use MediaWiki\Storage\RevisionRecord;
 use Revision;
 use WikitextContent;
 
@@ -29,6 +30,20 @@ class PreMcrRevisionStoreDbTest extends RevisionStoreDbTestBase {
                return $row;
        }
 
+       protected function assertRevisionExistsInDatabase( RevisionRecord $rev ) {
+               // Legacy schema is still being written
+               $this->assertSelect(
+                       [ 'revision', 'text' ],
+                       [ 'count(*)' ],
+                       [ 'rev_id' => $rev->getId(), 'rev_text_id > 0' ],
+                       [ [ 1 ] ],
+                       [],
+                       [ 'text' => [ 'INNER JOIN', [ 'rev_text_id = old_id' ] ] ]
+               );
+
+               parent::assertRevisionExistsInDatabase( $rev );
+       }
+
        public function provideGetArchiveQueryInfo() {
                yield [
                        [
@@ -130,7 +145,7 @@ class PreMcrRevisionStoreDbTest extends RevisionStoreDbTestBase {
                                                'content_size' => 'slots.rev_len',
                                                'content_sha1' => 'slots.rev_sha1',
                                                'content_address' =>
-                                                       'CONCAT(' . $db->addQuotes( 'tt:' ) . ',slots.rev_text_id)',
+                                                       $db->buildConcat( [ $db->addQuotes( 'tt:' ), 'slots.rev_text_id' ] ),
                                                'model_name' => 'slots.rev_content_model',
                                        ]
                                ),
diff --git a/tests/phpunit/includes/Storage/RevisionStoreFactoryTest.php b/tests/phpunit/includes/Storage/RevisionStoreFactoryTest.php
deleted file mode 100644 (file)
index a5c18ac..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-<?php
-
-namespace MediaWiki\Tests\Storage;
-
-use ActorMigration;
-use CommentStore;
-use MediaWiki\Logger\LoggerFactory;
-use MediaWiki\Storage\NameTableStore;
-use MediaWiki\Storage\RevisionStore;
-use MediaWiki\Storage\RevisionStoreFactory;
-use MediaWiki\Storage\SqlBlobStore;
-use MediaWikiTestCase;
-use WANObjectCache;
-use Wikimedia\Rdbms\LoadBalancer;
-use Wikimedia\TestingAccessWrapper;
-
-class RevisionStoreFactoryTest extends MediaWikiTestCase {
-
-       public function testValidConstruction_doesntCauseErrors() {
-               new RevisionStoreFactory(
-                       $this->getMockLoadBalancer(),
-                       $this->getMockSqlBlobStore(),
-                       $this->getHashWANObjectCache(),
-                       $this->getMockCommentStore(),
-                       $this->getMockNameTableStore(),
-                       $this->getMockNameTableStore(),
-                       MIGRATION_OLD,
-                       ActorMigration::newMigration(),
-                       LoggerFactory::getInstance( 'someInstance' ),
-                       true
-               );
-               $this->assertTrue( true );
-       }
-
-       public function provideWikiIds() {
-               yield [ true ];
-               yield [ false ];
-               yield [ 'somewiki' ];
-               yield [ 'somewiki', MIGRATION_OLD , false ];
-               yield [ 'somewiki', MIGRATION_NEW , true ];
-       }
-
-       /**
-        * @dataProvider provideWikiIds
-        */
-       public function testGetRevisionStore(
-               $wikiId,
-               $mcrMigrationStage = MIGRATION_OLD,
-               $contentHandlerUseDb = true
-       ) {
-               $lb = $this->getMockLoadBalancer();
-               $blobStore = $this->getMockSqlBlobStore();
-               $cache = $this->getHashWANObjectCache();
-               $commentStore = $this->getMockCommentStore();
-               $contentModelStore = $this->getMockNameTableStore();
-               $slotRoleStore = $this->getMockNameTableStore();
-               $actorMigration = ActorMigration::newMigration();
-               $logger = LoggerFactory::getInstance( 'someInstance' );
-
-               $factory = new RevisionStoreFactory(
-                       $lb,
-                       $blobStore,
-                       $cache,
-                       $commentStore,
-                       $contentModelStore,
-                       $slotRoleStore,
-                       $mcrMigrationStage,
-                       $actorMigration,
-                       $logger,
-                       $contentHandlerUseDb
-               );
-
-               $store = $factory->getRevisionStore( $wikiId );
-               $wrapper = TestingAccessWrapper::newFromObject( $store );
-
-               // ensure the correct object type is returned
-               $this->assertInstanceOf( RevisionStore::class, $store );
-
-               // ensure the RevisionStore is for the given wikiId
-               $this->assertSame( $wikiId, $wrapper->wikiId );
-
-               // ensure all other required services are correctly set
-               $this->assertSame( $lb, $wrapper->loadBalancer );
-               $this->assertSame( $blobStore, $wrapper->blobStore );
-               $this->assertSame( $cache, $wrapper->cache );
-               $this->assertSame( $commentStore, $wrapper->commentStore );
-               $this->assertSame( $contentModelStore, $wrapper->contentModelStore );
-               $this->assertSame( $slotRoleStore, $wrapper->slotRoleStore );
-               $this->assertSame( $mcrMigrationStage, $wrapper->mcrMigrationStage );
-               $this->assertSame( $actorMigration, $wrapper->actorMigration );
-               $this->assertSame( $logger, $wrapper->logger );
-               $this->assertSame( $contentHandlerUseDb, $store->getContentHandlerUseDB() );
-       }
-
-       /**
-        * @return \PHPUnit_Framework_MockObject_MockObject|NameTableStore
-        */
-       private function getMockNameTableStore() {
-               return $this->getMockBuilder( NameTableStore::class )
-                       ->disableOriginalConstructor()->getMock();
-       }
-
-       /**
-        * @return \PHPUnit_Framework_MockObject_MockObject|LoadBalancer
-        */
-       private function getMockLoadBalancer() {
-               return $this->getMockBuilder( LoadBalancer::class )
-                       ->disableOriginalConstructor()->getMock();
-       }
-
-       /**
-        * @return \PHPUnit_Framework_MockObject_MockObject|SqlBlobStore
-        */
-       private function getMockSqlBlobStore() {
-               return $this->getMockBuilder( SqlBlobStore::class )
-                       ->disableOriginalConstructor()->getMock();
-       }
-
-       /**
-        * @return \PHPUnit_Framework_MockObject_MockObject|CommentStore
-        */
-       private function getMockCommentStore() {
-               return $this->getMockBuilder( CommentStore::class )
-                       ->disableOriginalConstructor()->getMock();
-       }
-
-       private function getHashWANObjectCache() {
-               return new WANObjectCache( [ 'cache' => new \HashBagOStuff() ] );
-       }
-
-}
index 727697c..90bd57a 100644 (file)
@@ -86,13 +86,17 @@ class RevisionStoreTest extends MediaWikiTestCase {
 
        public function provideSetContentHandlerUseDB() {
                return [
-                       // ContentHandlerUseDB can be true of false pre migration
-                       [ false, MIGRATION_OLD, false ],
-                       [ true, MIGRATION_OLD, false ],
-                       // During migration it can not be false
-                       [ false, MIGRATION_WRITE_BOTH, true ],
-                       // But it can be true
-                       [ true, MIGRATION_WRITE_BOTH, false ],
+                       // ContentHandlerUseDB can be true of false pre migration.
+                       [ false, SCHEMA_COMPAT_OLD, false ],
+                       [ true, SCHEMA_COMPAT_OLD, false ],
+                       // During and after migration it can not be false...
+                       [ false, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, true ],
+                       [ false, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, true ],
+                       [ false, SCHEMA_COMPAT_NEW, true ],
+                       // ...but it can be true.
+                       [ true, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, false ],
+                       [ true, SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, false ],
+                       [ true, SCHEMA_COMPAT_NEW, false ],
                ];
        }
 
@@ -482,8 +486,13 @@ class RevisionStoreTest extends MediaWikiTestCase {
 
        public function provideMigrationConstruction() {
                return [
-                       [ MIGRATION_OLD, false ],
-                       [ MIGRATION_WRITE_BOTH, false ],
+                       [ SCHEMA_COMPAT_OLD, false ],
+                       [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD, false ],
+                       [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_NEW, false ],
+                       [ SCHEMA_COMPAT_NEW, false ],
+                       [ SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_BOTH, true ],
+                       [ SCHEMA_COMPAT_WRITE_OLD | SCHEMA_COMPAT_READ_BOTH, true ],
+                       [ SCHEMA_COMPAT_WRITE_NEW | SCHEMA_COMPAT_READ_BOTH, true ],
                ];
        }
 
index 17a4182..e50e1bc 100644 (file)
@@ -38,7 +38,7 @@ class TestLogger extends \Psr\Log\AbstractLogger {
 
        /**
         * @param bool $collect Whether to collect logs. @see setCollect()
-        * @param callable $filter Filter logs before collecting/printing. Signature is
+        * @param callable|null $filter Filter logs before collecting/printing. Signature is
         *  string|null function ( string $message, string $level, array $context );
         * @param bool $collectContext Whether to keep the context passed to log
         *             (since 1.29, @see setCollectContext()).
index 7fd5f1d..75e73be 100644 (file)
@@ -105,7 +105,7 @@ class RandomImageGenerator {
         *
         * @param int $number Number of filenames to write
         * @param string $format Optional, must be understood by ImageMagick, such as 'jpg' or 'gif'
-        * @param string $dir Directory, optional (will default to current working directory)
+        * @param string|null $dir Directory, optional (will default to current working directory)
         * @return array Filenames we just wrote
         */
        function writeImages( $number, $format = 'jpg', $dir = null ) {
index e7588cb..08af755 100644 (file)
@@ -87,9 +87,9 @@ STR;
         * Checks that the request's result matches the expected results.
         * Assumes no rawcontinue and a complete batch.
         * @param array $values Array is a two element array( request, expected_results )
-        * @param array $session
+        * @param array|null $session
         * @param bool $appendModule
-        * @param User $user
+        * @param User|null $user
         */
        protected function check( $values, array $session = null,
                $appendModule = false, User $user = null
index 82ca66a..5cd55ba 100644 (file)
@@ -74,6 +74,10 @@ class LBFactoryTest extends MediaWikiTestCase {
                ];
        }
 
+       /**
+        * @covers LBFactory::getLocalDomainID()
+        * @covers LBFactory::resolveDomainID()
+        */
        public function testLBFactorySimpleServer() {
                global $wgDBserver, $wgDBname, $wgDBuser, $wgDBpassword, $wgDBtype, $wgSQLiteDataDir;
 
@@ -99,6 +103,9 @@ class LBFactoryTest extends MediaWikiTestCase {
                $dbr = $lb->getConnection( DB_REPLICA );
                $this->assertTrue( $dbr->getLBInfo( 'master' ), 'DB_REPLICA also gets the master' );
 
+               $this->assertSame( 'my_test_wiki', $factory->resolveDomainID( 'my_test_wiki' ) );
+               $this->assertSame( $factory->getLocalDomainID(), $factory->resolveDomainID( false ) );
+
                $factory->shutdown();
                $lb->closeAll();
        }
index cf605c1..5a748cc 100644 (file)
@@ -48,6 +48,10 @@ class LoadBalancerTest extends MediaWikiTestCase {
                ];
        }
 
+       /**
+        * @covers LoadBalancer::getLocalDomainID()
+        * @covers LoadBalancer::resolveDomainID()
+        */
        public function testWithoutReplica() {
                global $wgDBname;
 
@@ -64,6 +68,9 @@ class LoadBalancerTest extends MediaWikiTestCase {
                $ld = DatabaseDomain::newFromId( $lb->getLocalDomainID() );
                $this->assertEquals( $wgDBname, $ld->getDatabase(), 'local domain DB set' );
                $this->assertEquals( $this->dbPrefix(), $ld->getTablePrefix(), 'local domain prefix set' );
+               $this->assertSame( 'my_test_wiki', $lb->resolveDomainID( 'my_test_wiki' ) );
+               $this->assertSame( $ld->getId(), $lb->resolveDomainID( false ) );
+               $this->assertSame( $ld->getId(), $lb->resolveDomainID( $ld ) );
 
                $this->assertFalse( $called );
                $dbw = $lb->getConnection( DB_MASTER );
@@ -190,32 +197,38 @@ class LoadBalancerTest extends MediaWikiTestCase {
 
        private function assertWriteAllowed( Database $db ) {
                $table = $db->tableName( 'some_table' );
+               // Trigger a transaction so that rollback() will remove all the tables.
+               // Don't do this for MySQL/Oracle as they auto-commit transactions for DDL
+               // statements such as CREATE TABLE.
+               $useAtomicSection = in_array( $db->getType(), [ 'sqlite', 'postgres', 'mssql' ], true );
                try {
                        $db->dropTable( 'some_table' ); // clear for sanity
+                       $this->assertNotEquals( $db::STATUS_TRX_ERROR, $db->trxStatus() );
 
-                       // Trigger DBO_TRX to create a transaction so the flush below will
-                       // roll everything here back in sqlite. But don't actually do the
-                       // code below inside an atomic section becaue MySQL and Oracle
-                       // auto-commit transactions for DDL statements like CREATE TABLE.
-                       $db->startAtomic( __METHOD__ );
-                       $db->endAtomic( __METHOD__ );
-
+                       if ( $useAtomicSection ) {
+                               $db->startAtomic( __METHOD__ );
+                       }
                        // Use only basic SQL and trivial types for these queries for compatibility
                        $this->assertNotSame(
                                false,
                                $db->query( "CREATE TABLE $table (id INT, time INT)", __METHOD__ ),
                                "table created"
                        );
+                       $this->assertNotEquals( $db::STATUS_TRX_ERROR, $db->trxStatus() );
                        $this->assertNotSame(
                                false,
                                $db->query( "DELETE FROM $table WHERE id=57634126", __METHOD__ ),
                                "delete query"
                        );
+                       $this->assertNotEquals( $db::STATUS_TRX_ERROR, $db->trxStatus() );
                } finally {
-                       // Drop the table to clean up, ignoring any error.
-                       $db->query( "DROP TABLE $table", __METHOD__, true );
-                       // Rollback the DBO_TRX transaction for sqlite's benefit.
+                       if ( !$useAtomicSection ) {
+                               // Drop the table to clean up, ignoring any error.
+                               $db->dropTable( 'some_table' );
+                       }
+                       // Rollback the atomic section for sqlite's benefit.
                        $db->rollback( __METHOD__, 'flush' );
+                       $this->assertNotEquals( $db::STATUS_TRX_ERROR, $db->trxStatus() );
                }
        }
 
index 05e15a3..06772e5 100644 (file)
@@ -54,4 +54,11 @@ class HTMLFormTest extends MediaWikiTestCase {
                $this->assertContains( ' autocomplete="off"', $form->wrapForm( '' ) );
        }
 
+       public function testGetPreText() {
+               $preText = 'TEST';
+               $form = $this->newInstance();
+               $form->setPreText( $preText );
+               $this->assertSame( $preText, $form->getPreText() );
+       }
+
 }
index c3cddc6..e130b23 100644 (file)
@@ -12,6 +12,7 @@ use Wikimedia\Rdbms\ResultWrapper;
  */
 class DBConnRefTest extends PHPUnit\Framework\TestCase {
 
+       use MediaWikiCoversValidator;
        use PHPUnit4And6Compat;
 
        /**
@@ -106,18 +107,9 @@ class DBConnRefTest extends PHPUnit\Framework\TestCase {
                new DBConnRef( $lb, 17 ); // bad constructor argument
        }
 
-       public function testGetWikiID() {
-               $lb = $this->getMock( ILoadBalancer::class );
-
-               // getWikiID is optimized to not create a connection
-               $lb->expects( $this->never() )
-                       ->method( 'getConnection' );
-
-               $ref = new DBConnRef( $lb, [ DB_REPLICA, [], 'dummy', 0 ] );
-
-               $this->assertSame( 'dummy', $ref->getWikiID() );
-       }
-
+       /**
+        * @covers Wikimedia\Rdbms\DBConnRef::getDomainId
+        */
        public function testGetDomainID() {
                $lb = $this->getMock( ILoadBalancer::class );
 
index c12882b..8f4aae3 100644 (file)
@@ -440,6 +440,7 @@ class DatabaseTest extends PHPUnit\Framework\TestCase {
                        'numFields', 'numRows',
                        'open',
                        'strencode',
+                       'tableExists'
                ];
                $db = $this->getMockBuilder( Database::class )
                        ->disableOriginalConstructor()
index a4e8056..7a536df 100644 (file)
@@ -70,7 +70,7 @@ abstract class MediaWikiMediaTestCase extends MediaWikiTestCase {
         *
         * File must be in the path returned by getFilePath()
         * @param string $name File name
-        * @param string $type MIME type [optional]
+        * @param string|null $type MIME type [optional]
         * @return UnregisteredLocalFile
         */
        protected function dataFile( $name, $type = null ) {
index e0364c4..aaaa73b 100644 (file)
@@ -104,7 +104,7 @@ abstract class WikiPageDbTestBase extends MediaWikiLangTestCase {
        /**
         * @param string|Title|WikiPage $page
         * @param string $text
-        * @param int $model
+        * @param int|null $model
         *
         * @return WikiPage
         */
@@ -249,6 +249,8 @@ abstract class WikiPageDbTestBase extends MediaWikiLangTestCase {
                        CONTENT_MODEL_WIKITEXT
                );
 
+               $preparedEditBefore = $page->prepareContentForEdit( $content, null, $user1 );
+
                $status = $page->doEditContent( $content, "[[testing]] 1", EDIT_NEW, false, $user1 );
 
                $this->assertTrue( $status->isOK(), 'OK' );
@@ -259,9 +261,14 @@ abstract class WikiPageDbTestBase extends MediaWikiLangTestCase {
                $this->assertTrue( $status->value['revision']->getContent()->equals( $content ), 'equals' );
 
                $rev = $page->getRevision();
+               $preparedEditAfter = $page->prepareContentForEdit( $content, $rev, $user1 );
+
                $this->assertNotNull( $rev->getRecentChange() );
                $this->assertSame( $rev->getId(), (int)$rev->getRecentChange()->getAttribute( 'rc_this_oldid' ) );
 
+               // make sure that cached ParserOutput gets re-used throughout
+               $this->assertSame( $preparedEditBefore->output, $preparedEditAfter->output );
+
                $id = $page->getId();
 
                // Test page creation logging
@@ -342,6 +349,26 @@ abstract class WikiPageDbTestBase extends MediaWikiLangTestCase {
                $this->assertEquals( 2, $n, 'pagelinks should contain two links from the page' );
        }
 
+       /**
+        * @covers WikiPage::doEditContent
+        */
+       public function testDoEditContent_twice() {
+               $title = Title::newFromText( __METHOD__ );
+               $page = WikiPage::factory( $title );
+               $content = ContentHandler::makeContent( '$1 van $2', $title );
+
+               // Make sure we can do the exact same save twice.
+               // This tests checks that internal caches are reset as appropriate.
+               $status1 = $page->doEditContent( $content, __METHOD__ );
+               $status2 = $page->doEditContent( $content, __METHOD__ );
+
+               $this->assertTrue( $status1->isOK(), 'OK' );
+               $this->assertTrue( $status2->isOK(), 'OK' );
+
+               $this->assertTrue( isset( $status1->value['revision'] ), 'OK' );
+               $this->assertFalse( isset( $status2->value['revision'] ), 'OK' );
+       }
+
        /**
         * Undeletion is covered in PageArchiveTest::testUndeleteRevisions()
         * TODO: Revision deletion
@@ -2278,14 +2305,25 @@ more stuff
                        ->method( 'getParserOutput' )
                        ->willReturn( new ParserOutput( 'HTML' ) );
 
-               $updater = $page->newPageUpdater( $user );
+               $preparedEditBefore = $page->prepareContentForEdit( $content, null, $user );
+
+               // provide context, so the cache can be kept in place
+               $slotsUpdate = new revisionSlotsUpdate();
+               $slotsUpdate->modifyContent( 'main', $content );
+
+               $updater = $page->newPageUpdater( $user, $slotsUpdate );
                $updater->setContent( 'main', $content );
                $revision = $updater->saveRevision(
                        CommentStoreComment::newUnsavedComment( 'test' ),
                        EDIT_NEW
                );
 
+               $preparedEditAfter = $page->prepareContentForEdit( $content, $revision, $user );
+
                $this->assertSame( $revision->getId(), $page->getLatest() );
+
+               // Parsed output must remain cached throughout.
+               $this->assertSame( $preparedEditBefore->output, $preparedEditAfter->output );
        }
 
        /**
@@ -2311,7 +2349,7 @@ more stuff
 
                $updater1->prepareUpdate( $revision );
 
-               // Re-use updater with same revision or content
+               // Re-use updater with same revision or content, even if base changed
                $this->assertSame( $updater1, $page->getDerivedDataUpdater( $user, $revision ) );
 
                $slotsUpdate = RevisionSlotsUpdate::newFromContent(
@@ -2319,6 +2357,12 @@ more stuff
                );
                $this->assertSame( $updater1, $page->getDerivedDataUpdater( $user, null, $slotsUpdate ) );
 
+               // Don't re-use for edit if base revision ID changed
+               $this->assertNotSame(
+                       $updater1,
+                       $page->getDerivedDataUpdater( $user, null, $slotsUpdate, true )
+               );
+
                // Don't re-use with different user
                $updater2a = $page->getDerivedDataUpdater( $admin, null, $slotsUpdate );
                $updater2a->prepareContent( $admin, $slotsUpdate, false );
diff --git a/tests/phpunit/includes/page/WikiPageMcrReadNewDbTest.php b/tests/phpunit/includes/page/WikiPageMcrReadNewDbTest.php
new file mode 100644 (file)
index 0000000..9e2ff09
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+use MediaWiki\Tests\Storage\McrReadNewSchemaOverride;
+
+/**
+ * Tests WikiPage against the intermediate MCR DB schema for use during schema migration.
+ *
+ * @covers WikiPage
+ *
+ * @group WikiPage
+ * @group Storage
+ * @group ContentHandler
+ * @group Database
+ * @group medium
+ */
+class WikiPageMcrReadNewDbTest extends WikiPageDbTestBase {
+
+       use McrReadNewSchemaOverride;
+
+       protected function getContentHandlerUseDB() {
+               return true;
+       }
+
+}
index 810d1fe..86956f2 100644 (file)
@@ -589,4 +589,49 @@ mw.loader.register( [
                );
        }
 
+       /**
+        * @covers ResourceLoaderStartupModule::getAllModuleHashes
+        * @covers ResourceLoaderStartupModule::getDefinitionSummary
+        */
+       public function testGetVersionHash_varyModule() {
+               $context1 = $this->getResourceLoaderContext();
+               $rl1 = $context1->getResourceLoader();
+               $rl1->register( [
+                       'test.a' => new ResourceLoaderTestModule(),
+                       'test.b' => new ResourceLoaderTestModule(),
+               ] );
+               $module = new ResourceLoaderStartupModule();
+               $version1 = $module->getVersionHash( $context1 );
+
+               $context2 = $this->getResourceLoaderContext();
+               $rl2 = $context2->getResourceLoader();
+               $rl2->register( [
+                       'test.b' => new ResourceLoaderTestModule(),
+                       'test.c' => new ResourceLoaderTestModule(),
+               ] );
+               $module = new ResourceLoaderStartupModule();
+               $version2 = $module->getVersionHash( $context2 );
+
+               $context3 = $this->getResourceLoaderContext();
+               $rl3 = $context3->getResourceLoader();
+               $rl3->register( [
+                       'test.a' => new ResourceLoaderTestModule(),
+                       'test.b' => new ResourceLoaderTestModule( [ 'script' => 'different' ] ),
+               ] );
+               $module = new ResourceLoaderStartupModule();
+               $version3 = $module->getVersionHash( $context3 );
+
+               $this->assertEquals(
+                       $version1,
+                       $version2,
+                       'Module name is insignificant'
+               );
+
+               $this->assertNotEquals(
+                       $version1,
+                       $version3,
+                       'Hash change of any module impacts startup hash'
+               );
+       }
+
 }
index 5db1ad0..ef8fb4e 100644 (file)
@@ -78,9 +78,9 @@ class TestUtils {
        /**
         * If you need a Session for testing but don't want to create a backend to
         * construct one, use this.
-        * @param object $backend Object to serve as the SessionBackend
+        * @param object|null $backend Object to serve as the SessionBackend
         * @param int $index
-        * @param LoggerInterface $logger
+        * @param LoggerInterface|null $logger
         * @return Session
         */
        public static function getDummySession( $backend = null, $index = -1, $logger = null ) {
index e8259d3..6bc7c44 100644 (file)
@@ -94,7 +94,7 @@ class DummyContentForTesting extends AbstractContent {
 
        /**
         * @param Title $title
-        * @param int $revId Unused.
+        * @param int|null $revId Unused.
         * @param null|ParserOptions $options
         * @param bool $generateHtml Whether to generate Html (default: true). If false, the result
         *  of calling getText() on the ParserOutput object returned by this method is undefined.
index b71577c..f203907 100644 (file)
@@ -10,7 +10,7 @@ class DummyContentHandlerForTesting extends ContentHandler {
         * @see ContentHandler::serializeContent
         *
         * @param Content $content
-        * @param string $format
+        * @param string|null $format
         *
         * @return string
         */
@@ -22,7 +22,7 @@ class DummyContentHandlerForTesting extends ContentHandler {
         * @see ContentHandler::unserializeContent
         *
         * @param string $blob
-        * @param string $format Unused.
+        * @param string|null $format Unused.
         *
         * @return Content
         */
index 91bb186..e65f522 100644 (file)
@@ -92,7 +92,7 @@ class DummyNonTextContent extends AbstractContent {
 
        /**
         * @param Title $title
-        * @param int $revId Unused.
+        * @param int|null $revId Unused.
         * @param null|ParserOptions $options
         * @param bool $generateHtml Whether to generate Html (default: true). If false, the result
         *  of calling getText() on the ParserOutput object returned by this method is undefined.
index 9d91d4a..3294953 100644 (file)
@@ -10,7 +10,7 @@ class DummyNonTextContentHandler extends DummyContentHandlerForTesting {
         * @see ContentHandler::serializeContent
         *
         * @param Content $content
-        * @param string $format
+        * @param string|null $format
         *
         * @return string
         */
@@ -22,7 +22,7 @@ class DummyNonTextContentHandler extends DummyContentHandlerForTesting {
         * @see ContentHandler::unserializeContent
         *
         * @param string $blob
-        * @param string $format Unused.
+        * @param string|null $format Unused.
         *
         * @return Content
         */
index 720547a..4a462c0 100644 (file)
@@ -29,7 +29,7 @@ class DummySerializeErrorContentHandler extends DummyContentHandlerForTesting {
         * @see ContentHandler::unserializeContent
         *
         * @param string $blob
-        * @param string $format
+        * @param string|null $format
         *
         * @return Content
         */
index 4afc16b..de593d5 100644 (file)
@@ -49,6 +49,7 @@
                        $element.css( 'height' );
                        // eslint-disable-next-line no-unused-expressions
                        el.innerHTML;
+                       // eslint-disable-next-line no-self-assign
                        el.className = el.className;
                        // eslint-disable-next-line no-unused-expressions
                        document.documentElement.clientHeight;
index b59116e..cc74c89 100644 (file)
--- a/thumb.php
+++ b/thumb.php
@@ -626,7 +626,7 @@ function wfThumbErrorText( $status, $msgText ) {
  *
  * @param int $status
  * @param string $msgHtml HTML
- * @param string $msgText Short error description, for internal logging. Defaults to $msgHtml.
+ * @param string|null $msgText Short error description, for internal logging. Defaults to $msgHtml.
  *   Only used for HTTP 500 errors.
  * @param array $context Error context, for internal logging. Only used for HTTP 500 errors.
  * @return void