Merge "[search] Fix method call on null value"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 29 Oct 2015 20:41:00 +0000 (20:41 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 29 Oct 2015 20:41:00 +0000 (20:41 +0000)
167 files changed:
RELEASE-NOTES-1.27
autoload.php
composer.json
docs/extension.schema.json
includes/Block.php
includes/Collation.php
includes/DefaultSettings.php
includes/EditPage.php
includes/MovePage.php
includes/OutputPage.php
includes/SiteStats.php
includes/User.php
includes/api/ApiQuerySiteinfo.php
includes/api/ApiStashEdit.php
includes/api/i18n/ksh.json
includes/api/i18n/tcy.json [new file with mode: 0644]
includes/api/i18n/zh-hans.json
includes/cache/FileCacheBase.php
includes/cache/MessageBlobStore.php
includes/cache/MessageCache.php
includes/changes/EnhancedChangesList.php
includes/compat/RunningStatCompat.php [new file with mode: 0644]
includes/db/DBConnRef.php
includes/db/Database.php
includes/db/DatabaseError.php
includes/db/IDatabase.php
includes/db/IORMRow.php [deleted file]
includes/db/IORMTable.php [deleted file]
includes/db/ORMIterator.php [deleted file]
includes/db/ORMResult.php [deleted file]
includes/db/ORMRow.php [deleted file]
includes/db/ORMTable.php [deleted file]
includes/db/loadbalancer/LBFactory.php
includes/deferred/LinksUpdate.php
includes/filebackend/FileBackend.php
includes/filebackend/SwiftFileBackend.php
includes/filerepo/ForeignAPIRepo.php
includes/filerepo/file/File.php
includes/filerepo/file/ForeignAPIFile.php
includes/htmlform/HTMLRadioField.php
includes/installer/i18n/he.json
includes/installer/i18n/pl.json
includes/installer/i18n/roa-tara.json
includes/installer/i18n/yi.json
includes/jobqueue/Job.php
includes/jobqueue/JobQueueDB.php
includes/jobqueue/JobQueueGroup.php
includes/jobqueue/jobs/RefreshLinksJob.php
includes/libs/RunningStat.php [deleted file]
includes/libs/Xhprof.php
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/IExpiringStore.php [new file with mode: 0644]
includes/libs/objectcache/WANObjectCache.php
includes/media/DjVu.php
includes/media/FormatMetadata.php
includes/media/MediaHandler.php
includes/media/TransformationalImageHandler.php
includes/objectcache/ObjectCache.php
includes/page/WikiPage.php
includes/parser/DateFormatter.php
includes/parser/MWTidy.php
includes/parser/Parser.php
includes/parser/ParserOutput.php
includes/resourceloader/DerivativeResourceLoaderContext.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderContext.php
includes/resourceloader/ResourceLoaderEditToolbarModule.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderImageModule.php
includes/resourceloader/ResourceLoaderLanguageDataModule.php
includes/resourceloader/ResourceLoaderLanguageNamesModule.php
includes/resourceloader/ResourceLoaderModule.php
includes/resourceloader/ResourceLoaderSiteModule.php
includes/resourceloader/ResourceLoaderSkinModule.php
includes/resourceloader/ResourceLoaderSpecialCharacterDataModule.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php
includes/resourceloader/ResourceLoaderUserDefaultsModule.php
includes/resourceloader/ResourceLoaderUserGroupsModule.php
includes/resourceloader/ResourceLoaderUserModule.php
includes/resourceloader/ResourceLoaderUserOptionsModule.php
includes/resourceloader/ResourceLoaderUserTokensModule.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/skins/Skin.php
includes/specials/SpecialSearch.php
includes/specials/SpecialUserlogin.php
includes/tidy/RaggettWrapper.php
includes/upload/UploadBase.php
languages/Language.php
languages/i18n/as.json
languages/i18n/ast.json
languages/i18n/be-tarask.json
languages/i18n/bn.json
languages/i18n/bs.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/ckb.json
languages/i18n/crh-cyrl.json
languages/i18n/de.json
languages/i18n/es.json
languages/i18n/fa.json
languages/i18n/fr.json
languages/i18n/he.json
languages/i18n/id.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/ko.json
languages/i18n/ksh.json
languages/i18n/ku-latn.json
languages/i18n/mk.json
languages/i18n/mr.json
languages/i18n/nap.json
languages/i18n/olo.json
languages/i18n/pl.json
languages/i18n/ps.json
languages/i18n/ru.json
languages/i18n/sah.json
languages/i18n/sgs.json
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sv.json
languages/i18n/tcy.json
languages/i18n/uz.json
languages/i18n/war.json
languages/i18n/wo.json
languages/i18n/yi.json
languages/i18n/zh-hans.json
load.php
maintenance/archives/patch-msg_resource.sql
maintenance/mssql/tables.sql
maintenance/refreshLinks.php
maintenance/tables.sql
resources/Resources.php
resources/lib/oojs-ui/i18n/bs.json
resources/lib/oojs-ui/i18n/cs.json
resources/lib/oojs-ui/i18n/fi.json
resources/lib/oojs-ui/i18n/nl.json
resources/lib/oojs-ui/oojs-ui-apex-noimages.css
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-mediawiki-noimages.css
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui.js
resources/src/mediawiki.ui/components/buttons.less
resources/src/mediawiki.ui/components/checkbox.less
resources/src/mediawiki.ui/components/inputs.less
resources/src/mediawiki.ui/components/radio.less
resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js
resources/src/mediawiki/mediawiki.Upload.BookletLayout.js
resources/src/mediawiki/mediawiki.Upload.Dialog.js
resources/src/mediawiki/mediawiki.jqueryMsg.js
resources/src/mediawiki/mediawiki.raggett.css [new file with mode: 0644]
tests/TestsAutoLoader.php
tests/parser/parserTests.txt
tests/phpunit/includes/GlobalFunctions/wfTimestampTest.php
tests/phpunit/includes/ImportTest.php
tests/phpunit/includes/db/ORMRowTest.php [deleted file]
tests/phpunit/includes/db/ORMTableTest.php [deleted file]
tests/phpunit/includes/db/TestORMRowTest.php [deleted file]
tests/phpunit/includes/libs/RunningStatTest.php [deleted file]
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
tests/phpunit/includes/objectcache/MemcachedBagOStuffTest.php
tests/phpunit/languages/LanguageTest.php
tests/phpunit/phpunit.php
tests/phpunit/structure/ResourcesTest.php
tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js
thumb.php

index b3add69..d0a5c4e 100644 (file)
@@ -92,6 +92,8 @@ changes to languages because of Bugzilla reports.
 * ProfilerOutputUdp was removed. Note that there is a ProfilerOutputStats class.
 * WikiPage::doDeleteArticleReal() and WikiPage::doDeleteArticle() now
   ignore the 2nd and 3rd arguments (formerly $id and $commit).
+* Removed "loaderScripts" option from ResourceLoaderFileModule class.
+* Removed ORM-like wrapper added in 1.20.
 
 == Compatibility ==
 
index 731bdaa..65a84bf 100644 (file)
@@ -541,9 +541,8 @@ $wgAutoloadLocalClasses = array(
        'IDatabase' => __DIR__ . '/includes/db/IDatabase.php',
        'IEContentAnalyzer' => __DIR__ . '/includes/libs/IEContentAnalyzer.php',
        'IEUrlExtension' => __DIR__ . '/includes/libs/IEUrlExtension.php',
+       'IExpiringStore' => __DIR__ . '/includes/libs/objectcache/IExpiringStore.php',
        'IJobSpecification' => __DIR__ . '/includes/jobqueue/JobSpecification.php',
-       'IORMRow' => __DIR__ . '/includes/db/IORMRow.php',
-       'IORMTable' => __DIR__ . '/includes/db/IORMTable.php',
        'IP' => __DIR__ . '/includes/utils/IP.php',
        'IPSet' => __DIR__ . '/includes/compat/IPSetCompat.php',
        'IPTC' => __DIR__ . '/includes/media/IPTC.php',
@@ -847,10 +846,6 @@ $wgAutoloadLocalClasses = array(
        'OOUIHTMLForm' => __DIR__ . '/includes/htmlform/OOUIHTMLForm.php',
        'ORAField' => __DIR__ . '/includes/db/DatabaseOracle.php',
        'ORAResult' => __DIR__ . '/includes/db/DatabaseOracle.php',
-       'ORMIterator' => __DIR__ . '/includes/db/ORMIterator.php',
-       'ORMResult' => __DIR__ . '/includes/db/ORMResult.php',
-       'ORMRow' => __DIR__ . '/includes/db/ORMRow.php',
-       'ORMTable' => __DIR__ . '/includes/db/ORMTable.php',
        'ObjectCache' => __DIR__ . '/includes/objectcache/ObjectCache.php',
        'ObjectCacheSessionHandler' => __DIR__ . '/includes/objectcache/ObjectCacheSessionHandler.php',
        'ObjectFactory' => __DIR__ . '/includes/libs/ObjectFactory.php',
@@ -1076,7 +1071,7 @@ $wgAutoloadLocalClasses = array(
        'RollbackEdits' => __DIR__ . '/maintenance/rollbackEdits.php',
        'RowUpdateGenerator' => __DIR__ . '/includes/utils/RowUpdateGenerator.php',
        'RunJobs' => __DIR__ . '/maintenance/runJobs.php',
-       'RunningStat' => __DIR__ . '/includes/libs/RunningStat.php',
+       'RunningStat' => __DIR__ . '/includes/compat/RunningStatCompat.php',
        'SQLiteField' => __DIR__ . '/includes/db/DatabaseSqlite.php',
        'SVGMetadataExtractor' => __DIR__ . '/includes/media/SVGMetadataExtractor.php',
        'SVGReader' => __DIR__ . '/includes/media/SVGMetadataExtractor.php',
index 6209017..4aa9b92 100644 (file)
@@ -21,7 +21,7 @@
                "ext-iconv": "*",
                "liuggio/statsd-php-client": "1.0.16",
                "mediawiki/at-ease": "1.1.0",
-               "oojs/oojs-ui": "0.12.12",
+               "oojs/oojs-ui": "0.13.0",
                "oyejorge/less.php": "1.7.0.9",
                "php": ">=5.3.3",
                "psr/log": "1.0.0",
@@ -31,6 +31,7 @@
                "wikimedia/composer-merge-plugin": "1.2.1",
                "wikimedia/ip-set": "1.0.1",
                "wikimedia/relpath": "1.0.3",
+               "wikimedia/running-stat": "1.1.0",
                "wikimedia/utfnormal": "1.0.3",
                "wikimedia/wrappedstring": "2.0.0",
                "zordius/lightncandy": "0.21"
@@ -39,7 +40,7 @@
                "jakub-onderka/php-parallel-lint": "0.9",
                "justinrainbow/json-schema": "~1.3",
                "mediawiki/mediawiki-codesniffer": "0.4.0",
-               "monolog/monolog": "1.14.0",
+               "monolog/monolog": "~1.17.2",
                "nmred/kafka-php": "0.1.4",
                "phpunit/phpunit": "3.7.37",
                "wikimedia/avro": "1.7.7"
index 218a19c..dde4fa1 100644 (file)
                "ResourceModules": {
                        "type": "object",
                        "description": "ResourceLoader modules to register",
-                       "additionalProperties": false,
                        "patternProperties": {
                                "^[a-zA-Z0-9-\\.]+$": {
                                        "type": "object",
-                                       "description": "A single ResourceLoader module descriptor",
-                                       "properties": {
-                                               "localBasePath": {
-                                                       "type": "string",
-                                                       "description": "Base path to prepend to all local paths in $options. Defaults to $IP"
-                                               },
-                                               "remoteBasePath": {
-                                                       "type": "string",
-                                                       "description": "Base path to prepend to all remote paths in $options. Defaults to $wgScriptPath"
-                                               },
-                                               "remoteExtPath": {
-                                                       "type": "string",
-                                                       "description": "Equivalent of remoteBasePath, but relative to $wgExtensionAssetsPath"
-                                               },
-                                               "scripts": {
-                                                       "type": ["string", "array"],
-                                                       "description": "Scripts to always include (array of file paths)",
-                                                       "items": {
-                                                               "type": "string"
-                                                       }
-                                               },
-                                               "languageScripts": {
-                                                       "type": "object",
-                                                       "description": "Scripts to include in specific language contexts (mapping of language code to file path(s))",
-                                                       "patternProperties": {
-                                                               "^[a-zA-Z0-9-]{2,}$": {
-                                                                       "type": [
-                                                                               "string",
-                                                                               "array"
-                                                                       ],
+                                       "anyOf": [
+                                               {
+                                                       "description": "A ResourceLoaderFileModule definition",
+                                                       "additionalProperties": false,
+                                                       "properties": {
+                                                               "localBasePath": {
+                                                                       "type": "string",
+                                                                       "description": "Base path to prepend to all local paths in $options. Defaults to $IP"
+                                                               },
+                                                               "remoteBasePath": {
+                                                                       "type": "string",
+                                                                       "description": "Base path to prepend to all remote paths in $options. Defaults to $wgScriptPath"
+                                                               },
+                                                               "remoteExtPath": {
+                                                                       "type": "string",
+                                                                       "description": "Equivalent of remoteBasePath, but relative to $wgExtensionAssetsPath"
+                                                               },
+                                                               "scripts": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Scripts to always include (array of file paths)",
                                                                        "items": {
                                                                                "type": "string"
                                                                        }
-                                                               }
-                                                       }
-                                               },
-                                               "skinScripts": {
-                                                       "type": "object",
-                                                       "description": "Scripts to include in specific skin contexts (mapping of skin name to script(s)",
-                                                       "patternProperties": {
-                                                               ".+": {
-                                                                       "type": [
-                                                                               "string",
-                                                                               "array"
-                                                                       ],
+                                                               },
+                                                               "languageScripts": {
+                                                                       "type": "object",
+                                                                       "description": "Scripts to include in specific language contexts (mapping of language code to file path(s))",
+                                                                       "patternProperties": {
+                                                                               "^[a-zA-Z0-9-]{2,}$": {
+                                                                                       "type": [
+                                                                                               "string",
+                                                                                               "array"
+                                                                                       ],
+                                                                                       "items": {
+                                                                                               "type": "string"
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               },
+                                                               "skinScripts": {
+                                                                       "type": "object",
+                                                                       "description": "Scripts to include in specific skin contexts (mapping of skin name to script(s)",
+                                                                       "patternProperties": {
+                                                                               ".+": {
+                                                                                       "type": [
+                                                                                               "string",
+                                                                                               "array"
+                                                                                       ],
+                                                                                       "items": {
+                                                                                               "type": "string"
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               },
+                                                               "debugScripts": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Scripts to include in debug contexts",
                                                                        "items": {
                                                                                "type": "string"
                                                                        }
-                                                               }
-                                                       }
-                                               },
-                                               "debugScripts": {
-                                                       "type": ["string", "array"],
-                                                       "description": "Scripts to include in debug contexts",
-                                                       "items": {
-                                                               "type": "string"
-                                                       }
-                                               },
-                                               "loaderScripts": {
-                                                       "type": ["string", "array"],
-                                                       "description": "Scripts to include in the startup module",
-                                                       "items": {
-                                                               "type": "string"
-                                                       }
-                                               },
-                                               "dependencies": {
-                                                       "type": ["string", "array"],
-                                                       "description": "Modules which must be loaded before this module",
-                                                       "items": {
-                                                               "type": "string"
-                                                       }
-                                               },
-                                               "styles": {
-                                                       "type": ["string", "array", "object"],
-                                                       "description": "Styles to always load",
-                                                       "items": {
-                                                               "type": "string"
-                                                       }
-                                               },
-                                               "skinStyles": {
-                                                       "type": "object",
-                                                       "description": "Styles to include in specific skin contexts (mapping of skin name to style(s))",
-                                                       "patternProperties": {
-                                                               ".+": {
-                                                                       "type": [
-                                                                               "string",
-                                                                               "array"
-                                                                       ],
+                                                               },
+                                                               "loaderScripts": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Scripts to include in the startup module",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "dependencies": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Modules which must be loaded before this module",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "styles": {
+                                                                       "type": ["string", "array", "object"],
+                                                                       "description": "Styles to always load",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "skinStyles": {
+                                                                       "type": "object",
+                                                                       "description": "Styles to include in specific skin contexts (mapping of skin name to style(s))",
+                                                                       "patternProperties": {
+                                                                               ".+": {
+                                                                                       "type": [
+                                                                                               "string",
+                                                                                               "array"
+                                                                                       ],
+                                                                                       "items": {
+                                                                                               "type": "string"
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                               },
+                                                               "messages": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "Messages to always load",
+                                                                       "items": {
+                                                                               "type": "string"
+                                                                       }
+                                                               },
+                                                               "group": {
+                                                                       "type": "string",
+                                                                       "description": "Group which this module should be loaded together with"
+                                                               },
+                                                               "position": {
+                                                                       "type": "string",
+                                                                       "description": "Position on the page to load this module at",
+                                                                       "enum": [
+                                                                               "bottom",
+                                                                               "top"
+                                                                       ]
+                                                               },
+                                                               "templates": {
+                                                                       "type": "object",
+                                                                       "description": "Templates to be loaded for client-side usage"
+                                                               },
+                                                               "targets": {
+                                                                       "type": ["string", "array"],
+                                                                       "description": "ResourceLoader target the module can run on",
                                                                        "items": {
                                                                                "type": "string"
                                                                        }
                                                                }
                                                        }
                                                },
-                                               "messages": {
-                                                       "type": ["string", "array"],
-                                                       "description": "Messages to always load",
-                                                       "items": {
-                                                               "type": "string"
+                                               {
+                                                       "description": "A ResourceLoaderImageModule definition",
+                                                       "additionalProperties": false,
+                                                       "properties": {
+                                                               "class": {
+                                                                       "enum": ["ResourceLoaderImageModule"]
+                                                               },
+                                                               "data": {
+                                                                       "type": "string"
+                                                               },
+                                                               "prefix": {
+                                                                       "type": "string"
+                                                               },
+                                                               "selector": {
+                                                                       "type": "string"
+                                                               },
+                                                               "selectorWithoutVariant": {
+                                                                       "type": "string"
+                                                               },
+                                                               "selectorWithVariant": {
+                                                                       "type": "string"
+                                                               },
+                                                               "variants": {
+                                                                       "type": "object"
+                                                               },
+                                                               "images": {
+                                                                       "type": "object"
+                                                               },
+                                                               "position": {
+                                                                       "enum": [
+                                                                               "top",
+                                                                               "bottom"
+                                                                       ]
+                                                               }
                                                        }
                                                },
-                                               "group": {
-                                                       "type": "string",
-                                                       "description": "Group which this module should be loaded together with"
-                                               },
-                                               "position": {
-                                                       "type": "string",
-                                                       "description": "Position on the page to load this module at",
-                                                       "enum": [
-                                                               "bottom",
-                                                               "top"
-                                                       ]
-                                               },
-                                               "templates": {
-                                                       "type": "object",
-                                                       "description": "Templates to be loaded for client-side usage"
+                                               {
+                                                       "description": "An arbitrary ResourceLoaderModule definition",
+                                                       "properties": {
+                                                               "class": {
+                                                                       "type": "string",
+                                                                       "pattern": "^((?!ResourceLoader(File|Image)Module).)*$"
+                                                               }
+                                                       },
+                                                       "required": ["class"]
                                                }
-                                       }
+                                       ]
                                }
                        }
                },
index 5dee23e..b57b3e8 100644 (file)
@@ -681,10 +681,10 @@ class Block {
        public static function isWhitelistedFromAutoblocks( $ip ) {
                // Try to get the autoblock_whitelist from the cache, as it's faster
                // than getting the msg raw and explode()'ing it.
-
-               $lines = ObjectCache::getMainWANInstance()->getWithSetCallback(
+               $cache = ObjectCache::getMainWANInstance();
+               $lines = $cache->getWithSetCallback(
                        wfMemcKey( 'ipb', 'autoblock', 'whitelist' ),
-                       86400,
+                       $cache::TTL_DAY,
                        function () {
                                return explode( "\n",
                                        wfMessage( 'autoblock_whitelist' )->inContentLanguage()->plain() );
index 40e8627..7fa5c6e 100644 (file)
@@ -508,7 +508,7 @@ class IcuCollation extends Collation {
 
                // Save to cache
                $this->firstLetterData = $data;
-               $cache->set( $cacheKey, $data, 86400 * 7 /* 1 week */ );
+               $cache->set( $cacheKey, $data, $cache::TTL_WEEK );
                return $data;
        }
 
index 910c121..e916938 100644 (file)
@@ -2263,7 +2263,8 @@ $wgMainWANCache = false;
  * the value is an associative array of parameters. The "cacheId" parameter is
  * a cache identifier from $wgObjectCaches. The "relayerConfig" parameter is an
  * array used to construct an EventRelayer object. The "pool" parameter is a
- * string that is used as a PubSub channel prefix.
+ * string that is used as a PubSub channel prefix. The "loggroup" parameter
+ * controls where log events are sent.
  *
  * @since 1.26
  */
@@ -3396,14 +3397,14 @@ $wgMangleFlashPolicy = true;
 /** @} */ # End of output format settings }
 
 /*************************************************************************//**
- * @name   Resource loader settings
+ * @name   ResourceLoader settings
  * @{
  */
 
 /**
  * Client-side resource modules.
  *
- * Extensions should add their resource loader module definitions
+ * Extensions should add their ResourceLoader module definitions
  * to the $wgResourceModules variable.
  *
  * @par Example:
@@ -3528,7 +3529,7 @@ $wgResourceLoaderSources = array();
 $wgResourceBasePath = null;
 
 /**
- * Maximum time in seconds to cache resources served by the resource loader.
+ * Maximum time in seconds to cache resources served by ResourceLoader.
  * Used to set last modified headers (max-age/s-maxage).
  *
  * Following options to distinguish:
@@ -3735,7 +3736,7 @@ $wgResourceLoaderStorageVersion = 1;
  */
 $wgAllowSiteCSSOnRestrictedPages = false;
 
-/** @} */ # End of resource loader settings }
+/** @} */ # End of ResourceLoader settings }
 
 /*************************************************************************//**
  * @name   Page title and interwiki link settings
@@ -6741,6 +6742,7 @@ $wgJobClasses = array(
        'ThumbnailRender' => 'ThumbnailRenderJob',
        'recentChangesUpdate' => 'RecentChangesUpdateJob',
        'refreshLinksPrioritized' => 'RefreshLinksJob', // for cascading protection
+       'refreshLinksDynamic' => 'RefreshLinksJob', // for pages with dynamic content
        'activityUpdateJob' => 'ActivityUpdateJob',
        'enqueue' => 'EnqueueJob', // local queue for multi-DC setups
        'null' => 'NullJob'
index 81f35f9..2bddc3e 100644 (file)
@@ -2041,7 +2041,7 @@ class EditPage {
        }
 
        /**
-        * @param Title $title
+        * @param User $user
         * @param string $oldModel
         * @param string $newModel
         * @param string $reason
@@ -2059,26 +2059,26 @@ class EditPage {
                $log->publish( $logid );
        }
 
-
        /**
         * Register the change of watch status
         */
        protected function updateWatchlist() {
                global $wgUser;
 
-               if ( $wgUser->isLoggedIn()
-                       && $this->watchthis != $wgUser->isWatched( $this->mTitle, WatchedItem::IGNORE_USER_RIGHTS )
-               ) {
-                       $fname = __METHOD__;
-                       $title = $this->mTitle;
-                       $watch = $this->watchthis;
-
-                       // Do this in its own transaction to reduce contention...
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->onTransactionIdle( function () use ( $dbw, $title, $watch, $wgUser, $fname ) {
-                               WatchAction::doWatchOrUnwatch( $watch, $title, $wgUser );
-                       } );
+               if ( !$wgUser->isLoggedIn() ) {
+                       return;
                }
+
+               $user = $wgUser;
+               $title = $this->mTitle;
+               $watch = $this->watchthis;
+               // Do this in its own transaction to reduce contention...
+               DeferredUpdates::addCallableUpdate( function () use ( $user, $title, $watch ) {
+                       if ( $watch == $user->isWatched( $title, WatchedItem::IGNORE_USER_RIGHTS ) ) {
+                               return; // nothing to change
+                       }
+                       WatchAction::doWatchOrUnwatch( $watch, $title, $user );
+               } );
        }
 
        /**
index 0f9374a..736cd8d 100644 (file)
@@ -371,7 +371,7 @@ class MovePage {
 
                $dbw->endAtomic( __METHOD__ );
 
-               $params = array( $this->oldTitle, $this->newTitle, $user, $pageid, $redirid, $reason );
+               $params = array( &$this->oldTitle, &$this->newTitle, &$user, $pageid, $redirid, $reason );
                $dbw->onTransactionIdle( function () use ( $params ) {
                        Hooks::run( 'TitleMoveComplete', $params );
                } );
index d29ec54..726fec1 100644 (file)
@@ -50,7 +50,7 @@ class OutputPage extends ContextSource {
 
        /**
         * @var array Additional stylesheets. Looks like this is for extensions.
-        *   Might be replaced by resource loader.
+        *   Might be replaced by ResourceLoader.
         */
        protected $mExtStyles = array();
 
@@ -132,7 +132,7 @@ class OutputPage extends ContextSource {
        private $mLanguageLinks = array();
 
        /**
-        * Used for JavaScript (pre resource loader)
+        * Used for JavaScript (predates ResourceLoader)
         * @todo We should split JS / CSS.
         * mScripts content is inserted as is in "<head>" by Skin. This might
         * contain either a link to a stylesheet or inline CSS.
@@ -151,8 +151,6 @@ class OutputPage extends ContextSource {
        /** @var array Array of elements in "<head>". Parser might add its own headers! */
        protected $mHeadItems = array();
 
-       // @todo FIXME: Next 5 variables probably come from the resource loader
-
        /** @var array */
        protected $mModules = array();
 
@@ -224,7 +222,7 @@ class OutputPage extends ContextSource {
        /**
         * @var bool Comes from the parser. This was probably made to load CSS/JS
         * only if we had "<gallery>". Used directly in CategoryPage.php.
-        * Looks like resource loader can replace this.
+        * Looks like ResourceLoader can replace this.
         */
        public $mNoGallery = false;
 
@@ -568,8 +566,8 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Add one or more modules recognized by the resource loader. Modules added
-        * through this function will be loaded by the resource loader when the
+        * Add one or more modules recognized by ResourceLoader. Modules added
+        * through this function will be loaded by ResourceLoader when the
         * page loads.
         *
         * @param string|array $modules Module name (string) or array of module names
@@ -591,8 +589,8 @@ class OutputPage extends ContextSource {
        }
 
        /**
-        * Add only JS of one or more modules recognized by the resource loader. Module
-        * scripts added through this function will be loaded by the resource loader when
+        * Add only JS of one or more modules recognized by ResourceLoader. Module
+        * scripts added through this function will be loaded by ResourceLoader when
         * the page loads.
         *
         * @param string|array $modules Module name (string) or array of module names
@@ -610,25 +608,11 @@ class OutputPage extends ContextSource {
         * @return array Array of module names
         */
        public function getModuleStyles( $filter = false, $position = null ) {
-               // T97420
-               $resourceLoader = $this->getResourceLoader();
-
-               foreach ( $this->mModuleStyles as $val ) {
-                       $module = $resourceLoader->getModule( $val );
-
-                       if ( $module instanceof ResourceLoaderModule && $module->isPositionDefault() ) {
-                               $warning = __METHOD__ . ': style module should define its position explicitly: ' .
-                                       $val . ' ' . get_class( $module );
-                               wfDebugLog( 'resourceloader', $warning );
-                               wfLogWarning( $warning );
-                       }
-               }
-
                return $this->getModules( $filter, $position, 'mModuleStyles' );
        }
 
        /**
-        * Add only CSS of one or more modules recognized by the resource loader.
+        * Add only CSS of one or more modules recognized by ResourceLoader.
         *
         * Module styles added through this function will be added using standard link CSS
         * tags, rather than as a combined Javascript and CSS package. Thus, they will
@@ -3074,10 +3058,6 @@ class OutputPage extends ContextSource {
                        ResourceLoaderModule::TYPE_SCRIPTS
                );
 
-               $links[] = $this->makeResourceLoaderLink( $this->getModuleStyles( true, 'bottom' ),
-                       ResourceLoaderModule::TYPE_STYLES
-               );
-
                // Modules requests - let the client calculate dependencies and batch requests as it likes
                // Only load modules that have marked themselves for loading at the bottom
                $modules = $this->getModules( true, 'bottom' );
@@ -3143,9 +3123,6 @@ class OutputPage extends ContextSource {
         * @return string
         */
        function getBottomScripts() {
-               // In case the skin wants to add bottom CSS
-               $this->getSkin()->setupSkinUserCss( $this );
-
                return $this->getScriptsForBottomQueue();
        }
 
@@ -3684,7 +3661,7 @@ class OutputPage extends ContextSource {
                $otherTags = array(); // Tags to append after the normal <link> tags
                $resourceLoader = $this->getResourceLoader();
 
-               $moduleStyles = $this->getModuleStyles( true, 'top' );
+               $moduleStyles = $this->getModuleStyles();
 
                // Per-site custom styles
                $moduleStyles[] = 'site';
index 5b361b9..33bab65 100644 (file)
@@ -180,9 +180,10 @@ class SiteStats {
         * @return int
         */
        static function numberingroup( $group ) {
-               return ObjectCache::getMainWANInstance()->getWithSetCallback(
+               $cache = ObjectCache::getMainWANInstance();
+               return $cache->getWithSetCallback(
                        wfMemcKey( 'SiteStats', 'groupcounts', $group ),
-                       3600,
+                       $cache::TTL_HOUR,
                        function ( $oldValue, &$ttl, array &$setOpts ) use ( $group ) {
                                $dbr = wfGetDB( DB_SLAVE );
 
index e1c9e35..a6b897d 100644 (file)
@@ -438,10 +438,11 @@ class User implements IDBAccessObject {
                        $data[$name] = $this->$name;
                }
                $data['mVersion'] = self::VERSION;
-               $key = wfMemcKey( 'user', 'id', $this->mId );
-
                $opts = Database::getCacheSetOptions( wfGetDB( DB_SLAVE ) );
-               ObjectCache::getMainWANInstance()->set( $key, $data, 3600, $opts );
+
+               $cache = ObjectCache::getMainWANInstance();
+               $key = wfMemcKey( 'user', 'id', $this->mId );
+               $cache->set( $key, $data, $cache::TTL_HOUR, $opts );
        }
 
        /** @name newFrom*() static factory methods */
@@ -1739,8 +1740,6 @@ class User implements IDBAccessObject {
                        return false;
                }
 
-               global $wgMemc;
-
                $limits = $wgRateLimits[$action];
                $keys = array();
                $id = $this->getId();
@@ -1795,11 +1794,13 @@ class User implements IDBAccessObject {
                        $keys[wfMemcKey( 'limiter', $action, 'user', $id )] = $userLimit;
                }
 
+               $cache = ObjectCache::getLocalClusterInstance();
+
                $triggered = false;
                foreach ( $keys as $key => $limit ) {
                        list( $max, $period ) = $limit;
                        $summary = "(limit $max in {$period}s)";
-                       $count = $wgMemc->get( $key );
+                       $count = $cache->get( $key );
                        // Already pinged?
                        if ( $count ) {
                                if ( $count >= $max ) {
@@ -1812,11 +1813,11 @@ class User implements IDBAccessObject {
                        } else {
                                wfDebug( __METHOD__ . ": adding record for $key $summary\n" );
                                if ( $incrBy > 0 ) {
-                                       $wgMemc->add( $key, 0, intval( $period ) ); // first ping
+                                       $cache->add( $key, 0, intval( $period ) ); // first ping
                                }
                        }
                        if ( $incrBy > 0 ) {
-                               $wgMemc->incr( $key, $incrBy );
+                               $cache->incr( $key, $incrBy );
                        }
                }
 
index c13b30b..3d56128 100644 (file)
@@ -800,7 +800,7 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                                'subscribers' => array_map( array( 'SpecialVersion', 'arrayToString' ), $subscribers ),
                        );
 
-                       ApiResult::setArrayType( $arr['subscribers'], 'BCarray' );
+                       ApiResult::setArrayType( $arr['subscribers'], 'array' );
                        ApiResult::setIndexedTagName( $arr['subscribers'], 's' );
                        $data[] = $arr;
                }
index e87fc97..ebddd51 100644 (file)
@@ -39,7 +39,7 @@ class ApiStashEdit extends ApiBase {
        const ERROR_UNCACHEABLE = 'uncacheable';
 
        public function execute() {
-               global $wgMemc;
+               $cache = ObjectCache::getLocalClusterInstance();
 
                $user = $this->getUser();
                $params = $this->extractRequestParams();
@@ -111,11 +111,10 @@ class ApiStashEdit extends ApiBase {
                // De-duplicate requests on the same key
                if ( $user->pingLimiter( 'stashedit' ) ) {
                        $status = 'ratelimited';
-               } elseif ( $wgMemc->lock( $key, 0, 30 ) ) {
+               } elseif ( $cache->lock( $key, 0, 30 ) ) {
                        /** @noinspection PhpUnusedLocalVariableInspection */
-                       $unlocker = new ScopedCallback( function() use ( $key ) {
-                               global $wgMemc;
-                               $wgMemc->unlock( $key );
+                       $unlocker = new ScopedCallback( function() use ( $cache, $key ) {
+                               $cache->unlock( $key );
                        } );
                        $status = self::parseAndStash( $page, $content, $user );
                } else {
@@ -133,7 +132,7 @@ class ApiStashEdit extends ApiBase {
         * @since 1.25
         */
        public static function parseAndStash( WikiPage $page, Content $content, User $user ) {
-               global $wgMemc;
+               $cache = ObjectCache::getLocalClusterInstance();
 
                $format = $content->getDefaultFormat();
                $editInfo = $page->prepareContentForEdit( $content, null, $user, $format, false );
@@ -146,7 +145,7 @@ class ApiStashEdit extends ApiBase {
                        );
 
                        if ( $stashInfo ) {
-                               $ok = $wgMemc->set( $key, $stashInfo, $ttl );
+                               $ok = $cache->set( $key, $stashInfo, $ttl );
                                if ( $ok ) {
                                        wfDebugLog( 'StashEdit', "Cached parser output for key '$key'." );
                                        return self::ERROR_NONE;
@@ -173,7 +172,7 @@ class ApiStashEdit extends ApiBase {
         * will do nothing. Provided the values are cacheable, they will be stored
         * in memcached so that final edit submission might make use of them.
         *
-        * @param Article|WikiPage $page Page title
+        * @param Page|Article|WikiPage $page Page title
         * @param Content $content Proposed page content
         * @param Content $pstContent The result of preSaveTransform() on $content
         * @param ParserOutput $pOut The result of getParserOutput() on $pstContent
@@ -186,7 +185,7 @@ class ApiStashEdit extends ApiBase {
                Page $page, Content $content, Content $pstContent, ParserOutput $pOut,
                ParserOptions $pstOpts, ParserOptions $pOpts, $timestamp
        ) {
-               global $wgMemc;
+               $cache = ObjectCache::getLocalClusterInstance();
 
                // getIsPreview() controls parser function behavior that references things
                // like user/revision that don't exists yet. The user/text should already
@@ -219,7 +218,7 @@ class ApiStashEdit extends ApiBase {
                        return false;
                }
 
-               $ok = $wgMemc->set( $key, $stashInfo, $ttl );
+               $ok = $cache->set( $key, $stashInfo, $ttl );
                if ( !$ok ) {
                        wfDebugLog( 'StashEdit', "Failed to cache preview parser output for key '$key'." );
                } else {
@@ -247,17 +246,17 @@ class ApiStashEdit extends ApiBase {
         * @return stdClass|bool Returns false on cache miss
         */
        public static function checkCache( Title $title, Content $content, User $user ) {
-               global $wgMemc;
+               $cache = ObjectCache::getLocalClusterInstance();
 
                $key = self::getStashKey( $title, $content, $user );
-               $editInfo = $wgMemc->get( $key );
+               $editInfo = $cache->get( $key );
                if ( !is_object( $editInfo ) ) {
                        $start = microtime( true );
                        // We ignore user aborts and keep parsing. Block on any prior parsing
                        // so as to use it's results and make use of the time spent parsing.
-                       if ( $wgMemc->lock( $key, 30, 30 ) ) {
-                               $editInfo = $wgMemc->get( $key );
-                               $wgMemc->unlock( $key );
+                       if ( $cache->lock( $key, 30, 30 ) ) {
+                               $editInfo = $cache->get( $key );
+                               $cache->unlock( $key );
                        }
                        $sec = microtime( true ) - $start;
                        if ( $sec > .01 ) {
index a13ad0a..d307fa7 100644 (file)
@@ -60,6 +60,7 @@
        "apihelp-delete-param-title": "De Övverschreff vun dä Sigg zom fottschmiiße. Kam_mer nit zersamme met „<var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1pageid</var>“ bruche.",
        "apihelp-delete-param-pageid": "De Kännong vun dä Sigg zom fottschmiiße. Kam_mer nit zersamme met „<var lang=\"en\" xml:lang=\"en\" dir=\"ltr\">$1title</var>“ bruche.",
        "apihelp-delete-param-reason": "Der Jrond för et Fottschmiiße. Wann dä nit aanjejovve es, weed ene automattesch usjräschnete Jrond jenumme.",
+       "apihelp-delete-param-tags": "Donn de Makehronge änndere, di för dä Enndraach em Logbohch jesaz wähde sulle.",
        "apihelp-delete-param-watch": "Donn di Sigg en däm aktoälle Metmaacher sing Oppaßleß opnämme.",
        "apihelp-delete-param-watchlist": "Donn di Sigg op däm aktoälle Metmaacher sing Oppaßleß udder nemm se druß fott, donn de Enschtällonge nämme, udder donn de Oppaßleß jaa nit verändere.",
        "apihelp-delete-param-unwatch": "Schmihß di Sigg us däm aktoälle Metmaacher singe Oppaßless erus.",
        "apihelp-query+allredirects-example-unique": "Ongerscheidlijje Sigge opleste.",
        "apihelp-query+allredirects-example-unique-generator": "Hollt alle Zihlsigge un makkehr di (noch) nit doh sin.",
        "apihelp-query+allredirects-example-generator": "Holl de Sigge met de Ömleidonge.",
+       "apihelp-query+allrevisions-description": "Donn alle Väsjohne opleßte.",
+       "apihelp-query+allrevisions-param-start": "Et Dattom un de Zigg vun woh aff opjezallt wähde sull.",
+       "apihelp-query+allrevisions-param-end": "Et Dattom un de Zigg bes woh hen opjezallt wähde sull.",
+       "apihelp-query+allrevisions-param-user": "Donn blohß Väsjohne vun heh däm Metmaacher opleßte.",
+       "apihelp-query+allrevisions-param-excludeuser": "Donn kein Väsjohne vun heh däm Metmaacher opleßte.",
+       "apihelp-query+allrevisions-param-namespace": "Donn blohß Sigge en heh däm Appachtemang opleßte.",
+       "apihelp-query+allrevisions-param-generatetitles": "Wann als ene  Jenerahtor enjesaz, brängk dat Övverschreffte un kein Kännonge vun Väsjohne.",
+       "apihelp-query+allrevisions-example-user": "Donn de läzde fuffzisch Beijdrähsch vum Metmaacher „<kbd lang=\"en\" xml:lang=\"en\" dir=\"ltr\">Example</kbd>“ opleßte.",
+       "apihelp-query+allrevisions-example-ns-main": "Donn de eezde fuffzisch Väsjohne em Houp-Appachemang opleßte.",
        "apihelp-query+alltransclusions-param-from": "De Övverschreff vun dä ennjeföhschte Sigg, woh de Leß medd aanfange sull.",
        "apihelp-query+alltransclusions-param-to": "De Övverschreff vun dä ennjeföhschte Sigg, woh et Zälle ophühre sull.",
        "apihelp-query+alltransclusions-param-prefix": "Söhk noh alle dä ennjeföhschte Sigge ier Övverschreffte, di met heh däm Täx aanfange.",
        "apihelp-query+recentchanges-param-limit": "Wi vill Änderonge ensjesammp zem aanzeije?",
        "apihelp-query+recentchanges-param-type": "Wat för en Zoot Änneronge aanzeije?",
        "apihelp-query+recentchanges-param-toponly": "Bloß Änderonge aanzeije, woh de neußte Väsjohn beij eruß kohm.",
+       "apihelp-query+recentchanges-param-generaterevisions": "Wann als ene  Jenerahtor enjesaz, brängk dat Kännonge vun Väsjohne un kein Övverschreffte. Enndrähsch en de neußte Änderonge der ohne en Väsjohnskännong, alsu de miehste Logbohchenndrähsch, bränge jaa nix.",
        "apihelp-query+recentchanges-example-simple": "Zeijsch de {{LCFIRST:{{int:recentchanges}}}}",
        "apihelp-query+redirects-description": "Jiff alle Ömleijdonge noh dä aanjejovve Sigge uß.",
        "apihelp-query+redirects-param-prop": "Wat för en Eijeschaffte holle:",
diff --git a/includes/api/i18n/tcy.json b/includes/api/i18n/tcy.json
new file mode 100644 (file)
index 0000000..bdee17e
--- /dev/null
@@ -0,0 +1,10 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Bharathesha Alasandemajalu"
+               ]
+       },
+       "apihelp-query+watchlist-param-type": "ವಾ ನಮೂನೆದ ಬದಲಾವಣೆ ತೊಜವೋಡು",
+       "apihelp-query+watchlist-paramvalue-type-external": "ಪಿದಯೀದ ಬದಲಾವಣೇ",
+       "apihelp-query+watchlist-paramvalue-type-new": "ಪಾಲೆ ಉಂಡುಮಾನ್ಪುನಾ"
+}
index 93617f9..b4275b5 100644 (file)
        "apihelp-query+recentchanges-param-token": "请改用<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>。",
        "apihelp-query+recentchanges-param-limit": "返回总计更新数。",
        "apihelp-query+recentchanges-param-type": "显示的更改类型。",
+       "apihelp-query+recentchanges-param-generaterevisions": "当作为生成器使用时,生成修订ID而不是标题。不带关联修订ID的最近更改记录(例如大多数日志记录)将不会生成任何东西。",
        "apihelp-query+recentchanges-example-simple": "最近更改列表。",
        "apihelp-query+recentchanges-example-generator": "获取有关最近未巡查更改的页面信息。",
        "apihelp-query+redirects-description": "返回至指定页面的所有重定向。",
index 5632596..360420b 100644 (file)
@@ -235,8 +235,8 @@ abstract class FileCacheBase {
         * @return void
         */
        public function incrMissesRecent( WebRequest $request ) {
-               global $wgMemc;
                if ( mt_rand( 0, self::MISS_FACTOR - 1 ) == 0 ) {
+                       $cache = ObjectCache::getLocalClusterInstance();
                        # Get a large IP range that should include the user  even if that
                        # person's IP address changes
                        $ip = $request->getIP();
@@ -249,17 +249,17 @@ abstract class FileCacheBase {
 
                        # Bail out if a request already came from this range...
                        $key = wfMemcKey( get_class( $this ), 'attempt', $this->mType, $this->mKey, $ip );
-                       if ( $wgMemc->get( $key ) ) {
+                       if ( $cache->get( $key ) ) {
                                return; // possibly the same user
                        }
-                       $wgMemc->set( $key, 1, self::MISS_TTL_SEC );
+                       $cache->set( $key, 1, self::MISS_TTL_SEC );
 
                        # Increment the number of cache misses...
                        $key = $this->cacheMissKey();
-                       if ( $wgMemc->get( $key ) === false ) {
-                               $wgMemc->set( $key, 1, self::MISS_TTL_SEC );
+                       if ( $cache->get( $key ) === false ) {
+                               $cache->set( $key, 1, self::MISS_TTL_SEC );
                        } else {
-                               $wgMemc->incr( $key );
+                               $cache->incr( $key );
                        }
                }
        }
@@ -269,9 +269,9 @@ abstract class FileCacheBase {
         * @return int
         */
        public function getMissesRecent() {
-               global $wgMemc;
+               $cache = ObjectCache::getLocalClusterInstance();
 
-               return self::MISS_FACTOR * $wgMemc->get( $this->cacheMissKey() );
+               return self::MISS_FACTOR * $cache->get( $this->cacheMissKey() );
        }
 
        /**
index b7c70c1..6290eae 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource message blobs storage used by the resource loader.
+ * Resource message blobs storage used by ResourceLoader.
  *
  * 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
@@ -23,8 +23,8 @@
  */
 
 /**
- * This class provides access to the resource message blobs storage used by
- * the ResourceLoader.
+ * This class provides access to the resource message blobs storage used
+ * by ResourceLoader.
  *
  * A message blob is a JSON object containing the interface messages for a
  * certain resource in a certain language. These message blobs are cached
index 09e9077..bcfa792 100644 (file)
@@ -574,7 +574,7 @@ class MessageCache {
 
                foreach ( $codes as $code ) {
                        $sidebarKey = wfMemcKey( 'sidebar', $code );
-                       $this->wanCache->delete( $sidebarKey, 5 );
+                       $this->wanCache->delete( $sidebarKey );
                }
 
                // Update the message in the message blob store
index b59437e..0883982 100644 (file)
@@ -608,7 +608,7 @@ class EnhancedChangesList extends ChangesList {
                }
 
                # Diff and hist links
-               if ( $type  == RC_LOG && $type != RC_CATEGORIZE ) {
+               if ( $type != RC_LOG && $type != RC_CATEGORIZE ) {
                        $query['action'] = 'history';
                        $data['historyLink'] = $this->getDiffHistLinks( $rcObj, $query );
                }
diff --git a/includes/compat/RunningStatCompat.php b/includes/compat/RunningStatCompat.php
new file mode 100644 (file)
index 0000000..ac82f44
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * Backward-compatibility alias for RunningStat, which was moved out
+ * into an external library and namespaced.
+ *
+ * @deprecated since 1.27 use RunningStat\RunningStat directly
+ */
+class RunningStat extends RunningStat\RunningStat {
+}
index 53e9a50..5443eeb 100644 (file)
@@ -91,6 +91,10 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
+       public function writesPending() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
        public function writesOrCallbacksPending() {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
index 57c28bb..cf774fa 100644 (file)
@@ -441,6 +441,14 @@ abstract class DatabaseBase implements IDatabase {
                return $this->mDoneWrites ?: false;
        }
 
+       /**
+        * @return bool Whether there is a transaction open with possible write queries
+        * @since 1.27
+        */
+       public function writesPending() {
+               return $this->mTrxLevel && $this->mTrxDoneWrites;
+       }
+
        /**
         * Returns true if there is a transaction open with possible write
         * queries or transaction pre-commit/idle callbacks waiting on it to finish.
@@ -3819,16 +3827,20 @@ abstract class DatabaseBase implements IDatabase {
         *
         * @param IDatabase $db1
         * @param IDatabase ...
-        * @return array ('lag': highest lag, 'since': lowest estimate UNIX timestamp)
+        * @return array Map of values:
+        *   - lag: highest lag of any of the DBs
+        *   - since: oldest UNIX timestamp of any of the DB lag estimates
+        *   - pending: whether any of the DBs have uncommitted changes
         * @since 1.27
         */
        public static function getCacheSetOptions( IDatabase $db1 ) {
-               $res = array( 'lag' => 0, 'since' => INF );
+               $res = array( 'lag' => 0, 'since' => INF, 'pending' => false );
                foreach ( func_get_args() as $db ) {
                        /** @var IDatabase $db */
                        $status = $db->getSessionLagStatus();
                        $res['lag'] = max( $res['lag'], $status['lag'] );
                        $res['since'] = min( $res['since'], $status['since'] );
+                       $res['pending'] = $res['pending'] ?: $db->writesPending();
                }
 
                return $res;
index e6c285e..78d26ae 100644 (file)
@@ -78,6 +78,10 @@ class DBExpectedError extends DBError {
                return $s;
        }
 
+       function getPageTitle() {
+               return $this->msg( 'databaseerror', 'Database error' );
+       }
+
        /**
         * @return string
         */
@@ -456,4 +460,7 @@ class DBUnexpectedError extends DBError {
  * @ingroup Database
  */
 class DBReadOnlyError extends DBExpectedError {
+       function getPageTitle() {
+               return $this->msg( 'readonly', 'Database is locked' );
+       }
 }
index d110583..19eb126 100644 (file)
@@ -159,6 +159,12 @@ interface IDatabase {
         */
        public function lastDoneWrites();
 
+       /**
+        * @return bool Whether there is a transaction open with possible write queries
+        * @since 1.27
+        */
+       public function writesPending();
+
        /**
         * Returns true if there is a transaction open with possible write
         * queries or transaction pre-commit/idle callbacks waiting on it to finish.
diff --git a/includes/db/IORMRow.php b/includes/db/IORMRow.php
deleted file mode 100644 (file)
index c66cddf..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-<?php
-/**
- * Interface for representing objects that are stored in some DB table.
- * This is basically an ORM-like wrapper around rows in database tables that
- * aims to be both simple and very flexible. It is centered around an associative
- * array of fields and various methods to do common interaction with the database.
- *
- * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @since 1.20
- *
- * @file
- * @ingroup ORM
- *
- * @license GNU GPL v2 or later
- * @author Jeroen De Dauw < jeroendedauw@gmail.com >
- */
-
-interface IORMRow {
-       /**
-        * Load the specified fields from the database.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param array|null $fields
-        * @param bool $override
-        * @param bool $skipLoaded
-        *
-        * @return bool Success indicator
-        */
-       public function loadFields( $fields = null, $override = true, $skipLoaded = false );
-
-       /**
-        * Gets the value of a field.
-        *
-        * @since 1.20
-        *
-        * @param string $name
-        * @param mixed $default
-        *
-        * @throws MWException
-        * @return mixed
-        */
-       public function getField( $name, $default = null );
-
-       /**
-        * Gets the value of a field but first loads it if not done so already.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param string $name
-        *
-        * @return mixed
-        */
-       public function loadAndGetField( $name );
-
-       /**
-        * Remove a field.
-        *
-        * @since 1.20
-        *
-        * @param string $name
-        */
-       public function removeField( $name );
-
-       /**
-        * Returns the objects database id.
-        *
-        * @since 1.20
-        *
-        * @return int|null
-        */
-       public function getId();
-
-       /**
-        * Sets the objects database id.
-        *
-        * @since 1.20
-        *
-        * @param int|null $id
-        */
-       public function setId( $id );
-
-       /**
-        * Gets if a certain field is set.
-        *
-        * @since 1.20
-        *
-        * @param string $name
-        *
-        * @return bool
-        */
-       public function hasField( $name );
-
-       /**
-        * Gets if the id field is set.
-        *
-        * @since 1.20
-        *
-        * @return bool
-        */
-       public function hasIdField();
-
-       /**
-        * Sets multiple fields.
-        *
-        * @since 1.20
-        *
-        * @param array $fields The fields to set
-        * @param bool $override Override already set fields with the provided values?
-        */
-       public function setFields( array $fields, $override = true );
-
-       /**
-        * Serializes the object to an associative array which
-        * can then easily be converted into JSON or similar.
-        *
-        * @since 1.20
-        *
-        * @param null|array $fields
-        * @param bool $incNullId
-        *
-        * @return array
-        */
-       public function toArray( $fields = null, $incNullId = false );
-
-       /**
-        * Load the default values, via getDefaults.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param bool $override
-        */
-       public function loadDefaults( $override = true );
-
-       /**
-        * Writes the answer to the database, either updating it
-        * when it already exists, or inserting it when it doesn't.
-        *
-        * @since 1.20
-        *
-        * @param string|null $functionName
-        * @deprecated since 1.22
-        *
-        * @return bool Success indicator
-        */
-       public function save( $functionName = null );
-
-       /**
-        * Removes the object from the database.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @return bool Success indicator
-        */
-       public function remove();
-
-       /**
-        * Return the names and values of the fields.
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getFields();
-
-       /**
-        * Return the names of the fields.
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getSetFieldNames();
-
-       /**
-        * Sets the value of a field.
-        * Strings can be provided for other types,
-        * so this method can be called from unserialization handlers.
-        *
-        * @since 1.20
-        *
-        * @param string $name
-        * @param mixed $value
-        *
-        * @throws MWException
-        */
-       public function setField( $name, $value );
-
-       /**
-        * Add an amount (can be negative) to the specified field (needs to be numeric).
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param string $field
-        * @param int $amount
-        *
-        * @return bool Success indicator
-        */
-       public function addToField( $field, $amount );
-
-       /**
-        * Return the names of the fields.
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getFieldNames();
-
-       /**
-        * Computes and updates the values of the summary fields.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param array|string|null $summaryFields
-        */
-       public function loadSummaryFields( $summaryFields = null );
-
-       /**
-        * Sets the value for the @see $updateSummaries field.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param bool $update
-        */
-       public function setUpdateSummaries( $update );
-
-       /**
-        * Sets the value for the @see $inSummaryMode field.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param bool $summaryMode
-        */
-       public function setSummaryMode( $summaryMode );
-
-       /**
-        * Returns the table this IORMRow is a row in.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @return IORMTable
-        */
-       public function getTable();
-}
diff --git a/includes/db/IORMTable.php b/includes/db/IORMTable.php
deleted file mode 100644 (file)
index b2527f9..0000000
+++ /dev/null
@@ -1,520 +0,0 @@
-<?php
-/**
- * Interface for objects representing a single database table.
- * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @since 1.20
- *
- * @file
- * @ingroup ORM
- *
- * @license GNU GPL v2 or later
- * @author Jeroen De Dauw < jeroendedauw@gmail.com >
- */
-
-interface IORMTable {
-       /**
-        * Returns the name of the database table objects of this type are stored in.
-        *
-        * @since 1.20
-        *
-        * @return string
-        */
-       public function getName();
-
-       /**
-        * Returns the name of a IORMRow implementing class that
-        * represents single rows in this table.
-        *
-        * @since 1.20
-        *
-        * @return string
-        */
-       public function getRowClass();
-
-       /**
-        * Returns an array with the fields and their types this object contains.
-        * This corresponds directly to the fields in the database, without prefix.
-        *
-        * field name => type
-        *
-        * Allowed types:
-        * * id
-        * * str
-        * * int
-        * * float
-        * * bool
-        * * array
-        * * blob
-        *
-        * @todo Get rid of the id field. Every row instance needs to have one so
-        *   this is just causing hassle at various locations by requiring an extra
-        *   check for field name.
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getFields();
-
-       /**
-        * Returns a list of default field values.
-        * field name => field value
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getDefaults();
-
-       /**
-        * Returns a list of the summary fields.
-        * These are fields that cache computed values, such as the amount of linked objects of $type.
-        * This is relevant as one might not want to do actions such as log changes when these get updated.
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getSummaryFields();
-
-       /**
-        * Selects the specified fields of the records matching the provided
-        * conditions and returns them as DBDataObject. Field names get prefixed.
-        *
-        * @see DatabaseBase::select()
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $fields
-        * @param array $conditions
-        * @param array $options
-        * @param string|null $functionName
-        *
-        * @return ORMResult The result set
-        * @throws DBQueryError If the query failed (even if the database was in ignoreErrors mode)
-        */
-       public function select( $fields = null, array $conditions = array(),
-               array $options = array(), $functionName = null );
-
-       /**
-        * Selects the specified fields of the records matching the provided
-        * conditions and returns them as DBDataObject. Field names get prefixed.
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $fields
-        * @param array $conditions
-        * @param array $options
-        * @param string|null $functionName
-        *
-        * @return array Array of self
-        */
-       public function selectObjects( $fields = null, array $conditions = array(),
-               array $options = array(), $functionName = null );
-
-       /**
-        * Do the actual select.
-        *
-        * @since 1.20
-        *
-        * @param null|string|array $fields
-        * @param array $conditions
-        * @param array $options
-        * @param null|string $functionName
-        *
-        * @return ResultWrapper
-        * @throws DBQueryError If the query failed (even if the database was in ignoreErrors mode)
-        */
-       public function rawSelect( $fields = null, array $conditions = array(),
-               array $options = array(), $functionName = null );
-
-       /**
-        * Selects the specified fields of the records matching the provided
-        * conditions and returns them as associative arrays.
-        * Provided field names get prefixed.
-        * Returned field names will not have a prefix.
-        *
-        * When $collapse is true:
-        * If one field is selected, each item in the result array will be this field.
-        * If two fields are selected, each item in the result array will have as key
-        * the first field and as value the second field.
-        * If more then two fields are selected, each item will be an associative array.
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $fields
-        * @param array $conditions
-        * @param array $options
-        * @param bool $collapse Set to false to always return each result row as associative array.
-        * @param string|null $functionName
-        *
-        * @return array Array of array
-        */
-       public function selectFields( $fields = null, array $conditions = array(),
-               array $options = array(), $collapse = true, $functionName = null );
-
-       /**
-        * Selects the specified fields of the first matching record.
-        * Field names get prefixed.
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $fields
-        * @param array $conditions
-        * @param array $options
-        * @param string|null $functionName
-        *
-        * @return IORMRow|bool False on failure
-        */
-       public function selectRow( $fields = null, array $conditions = array(),
-               array $options = array(), $functionName = null );
-
-       /**
-        * Selects the specified fields of the records matching the provided
-        * conditions. Field names do NOT get prefixed.
-        *
-        * @since 1.20
-        *
-        * @param array $fields
-        * @param array $conditions
-        * @param array $options
-        * @param string|null $functionName
-        *
-        * @return ResultWrapper
-        */
-       public function rawSelectRow( array $fields, array $conditions = array(),
-               array $options = array(), $functionName = null );
-
-       /**
-        * Selects the specified fields of the first record matching the provided
-        * conditions and returns it as an associative array, or false when nothing matches.
-        * This method makes use of selectFields and expects the same parameters and
-        * returns the same results (if there are any, if there are none, this method returns false).
-        * @see IORMTable::selectFields
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $fields
-        * @param array $conditions
-        * @param array $options
-        * @param bool $collapse Set to false to always return each result row as associative array.
-        * @param string|null $functionName
-        *
-        * @return mixed|array|bool False on failure
-        */
-       public function selectFieldsRow( $fields = null, array $conditions = array(),
-               array $options = array(), $collapse = true, $functionName = null );
-
-       /**
-        * Returns if there is at least one record matching the provided conditions.
-        * Condition field names get prefixed.
-        *
-        * @since 1.20
-        *
-        * @param array $conditions
-        *
-        * @return bool
-        */
-       public function has( array $conditions = array() );
-
-       /**
-        * Checks if the table exists
-        *
-        * @since 1.21
-        *
-        * @return bool
-        */
-       public function exists();
-
-       /**
-        * Returns the amount of matching records.
-        * Condition field names get prefixed.
-        *
-        * Note that this can be expensive on large tables.
-        * In such cases you might want to use DatabaseBase::estimateRowCount instead.
-        *
-        * @since 1.20
-        *
-        * @param array $conditions
-        * @param array $options
-        *
-        * @return int
-        */
-       public function count( array $conditions = array(), array $options = array() );
-
-       /**
-        * Removes the object from the database.
-        *
-        * @since 1.20
-        *
-        * @param array $conditions
-        * @param string|null $functionName
-        *
-        * @return bool Success indicator
-        */
-       public function delete( array $conditions, $functionName = null );
-
-       /**
-        * Get API parameters for the fields supported by this object.
-        *
-        * @since 1.20
-        *
-        * @param bool $requireParams
-        * @param bool $setDefaults
-        *
-        * @return array
-        */
-       public function getAPIParams( $requireParams = false, $setDefaults = false );
-
-       /**
-        * Returns an array with the fields and their descriptions.
-        *
-        * field name => field description
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getFieldDescriptions();
-
-       /**
-        * Get the database type used for read operations.
-        *
-        * @since 1.20
-        *
-        * @return int DB_ enum
-        */
-       public function getReadDb();
-
-       /**
-        * Set the database type to use for read operations.
-        *
-        * @param int $db
-        *
-        * @since 1.20
-        */
-       public function setReadDb( $db );
-
-       /**
-        * Get the ID of the any foreign wiki to use as a target for database operations
-        *
-        * @since 1.20
-        *
-        * @return string|bool The target wiki, in a form that LBFactory
-        *   understands (or false if the local wiki is used)
-        */
-       public function getTargetWiki();
-
-       /**
-        * Set the ID of the any foreign wiki to use as a target for database operations
-        *
-        * @param string|bool $wiki The target wiki, in a form that LBFactory
-        *   understands (or false if the local wiki shall be used)
-        *
-        * @since 1.20
-        */
-       public function setTargetWiki( $wiki );
-
-       /**
-        * Get the database type used for read operations.
-        * This is to be used instead of wfGetDB.
-        *
-        * @see LoadBalancer::getConnection
-        *
-        * @since 1.20
-        *
-        * @return DatabaseBase The database object
-        */
-       public function getReadDbConnection();
-
-       /**
-        * Get the database type used for read operations.
-        * This is to be used instead of wfGetDB.
-        *
-        * @see LoadBalancer::getConnection
-        *
-        * @since 1.20
-        *
-        * @return DatabaseBase The database object
-        */
-       public function getWriteDbConnection();
-
-       /**
-        * Get the database type used for read operations.
-        *
-        * @see wfGetLB
-        *
-        * @since 1.20
-        *
-        * @return LoadBalancer The database load balancer object
-        */
-       public function getLoadBalancer();
-
-       /**
-        * Releases the lease on the given database connection. This is useful mainly
-        * for connections to a foreign wiki. It does nothing for connections to the local wiki.
-        *
-        * @see LoadBalancer::reuseConnection
-        *
-        * @param DatabaseBase $db The database
-        *
-        * @since 1.20
-        */
-       public function releaseConnection( DatabaseBase $db );
-
-       /**
-        * Update the records matching the provided conditions by
-        * setting the fields that are keys in the $values param to
-        * their corresponding values.
-        *
-        * @since 1.20
-        *
-        * @param array $values
-        * @param array $conditions
-        *
-        * @return bool Success indicator
-        */
-       public function update( array $values, array $conditions = array() );
-
-       /**
-        * Computes the values of the summary fields of the objects matching the provided conditions.
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $summaryFields
-        * @param array $conditions
-        */
-       public function updateSummaryFields( $summaryFields = null, array $conditions = array() );
-
-       /**
-        * Takes in an associative array with field names as keys and
-        * their values as value. The field names are prefixed with the
-        * db field prefix.
-        *
-        * @since 1.20
-        *
-        * @param array $values
-        *
-        * @return array
-        */
-       public function getPrefixedValues( array $values );
-
-       /**
-        * Takes in a field or array of fields and returns an
-        * array with their prefixed versions, ready for db usage.
-        *
-        * @since 1.20
-        *
-        * @param array $fields
-        *
-        * @return array
-        */
-       public function getPrefixedFields( array $fields );
-
-       /**
-        * Takes in a field and returns an it's prefixed version, ready for db usage.
-        *
-        * @since 1.20
-        *
-        * @param string|array $field
-        *
-        * @return string
-        */
-       public function getPrefixedField( $field );
-
-       /**
-        * Takes an array of field names with prefix and returns the unprefixed equivalent.
-        *
-        * @since 1.20
-        * @deprecated since 1.25, will be removed
-        *
-        * @param string[] $fieldNames
-        *
-        * @return string[]
-        */
-       public function unprefixFieldNames( array $fieldNames );
-
-       /**
-        * Takes a field name with prefix and returns the unprefixed equivalent.
-        *
-        * @since 1.20
-        * @deprecated since 1.25, will be removed
-        *
-        * @param string $fieldName
-        *
-        * @return string
-        */
-       public function unprefixFieldName( $fieldName );
-
-       /**
-        * Get an array with fields from a database result,
-        * that can be fed directly to the constructor or
-        * to setFields.
-        *
-        * @since 1.20
-        *
-        * @param stdClass $result
-        *
-        * @return array
-        */
-       public function getFieldsFromDBResult( stdClass $result );
-
-       /**
-        * Get a new instance of the class from a database result.
-        *
-        * @since 1.20
-        *
-        * @param stdClass $result
-        *
-        * @return IORMRow
-        */
-       public function newRowFromDBResult( stdClass $result );
-
-       /**
-        * Get a new instance of the class from an array.
-        *
-        * @since 1.20
-        *
-        * @param array $data
-        * @param bool $loadDefaults
-        *
-        * @return IORMRow
-        */
-       public function newRow( array $data, $loadDefaults = false );
-
-       /**
-        * Return the names of the fields.
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getFieldNames();
-
-       /**
-        * Gets if the object can take a certain field.
-        *
-        * @since 1.20
-        *
-        * @param string $name
-        *
-        * @return bool
-        */
-       public function canHaveField( $name );
-}
diff --git a/includes/db/ORMIterator.php b/includes/db/ORMIterator.php
deleted file mode 100644 (file)
index e8104b6..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-/**
- * Interface for Iterators containing IORMRows.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @since 1.20
- *
- * @file
- * @ingroup ORM
- *
- * @license GNU GPL v2 or later
- * @author Jeroen De Dauw < jeroendedauw@gmail.com >
- */
-interface ORMIterator extends Iterator {
-}
diff --git a/includes/db/ORMResult.php b/includes/db/ORMResult.php
deleted file mode 100644 (file)
index 327d20d..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-<?php
-/**
- * ORMIterator that takes a ResultWrapper object returned from
- * a select operation returning IORMRow objects (ie IORMTable::select).
- *
- * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @since 1.20
- *
- * @file ORMResult.php
- * @ingroup ORM
- *
- * @license GNU GPL v2 or later
- * @author Jeroen De Dauw < jeroendedauw@gmail.com >
- */
-
-class ORMResult implements ORMIterator {
-       /**
-        * @var ResultWrapper
-        */
-       protected $res;
-
-       /**
-        * @var int
-        */
-       protected $key;
-
-       /**
-        * @var IORMRow
-        */
-       protected $current;
-
-       /**
-        * @var IORMTable
-        */
-       protected $table;
-
-       /**
-        * @param IORMTable $table
-        * @param ResultWrapper $res
-        */
-       public function __construct( IORMTable $table, ResultWrapper $res ) {
-               $this->table = $table;
-               $this->res = $res;
-               $this->key = 0;
-               $this->setCurrent( $this->res->current() );
-       }
-
-       /**
-        * @param bool|object $row
-        */
-       protected function setCurrent( $row ) {
-               if ( $row === false ) {
-                       $this->current = false;
-               } else {
-                       $this->current = $this->table->newRowFromDBResult( $row );
-               }
-       }
-
-       /**
-        * @return int
-        */
-       public function count() {
-               return $this->res->numRows();
-       }
-
-       /**
-        * @return bool
-        */
-       public function isEmpty() {
-               return $this->res->numRows() === 0;
-       }
-
-       /**
-        * @return IORMRow
-        */
-       public function current() {
-               return $this->current;
-       }
-
-       /**
-        * @return int
-        */
-       public function key() {
-               return $this->key;
-       }
-
-       public function next() {
-               $row = $this->res->next();
-               $this->setCurrent( $row );
-               $this->key++;
-       }
-
-       public function rewind() {
-               $this->res->rewind();
-               $this->key = 0;
-               $this->setCurrent( $this->res->current() );
-       }
-
-       /**
-        * @return bool
-        */
-       public function valid() {
-               return $this->current !== false;
-       }
-}
diff --git a/includes/db/ORMRow.php b/includes/db/ORMRow.php
deleted file mode 100644 (file)
index b0bade3..0000000
+++ /dev/null
@@ -1,593 +0,0 @@
-<?php
-/**
- * Abstract base class for representing objects that are stored in some DB table.
- * This is basically an ORM-like wrapper around rows in database tables that
- * aims to be both simple and very flexible. It is centered around an associative
- * array of fields and various methods to do common interaction with the database.
- *
- * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @since 1.20
- *
- * @file ORMRow.php
- * @ingroup ORM
- *
- * @license GNU GPL v2 or later
- * @author Jeroen De Dauw < jeroendedauw@gmail.com >
- */
-
-class ORMRow implements IORMRow {
-       /**
-        * The fields of the object.
-        * field name (w/o prefix) => value
-        *
-        * @since 1.20
-        * @var array
-        */
-       protected $fields = array( 'id' => null );
-
-       /**
-        * If the object should update summaries of linked items when changed.
-        * For example, update the course_count field in universities when a course in courses is deleted.
-        * Settings this to false can prevent needless updating work in situations
-        * such as deleting a university, which will then delete all it's courses.
-        *
-        * @deprecated since 1.22
-        * @since 1.20
-        * @var bool
-        */
-       protected $updateSummaries = true;
-
-       /**
-        * Indicates if the object is in summary mode.
-        * This mode indicates that only summary fields got updated,
-        * which allows for optimizations.
-        *
-        * @deprecated since 1.22
-        * @since 1.20
-        * @var bool
-        */
-       protected $inSummaryMode = false;
-
-       /**
-        * @deprecated since 1.22
-        * @since 1.20
-        * @var ORMTable|null
-        */
-       protected $table;
-
-       /**
-        * Constructor.
-        *
-        * @since 1.20
-        *
-        * @param IORMTable|null $table Deprecated since 1.22
-        * @param array|null $fields
-        * @param bool $loadDefaults Deprecated since 1.22
-        */
-       public function __construct( IORMTable $table = null, $fields = null, $loadDefaults = false ) {
-               $this->table = $table;
-
-               if ( !is_array( $fields ) ) {
-                       $fields = array();
-               }
-
-               if ( $loadDefaults ) {
-                       $fields = array_merge( $this->table->getDefaults(), $fields );
-               }
-
-               $this->setFields( $fields );
-       }
-
-       /**
-        * Load the specified fields from the database.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param array|null $fields
-        * @param bool $override
-        * @param bool $skipLoaded
-        *
-        * @return bool Success indicator
-        */
-       public function loadFields( $fields = null, $override = true, $skipLoaded = false ) {
-               if ( is_null( $this->getId() ) ) {
-                       return false;
-               }
-
-               if ( is_null( $fields ) ) {
-                       $fields = array_keys( $this->table->getFields() );
-               }
-
-               if ( $skipLoaded ) {
-                       $fields = array_diff( $fields, array_keys( $this->fields ) );
-               }
-
-               if ( !empty( $fields ) ) {
-                       $result = $this->table->rawSelectRow(
-                               $this->table->getPrefixedFields( $fields ),
-                               array( $this->table->getPrefixedField( 'id' ) => $this->getId() ),
-                               array( 'LIMIT' => 1 ),
-                               __METHOD__
-                       );
-
-                       if ( $result !== false ) {
-                               $this->setFields( $this->table->getFieldsFromDBResult( $result ), $override );
-
-                               return true;
-                       }
-
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * Gets the value of a field.
-        *
-        * @since 1.20
-        *
-        * @param string $name Field name
-        * @param mixed $default Default value to return when none is found
-        * (default: null)
-        *
-        * @throws MWException
-        * @return mixed
-        */
-       public function getField( $name, $default = null ) {
-               if ( $this->hasField( $name ) ) {
-                       return $this->fields[$name];
-               } elseif ( !is_null( $default ) ) {
-                       return $default;
-               } else {
-                       throw new MWException( 'Attempted to get not-set field ' . $name );
-               }
-       }
-
-       /**
-        * Gets the value of a field but first loads it if not done so already.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param string $name
-        *
-        * @return mixed
-        */
-       public function loadAndGetField( $name ) {
-               if ( !$this->hasField( $name ) ) {
-                       $this->loadFields( array( $name ) );
-               }
-
-               return $this->getField( $name );
-       }
-
-       /**
-        * Remove a field.
-        *
-        * @since 1.20
-        *
-        * @param string $name
-        */
-       public function removeField( $name ) {
-               unset( $this->fields[$name] );
-       }
-
-       /**
-        * Returns the objects database id.
-        *
-        * @since 1.20
-        *
-        * @return int|null
-        */
-       public function getId() {
-               return $this->getField( 'id' );
-       }
-
-       /**
-        * Sets the objects database id.
-        *
-        * @since 1.20
-        *
-        * @param int|null $id
-        */
-       public function setId( $id ) {
-               $this->setField( 'id', $id );
-       }
-
-       /**
-        * Gets if a certain field is set.
-        *
-        * @since 1.20
-        *
-        * @param string $name
-        *
-        * @return bool
-        */
-       public function hasField( $name ) {
-               return array_key_exists( $name, $this->fields );
-       }
-
-       /**
-        * Gets if the id field is set.
-        *
-        * @since 1.20
-        *
-        * @return bool
-        */
-       public function hasIdField() {
-               return $this->hasField( 'id' ) && !is_null( $this->getField( 'id' ) );
-       }
-
-       /**
-        * Gets the fields => values to write to the table.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @return array
-        */
-       protected function getWriteValues() {
-               $values = array();
-
-               foreach ( $this->table->getFields() as $name => $type ) {
-                       if ( array_key_exists( $name, $this->fields ) ) {
-                               $value = $this->fields[$name];
-
-                               // Skip null id fields so that the DBMS can set the default.
-                               if ( $name === 'id' && is_null( $value ) ) {
-                                       continue;
-                               }
-
-                               switch ( $type ) {
-                                       case 'array':
-                                               $value = (array)$value;
-                                       // fall-through!
-                                       case 'blob':
-                                               $value = serialize( $value );
-                                       // fall-through!
-                               }
-
-                               $values[$this->table->getPrefixedField( $name )] = $value;
-                       }
-               }
-
-               return $values;
-       }
-
-       /**
-        * Sets multiple fields.
-        *
-        * @since 1.20
-        *
-        * @param array $fields The fields to set
-        * @param bool $override Override already set fields with the provided values?
-        */
-       public function setFields( array $fields, $override = true ) {
-               foreach ( $fields as $name => $value ) {
-                       if ( $override || !$this->hasField( $name ) ) {
-                               $this->setField( $name, $value );
-                       }
-               }
-       }
-
-       /**
-        * Serializes the object to an associative array which
-        * can then easily be converted into JSON or similar.
-        *
-        * @since 1.20
-        *
-        * @param null|array $fields
-        * @param bool $incNullId
-        *
-        * @return array
-        */
-       public function toArray( $fields = null, $incNullId = false ) {
-               $data = array();
-               $setFields = array();
-
-               if ( !is_array( $fields ) ) {
-                       $setFields = $this->getSetFieldNames();
-               } else {
-                       foreach ( $fields as $field ) {
-                               if ( $this->hasField( $field ) ) {
-                                       $setFields[] = $field;
-                               }
-                       }
-               }
-
-               foreach ( $setFields as $field ) {
-                       if ( $incNullId || $field != 'id' || $this->hasIdField() ) {
-                               $data[$field] = $this->getField( $field );
-                       }
-               }
-
-               return $data;
-       }
-
-       /**
-        * Load the default values, via getDefaults.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param bool $override
-        */
-       public function loadDefaults( $override = true ) {
-               $this->setFields( $this->table->getDefaults(), $override );
-       }
-
-       /**
-        * Writes the answer to the database, either updating it
-        * when it already exists, or inserting it when it doesn't.
-        *
-        * @since 1.20
-        * @deprecated since 1.22 Use IORMTable->updateRow or ->insertRow
-        *
-        * @param string|null $functionName
-        *
-        * @return bool Success indicator
-        */
-       public function save( $functionName = null ) {
-               if ( $this->hasIdField() ) {
-                       return $this->table->updateRow( $this, $functionName );
-               } else {
-                       return $this->table->insertRow( $this, $functionName );
-               }
-       }
-
-       /**
-        * Updates the object in the database.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param string|null $functionName
-        *
-        * @return bool Success indicator
-        */
-       protected function saveExisting( $functionName = null ) {
-               $dbw = $this->table->getWriteDbConnection();
-
-               $success = $dbw->update(
-                       $this->table->getName(),
-                       $this->getWriteValues(),
-                       $this->table->getPrefixedValues( $this->getUpdateConditions() ),
-                       is_null( $functionName ) ? __METHOD__ : $functionName
-               );
-
-               $this->table->releaseConnection( $dbw );
-
-               // DatabaseBase::update does not always return true for success as documented...
-               return $success !== false;
-       }
-
-       /**
-        * Returns the WHERE considtions needed to identify this object so
-        * it can be updated.
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       protected function getUpdateConditions() {
-               return array( 'id' => $this->getId() );
-       }
-
-       /**
-        * Inserts the object into the database.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param string|null $functionName
-        * @param array|null $options
-        *
-        * @return bool Success indicator
-        */
-       protected function insert( $functionName = null, array $options = null ) {
-               $dbw = $this->table->getWriteDbConnection();
-
-               $success = $dbw->insert(
-                       $this->table->getName(),
-                       $this->getWriteValues(),
-                       is_null( $functionName ) ? __METHOD__ : $functionName,
-                       $options
-               );
-
-               // DatabaseBase::insert does not always return true for success as documented...
-               $success = $success !== false;
-
-               if ( $success ) {
-                       $this->setField( 'id', $dbw->insertId() );
-               }
-
-               $this->table->releaseConnection( $dbw );
-
-               return $success;
-       }
-
-       /**
-        * Removes the object from the database.
-        *
-        * @since 1.20
-        * @deprecated since 1.22, use IORMTable->removeRow
-        *
-        * @return bool Success indicator
-        */
-       public function remove() {
-               $this->beforeRemove();
-
-               $success = $this->table->removeRow( $this, __METHOD__ );
-
-               if ( $success ) {
-                       $this->onRemoved();
-               }
-
-               return $success;
-       }
-
-       /**
-        * Gets called before an object is removed from the database.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        */
-       protected function beforeRemove() {
-               $this->loadFields( $this->getBeforeRemoveFields(), false, true );
-       }
-
-       /**
-        * Before removal of an object happens, @see beforeRemove gets called.
-        * This method loads the fields of which the names have been returned by
-        * this one (or all fields if null is returned). This allows for loading
-        * info needed after removal to get rid of linked data and the like.
-        *
-        * @since 1.20
-        *
-        * @return array|null
-        */
-       protected function getBeforeRemoveFields() {
-               return array();
-       }
-
-       /**
-        * Gets called after successful removal.
-        * Can be overridden to get rid of linked data.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        */
-       protected function onRemoved() {
-               $this->setField( 'id', null );
-       }
-
-       /**
-        * Return the names and values of the fields.
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getFields() {
-               return $this->fields;
-       }
-
-       /**
-        * Return the names of the fields.
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getSetFieldNames() {
-               return array_keys( $this->fields );
-       }
-
-       /**
-        * Sets the value of a field.
-        * Strings can be provided for other types,
-        * so this method can be called from unserialization handlers.
-        *
-        * @since 1.20
-        *
-        * @param string $name
-        * @param mixed $value
-        *
-        * @throws MWException
-        */
-       public function setField( $name, $value ) {
-               $this->fields[$name] = $value;
-       }
-
-       /**
-        * Add an amount (can be negative) to the specified field (needs to be numeric).
-        *
-        * @since 1.20
-        * @deprecated since 1.22, use IORMTable->addToField
-        *
-        * @param string $field
-        * @param int $amount
-        *
-        * @return bool Success indicator
-        */
-       public function addToField( $field, $amount ) {
-               return $this->table->addToField( $this->getUpdateConditions(), $field, $amount );
-       }
-
-       /**
-        * Return the names of the fields.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @return array
-        */
-       public function getFieldNames() {
-               return array_keys( $this->table->getFields() );
-       }
-
-       /**
-        * Computes and updates the values of the summary fields.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param array|string|null $summaryFields
-        */
-       public function loadSummaryFields( $summaryFields = null ) {
-       }
-
-       /**
-        * Sets the value for the @see $updateSummaries field.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param bool $update
-        */
-       public function setUpdateSummaries( $update ) {
-               $this->updateSummaries = $update;
-       }
-
-       /**
-        * Sets the value for the @see $inSummaryMode field.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @param bool $summaryMode
-        */
-       public function setSummaryMode( $summaryMode ) {
-               $this->inSummaryMode = $summaryMode;
-       }
-
-       /**
-        * Returns the table this IORMRow is a row in.
-        *
-        * @since 1.20
-        * @deprecated since 1.22
-        *
-        * @return IORMTable
-        */
-       public function getTable() {
-               return $this->table;
-       }
-}
diff --git a/includes/db/ORMTable.php b/includes/db/ORMTable.php
deleted file mode 100644 (file)
index 562a810..0000000
+++ /dev/null
@@ -1,1144 +0,0 @@
-<?php
-/**
- * Abstract base class for representing a single database table.
- * Documentation inline and at https://www.mediawiki.org/wiki/Manual:ORMTable
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @since 1.20
- * Non-abstract since 1.21
- *
- * @file ORMTable.php
- * @ingroup ORM
- *
- * @license GNU GPL v2 or later
- * @author Jeroen De Dauw < jeroendedauw@gmail.com >
- */
-
-class ORMTable extends DBAccessBase implements IORMTable {
-       /**
-        * Cache for instances, used by the singleton method.
-        *
-        * @since 1.20
-        * @deprecated since 1.21
-        *
-        * @var ORMTable[]
-        */
-       protected static $instanceCache = array();
-
-       /**
-        * @since 1.21
-        *
-        * @var string
-        */
-       protected $tableName;
-
-       /**
-        * @since 1.21
-        *
-        * @var string[]
-        */
-       protected $fields = array();
-
-       /**
-        * @since 1.21
-        *
-        * @var string
-        */
-       protected $fieldPrefix = '';
-
-       /**
-        * @since 1.21
-        *
-        * @var string
-        */
-       protected $rowClass = 'ORMRow';
-
-       /**
-        * @since 1.21
-        *
-        * @var array
-        */
-       protected $defaults = array();
-
-       /**
-        * ID of the database connection to use for read operations.
-        * Can be changed via @see setReadDb.
-        *
-        * @since 1.20
-        *
-        * @var int DB_ enum
-        */
-       protected $readDb = DB_SLAVE;
-
-       /**
-        * Constructor.
-        *
-        * @since 1.21
-        *
-        * @param string $tableName
-        * @param string[] $fields
-        * @param array $defaults
-        * @param string|null $rowClass
-        * @param string $fieldPrefix
-        */
-       public function __construct( $tableName = '', array $fields = array(),
-               array $defaults = array(), $rowClass = null, $fieldPrefix = ''
-       ) {
-               $this->tableName = $tableName;
-               $this->fields = $fields;
-               $this->defaults = $defaults;
-
-               if ( is_string( $rowClass ) ) {
-                       $this->rowClass = $rowClass;
-               }
-
-               $this->fieldPrefix = $fieldPrefix;
-       }
-
-       /**
-        * @see IORMTable::getName
-        *
-        * @since 1.21
-        *
-        * @return string
-        * @throws MWException
-        */
-       public function getName() {
-               if ( $this->tableName === '' ) {
-                       throw new MWException( 'The table name needs to be set' );
-               }
-
-               return $this->tableName;
-       }
-
-       /**
-        * Gets the db field prefix.
-        *
-        * @since 1.20
-        * @deprecated since 1.25, use the $this->fieldPrefix property instead
-        *
-        * @return string
-        */
-       protected function getFieldPrefix() {
-               return $this->fieldPrefix;
-       }
-
-       /**
-        * @see IORMTable::getRowClass
-        *
-        * @since 1.21
-        *
-        * @return string
-        */
-       public function getRowClass() {
-               return $this->rowClass;
-       }
-
-       /**
-        * @see ORMTable::getFields
-        *
-        * @since 1.21
-        *
-        * @return array
-        * @throws MWException
-        */
-       public function getFields() {
-               if ( $this->fields === array() ) {
-                       throw new MWException( 'The table needs to have one or more fields' );
-               }
-
-               return $this->fields;
-       }
-
-       /**
-        * Returns a list of default field values.
-        * field name => field value
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getDefaults() {
-               return $this->defaults;
-       }
-
-       /**
-        * Returns a list of the summary fields.
-        * These are fields that cache computed values, such as the amount of linked objects of $type.
-        * This is relevant as one might not want to do actions such as log changes when these get updated.
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getSummaryFields() {
-               return array();
-       }
-
-       /**
-        * Selects the specified fields of the records matching the provided
-        * conditions and returns them as DBDataObject. Field names get prefixed.
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $fields
-        * @param array $conditions
-        * @param array $options
-        * @param string|null $functionName
-        *
-        * @return ORMResult
-        */
-       public function select( $fields = null, array $conditions = array(),
-               array $options = array(), $functionName = null
-       ) {
-               $res = $this->rawSelect( $fields, $conditions, $options, $functionName );
-
-               return new ORMResult( $this, $res );
-       }
-
-       /**
-        * Selects the specified fields of the records matching the provided
-        * conditions and returns them as DBDataObject. Field names get prefixed.
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $fields
-        * @param array $conditions
-        * @param array $options
-        * @param string|null $functionName
-        *
-        * @return array Array of row objects
-        * @throws DBQueryError If the query failed (even if the database was in ignoreErrors mode).
-        */
-       public function selectObjects( $fields = null, array $conditions = array(),
-               array $options = array(), $functionName = null
-       ) {
-               $result = $this->selectFields( $fields, $conditions, $options, false, $functionName );
-
-               $objects = array();
-
-               foreach ( $result as $record ) {
-                       $objects[] = $this->newRow( $record );
-               }
-
-               return $objects;
-       }
-
-       /**
-        * Do the actual select.
-        *
-        * @since 1.20
-        *
-        * @param null|string|array $fields
-        * @param array $conditions
-        * @param array $options
-        * @param null|string $functionName
-        * @return ResultWrapper
-        * @throws Exception
-        * @throws MWException
-        */
-       public function rawSelect( $fields = null, array $conditions = array(),
-               array $options = array(), $functionName = null
-       ) {
-               if ( is_null( $fields ) ) {
-                       $fields = array_keys( $this->getFields() );
-               } else {
-                       $fields = (array)$fields;
-               }
-
-               $dbr = $this->getReadDbConnection();
-               $result = $dbr->select(
-                       $this->getName(),
-                       $this->getPrefixedFields( $fields ),
-                       $this->getPrefixedValues( $conditions ),
-                       is_null( $functionName ) ? __METHOD__ : $functionName,
-                       $options
-               );
-
-               /* @var Exception $error */
-               $error = null;
-
-               if ( $result === false ) {
-                       // Database connection was in "ignoreErrors" mode. We don't like that.
-                       // So, we emulate the DBQueryError that should have been thrown.
-                       $error = new DBQueryError(
-                               $dbr,
-                               $dbr->lastError(),
-                               $dbr->lastErrno(),
-                               $dbr->lastQuery(),
-                               is_null( $functionName ) ? __METHOD__ : $functionName
-                       );
-               }
-
-               $this->releaseConnection( $dbr );
-
-               if ( $error ) {
-                       // Note: construct the error before releasing the connection,
-                       // but throw it after.
-                       throw $error;
-               }
-
-               return $result;
-       }
-
-       /**
-        * Selects the specified fields of the records matching the provided
-        * conditions and returns them as associative arrays.
-        * Provided field names get prefixed.
-        * Returned field names will not have a prefix.
-        *
-        * When $collapse is true:
-        * If one field is selected, each item in the result array will be this field.
-        * If two fields are selected, each item in the result array will have as key
-        * the first field and as value the second field.
-        * If more then two fields are selected, each item will be an associative array.
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $fields
-        * @param array $conditions
-        * @param array $options
-        * @param bool $collapse Set to false to always return each result row as associative array.
-        * @param string|null $functionName
-        *
-        * @return array Array of array
-        */
-       public function selectFields( $fields = null, array $conditions = array(),
-               array $options = array(), $collapse = true, $functionName = null
-       ) {
-               $objects = array();
-
-               $result = $this->rawSelect( $fields, $conditions, $options, $functionName );
-
-               foreach ( $result as $record ) {
-                       $objects[] = $this->getFieldsFromDBResult( $record );
-               }
-
-               if ( $collapse ) {
-                       if ( count( $fields ) === 1 ) {
-                               $objects = array_map( 'array_shift', $objects );
-                       } elseif ( count( $fields ) === 2 ) {
-                               $o = array();
-
-                               foreach ( $objects as $object ) {
-                                       $o[array_shift( $object )] = array_shift( $object );
-                               }
-
-                               $objects = $o;
-                       }
-               }
-
-               return $objects;
-       }
-
-       /**
-        * Selects the specified fields of the first matching record.
-        * Field names get prefixed.
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $fields
-        * @param array $conditions
-        * @param array $options
-        * @param string|null $functionName
-        *
-        * @return IORMRow|bool False on failure
-        */
-       public function selectRow( $fields = null, array $conditions = array(),
-               array $options = array(), $functionName = null
-       ) {
-               $options['LIMIT'] = 1;
-
-               $objects = $this->select( $fields, $conditions, $options, $functionName );
-
-               return ( !$objects || $objects->isEmpty() ) ? false : $objects->current();
-       }
-
-       /**
-        * Selects the specified fields of the records matching the provided
-        * conditions. Field names do NOT get prefixed.
-        *
-        * @since 1.20
-        *
-        * @param array $fields
-        * @param array $conditions
-        * @param array $options
-        * @param string|null $functionName
-        *
-        * @return stdClass
-        */
-       public function rawSelectRow( array $fields, array $conditions = array(),
-               array $options = array(), $functionName = null
-       ) {
-               $dbr = $this->getReadDbConnection();
-
-               $result = $dbr->selectRow(
-                       $this->getName(),
-                       $fields,
-                       $conditions,
-                       is_null( $functionName ) ? __METHOD__ : $functionName,
-                       $options
-               );
-
-               $this->releaseConnection( $dbr );
-
-               return $result;
-       }
-
-       /**
-        * Selects the specified fields of the first record matching the provided
-        * conditions and returns it as an associative array, or false when nothing matches.
-        * This method makes use of selectFields and expects the same parameters and
-        * returns the same results (if there are any, if there are none, this method returns false).
-        * @see ORMTable::selectFields
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $fields
-        * @param array $conditions
-        * @param array $options
-        * @param bool $collapse Set to false to always return each result row as associative array.
-        * @param string|null $functionName
-        *
-        * @return mixed|array|bool False on failure
-        */
-       public function selectFieldsRow( $fields = null, array $conditions = array(),
-               array $options = array(), $collapse = true, $functionName = null
-       ) {
-               $options['LIMIT'] = 1;
-
-               $objects = $this->selectFields( $fields, $conditions, $options, $collapse, $functionName );
-
-               return empty( $objects ) ? false : $objects[0];
-       }
-
-       /**
-        * Returns if there is at least one record matching the provided conditions.
-        * Condition field names get prefixed.
-        *
-        * @since 1.20
-        *
-        * @param array $conditions
-        *
-        * @return bool
-        */
-       public function has( array $conditions = array() ) {
-               return $this->selectRow( array( 'id' ), $conditions ) !== false;
-       }
-
-       /**
-        * Checks if the table exists
-        *
-        * @since 1.21
-        *
-        * @return bool
-        */
-       public function exists() {
-               $dbr = $this->getReadDbConnection();
-               $exists = $dbr->tableExists( $this->getName() );
-               $this->releaseConnection( $dbr );
-
-               return $exists;
-       }
-
-       /**
-        * Returns the amount of matching records.
-        * Condition field names get prefixed.
-        *
-        * Note that this can be expensive on large tables.
-        * In such cases you might want to use DatabaseBase::estimateRowCount instead.
-        *
-        * @since 1.20
-        *
-        * @param array $conditions
-        * @param array $options
-        *
-        * @return int
-        */
-       public function count( array $conditions = array(), array $options = array() ) {
-               $res = $this->rawSelectRow(
-                       array( 'rowcount' => 'COUNT(*)' ),
-                       $this->getPrefixedValues( $conditions ),
-                       $options,
-                       __METHOD__
-               );
-
-               return $res->rowcount;
-       }
-
-       /**
-        * Removes the object from the database.
-        *
-        * @since 1.20
-        *
-        * @param array $conditions
-        * @param string|null $functionName
-        *
-        * @return bool Success indicator
-        */
-       public function delete( array $conditions, $functionName = null ) {
-               $dbw = $this->getWriteDbConnection();
-
-               $result = $dbw->delete(
-                       $this->getName(),
-                       $conditions === array() ? '*' : $this->getPrefixedValues( $conditions ),
-                       is_null( $functionName ) ? __METHOD__ : $functionName
-               ) !== false; // DatabaseBase::delete does not always return true for success as documented...
-
-               $this->releaseConnection( $dbw );
-
-               return $result;
-       }
-
-       /**
-        * Get API parameters for the fields supported by this object.
-        *
-        * @since 1.20
-        *
-        * @param bool $requireParams
-        * @param bool $setDefaults
-        *
-        * @return array
-        */
-       public function getAPIParams( $requireParams = false, $setDefaults = false ) {
-               $typeMap = array(
-                       'id' => 'integer',
-                       'int' => 'integer',
-                       'float' => 'NULL',
-                       'str' => 'string',
-                       'bool' => 'integer',
-                       'array' => 'string',
-                       'blob' => 'string',
-               );
-
-               $params = array();
-               $defaults = $this->getDefaults();
-
-               foreach ( $this->getFields() as $field => $type ) {
-                       if ( $field == 'id' ) {
-                               continue;
-                       }
-
-                       $hasDefault = array_key_exists( $field, $defaults );
-
-                       $params[$field] = array(
-                               ApiBase::PARAM_TYPE => $typeMap[$type],
-                               ApiBase::PARAM_REQUIRED => $requireParams && !$hasDefault
-                       );
-
-                       if ( $type == 'array' ) {
-                               $params[$field][ApiBase::PARAM_ISMULTI] = true;
-                       }
-
-                       if ( $setDefaults && $hasDefault ) {
-                               $default = is_array( $defaults[$field] )
-                                       ? implode( '|', $defaults[$field] )
-                                       : $defaults[$field];
-                               $params[$field][ApiBase::PARAM_DFLT] = $default;
-                       }
-               }
-
-               return $params;
-       }
-
-       /**
-        * Returns an array with the fields and their descriptions.
-        *
-        * field name => field description
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getFieldDescriptions() {
-               return array();
-       }
-
-       /**
-        * Get the database ID used for read operations.
-        *
-        * @since 1.20
-        *
-        * @return int DB_ enum
-        */
-       public function getReadDb() {
-               return $this->readDb;
-       }
-
-       /**
-        * Set the database ID to use for read operations, use DB_XXX constants or
-        *   an index to the load balancer setup.
-        *
-        * @param int $db
-        *
-        * @since 1.20
-        */
-       public function setReadDb( $db ) {
-               $this->readDb = $db;
-       }
-
-       /**
-        * Get the ID of the any foreign wiki to use as a target for database operations
-        *
-        * @since 1.20
-        *
-        * @return string|bool The target wiki, in a form that LBFactory understands
-        *   (or false if the local wiki is used)
-        */
-       public function getTargetWiki() {
-               return $this->wiki;
-       }
-
-       /**
-        * Set the ID of the any foreign wiki to use as a target for database operations
-        *
-        * @param string|bool $wiki The target wiki, in a form that  LBFactory
-        *   understands (or false if the local wiki shall be used)
-        *
-        * @since 1.20
-        */
-       public function setTargetWiki( $wiki ) {
-               $this->wiki = $wiki;
-       }
-
-       /**
-        * Get the database type used for read operations.
-        * This is to be used instead of wfGetDB.
-        *
-        * @see LoadBalancer::getConnection
-        *
-        * @since 1.20
-        *
-        * @return DatabaseBase The database object
-        */
-       public function getReadDbConnection() {
-               return $this->getConnection( $this->getReadDb(), array() );
-       }
-
-       /**
-        * Get the database type used for read operations.
-        * This is to be used instead of wfGetDB.
-        *
-        * @see LoadBalancer::getConnection
-        *
-        * @since 1.20
-        *
-        * @return DatabaseBase The database object
-        */
-       public function getWriteDbConnection() {
-               return $this->getConnection( DB_MASTER, array() );
-       }
-
-       /**
-        * Releases the lease on the given database connection. This is useful mainly
-        * for connections to a foreign wiki. It does nothing for connections to the local wiki.
-        *
-        * @see LoadBalancer::reuseConnection
-        *
-        * @param DatabaseBase $db
-        *
-        * @since 1.20
-        */
-       // @codingStandardsIgnoreStart Suppress "useless method overriding" sniffer warning
-       public function releaseConnection( DatabaseBase $db ) {
-               parent::releaseConnection( $db ); // just make it public
-       }
-       // @codingStandardsIgnoreEnd
-
-       /**
-        * Update the records matching the provided conditions by
-        * setting the fields that are keys in the $values param to
-        * their corresponding values.
-        *
-        * @since 1.20
-        *
-        * @param array $values
-        * @param array $conditions
-        *
-        * @return bool Success indicator
-        */
-       public function update( array $values, array $conditions = array() ) {
-               $dbw = $this->getWriteDbConnection();
-
-               $result = $dbw->update(
-                       $this->getName(),
-                       $this->getPrefixedValues( $values ),
-                       $this->getPrefixedValues( $conditions ),
-                       __METHOD__
-               ) !== false; // DatabaseBase::update does not always return true for success as documented...
-
-               $this->releaseConnection( $dbw );
-
-               return $result;
-       }
-
-       /**
-        * Computes the values of the summary fields of the objects matching the provided conditions.
-        *
-        * @since 1.20
-        *
-        * @param array|string|null $summaryFields
-        * @param array $conditions
-        */
-       public function updateSummaryFields( $summaryFields = null, array $conditions = array() ) {
-               $slave = $this->getReadDb();
-               $this->setReadDb( DB_MASTER );
-
-               /**
-                * @var IORMRow $item
-                */
-               foreach ( $this->select( null, $conditions ) as $item ) {
-                       $item->loadSummaryFields( $summaryFields );
-                       $item->setSummaryMode( true );
-                       $item->save();
-               }
-
-               $this->setReadDb( $slave );
-       }
-
-       /**
-        * Takes in an associative array with field names as keys and
-        * their values as value. The field names are prefixed with the
-        * db field prefix.
-        *
-        * @since 1.20
-        *
-        * @param array $values
-        *
-        * @return array
-        */
-       public function getPrefixedValues( array $values ) {
-               $prefixedValues = array();
-
-               foreach ( $values as $field => $value ) {
-                       if ( is_integer( $field ) ) {
-                               if ( is_array( $value ) ) {
-                                       $field = $value[0];
-                                       $value = $value[1];
-                               } else {
-                                       $value = explode( ' ', $value, 2 );
-                                       $value[0] = $this->getPrefixedField( $value[0] );
-                                       $prefixedValues[] = implode( ' ', $value );
-                                       continue;
-                               }
-                       }
-
-                       $prefixedValues[$this->getPrefixedField( $field )] = $value;
-               }
-
-               return $prefixedValues;
-       }
-
-       /**
-        * Takes in a field or array of fields and returns an
-        * array with their prefixed versions, ready for db usage.
-        *
-        * @since 1.20
-        *
-        * @param array $fields
-        *
-        * @return array
-        */
-       public function getPrefixedFields( array $fields ) {
-               foreach ( $fields as &$field ) {
-                       $field = $this->getPrefixedField( $field );
-               }
-
-               return $fields;
-       }
-
-       /**
-        * Takes in a field and returns an it's prefixed version, ready for db usage.
-        *
-        * @since 1.20
-        *
-        * @param string|array $field
-        *
-        * @return string
-        */
-       public function getPrefixedField( $field ) {
-               return $this->fieldPrefix . $field;
-       }
-
-       /**
-        * Takes an array of field names with prefix and returns the unprefixed equivalent.
-        *
-        * @since 1.20
-        * @deprecated since 1.25, will be removed
-        *
-        * @param string[] $fieldNames
-        *
-        * @return string[]
-        */
-       public function unprefixFieldNames( array $fieldNames ) {
-               wfDeprecated( __METHOD__, '1.25' );
-
-               return $this->stripFieldPrefix( $fieldNames );
-       }
-
-       /**
-        * Takes an array of field names with prefix and returns the unprefixed equivalent.
-        *
-        * @param string[] $fieldNames
-        *
-        * @return string[]
-        */
-       private function stripFieldPrefix( array $fieldNames ) {
-               $start = strlen( $this->fieldPrefix );
-
-               return array_map( function ( $fieldName ) use ( $start ) {
-                       return substr( $fieldName, $start );
-               }, $fieldNames );
-       }
-
-       /**
-        * Takes a field name with prefix and returns the unprefixed equivalent.
-        *
-        * @since 1.20
-        * @deprecated since 1.25, will be removed
-        *
-        * @param string $fieldName
-        *
-        * @return string
-        */
-       public function unprefixFieldName( $fieldName ) {
-               wfDeprecated( __METHOD__, '1.25' );
-
-               return substr( $fieldName, strlen( $this->fieldPrefix ) );
-       }
-
-       /**
-        * Get an instance of this class.
-        *
-        * @since 1.20
-        * @deprecated since 1.21
-        *
-        * @return IORMTable
-        */
-       public static function singleton() {
-               $class = get_called_class();
-
-               if ( !array_key_exists( $class, self::$instanceCache ) ) {
-                       self::$instanceCache[$class] = new $class;
-               }
-
-               return self::$instanceCache[$class];
-       }
-
-       /**
-        * Get an array with fields from a database result,
-        * that can be fed directly to the constructor or
-        * to setFields.
-        *
-        * @since 1.20
-        *
-        * @param stdClass $result
-        * @throws MWException
-        * @return array
-        */
-       public function getFieldsFromDBResult( stdClass $result ) {
-               $result = (array)$result;
-
-               $rawFields = array_combine(
-                       $this->stripFieldPrefix( array_keys( $result ) ),
-                       array_values( $result )
-               );
-
-               $fieldDefinitions = $this->getFields();
-               $fields = array();
-
-               foreach ( $rawFields as $name => $value ) {
-                       if ( array_key_exists( $name, $fieldDefinitions ) ) {
-                               switch ( $fieldDefinitions[$name] ) {
-                                       case 'int':
-                                               $value = (int)$value;
-                                               break;
-                                       case 'float':
-                                               $value = (float)$value;
-                                               break;
-                                       case 'bool':
-                                               if ( is_string( $value ) ) {
-                                                       $value = $value !== '0';
-                                               } elseif ( is_int( $value ) ) {
-                                                       $value = $value !== 0;
-                                               }
-                                               break;
-                                       case 'array':
-                                               if ( is_string( $value ) ) {
-                                                       $value = unserialize( $value );
-                                               }
-
-                                               if ( !is_array( $value ) ) {
-                                                       $value = array();
-                                               }
-                                               break;
-                                       case 'blob':
-                                               if ( is_string( $value ) ) {
-                                                       $value = unserialize( $value );
-                                               }
-                                               break;
-                                       case 'id':
-                                               if ( is_string( $value ) ) {
-                                                       $value = (int)$value;
-                                               }
-                                               break;
-                               }
-
-                               $fields[$name] = $value;
-                       } else {
-                               throw new MWException( 'Attempted to set unknown field ' . $name );
-                       }
-               }
-
-               return $fields;
-       }
-
-       /**
-        * @see ORMTable::newRowFromFromDBResult
-        *
-        * @deprecated since 1.20 use newRowFromDBResult instead
-        * @since 1.20
-        *
-        * @param stdClass $result
-        *
-        * @return IORMRow
-        */
-       public function newFromDBResult( stdClass $result ) {
-               return self::newRowFromDBResult( $result );
-       }
-
-       /**
-        * Get a new instance of the class from a database result.
-        *
-        * @since 1.20
-        *
-        * @param stdClass $result
-        *
-        * @return IORMRow
-        */
-       public function newRowFromDBResult( stdClass $result ) {
-               return $this->newRow( $this->getFieldsFromDBResult( $result ) );
-       }
-
-       /**
-        * @see ORMTable::newRow
-        *
-        * @deprecated since 1.20 use newRow instead
-        * @since 1.20
-        *
-        * @param array $data
-        * @param bool $loadDefaults
-        *
-        * @return IORMRow
-        */
-       public function newFromArray( array $data, $loadDefaults = false ) {
-               return static::newRow( $data, $loadDefaults );
-       }
-
-       /**
-        * Get a new instance of the class from an array.
-        *
-        * @since 1.20
-        *
-        * @param array $fields
-        * @param bool $loadDefaults
-        *
-        * @return IORMRow
-        */
-       public function newRow( array $fields, $loadDefaults = false ) {
-               $class = $this->getRowClass();
-
-               return new $class( $this, $fields, $loadDefaults );
-       }
-
-       /**
-        * Return the names of the fields.
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getFieldNames() {
-               return array_keys( $this->getFields() );
-       }
-
-       /**
-        * Gets if the object can take a certain field.
-        *
-        * @since 1.20
-        *
-        * @param string $name
-        *
-        * @return bool
-        */
-       public function canHaveField( $name ) {
-               return array_key_exists( $name, $this->getFields() );
-       }
-
-       /**
-        * Updates the provided row in the database.
-        *
-        * @since 1.22
-        *
-        * @param IORMRow $row The row to save
-        * @param string|null $functionName
-        *
-        * @return bool Success indicator
-        */
-       public function updateRow( IORMRow $row, $functionName = null ) {
-               $dbw = $this->getWriteDbConnection();
-
-               $success = $dbw->update(
-                       $this->getName(),
-                       $this->getWriteValues( $row ),
-                       $this->getPrefixedValues( array( 'id' => $row->getId() ) ),
-                       is_null( $functionName ) ? __METHOD__ : $functionName
-               );
-
-               $this->releaseConnection( $dbw );
-
-               // DatabaseBase::update does not always return true for success as documented...
-               return $success !== false;
-       }
-
-       /**
-        * Inserts the provided row into the database.
-        *
-        * @since 1.22
-        *
-        * @param IORMRow $row
-        * @param string|null $functionName
-        * @param array|null $options
-        *
-        * @return bool Success indicator
-        */
-       public function insertRow( IORMRow $row, $functionName = null, array $options = null ) {
-               $dbw = $this->getWriteDbConnection();
-
-               $success = $dbw->insert(
-                       $this->getName(),
-                       $this->getWriteValues( $row ),
-                       is_null( $functionName ) ? __METHOD__ : $functionName,
-                       $options
-               );
-
-               // DatabaseBase::insert does not always return true for success as documented...
-               $success = $success !== false;
-
-               if ( $success ) {
-                       $row->setField( 'id', $dbw->insertId() );
-               }
-
-               $this->releaseConnection( $dbw );
-
-               return $success;
-       }
-
-       /**
-        * Gets the fields => values to write to the table.
-        *
-        * @since 1.22
-        *
-        * @param IORMRow $row
-        *
-        * @return array
-        */
-       protected function getWriteValues( IORMRow $row ) {
-               $values = array();
-
-               $rowFields = $row->getFields();
-
-               foreach ( $this->getFields() as $name => $type ) {
-                       if ( array_key_exists( $name, $rowFields ) ) {
-                               $value = $rowFields[$name];
-
-                               switch ( $type ) {
-                                       case 'array':
-                                               $value = (array)$value;
-                                       // fall-through!
-                                       case 'blob':
-                                               $value = serialize( $value );
-                                       // fall-through!
-                               }
-
-                               $values[$this->getPrefixedField( $name )] = $value;
-                       }
-               }
-
-               return $values;
-       }
-
-       /**
-        * Removes the provided row from the database.
-        *
-        * @since 1.22
-        *
-        * @param IORMRow $row
-        * @param string|null $functionName
-        *
-        * @return bool Success indicator
-        */
-       public function removeRow( IORMRow $row, $functionName = null ) {
-               $success = $this->delete(
-                       array( 'id' => $row->getId() ),
-                       is_null( $functionName ) ? __METHOD__ : $functionName
-               );
-
-               // DatabaseBase::delete does not always return true for success as documented...
-               return $success !== false;
-       }
-
-       /**
-        * Add an amount (can be negative) to the specified field (needs to be numeric).
-        *
-        * @since 1.22
-        *
-        * @param array $conditions
-        * @param string $field
-        * @param int $amount
-        *
-        * @return bool Success indicator
-        * @throws MWException
-        */
-       public function addToField( array $conditions, $field, $amount ) {
-               if ( !array_key_exists( $field, $this->fields ) ) {
-                       throw new MWException( 'Unknown field "' . $field . '" provided' );
-               }
-
-               if ( $amount == 0 ) {
-                       return true;
-               }
-
-               $absoluteAmount = abs( $amount );
-               $isNegative = $amount < 0;
-
-               $fullField = $this->getPrefixedField( $field );
-
-               $dbw = $this->getWriteDbConnection();
-
-               $success = $dbw->update(
-                       $this->getName(),
-                       array( "$fullField=$fullField" . ( $isNegative ? '-' : '+' ) . $absoluteAmount ),
-                       $this->getPrefixedValues( $conditions ),
-                       __METHOD__
-               ) !== false; // DatabaseBase::update does not always return true for success as documented...
-
-               $this->releaseConnection( $dbw );
-
-               return $success;
-       }
-}
index e7b7627..86f0110 100644 (file)
@@ -200,7 +200,10 @@ abstract class LBFactory {
         * Commit changes on all master connections
         */
        public function commitMasterChanges() {
+               $start = microtime( true );
                $this->forEachLBCallMethod( 'commitMasterChanges' );
+               $timeMs = 1000 * ( microtime( true ) - $start );
+               RequestContext::getMain()->getStats()->timing( "db.commit-masters", $timeMs );
        }
 
        /**
index 242a1a5..9a24b96 100644 (file)
@@ -77,6 +77,11 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
         */
        private $linkDeletions = null;
 
+       /**
+        * @var User|null
+        */
+       private $user;
+
        /**
         * Constructor
         *
@@ -267,7 +272,7 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                // Which ever runs first generally no-ops the other one.
                $jobs = array();
                foreach ( $bc->getCascadeProtectedLinks() as $title ) {
-                       $jobs[] = new RefreshLinksJob( $title, array( 'prioritize' => true ) );
+                       $jobs[] = RefreshLinksJob::newPrioritized( $title, array() );
                }
                JobQueueGroup::singleton()->push( $jobs );
        }
@@ -907,6 +912,24 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                $this->mRevision = $revision;
        }
 
+       /**
+        * Set the User who triggered this LinksUpdate
+        *
+        * @since 1.27
+        * @param User $user
+        */
+       public function setTriggeringUser( User $user ) {
+               $this->user = $user;
+       }
+
+       /**
+        * @since 1.27
+        * @return null|User
+        */
+       public function getTriggeringUser() {
+               return $this->user;
+       }
+
        /**
         * Invalidate any necessary link lists related to page property changes
         * @param array $changed
@@ -980,15 +1003,23 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
        }
 
        public function getAsJobSpecification() {
+               if ( $this->user ) {
+                       $userInfo = array(
+                               'userId' => $this->user->getId(),
+                               'userName' => $this->user->getName(),
+                       );
+               } else {
+                       $userInfo = false;
+               }
                return array(
                        'wiki' => $this->mDb->getWikiID(),
                        'job'  => new JobSpecification(
                                'refreshLinksPrioritized',
                                array(
-                                       'prioritize' => true,
                                        // Reuse the parser cache if it was saved
                                        'rootJobTimestamp' => $this->mParserOutput->getCacheTime(),
-                                       'useRecursiveLinksUpdate' => $this->mRecursive
+                                       'useRecursiveLinksUpdate' => $this->mRecursive,
+                                       'triggeringUser' => $userInfo,
                                ),
                                array( 'removeDuplicates' => true ),
                                $this->getTitle()
index cd82ab1..8d76e96 100644 (file)
@@ -375,11 +375,6 @@ abstract class FileBackend {
                if ( empty( $opts['force'] ) ) { // sanity
                        unset( $opts['nonLocking'] );
                }
-               foreach ( $ops as &$op ) {
-                       if ( isset( $op['disposition'] ) ) { // b/c (MW 1.20)
-                               $op['headers']['Content-Disposition'] = $op['disposition'];
-                       }
-               }
                /** @noinspection PhpUnusedLocalVariableInspection */
                $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
                return $this->doOperationsInternal( $ops, $opts );
@@ -611,9 +606,6 @@ abstract class FileBackend {
                }
                foreach ( $ops as &$op ) {
                        $op['overwrite'] = true; // avoids RTTs in key/value stores
-                       if ( isset( $op['disposition'] ) ) { // b/c (MW 1.20)
-                               $op['headers']['Content-Disposition'] = $op['disposition'];
-                       }
                }
                /** @noinspection PhpUnusedLocalVariableInspection */
                $scope = $this->getScopedPHPBehaviorForOps(); // try to ignore client aborts
index e72d026..83c1da1 100644 (file)
@@ -262,7 +262,9 @@ class SwiftFileBackend extends FileBackendStore {
                }
 
                $sha1Hash = wfBaseConvert( sha1( $params['content'] ), 16, 36, 31 );
-               $contentType = $this->getContentType( $params['dst'], $params['content'], null );
+               $contentType = isset( $params['headers']['content-type'] )
+                       ? $params['headers']['content-type']
+                       : $this->getContentType( $params['dst'], $params['content'], null );
 
                $reqs = array( array(
                        'method' => 'PUT',
@@ -318,7 +320,9 @@ class SwiftFileBackend extends FileBackendStore {
                        return $status;
                }
                $sha1Hash = wfBaseConvert( $sha1Hash, 16, 36, 31 );
-               $contentType = $this->getContentType( $params['dst'], null, $params['src'] );
+               $contentType = isset( $params['headers']['content-type'] )
+                       ? $params['headers']['content-type']
+                       : $this->getContentType( $params['dst'], null, $params['src'] );
 
                $handle = fopen( $params['src'], 'rb' );
                if ( $handle === false ) { // source doesn't exist?
index 4ffbf4a..f898bb6 100644 (file)
@@ -55,11 +55,11 @@ class ForeignAPIRepo extends FileRepo {
        );
 
        protected $fileFactory = array( 'ForeignAPIFile', 'newFromTitle' );
-       /** @var int Check back with Commons after a day (24*60*60) */
-       protected $apiThumbCacheExpiry = 86400;
+       /** @var int Check back with Commons after this expiry */
+       protected $apiThumbCacheExpiry = 86400; // 1 day (24*3600)
 
-       /** @var int Redownload thumbnail files after a month (86400*30) */
-       protected $fileCacheExpiry = 2592000;
+       /** @var int Redownload thumbnail files after this expiry */
+       protected $fileCacheExpiry = 2592000; // 1 month (30*24*3600)
 
        /** @var array */
        protected $mFileExists = array();
@@ -329,7 +329,7 @@ class ForeignAPIRepo extends FileRepo {
         * @return bool|string
         */
        function getThumbUrlFromCache( $name, $width, $height, $params = "" ) {
-               global $wgMemc;
+               $cache = ObjectCache::getMainWANInstance();
                // We can't check the local cache using FileRepo functions because
                // we override fileExistsBatch(). We have to use the FileBackend directly.
                $backend = $this->getBackend(); // convenience
@@ -342,7 +342,7 @@ class ForeignAPIRepo extends FileRepo {
                $sizekey = "$width:$height:$params";
 
                /* Get the array of urls that we already know */
-               $knownThumbUrls = $wgMemc->get( $key );
+               $knownThumbUrls = $cache->get( $key );
                if ( !$knownThumbUrls ) {
                        /* No knownThumbUrls for this file */
                        $knownThumbUrls = array();
@@ -388,7 +388,7 @@ class ForeignAPIRepo extends FileRepo {
                        if ( $remoteModified < $modified && $diff < $this->fileCacheExpiry ) {
                                /* Use our current and already downloaded thumbnail */
                                $knownThumbUrls[$sizekey] = $localUrl;
-                               $wgMemc->set( $key, $knownThumbUrls, $this->apiThumbCacheExpiry );
+                               $cache->set( $key, $knownThumbUrls, $this->apiThumbCacheExpiry );
 
                                return $localUrl;
                        }
@@ -410,7 +410,7 @@ class ForeignAPIRepo extends FileRepo {
                        return $foreignUrl;
                }
                $knownThumbUrls[$sizekey] = $localUrl;
-               $wgMemc->set( $key, $knownThumbUrls, $this->apiThumbCacheExpiry );
+               $cache->set( $key, $knownThumbUrls, $this->apiThumbCacheExpiry );
                wfDebug( __METHOD__ . " got local thumb $localUrl, saving to cache \n" );
 
                return $localUrl;
@@ -552,19 +552,16 @@ class ForeignAPIRepo extends FileRepo {
                }
 
                if ( !isset( $this->mQueryCache[$url] ) ) {
-                       global $wgMemc;
-
-                       $key = $this->getLocalCacheKey( get_class( $this ), $target, md5( $url ) );
-                       $data = $wgMemc->get( $key );
-
-                       if ( !$data ) {
-                               $data = self::httpGet( $url );
-
-                               if ( !$data ) {
-                                       return null;
+                       $data = ObjectCache::getMainWANInstance()->getWithSetCallback(
+                               $this->getLocalCacheKey( get_class( $this ), $target, md5( $url ) ),
+                               $cacheTTL,
+                               function () use ( $url ) {
+                                       return ForeignAPIRepo::httpGet( $url );
                                }
+                       );
 
-                               $wgMemc->set( $key, $data, $cacheTTL );
+                       if ( !$data ) {
+                               return null;
                        }
 
                        if ( count( $this->mQueryCache ) > 100 ) {
index 5eda550..ee11df9 100644 (file)
@@ -1137,6 +1137,7 @@ abstract class File implements IDBAccessObject {
                if ( !$thumb ) { // bad params?
                        $thumb = false;
                } elseif ( $thumb->isError() ) { // transform error
+                       /** @var $thumb MediaTransformError */
                        $this->lastError = $thumb->toText();
                        // Ignore errors if requested
                        if ( $wgIgnoreImageErrors && !( $flags & self::RENDER_NOW ) ) {
@@ -2019,15 +2020,19 @@ abstract class File implements IDBAccessObject {
         * @return string
         */
        function getDescriptionText( $lang = false ) {
-               global $wgMemc, $wgLang;
+               global $wgLang;
+
                if ( !$this->repo || !$this->repo->fetchDescription ) {
                        return false;
                }
-               if ( !$lang ) {
-                       $lang = $wgLang;
-               }
+
+               $lang = $lang ?: $wgLang;
+
                $renderUrl = $this->repo->getDescriptionRenderUrl( $this->getName(), $lang->getCode() );
                if ( $renderUrl ) {
+                       $cache = ObjectCache::getMainWANInstance();
+
+                       $key = null;
                        if ( $this->repo->descriptionCacheExpiry > 0 ) {
                                wfDebug( "Attempting to get the description from cache..." );
                                $key = $this->repo->getLocalCacheKey(
@@ -2036,7 +2041,7 @@ abstract class File implements IDBAccessObject {
                                        $lang->getCode(),
                                        $this->getName()
                                );
-                               $obj = $wgMemc->get( $key );
+                               $obj = $cache->get( $key );
                                if ( $obj ) {
                                        wfDebug( "success!\n" );
 
@@ -2046,8 +2051,8 @@ abstract class File implements IDBAccessObject {
                        }
                        wfDebug( "Fetching shared description from $renderUrl\n" );
                        $res = Http::get( $renderUrl, array(), __METHOD__ );
-                       if ( $res && $this->repo->descriptionCacheExpiry > 0 ) {
-                               $wgMemc->set( $key, $res, $this->repo->descriptionCacheExpiry );
+                       if ( $res && $key ) {
+                               $cache->set( $key, $res, $this->repo->descriptionCacheExpiry );
                        }
 
                        return $res;
index 3c78290..cad806d 100644 (file)
@@ -334,22 +334,20 @@ class ForeignAPIFile extends File {
        }
 
        function purgeDescriptionPage() {
-               global $wgMemc, $wgContLang;
+               global $wgContLang;
 
                $url = $this->repo->getDescriptionRenderUrl( $this->getName(), $wgContLang->getCode() );
                $key = $this->repo->getLocalCacheKey( 'RemoteFileDescription', 'url', md5( $url ) );
 
-               $wgMemc->delete( $key );
+               ObjectCache::getMainWANInstance()->delete( $key );
        }
 
        /**
         * @param array $options
         */
        function purgeThumbnails( $options = array() ) {
-               global $wgMemc;
-
                $key = $this->repo->getLocalCacheKey( 'ForeignAPIRepo', 'ThumbUrl', $this->getName() );
-               $wgMemc->delete( $key );
+               ObjectCache::getMainWANInstance()->delete( $key );
 
                $files = $this->getThumbnails();
                // Give media handler a chance to filter the purge list
index 19b45be..2d05704 100644 (file)
@@ -40,9 +40,9 @@ class HTMLRadioField extends HTMLFormField {
 
        function getInputOOUI( $value ) {
                $options = array();
-               foreach ( $this->getOptions() as $label => $value ) {
+               foreach ( $this->getOptions() as $label => $data ) {
                        $options[] = array(
-                               'data' => $value,
+                               'data' => $data,
                                'label' => $this->mOptionsLabelsNotFromMessage ? new OOUI\HtmlSnippet( $label ) : $label,
                        );
                }
index 9b5a322..09cfe6a 100644 (file)
@@ -6,7 +6,8 @@
                        "ערן",
                        "아라",
                        "Inkbug",
-                       "Yona b"
+                       "Yona b",
+                       "Rotemliss"
                ]
        },
        "config-desc": "תכנית ההתקנה של מדיה־ויקי",
        "config-db-install-account": "חשבון משתמש להתקנה",
        "config-db-username": "שם המשתמש במסד הנתונים:",
        "config-db-password": "הססמה במסד הנתונים:",
-       "config-db-password-empty": "נא להזין ססמה למשתמש מסד הנתונים החדש: $1.\nאף־על־פי שאפשר ליצור חשבונות ללא ססמה, זה לא מאובטח.",
-       "config-db-username-empty": "יש להזין ערך עבור \"{{int:config-db-username}}\".",
        "config-db-install-username": "יש להכניס שם משתמש שישמש לחיבור למסד נתונים במהלך ההתקנה.\nזהו לא שם משתמש לחשבון במדיה־ויקי; זהו שם משתמש בשרת מסד נתונים.",
        "config-db-install-password": "יש להקליד ססמה שתשמש אותך לצורך חיבור למסד נתונים במהלך ההתקנה.\nזוהי לא ססמה של חשבון במדיה־ויקי; זוהי ססמה לשרת מסד נתונים.",
        "config-db-install-help": "יש להקליד את שם המשתמש ואת הססמה להתחברות למסד הנתונים במהלך ההתקנה.",
        "config-nofile": "הקובץ \"$1\" לא נמצא. האם הוא נמחק?",
        "config-extension-link": "הידעת שמדיה־ויקי תומכת ב־[//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions הרחבות]?\n\nבאפשרותך לעיין ב־[//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category הרחבות לפי קטגוריה].",
        "mainpagetext": "'''תוכנת מדיה־ויקי הותקנה בהצלחה.'''",
-       "mainpagedocfooter": "היעזרו ב[//meta.wikimedia.org/wiki/Help:Contents מדריך למשתמש] למידע על שימוש בתוכנת הוויקי.\n\n== קישורים שימושיים ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings רשימת ההגדרות]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ שאלות ותשובות על מדיה־ויקי]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce רשימת התפוצה על השקת גרסאות]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources תרגום מדיה־ויקי לשפה שלך]"
+       "mainpagedocfooter": "היעזרו ב[//meta.wikimedia.org/wiki/Help:Contents מדריך למשתמש] למידע על שימוש בתוכנת הוויקי.\n\n== קישורים שימושיים ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings רשימת ההגדרות]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ שאלות ותשובות על מדיה־ויקי]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce רשימת התפוצה על השקת גרסאות]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources תרגום מדיה־ויקי לשפה שלך]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam איך להיאבק נגד ספאם באתר הוויקי שלך]"
 }
index 6a1d4a4..051fe92 100644 (file)
        "config-nofile": "Nie udało się odnaleźć pliku \"$1\". Czy nie został usunięty?",
        "config-extension-link": "Czy wiesz, że twoja wiki obsługuje [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions/pl rozszerzenia]?\n\nMożesz przejrzeć [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category rozszerzenia według kategorii] lub [//www.mediawiki.org/wiki/Extension_Matrix Extension Matrix] aby zobaczyć pełną listę rozszerzeń.",
        "mainpagetext": "'''Instalacja MediaWiki powiodła się.'''",
-       "mainpagedocfooter": "Zobacz [//meta.wikimedia.org/wiki/Help:Contents przewodnik użytkownika] w celu uzyskania informacji o działaniu oprogramowania wiki.\n\n== Na początek ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings/pl Lista ustawień konfiguracyjnych]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/pl MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Komunikaty o nowych wersjach MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Przetłumacz MediaWiki na swój język]"
+       "mainpagedocfooter": "Zobacz [//meta.wikimedia.org/wiki/Help:Contents przewodnik użytkownika], aby uzyskać informacje o działaniu oprogramowania wiki.\n\n== Na początek ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings/pl Lista ustawień konfiguracyjnych]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/pl MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Komunikaty o nowych wersjach MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Przetłumacz MediaWiki na swój język]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam \nDowiedz się, jak walczyć ze spamem na swojej wiki]"
 }
index 8b82db0..bf8da76 100644 (file)
@@ -65,5 +65,5 @@
        "config-help": "ajute",
        "config-help-tooltip": "cazze pe spannere",
        "mainpagetext": "'''MediaUicchi ha state 'nstallete.'''",
-       "mainpagedocfooter": "Vè vide [//meta.wikimedia.org/wiki/Help:Contents User's Guide] pe l'mbormaziune sus a cumme s'ause 'u softuer wiki.\n\n== Pe accumenzà ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Liste pe le configuraziune]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ de MediaWiki]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Elenghe d'a poste de MediaUicchi]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localizzazzione de MediaUicchi pa lènga toje]"
+       "mainpagedocfooter": "Vè 'ndruche [//meta.wikimedia.org/wiki/Help:Contents User's Guide] pe l'mbormaziune sus a cumme s'ause 'u softuer uicchi.\n\n== Pe accumenzà ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Elenghe de le 'mbostaziune pa configurazione]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ de MediaUicchi]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Elenghe d'a poste de MediaUicchi]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localizzazzione de MediaUicchi pa lènga toje]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam 'Mbare accume combattere condre a 'u rummate sus 'a uicchi toje]"
 }
index e877280..f3cc63e 100644 (file)
@@ -70,5 +70,5 @@
        "config-help": "הילף",
        "config-nofile": "מ'האט נישט געקענט טרעפן די טעקע \"$1\". צי האט מען זי אויסגעמעקט?",
        "mainpagetext": "'''מעדיעוויקי אינסטאלירט מיט דערפאלג.'''",
-       "mainpagedocfooter": "גיט זיך אן עצה מיט [//meta.wikimedia.org/wiki/Help:Contents באניצער'ס וועגווײַזער] פֿאר אינפֿארמאציע וויאזוי זיך באנוצן מיט וויקי ווייכוואַרג.\n\n== נוצליכע וועבלינקען פֿאַר אנהייבערס ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings רשימה פון קאנפֿיגוראציעס]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ אפֿט געפֿרעגטע שאלות]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce מעדיעוויקי באפֿרײַאונג פאסטליסטע]* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources איבערזעצן מעדיעוויקי אין אײַער שפראך]"
+       "mainpagedocfooter": "גיט זיך אן עצה מיט [//meta.wikimedia.org/wiki/Help:Contents באניצער'ס וועגווײַזער] פֿאר אינפֿארמאציע וויאזוי זיך באנוצן מיט וויקי ווייכוואַרג.\n\n== נוצליכע וועבלינקען פֿאַר אנהייבערס ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings רשימה פון קאנפֿיגוראציעס]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ אפֿט געפֿרעגטע שאלות]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce מעדיעוויקי באפֿרײַאונג פאסטליסטע]* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources איבערזעצן מעדיעוויקי אין אײַער שפראך]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam לערנט וויאזוי צו באקעמפן בפעם אויף אייער וויקי]"
 }
index 3e23391..2d13c7e 100644 (file)
@@ -64,12 +64,17 @@ abstract class Job implements IJobSpecification {
         */
        public static function factory( $command, Title $title, $params = array() ) {
                global $wgJobClasses;
+
                if ( isset( $wgJobClasses[$command] ) ) {
                        $class = $wgJobClasses[$command];
 
-                       return new $class( $title, $params );
+                       $job = new $class( $title, $params );
+                       $job->command = $command;
+
+                       return $job;
                }
-               throw new MWException( "Invalid job command `{$command}`" );
+
+               throw new InvalidArgumentException( "Invalid job command '{$command}'" );
        }
 
        /**
index 7907614..6ecfaf4 100644 (file)
@@ -144,15 +144,13 @@ class JobQueueDB extends JobQueue {
         * @throws MWException
         */
        protected function doGetAbandonedCount() {
-               global $wgMemc;
-
                if ( $this->claimTTL <= 0 ) {
                        return 0; // no acknowledgements
                }
 
                $key = $this->getCacheKey( 'abandonedcount' );
 
-               $count = $wgMemc->get( $key );
+               $count = $this->cache->get( $key );
                if ( is_int( $count ) ) {
                        return $count;
                }
@@ -170,7 +168,8 @@ class JobQueueDB extends JobQueue {
                } catch ( DBError $e ) {
                        $this->throwDBException( $e );
                }
-               $wgMemc->set( $key, $count, self::CACHE_TTL_SHORT );
+
+               $this->cache->set( $key, $count, self::CACHE_TTL_SHORT );
 
                return $count;
        }
index 5bd1cc9..4609d2d 100644 (file)
@@ -286,18 +286,17 @@ class JobQueueGroup {
         * @since 1.23
         */
        public function queuesHaveJobs( $type = self::TYPE_ANY ) {
-               global $wgMemc;
-
                $key = wfMemcKey( 'jobqueue', 'queueshavejobs', $type );
+               $cache = ObjectCache::getLocalClusterInstance();
 
-               $value = $wgMemc->get( $key );
+               $value = $cache->get( $key );
                if ( $value === false ) {
                        $queues = $this->getQueuesWithJobs();
                        if ( $type == self::TYPE_DEFAULT ) {
                                $queues = array_intersect( $queues, $this->getDefaultQueueTypes() );
                        }
                        $value = count( $queues ) ? 'true' : 'false';
-                       $wgMemc->add( $key, $value, 15 );
+                       $cache->add( $key, $value, 15 );
                }
 
                return ( $value === 'true' );
@@ -382,19 +381,24 @@ class JobQueueGroup {
         * @return mixed
         */
        private function getCachedConfigVar( $name ) {
-               global $wgConf, $wgMemc;
+               global $wgConf;
 
                if ( $this->wiki === wfWikiID() ) {
                        return $GLOBALS[$name]; // common case
                } else {
+                       $cache = ObjectCache::getLocalClusterInstance();
                        list( $db, $prefix ) = wfSplitWikiID( $this->wiki );
                        $key = wfForeignMemcKey( $db, $prefix, 'configvalue', $name );
-                       $value = $wgMemc->get( $key ); // ('v' => ...) or false
+                       $value = $cache->get( $key ); // ('v' => ...) or false
                        if ( is_array( $value ) ) {
                                return $value['v'];
                        } else {
                                $value = $wgConf->getConfig( $this->wiki, $name );
-                               $wgMemc->set( $key, array( 'v' => $value ), 86400 + mt_rand( 0, 86400 ) );
+                               $cache->set(
+                                       $key,
+                                       array( 'v' => $value ),
+                                       $cache::TTL_DAY + mt_rand( 0, $cache::TTL_DAY )
+                               );
 
                                return $value;
                        }
index 3a83cb8..26f4520 100644 (file)
@@ -41,10 +41,6 @@ class RefreshLinksJob extends Job {
 
        function __construct( Title $title, array $params ) {
                parent::__construct( 'refreshLinks', $title, $params );
-               // A separate type is used just for cascade-protected backlinks
-               if ( !empty( $this->params['prioritize'] ) ) {
-                       $this->command .= 'Prioritized';
-               }
                // Base backlink update jobs and per-title update jobs can be de-duplicated.
                // If template A changes twice before any jobs run, a clean queue will have:
                //              (A base, A base)
@@ -64,6 +60,30 @@ class RefreshLinksJob extends Job {
                        && ( !isset( $params['pages'] ) || count( $params['pages'] ) == 1 );
        }
 
+       /**
+        * @param Title $title
+        * @param array $params
+        * @return RefreshLinksJob
+        */
+       public static function newPrioritized( Title $title, array $params ) {
+               $job = new self( $title, $params );
+               $job->command = 'refreshLinksPrioritized';
+
+               return $job;
+       }
+
+       /**
+        * @param Title $title
+        * @param array $params
+        * @return RefreshLinksJob
+        */
+       public static function newDynamic( Title $title, array $params ) {
+               $job = new self( $title, $params );
+               $job->command = 'refreshLinksDynamic';
+
+               return $job;
+       }
+
        function run() {
                global $wgUpdateRowsPerJob;
 
@@ -195,8 +215,20 @@ class RefreshLinksJob extends Job {
                $updates = $content->getSecondaryDataUpdates(
                        $title, null, !empty( $this->params['useRecursiveLinksUpdate'] ), $parserOutput );
                foreach ( $updates as $key => $update ) {
-                       if ( $update instanceof LinksUpdate && isset( $this->params['triggeredRecursive'] ) ) {
-                               $update->setTriggeredRecursive();
+                       if ( $update instanceof LinksUpdate ) {
+                               if ( isset( $this->params['triggeredRecursive'] ) ) {
+                                       $update->setTriggeredRecursive();
+                               }
+                               if ( isset( $this->params['triggeringUser'] ) && $this->params['triggeringUser'] ) {
+                                       $userInfo = $this->params['triggeringUser'];
+                                       if ( $userInfo['userId'] ) {
+                                               $user = User::newFromId( $userInfo['userId'] );
+                                       } else {
+                                               // Anonymous, use the username
+                                               $user = User::newFromName( $userInfo['userName'], false );
+                                       }
+                                       $update->setTriggeringUser( $user );
+                               }
                        }
                }
 
diff --git a/includes/libs/RunningStat.php b/includes/libs/RunningStat.php
deleted file mode 100644 (file)
index 8bd4656..0000000
+++ /dev/null
@@ -1,176 +0,0 @@
-<?php
-/**
- * Compute running mean, variance, and extrema of a stream of numbers.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Profiler
- */
-
-// Needed due to PHP non-bug <https://bugs.php.net/bug.php?id=49828>.
-define( 'NEGATIVE_INF', -INF );
-
-/**
- * Represents a running summary of a stream of numbers.
- *
- * RunningStat instances are accumulator-like objects that provide a set of
- * continuously-updated summary statistics for a stream of numbers, without
- * requiring that each value be stored. The measures it provides are the
- * arithmetic mean, variance, standard deviation, and extrema (min and max);
- * together they describe the central tendency and statistical dispersion of a
- * set of values.
- *
- * One RunningStat instance can be merged into another; the resultant
- * RunningStat has the state it would have had if it had accumulated each
- * individual point. This allows data to be summarized in parallel and in
- * stages without loss of fidelity.
- *
- * Based on a C++ implementation by John D. Cook:
- *  <http://www.johndcook.com/standard_deviation.html>
- *  <http://www.johndcook.com/skewness_kurtosis.html>
- *
- * The in-line documentation for this class incorporates content from the
- * English Wikipedia articles "Variance", "Algorithms for calculating
- * variance", and "Standard deviation".
- *
- * @since 1.23
- */
-class RunningStat implements Countable {
-
-       /** @var int Number of samples. **/
-       public $n = 0;
-
-       /** @var float The first moment (or mean, or expected value). **/
-       public $m1 = 0.0;
-
-       /** @var float The second central moment (or variance). **/
-       public $m2 = 0.0;
-
-       /** @var float The least value in the set. **/
-       public $min = INF;
-
-       /** @var float The greatest value in the set. **/
-       public $max = NEGATIVE_INF;
-
-       /**
-        * Count the number of accumulated values.
-        * @return int Number of values
-        */
-       public function count() {
-               return $this->n;
-       }
-
-       /**
-        * Add a number to the data set.
-        * @param int|float $x Value to add
-        */
-       public function push( $x ) {
-               $x = (float) $x;
-
-               $this->min = min( $this->min, $x );
-               $this->max = max( $this->max, $x );
-
-               $n1 = $this->n;
-               $this->n += 1;
-               $delta = $x - $this->m1;
-               $delta_n = $delta / $this->n;
-               $this->m1 += $delta_n;
-               $this->m2 += $delta * $delta_n * $n1;
-       }
-
-       /**
-        * Get the mean, or expected value.
-        *
-        * The arithmetic mean is the sum of all measurements divided by the number
-        * of observations in the data set.
-        *
-        * @return float Mean
-        */
-       public function getMean() {
-               return $this->m1;
-       }
-
-       /**
-        * Get the estimated variance.
-        *
-        * Variance measures how far a set of numbers is spread out. A small
-        * variance indicates that the data points tend to be very close to the
-        * mean (and hence to each other), while a high variance indicates that the
-        * data points are very spread out from the mean and from each other.
-        *
-        * @return float Estimated variance
-        */
-       public function getVariance() {
-               if ( $this->n === 0 ) {
-                       // The variance of the empty set is undefined.
-                       return NAN;
-               } elseif ( $this->n === 1 ) {
-                       return 0.0;
-               } else {
-                       return $this->m2 / ( $this->n - 1.0 );
-               }
-       }
-
-       /**
-        * Get the estimated standard deviation.
-        *
-        * The standard deviation of a statistical population is the square root of
-        * its variance. It shows how much variation from the mean exists. In
-        * addition to expressing the variability of a population, the standard
-        * deviation is commonly used to measure confidence in statistical conclusions.
-        *
-        * @return float Estimated standard deviation
-        */
-       public function getStdDev() {
-               return sqrt( $this->getVariance() );
-       }
-
-       /**
-        * Merge another RunningStat instance into this instance.
-        *
-        * This instance then has the state it would have had if all the data had
-        * been accumulated by it alone.
-        *
-        * @param RunningStat RunningStat instance to merge into this one
-        */
-       public function merge( RunningStat $other ) {
-               // If the other RunningStat is empty, there's nothing to do.
-               if ( $other->n === 0 ) {
-                       return;
-               }
-
-               // If this RunningStat is empty, copy values from other RunningStat.
-               if ( $this->n === 0 ) {
-                       $this->n = $other->n;
-                       $this->m1 = $other->m1;
-                       $this->m2 = $other->m2;
-                       $this->min = $other->min;
-                       $this->max = $other->max;
-                       return;
-               }
-
-               $n = $this->n + $other->n;
-               $delta = $other->m1 - $this->m1;
-               $delta2 = $delta * $delta;
-
-               $this->m1 = ( ( $this->n * $this->m1 ) + ( $other->n * $other->m1 ) ) / $n;
-               $this->m2 = $this->m2 + $other->m2 + ( $delta2 * $this->n * $other->n / $n );
-               $this->min = min( $this->min, $other->min );
-               $this->max = max( $this->max, $other->max );
-               $this->n = $n;
-       }
-}
index 5ed67c7..d2cd0e4 100644 (file)
@@ -18,6 +18,8 @@
  * @file
  */
 
+use RunningStat\RunningStat;
+
 /**
  * Convenience class for working with XHProf
  * <https://github.com/phacility/xhprof>. XHProf can be installed as a PECL
@@ -254,7 +256,7 @@ class Xhprof {
                                        }
 
                                        for ( $i = 0; $i < $stats['ct']; $i++ ) {
-                                               $this->inclusive[$child][$stat]->push(
+                                               $this->inclusive[$child][$stat]->addObservation(
                                                        $value / $stats['ct']
                                                );
                                        }
index ecc5e37..703c195 100644 (file)
@@ -42,7 +42,7 @@ use Psr\Log\NullLogger;
  *
  * @ingroup Cache
  */
-abstract class BagOStuff implements LoggerAwareInterface {
+abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
        /** @var array[] Lock tracking */
        protected $locks = array();
 
@@ -220,7 +220,7 @@ abstract class BagOStuff implements LoggerAwareInterface {
                do {
                        $this->clearLastError();
                        $casToken = null; // passed by reference
-                       $currentValue = $this->getWithToken( $key, $casToken, BagOStuff::READ_LATEST );
+                       $currentValue = $this->getWithToken( $key, $casToken, self::READ_LATEST );
                        if ( $this->getLastError() ) {
                                return false; // don't spam retries (retry only on races)
                        }
@@ -276,7 +276,7 @@ abstract class BagOStuff implements LoggerAwareInterface {
                }
 
                $this->clearLastError();
-               $currentValue = $this->get( $key, BagOStuff::READ_LATEST );
+               $currentValue = $this->get( $key, self::READ_LATEST );
                if ( $this->getLastError() ) {
                        $success = false;
                } else {
@@ -319,7 +319,7 @@ abstract class BagOStuff implements LoggerAwareInterface {
                        }
                }
 
-               $expiry = min( $expiry ?: INF, 86400 );
+               $expiry = min( $expiry ?: INF, self::TTL_DAY );
 
                $this->clearLastError();
                $timestamp = microtime( true ); // starting UNIX timestamp
@@ -389,7 +389,7 @@ abstract class BagOStuff implements LoggerAwareInterface {
         * @since 1.26
         */
        final public function getScopedLock( $key, $timeout = 6, $expiry = 30, $rclass = '' ) {
-               $expiry = min( $expiry ?: INF, 86400 );
+               $expiry = min( $expiry ?: INF, self::TTL_DAY );
 
                if ( !$this->lock( $key, $timeout, $expiry, $rclass ) ) {
                        return null;
@@ -582,7 +582,7 @@ abstract class BagOStuff implements LoggerAwareInterface {
         * @return int
         */
        protected function convertExpiry( $exptime ) {
-               if ( ( $exptime != 0 ) && ( $exptime < 86400 * 3650 /* 10 years */ ) ) {
+               if ( $exptime != 0 && $exptime < ( 10 * self::TTL_YEAR ) ) {
                        return time() + $exptime;
                } else {
                        return $exptime;
@@ -597,7 +597,7 @@ abstract class BagOStuff implements LoggerAwareInterface {
         * @return int
         */
        protected function convertToRelative( $exptime ) {
-               if ( $exptime >= 86400 * 3650 /* 10 years */ ) {
+               if ( $exptime >= ( 10 * self::TTL_YEAR ) ) {
                        $exptime -= time();
                        if ( $exptime <= 0 ) {
                                $exptime = 1;
diff --git a/includes/libs/objectcache/IExpiringStore.php b/includes/libs/objectcache/IExpiringStore.php
new file mode 100644 (file)
index 0000000..b5ad702
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Cache
+ * @author 2015 Timo Tijhof
+ */
+
+/**
+ * Generic base class for storage interfaces.
+ *
+ * Provides convenient TTL constants.
+ *
+ * @ingroup Cache
+ * @since 1.27
+ */
+interface IExpiringStore {
+
+       // Constants for TTL values, in seconds
+       const TTL_MINUTE = 60;
+       const TTL_HOUR = 3600;
+       const TTL_DAY = 86400; // 24 * 3600
+       const TTL_WEEK = 604800; // 7 * 24 * 3600
+       const TTL_MONTH = 2592000; // 30 * 24 * 3600
+       const TTL_YEAR = 31536000; // 365 * 24 * 3600
+}
index 39eb792..6ef2bce 100644 (file)
  * @author Aaron Schulz
  */
 
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
+
 /**
  * Multi-datacenter aware caching interface
  *
@@ -60,7 +64,7 @@
  * @ingroup Cache
  * @since 1.26
  */
-class WANObjectCache {
+class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
        /** @var BagOStuff The local datacenter cache */
        protected $cache;
        /** @var HashBagOStuff Script instance PHP cache */
@@ -69,23 +73,23 @@ class WANObjectCache {
        protected $pool;
        /** @var EventRelayer Bus that handles purge broadcasts */
        protected $relayer;
+       /** @var LoggerInterface */
+       protected $logger;
 
        /** @var int ERR_* constant for the "last error" registry */
        protected $lastRelayError = self::ERR_NONE;
 
        /** Max time expected to pass between delete() and DB commit finishing */
        const MAX_COMMIT_DELAY = 3;
-       /** Max replication lag before applying TTL_LAGGED to set() */
-       const MAX_REPLICA_LAG = 5;
-       /** Max time since snapshot transaction start to avoid no-op of set() */
-       const MAX_SNAPSHOT_LAG = 5;
+       /** Max replication+snapshot lag before applying TTL_LAGGED or disallowing set() */
+       const MAX_READ_LAG = 7;
        /** Seconds to tombstone keys on delete() */
-       const HOLDOFF_TTL = 14; // MAX_COMMIT_DELAY + MAX_REPLICA_LAG + MAX_SNAPSHOT_LAG + 1
+       const HOLDOFF_TTL = 11; // MAX_COMMIT_DELAY + MAX_READ_LAG + 1
 
        /** Seconds to keep dependency purge keys around */
-       const CHECK_KEY_TTL = 31536000; // 1 year
+       const CHECK_KEY_TTL = self::TTL_YEAR;
        /** Seconds to keep lock keys around */
-       const LOCK_TTL = 5;
+       const LOCK_TTL = 10;
        /** Default remaining TTL at which to consider pre-emptive regeneration */
        const LOW_TTL = 30;
        /** Default time-since-expiry on a miss that makes a key "hot" */
@@ -100,6 +104,9 @@ class WANObjectCache {
        /** Max TTL to store keys when a data sourced is lagged */
        const TTL_LAGGED = 30;
 
+       /** Tiny negative float to use when CTL comes up >= 0 due to clock skew */
+       const TINY_NEGATIVE = -0.000001;
+
        /** Cache format version number */
        const VERSION = 1;
 
@@ -107,6 +114,10 @@ class WANObjectCache {
        const FLD_VALUE = 1;
        const FLD_TTL = 2;
        const FLD_TIME = 3;
+       const FLD_FLAGS = 4;
+
+       /** @var integer Treat this value as expired-on-arrival */
+       const FLG_STALE = 1;
 
        const ERR_NONE = 0; // no error
        const ERR_NO_RESPONSE = 1; // no response
@@ -125,12 +136,18 @@ class WANObjectCache {
         *   - cache   : BagOStuff object
         *   - pool    : pool name
         *   - relayer : EventRelayer object
+        *   - logger  : LoggerInterface object
         */
        public function __construct( array $params ) {
                $this->cache = $params['cache'];
                $this->pool = $params['pool'];
                $this->relayer = $params['relayer'];
                $this->procCache = new HashBagOStuff();
+               $this->setLogger( isset( $params['logger'] ) ? $params['logger'] : new NullLogger() );
+       }
+
+       public function setLogger( LoggerInterface $logger ) {
+               $this->logger = $logger;
        }
 
        /**
@@ -149,11 +166,11 @@ class WANObjectCache {
        /**
         * Fetch the value of a key from cache
         *
-        * If passed in, $curTTL is set to the remaining TTL (current time left):
-        *   - a) INF; if the key exists, has no TTL, and is not expired by $checkKeys
-        *   - b) float (>=0); if the key exists, has a TTL, and is not expired by $checkKeys
-        *   - c) float (<0); if the key is tombstoned or existing but expired by $checkKeys
-        *   - d) null; if the key does not exist and is not tombstoned
+        * If supplied, $curTTL is set to the remaining TTL (current time left):
+        *   - a) INF; if $key exists, has no TTL, and is not expired by $checkKeys
+        *   - b) float (>=0); if $key exists, has a TTL, and is not expired by $checkKeys
+        *   - c) float (<0); if $key is tombstoned, stale, or existing but expired by $checkKeys
+        *   - d) null; if $key does not exist and is not tombstoned
         *
         * If a key is tombstoned, $curTTL will reflect the time since delete().
         *
@@ -284,7 +301,7 @@ class WANObjectCache {
         *     // Fetch the row from the DB
         *     $row = $dbr->selectRow( ... );
         *     $key = $cache->makeKey( 'building', $buildingId );
-        *     $cache->set( $key, $row, 86400, $setOpts );
+        *     $cache->set( $key, $row, $cache::TTL_DAY, $setOpts );
         * @endcode
         *
         * @param string $key Cache key
@@ -300,10 +317,13 @@ class WANObjectCache {
         *               the current time the data was read or (if applicable) the time when
         *               the snapshot-isolated transaction the data was read from started.
         *               Default: 0 seconds
-        *   - lockTSE : if excessive possible snapshot lag is detected,
-        *               then stash the value into a temporary location
-        *               with this TTL. This is only useful if the reads
-        *               use getWithSetCallback() with "lockTSE" set.
+        *   - pending : Whether this data is possibly from an uncommitted write transaction.
+        *               Generally, other threads should not see values from the future and
+        *               they certainly should not see ones that ended up getting rolled back.
+        *               Default: false
+        *   - lockTSE : if excessive replication/snapshot lag is detected, then store the value
+        *               with this TTL and flag it as stale. This is only useful if the reads for
+        *               this key use getWithSetCallback() with "lockTSE" set.
         *               Default: WANObjectCache::TSE_NONE
         * @return bool Success
         */
@@ -312,21 +332,39 @@ class WANObjectCache {
                $age = isset( $opts['since'] ) ? max( 0, microtime( true ) - $opts['since'] ) : 0;
                $lag = isset( $opts['lag'] ) ? $opts['lag'] : 0;
 
-               if ( $lag > self::MAX_REPLICA_LAG ) {
-                       // Too much lag detected; lower TTL so it converges faster
-                       $ttl = $ttl ? min( $ttl, self::TTL_LAGGED ) : self::TTL_LAGGED;
+               // Do not cache potentially uncommitted data as it might get rolled back
+               if ( !empty( $opts['pending'] ) ) {
+                       $this->logger->info( "Rejected set() for $key due to pending writes." );
+
+                       return true; // no-op the write for being unsafe
                }
 
-               if ( $age > self::MAX_SNAPSHOT_LAG ) {
+               $wrapExtra = array(); // additional wrapped value fields
+               // Check if there's a risk of writing stale data after the purge tombstone expired
+               if ( ( $lag + $age ) > self::MAX_READ_LAG ) {
+                       // Case A: read lag with "lockTSE"; save but record value as stale
                        if ( $lockTSE >= 0 ) {
-                               $tempTTL = max( 1, (int)$lockTSE ); // set() expects seconds
-                               $this->cache->set( self::STASH_KEY_PREFIX . $key, $value, $tempTTL );
-                       }
+                               $ttl = max( 1, (int)$lockTSE ); // set() expects seconds
+                               $wrapExtra[self::FLD_FLAGS] = self::FLG_STALE; // mark as stale
+                       // Case B: any long-running transaction; ignore this set()
+                       } elseif ( $age > self::MAX_READ_LAG ) {
+                               $this->logger->warning( "Rejected set() for $key due to snapshot lag." );
+
+                               return true; // no-op the write for being unsafe
+                       // Case C: high replication lag; lower TTL instead of ignoring all set()s
+                       } elseif ( $lag > self::MAX_READ_LAG ) {
+                               $ttl = $ttl ? min( $ttl, self::TTL_LAGGED ) : self::TTL_LAGGED;
+                               $this->logger->warning( "Lowered set() TTL for $key due to replication lag." );
+                       // Case D: medium length request with medium replication lag; ignore this set()
+                       } else {
+                               $this->logger->warning( "Rejected set() for $key due to high read lag." );
 
-                       return true; // no-op the write for being unsafe
+                               return true; // no-op the write for being unsafe
+                       }
                }
 
-               $wrapped = $this->wrap( $value, $ttl );
+               // Wrap that value with time/TTL/version metadata
+               $wrapped = $this->wrap( $value, $ttl ) + $wrapExtra;
 
                $func = function ( $cache, $key, $cWrapped ) use ( $wrapped ) {
                        return ( is_string( $cWrapped ) )
@@ -382,13 +420,16 @@ class WANObjectCache {
         *     $dbw->commit(); // end of request
         * @endcode
         *
-        * If called twice on the same key, then the last hold-off TTL takes
-        * precedence. For idempotence, the $ttl should not vary for different
-        * delete() calls on the same key. Also note that lowering $ttl reduces
-        * the effective range of the 'lockTSE' parameter to getWithSetCallback().
+        * The $ttl parameter can be used when purging values that have not actually changed
+        * recently. For example, a cleanup script to purge cache entries does not really need
+        * a hold-off period, so it can use the value 1. Likewise for user-requested purge.
+        * Note that $ttl limits the effective range of 'lockTSE' for getWithSetCallback().
+        *
+        * If called twice on the same key, then the last hold-off TTL takes precedence. For
+        * idempotence, the $ttl should not vary for different delete() calls on the same key.
         *
         * @param string $key Cache key
-        * @param integer $ttl How long to block writes to the key [seconds]
+        * @param integer $ttl Tombstone TTL; Default: WANObjectCache::HOLDOFF_TTL
         * @return bool True if the item was purged or not found, false on failure
         */
        final public function delete( $key, $ttl = self::HOLDOFF_TTL ) {
@@ -538,8 +579,8 @@ class WANObjectCache {
         *     $catInfo = $cache->getWithSetCallback(
         *         // Key to store the cached value under
         *         $cache->makeKey( 'cat-attributes', $catId ),
-        *         // Time-to-live (seconds)
-        *         60,
+        *         // Time-to-live (in seconds)
+        *         $cache::TTL_MINUTE,
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
         *             $dbr = wfGetDB( DB_SLAVE );
@@ -556,8 +597,8 @@ class WANObjectCache {
         *     $catConfig = $cache->getWithSetCallback(
         *         // Key to store the cached value under
         *         $cache->makeKey( 'site-cat-config' ),
-        *         // Time-to-live (seconds)
-        *         86400,
+        *         // Time-to-live (in seconds)
+        *         $cache::TTL_DAY,
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
         *             $dbr = wfGetDB( DB_SLAVE );
@@ -581,7 +622,7 @@ class WANObjectCache {
         *         // Key to store the cached value under
         *         $cache->makeKey( 'cat-state', $cat->getId() ),
         *         // Time-to-live (seconds)
-        *         900,
+        *         $cache::TTL_HOUR,
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
         *             // Determine new value from the DB
@@ -608,7 +649,7 @@ class WANObjectCache {
         *     $lastCatActions = $cache->getWithSetCallback(
         *         // Key to store the cached value under
         *         $cache->makeKey( 'cat-last-actions', 100 ),
-        *         // Time-to-live (seconds)
+        *         // Time-to-live (in seconds)
         *         10,
         *         // Function that derives the new key value
         *         function ( $oldValue, &$ttl, array &$setOpts ) {
@@ -890,7 +931,7 @@ class WANObjectCache {
         *
         * @param mixed $value
         * @param integer $ttl [0=forever]
-        * @return string
+        * @return array
         */
        protected function wrap( $value, $ttl ) {
                return array(
@@ -913,7 +954,7 @@ class WANObjectCache {
                $purgeTimestamp = self::parsePurgeValue( $wrapped );
                if ( is_float( $purgeTimestamp ) ) {
                        // Purged values should always have a negative current $ttl
-                       $curTTL = min( -0.000001, $purgeTimestamp - $now );
+                       $curTTL = min( $purgeTimestamp - $now, self::TINY_NEGATIVE );
                        return array( false, $curTTL );
                }
 
@@ -924,7 +965,12 @@ class WANObjectCache {
                        return array( false, null );
                }
 
-               if ( $wrapped[self::FLD_TTL] > 0 ) {
+               $flags = isset( $wrapped[self::FLD_FLAGS] ) ? $wrapped[self::FLD_FLAGS] : 0;
+               if ( ( $flags & self::FLG_STALE ) == self::FLG_STALE ) {
+                       // Treat as expired, with the cache time as the expiration
+                       $age = $now - $wrapped[self::FLD_TIME];
+                       $curTTL = min( -$age, self::TINY_NEGATIVE );
+               } elseif ( $wrapped[self::FLD_TTL] > 0 ) {
                        // Get the approximate time left on the key
                        $age = $now - $wrapped[self::FLD_TIME];
                        $curTTL = max( $wrapped[self::FLD_TTL] - $age, 0.0 );
index b422bfa..662c330 100644 (file)
@@ -289,7 +289,7 @@ class DjVuHandler extends ImageHandler {
         * @param bool $gettext DOCUMENT (Default: false)
         * @return bool|SimpleXMLElement
         */
-       function getMetaTree( $image, $gettext = false ) {
+       public function getMetaTree( $image, $gettext = false ) {
                if ( $gettext && isset( $image->djvuTextTree ) ) {
                        return $image->djvuTextTree;
                }
@@ -375,56 +375,52 @@ class DjVuHandler extends ImageHandler {
                return !empty( $metadata ) && $metadata != serialize( array() );
        }
 
-       function pageCount( $image ) {
-               global $wgMemc;
+       function pageCount( File $image ) {
+               $info = $this->getDimensionInfo( $image );
 
-               $key = wfMemcKey( 'file-djvu', 'pageCount', $image->getSha1() );
+               return $info ? $info['pageCount'] : false;
+       }
 
-               $count = $wgMemc->get( $key );
-               if ( $count === false ) {
-                       $tree = $this->getMetaTree( $image );
-                       if ( !$tree ) {
-                               return false;
-                       }
-                       $count = count( $tree->xpath( '//OBJECT' ) );
-                       $wgMemc->set( $key, $count );
+       function getPageDimensions( File $image, $page ) {
+               $index = $page - 1; // MW starts pages at 1
+
+               $info = $this->getDimensionInfo( $image );
+               if ( $info && isset( $info['dimensionsByPage'][$index] ) ) {
+                       return $info['dimensionsByPage'][$index];
                }
 
-               return $count;
+               return false;
        }
 
-       function getPageDimensions( $image, $page ) {
-               global $wgMemc;
-
-               $key = wfMemcKey( 'file-djvu', 'dimensions', $image->getSha1() );
-
-               $dimsByPage = $wgMemc->get( $key );
-               if ( !is_array( $dimsByPage ) ) {
-                       $tree = $this->getMetaTree( $image );
-                       if ( !$tree ) {
-                               return false;
-                       }
+       protected function getDimensionInfo( File $file ) {
+               $that = $this;
 
-                       $dimsByPage = array();
-                       $count = count( $tree->xpath( '//OBJECT' ) );
-                       for ( $i = 0; $i < $count; ++$i ) {
-                               $o = $tree->BODY[0]->OBJECT[$i];
-                               if ( $o ) {
-                                       $dimsByPage[$i] = array(
-                                               'width' => (int)$o['width'],
-                                               'height' => (int)$o['height']
-                                       );
-                               } else {
-                                       $dimsByPage[$i] = false;
+               return ObjectCache::getMainWANInstance()->getWithSetCallback(
+                       wfMemcKey( 'file-djvu', 'dimensions', $file->getSha1() ),
+                       WANObjectCache::TTL_INDEFINITE,
+                       function () use ( $that, $file ) {
+                               $tree = $that->getMetaTree( $file );
+                               if ( !$tree ) {
+                                       return false;
                                }
-                       }
-
-                       $wgMemc->set( $key, $dimsByPage );
-               }
 
-               $index = $page - 1; // MW starts pages at 1
+                               $dimsByPage = array();
+                               $count = count( $tree->xpath( '//OBJECT' ) );
+                               for ( $i = 0; $i < $count; ++$i ) {
+                                       $o = $tree->BODY[0]->OBJECT[$i];
+                                       if ( $o ) {
+                                               $dimsByPage[$i] = array(
+                                                       'width' => (int)$o['width'],
+                                                       'height' => (int)$o['height']
+                                               );
+                                       } else {
+                                               $dimsByPage[$i] = false;
+                                       }
+                               }
 
-               return isset( $dimsByPage[$index] ) ? $dimsByPage[$index] : false;
+                               return array( 'pageCount' => $count, 'dimensionsByPage' => $dimsByPage );
+                       }
+               );
        }
 
        /**
@@ -432,7 +428,7 @@ class DjVuHandler extends ImageHandler {
         * @param int $page Page number to get information for
         * @return bool|string Page text or false when no text found.
         */
-       function getPageText( $image, $page ) {
+       function getPageText( File $image, $page ) {
                $tree = $this->getMetaTree( $image, true );
                if ( !$tree ) {
                        return false;
index 5b57821..25f4806 100644 (file)
@@ -1571,7 +1571,7 @@ class FormatMetadata extends ContextSource {
         * @since 1.23
         */
        public function fetchExtendedMetadata( File $file ) {
-               global $wgMemc;
+               $cache = ObjectCache::getMainWANInstance();
 
                // If revision deleted, exit immediately
                if ( $file->isDeleted( File::DELETED_FILE ) ) {
@@ -1585,7 +1585,7 @@ class FormatMetadata extends ContextSource {
                        $file->getSha1()
                );
 
-               $cachedValue = $wgMemc->get( $cacheKey );
+               $cachedValue = $cache->get( $cacheKey );
                if (
                        $cachedValue
                        && Hooks::run( 'ValidateExtendedMetadataCache', array( $cachedValue['timestamp'], $file ) )
@@ -1605,7 +1605,7 @@ class FormatMetadata extends ContextSource {
                        // computation on a cache hit.
                        $this->sanitizeArrayForAPI( $extendedMetadata );
                        $valueToCache = array( 'data' => $extendedMetadata, 'timestamp' => wfTimestampNow() );
-                       $wgMemc->set( $cacheKey, $valueToCache, $maxCacheTime );
+                       $cache->set( $cacheKey, $valueToCache, $maxCacheTime );
                }
 
                return $extendedMetadata;
index 015eb5a..bad1468 100644 (file)
@@ -373,7 +373,7 @@ abstract class MediaHandler {
         * @param File $file
         * @return bool
         */
-       function pageCount( $file ) {
+       function pageCount( File $file ) {
                return false;
        }
 
@@ -434,7 +434,7 @@ abstract class MediaHandler {
         * @param int $page What page to get dimensions of
         * @return array|bool
         */
-       function getPageDimensions( $image, $page ) {
+       function getPageDimensions( File $image, $page ) {
                $gis = $this->getImageSize( $image, $image->getLocalRefPath() );
                if ( $gis ) {
                        return array(
@@ -454,7 +454,7 @@ abstract class MediaHandler {
         * @return bool|string Page text or false when no text found or if
         *   unsupported.
         */
-       function getPageText( $image, $page ) {
+       function getPageText( File $image, $page ) {
                return false;
        }
 
index 3558149..30f9e2e 100644 (file)
@@ -508,9 +508,10 @@ abstract class TransformationalImageHandler extends ImageHandler {
         * @return string|bool Representing the IM version; false on error
         */
        protected function getMagickVersion() {
-               return ObjectCache::newAccelerator( CACHE_NONE )->getWithSetCallback(
-                       "imagemagick-version",
-                       3600,
+               $cache = ObjectCache::newAccelerator( CACHE_NONE );
+               return $cache->getWithSetCallback(
+                       'imagemagick-version',
+                       $cache::TTL_HOUR,
                        function () {
                                global $wgImageMagickConvertCommand;
 
@@ -523,7 +524,6 @@ abstract class TransformationalImageHandler extends ImageHandler {
                                );
                                if ( $x != 1 ) {
                                        wfDebug( __METHOD__ . ": ImageMagick version check failed\n" );
-
                                        return false;
                                }
 
index cb783a7..59c3c1f 100644 (file)
@@ -167,8 +167,6 @@ class ObjectCache {
                if ( isset( $params['loggroup'] ) ) {
                        $params['logger'] = LoggerFactory::getInstance( $params['loggroup'] );
                } else {
-                       // For backwards-compatability with custom parameters, lets not
-                       // have all logging suddenly disappear
                        $params['logger'] = LoggerFactory::getInstance( 'objectcache' );
                }
                if ( !isset( $params['keyspace'] ) ) {
@@ -294,6 +292,11 @@ class ObjectCache {
                $class = $params['relayerConfig']['class'];
                $params['relayer'] = new $class( $params['relayerConfig'] );
                $params['cache'] = self::newFromId( $params['cacheId'] );
+               if ( isset( $params['loggroup'] ) ) {
+                       $params['logger'] = LoggerFactory::getInstance( $params['loggroup'] );
+               } else {
+                       $params['logger'] = LoggerFactory::getInstance( 'objectcache' );
+               }
                $class = $params['class'];
 
                return new $class( $params );
index fe64821..e71c0ec 100644 (file)
@@ -2186,6 +2186,7 @@ class WikiPage implements Page, IDBAccessObject {
                        foreach ( $updates as $update ) {
                                if ( $update instanceof LinksUpdate ) {
                                        $update->setRevision( $revision );
+                                       $update->setTriggeringUser( $user );
                                }
                                DeferredUpdates::addUpdate( $update );
                        }
@@ -2914,8 +2915,9 @@ class WikiPage implements Page, IDBAccessObject {
                $status->value = $logid;
 
                // Show log excerpt on 404 pages rather than just a link
+               $cache = ObjectCache::getMainStashInstance();
                $key = wfMemcKey( 'page-recent-delete', md5( $logTitle->getPrefixedText() ) );
-               ObjectCache::getMainStashInstance()->set( $key, 1, 86400 );
+               $cache->set( $key, 1, $cache::TTL_DAY );
 
                return $status;
        }
@@ -3477,22 +3479,34 @@ class WikiPage implements Page, IDBAccessObject {
                        return;
                }
 
+               $params = array(
+                       'isOpportunistic' => true,
+                       'rootJobTimestamp' => $parserOutput->getCacheTime()
+               );
+
                if ( $this->mTitle->areRestrictionsCascading() ) {
                        // If the page is cascade protecting, the links should really be up-to-date
-                       $params = array( 'prioritize' => true );
+                       JobQueueGroup::singleton()->lazyPush(
+                               RefreshLinksJob::newPrioritized( $this->mTitle, $params )
+                       );
                } elseif ( $parserOutput->hasDynamicContent() ) {
-                       // Assume the output contains time/random based magic words
-                       $params = array();
-               } else {
-                       // If the inclusions are deterministic, the edit-triggered link jobs are enough
-                       return;
-               }
-
-               // Check if the last link refresh was before page_touched
-               if ( $this->getLinksTimestamp() < $this->getTouched() ) {
-                       $params['isOpportunistic'] = true;
-                       $params['rootJobTimestamp'] = $parserOutput->getCacheTime();
-                       JobQueueGroup::singleton()->lazyPush( new RefreshLinksJob( $this->mTitle, $params ) );
+                       // Assume the output contains "dynamic" time/random based magic words.
+                       // Only update pages that expired due to dynamic content and NOT due to edits
+                       // to referenced templates/files. When the cache expires due to dynamic content,
+                       // page_touched is unchanged. We want to avoid triggering redundant jobs due to
+                       // views of pages that were just purged via HTMLCacheUpdateJob. In that case, the
+                       // template/file edit already triggered recursive RefreshLinksJob jobs.
+                       if ( $this->getLinksTimestamp() > $this->getTouched() ) {
+                               // If a page is uncacheable, do not keep spamming a job for it.
+                               // Although it would be de-duplicated, it would still waste I/O.
+                               $cache = ObjectCache::getLocalClusterInstance();
+                               $key = $cache->makeKey( 'dynamic-linksupdate', 'last', $this->getId() );
+                               if ( $cache->add( $key, time(), 60 ) ) {
+                                       JobQueueGroup::singleton()->lazyPush(
+                                               RefreshLinksJob::newDynamic( $this->mTitle, $params )
+                                       );
+                               }
+                       }
                }
        }
 
index 78f7775..5ffca23 100644 (file)
@@ -134,7 +134,7 @@ class DateFormatter {
                if ( !$dateFormatter ) {
                        $dateFormatter = $cache->getWithSetCallback(
                                $cache->makeKey( 'dateformatter', $lang->getCode() ),
-                               3600,
+                               $cache::TTL_HOUR,
                                function () use ( $lang ) {
                                        return new DateFormatter( $lang );
                                }
index 3a2bb17..746e15b 100644 (file)
@@ -51,6 +51,24 @@ class MWTidy {
                return $driver->tidy( $text );
        }
 
+       /**
+        * Get CSS modules needed if HTML from the current driver is to be displayed.
+        *
+        * This is just a migration tool to allow some changes expected as part of
+        * Tidy replacement (T89331) to be exposed on the client side via user
+        * scripts, without actually replacing tidy. See T49673.
+        *
+        * @return array
+        */
+       public static function getModuleStyles() {
+               $driver = self::singleton();
+               if ( $driver && $driver instanceof MediaWiki\Tidy\RaggettBase ) {
+                       return array( 'mediawiki.raggett' );
+               } else {
+                       return array();
+               }
+       }
+
        /**
         * Check HTML for errors, used if $wgValidateAllHtml = true.
         *
index 9060756..cfbf0b4 100644 (file)
@@ -1340,6 +1340,7 @@ class Parser {
 
                if ( MWTidy::isEnabled() && $this->mOptions->getTidy() ) {
                        $text = MWTidy::tidy( $text );
+                       $this->mOutput->addModuleStyles( MWTidy::getModuleStyles() );
                } else {
                        # attempt to sanitize at least some nesting problems
                        # (bug #2702 and quite a few others)
index 37b45a6..e4c287a 100644 (file)
@@ -108,19 +108,17 @@ class ParserOutput extends CacheTime {
        public $mHeadItems = array();
 
        /**
-        * @var array $mModules Modules to be loaded by the resource loader
+        * @var array $mModules Modules to be loaded by ResourceLoader
         */
        public $mModules = array();
 
        /**
-        * @var array $mModuleScripts Modules of which only the JS will be loaded by
-        *  the resource loader.
+        * @var array $mModuleScripts Modules of which only the JS will be loaded by ResourceLoader.
         */
        public $mModuleScripts = array();
 
        /**
-        * @var array $mModuleStyles Modules of which only the CSSS will be loaded by
-        *  the resource loader.
+        * @var array $mModuleStyles Modules of which only the CSSS will be loaded by ResourceLoader.
         */
        public $mModuleStyles = array();
 
index 5967537..1db9ce5 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Derivative context for resource loader modules.
+ * Derivative context for ResourceLoader modules.
  *
  * 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
index dfd9a8f..5208c23 100644 (file)
@@ -325,12 +325,10 @@ class ResourceLoader implements LoggerAwareInterface {
                        // Warn on duplicate registrations
                        if ( isset( $this->moduleInfos[$name] ) ) {
                                // A module has already been registered by this name
-                               if ( $this->moduleInfos[$name] === $info ) {
-                                       $this->logger->warning(
-                                               'ResourceLoader duplicate registration warning. ' .
-                                               'Another module has already been registered as ' . $name
-                                       );
-                               }
+                               $this->logger->warning(
+                                       'ResourceLoader duplicate registration warning. ' .
+                                       'Another module has already been registered as ' . $name
+                               );
                        }
 
                        // Check $name for validity
index 2e1752a..c797fd6 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Context for resource loader modules.
+ * Context for ResourceLoader modules.
  *
  * 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
index b415bca..2a6af71 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for the edit toolbar.
+ * ResourceLoader module for the edit toolbar.
  *
  * 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
index 1b827db..ef98400 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module based on local JavaScript/CSS files.
+ * ResourceLoader module based on local JavaScript/CSS files.
  *
  * 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
@@ -73,15 +73,6 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         */
        protected $debugScripts = array();
 
-       /**
-        * @var array List of paths to JavaScript files to include in the startup module
-        * @par Usage:
-        * @code
-        * array( [file-path], [file-path], ... )
-        * @endcode
-        */
-       protected $loaderScripts = array();
-
        /**
         * @var array List of paths to CSS files to always include
         * @par Usage:
@@ -195,8 +186,6 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         *         ),
         *         // Scripts to include in debug contexts
         *         'debugScripts' => [file path string or array of file path strings],
-        *         // Scripts to include in the startup module
-        *         'loaderScripts' => [file path string or array of file path strings],
         *         // Modules which must be loaded before this module
         *         'dependencies' => [module name string or array of module name strings],
         *         'templates' => array(
@@ -239,7 +228,6 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                                // Lists of file paths
                                case 'scripts':
                                case 'debugScripts':
-                               case 'loaderScripts':
                                case 'styles':
                                        $this->{$member} = (array)$option;
                                        break;
@@ -279,7 +267,6 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                                        break;
                                // Single strings
                                case 'position':
-                                       $this->isPositionDefined = true;
                                case 'group':
                                case 'skipFunction':
                                        $this->{$member} = (string)$option;
@@ -391,18 +378,6 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                return $this->debugRaw;
        }
 
-       /**
-        * Get loader script.
-        *
-        * @return string|bool JavaScript code to be added to startup module
-        */
-       public function getLoaderScript() {
-               if ( count( $this->loaderScripts ) === 0 ) {
-                       return false;
-               }
-               return $this->readScriptFiles( $this->loaderScripts );
-       }
-
        /**
         * Get all styles for a given context.
         *
@@ -552,8 +527,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                        $this->templates,
                        $context->getDebug() ? $this->debugScripts : array(),
                        $this->getLanguageScripts( $context->getLanguage() ),
-                       self::tryForKey( $this->skinScripts, $context->getSkin(), 'default' ),
-                       $this->loaderScripts
+                       self::tryForKey( $this->skinScripts, $context->getSkin(), 'default' )
                );
                if ( $this->skipFunction ) {
                        $files[] = $this->skipFunction;
@@ -593,7 +567,6 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                        // - position (only used by OutputPage)
                        'scripts',
                        'debugScripts',
-                       'loaderScripts',
                        'styles',
                        'languageScripts',
                        'skinScripts',
index 8de87f2..8fd94f7 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for generated and embedded images.
+ * ResourceLoader module for generated and embedded images.
  *
  * 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
@@ -22,7 +22,7 @@
  */
 
 /**
- * Resource loader module for generated and embedded images.
+ * ResourceLoader module for generated and embedded images.
  *
  * @since 1.25
  */
@@ -184,7 +184,6 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
                                        break;
 
                                case 'position':
-                                       $this->isPositionDefined = true;
                                case 'prefix':
                                case 'selectorWithoutVariant':
                                case 'selectorWithVariant':
@@ -456,9 +455,4 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
                $this->loadFromDefinition();
                return $this->position;
        }
-
-       public function isPositionDefault() {
-               $this->loadFromDefinition();
-               return parent::isPositionDefault();
-       }
 }
index 27c74d7..83db567 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for populating language specific data.
+ * ResourceLoader module for populating language specific data.
  *
  * 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
index 081c728..3c6e1fa 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for providing language names.
+ * ResourceLoader module for providing language names.
  *
  * By default these names will be autonyms however other extensions may
  * provided language names in the context language (e.g. cldr extension)
index 714a8ff..63ba5c1 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Abstraction for resource loader modules.
+ * Abstraction for ResourceLoader modules.
  *
  * 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
@@ -23,7 +23,7 @@
  */
 
 /**
- * Abstraction for resource loader modules, with name registration and maxage functionality.
+ * Abstraction for ResourceLoader modules, with name registration and maxage functionality.
  */
 abstract class ResourceLoaderModule {
        # Type of resource
@@ -67,10 +67,6 @@ abstract class ResourceLoaderModule {
        // In-object cache for module content
        protected $contents = array();
 
-       // Whether the position returned by getPosition() is defined in the module configuration
-       // and not a default value
-       protected $isPositionDefined = false;
-
        /**
         * @var Config
         */
@@ -291,19 +287,6 @@ abstract class ResourceLoaderModule {
                return 'bottom';
        }
 
-       /**
-        * Whether the position returned by getPosition() is a default value or comes from the module
-        * definition. This method is meant to be short-lived, and is only useful until classes added
-        * via addModuleStyles with a default value define an explicit position. See getModuleStyles()
-        * in OutputPage for the related migration warning.
-        *
-        * @return bool
-        * @since  1.26
-        */
-       public function isPositionDefault() {
-               return !$this->isPositionDefined;
-       }
-
        /**
         * Whether this module's JS expects to work without the client-side ResourceLoader module.
         * Returning true from this function will prevent mw.loader.state() call from being
@@ -315,25 +298,12 @@ abstract class ResourceLoaderModule {
                return false;
        }
 
-       /**
-        * Get the loader JS for this module, if set.
-        *
-        * @return mixed JavaScript loader code as a string or boolean false if no custom loader set
-        */
-       public function getLoaderScript() {
-               // Stub, override expected
-               return false;
-       }
-
        /**
         * Get a list of modules this module depends on.
         *
         * Dependency information is taken into account when loading a module
         * on the client side.
         *
-        * To add dependencies dynamically on the client side, use a custom
-        * loader script, see getLoaderScript()
-        *
         * Note: It is expected that $context will be made non-optional in the near
         * future.
         *
index 380b7a5..eba6815 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for site customizations.
+ * ResourceLoader module for site customizations.
  *
  * 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
index 911d953..e1df6d9 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for skin stylesheets.
+ * ResourceLoader module for skin stylesheets.
  *
  * 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
index 8170cb1..862f629 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for populating special characters data for some
+ * ResourceLoader module for populating special characters data for some
  * editing extensions to use.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -22,7 +22,7 @@
  */
 
 /**
- * Resource loader module for populating special characters data for some
+ * ResourceLoader module for populating special characters data for some
  * editing extensions to use.
  */
 class ResourceLoaderSpecialCharacterDataModule extends ResourceLoaderModule {
index 4a672f2..e900200 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Module for resource loader initialization.
+ * Module for ResourceLoader initialization.
  *
  * 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
@@ -163,13 +163,9 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
         *  - array 'dependencies'
         *  - string|null 'group'
         *  - string 'source'
-        *  - string|false 'loader'
         */
        public static function compileUnresolvedDependencies( array &$registryData ) {
                foreach ( $registryData as $name => &$data ) {
-                       if ( $data['loader'] !== false ) {
-                               continue;
-                       }
                        $dependencies = $data['dependencies'];
                        foreach ( $data['dependencies'] as $dependency ) {
                                $implicitDependencies = self::getImplicitDependencies( $registryData, $dependency );
@@ -230,7 +226,6 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                                'dependencies' => $module->getDependencies( $context ),
                                'group' => $module->getGroup(),
                                'source' => $module->getSource(),
-                               'loader' => $module->getLoaderScript(),
                                'skip' => $skipFunction,
                        );
                }
@@ -240,22 +235,9 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                // Register sources
                $out .= ResourceLoader::makeLoaderSourcesScript( $resourceLoader->getSources() );
 
-               // Concatenate module loader scripts and figure out the different call
-               // signatures for mw.loader.register
+               // Figure out the different call signatures for mw.loader.register
                $registrations = array();
                foreach ( $registryData as $name => $data ) {
-                       if ( $data['loader'] !== false ) {
-                               $out .= ResourceLoader::makeCustomLoaderScript(
-                                       $name,
-                                       $data['version'],
-                                       $data['dependencies'],
-                                       $data['group'],
-                                       $data['source'],
-                                       $data['loader']
-                               );
-                               continue;
-                       }
-
                        // Call mw.loader.register(name, version, dependencies, group, source, skip)
                        $registrations[] = array(
                                $name,
index 65d770e..6705336 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for user preference customizations.
+ * ResourceLoader module for user preference customizations.
  *
  * 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
index eba61ed..abcf9c6 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for default user preferences.
+ * ResourceLoader module for default user preferences.
  *
  * 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
index 417cfce..dd4dd5c 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for user customizations.
+ * ResourceLoader module for user customizations.
  *
  * 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
index a097844..d45e281 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for user customizations.
+ * ResourceLoader module for user customizations.
  *
  * 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
index 0847109..94f1e68 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for user preference customizations.
+ * ResourceLoader module for user preference customizations.
  *
  * 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
index 28f1b9a..d37aa55 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Resource loader module for user tokens.
+ * ResourceLoader module for user tokens.
  *
  * 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
index 156ff4e..52eb2d8 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Abstraction for resource loader modules which pull from wiki pages.
+ * Abstraction for ResourceLoader modules that pull from wiki pages.
  *
  * 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
@@ -23,7 +23,7 @@
  */
 
 /**
- * Abstraction for resource loader modules which pull from wiki pages
+ * Abstraction for ResourceLoader modules which pull from wiki pages
  *
  * This can only be used for wiki pages in the MediaWiki and User namespaces,
  * because of its dependence on the functionality of Title::isCssJsSubpage.
@@ -72,8 +72,6 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                foreach ( $options as $member => $option ) {
                        switch ( $member ) {
                                case 'position':
-                                       $this->isPositionDefined = true;
-                                       // Don't break since we need the member set as well
                                case 'styles':
                                case 'scripts':
                                case 'group':
index 12ebb54..08e4885 100644 (file)
@@ -1225,29 +1225,31 @@ abstract class Skin extends ContextSource {
        function buildSidebar() {
                global $wgEnableSidebarCache, $wgSidebarCacheExpiry;
 
-               $cache = ObjectCache::getMainWANInstance();
-               $key = wfMemcKey( 'sidebar', $this->getLanguage()->getCode() );
+               $that = $this;
+               $callback = function () use ( $that ) {
+                       $bar = array();
+                       $that->addToSidebar( $bar, 'sidebar' );
+                       Hooks::run( 'SkinBuildSidebar', array( $that, &$bar ) );
 
-               if ( $wgEnableSidebarCache ) {
-                       $cachedsidebar = $cache->get( $key );
-                       if ( $cachedsidebar ) {
-                               Hooks::run( 'SidebarBeforeOutput', array( $this, &$cachedsidebar ) );
-
-                               return $cachedsidebar;
-                       }
-               }
+                       return $bar;
+               };
 
-               $bar = array();
-               $this->addToSidebar( $bar, 'sidebar' );
-
-               Hooks::run( 'SkinBuildSidebar', array( $this, &$bar ) );
                if ( $wgEnableSidebarCache ) {
-                       $cache->set( $key, $bar, $wgSidebarCacheExpiry );
+                       $cache = ObjectCache::getMainWANInstance();
+                       $sidebar = $cache->getWithSetCallback(
+                               $cache->makeKey( 'sidebar', $this->getLanguage()->getCode() ),
+                               $wgSidebarCacheExpiry,
+                               $callback,
+                               array( 'lockTSE' => 30 )
+                       );
+               } else {
+                       $sidebar = $callback();
                }
 
-               Hooks::run( 'SidebarBeforeOutput', array( $this, &$bar ) );
+               // Apply post-processing to the cached value
+               Hooks::run( 'SidebarBeforeOutput', array( $this, &$sidebar ) );
 
-               return $bar;
+               return $sidebar;
        }
 
        /**
@@ -1259,7 +1261,7 @@ abstract class Skin extends ContextSource {
         * @param array $bar
         * @param string $message
         */
-       function addToSidebar( &$bar, $message ) {
+       public function addToSidebar( &$bar, $message ) {
                $this->addToSidebarPlain( $bar, wfMessage( $message )->inContentLanguage()->plain() );
        }
 
index fd24521..b100e49 100644 (file)
@@ -99,6 +99,7 @@ class SpecialSearch extends SpecialPage {
                        'mediawiki.special', 'mediawiki.special.search', 'mediawiki.ui', 'mediawiki.ui.button',
                        'mediawiki.ui.input',
                ) );
+               $this->addHelpLink( 'Help:Searching' );
 
                // Strip underscores from title parameter; most of the time we'll want
                // text form here. But don't strip underscores from actual text params!
index 8facb35..6c6ba3b 100644 (file)
@@ -485,7 +485,7 @@ class LoginForm extends SpecialPage {
         * @return Status
         */
        public function addNewAccountInternal() {
-               global $wgAuth, $wgMemc, $wgAccountCreationThrottle, $wgEmailConfirmToEdit;
+               global $wgAuth, $wgAccountCreationThrottle, $wgEmailConfirmToEdit;
 
                // If the user passes an invalid domain, something is fishy
                if ( !$wgAuth->validDomain( $this->mDomain ) ) {
@@ -565,8 +565,9 @@ class LoginForm extends SpecialPage {
                        return Status::newFatal( 'noname' );
                }
 
+               $cache = ObjectCache::getLocalClusterInstance();
                # Make sure the user does not exist already
-               $lock = $wgMemc->getScopedLock( wfGlobalCacheKey( 'account', md5( $this->mUsername ) ) );
+               $lock = $cache->getScopedLock( wfGlobalCacheKey( 'account', md5( $this->mUsername ) ) );
                if ( !$lock ) {
                        return Status::newFatal( 'usernameinprogress' );
                } elseif ( $u->idForName( User::READ_LOCKING ) ) {
@@ -633,14 +634,14 @@ class LoginForm extends SpecialPage {
                } else {
                        if ( ( $wgAccountCreationThrottle && $currentUser->isPingLimitable() ) ) {
                                $key = wfMemcKey( 'acctcreate', 'ip', $ip );
-                               $value = $wgMemc->get( $key );
+                               $value = $cache->get( $key );
                                if ( !$value ) {
-                                       $wgMemc->set( $key, 0, 86400 );
+                                       $cache->set( $key, 0, $cache::TTL_DAY );
                                }
                                if ( $value >= $wgAccountCreationThrottle ) {
                                        return Status::newFatal( 'acct_creation_throttle_hit', $wgAccountCreationThrottle );
                                }
-                               $wgMemc->incr( $key );
+                               $cache->incr( $key );
                        }
                }
 
@@ -869,7 +870,7 @@ class LoginForm extends SpecialPage {
         * @return bool|int The integer hit count or True if it is already at the limit
         */
        public static function incLoginThrottle( $username ) {
-               global $wgPasswordAttemptThrottle, $wgMemc, $wgRequest;
+               global $wgPasswordAttemptThrottle, $wgRequest;
                $username = trim( $username ); // sanity
 
                $throttleCount = 0;
@@ -878,11 +879,12 @@ class LoginForm extends SpecialPage {
                        $count = $wgPasswordAttemptThrottle['count'];
                        $period = $wgPasswordAttemptThrottle['seconds'];
 
-                       $throttleCount = $wgMemc->get( $throttleKey );
+                       $cache = ObjectCache::getLocalClusterInstance();
+                       $throttleCount = $cache->get( $throttleKey );
                        if ( !$throttleCount ) {
-                               $wgMemc->add( $throttleKey, 1, $period ); // start counter
+                               $cache->add( $throttleKey, 1, $period ); // start counter
                        } elseif ( $throttleCount < $count ) {
-                               $wgMemc->incr( $throttleKey );
+                               $cache->incr( $throttleKey );
                        } elseif ( $throttleCount >= $count ) {
                                return true;
                        }
@@ -897,11 +899,11 @@ class LoginForm extends SpecialPage {
         * @return void
         */
        public static function clearLoginThrottle( $username ) {
-               global $wgMemc, $wgRequest;
+               global $wgRequest;
                $username = trim( $username ); // sanity
 
                $throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
-               $wgMemc->delete( $throttleKey );
+               ObjectCache::getLocalClusterInstance()->delete( $throttleKey );
        }
 
        /**
@@ -960,9 +962,9 @@ class LoginForm extends SpecialPage {
        }
 
        function processLogin() {
-               global $wgMemc, $wgLang, $wgSecureLogin, $wgPasswordAttemptThrottle,
-                       $wgInvalidPasswordReset;
+               global $wgLang, $wgSecureLogin, $wgPasswordAttemptThrottle, $wgInvalidPasswordReset;
 
+               $cache = ObjectCache::getLocalClusterInstance();
                $authRes = $this->authenticateUserData();
                switch ( $authRes ) {
                        case self::SUCCESS:
@@ -984,7 +986,7 @@ class LoginForm extends SpecialPage {
                                // Reset the throttle
                                $request = $this->getRequest();
                                $key = wfMemcKey( 'password-throttle', $request->getIP(), md5( $this->mUsername ) );
-                               $wgMemc->delete( $key );
+                               $cache->delete( $key );
 
                                if ( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
                                        /* Replace the language object to provide user interface in
index 3f549d0..4759023 100644 (file)
@@ -51,6 +51,11 @@ class RaggettWrapper {
                // we can trick Tidy into not stripping them out by including them in tidy's new-empty-tags config
                $wrappedtext = preg_replace( '!<(link|meta)([^>]*?)(/{0,1}>)!', '<html-$1$2$3', $wrappedtext );
 
+               // Preserve empty li elements (T49673) by abusing Tidy's datafld hack
+               // The whitespace class is as in TY_(InitMap)
+               $wrappedtext = preg_replace( "!<li>([ \r\n\t\f]*)</li>!",
+                       '<li datafld="" class="mw-empty-li">\1</li>', $wrappedtext );
+
                // Wrap the whole thing in a doctype and body for Tidy.
                $wrappedtext = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"' .
                        ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html>' .
@@ -79,6 +84,9 @@ class RaggettWrapper {
                // Revert <html-{link,meta}> back to <{link,meta}>
                $text = preg_replace( '!<html-(link|meta)([^>]*?)(/{0,1}>)!', '<$1$2$3', $text );
 
+               // Remove datafld
+               $text = str_replace( '<li datafld=""', '<li', $text );
+
                // Restore the contents of placeholder tokens
                $text = $this->mTokens->replace( $text );
 
index f600e32..17fcab8 100644 (file)
@@ -1968,7 +1968,7 @@ abstract class UploadBase {
                if ( $value === false ) {
                        $cache->delete( $key );
                } else {
-                       $cache->set( $key, $value, 86400 );
+                       $cache->set( $key, $value, $cache::TTL_DAY );
                }
        }
 }
index 50ed513..c7871c1 100644 (file)
@@ -207,10 +207,7 @@ class Language {
         * @return Language
         */
        protected static function newFromCode( $code ) {
-               // Protect against path traversal below
-               if ( !Language::isValidCode( $code )
-                       || strcspn( $code, ":/\\\000" ) !== strlen( $code )
-               ) {
+               if ( !Language::isValidCode( $code ) ) {
                        throw new MWException( "Invalid language code \"$code\"" );
                }
 
@@ -224,7 +221,6 @@ class Language {
 
                // Check if there is a language class for the code
                $class = self::classFromCode( $code );
-               self::preloadLanguageClass( $class );
                if ( class_exists( $class ) ) {
                        $lang = new $class;
                        return $lang;
@@ -238,9 +234,8 @@ class Language {
                        }
 
                        $class = self::classFromCode( $fallbackCode );
-                       self::preloadLanguageClass( $class );
                        if ( class_exists( $class ) ) {
-                               $lang = Language::newFromCode( $fallbackCode );
+                               $lang = new $class;
                                $lang->setCode( $code );
                                return $lang;
                        }
@@ -341,17 +336,16 @@ class Language {
         */
        public static function isValidCode( $code ) {
                static $cache = array();
-               if ( isset( $cache[$code] ) ) {
-                       return $cache[$code];
+               if ( !isset( $cache[$code] ) ) {
+                       // People think language codes are html safe, so enforce it.
+                       // Ideally we should only allow a-zA-Z0-9-
+                       // but, .+ and other chars are often used for {{int:}} hacks
+                       // see bugs 37564, 37587, 36938
+                       $cache[$code] =
+                               // Protect against path traversal
+                               strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code )
+                               && !preg_match( MediaWikiTitleCodec::getTitleInvalidRegex(), $code );
                }
-               // People think language codes are html safe, so enforce it.
-               // Ideally we should only allow a-zA-Z0-9-
-               // but, .+ and other chars are often used for {{int:}} hacks
-               // see bugs 37564, 37587, 36938
-               $cache[$code] =
-                       strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code )
-                       && !preg_match( MediaWikiTitleCodec::getTitleInvalidRegex(), $code );
-
                return $cache[$code];
        }
 
@@ -411,35 +405,6 @@ class Language {
                return false;
        }
 
-       /**
-        * @param string $code
-        * @return string Name of the language class
-        */
-       public static function classFromCode( $code ) {
-               if ( $code == 'en' ) {
-                       return 'Language';
-               } else {
-                       return 'Language' . str_replace( '-', '_', ucfirst( $code ) );
-               }
-       }
-
-       /**
-        * Includes language class files
-        *
-        * @param string $class Name of the language class
-        */
-       public static function preloadLanguageClass( $class ) {
-               global $IP;
-
-               if ( $class === 'Language' ) {
-                       return;
-               }
-
-               if ( file_exists( "$IP/languages/classes/$class.php" ) ) {
-                       include_once "$IP/languages/classes/$class.php";
-               }
-       }
-
        /**
         * Get the LocalisationCache instance
         *
@@ -3691,8 +3656,9 @@ class Language {
                                # We got the first byte only of a multibyte char; remove it.
                                $string = substr( $string, 0, -1 );
                        } elseif ( $char >= 0x80 &&
+                               // Use the /s modifier (PCRE_DOTALL) so (.*) also matches newlines
                                preg_match( '/^(.*)(?:[\xe0-\xef][\x80-\xbf]|' .
-                                       '[\xf0-\xf7][\x80-\xbf]{1,2})$/', $string, $m )
+                                       '[\xf0-\xf7][\x80-\xbf]{1,2})$/s', $string, $m )
                        ) {
                                # We chopped in the middle of a character; remove it
                                $string = $m[1];
@@ -4406,22 +4372,6 @@ class Language {
                $this->mParentLanguage = false;
        }
 
-       /**
-        * Get the name of a file for a certain language code
-        * @param string $prefix Prepend this to the filename
-        * @param string $code Language code
-        * @param string $suffix Append this to the filename
-        * @throws MWException
-        * @return string $prefix . $mangledCode . $suffix
-        */
-       public static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) {
-               if ( !self::isValidBuiltInCode( $code ) ) {
-                       throw new MWException( "Invalid language code \"$code\"" );
-               }
-
-               return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
-       }
-
        /**
         * Get the language code from a file name. Inverse of getFileName()
         * @param string $filename $prefix . $languageCode . $suffix
@@ -4439,6 +4389,34 @@ class Language {
                return str_replace( '_', '-', strtolower( $m[1] ) );
        }
 
+       /**
+        * @param string $code
+        * @return string Name of the language class
+        */
+       public static function classFromCode( $code ) {
+               if ( $code == 'en' ) {
+                       return 'Language';
+               } else {
+                       return 'Language' . str_replace( '-', '_', ucfirst( $code ) );
+               }
+       }
+
+       /**
+        * Get the name of a file for a certain language code
+        * @param string $prefix Prepend this to the filename
+        * @param string $code Language code
+        * @param string $suffix Append this to the filename
+        * @throws MWException
+        * @return string $prefix . $mangledCode . $suffix
+        */
+       public static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) {
+               if ( !self::isValidBuiltInCode( $code ) ) {
+                       throw new MWException( "Invalid language code \"$code\"" );
+               }
+
+               return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix;
+       }
+
        /**
         * @param string $code
         * @return string
@@ -4465,15 +4443,6 @@ class Language {
                return "$IP/languages/i18n/$code.json";
        }
 
-       /**
-        * @param string $code
-        * @return string
-        */
-       public static function getClassFileName( $code ) {
-               global $IP;
-               return self::getFileName( "$IP/languages/classes/Language", $code, '.php' );
-       }
-
        /**
         * Get the first fallback for a given language.
         *
index cb750de..e66358a 100644 (file)
@@ -26,6 +26,7 @@
        "tog-hideminor": "সাম্প্ৰতিক সাল-সলনিত অগুৰুত্বপূৰ্ণ সম্পাদনা নেদেখুৱাব",
        "tog-hidepatrolled": "সাম্প্ৰতিক সাল-সলনিত তহলদাৰী সম্পাদনা নেদেখুৱাব",
        "tog-newpageshidepatrolled": "নতুন পৃষ্ঠা তালিকাত তহলদাৰী পৃষ্ঠাসমূহ নেদেখুৱাব",
+       "tog-hidecategorization": "পৃষ্ঠাবোৰৰ শ্ৰেণীকৰণ লুকুৱাওক",
        "tog-extendwatchlist": "কেৱল সাম্প্ৰতিকেই নহয, লক্ষ্য-তালিকাৰ সকলো সাল-সলনি বহলাই দেখুৱাওক",
        "tog-usenewrc": "পৃষ্ঠাৰ পৰিৱৰ্তনসমূহ শেহতীয়া সালসলনি আৰু লক্ষ্যতালিকাত ভাগ কৰক",
        "tog-numberheadings": "শীৰ্ষকত স্বয়ংক্ৰিয়ভাৱে ক্ৰমিক নং দিয়ক",
@@ -55,6 +56,7 @@
        "tog-watchlisthideliu": "প্ৰবেশ কৰা সদস্যৰ সম্পাদনাসমূহ আঁতৰাই অনুসৰণ-তালিকা দেখুৱাওক",
        "tog-watchlisthideanons": "বেনামী সদস্যৰ সম্পাদনাসমূহ আঁতৰাই অনুসৰণ-তালিকা দেখুৱাওক",
        "tog-watchlisthidepatrolled": "পৰীক্ষিত সম্পাদনাসমূহ লক্ষ্য-তালিকাৰ পৰা লুকুৱাই ৰাখক",
+       "tog-watchlisthidecategorization": "পৃষ্ঠাবোৰৰ শ্ৰেণীকৰণ লুকুৱাওক",
        "tog-ccmeonemails": "মই অন্য সদস্যলৈ পঠোৱা ই-মেইলৰ প্ৰতিলিপি এটা মোলৈও পঠাব",
        "tog-diffonly": "পার্থক্যৰ তলত পৃষ্ঠাৰ বিষয়বস্তু নেদেখুৱাব",
        "tog-showhiddencats": "নিহিত শ্ৰেণীসমূহ দেখুৱাওক",
        "createaccountreason": "কাৰণ:",
        "createacct-reason": "কাৰণ",
        "createacct-reason-ph": "আপুনি কিয় আন এটা একাউণ্ট সৃষ্টি কৰিছে",
-       "createacct-captcha": "সুৰক্ষা পৰীক্ষা",
-       "createacct-imgcaptcha-ph": "ওপৰত দেখা পোৱা পাঠ্য লিখক",
        "createacct-submit": "আপোনাৰ একাউণ্ট সৃষ্টি কৰক",
        "createacct-another-submit": "একাউণ্ট সৃষ্টি কৰক",
        "createacct-benefit-heading": "আপোনাৰ দৰে মানুহেই {{SITENAME}} তৈয়াৰ কৰিছে",
        "showingresultsinrange": "তলত #<strong>$2</strong>ৰ পৰা #<strong>$3</strong> পৰিসৰৰ ভিতৰত {{PLURAL:$1|<strong>1</strong>টা ফলাফল|<strong>$1</strong>টা লৈকে ফলাফল}} দেখুওৱা হৈছে।",
        "search-showingresults": "{{PLURAL:$4|<strong>$3</strong>-ৰ <strong>$1</strong>টো ফলাফল|<strong>$3</strong>-ৰ <strong>$1 - $2</strong>টো ফলাফল}}",
        "search-nonefound": "এই অনুসন্ধানৰ কোনো ফলাফল নাই ।",
+       "search-nonefound-thiswiki": "এই ছাইটত এই সন্ধানৰ লগত মিলা কোনো ফলাফল নাই।",
        "powersearch-legend": "শক্তিশালী সন্ধান",
        "powersearch-ns": "নামস্থানবোৰত সন্ধান:",
        "powersearch-togglelabel": "পৰীক্ষা কৰক:",
        "rcshowhidemine": "মোৰ সম্পাদনা $1",
        "rcshowhidemine-show": "দেখুৱাওক",
        "rcshowhidemine-hide": "লুকুৱাওক",
+       "rcshowhidecategorization-show": "দেখুৱাওক",
+       "rcshowhidecategorization-hide": "লুকুৱাওক",
        "rclinks": "যোৱা $2 দিনত হোৱা $1 টা সাল-সলনি চাওক ।<br />$3",
        "diff": "পাৰ্থক্য",
        "hist": "ইতিবৃত্ত",
index 26b4ddd..9374954 100644 (file)
        "showingresultsinrange": "Más abaxo s'{{PLURAL:$1|amuesa|amuesen}} fasta {{PLURAL:$1|<strong>1</strong> resultáu|<strong>$1</strong> resultaos}} nel rangu ente #<strong>$2</strong> y #<strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Resultáu <strong>$1</strong> de <strong>$3</strong>|Resultaos <strong>$1 - $2</strong> de <strong>$3</strong>}}",
        "search-nonefound": "Nun hebo resultaos que casaren cola consulta.",
+       "search-nonefound-thiswiki": "Nun hebo resultaos que casaran cola consulta nesti sitiu.",
        "powersearch-legend": "Busca avanzada",
        "powersearch-ns": "Buscar nos espacios de nome:",
        "powersearch-togglelabel": "Comprobar:",
index 20d601f..2a9a2b8 100644 (file)
        "showingresultsinrange": "Ніжэй паказаныя да {{PLURAL:$1|<strong>$1</strong> выніку ў|<strong>$1</strong> вынікаў у}} дыяпазоне ад <strong>$2</strong> да <strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|1=Вынік <strong>$1</strong> з <strong>$3</strong>|Вынікі <strong>$1—$2</strong> з <strong>$3</strong>}}",
        "search-nonefound": "Супадзеньняў па запыце ня знойдзена.",
+       "search-nonefound-thiswiki": "Супадзеньняў па запыце ня знойдзена на гэтым сайце.",
        "powersearch-legend": "Удасканалены пошук",
        "powersearch-ns": "Шукаць у прасторах назваў:",
        "powersearch-togglelabel": "Пазначыць:",
        "foreign-structured-upload-form-label-not-own-work-message-default": "Калі вы ня можаце загрузіць гэты файл паводле правілаў агульнага сховішча, калі ласка, закрыйце гэты дыялёг і паспрабуйце іншы мэтад.",
        "foreign-structured-upload-form-label-not-own-work-local-default": "Вы можаце паспрабаваць скарыстацца [[Special:Upload|старонкай загрузкі {{GRAMMAR:родны|{{SITENAME}}}}]], калі гэты файл можна туды загрузіць згодна з правіламі.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Я пацьвярджаю, што зьяўляюся ўласьнікам аўтарскіх правоў на гэты файл, і згодны незваротна перадаць гэты файл ў Вікісховішча на ўмовах ліцэнзіі [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], а таксама згодны з [https://wikimediafoundation.org/wiki/Terms_of_Use умовамі выкарыстаньня].",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "Калі вы не зьяўляецеся ўласьнікам аўтарскіх правоў на гэты файл, або вы жадаеце распаўсюджваць яго пад іншай ліцэнзіяй, можаце скарыстацца [https://commons.wikimedia.org/wiki/Special:UploadWizard Майстарам загрузкі ў Вікісховішча].",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "Вы таксама можаце скарыстацца [[Special:Upload|старонкай загрузкі {{GRAMMAR:родны|{{SITENAME}}}}]], калі правілы сайту дазваляюць загрузку такога файлу.",
        "backend-fail-stream": "Немагчыма накіраваць файл $1.",
        "backend-fail-backup": "Немагчыма зрабіць рэзэрвовую копію файла $1.",
        "backend-fail-notexists": "Файл $1 не існуе.",
index a19587b..c1db742 100644 (file)
        "version-poweredby-others": "অন্যান্য",
        "version-poweredby-translators": "translatewiki.net অনুবাদকগণ",
        "version-credits-summary": "[[Special:Version|মিডিয়াউইকি]] সফটওয়্যারে অবদানের জন্য আমরা এই ব্যক্তিকে স্বীকৃতি দিতে চাই।",
-       "version-license-info": "মিডিয়াউইকি একটি ফ্রি সফটওয়্যার, আপনি এটি বিতরণ করতে পারবেন এবং/অথবা সম্পদানা করতে পারবেন, এক্ষেত্রে ফ্রি সফটওয়্যার ফাউন্ডেশনের প্রকাশিত গনু জেনারেল পাবলিক লাইসেন্সের ২য় অথবা সাম্প্রতিকতম কোনো সংস্করণ মেনে চলতে হবে। \n\nসকলের উপকারের লক্ষ্যে এটি বিতরণ করা হয়ে থাকে, কিন্তু এক্ষেত্রে কোনো ওয়ারেন্টি দেয়া হয় না, এমনকি বিশেষ কোনো কার্যক্ষেত্রে ব্যবহারের জন্যও তথাকথিত ওয়ারেন্টি দেয়া হয় না। বিস্তারিত জানতে দেখুন গনু জেনারেল পাবলিক লাইসেন্স। \n\nএই সফটওয়্যারের সাথে [{{SERVER}}{{SCRIPTPATH}}/COPYING গনু জেনারেল পাবলিক লাইসেন্সের একটি কপি] থাকার কথা; যদি আপনি না পেয়ে থাকেন তাহলে অনুগ্রহ করে ফ্রি সফটওয়্যার ফাউন্ডেশনকে জানান এই ঠিকানায়, Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA অথবা [//www.gnu.org/licenses/old-licenses/gpl-2.0.html অনলাইনে দেখুন]।",
+       "version-license-info": "মিডিয়াউইকি একটি ফ্রি সফটওয়্যার; আপনি এটি বিতরণ করতে পারবেন এবং/অথবা সম্পদানা করতে পারবেন, এক্ষেত্রে ফ্রি সফটওয়্যার ফাউন্ডেশনের প্রকাশিত গনু জেনারেল পাবলিক লাইসেন্সের ২য় অথবা সাম্প্রতিকতম কোনো সংস্করণ মেনে চলতে হবে। \n\nসকলের উপকারের লক্ষ্যে এটি বিতরণ করা হয়ে থাকে, কিন্তু এক্ষেত্রে কোনো ওয়ারেন্টি দেয়া হয় না, এমনকি বিশেষ কোনো কার্যক্ষেত্রে ব্যবহারের জন্যও তথাকথিত ওয়ারেন্টি দেয়া হয় না। বিস্তারিত জানতে দেখুন গনু জেনারেল পাবলিক লাইসেন্স। \n\nএই সফটওয়্যারের সাথে [{{SERVER}}{{SCRIPTPATH}}/COPYING গনু জেনারেল পাবলিক লাইসেন্সের একটি অনুলিপি] থাকার কথা; যদি আপনি না পেয়ে থাকেন তাহলে অনুগ্রহ করে ফ্রি সফটওয়্যার ফাউন্ডেশনকে জানান এই ঠিকানায়, Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA অথবা [//www.gnu.org/licenses/old-licenses/gpl-2.0.html অনলাইনে দেখুন]।",
        "version-software": "ইনস্টলকৃত সফটওয়্যার",
        "version-software-product": "পণ্য",
        "version-software-version": "সংস্করণ",
index 1d7b339..8e24e0e 100644 (file)
        "logentry-delete-delete": "$1 {{GENDER:$2|obrisao|obrisala}} je stranicu $3",
        "logentry-delete-restore": "$1 {{GENDER:$2|vratio|vratila}} je stranicu $3",
        "logentry-delete-event": "$1 je {{GENDER:$2|promijenio|promijenila}} vidljivost {{PLURAL:$5|događaja|$5 događaja}} u evidenciji na $3: $4",
-       "logentry-delete-revision": "$1 je {{GENDER:$2|promijenio|promijenila}} vidljivost {{PLURAL:$5|izmjene|$5 izmjene|$5 izmjena}} na stranici $3: $4",
+       "logentry-delete-revision": "$1 {{GENDER:$2|promijenio|promijenila}} je vidljivost {{PLURAL:$5|izmjene|$5 izmjene|$5 izmjena}} na stranici $3: $4",
        "logentry-delete-event-legacy": "$1 je {{GENDER:$2|promijenio|promijenila}} vidljivost događaja u evidenciji na $3",
        "logentry-delete-revision-legacy": "$1 je {{GENDER:$2|promijenio|promijenila}} vidljivost izmjena na stranici $3",
        "logentry-suppress-delete": "$1 {{GENDER:$2|potisnuo|potisnula}} je stranicu $3",
        "logentry-suppress-revision": "$1 je tajno {{GENDER:$2|promijenio|promijenila}} vidljivost {{PLURAL:$5|izmjene|$5 izmjene|$5 izmjena}} na stranici $3: $4",
        "logentry-suppress-event-legacy": "$1 je tajno {{GENDER:$2|promijenio|promijenila}} vidljivost događaja u evidenciji na $3",
        "logentry-suppress-revision-legacy": "$1 je tajno {{GENDER:$2|promijenio|promijenila}} vidljivost izmjena na stranici $3",
-       "revdelete-content-hid": "skriveni sadržaj",
+       "revdelete-content-hid": "sadržaj je sakriven",
        "revdelete-summary-hid": "sažetak izmjene je sakriven",
        "revdelete-uname-hid": "sažetak izmjene je sakriven",
        "revdelete-content-unhid": "sadržaj je otkriven",
index a89bc56..003e797 100644 (file)
@@ -51,7 +51,8 @@
                        "Xavier Dengra",
                        "Pginer",
                        "Eduardo Martinez",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "KRLS"
                ]
        },
        "tog-underline": "Subratlla els enllaços:",
        "revdelete-uname-unhid": "ha revelat un nom d'usuari que era ocult",
        "revdelete-restricted": "ha aplicat restriccions als administradors",
        "revdelete-unrestricted": "ha tret les restriccions als administradors",
-       "logentry-block-block": "$1 {{GENDER:$2|ha estat blocat|ha estat blocada}} {{GENDER:$4|$3}} per un període de temps de $5 $6",
+       "logentry-block-block": "$1 {{GENDER:$2|ha blocat}} {{GENDER:$4|$3}} per un temps de $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$2|va desblocar}} {{GENDER:$4|$3}}",
-       "logentry-block-reblock": "$1 {{GENDER:$2|ha canviat}} la configuració del blocatge de {{GENDER:$4|$3}} per un període de temps de $5 $6",
-       "logentry-suppress-block": "$1 {{GENDER:$2|ha blocat}} {{GENDER:$4|$3}} per un període de temps de $5 $6",
-       "logentry-suppress-reblock": "$1 {{GENDER:$2|ha canviat}} la configuració de blocatge de {{GENDER:$4|$3}} per un període de temps de $5 $6",
+       "logentry-block-reblock": "$1 {{GENDER:$2|ha canviat}} la configuració del blocatge de {{GENDER:$4|$3}} per un temps de $5 $6",
+       "logentry-suppress-block": "$1 {{GENDER:$2|ha blocat}} {{GENDER:$4|$3}} per un temps de $5 $6",
+       "logentry-suppress-reblock": "$1 {{GENDER:$2|ha canviat}} la configuració de blocatge de {{GENDER:$4|$3}} per un temps de $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2|va importar}} $3 a través de càrrega de fitxer",
        "logentry-import-interwiki": "$1 {{GENDER:$2|va importar}} $3 d'un altre wiki",
        "logentry-merge-merge": "$1 {{GENDER:$2|ha fusionat}} $3 en $4 (revisions fins a $5)",
index d9212b9..4ee4f0c 100644 (file)
        "createacct-yourpasswordagain": "Бакъе пароль",
        "createacct-yourpasswordagain-ph": "Кхин цкъа язъе пароль",
        "remembermypassword": "Даглаца сан дӀаяздар хӀокху компьютеран тӀехь (цхьан $1 {{PLURAL:$1|дийнахь}})",
-       "userlogin-remembermypassword": "Ð\94агаÑ\85Ñ\8c Ð»Ð°Ñ\82Ñ\82 Ð²Ðµ/е Ñ\81о",
+       "userlogin-remembermypassword": "СиÑ\81Ñ\82емин Ñ\87оÑ\85Ñ\8c Ó\80ойла",
        "userlogin-signwithsecure": "Ларийна цхьаьнакхетар",
        "yourdomainname": "Хьан машан меттиг:",
        "password-change-forbidden": "Хьан йиш яц хӀокху вики чохь пароль хийца.",
index 3972e39..ccf161b 100644 (file)
        "createaccountreason": "هۆکار:",
        "createacct-reason": "ھۆکار",
        "createacct-reason-ph": "بۆ ھەژمارێکی تر دروست دەکەی",
-       "createacct-captcha": "تاوتوێی ئاسایشی",
-       "createacct-imgcaptcha-ph": "دەقەکەی لە ژێرەوە دەیبینی بینووسە",
        "createacct-submit": "ھەژمارەکەت دروست بکە",
        "createacct-another-submit": "ھەژمارێکی تر دروست بکە",
        "createacct-benefit-heading": "{{SITENAME}} لە لایەن کەسانێک وەکوو خۆت دروست کراوە.",
        "revdelete-uname-unhid": "ناوی بەکارهێنەری نیشان درا",
        "revdelete-restricted": "ئەو سنووری بەرگریانەی خستراوەتە سەر بەڕێوبەران",
        "revdelete-unrestricted": "ئەو سنووری بەرگریانەی لابردراوە لە سەر بەڕێوبەران",
+       "logentry-block-block": "$1 {{GENDER:$4|$3}}ی بۆ ماوەی $5 {{GENDER:$2|بەربەست کرد}} $6",
        "logentry-move-move": "$1 پەڕەی $3ی {{GENDER:$2|گواستەوە}} بۆ $4",
        "logentry-move-move-noredirect": "$1 پەڕەی $3ی بەبێ بەجێھشتنی ڕەوانەکەرێک {{GENDER:$2|گواستەوە}} بۆ $4",
        "logentry-move-move_redir": "$1 پەڕەی $3 {{GENDER:$2|گواستەوە}} بۆ $4 کە پێشتر ڕەوانەکەر بوو",
index 29d0707..1dc61ea 100644 (file)
@@ -5,7 +5,8 @@
                        "Alessandro",
                        "Don Alessandro",
                        "Urhixidur",
-                       "아라"
+                       "아라",
+                       "Исмаил Садуев"
                ]
        },
        "tog-underline": "Багълантыларнынъ тюбюни сызув:",
        "mytalk": "Музакере",
        "anontalk": "Бу IP-нинъ музакереси",
        "navigation": "Сайтта ёл тапув",
-       "and": "&#32;ве",
+       "and": "&#32;а",
        "qbfind": "Тап",
        "qbbrowse": "Бакъып чыкъ",
        "qbedit": "Денъиштир",
        "createaccountreason": "Себеп:",
        "createacct-reason": "Себеп",
        "createacct-reason-ph": "Башкъа бир эсап язысы неден себеп яратасынъыз",
-       "createacct-captcha": "Телюкесизлик контроли",
-       "createacct-imgcaptcha-ph": "Юкъарыда корьген метнинъизни язынъыз",
        "createacct-submit": "Эсап язынъызны яратынъыз",
        "createacct-another-submit": "Башкъа бир эсап язысы яратынъыз",
        "createacct-benefit-heading": "{{SITENAME}} сизинъ киби адамлар тарафындан языла.",
        "move-page-legend": "Саифенинъ адыны денъиштирюв",
        "movepagetext": "Ашагъыдаки форма къулланылып саифенинъ ады денъиштирилир. Бунынънен берабер денъиштирмелер журналы да янъы адгъа авуштырылыр.\nЭски ады янъы адына ёнетме олур. Эски серлевагъа ёнетип тургъан саифелерни автоматик оларакъ янъартып оласынъыз. Бу арекетни автоматик япмагъа истемесенъиз, бутюн [[Special:DoubleRedirects|чифт]] ве [[Special:BrokenRedirects|йыртыкъ]] ёнетме саифелерини озюнъиз тешкермеге меджбур олурсынъыз. Багълантылар эндиден берли догъру чалышмасындан эмин олмалысынъыз.\n\nЯнъы адда бир саифе энди бар олса, ад денъиштирилюви <strong>япылмайджакъ</strong>, анджакъ бар олгъан саифе ёнетме я да бош олса ад денъиштирилюви мумкюн оладжакъ. Бу демек ки, саифенинъ адыны янълыштан денъиштирген олсанъыз деминки адыны кери къайтарып оласынъыз, амма бар олгъан саифени тесадюфен ёкъ этамайсынъыз.\n\n<strong>ТЕНБИ!</strong>\nАд денъиштирилюви популяр саифелер ичюн буюк ве бекленмеген денъишмелерге себеп ола билир. Лютфен, денъиштирме япмаздан эвель ола биледжеклерни козь огюне алынъыз.",
        "movepagetalktext": "Къошулгъан музакере саифесининъ де (бар олса)\nады автоматик тарзда денъиштириледжек. '''Мустесналар:'''\n\n* Айны бу адда бош олмагъан бир музакере саифеси энди бар;\n* Ашагъыдаки бошлукъкъа ишарет къоймадынъыз.\n\nБойле алларда, керек олса, саифелерни къолнен ташымагъа я да бирлештирмеге меджбур олурсынъыз.",
-       "movearticle": "Эски ад",
        "movecategorypage-warning": "<strong>Ихтар:</strong> Бир категория саифесининъ адыны денъиштирмек узьресинъиз. Лютфен, ялынъыз категория саифесининъ кочюриледжегини ве эски категорияда ер алгъан саифелернинъ янъы категориягъа авотматик оларакъ <em>авуштырылмайджагъыны</em> унутманъыз.",
        "movenologintext": "Саифенинъ адыны денъиштирип олмакъ ичюн [[Special:UserLogin|отурым ачынъыз]].",
        "movenotallowed": "Саифелер адларыны денъиштирмеге изининъиз ёкъ.",
index 4414b1b..99fc15b 100644 (file)
        "showingresultsinrange": "Unten {{PLURAL:$1|wird <strong>ein</strong> Ergebnis|werden bis zu <strong>$1</strong> Ergebnisse}} im Bereich <strong>$2</strong> bis <strong>$3</strong> angezeigt.",
        "search-showingresults": "{{PLURAL:$4|Ergebnis <strong>$1</strong> von <strong>$3</strong>|Ergebnisse <strong>$1 bis $2</strong> von <strong>$3</strong>}}",
        "search-nonefound": "Zu deiner Suchanfrage wurden keine Ergebnisse gefunden.",
+       "search-nonefound-thiswiki": "Es gibt auf dieser Website keine der Suchanfrage entsprechenden Ergebnisse.",
        "powersearch-legend": "Erweiterte Suche",
        "powersearch-ns": "Suche in Namensräumen:",
        "powersearch-togglelabel": "Wähle aus:",
index 3b1f548..4b8b4fa 100644 (file)
                        "JasterTDC",
                        "Laurenslimb",
                        "Tusca",
-                       "Tadol"
+                       "Tadol",
+                       "Nelson6e65"
                ]
        },
        "tog-underline": "Subrayar los enlaces:",
        "tog-oldsig": "Firma actual:",
        "tog-fancysig": "Tratar la firma como wikitexto (sin un enlace automático)",
        "tog-uselivepreview": "Usar previsualización dinámica",
-       "tog-forceeditsummary": "Avisarme cuando grabe la página sin escribir un resumen de edición",
+       "tog-forceeditsummary": "Avisarme cuando deje en blanco el resumen de la edición",
        "tog-watchlisthideown": "Ocultar mis ediciones de la lista de seguimiento",
        "tog-watchlisthidebots": "Ocultar las ediciones de bots de la lista de seguimiento",
        "tog-watchlisthideminor": "Ocultar las ediciones menores de la lista de seguimiento",
index 473ae02..4313261 100644 (file)
        "group-bot": "ربات‌ها",
        "group-sysop": "مدیران",
        "group-bureaucrat": "دیوان‌سالاران",
-       "group-suppress": "Ù¾Ù\86Ù\87اÙ\86گران",
+       "group-suppress": "Ù\81رÙ\88Ù\86شاÙ\86Ù\86دگان",
        "group-all": "(همه)",
        "group-user-member": "{{GENDER:$1|کاربر}}",
        "group-autoconfirmed-member": "{{GENDER:$1|کاربر تأییدشده}}",
        "group-bot-member": "ربات",
        "group-sysop-member": "{{GENDER:$1|مدیر}}",
        "group-bureaucrat-member": "{{GENDER:$1|دیوان‌سالار}}",
-       "group-suppress-member": "{{GENDER:$1|Ù¾Ù\86Ù\87اÙ\86گر}}",
+       "group-suppress-member": "{{GENDER:$1|Ù\81رÙ\88Ù\86شاÙ\86Ù\86دÙ\87}}",
        "grouppage-user": "{{ns:project}}:کاربران",
        "grouppage-autoconfirmed": "{{ns:project}}:کاربران تأییدشده",
        "grouppage-bot": "{{ns:project}}:ربات‌ها",
        "grouppage-sysop": "{{ns:project}}:مدیران",
        "grouppage-bureaucrat": "{{ns:project}}:دیوان‌سالاران",
-       "grouppage-suppress": "{{ns:project}}:Ù¾Ù\86Ù\87اÙ\86گر",
+       "grouppage-suppress": "{{ns:project}}:Ù\81رÙ\88Ù\86شاÙ\86Û\8c",
        "right-read": "خواندن صفحه",
        "right-edit": "ویرایش صفحه",
        "right-createpage": "ایجاد صفحه (در مورد صفحه‌های غیر بحث)",
index 1d9e8ce..3890845 100644 (file)
        "hebrew-calendar-m4": "tévet",
        "hebrew-calendar-m5": "chevat",
        "hebrew-calendar-m6": "adar",
-       "hebrew-calendar-m7": "Nissane",
-       "hebrew-calendar-m8": "Iyar",
-       "hebrew-calendar-m9": "Sivane",
-       "hebrew-calendar-m10": "Tamouz",
-       "hebrew-calendar-m11": "Av",
-       "hebrew-calendar-m12": "Éloul",
+       "hebrew-calendar-m6a": "adar I",
+       "hebrew-calendar-m6b": "adar II",
+       "hebrew-calendar-m7": "nissan",
+       "hebrew-calendar-m8": "iyar",
+       "hebrew-calendar-m9": "sivan",
+       "hebrew-calendar-m10": "tamouz",
+       "hebrew-calendar-m11": "av",
+       "hebrew-calendar-m12": "eloul",
        "hebrew-calendar-m1-gen": "tichri",
        "hebrew-calendar-m2-gen": "hechvan",
        "hebrew-calendar-m3-gen": "kislev",
        "hebrew-calendar-m4-gen": "tévet",
        "hebrew-calendar-m5-gen": "chevat",
        "hebrew-calendar-m6-gen": "adar",
-       "hebrew-calendar-m7-gen": "Nissane",
-       "hebrew-calendar-m8-gen": "Iyar",
-       "hebrew-calendar-m9-gen": "Sivane",
-       "hebrew-calendar-m10-gen": "Tamouz",
-       "hebrew-calendar-m11-gen": "Av",
-       "hebrew-calendar-m12-gen": "Éloul",
+       "hebrew-calendar-m6a-gen": "adar I",
+       "hebrew-calendar-m6b-gen": "adar II",
+       "hebrew-calendar-m7-gen": "nissan",
+       "hebrew-calendar-m8-gen": "iyar",
+       "hebrew-calendar-m9-gen": "sivan",
+       "hebrew-calendar-m10-gen": "tamouz",
+       "hebrew-calendar-m11-gen": "av",
+       "hebrew-calendar-m12-gen": "eloul",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discussion]])",
        "duplicate-defaultsort": "Attention : la clé de tri par défaut « $2 » écrase la précédente clé « $1 ».",
        "duplicate-displaytitle": "<strong>Attention :</strong> Le titre d'affichage «$2» remplace l'ancien titre d'affichage «$1».",
index 98e762e..c6aa30b 100644 (file)
        "showingresultsinrange": "{{PLURAL:$1|מוצגת תוצאה <strong>אחת</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": "לא נמצאו תוצאות המתאימות לחיפוש.",
+       "search-nonefound-thiswiki": "לא נמצאו תוצאות המתאימות לחיפוש באתר זה.",
        "powersearch-legend": "חיפוש מתקדם",
        "powersearch-ns": "חיפוש על־פי מרחבי שם:",
        "powersearch-togglelabel": "בחירה:",
index 4532ada..67c616b 100644 (file)
@@ -42,7 +42,8 @@
                        "Mirws",
                        "Ilham",
                        "Matma Rex",
-                       "WongKentir"
+                       "WongKentir",
+                       "Rachmat.Wahidi"
                ]
        },
        "tog-underline": "Garis bawahi pranala:",
        "sp-contributions-newbies": "Hanya dari para pengguna baru",
        "sp-contributions-newbies-sub": "Untuk pengguna baru",
        "sp-contributions-newbies-title": "Kontribusi pengguna baru",
-       "sp-contributions-blocklog": "Log pemblokiran",
+       "sp-contributions-blocklog": "log pemblokiran",
        "sp-contributions-suppresslog": "kontribusi pengguna yang disembunyikan",
        "sp-contributions-deleted": "kontribusi pengguna yang dihapus",
        "sp-contributions-uploads": "unggahan",
index f0b98c9..f3cab6b 100644 (file)
        "showingresultsinrange": "{{PLURAL:$1|Viene mostrato|Vengono mostrati}} sotto {{PLURAL:$1|<strong>1</strong> risultato|<strong>$1</strong> risultati}} dal <strong>$2</strong> al <strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Risultato <strong>$1</strong> di <strong>$3</strong>|Risultati <strong>$1 - $2</strong> di <strong>$3</strong>}}",
        "search-nonefound": "La ricerca non ha prodotto risultati.",
+       "search-nonefound-thiswiki": "La ricerca non ha prodotto risultati in questo sito.",
        "powersearch-legend": "Ricerca avanzata",
        "powersearch-ns": "Cerca nei namespace:",
        "powersearch-togglelabel": "Seleziona:",
index 1ee5473..f209c00 100644 (file)
        "showingresultsinrange": "<strong>$2</strong> 件目から<strong>$3</strong> 件目までの範囲内で最大 {{PLURAL:$1|<strong>$1</strong> 件の結果}}を表示しています。",
        "search-showingresults": "{{PLURAL:$4|<strong>$3</strong> 件中の <strong>$1</strong> 件目|<strong>$3</strong> 件中の <strong>$1</strong> 件目から <strong>$2</strong> 件目}}",
        "search-nonefound": "問い合わせに合致する検索結果はありませんでした。",
+       "search-nonefound-thiswiki": "このサイトでの、そのクエリに一致する結果は、何もありませんでした。",
        "powersearch-legend": "高度な検索",
        "powersearch-ns": "名前空間を指定して検索:",
        "powersearch-togglelabel": "チェックを入れる:",
index 7b783cc..8859cab 100644 (file)
        "showingresultsinrange": "#<strong>$2</strong>부터 #<strong>$3</strong>까지의 범위에서 <strong>$1</strong>개의 {{PLURAL:$1|결과}}가 아래에 보입니다.",
        "search-showingresults": "{{PLURAL:$4|결과 <strong>$3</strong>개 중 <strong>$1</strong>개|결과 <strong>$3</strong>개 중 <strong>$1 - $2</strong>개}}",
        "search-nonefound": "검색어와 일치하는 결과가 없습니다.",
+       "search-nonefound-thiswiki": "이 사이트에서 검색어와 일치하는 결과가 없습니다.",
        "powersearch-legend": "고급 검색",
        "powersearch-ns": "다음 이름공간에서 검색:",
        "powersearch-togglelabel": "확인:",
index ba06d01..c836c07 100644 (file)
        "passwordreset-emailerror-capture": "En <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> met Aanjahbe zom neue Paßwoot för der Zohjang heh sullt verschek wääde, ävver dat Verscheke aan {{GENDER:$2|dä|dat|dä Metmaacher|de|dat}} $2 hät nit jeflup: $1",
        "changeemail": "Donn en Adräß för de <i lang=\"en\">e-mail</i> ändere udder fott schmiiße",
        "changeemail-header": "Donn heh dat Fommulaa ußfölle, öm Ding Adräß för de <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> ze ändere. Wann De en Adräß loß wähde wells, maach dat Fäld läddesch, ih dat De dat Fommolaa loß scheks.",
-       "changeemail-passwordrequired": "Do moÃ\9f Ding PaÃ\9fwood enjävve, Ã¶m di Ã¤nderong ze beschtähteje.",
+       "changeemail-passwordrequired": "Do moÃ\9f Ding PaÃ\9fwood enjävve, Ã¶m di Ã\84nderong ze beschtähteje.",
        "changeemail-no-info": "Do mööts ald enjelogg sin, öm tiräk op di Sigg jonn ze dörve",
        "changeemail-oldemail": "Ding Address för de <i lang=\"en\">e-mail</i> es jäz:",
        "changeemail-newemail": "Ding neue Address för de <i lang=\"en\">e-mail</i> sull wääde:",
index ed1dac4..b496fbc 100644 (file)
        "userlogin-yourpassword-ph": "Şîfreya xwe binivîse",
        "createacct-yourpassword-ph": "Şîfreya xwe binivîse",
        "yourpasswordagain": "Şîfreyê dîsa binivîse:",
-       "createacct-yourpasswordagain": "Şîfreye bipejirîne",
-       "createacct-yourpasswordagain-ph": "Şîfreyê ji nû ve têkeve",
+       "createacct-yourpasswordagain": "Şîfreyê bipejirîne",
+       "createacct-yourpasswordagain-ph": "Şîfreyê ji nû ve binivîse",
        "remembermypassword": "Şifreya min di her têketina min de bîne bîra xwe (herî zêde $1 {{PLURAL:$1|rojekê|rojan}})",
        "userlogin-remembermypassword": "Min têketî bihêle",
        "userlogin-signwithsecure": "Girêdana parastî bikarbîne",
        "gotaccount": "Hesabê te heye? $1.",
        "gotaccountlink": "Têkeve",
        "userlogin-resetlink": "Te agahiyên hesabê xwe ji bîr kirin?",
-       "userlogin-resetpassword-link": "Te şîfreye xwe jibîrkir?",
+       "userlogin-resetpassword-link": "Te şîfreya xwe ji bîr kir?",
        "userlogin-helplink2": "Alîkariya têketinê",
-       "userlogin-createanother": "Hesabek din çeke",
+       "userlogin-createanother": "Hesabekî din çeke",
        "createacct-emailrequired": "E-name",
        "createacct-emailoptional": "E-name",
        "createacct-email-ph": "E-nameya xwe binivîse",
-       "createacct-another-email-ph": "E-nameya xwe têkeve",
-       "createaccountmail": "Şîfreyek ji bo ji bo demeke kin bikarbînin û ji navnîşana hatiye diyarkirin re e-nameyek bişînin.",
+       "createacct-another-email-ph": "E-nameya xwe binivîse",
+       "createaccountmail": "Şîfreyeke demkî bikar bîne û wê ji navnîşana hatiye diyarkirin re bişîne.",
        "createacct-realname": "Navê te ya rast (Ko tu bixwazi bikeve, pêdivî nîne)",
        "createaccountreason": "Sedem:",
        "createacct-reason": "Sedem",
-       "createacct-reason-ph": "Çima hesabek din çedikîy",
-       "createacct-captcha": "Kontrola asayîşê",
-       "createacct-imgcaptcha-ph": "Nivîsa ku tu li jor dibînî binivîse",
+       "createacct-reason-ph": "Çima hesabekî din çêdikî",
        "createacct-submit": "Hesabê xwe biafirîne",
-       "createacct-another-submit": "Hesabek çêke",
-       "createacct-benefit-heading": "{{SITENAME}} ji alî mirovên wek te tê çêkirin.",
+       "createacct-another-submit": "Hesabekî çêke",
+       "createacct-benefit-heading": "{{SITENAME}} ji aliyê mirovên wek te ve tê çêkirin.",
        "createacct-benefit-body1": "{{PLURAL:$1|guhertin}}",
        "createacct-benefit-body2": "{{PLURAL:$1|rûpel}}",
        "badretype": "Her du şîfreyên ku te nivîsîn li hevdu nayên.",
        "userexists": "Ev navî bikarhênerî berê tê bikaranîn. Xêra xwe navekî din dake.",
        "loginerror": "Çewtiya têketinê",
-       "createacct-error": "Çewtîya çêkirine hesabê",
+       "createacct-error": "Çewtiya çêkirina hesabî",
        "createaccounterror": "Hesab nikare were çêkirin: $1",
        "nocookiesnew": "Hesabê bikarhêner hatibû çêkirin, lê te xwe qeyd nekiriye. {{SITENAME}} ji bo qeydkirina bikarhêneran cookie'yan bikartîne. Te bikaranîna cookie'yan girtiye. Xêra xwe cookie'yan qebûl bike, piştre bi navê bikarhêner û şîfreya xwe têkeve.",
        "nocookieslogin": "Ji bo qeydkirina bikarhêneran {{SITENAME}} \"cookies\" bikartîne. Te fonksîyona \"cookies\" girtîye. Xêra xwe kerema xwe \"cookies\" gengaz bike û careke din biceribîne.",
-       "noname": "Navê ku te nivîsand derbas nabe.",
+       "noname": "Navê ku te nivîsand ne derbasdar e.",
        "loginsuccesstitle": "Têketin serkevtî bû!",
        "loginsuccess": "Tu niha di {{SITENAME}} de tomarkirî yî wek \"$1\".",
        "nosuchuser": "Bikarhênerê bi navê \"$1\" tune. Navê rast binivîse an bi vê formê <b>hesabekî nû çêke</b>. (Ji bo hevalên nû \"Têkeve\" çênabe!)",
index 1e22c00..4dd3759 100644 (file)
        "showingresultsinrange": "Долу {{PLURAL:$1|е прикажан до <strong>еден</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": "Нема резултати што одговараат на бараното.",
+       "search-nonefound-thiswiki": "Нема резултати што одговараат на бараното на ова мрежно место.",
        "powersearch-legend": "Напредно пребарување",
        "powersearch-ns": "Пребарај во следниве именски простори:",
        "powersearch-togglelabel": "Одбери:",
index c6e7f2c..c8f4d16 100644 (file)
        "pool-errorunknown": "अपरिचित त्रुटी",
        "pool-servererror": "पूल काउंटर सेवा उपलब्ध नाही($1).",
        "poolcounter-usage-error": "वापर त्रूटी:$1",
-       "aboutsite": "{{SITENAME}}बद्दल",
+       "aboutsite": "{{SITENAME}} बद्दल",
        "aboutpage": "Project:माहितीपृष्ठ",
        "copyright": "येथील मजकूर $1च्या अंतर्गत उपलब्ध आहे जोपर्यंत इतर नोंदी केलेल्या नाहीत.",
        "copyrightpage": "{{ns:project}}:प्रताधिकार",
        "grouppage-suppress": "{{ns:project}}:झापडबंद",
        "right-read": "पृष्ठे वाचा",
        "right-edit": "पाने संपादा",
-       "right-createpage": "पृष्ठे तयार करा (जी चर्चापानांव्यतिरिक्त)",
+       "right-createpage": "पृष्ठे तयार करा (जी चर्चापानांव्यतिरिक्त आहेत)",
        "right-createtalk": "चर्चा पृष्ठे तयार करा",
        "right-createaccount": "नवीन सदस्य खाती तयार करा",
        "right-minoredit": "बदल किरकोळ म्हणून जतन करा",
        "download": "अधिभारण करा",
        "unwatchedpages": "देखरेख नसलेली पाने",
        "listredirects": "पुनर्निर्देशनांची यादी",
+       "listduplicatedfiles": "प्रतिलिपी(डुप्लिकेट) संचिकांची यादी",
        "unusedtemplates": "न वापरलेले साचे",
        "unusedtemplatestext": "या पानावर साचा नामविश्वातील अशी सर्व पाने आहेत जी कुठल्याही पानात वापरलेली नाहीत. वगळण्यापूर्वी साच्यांना जोडणारे इतर दुवे पाहण्यास विसरू नका.",
        "unusedtemplateswlh": "इतर दुवे",
        "unusedimages": "न वापरलेल्या संचिका",
        "wantedcategories": "पाहिजे असलेले वर्ग",
        "wantedpages": "पाहिजे असलेले लेख",
+       "wantedpages-summary": "ही,ज्यांना अधिकांश दुवे आहेत अश्या अस्तित्वात नसलेल्या पानांची यादी आहे. यात ती पाने वगळली आहेत, ज्यांना फक्त पुनर्निर्देशनाचा दुवा आहे. अस्तित्वात नसलेली पण पुनर्निर्देशनाने जोडलेली जी पाने आहेत, अश्यांच्या यादीसाठी [[{{#special:BrokenRedirects}}|मोडकी पुनर्निर्देशने असलेल्या पानांची यादी]] बघा.",
        "wantedpages-badtitle": "परिणामाच्या यादीत अवैध शीर्षक: $1",
        "wantedfiles": "पाहिजे असलेल्या संचिका",
        "wantedfiletext-cat": "पुढील फाइल्स वापरल्या असतील पण आता अस्तित्वात नाहीत. बाहेरील ठिकाणांच्या फाइल्स येथे दिसतात पण असतीलच असे नाही. अशा फाइल्स आढळल्यास वगळल्या जातील. अतिरिक्तपणे,अशी पाने, ज्यात टाकलेल्या संचिका अस्तित्वात नाहीत,त्याची यादी [[:$1]] येथे दिसेल.",
        "exif-label": "लेबल",
        "exif-datetimemetadata": "मेटाडाटाच्या शेवटच्या बदलाची तारीख",
        "exif-nickname": "चित्राचे / फोटोचे सामान्य नाव",
-       "exif-rating": "गुण (५ पैकी)",
+       "exif-rating": "गुणानुक्रम (५ पैकी)",
        "exif-rightscertificate": "अधिकार व्यवस्थापन प्रमाणपत्र",
        "exif-copyrighted": "प्रताधिकार स्थिती",
        "exif-copyrightowner": "प्रताधिकार धारक",
index 04b324e..266cbb1 100644 (file)
        "showingresultsinrange": "{{PLURAL:$1|Vene mmustato|Veneno mmustate}} abbascio {{PLURAL:$1|<strong>1</strong> risultato|<strong>$1</strong> risultate}} d' 'o <strong>$2</strong> a 'o <strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Risultato <strong>$1</strong> 'e <strong>$3</strong>|Risultate <strong>$1 - $2</strong> 'e <strong>$3</strong>}}",
        "search-nonefound": "'A ricerca nun ha produtto risultate.",
+       "search-nonefound-thiswiki": "Nun ce stevano risultate p' 'a ricerca fatta.",
        "powersearch-legend": "Ricerca avanzata",
        "powersearch-ns": "Ascìa dint' 'o namespace:",
        "powersearch-togglelabel": "Cuntrolla:",
index f146b27..600b7dc 100644 (file)
@@ -15,7 +15,7 @@
        "tog-numberheadings": "Automuattizesti numeroija kirjutuksien nimet",
        "tog-showtoolbar": "Ozuta ruadovälinehpalki",
        "tog-editondblclick": "Edituiče sivuloi kaksoispainalduksel",
-       "tog-editsectiononrightclick": "Korjua sektsielöi painamal sektsien nimie hiiren oigiel näppäimel",
+       "tog-editsectiononrightclick": "Kohendele sektsielöi painamal sektsien nimie hiiren oigiel näppäimel",
        "tog-watchcreations": "Ližiä minun luajitut sivut da ližätyt failat minun valvonduluvetteloh",
        "tog-watchdefault": "Ližiä minun kohendetut sivut da failat minun valvonduluvetteloh",
        "tog-watchmoves": "Ližiä minun siirretyt sivut da failat minun valvonduluvetteloh",
@@ -33,7 +33,7 @@
        "tog-fancysig": "Allekirjutus wikitekstannu (ilmai automuattistu linkii)",
        "tog-uselivepreview": "Käytä välittömiä ezikaččeluu",
        "tog-forceeditsummary": "Huomavuta minuu, gu en olle kirjutannuh yhtehveduo",
-       "tog-watchlisthideown": "Peitä minun korjavukset valvonduluvettelospäi",
+       "tog-watchlisthideown": "Peitä minun kohendelut valvonduluvettelospäi",
        "tog-watchlisthidebots": "Peitä botan kohendukset valdondulistalpäi",
        "tog-watchlisthideminor": "Peitä pienet kohendukset valvondulistalpäi",
        "tog-watchlisthideliu": "Peitä kirjutannuhuoloin käyttäjien kohendukset valvondulistalpäi",
        "print": "Pane bumuagale",
        "view": "Kačo",
        "view-foreign": "Kačo saital $1",
-       "edit": "Korjua",
+       "edit": "Kohendele",
        "edit-local": "Edituiče paikallistu kuvavustu",
        "create": "Luaji",
        "create-local": "Ližiä paikalline kuvavus",
-       "editthispage": "Korjua tädä sivuu",
+       "editthispage": "Kohendele tädä sivuu",
        "create-this-page": "Luaji tämä sivu",
        "delete": "Ota iäre",
        "deletethispage": "Ota tämä sivu iäre",
        "disclaimers": "Kieldävymine vastuos",
        "disclaimerpage": "Project:Vastuos kieldävymine",
        "edithelp": "Abu korjuamizeh",
-       "helppage-top-gethelp": "Kyzyö abuu",
+       "helppage-top-gethelp": "Kyzy abuu",
        "mainpage": "Piäsivu",
        "mainpage-description": "Piäsivu",
        "policy-url": "Project:Käytändöt",
        "newmessageslinkplural": "{{PLURAL:$1|uuzi viesti|999=uuzii viestilöi}}",
        "newmessagesdifflinkplural": "{{PLURAL:$1|jälgimäine muutos|jälgimästy muutostu}}",
        "youhavenewmessagesmulti": "Sinul on uuzii viestilöi sivuloil $1",
-       "editsection": "Korjua",
-       "editold": "korjua",
+       "editsection": "Kohendele",
+       "editold": "kohendele",
        "viewsourceold": "Kačo algukoodu",
-       "editlink": "korjua",
+       "editlink": "kohendele",
        "viewsourcelink": "Kačo algukoodu",
-       "editsectionhint": "Korjua tädä kohtua: $1",
+       "editsectionhint": "Kohendele tädä kohtua: $1",
        "toc": "Sizäldö",
        "showtoc": "ozuttua",
        "hidetoc": "peittiä",
        "hr_tip": "Horizontualine viivu (älä käytä liijakse)",
        "summary": "Yhtehvedo:",
        "subject": "Tiemu/rubriekku:",
-       "minoredit": "Tämä on pieni korjavus",
+       "minoredit": "Tämä on pieni kohendelu",
        "watchthis": "Tarkaile tädä sivuu",
        "savearticle": "Tallenda sivu",
        "preview": "Ezikačo",
        "showpreview": "Ezikačo",
-       "showdiff": "Luajitut korjavukset",
+       "showdiff": "Luajitut kohendelut",
        "anoneditwarning": "<strong>Varaitus:</strong> Et ole kirjutannuhes. Luadinet muutoksii syväindölöih, sinun Ip-adressu tulou nägövih kaikile. Ku <strong>[$1 kirjutannuttos]</strong> libo <strong>[$2 registriiruičettos]</strong>, sinun syväindömuutokset nävytäh sinun käyttäinimel, toizien eduloin ližäkse.",
        "summary-preview": "Yhtehvevon ezikačondu:",
        "subject-preview": "Teeman ezikačondu:",
        "template-semiprotected": "(puolekse suojattu)",
        "hiddencategories": "Tämä sivu kuuluu {{PLURAL:$1|1 peitettyh kategourieh|$1 peitettyh kategourieh}}:",
        "sectioneditnotsupported-title": "Sektsien kohendustu ei tuveta.",
-       "sectioneditnotsupported-text": "Sektsieb kohendustu ei tuveta täl sivul.",
+       "sectioneditnotsupported-text": "Sektsien kohendustu ei tuveta täl sivul.",
        "permissionserrors": "Ei oigevuksii",
        "permissionserrorstext": "Sinul ei ole lubua toimindoh {{PLURAL:$1|tämän syyn periä|nämmien syylöin periä}}:",
        "permissionserrorstext-withaction": "Sinul ei ole lubua toimindoh $2 niškoi, {{PLURAL:$1|tämän syyn periä|nämmien syylöin periä}}:",
        "action-move-subpages": "siirrä tämä sivu, da sen alisivut",
        "action-movefile": "siirrä tämä failu",
        "enhancedrc-history": "histourii",
-       "recentchanges": "Uvvet korjavukset",
+       "recentchanges": "Uvvet kohendelut",
        "recentchanges-legend": "Tuorehien muutoksien azetukset",
        "recentchanges-summary": "Jällitä kaikkii jälgimäzet muutokset wikih täl sivul.",
        "recentchanges-label-newpage": "Tämä korjavus on johtanuh uvven sivun luadimizeh",
-       "recentchanges-label-minor": "Tämä on pieni korjavus",
+       "recentchanges-label-minor": "Tämä on pieni kohendelu",
        "recentchanges-label-bot": "Tämän muutoksen on luadinuh bot",
        "recentchanges-label-unpatrolled": "Tädä korjuandua vie ei ole tarkistettu",
        "recentchanges-label-plusminus": "Sivu on kazvanuh [] baital",
        "boteditletter": "b",
        "rc-change-size-new": "Kogo jälles muutoksii: $1 {{PLURAL:$1|baitu|baitua}}",
        "recentchangeslinked": "Koskijat muutokset",
-       "recentchangeslinked-toolbox": "Toine toizeh liittyjät korjavukset",
+       "recentchangeslinked-toolbox": "Toine toizeh liittyjät kohendelut",
        "recentchangeslinked-title": "Muutokset koskijen sivuu \"$1\"",
        "recentchangeslinked-summary": "Tämä on nengomien sivuloin korjavuksien luvettelo, kudamih viittuau tämä sivu (libo sih kategourieh kuulujat).Sivut, kudamat kuulutah [[Special:Watchlist|teijän valvonduluvettelo]], ollah  <strong>bold</strong>.",
        "recentchangeslinked-page": "Sivun nimi:",
        "whatlinkshere-hidelinks": "$1 linkat",
        "whatlinkshere-filters": "Filtrat",
        "blocklink": "Lukiče",
-       "contribslink": "korjavukset",
+       "contribslink": "kohendelut",
        "movelogpage": "Siirrä loga",
        "export": "Vie sivut",
        "allmessages-language": "Kieli:",
        "tooltip-pt-logout": "Kirjuttai ullos",
        "tooltip-pt-createaccount": "Voit registriiruijakseh da kirjuttuakseh järjestelmäh, ga se ei ole vältämätöi",
        "tooltip-ca-talk": "Pagize piäsivun sizäldös",
-       "tooltip-ca-edit": "Korjua tädä sivuu",
+       "tooltip-ca-edit": "Kohendele tädä sivuu",
        "tooltip-ca-addsection": "Luaji uuzi alalugu",
        "tooltip-ca-viewsource": "Tämä sivu on suojattu. Voit kaččuo sen lähtehkoudan",
        "tooltip-ca-history": "Tämän sivun jälgimäzet muutokset",
        "tooltip-save": "Tallenda muutokset",
        "tooltip-preview": "Ezikačo muutokset. Ole hyvä, luaji nenga ainos enne tallendamistu!",
        "tooltip-diff": "Ozuta sinun luajitut muutokset tekstah",
-       "tooltip-rollback": "Yhtel painalluksel poistua jälgimäine korjavus",
+       "tooltip-rollback": "Yhtel painalluksel poistua jälgimäine kohendelu",
        "tooltip-undo": "\"Kumua\" tuou järilleh aijemban edituičenduversien da avuau edituičenduankietan ezikaččelendutilas. Sen vuoh voi ližätä kumuandumotiivan yhtehvevos.",
        "tooltip-summary": "Kirjuta lyhyt kuvavus",
        "simpleantispam-label": "Anti-spam-tarkistus. \n<strong>älä</strong> täytä!",
index a4ae895..5a61cc9 100644 (file)
        "showingresultsinrange": "Poniżej wyświetlono co najwyżej {{PLURAL:$1|<strong>1</strong> wynik|<strong>$1</strong> wyniki|<strong>$1</strong> wyników}} w zakresie od <strong>$2</strong> do <strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Wynik <strong>$1</strong> z <strong>$3</strong>|Wyniki <strong>$1 - $2</strong> z <strong>$3</strong>}}",
        "search-nonefound": "Brak wyników spełniających kryteria podane w zapytaniu.",
+       "search-nonefound-thiswiki": "Brak wyników spełniających kryteria podane w zapytaniu.",
        "powersearch-legend": "Wyszukiwanie zaawansowane",
        "powersearch-ns": "Przeszukaj przestrzenie nazw:",
        "powersearch-togglelabel": "Zaznacz:",
        "prefs-help-recentchangescount": "Uwzględnia ostatnie zmiany, historię stron i rejestry.",
        "prefs-help-watchlist-token2": "To jest tajny klucz umożliwiający dostęp do kanału internetowego zmian w obserwowanych przez Ciebie stronach.\nKażdy, kto go zna, będzie mógł je zobaczyć, więc zachowaj go dla siebie.\n[[Special:ResetTokens|Kliknij tu, jeśli musisz go zresetować]].",
        "savedprefs": "Twoje preferencje zostały zapisane.",
+       "savedrights": "Zapisano uprawnienia {{GENDER:$1|użytkownika $1|użytkowniczki $1}}.",
        "timezonelegend": "Strefa czasowa:",
        "localtime": "Czas lokalny:",
        "timezoneuseserverdefault": "Użyj domyślnej dla tej wiki ($1)",
index f3f39a9..73dea04 100644 (file)
@@ -12,6 +12,7 @@
        "tog-hideminor": "په وروستيو بدلونو کې واړه سمونونه پټول",
        "tog-hidepatrolled": "په وروستيو بدلونونو کې څارل شوې سمونونه پټول",
        "tog-newpageshidepatrolled": "د نوؤ مخونو په لړليک کې کتل شوي مخونه پټول",
+       "tog-hidecategorization": "په وېشنيزو کې د مخونو وېشنه پټول",
        "tog-extendwatchlist": "يوازې د وروستني بدلونونو د ښکاره کولو لپاره نه بلکه د ټولو بدلونونو د ښکاره کولو لپاره کتنلړ غځول",
        "tog-usenewrc": "په کتنلړ او وروستي بدلونو مخ باندې ډله ايز بدلونونه",
        "tog-numberheadings": "د سرليکونو خپلکاره شمېرايښودنه",
@@ -41,6 +42,7 @@
        "tog-watchlisthideliu": "په کتنلړ کې د ثبت شويو کارنانو سمونې پټول",
        "tog-watchlisthideanons": "په کتنلړ کې د ورکنومو کارنانو سمونې پټول",
        "tog-watchlisthidepatrolled": "په کتنلړ کې څارل شوې سمونې پټول",
+       "tog-watchlisthidecategorization": "په وېشنيزو کې د مخونو وېشنه پټول",
        "tog-ccmeonemails": "هغه برېښليکونه چې زه يې نورو ته لېږم، د هغو يوه کاپي دې ماته هم راشي",
        "tog-diffonly": "د توپيرونو نه لاندې د مخ مېنځپانگه پټول",
        "tog-showhiddencats": "پټې وېشنيزې ښکاره کول",
        "createaccountreason": "سبب:",
        "createacct-reason": "سبب",
        "createacct-reason-ph": "ولې تاسې بل گڼون جوړول غوااړۍ",
-       "createacct-captcha": "امنيتي تدبير",
-       "createacct-imgcaptcha-ph": "پورته ښکاره شوی متن دلته وټاپئ",
        "createacct-submit": "گڼون مو جوړ کړئ",
        "createacct-another-submit": "گڼون جوړول",
        "createacct-benefit-heading": "{{SITENAME}} ستاسې په شان خلکو لخوا جوړ شوی.",
        "sig_tip": "ستاسې لاسليک د وخت د ټاپې سره",
        "hr_tip": "څنډيزه ليکه (ددې په کارولو کې سپما وکړۍ)",
        "summary": "لنډيز:",
-       "subject": "سکالو/سرليک:",
+       "subject": "سکالو:",
        "minoredit": "دا يو وړوکی سمون دی",
        "watchthis": "همدا مخ کتل",
        "savearticle": "مخ خوندي کول",
        "anonpreviewwarning": "''تاسې غونډال ته نه ياست ننوتي. خوندي کولو سره به ستاسې IP پته به د دې مخ د سمونونو په پېښليک کې ثبت شي.''",
        "missingcommenttext": "لطفاً تبصره لاندې وليکۍ.",
        "summary-preview": "د لنډيز مخليدنه:",
-       "subject-preview": "سکاÙ\84Ù\88/سرÙ\84Ù\8aÚ© مخليدنه:",
+       "subject-preview": "د Ø³Ú©Ø§Ù\84Ù\88 مخليدنه:",
        "previewerrortext": "د بدلونونو د مخليدنو په وخت کې مو يوه ستونزه رامېنځ ته شوه.",
        "blockedtitle": "پر کارن بنديز لگېدلی",
        "blockedtext": "'''ستاسې د کارن-نوم يا آی پي پتې مخنيوی شوی.'''\n\nهمدا بنديز د $1 له خوا پر تاسې لږېدلی. او د همدې کړنې سبب ''$2'' دی.\n\n* د بنديز د پېل نېټه: $8\n* د بنديز د پای نېټه: $6\n* بنديزونه دي پر: $7\n\nتاسې کولای شی چې د $1 او يا هم د يو بل [[{{MediaWiki:Grouppage-sysop}}|پازوال]] سره اړيکې ټينگې کړی او د بنديز ستونزې مو هوارې کړی.\nتاسې نه شی کولای چې د 'کارن ته برېښلک لېږل' کړنې نه گټه پورته کړی تر څو چې تاسې د خپل گڼون په [[Special:Preferences|غوره توبونو]] کې يوه کره برېښليک پته نه وي ځانگړې کړې او تر دې بريده چې پر تاسې د هغې د کارولو بنديز نه وي لگېدلی.\nستاسې د دم مهال آی پي پته $3 ده، او ستاسې د بنديز پېژند #$5 دی. مهرباني وکړۍ د خپلې يادونې پر مهال د دغو دوو څخه د يوه او يا هم د دواړو ورکول مه هېروۍ.",
        "rcshowhidemine": "زما سمونې $1",
        "rcshowhidemine-show": "ښکاره کول",
        "rcshowhidemine-hide": "پټول",
+       "rcshowhidecategorization-show": "ښکاره کول",
+       "rcshowhidecategorization-hide": "پټول",
        "rclinks": "هغه وروستي $1 بدلونونه ښکاره کړی چې په $2 ورځو کې پېښ شوي<br />$3",
        "diff": "توپير",
        "hist": "پېښليک",
        "newpageletter": "ن",
        "boteditletter": "ر",
        "number_of_watching_users_pageview": "[$1  {{PLURAL:$1|کتونکی کارن|کتونکي کارنان}}]",
-       "rc_categories": "د Ù\88Û\90Ø´Ù\86Ù\8aزÙ\88 Ø¨Ø±Ù\8aدÙ\88Ù\86ه (په \"|\" بېلول)",
+       "rc_categories": "د Ù\88Û\90Ø´Ù\86Ù\8aزÙ\88 ØªØ± Ø¨Ø±Ù\8aده (په \"|\" بېلول)",
        "rc_categories_any": "له ټاکل شويو هر يو",
        "rc-change-size-new": "$1 {{PLURAL:$1|بايټ|بايټونه}} د بدلون وروسته",
        "newsectionsummary": "/* $1 */ نوې برخه",
        "recentchangeslinked-summary": "دا د هغه بدلونونو لړليک دی چې وروستۍ ځل په تړن لرونکيو مخونو کې د يوه ځانگړي مخ (او يا هم د يوې ځانگړې وېشنيزې غړو) نه رامېنځ ته شوي.\n[[Special:Watchlist|ستاسې د کتنلړ]] مخونه په '''زغرد ليک''' کې ښکاري.",
        "recentchangeslinked-page": "د مخ نوم:",
        "recentchangeslinked-to": "د ورکړل شوي مخ پر ځای د اړونده تړلي مخونو بدلونونه ښکاره کول",
+       "recentchanges-page-added-to-category": "[[:$1]] وېشنيزې کې ورگډ شو",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] او {{PLURAL:$2|يو مخ|$2 مخونه}} وېشنيزې کې ورگډ شول",
+       "recentchanges-page-removed-from-category": "[[:$1]] له وېشنيزې وغورځول شو",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] او {{PLURAL:$2|يو مخ|$2 مخونه}} له وېشنيزې وغورځول شول",
+       "autochange-username": "د مېډياويکي خپلکاره بدلون",
        "upload": "دوتنه پورته کول",
        "uploadbtn": "دوتنه پورته کول",
        "reuploaddesc": "پورته کېدنه ناگارل او بېرته د پورته کېدنې فورمې ته ورگرځېدل",
        "upload-form-label-infoform-description": "څرگندونه",
        "upload-form-label-usage-title": "کارېدنې",
        "upload-form-label-usage-filename": "د دوتنې نوم",
+       "foreign-structured-upload-form-label-infoform-categories": "وېشنيزې",
+       "foreign-structured-upload-form-label-infoform-date": "نېټه",
        "backend-fail-notexists": "د $1 په نوم دوتنه نشته.",
        "backend-fail-delete": "د \"$1\" دوتنه ړنګه نه شوه.",
        "backend-fail-alreadyexists": "د $1 دوتنه له پخوا نه شته.",
        "movenotallowedfile": "تاسې د دوتنو د لېږدولو پرېښله نلرۍ.",
        "cant-move-user-page": "تاسې د کارن مخونو د لېږدولو پرېښله نلرۍ (د څېرمه مخونو نه پرته).",
        "cant-move-to-user-page": "تاسې د يو کارن مخ ته د يوه بل مخ د لېږدولو پرېښله نلرۍ (د يو کارن د څېرمه مخ نه پرته).",
-       "newtitle": "نوي سرليک ته:",
+       "newtitle": "نوی سرليک:",
        "move-watch": "همدا مخ کتل",
        "movepagebtn": "مخ لېږدول",
        "pagemovedsub": "لېږدول په برياليتوب سره ترسره شوه",
        "htmlform-cloner-create": "نور ورگډول",
        "htmlform-cloner-delete": "غورځول",
        "htmlform-cloner-required": "لږ تر لږه يو ارزښت ته اړتيا شته.",
+       "htmlform-title-not-exists": "[[:$1]] نشته.",
+       "htmlform-user-not-exists": "<strong>$1</strong> نشته.",
        "logentry-delete-delete": "$1 د $3 مخ {{GENDER:$2|ړنگ کړ}}",
        "revdelete-content-hid": "مېنځپانگه پټېدلې",
        "revdelete-uname-hid": "کارن نوم پټ شوی",
        "special-characters-group-thai": "تايلنډي",
        "special-characters-group-lao": "لاوي",
        "special-characters-group-khmer": "خمري",
-       "mw-widgets-titleinput-description-new-page": "پدې نوم لا تر اوسه پورې مخ نشته",
-       "mw-widgets-titleinput-description-redirect": "$1 ته ورگرځول"
+       "mw-widgets-titleinput-description-new-page": "تر اوسه پورې دا مخ نشته",
+       "mw-widgets-titleinput-description-redirect": "$1 ته ورگرځېدنه"
 }
index d8c2cb1..183c3cb 100644 (file)
        "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": "Соответствий запросу не найдено.",
+       "search-nonefound-thiswiki": "Нет результатов, соответствующих запросу на этом сайте.",
        "powersearch-legend": "Расширенный поиск",
        "powersearch-ns": "Поиск в пространствах имён:",
        "powersearch-togglelabel": "Отметить:",
index a9df913..17f1e1b 100644 (file)
        "import-error-special": "\"$1\" сирэй импортаммата, тоҕо диэтэххэ кини угуллубут аатын далыгар саҥа сирэйдэри оҥорор көҥүллэммэт эбит.",
        "import-error-invalid": "\"$1\" сирэй импортаммата, тоҕо диэтэххэ маннык аат туттуллара бу биикигэ бобуулаах.",
        "import-error-unserialize": "«$1» сирэй $2 барыла структуураланар (десериализация) кыаҕа суох. Барылга иһинээҕитин модела маннык: $3, маннык серияланар: $4.",
+       "import-error-bad-location": "$2 уларытыы, $3 мадьыалы туһанар буолан бу биики «$1» сирэйигэр бигэргэтиллэр кыаҕа суох эбит, тоҕо диэтэххэ ити мадьыал бу сирэйгэ өйөнүллүбэт.",
        "import-options-wrong": "Алҕастаах {{PLURAL:$2|опция|опциялар}}: <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "Тирэх сирэй ыйыллыбыт аата алҕастаах.",
        "import-rootpage-nosubpage": "\"$1\" тирэх сирэй аатын далыгар сирэй үөдүҥнэрэ (подстраницалар) көҥүллэммэттэр",
        "importlogpage": "Импорт сурунаала",
        "importlogpagetext": "Сирэйдэри историяларын кытта холбуу атын биикилэртэн импортааһын.",
-       "import-logentry-upload-detail": "$1 {{PLURAL:$1|барыл|барыл баар}}",
-       "import-logentry-interwiki-detail": "баÑ\80Ñ\8bÑ\82а $2 Ð±Ð°Ñ\80Ñ\8bлÑ\82ан $1 Ð±Ð°Ñ\80Ñ\8bл",
+       "import-logentry-upload-detail": "$1 {{PLURAL:$1|барыл угулунна|барылы уктубут}}",
+       "import-logentry-interwiki-detail": "Ð\91аÑ\80Ñ\8bÑ\82а $2 Ð±Ð°Ñ\80Ñ\8bлÑ\82ан $1 Ð±Ð°Ñ\80Ñ\8bл Ñ\83гÑ\83лÑ\83нна",
        "javascripttest": "JavaScript тургутуу",
        "javascripttest-pagetext-noframework": "Бу сирэй JavaScript тургутууларга анаммыт.",
        "javascripttest-pagetext-unknownframework": "\"$1\" тургутуу биллибэт эйгэтэ.",
        "pageinfo-watchers": "Кэтээнэр сирэйдэр ахсааннара",
        "pageinfo-visiting-watchers": "Сирэйи кэтиир уонна тиһэх көннөрүүлэри көрбүт дьон ахсаана",
        "pageinfo-few-watchers": "$1 кыттааччыттан аҕыйах кэтээччи",
+       "pageinfo-few-visiting-watchers": "Сирэйи кэтиир уонна тиһэх уларыйыылары көрбүт да, атын да кыттааччылар буолуохтарын сөп",
        "pageinfo-redirects-name": "Бу сирэйгэ утаарыы ахсаана",
        "pageinfo-subpages-name": "Сирэй аннынааҕы сирэйдэр ахсааннара",
        "pageinfo-subpages-value": "$1 ($2 утаарыы; $3 көннөрү (утаарыыта суох))",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|ырытыы]])",
        "duplicate-defaultsort": "Болҕой: Наардааһын «$2» күлүүһэ урукку «$1» күлүүһү сабар (Ключ сортировки переопределяет прежний ключ).",
        "duplicate-displaytitle": "<strong>Болҕой:</strong> Көрдөрүллүбүт «$2» аат урут көрдөрүллүбүт «$1» ааты уларытар.",
+       "invalid-indicator-name": "<strong>Алҕас:Сирэй туругун көрдөрөр индикатор </strong> атрибута <code>name</code> кураанах буолуо суохтаах.",
        "version": "MediaWiki барыла (биэрсийэтэ)",
        "version-extensions": "Туруоруллубут расширениялар",
        "version-skins": "Туруоруллубут тас көстүү барыллара",
        "tags-create-invalid-chars": "Бэлиэ аатыгар сопутуой (<code>,</code>) эбэтэр слэш  (<code>/</code>) буолуохтаах.",
        "tags-create-invalid-title-chars": "Тиэк аатыгар сирэй баһыгар туттуллуо суохтаах бэлиэ киириэ суохтаах",
        "tags-create-already-exists": "«$1» тиэк хайыы-үйэ баар эбит.",
+       "tags-create-warnings-above": "«$1» тиэги оҥорорго маннык {{PLURAL:$2|сэрэтии көһүннэ|сэрэтиилэр көһүннүлэр}}:",
+       "tags-create-warnings-below": "Тиэги салгыы оҥоруоххун баҕараҕын дуо?",
        "tags-delete-title": "Тиэги сот",
        "tags-delete-explanation-initial": "«$1» тиэги билии олоҕуттан сотон эрэҕин.",
        "tags-delete-reason": "Төрүөтэ:",
index 160d151..a72edcf 100644 (file)
        "nstab-template": "Šabluons",
        "nstab-help": "Pagelbuos poslapis",
        "nstab-category": "Kateguorėjė",
+       "mainpage-nstab": "Pėrms poslapis",
        "nosuchaction": "Nier tuokė vēksma",
        "nosuchspecialpage": "Nier tuokė specēlėjė poslapė",
        "nospecialpagetext": "Tamsta prašiet nelaistėna specēlė̄jė poslapė, laistėnū specēliūju poslapiu sōraša rasėt [[Special:SpecialPages|specēliūju poslapiu sārošė]].",
        "right-read": "Skaitītė poslapius",
        "right-edit": "Keistė poslapius",
        "right-upload": "Ikeltė failus",
+       "right-writeapi": "Nauduotė API rašīmō",
        "right-delete": "Trintė poslapius",
        "newuserlogpage": "Nauduotuojė kūrėma sārošos",
        "rightslog": "Nauduotuoju teisiu istuorėjė",
        "undelete-show-file-submit": "Tēp",
        "namespace": "Vardū srėtės:",
        "invert": "Žīmietė prīšėngā",
+       "tooltip-invert": "Pažīmiekat ton varnalė, ka pakavuotomiet pakeitėmus pasėrinktūs poslapiūs (ė prėgolontės vardū srėtis)",
        "namespace_association": "Prėgolontė vardū srėtės",
+       "tooltip-namespace_association": "Pažīmiekat ton varnalė, ka prėgoldītomat aptarėmus, katrėi ī sosėjė so parinkta vardū srėtim",
        "blanknamespace": "(Pagrėndinė)",
        "contributions": "Nauduotuojė duovis",
        "contributions-title": "Nauduotuojė $1 duovis",
        "tooltip-t-permalink": "Nūlatėnė nūruoda ton poslapė atmainuō",
        "tooltip-ca-nstab-main": "Ruodītė poslapė torėni",
        "tooltip-ca-nstab-user": "Ruodītė nauduotuojė poslapi",
-       "tooltip-ca-nstab-special": "Tas poslapis īr specēlos - anuo nagalėm keistė.",
+       "tooltip-ca-nstab-special": "Tas poslapis īr specēlos - anon nagalam keistė.",
        "tooltip-ca-nstab-project": "Ruodītė pruojekta poslapi",
        "tooltip-ca-nstab-image": "Ruodītė abruozdielė poslapi",
        "tooltip-ca-nstab-mediawiki": "Veizietė sėstėmas pranešėma",
        "spambot_username": "''MediaWiki'' reklamu šalėnėms",
        "spam_reverting": "Atkoriama i onkstesne versėje, katra nator nūruodu i $1",
        "spam_blanking": "Vėsos versėjės toriejė nūruodu i $1. Ėšvaluoma",
+       "simpleantispam-label": "Patikrėnėms nug šiokšlėnėma.\n<strong>Napildėkat</strong> šėton!",
        "pageinfo-header-basic": "Poslapė žėnės",
        "pageinfo-header-edits": "Keitėma istuorėjė",
        "pageinfo-header-restrictions": "Poslapė apsauga",
        "exif-make": "Puortaparata dėrbies",
        "exif-model": "Puortaparata muodelis",
        "exif-artist": "Autuorios",
+       "exif-exifversion": "Exif atmains",
        "exif-colorspace": "Spalvū lauks",
        "exif-compressedbitsperpixel": "Abruozdielė sospaudėma rėžėms",
        "exif-pixelydimension": "Abruozdielė platoms",
        "external_image_whitelist": " #Palikėt ta eilotė, tuokė kāp īr <pre>\n#Īrašīkat standartėniu ėšraišku fragmentus (tėktās dali terp //)\n#Anūs bus miegėnama sotapatintė so ėšuorėniu abruozdieliu adresās\n#Tė, katrėi sotaps, bus ruodomė kāp abruozdielē, a kėtė bus ruodomė tėktās kāp nūoruodas\n#Raidiu dėdoms nier svarbos\n#Eilotės, katuos prasided # īr kuomentarā \n\n#Īterpkat vėsus standartiėniu ėšraišku fragmentus prīš šėta eilote. Palikat šėta eilote, tuokė kāp ana īr </pre>",
        "tag-filter": "[[Special:Tags|Žīmiū]] kuošeklis:",
        "tag-filter-submit": "Kuošeklis",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Žīms|Žīmā|Žīmū}}]]: $2)",
        "tags-edit": "taisītė",
        "comparepages": "Primestė poslapio",
        "logentry-delete-delete": "$1 ėštrīnė poslapi $3",
        "logentry-delete-restore": "$1 atkūrė poslapi $3",
        "revdelete-content-hid": "torėnīs pakavuots",
+       "logentry-block-block": "ožgīnė „[[$1]]“ nug dėrbėma, tas vēk ton čiesa - $2 $3",
        "logentry-move-move": "$1 {{GENDER:$2|parvadėna}} poslapi $3 i $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|parvadėna}} poslapi nug $3 i $4 nepalinkdoms nusokėma",
        "logentry-move-move_redir": "$1 {{GENDER:$2|parvadėna}} poslapi nog $3 i $4 ont bovosė nusokėma",
index a9336da..51d00dc 100644 (file)
        "showingresultsinrange": "Spodaj prikazujem {{PLURAL:$1|1=<strong>1</strong> rezultat|<strong>$1</strong> rezultata|<strong>$1</strong> rezultate|<strong>$1</strong> rezultatov}} v razponu od št. <strong>$2</strong> do št. <strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Rezultat <strong>$1</strong> od <strong>$3</strong>|Rezultati <strong>$1–$2</strong> od <strong>$3</strong>}}",
        "search-nonefound": "Ni bilo zadetkov, ki ustrezajo poizvedbi.",
+       "search-nonefound-thiswiki": "Našli nismo nobenega rezultata, ki bi se ujemal s poizvedbo na tej strani.",
        "powersearch-legend": "Napredno iskanje",
        "powersearch-ns": "Iskanje v imenskih prostorih:",
        "powersearch-togglelabel": "Izberi:",
index b364e93..3c0adf8 100644 (file)
        "parser-template-recursion-depth-warning": "Дубина укључивања шаблона је прекорачена ($1)",
        "language-converter-depth-warning": "Прекорачена је граница дубине језичког претварача ($1)",
        "node-count-exceeded-category": "Странице у којима је прекорачен број чворова",
-       "node-count-exceeded-category-desc": "СÑ\82Ñ\80аниÑ\86а Ñ\98е Ð¿Ñ\80екоÑ\80аÑ\87ила Ð±Ñ\80оÑ\98 Ñ\87воÑ\80ова.",
+       "node-count-exceeded-category-desc": "СÑ\82Ñ\80аниÑ\86е Ñ\81а Ð¿Ñ\80евиÑ\88е Ñ\87воÑ\80ова (node).",
        "node-count-exceeded-warning": "Страница у којој је прекорачен број чворова",
        "expansion-depth-exceeded-category": "Странице у којима је прекорачена дубина проширења",
        "expansion-depth-exceeded-category-desc": "Страница је прекорачила највећу дубину проширења.",
index 1692650..a16a141 100644 (file)
@@ -66,7 +66,8 @@
                        "Marfuas",
                        "Macofe",
                        "Aaoo",
-                       "Josve05a"
+                       "Josve05a",
+                       "Pipetricker"
                ]
        },
        "tog-underline": "Stryk under länkar:",
        "logentry-upload-upload": "$1 {{GENDER:$2|laddade upp}} $3",
        "logentry-upload-overwrite": "$1 {{GENDER:$2|laddade upp}} en ny version av $3",
        "logentry-upload-revert": "$1 {{GENDER:$2|laddade upp}} $3",
-       "log-name-managetags": "Tagghanterings logg",
+       "log-name-managetags": "Märkeshanteringslogg",
        "log-description-managetags": "Denna sida innehåller administrativa [[Special:Tags|märke]]srelaterade uppgifter. Loggen innehåller bara åtgärder som utförts manuellt av en administratör; märken kan skapas eller raderas av wikins mjukvara utan att en post registreras i loggen.",
        "logentry-managetags-create": "$1 {{GENDER:$2|skapade}} taggen \"$4\"",
        "logentry-managetags-delete": "$1 {{GENDER:$2|raderade}} taggen \"$4\" (borttagen från $5 {{PLURAL:$5|version eller loggpost|versioner och/eller loggposter}})",
index 73b3b6a..839e128 100644 (file)
@@ -5,7 +5,10 @@
                        "VASANTH S.N.",
                        "VinodSBangera",
                        "아라",
-                       "Vishwanatha Badikana"
+                       "Vishwanatha Badikana",
+                       "Bharathesha Alasandemajalu",
+                       "Soundarya shetty s",
+                       "రహ్మానుద్దీన్"
                ]
        },
        "tog-underline": "ಲಿಂಕ್’ಲೆದ ತಿರ್ತ್ ಗೆರೆ(ಅಂಡರ್ ಲೈನ್) ಪಾಡ್’ಲೆ",
@@ -49,8 +52,8 @@
        "editfont-sansserif": "ಸಾನ್ಸ್-ಸೆರಿಫ್ ಲಿಪಿ",
        "editfont-serif": "ಸೆರಿಫ್ ಲಿಪಿ",
        "sunday": "ಐತಾರ",
-       "monday": "ಸà³\8bಮವಾರ",
-       "tuesday": "ಅಂಗರೆ",
+       "monday": "ಸà³\8bಮಾರà³\8a",
+       "tuesday": "à²\85à²\82à²\97ಾರà³\86",
        "wednesday": "ಬುಧವಾರ",
        "thursday": "ಗುರುವಾರ",
        "friday": "ಶುಕ್ರವಾರ",
        "faqpage": "Project:ಸಾಮಾನ್ಯವಾದ್ ಕೇನುನ ಪ್ರಶ್ನೆಲು",
        "actions": "ಕ್ರಿಯೆಕ್ಕುಲು",
        "namespaces": "ಪುದರ್ ದ ವರ್ಗೊಲು",
-       "variants": "ರà³\82ಪಾà²\82ತರ à²¹à³\8aà²\82ದà³\8dâ\80\98ನ",
-       "navigation-heading": "ಸà²\82à²\9aರಣà³\86 à²®à³\86ನà³\81",
+       "variants": "ರà³\82ಪಾà²\82ತರ à²¹à³\8aà²\82ದà³\8dâ\80\8dನ",
+       "navigation-heading": "ಸà²\82à²\9aಾರà³\8aದ à²ªà²°à²¿à²µà²¿à²¡à²¿",
        "errorpagetitle": "ದೋಷ",
        "returnto": "$1 ಗ್ ಪಿರ ಪೋಲೆ.",
        "tagline": "{{SITENAME}} ರ್ದ್",
-       "help": "ಸಹಾಯ",
+       "help": "ಸಹಾಯ",
        "search": "ನಾಡ್",
        "searchbutton": "ನಾಡ್",
        "go": "ಪೋ",
        "history_short": "ಇತಿಹಾಸ",
        "updatedmarker": "ಎನ್ನ ಅಕೇರಿದ ವೀಕ್ಷಣೆ ಡ್ದ್ ಬುಕ್ಕ ಆಯಿನ ಬದಲಾವಣೆಲು",
        "printableversion": "ಪ್ರಿಂಟ್ ಆವೃತ್ತಿ",
-       "permalink": "ಸà³\8dಥಿರ ಸಂಪರ್ಕ",
+       "permalink": "ಸà³\8dತಿರà³\8a ಸಂಪರ್ಕ",
        "print": "ಪ್ರಿ೦ಟ್ ಮನ್ಪುಲೆ",
        "view": "ತೂಲೆ",
        "view-foreign": "$1ಡ್ ತೂಲೆ",
-       "edit": "ಸಂಪಾದನೆ ಮಲ್ಪುಲೆ(Edit this page)",
+       "edit": "ಸಂಪಾದನೆ ಮಲ್ಪುಲೆ(ಪಾಲೆನ್ ಸಂಪಾದನೆ ಮಲ್ಪುಲೆ)",
        "create": "ಸೃಷ್ಟಿಸಾಲೆ",
        "create-local": "ಸ್ಥಳೀಯ ವಿವರಣೆ ಸೇರಾಲೆ",
        "editthispage": "ಈ ಪುಟೊನು ಬದಲಾಯಿಸಾಲೆ",
        "viewhelppage": "ಸಹಾಯ ಪುಟೊನು ತೂಲೆ",
        "categorypage": "ವರ್ಗ ಪುಟೊನು ತೂಲೆ",
        "viewtalkpage": "ಚರ್ಚೆನ್ ತೂಲೆ",
-       "otherlanguages": "ಬà³\87ತà³\86 à²­à²¾à²·ೆಲೆಡ್",
+       "otherlanguages": "ಬà³\87ತà³\86 à²¬à²¾à²¸ೆಲೆಡ್",
        "redirectedfrom": "($1 ರ್ದ್ ಪುನರ್ನಿರ್ದೇಶಿತ)",
        "redirectpagesub": "ಪುನರ್ನಿರ್ದೇಶನ ಪುಟ",
-       "lastmodifiedat": "ಈ ಪುಟ ಇಂದೆತ ದುಂಬು $2, $1 ಕ್ ಬದಲಾತ್’ನ್ಡ್.",
+       "redirectto": "ಪಿರ ಕಡಪುಡ್ಲೆ:",
+       "lastmodifiedat": "ಈ ಪಾಲೆ ಇಂದೆತ ದುಂಬು $2, $1 ಕ್ ಬದಲಾತ್ಂಡ್.",
        "viewcount": "ಈ ಪುಟೊನು {{PLURAL:$1|1 ಸರಿ|$1 ಸರಿ}} ತೂತೆರ್.",
        "protectedpage": "ಸಂರಕ್ಷಿತ ಪುಟ",
-       "jumpto": "ಇಡೆ ಪೋಲೆ:",
+       "jumpto": "ಇಡೆ ಪೋ:",
        "jumptonavigation": "ಸಂಚಾರ",
        "jumptosearch": "ನಾಡ್’ಲೆ",
        "pool-errorunknown": "ಗೊತ್ತಿಂಜಂದಿನ  ದೋಷ",
        "aboutsite": "{{SITENAME}} ದ ಬಗ್ಗೆ",
-       "aboutpage": "Project:ನಮ್ಮ ಬಗ್ಗೆ",
+       "aboutpage": "ಯೋಜನೆ:ಬಗೆಟ್",
        "copyright": "ಉಂದು ಈ ಕಾಪಿರೈಟ್‌ಡ್ ಲಭ್ಯವುಂಡು $1.",
        "copyrightpage": "{{ns:project}}:ಕೃತಿಸ್ವಾಮ್ಯತೆಲು",
        "currentevents": "ಇತ್ತೆದ ಸಂಗತಿಲು",
-       "currentevents-url": "Project:ಇತ್ತೆದ ಸಂಗತಿಲು",
-       "disclaimers": "à²\85ಬಾಧà³\8dಯತà³\86ಲà³\81",
-       "disclaimerpage": "Project:ಸಾಮಾನ್ಯ ಅಬಾಧ್ಯತೆಲು",
+       "currentevents-url": "ಯೋಜನೆ:ಇತ್ತೆದ ಸಂಗತಿಲು",
+       "disclaimers": "ಹà²\95à³\8dà²\95à³\8dâ\80\8cದà²\95à³\81ಲತà³\8dತà³\8d",
+       "disclaimerpage": "ಯೋಜನೆ:ಸಾಮಾನ್ಯೊ ಹಕ್ಕುದಕುಲತ್ತ್",
        "edithelp": "ಸಂಪಾದನೆ(ಎಡಿಟ್) ಮಲ್ಪೆರೆ ಸಹಾಯ",
        "mainpage": "ಮುಖ್ಯ ಪುಟ",
-       "mainpage-description": "ಮà³\81à²\96à³\8dಯ à²ªà³\81à²\9f",
+       "mainpage-description": "ಮà³\81à²\95à³\8dಯà³\8a à²ªà²¾à²²à³\86",
        "policy-url": "Project:ನಿಯಮಾವಳಿ",
-       "portal": "ಸಮà³\81ದಾಯ à²ªà³\81à²\9f",
-       "portal-url": "Project:ಸಮà³\81ದಾಯ à²ªà³\81à²\9f",
+       "portal": "ಸಮà³\81ದಾಯ à²ªà²¾à²²à³\86",
+       "portal-url": "Project:ಸಮà³\81ದಾಯ à²ªà²¾à²²à³\86",
        "privacy": "ಖಾಸಗಿ ನಿಯಮಾವಳಿ",
-       "privacypage": "Project:ಖಾಸಗಿಮಾಹಿತಿ ನಿಯಮ",
+       "privacypage": "ಯೋಜನೆ:ಗುಟ್ಟುದ ನೀತಿ",
        "badaccess": "ಅನುಮತಿ ದೋಷ",
        "badaccess-group0": "ಈರ್ ಕೇನಿನ ಬೇಲೆನ್ ಮಲ್ಪೆರೆ ಇರೆಗ್ ಅನುಮತಿ ಇಜ್ಜಿ.",
        "badaccess-groups": "ಈರ್ ಕೇನಿನಂಚಿನ ಕ್ರಿಯೆ ಖಾಲಿ $1 ಗುಂಪುಲೆಡ್ ಒಂಜೆಕ್ ಸೇರ್ದುಪ್ಪುನ ಬಳಕೆದಾರೆರೆಗ್ ಮಾತ್ರ.",
        "viewsourceold": "ಮೂಲೊನು ತೂಲೆ",
        "editlink": "ಎಡಿಟ್ ಮಲ್ಪುಲೆ",
        "viewsourcelink": "ಮೂಲೊನು ತೂಲೆ",
-       "editsectionhint": "$1 à²µà²¿à²­à²¾à²\97ದ à²¸à²\82ಪಾದನà³\86 à²®à²²à³\8dಪà³\81ಲೆ",
+       "editsectionhint": "$1 à²µà²¿à²¬à²¾à²\97ದ à²¸à²\82ಪಾದಿಸಲೆ",
        "toc": "ಪರಿವಿಡಿ",
        "showtoc": "ತೊಜ್ಪಾವು",
        "hidetoc": "ದೆಂಗಾವು",
        "red-link-title": "$1 (ಈ ಪುಟ ನನಲ ಅಸ್ತಿತ್ವಡ್ ಇಜ್ಜಿ)",
        "sort-descending": "ಇಳಿಕೆ ಕ್ರಮೊಟ್ಟು ಜೋಡಿಸಾಲ",
        "sort-ascending": "ಏರಿಕೆ ಕ್ರಮೊಟ್ಟು ಜೋಡಿಸಾಲ",
-       "nstab-main": "ಪà³\81à²\9f",
+       "nstab-main": "ಪಾಲà³\86",
        "nstab-user": "ಸದಸ್ಯೆರ್ನ ಪುಟ",
        "nstab-media": "ಮೀಡಿಯ ಪುಟ",
        "nstab-special": "ವಿಶೇಷ ಪುಟ",
        "nstab-template": "ಫಲಕ",
        "nstab-help": "ಸಹಾಯ ಪುಟ",
        "nstab-category": "ವರ್ಗ",
+       "mainpage-nstab": "ಮುಕ್ಯ ಪಾಲೆ",
        "nosuchaction": "ಈ ರೀತಿದ ಓವು ಕ್ರಿಯೆಲಾ(ಆಕ್ಶನ್) ಇಜ್ಜಿ",
        "nosuchactiontext": "ಈ URLದ ಒಟ್ಟಿಗೆ ಉಪ್ಪುನ ಕ್ರಿಯೆನ್ ವಿಕಿ ಗುರ್ತ ಪತ್ತುಜಿ",
        "nosuchspecialpage": "ಈ ಪುದರ್’ದ ಒವುಲಾ ವಿಷೇಶ ಪುಟ ಇಜ್ಜಿ",
        "createaccountmail": "ಇ ಮೈಲ್ ಮೂಲಕ",
        "createaccountreason": "ಕಾರಣ",
        "createacct-submit": "ಪೊಸ ಖಾತೆ ಸುರು ಮಲ್ಪುಲೆ",
+       "createacct-benefit-heading": "{{SITENAME}}ನಿಕ್ಲೆನಂಚಿತ್ತಿನ ಜನೊಕ್ಲೆಡ್ದ್ ಉಂಡಾಪುಂಡು.",
        "createacct-benefit-body1": "{{PLURAL:$1|ಸಂಪಾದನೆ|ಸಂಪಾದನೆಲು}}",
        "createacct-benefit-body2": "{{PLURAL:$1|ಪುಟ|ಪುಟಕ್ಕುಲು}}",
        "createacct-benefit-body3": "{{PLURAL:$1|ಕೊಡುಗೆ|ಕೊಡುಗೆಲು}}",
        "preview": "ಮುನ್ನೋಟ",
        "showpreview": "ಮುನ್ನೋಟ ತೊಜ್ಪಾವ್",
        "showdiff": "ಬದಲಾವಣೆಲೆನ್ ತೊಜ್ಪಾವ್",
-       "anoneditwarning": "'''ಜಾಗ್ರತೆ:''' ಈರ್ ಇತ್ತೆ ಲಾಗ್ ಇನ್ ಆತಿಜರ್.\nಈರ್ನ ಐ.ಪಿ ಎಡ್ರೆಸ್ ಈ ಪುಟೊತ ಬದಲಾವಣೆ ಇತಿಹಾಸೊಡು ದಾಖಲಾಪು೦ಡು.",
+       "anoneditwarning": "<strong>ಜಾಗ್‍ರ್ತೆ:</strong> ಈರ್ ಇತ್ತೆ ಲಾಗ್ ಇನ್ ಆತಿಜರ್. ಈರ್ ಸಂಪೊಲಿತರ್ಂಡ ಈರೆನ ಐ.ಪಿ ಎಡ್ರೆಸ್ ಮಾಂತೆರೆಗ್ಲಾ ತೆರಿವುಂಡು. ಒಂಜೇಲ್ಯೆ <strong>[$1 ಲಾಗಿನ್ ಆಯರ್ಂದಾಂಡ]</strong> ಅತ್ತಂಡ <strong>[$2 ಈ ಅಕೌಂಟ್ ಮಲ್ತರ್ಂಡ]</strong>, ಈರ ಸಂಪೊಲ್ತಿನ ಪೂರ ಬೇತೆ ಲಾಬೊದೊಟ್ಟುಗು ಈರೆನ ಪುದರ್‍ಗ್ ಸೇರುಂಡು.'",
        "anonpreviewwarning": "ಈರ್ ಇತ್ತೆ ಲಾಗ್ ಇನ್ ಆತಿಜರ್. ಈರ್ನ ಐ.ಪಿ ಎಡ್ರೆಸ್ ಈ ಪುಟೊತ ಬದಲಾವಣೆ ಇತಿಹಾಸೊಡು ದಾಖಲಾಪು೦ಡು",
        "missingsummary": "'''ಗಮನಿಸಾಲೆ:''' ಈರ್ ಬದಲಾವಣೆದ ಸಾರಾ೦ಶನ್ ಕೊರ್ತಿಜರ್.\nಈರ್ ಪಿರ 'ಒರಿಪಾಲೆ' ಬಟನ್ ನ್ ಒತ್ತ್೦ಡ ಸಾರಾ೦ಶ ಇಜ್ಜ೦ದೆನೇ ಈರ್ನ ಬದಲಾವಣೆ ದಾಖಲಾಪು೦ಡು.",
        "missingcommenttext": "ದಯ ಮಲ್ತ್ ದ ಈರ್ನ ಅಭಿಪ್ರಾಯನ್ ತಿರ್ತ್ ಕೊರ್ಲೆ",
        "accmailtitle": "ಪ್ರವೇಶಪದ ಕಡಪುಡ್‘ದುಂಡು",
        "newarticle": "(ಪೊಸತ್)",
        "newarticletext": "ನನಲ ಅಸ್ಥಿತ್ವಡ್ ಉಪ್ಪಂದಿನ ಪುಟೊಗು ಈರ್ ಬೈದರ್.\nಈ ಪುಟೊನು ಸೃಷ್ಟಿ ಮಲ್ಪೆರೆ ತಿರ್ತ್’ದ ಚೌಕೊಡು ಬರೆಯೆರೆ ಸುರು ಮಲ್ಪುಲೆ.\n(ಜಾಸ್ತಿ ಮಾಹಿತಿಗ್ [$1 ಸಹಾಯ ಪುಟೊನು] ತೂಲೆ).\nಈ ಪುಟೊಕು ಈರ್ ತಪ್ಪಾದ್ ಬತ್ತಿತ್ತ್’ನ್ಡ ಇರೆನ ಬ್ರೌಸರ್’ದ '''back''' ಬಟನ್’ನ್ ಒತ್ತ್’ಲೆ.",
-       "noarticletext": "ಈ ಪುಟೊಟು ಸದ್ಯಗ್ ಓ ಬರಹಲಾ ಇಜ್ಜಿ, ಈರ್ ಬೇತೆ ಪೂಟೊಲೆಡ್ [[Special:Search/{{PAGENAME}}|ಈ ಲೇಖನೊನು ನಾಡೊಲಿ]] ಅತ್ತ್’ನ್ಡ [{{fullurl:{{FULLPAGENAME}}|action=edit}} ಈ ಪುಟೊನು ಸಂಪಾದನೆ ಮಲ್ಪೊಲಿ].",
+       "noarticletext": "ಈ ಪಾಲೆಡ್ ಸದ್ಯಗ್ ಒವ್ವೇ ಬರವುಲಾ ಇಜ್ಜಿ, ಈರ್ ಬೇತೆ ಪಾಲೆಡ್ [[Special:Search/{{PAGENAME}}|ಈ ಲೇಕನೊನು ನಾಡೊಲಿ]] <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ಸಂಬಂಧ ಇತ್ತಿನ ಲಾಗ್ಸ್ ನ್ ನಾಡ್ ಲೆ], ಅತ್ತ್ಂಡ  [{{fullurl:{{FULLPAGENAME}}|action=edit}} ಈ ಪಾಲೆನು ಸಂಪೊಲಿಪೊಲಿ]</span>.",
+       "noarticletext-nopermission": "ಈ ಪಾಲೆಡ್ ಸದ್ಯಗ್ ಒವ್ವೇ ಬರವುಲಾ ಇಜ್ಜಿ, ಈರ್ ಬೇತೆ ಪಾಲೆಡ್ [[Special:Search/{{PAGENAME}}|ಈ ಲೇಕನೊನು ನಾಡೊಲಿ]] <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ಸಂಬಂಧ ಇತ್ತಿನ ಲಾಗ್ಸ್ ನ್ ನಾಡ್ ಲೆ], ಅತ್ತ್ಂಡ  [{{fullurl:{{FULLPAGENAME}}|action=edit}} ಈ ಪಾಲೆನು ಸಂಪೊಲಿಪೊಲಿ]</span>.",
        "userpage-userdoesnotexist": "ಬಳಕೆದಾರ ಖಾತೆ \"<nowiki>$1</nowiki>\" ದಾಖಲಾತ್‘ಜ್ಜಿ. ಈರ್ ಉಂದುವೇ ಪುಟನ್ ಸಂಪಾದನೆ ಮಲ್ಪರ ಉಂಡಾಂದ್ ಖಾತ್ರಿ ಮಲ್ತೊನಿ.",
        "previewnote": "'''ಉಂದು ಕೇವಲ ಮುನ್ನೋಟ; ಪುಟೊನು ನನಲ ಒರಿಪಾದಿಜಿ ಪನ್ಪುನೇನ್ ಮರಪೊರ್ಚಿ!'''",
        "editing": "$1 ಲೇಖನೊನು ಈರ್ ಸಂಪಾದನೆ ಮಲ್ತೊಂದುಲ್ಲರ್",
        "template-semiprotected": "(ಅರೆ-ಸಂರಕ್ಷಿತ)",
        "hiddencategories": "ಈ ಪುಟ {{PLURAL:$1|೧ ಗುಪ್ತ ವರ್ಗಗ್|$1 ಗುಪ್ತ ವರ್ಗೊಲೆಗ್}} ಸೇರ್ದ್’ನ್ಡ್:",
        "permissionserrorstext-withaction": "$2 ಗ್ ಇರೆಗ್ ಅನುಮತಿ ಇಜ್ಜಿ, ಐಕ್ {{PLURAL:$1|ಕಾರಣ|ಕಾರಣೊಲು}}:",
-       "moveddeleted-notice": "à²\88 à²ªà³\87à²\9cà³\8d à²\85ಸà³\8dತಿತà³\8dವಡà³\8d à²\87à²\9cà³\8dà²\9cಿ.\nಪà³\82à²\9fà³\8aತ à²¡à²¿à²²à³\80ಶನà³\8d à²²à²¾à²\97à³\8dâ\80\99ನà³\8d à²¤à²¿à²°à³\8dತà³\8d à²\95à³\8aರà³\8dತà³\81à²\82ಡà³\81.",
+       "moveddeleted-notice": "à²\88 à²ªà²¾à²²à³\86 à²\85ಸà³\8dತಿತà³\8dವಡà³\8d à²\87à²\9cà³\8dà²\9cಿ.\nಪಾಲà³\86ದ à²¡à²¿à²²à³\80ಶನà³\8d à²\85ತà³\8dತà³\8dà²\82ಡà³\8d à²\95ಡಪà³\8dಪà³\81ಡà³\81ನà³\86 à²²à²¾à²\97à³\8dâ\80\8dನà³\8d à²¤à³\82ಯರà³\86 à²¤à²¿à²°à³\8dತà³\8d à²\95à³\8aರà³\8dತà³\8dà²\82ಡà³\8d.",
        "viewpagelogs": "ಈ ಪುಟೊತ ದಾಖಲೆಲೆನ್ ತೂಲೆ",
        "nohistory": "ಈ ಪುಟಕ್ ಬದಲಾವಣೆದ ಇತಿಹಾಸ ಇಜ್ಜಿ",
        "currentrev": "ಇತ್ತೆದ ಆವೃತ್ತಿ",
        "mergehistory-reason": "ಕಾರಣ:",
        "revertmerge": "ಅನ್-ಮರ್ಜ್ ಮಲ್ಪುಲೆ",
        "history-title": "\"$1\" ಪುಟೊತ ಆವೃತ್ತಿ ಇತಿಹಾಸೊ",
+       "difference-title": "ಪುನರ್ ಪರಿಸೀಲನೆದ ನಡುತ ಯತ್ವಾಸೊ \"$1\"",
        "lineno": "$1 ನೇ ಸಾಲ್:",
        "compareselectedversions": "ಆಯ್ಕೆ ಮಲ್ತಿನ ಆವೃತ್ತಿಲೆನ್ ಹೊಂದಾಣಿಕೆ ಮಲ್ತ್ ತೂಲೆ",
        "editundo": "ದುಂಬುದಲೆಕ",
+       "diff-multi-sameuser": "({{PLURAL:$1|One intermediate revision|$1 intermediate revisions }} ಅವ್ವೇ ಬಳಕೆದಾರೆರೆನ್ ತೋಜಾದ್‍ಜಿ)",
        "searchresults": "ನಾಡಟದ ಫಲಿತಾಂಶೊಲು",
        "searchresults-title": "\"$1\" ಕ್ ನಾಡಟದ ಫಲಿತಾಂಶೊಲು",
        "notextmatches": "ವಾ ಪುಟೊತ ಪಠ್ಯೊಡುಲಾ ಹೋಲಿಕೆ ಇಜ್ಜಿ",
        "prevn": "ದುಂಬುದ {{PLURAL:$1|$1}}",
        "nextn": "ಬೊಕ್ಕದ {{PLURAL:$1|$1}}",
+       "nextn-title": "ದುಂಬುದ $1 {{PLURAL:$1|result|ಪಲಿತಾಂಸೊಲ}}",
        "shown-title": "ಪ್ರತಿ ಪುಟೊಡುಲಾ $1 {{PLURAL:$1|result|results}} ತೋಜಿಪಾವು",
        "viewprevnext": "ತೂಲೆ ($1 {{int:pipe-separator}} $2) ($3)",
+       "searchmenu-new": "<strong>ಈ ಪಾಲೆನ್ ರಚಿಸಲೆ \"[[:$1]]\" ಈ ವಿಕಿಡ್!</strong> {{PLURAL:$2|0=|See also the page found with your search.|See also the search results found.}}",
        "searchprofile-articles": "ಲೇಖನ ಪುಟೊ",
        "searchprofile-images": "ಬಹುಮಾಧ್ಯಮ",
        "searchprofile-everything": "ಪ್ರತಿಯೊಂಜಿ",
        "searchprofile-articles-tooltip": "$1 ಟ್ ನಾಡ್ಲೆ",
        "searchprofile-images-tooltip": "ಫೈಲ್ ನಾಡ್ಲೆ",
        "searchprofile-everything-tooltip": "ಮಾತಾ ಪುಟಕ್ಕುಲೆಡ್ ನಾಡ್ಲೆ (ಪಾತೆರದ ಪುತಲ ಸೇರ್ದ್)",
+       "searchprofile-advanced-tooltip": "ಪುದರ್‍ದ ಕ್ರಮೊನು ನಾಡ್‍ಲೆ",
        "search-result-size": "$1 ({{PLURAL:$2|೧ ಪದ|$2 ಪದೊಲು}})",
        "search-redirect": "(ಪುನರ್ನಿರ್ದೇಶನ $1)",
        "search-section": "(ವಿಭಾಗ $1)",
        "search-interwiki-more": "(ಮಸ್ತ್)",
        "searchrelated": "ಸ೦ಬ೦ಧ ಇತ್ತಿನ",
        "searchall": "ಮಾತಾ",
+       "search-showingresults": "{{PLURAL:$4|ಫಲಿತಾಂಶ<strong>$1</strong> of <strong>$3</strong>|ಫಲಿತಾಂಶ <strong>$1 - $2</strong> of <strong>$3</strong>}}",
        "search-nonefound": "ಈರೆನ ವಿಚಾರಣೆಗ್ ತಕ್ಕುದಾಯಿನ ಪಲಿತಾಂಶೊಲು ಇಜ್ಜಿ.",
+       "search-nonefound-thiswiki": "ಈ ಸೈಟ್‍ಡ್ ಪ್ರಶ್ನೆದ ಫಲಿತಾಂಶ ಕೂಡೊಂದಿಜ್ಜಿ",
        "powersearch-legend": "ಅಡ್ವಾನ್ಸ್’ಡ್ ಸರ್ಚ್",
        "powersearch-ns": "ನೇಮ್-ಸ್ಪೇಸ್’ಲೆಡ್ ನಾಡ್ಲೆ",
        "powersearch-toggleall": "ಮಾತಾ",
        "grouppage-sysop": "{{ns:project}}:ನಿರ್ವಾಹಕೆರ್",
        "right-read": "ಪುಟಕ್‍ಲೆನ್ ಓದುಲೆ",
        "right-edit": "ಪುಟೊನ್ ಸಂಪಾದನೆ ಮಲ್ಪುಲೆ",
+       "right-writeapi": "ಬರೆಯಿನ ಎಪಿಐದ ಬಳಕೆ",
        "right-delete": "ಪುಟೊಕುಲೆನ್ ಮಾಜಾಲೆ",
        "right-undelete": "ಪುಟೊನ್ ಮಾಜಾವಡೆ",
        "newuserlogpage": "ಸದಸ್ಯ ರಚನೆ ಲಾಗ್",
        "recentchanges-label-minor": "ಉಂದು ಎಲ್ಯ ಬದಲಾವಣೆ",
        "recentchanges-label-bot": "ಈ ಸಂಪಾದನೆನ್ ಒಂಜಿ ಬಾಟ್ ಮಲ್ತ್‍ದುಂಡು",
        "recentchanges-label-unpatrolled": "ಈ ಸಂಪಾದನೆನ್ ನನಲಾ ಪರೀಕ್ಷೆ ಮಲ್ತ್‍ದಿಜ್ಜಿ.",
+       "recentchanges-label-plusminus": "ಬೈಟ್ಸ್‌ದ ಲೆಕ್ಕೊಡು ಈ ಪಾಲೆದ ಗಾತ್ರೊ ಬದಲಾತ್ಂಡ್",
+       "recentchanges-legend-heading": "'''ಟಿಪ್ಪಣಿ:'''",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ಬೊಕ್ಕೊಲಾ ತೂಲೆ [[Special:NewPages|ಪೊಸ ಪಾಲೆದ ಪಟ್ಟಿ]])",
        "rclistfrom": "$3 $2 ರ್ದ್ ಶುರುವಾತಿನ ಪೊಸ ಬದಲಾವಣೆಲೆನ್ ತೊಜ್ಪಾವು",
        "rcshowhideminor": "$1 ಎಲ್ಯೆಲ್ಯ ಬದಲಾವಣೆಲು",
        "rcshowhideminor-show": "ತೋಜಾಲೆ",
        "newpageletter": "ಪೊ",
        "boteditletter": "ಬಾ",
        "rc_categories_any": "ಒವ್ವೇ",
-       "rc-change-size-new": "$1 {{ಬಹುವಚನೊ:$1|ಬೈಟ್|ಬೈಟ್‍ಲು}}ಬದಲಾವಣೆದ ಬುಕ್ಕೊ",
+       "rc-change-size-new": "$1 {{PLURAL:$1|ಬೈಟ್|ಬೈಟ್‍ಲು}}ಬದಲಾವಣೆದ ಬುಕ್ಕೊ",
        "newsectionsummary": "\n/* $1 */ಪೊಸ ವಿಭಾಗ",
        "rc-enhanced-expand": "ವಿವರೊಲೆನ್ ತೊಜ್ಪಾವು (ಜಾವ ಸ್ಕ್ರಿಪ್ಟ್ ಬೋಡಾಪುಂಡು)",
        "rc-enhanced-hide": "ವಿವರೊಲೆನ್ ದೆಂಗಾವು",
        "recentchangeslinked": "ಸಂಬಂಧ ಉಪ್ಪುನಂಚಿನ ಬದಲಾವಣೆಲು",
        "recentchangeslinked-feed": "ಸಂಬಂಧ ಉಪ್ಪುನಂಚಿನ ಬದಲಾವಣೆಲು",
-       "recentchangeslinked-toolbox": "ಸà²\82ಬà²\82ಧ ಉಪ್ಪುನಂಚಿನ ಬದಲಾವಣೆಲು",
+       "recentchangeslinked-toolbox": "ಸà²\82ಬà²\82ದà³\8a ಉಪ್ಪುನಂಚಿನ ಬದಲಾವಣೆಲು",
        "recentchangeslinked-title": "\"$1\" ಪುಟೊಟು ಆತಿನ ಬದಲಾವಣೆಲು",
        "recentchangeslinked-summary": "ಒಂಜಿ ನಿರ್ದಿಷ್ಟ ಪುಟೊರ್ದು (ಅತ್ತ್’ನ್ಡ ನಿರ್ದಿಷ್ಟ ವರ್ಗೊಗು ಸೇರ್ದಿನ ಪುಟೊಲೆರ್ದ್) ಸಂಪರ್ಕ ಉಪ್ಪುನ ಪುಟೊಲೆಡ್ ಇಂಚಿಪ ಮಲ್ತಿನಂಚಿನ ಬದಲಾವಣೆಲೆನ್ ತಿರ್ತ್ ಪಟ್ಟಿ ಮಲ್ಪೆರಾತ್’ನ್ಡ್.\n[[Special:Watchlist|ಇರೆನ ವೀಕ್ಷಣಾಪಟ್ಟಿಡ್]] ಉಪ್ಪುನ ಪುಟೊಲು '''ದಪ್ಪ ಅಕ್ಷರೊಡು''' ಉಂಡು.",
        "recentchangeslinked-page": "ಪುಟೊತ ಪುದರ್:",
        "linkstoimage": "ಈ ಫೈಲ್’ಗ್ ತಿರ್ತ್’ದ ಈ {{PLURAL:$1|ಪುಟ|$1 ಪುಟೊಲು}} ಲಿಂಕ್ ಕೊರ್ಪುಂಡು.",
        "nolinkstoimage": "ಈ ಫೈಲ್‍ಗ್ ಸಂಪರ್ಕ ಉಪ್ಪುನ ವಾ ಪುಟಲಾ ಇಜ್ಜಿ.",
        "sharedupload": "ಈ ಫೈಲ್’ನ್ ಮಸ್ತ್ ಜನ ಪಟ್ಟ್’ದುಲ್ಲೆರ್ ಅಂಚೆನೆ ಉಂದು ಮಸ್ತ್ ಪ್ರೊಜೆಕ್ಟ್’ಲೆಡ್ ಉಪಯೋಗಡುಪ್ಪು.",
+       "sharedupload-desc-here": "ಈ ಪಾಲೆ $1ಡ್ದ್ ಬೊಕ್ಕ ಬೇತೆ ಯೋಜನೆಡ್ದ್ ಗಳಸೊಲಿ.\nಈ ಪಾಲೆದ ವಿವರೊ [$2 ಪಾಲೆದ ವಿವರೊ] ತಿರ್ತ ಸಾಲ್‍ಡ್ ತೋಜಾದ್ಂಡ್",
        "upload-disallowed-here": "ಈರ್ ಈ ಕಡತನ್ ಕುಡ ಬರೆವರೆ ಸಾದ್ಯ ಇಜ್ಜಿ.",
        "filedelete-comment": "ಕಾರಣ",
        "filedelete-submit": "ಮಾಜಾಲೆ",
-       "randompage": "ಯಾದà³\83à²\9aà³\8dà²\9bಿà²\95 à²ªà³\81à²\9f",
+       "randompage": "ಯಾದà³\83à²\9aà³\8dà²\9bಿà²\95 à²ªà²¾à²²à³\86",
        "statistics": "ಅಂಕಿ ಅಂಶೊಲು",
        "statistics-header-pages": "ಪುಟೊತ ಅಂಕಿ ಅಂಶಲು",
        "nbytes": "$1 {{PLURAL:$1|ಬೈಟ್|ಬೈಟ್‍ಲು}}",
        "actioncomplete": "ಕಾರ್ಯ ಸಂಪೂರ್ಣ",
        "dellogpage": "ಡಿಲೀಟ್ ಮಲ್ತಿನ ಫೈಲ್’ಲೆದ ದಾಖಲೆ",
        "rollbacklink": "ಪಿರ ಪೋಲೆ",
+       "rollbacklinkcount": "ಪಿರ ದೆತೊನ್ಲೆ $1 {{PLURAL:$1|edit|ಸಂಪಾದನೆಲು}}",
        "protectlogpage": "ಸಂರಕ್ಷಣೆ ದಿನಚರಿ",
        "protectedarticle": "\"[[$1]]\" ಸಂರಕ್ಷಿತವಾದುಂಡು.",
        "modifiedarticleprotection": "\"[[$1]]\" ಪುಟೊತ ಸಂರಕ್ಷಣೆ ಮಟ್ಟ ಬದಲಾಂಡ್",
        "undeleteviewlink": "ತೂಲೆ",
        "namespace": "ನೇಮ್-ಸ್ಪೇಸ್:",
        "invert": "ಆಯ್ಕೆನ್ ತಿರ್ಗಾಲೆ",
-       "blanknamespace": "(ಮುಖ್ಯ)",
+       "tooltip-invert": "ಈ ಚೌಕೊದುಲಯಿಡ್ ಅಡೆಂಗಿನ ಪುದರ್‍ನ್ ಈ ಚೌಕೊಡೆ ಪರೀಕ್ಷಿಪುಲೆ(ಬೊಕ್ಕೊ ಒಟ್ಟುಗಿತ್ತಿನ ಪುದರ್‍ನ್‍ಲಾ ಪರೀಕ್ಷಿಪೊಲಿ)",
+       "namespace_association": "ಜತೆಟಿತ್ತಿನ ಪುದರ್",
+       "tooltip-namespace_association": "ಈ ಚೌಕೊನು ಚರ್ಚೆನ್ ಸೇರಾದ್ ಪರೀಕ್ಷಿಪುಲೆ ಅತ್ತ್ಂಡ ವಿಸಯೊಗು ಸರಿಯಾಯಿನ ಪುದರ್ದ ಜತೆಟ್ ಸೇರಾಲೆ",
+       "blanknamespace": "(ಮುಕ್ಯೊ)",
        "contributions": "{{$1ಸದಸ್ಯೆರ್ನ}} ಕಾಣಿಕೆಲು",
        "contributions-title": "$1 ಗ್ ಸದಸ್ಯೆರ್ನ ಕಾಣಿಕೆ",
        "mycontris": "ಎನ್ನ ಕಾಣಿಕೆಲು",
        "sp-contributions-search": "ಕಾಣಿಕೆಲೆನ್ ನಾಡ್ಲೆ",
        "sp-contributions-username": "ಐ.ಪಿ ವಿಳಾಸ ಅತ್ತ್’ನ್ಡ ಬಳಕೆದ ಪುದರ್:",
        "sp-contributions-submit": "ನಾಡ್",
-       "whatlinkshere": "à²\87ಡà³\86 à²µà²¾ à²ªà³\81à²\9fà³\8aಲà³\81 ಲಿಂಕ್ ಕೊರ್ಪುಂಡು",
+       "whatlinkshere": "à²\87ಡà³\86 à²µà²¾ à²ªà²¾à²²à³\86 ಲಿಂಕ್ ಕೊರ್ಪುಂಡು",
        "whatlinkshere-title": "\"$1\" ಪುಟೊಗು ಲಿಂಕ್ ಕೊರ್ಪುನ ಪುಟೊಲು",
        "whatlinkshere-page": "ಪುಟ:",
        "linkshere": "'''[[:$1]]'''ಗ್ ಈ ತಿರ್ತ್’ದ ಪುಟೊಲು ಲಿಂಕ್ ಕೊರ್ಪುಂಡು.",
        "tooltip-pt-logout": "ಲಾಗ್ ಔಟ್",
        "tooltip-pt-createaccount": "ನಿಕುಲು ಪೊಸ ಖಾತೆ ಸುರುಮಲ್ತ್‍ದ್ ಲಾಗಿನ್ ಆಪುನೈನ್ ಸ್ವಾಗತ ಮಲ್ಪುವೊ, ಆಂಡಲಾ ಉಂದು ಕಡ್ಡಾಯ ಅತ್ತ್.",
        "tooltip-ca-talk": "ಮಾಹಿತಿ ಪುಟೊತ ಬಗ್ಗೆ ಚರ್ಚೆ",
-       "tooltip-ca-edit": "à²\88 à²ªà³\81à²\9fà³\8aನà³\81 à²\88ರà³\8d à²¸à²\82ಪಾದನà³\86 à²®à²²à³\8dಪà³\8aಲಿ. à²¸à³\87ವà³\8d à²®à²²à³\8dಪà³\81ನ à²¦à³\81à²\82ಬà³\81 à²®à³\81ನà³\8dನà³\8bà²\9fದ à²\89ಪಯà³\8aà²\97 à²®à²¨à³\8dತà³\8aನà³\8dಲà³\86.",
+       "tooltip-ca-edit": "à²\88 à²ªà²¾à²²à³\86ನà³\8d à²¸à²\82ಪà³\8aಲಿಪà³\81ಲà³\86",
        "tooltip-ca-addsection": "ಪೊಸ ಸೆಶನ್ನ್ ಶರು ಮಲ್ಪುಲೆ",
        "tooltip-ca-viewsource": "ಉಂದೊಂಜಿ ಸಂರಕ್ಷಿತ ಪುಟ.\nಇಂದೆತ ಮೂಲೊನು ಈರ್ ತೂವೊಲಿ.",
-       "tooltip-ca-history": "à²\88 à²ªà³\81à²\9fà³\8aತ ಪರತ್ತ್ ಆವೃತ್ತಿಲು",
+       "tooltip-ca-history": "à²\88 à²ªà²¾à²²à³\86ದ ಪರತ್ತ್ ಆವೃತ್ತಿಲು",
        "tooltip-ca-protect": "ಈ ಪುಟೊನು ಸಂರಕ್ಷಣೆ ಮಲ್ಪುಲೆ",
        "tooltip-ca-delete": "ಈ ಪುಟೊನು ಡಿಲೀಟ್ ಮಲ್ಪುಲೆ",
        "tooltip-ca-move": "ಈ ಪೂಟೊನು ಮೂವ್(ಸ್ಥಳಾಂತರ) ಮಲ್ಪುಲೆ",
        "tooltip-search-fulltext": "ಈ ಪಠ್ಯ ಉಪ್ಪುನಂಚಿನ ಪುಟೊಲೆನ್ ನಾಡ್’ಲ",
        "tooltip-p-logo": "ಮುಖ್ಯ ಪುಟೊನು ತೂಲೆ",
        "tooltip-n-mainpage": "ಮುಖ್ಯ ಪುಟೊನು ತೂಲೆ",
-       "tooltip-n-mainpage-description": "ಮà³\81à²\96à³\8dಯ à²ªà³\81à²\9fà³\8aನà³\81 ತೂಲೆ",
+       "tooltip-n-mainpage-description": "ಮà³\81à²\95à³\8dಯà³\8a à²ªà²¾à²²à³\86ನà³\8d ತೂಲೆ",
        "tooltip-n-portal": "ಪ್ರೊಜೆಕ್ಟ್’ದ ಬಗ್ಗೆ, ಈರ್ ದಾದ ಮಲ್ಪೊಲಿ, ಓಲು ಇಂದೆತ ಬಗ್ಗೆ ತೆರಿಯೊನೊಲಿ",
-       "tooltip-n-currentevents": "ಪà³\8dರಸà²\95à³\8dತ à²\98à²\9fನà³\86ಲà³\8dದ à²¬à²\97à³\8dà²\97à³\86 ಹಿನ್ನೆಲೆ ಮಾಹಿತಿ ತೆರಿಯೊನ್ಲೆ",
+       "tooltip-n-currentevents": "à²\87ತà³\8dತà³\86ದ à²\98à²\9fನà³\86ಲà³\86 à²¬à²\97à³\86ತ ಹಿನ್ನೆಲೆ ಮಾಹಿತಿ ತೆರಿಯೊನ್ಲೆ",
        "tooltip-n-recentchanges": "ವಿಕಿಡ್ ದುಂಬುದ ಒಂತೆ ಸಮಯಡ್ ಆತಿನಂಚಿನ ಬದಲಾವಣೆಲ್ದ ಪಟ್ಟಿ",
-       "tooltip-n-randompage": "ಯಾದà³\83à²\9aà³\8dà²\9bಿà²\95 à²ªà³\81à²\9fವà³\8aಂಜೇನ್ ತೊಜ್ಪಾವ್",
-       "tooltip-n-help": "ತà³\86ರಿತà³\8aನà³\86ರà³\86 à²\9cಾà²\97",
-       "tooltip-t-whatlinkshere": "à²\87ಡà³\86 à²²à²¿à²\82à²\95à³\8d à²\95à³\8aರà³\8dಪà³\81ನà²\82à²\9aಿನ à²ªà³\82ರ à²µà²¿à²\95ಿ à²ªà³\81à²\9fà³\8aಲà³\8dದ ಪಟ್ಟಿ",
-       "tooltip-t-recentchangeslinked": "à²\88 à²ªà³\81à²\9fà³\8aರà³\8dದà³\81 à²¸à²\82ಪರà³\8dà²\95 à²\89ಪà³\8dಪà³\81ನà²\82à²\9aಿನ à²ªà³\81à²\9fà³\8aಲೆಡ್ ಇಂಚಿಪದ ಬದಲಾವಣೆಲು",
+       "tooltip-n-randompage": "ಯಾದà³\83à²\9aà³\8dà²\9bಿà²\95 à²ªà²¾à²²à³\86 à²\92ಂಜೇನ್ ತೊಜ್ಪಾವ್",
+       "tooltip-n-help": "à²\9cಾà²\97à³\86 à²¨à²¾à²¡à³\8dದà³\8d à²ªà²¤à³\8dತà³\86ರà³\86",
+       "tooltip-t-whatlinkshere": "à²\87ಡà³\86 à²²à²¿à²\82à²\95à³\8d à²\95à³\8aರà³\8dಪà³\81ನà²\82à²\9aಿನ à²ªà³\82ರ à²µà²¿à²\95ಿ à²ªà²¾à²²à³\86ಲà³\86ನ ಪಟ್ಟಿ",
+       "tooltip-t-recentchangeslinked": "à²\88 à²ªà²¾à²²à³\86ಡà³\8dದà³\8d à²¸à²\82ಪರà³\8dà²\95 à²\89ಪà³\8dಪà³\81ನà²\82à²\9aಿನ à²ªà²¾ಲೆಡ್ ಇಂಚಿಪದ ಬದಲಾವಣೆಲು",
        "tooltip-feed-rss": "ಈ ಪುಟೊಗು ಆರ್.ಎಸ್.ಎಸ್ ಫೀಡ್",
        "tooltip-feed-atom": "ಈ ಪುಟೊಗು Atom ಫೀಡ್",
        "tooltip-t-contributions": "ಈ ಸದಸ್ಯೆರ್ನ ಕಾಣಿಕೆಲ್ದ ಪಟ್ಟಿನ್ ತೊಜ್ಪಾವು",
        "tooltip-t-emailuser": "ಈ ಸದಸ್ಯೆರೆಗ್ ಇ-ಮೇಲ್ ಕಡಪುಡ್ಲೆ",
        "tooltip-t-upload": "ಫೈಲ್’ನ್ ಅಪ್ಲೋಡ್ ಮಲ್ಪುಲೆ",
-       "tooltip-t-specialpages": "ಪà³\82ರ à²µà²¿à²·à³\87ಶ à²ªà³\81à²\9fà³\8aಲà³\8dದ ಪಟ್ಟಿ",
-       "tooltip-t-print": "à²\88 à²ªà³\81à²\9fà³\8aತ ಪ್ರಿಂಟ್ ಆವೃತ್ತಿ",
-       "tooltip-t-permalink": "ಪà³\81à²\9fà³\8aತ à²\88 à²\86ವà³\83ತà³\8dತಿà²\97à³\8d à²¶à²¾à²¶್ವತ ಲಿಂಕ್",
-       "tooltip-ca-nstab-main": "ಮಾಹಿತಿ à²ªà³\81à²\9fà³\8aನà³\81 ತೂಲೆ",
+       "tooltip-t-specialpages": "ಪà³\82ರ à²ªà²¾à²²à³\86ಲà³\86ನ à²µà²¿à²¸à³\87ಸà³\8a ಪಟ್ಟಿ",
+       "tooltip-t-print": "à²\88 à²ªà²¾à²²à³\86ದ ಪ್ರಿಂಟ್ ಆವೃತ್ತಿ",
+       "tooltip-t-permalink": "ಪಾಲà³\86ದ à²\88 à²\86ವà³\83ತà³\8dತಿà²\97à³\8d à²¸à²¾à²¸್ವತ ಲಿಂಕ್",
+       "tooltip-ca-nstab-main": "ಮಾಹಿತಿ à²ªà²¾à²²à³\86ನà³\8d ತೂಲೆ",
        "tooltip-ca-nstab-user": "ಸದಸ್ಯೆರ್ನ ಪುಟೊನು ತೂಲೆ",
-       "tooltip-ca-nstab-special": "à²\89à²\82ದà³\8aà²\82à²\9cಿ à²µà²¿à²¶à³\87ಷ à²ªà³\81à²\9f, à²\87à²\82ದà³\86ನà³\8d à²\88ರà³\8d à²\8eಡಿà²\9fà³\8d à²®à²²à³\8dಪೆರೆ ಆಪುಜಿ",
+       "tooltip-ca-nstab-special": "à²\89à²\82ದà³\8aà²\82à²\9cಿ à²\87ಸà³\87ಸ à²ªà²¾à²²à³\86, à²\87à²\82ದà³\86ನà³\8d à²\88ರà³\8d à²¸à²\82ಪà³\8aಲಿಪೆರೆ ಆಪುಜಿ",
        "tooltip-ca-nstab-project": "ಪ್ರೊಜೆಕ್ಟ್ ಪುಟೊನು ತೂಲೆ",
        "tooltip-ca-nstab-image": "ಫೈಲ್’ದ ಪುಟೊನು ತೂಲೆ",
        "tooltip-ca-nstab-template": "ಟೆಂಪ್ಲೇಟ್’ನ್ ತೂಲೆ",
        "tooltip-rollback": "\"Rollback\", ಈ ಪುಟದ ಕರಿನ ಬದಾಲವಣೆಗ್ ಒ೦ಜಿ ಕ್ಲಿಕ್ ಡ್ ಕೊನೊಪು೦ಡು",
        "tooltip-undo": "\"Undo\" ಈ ಬದಲಾವಣೆನ್ ದೆತೊನುಜಿ ಬುಕ ಪ್ರಿವ್ಯೂ ಮೋಡ್ ಡ್ ಬದಲಾವಣೆ ಮಲ್ಪೆರ್ ಕೊನೊಪು೦ಡು. ಅ೦ಚೆನೆ ಸಮ್ಮರಿ ಡ್ ಬದಲಾವಣೆ ಗ್ ಕಾರಣ ಕೊರ್ರ್‍ಎ ಆಪು೦ಡು.",
        "tooltip-summary": "ಒಂಜಿ ಎಲ್ಯ ಸಾರಾಂಶ ಕೊರ್ಲೆ",
-       "pageinfo-toolboxlink": "ಪುಟೊತ ಮಾಹಿತಿ",
+       "simpleantispam-label": "ಯಾಂಟಿ-ಸ್ಪಾಮ್ ಚೆಕ್.\nಮುಲ್ಪ <strong>ದಿಂಜಾವೊಡ್ಚಿ</strong>",
+       "pageinfo-toolboxlink": "ಪಾಲೆದ ಮಾಹಿತಿ",
        "previousdiff": "← ದುಂಬುದ ಸಂಪಾದನೆ",
        "nextdiff": "ಪೊಸ ಎಡಿಟ್ →",
        "file-info-size": "$1 × $2 ಪಿಕ್ಸೆಲ್, ಫೈಲ್’ದ ಗಾತ್ರ: $3, MIME ಪ್ರಕಾರ: $4",
        "file-nohires": "ಇಂದೆರ್ದ್ ಜಾಸ್ತಿ ವಿವರವಾಯಿನ ನೋಟ ಇಜ್ಜಿ.",
        "svg-long-desc": "ಎಸ್.ವಿ.ಜಿ ಫೈಲ್, ಸುಮಾರಾದ್ $1 × $2 ಪಿಕ್ಸೆಲ್, ಫೈಲ್’ದ ಗಾತ್ರ: $3",
-       "show-big-image": "ಮೂಲ ಕಡತ",
+       "show-big-image": "ನಿಜಾವಾಯಿನ ಕಡತ",
+       "show-big-image-preview": "ಪಿರವುದ ಪಾಲೆದ ಗಾತ್ರೊ: $1.",
+       "show-big-image-other": "ಬೇತೆ{{PLURAL:$2|resolution|ನಿರ್ನಯೊಲು}}: $1.",
        "show-big-image-size": "$1 × $2 ಪಿಕ್ಸೆಲ್‌ಸ್",
        "bad_image_list": "ವ್ಯವಸ್ಥೆದ ಆಕಾರ ಈ ರೀತಿ ಉಂಡು:\n\nಪಟ್ಟಿಡುಪ್ಪುನಂಚಿನ ದಾಖಲೆಲೆನ್ (* ರ್ದ್ ಶುರು ಆಪುನ ಸಾಲ್’ಲು) ಮಾತ್ರ ಪರಿಗಣನೆಗ್ ದೆತೊನೆರಾಪುಂಡು.\nಪ್ರತಿ ಸಾಲ್’ದ ಶುರುತ ಲಿಂಕ್ ಒಂಜಿ ದೋಷ ಉಪ್ಪುನಂಚಿನ ಫೈಲ್’ಗ್ ಲಿಂಕಾದುಪ್ಪೊಡು.\nಅವ್ವೇ ಸಾಲ್’ದ ಶುರುತ ಪೂರಾ ಲಿಂಕ್’ಲೆನ್ ಪರಿಗನೆರ್ದ್ ದೆಪ್ಪೆರಾಪುಂಡು, ಪಂಡ ಓವು ಪುಟೊಲೆಡ್ ಫೈಲ್’ದ ಬಗ್ಗೆ ಬರ್ಪುಂಡೋ ಔಲು.",
        "metadata": "ಮೇಲ್ದರ್ಜೆ ಮಾಹಿತಿ",
        "metadata-expand": "ವಿಸ್ತಾರವಾಯಿನ ವಿವರೊಲೆನ್ ತೊಜ್ಪಾವು",
        "metadata-collapse": "ವಿಸ್ತಾರವಾಯಿನ ವಿವರೊಲೆನ್ ದೆಂಗಾವು",
        "metadata-fields": "ಈ ಸಂದೇಶೊಡು ಪಟ್ಟಿ ಮಲ್ತಿನಂಚಿನ EXIF ಮಿತ್ತ ದರ್ಜೆದ ಮಾಹಿತಿನ್ ಚಿತ್ರ ಪುಟೊಕು ಸೇರ್ಪಾಯೆರೆ ಆವೊಂದುಂಡು. ಪುಟೊಟು ಮಿತ್ತ ದರ್ಜೆ ಮಾಹಿತಿದ ಪಟ್ಟಿನ್ ದೆಪ್ಪುನಗ ಉಂದು ತೋಜುಂಡು.\nಒರಿದನವು ಮೂಲಸ್ಥಿತಿಟ್ ಅಗೋಚರವಾದುಪ್ಪುಂಡು.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude \n* gpsaltitude",
+       "exif-orientation": "ದಿಕ್ಕ್ ದಿಸೆ",
+       "exif-xresolution": "ಅಡ್ಡಗಲದ ರೆಜ಼ಲ್ಯೂಶನ್",
+       "exif-yresolution": "ಉದ್ದೊದ ರೆಜ಼ಲ್ಯೂಶನ್",
        "exif-datetime": "ಕಡೊತೊನು ಬದಲಾವಣೆ ಮಲ್ತ್‍ನ ದಿನಾಂಕೊ ಬೊಕ್ಕ ಸಮಯೊ",
        "exif-make": "ಕ್ಯಾಮೆರಾದ ತಯಾರೆಕೆರ್",
        "exif-model": "ಕ್ಯಾಮೆರಾ ಮಾದರಿ",
        "exif-software": "ಉಪಯೋಗ ಮಲ್ತಿನ ತಂತ್ರಾಂಶ",
        "exif-exifversion": "Exif ಆವೃತ್ತಿ",
+       "exif-colorspace": "ಬಣ್ಣದ ಜಾಗೆ",
        "exif-datetimeoriginal": "ಮಾಹಿತಿ ಸೃಷ್ಟಿಯಾಯಿನ ದಿನಾಂಕೊ ಬೊಕ್ಕ ಸಮಯ",
        "exif-datetimedigitized": "ಗಣಕೀಕರಣದ ದಿನಾಂಕೊ ಬೊಕ್ಕ ಸಮಯೊ",
        "exif-orientation-1": "ಸಾಧಾರಣ",
        "monthsall": "ಪೂರಾ",
        "watchlisttools-view": "ಪ್ರಸ್ತುತ ಬದಲಾವಣೆಲ್ ತೋಜಾಲೆ",
        "watchlisttools-edit": "ವೀಕ್ಷಣಾಪಟ್ಟಿನ್ ತೂಲೆ ಬೊಕ್ಕ ಎಡಿಟ್ ಮಲ್ಪುಲೆ",
-       "specialpages": "ವಿಷೇಶ ಪುಟೊಲು",
-       "tag-list-wrapper": "([[ವಿಸೇಸೊ:ಟ್ಯಾಗುಲು|{{ಬಹುವಚನೊ:$1|ಟ್ಯಾಗ್|ಟ್ಯಾಗುಲು}}]]:$2)",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|ಪಾತೆರ್ಲೆ]])",
+       "specialpages": "ವಿಸೇಸೊ ಪಾಲೆಲು",
+       "tag-filter": "[[Special:Tags|ಟ್ಯಾಗ್]] ಅರಿಪು:",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Tag|ಟ್ಯಾಗುಲು}}]]: $2)",
        "logentry-delete-delete": "$1 {{GENDER:$2|ಮಾಜಾದ್‍ಂಡ್}} ಪುಟ $3",
+       "logentry-move-move": "$1 {{GENDER:$2|ಜಾರಲೆ}} ಪಾಲೆ $3 ಡ್ದ್ $4",
        "logentry-newusers-create": "ಬಳಕೆದಾರ ಖಾತೆ $1 ನ್ನು {{GENDER:$2|ಸೃಷ್ಟಿ ಮಲ್ತ್‍ದುಂಡು}}",
        "logentry-upload-upload": "$1 {{GENDER:$2|ಅಪ್ಲೋಡ್ ಮಲ್ತ್‍ದೆರ್}} $3",
        "searchsuggest-search": "ನಾಡ್‍ಲೆ"
index 37df561..c2f5a22 100644 (file)
        "versionrequiredtext": "Bu sahifada ishlash uchun MediaWikining $1-versiyasi talab etiladi.\n[[Special:Version|Dasturiy taʼminot haqida axborot]]ni koʻring.",
        "ok": "OK",
        "retrievedfrom": " \"$1\" dan olindi",
-       "youhavenewmessages": "Sizga $1 keldi ($2).",
+       "youhavenewmessages": "{{PLURAL:$3|Sizga}} $1 keldi ($2).",
        "youhavenewmessagesfromusers": "Siz {{PLURAL:$3|boshqa foydalanuvchidan|$3 ta foydalanuvchidan}} $1 oldingiz ($2).",
        "youhavenewmessagesmanyusers": "Siz ko'p foydalanuvchilardan $1 oldingiz ($2).",
        "newmessageslinkplural": "{{PLURAL:$1|yangi xabar|999=yangi xabarlar}}",
        "newmessagesdifflinkplural": "oxirgi {{PLURAL:$1|oʻzgarish|oʻzgarishlar}}",
-       "youhavenewmessagesmulti": "Siz $1ga yangi xat oldingiz",
+       "youhavenewmessagesmulti": "Sizga $1da yangi xat keldi",
        "editsection": "tahrirlash",
        "editold": "tahrirlash",
        "viewsourceold": "manbasini koʻrish",
        "protectedpagetext": "Bu sahifa tahrirlash va boshqa oʻzgarishlar kiritishdan himoyalangan.",
        "viewsourcetext": "Siz bu sahifaning manbasini koʻrishingiz va uni nusxasini olishingiz mumkin:",
        "protectedinterface": "Ushbu sahifada dasturiy taʼminot interfeysi xabari mavjud. Bezoriliklardan saqlash uchun uni oʻzgartirish taʼqiqlangan.\nUshbu xabar tarjimasini qoʻshish yoki oʻzgartirish uchun, iltimos, MediaWikining [//translatewiki.net/ translatewiki.net] mahalliylashtirish saytidan foydalaning.",
-       "editinginterface": "'''Diqqat:''' Siz dasturiy taʼminot interfeysi matni mavjud boʻlgan sahifani tahrirlamoqdasiz.\nUning oʻzgartirilishi ushbu vikidagi boshqa foydalanuvchilar uchun ham interfeys oʻzgarishiga olib keladi.\nUshbu xabar tarjimasini qoʻshish yoki oʻzgartirish uchun, iltimos, MediaWikining [//translatewiki.net/ translatewiki.net] mahalliylashtirish saytidan foydalaning.",
+       "editinginterface": "<strong>Eʼtibor bering:</strong> Siz interfeys matnini aks ettiruvchi sahifani tahrirlamoqdasiz.\nUning oʻzgartirilishi barcha ushbu vikidan foydalanuvchilar uchun ham interfeys oʻzgarishiga olib keladi.",
        "namespaceprotected": "Sizda '''$1''' nomfazosi sahifalarini tahrirlash huquqi yoʻq",
        "customcssprotected": "Sizda uchbu CSS sahifani tahrirlash huquqi yoʻq, chunki bu yerda boshqa foydalanuvchining shaxsiy moslamalari saqlanadi.",
        "customjsprotected": "Sizda uchbu JavaScript sahifani tahrirlash huquqi yoʻq, chunki bu yerda boshqa foydalanuvchining shaxsiy moslamalari saqlanadi.",
        "prefs-displayrc": "Tasvirlash moslamalari",
        "prefs-displaywatchlist": "Tasvirlash moslamalari",
        "prefs-diffs": "Versiyalar farqi",
-       "userrights": "Foydalanuvchi huquqlarini oʻzgartirish",
+       "userrights": "Huquqlarini oʻzgartirish",
        "userrights-lookup-user": "Foydalanuvchini tanlash",
        "userrights-user-editname": "Foydalanuvchi nomi:",
        "editusergroup": "Shu foydalanuvchi huquqlarini oʻzgartirish",
        "listgrouprights-rights": "Huquqlar",
        "listgrouprights-helppage": "Help:Guruhlar huquqlari",
        "listgrouprights-members": "(a’zolar ro‘yxati)",
-       "emailuser": "Foydalanuvchiga maktub",
+       "emailuser": "Maktub yuborish",
        "emailuser-title-target": "Ushbu {{GENDER:$1|foydalanuvchi}}ga maktub joʻnatish",
        "emailuser-title-notarget": "Foydalanuvchiga elektron maktub yozish",
        "defemailsubject": "{{SITENAME}} — $1 tomonidan maktub",
        "usermaildisabled": "Foydalanuvchi elektron pochtasi o‘chirilgan",
        "noemailtitle": "Elektron pochta manzili mavjud emas",
        "noemailtext": "Bu foydalanuvchi e-mail manzil koʻrsatgani yoʻq.",
-       "emailtarget": "Oluvchi ishtirokchining ismini kiriting",
+       "emailtarget": "Kimga xat joʻnatmoqchisiz?",
        "emailusername": "Foydalanuvchi nomi:",
-       "emailusernamesubmit": "Jo'natish",
-       "email-legend": "Boshqa {{SITENAME}} ishtirokchisiga xat jo'natish",
+       "emailusernamesubmit": "Joʻnatish",
+       "email-legend": "{{SITENAME}} loyihasining boshqa bir foydalanuvchisiga xat joʻnatish",
        "emailfrom": "Kimdan:",
        "emailto": "Kimga:",
        "emailsubject": "Sarlavha:",
        "emailsend": "Joʻnatish",
        "emailccme": "Maktub nusxasi mening elektron pochtamga joʻnatilsin",
        "emailccsubject": "$1ga maktubingizning nusxasi: $2",
-       "emailsent": "Xat jo'natildi",
+       "emailsent": "Xat joʻnatildi",
        "emailsenttext": "Sizning elektron maktubingiz jo'natildi.",
        "usermessage-summary": "Tizimli xabar qoldirish.",
        "usermessage-editor": "Tizimli etkazish",
        "tooltip-feed-rss": "Bu sahifa uchun RSS ta'minot",
        "tooltip-feed-atom": "Bu sahifa uchun Atom ta'minot",
        "tooltip-t-contributions": "Ushbu foydalanuvchi qoʻshgan hissasini koʻrish",
-       "tooltip-t-emailuser": "Ushbu foydalanuvchiga xat jo‘natish",
+       "tooltip-t-emailuser": "Ushbu foydalanuvchiga elektron maktub yozish",
        "tooltip-t-upload": "Rasmlar yoki media fayllar yuklash",
        "tooltip-t-specialpages": "Maxsus sahifalar ro‘yxati",
        "tooltip-t-print": "Ushbu sahifaning bosma uchun versiyasi",
index d5ef92c..25e2a0b 100644 (file)
        "log-fulllog": "Kitaa an bug-os nga taramdan",
        "edit-conflict": "Diri pagkakauroyon han pagliwat.",
        "edit-no-change": "Ginpabay-an an im pagliwat, mahitungod nga waray pagbalyo nga nabuhat ha nakasurat.",
+       "postedit-confirmation-created": "Nahimo an pakli.",
        "postedit-confirmation-saved": "Natipig an imo ginliwat.",
        "edit-already-exists": "Diri nakakahimo hin bag-o nga pakli.\nAada na ito.",
        "defaultmessagetext": "Aada-nga-daan nga teksto han mensahe",
        "upload-file-error": "Sayop ha sulod",
        "upload-misc-error": "Waray kasasabti nga sayop hin pagkarga-paigbaw",
        "upload-http-error": "Mayda nahitabo nga sayop hin HTTP: $1",
+       "upload-dialog-button-cancel": "Pasagda",
+       "upload-dialog-button-done": "Tima na",
+       "upload-dialog-button-save": "Igtipig",
+       "upload-dialog-button-upload": "Upload",
+       "upload-form-label-select-file": "Pagpili hin file",
+       "upload-form-label-infoform-title": "Mga detalye",
+       "upload-form-label-infoform-name": "Ngaran",
+       "upload-form-label-usage-title": "Paggamit",
+       "upload-form-label-usage-filename": "Ngaran han file",
+       "foreign-structured-upload-form-label-own-work": "Buhat ko ini",
+       "foreign-structured-upload-form-label-infoform-categories": "Mga kategorya",
+       "foreign-structured-upload-form-label-infoform-date": "Petsa",
        "backend-fail-notexists": "Waray ngada an paypay nga $1.",
        "backend-fail-delete": "Diri nakakapara han paypay nga \"$1\".",
        "backend-fail-alreadyexists": "May-ada na paypay nga \"$1\".",
index 80de25e..288734a 100644 (file)
        "editfont-monospace": "Dayoob mbind genn dig-digal",
        "editfont-sansserif": "Dayoob mbind bu amul-dig",
        "editfont-serif": "Dayoob mbind bu am-dig",
-       "sunday": "dibéer",
-       "monday": "altine",
-       "tuesday": "talaata",
+       "sunday": "Dibéer",
+       "monday": "Altine",
+       "tuesday": "Talaata",
        "wednesday": "àllarba",
        "thursday": "alxamis",
-       "friday": "àjjuma",
-       "saturday": "gaawu",
+       "friday": "Ã\80jjuma",
+       "saturday": "Gaawu",
        "sun": "dib",
        "mon": "alt",
-       "tue": "tal",
+       "tue": "Tal",
        "wed": "àll",
        "thu": "alx",
        "fri": "àjj",
        "sat": "gaa",
-       "january": "Semwiyee",
+       "january": "Samwiyee",
        "february": "Fewriyee",
        "march": "Maars",
        "april": "Awril",
@@ -79,7 +79,7 @@
        "september": "Sattumbar",
        "october": "Oktoobar",
        "november": "Nowembar",
-       "december": "Deesàmbar",
+       "december": "Samwiye",
        "january-gen": "Samwie",
        "february-gen": "Fewirie",
        "march-gen": "Maars",
        "actions": "Jëf",
        "namespaces": "Barabu tur",
        "variants": "Wuute",
-       "navigation-heading": "Njëlul joowiin",
+       "navigation-heading": "Njëlu joowiin",
        "errorpagetitle": "Njuumte",
        "returnto": "Dellu ci wii xët $1.",
        "tagline": "Jóge  {{SITENAME}}.",
        "unprotectthispage": "Aaradil wii xët",
        "newpage": "Xët wu bees",
        "talkpage": "Xëtu waxtaanuwaay",
-       "talkpagelinktext": "Diisoo",
+       "talkpagelinktext": "Waxtaan",
        "specialpage": "Xëtu jagleel",
        "personaltools": "Samay jumtukaay",
        "articlepage": "Gis jukki bi",
        "talk": "Waxtaan",
-       "views": "Xool yo",
-       "toolbox": "Boyotu jumtukaay yi",
+       "views": "Wone yi",
+       "toolbox": "Boyotu jumtukaay",
        "userpage": "Xëtu jëfandikukat",
        "projectpage": "Wone xëtu sémb wi",
        "imagepage": "Wone xëtu dencukaay bi",
        "redirectedfrom": "(Yoonalaat gu jóge $1)",
        "redirectpagesub": "Xëtu yoonalaat",
        "redirectto": "Jëmalewaat:",
-       "lastmodifiedat": "Coppite bu mujj bu xët wii $1 ci $2.<br />",
+       "lastmodifiedat": "Coppite gu mujj gu xët wii $1 ci $2.<br />",
        "viewcount": "Xët wii nemmeeku nañ ko {{PLURAL:$1|$1 yoon|$1 yoon}}.",
        "protectedpage": "Xët wees aar",
-       "jumpto": "Dem :",
+       "jumpto": "Dem:",
        "jumptonavigation": "Joowiin",
        "jumptosearch": "Seet",
        "view-pool-error": "jéggalu, joxekaay yi dañoo xat nii-nii.\nJëfandikukat yiy jéem a ubbi xët wii dañoo bari.\nTaaxiirlul ba ci kanam nga jéemaat.\n\n$1",
        "aboutpage": "Project:Ci mbiri",
        "copyright": "Ëmbit laa ngi  jàppandi ci $1.",
        "copyrightpage": "{{ns:project}}:Copyright",
-       "currentevents": "Luy xew",
-       "currentevents-url": "Project:Luy xew",
+       "currentevents": "Liy xew",
+       "currentevents-url": "Project:Liy xew",
        "disclaimers": "Ay aartu",
        "disclaimerpage": "Project:Aartu yu daj",
        "edithelp": "Ndimbal",
        "editlink": "soppi",
        "viewsourcelink": "xool gongikuwaayam",
        "editsectionhint": "Soppi bii xaaj : $1",
-       "toc": "Tëraliin",
+       "toc": "Ëmbiit",
        "showtoc": "Wone",
        "hidetoc": "Nëbb",
        "thisisdeleted": "Da ngaa bëgg a wone walla delloowaat $1 ?",
        "createacct-emailoptional": "Màkkaanu m-bataaxal (mu-neex-la)",
        "createacct-email-ph": "Duggalal sa màkkaanu m-bataaxal",
        "createaccountmail": "Jaare ko ci m-bataaxal",
-       "createacct-captcha": "Caytug kaaraange",
-       "createacct-imgcaptcha-ph": "Duggalal mbind  miy toftal mi ngay gis",
        "createacct-submit": "Sos sa sàq",
        "createacct-benefit-heading": "{{SITENAME}} ñu mel ni yaw a koy toppatoo.",
        "createacct-benefit-body1": "{{PLURAL:$1|Coppite}}",
        "templatesused": "{{PLURAL:$1| Royuwaay bi| Royuwaay yi}} nekk ci wii xët :",
        "templatesusedpreview": "{{PLURAL:$1| Royuwaay  bi|Royuwaay yi}} nekk ci gii wonendi :",
        "templatesusedsection": "Royuwaay yi ne ci bii xaaj:",
-       "template-protected": "(aar)",
+       "template-protected": "(aarees)",
        "template-semiprotected": "(aar-diggu)",
        "hiddencategories": "{{PLURAL:$1|wàll bu nëbbu bu|wàll yu nëbbu yu }} xët wii bokk :",
        "nocreatetext": "Jëfandikukat yi bindu rekk a man a sosi xët ci {{SITENAME}}. Man nga dellu ginnaaw walla soppi aw xët wu am ba noppi, [[Special:UserLogin|duggu walla sos am sàq]].",
        "rcshowhidemine-hide": "Nëbb",
        "rclinks": "Wone $1 coppite yi mujj ci $2  fan yi mujj <br />$3.",
        "diff": "wuute",
-       "hist": "Jaar",
+       "hist": "jaar",
        "hide": "Nëbb",
        "show": "Wone",
        "minoreditletter": "m",
        "filehist-current": "teew",
        "filehist-datetime": "Taariix ak Waxtu",
        "filehist-thumb": "Tuutal",
-       "filehist-thumbtext": "Tuutal gu  sumb bu $1",
+       "filehist-thumbtext": "Tuutal gu sumb bu $1",
        "filehist-user": "Jëfandikukat",
        "filehist-dimensions": "Dayoo",
        "filehist-filesize": "Dayoo ŋara wi",
        "tooltip-pt-preferences": "Say tànneef",
        "tooltip-pt-watchlist": "Limu xët yi ngay topp",
        "tooltip-pt-mycontris": "Limu say cëru",
-       "tooltip-pt-login": "Woo nan la ngir nga xammeku, waaye doonul lu manuta ñakk.",
+       "tooltip-pt-login": "Woo nan la ngir nga xammeku, waaye doonul lu manul-ñàkk.",
        "tooltip-pt-logout": "Génn",
        "tooltip-pt-createaccount": "Dees na la digal nga bindu te dugg, donte doonul lu manul-ñàkk",
        "tooltip-ca-talk": "Waxtaan yi ñeel xët wii",
        "tooltip-ca-move": "Tuddewaatal xët wii",
        "tooltip-ca-watch": "Yokk xët wii ci sa limu toppte",
        "tooltip-ca-unwatch": "Jële xët wii ci sa limu toppte",
-       "tooltip-search": "Seet ci biir {{SITENAME}}",
+       "tooltip-search": "Seet ci {{SITENAME}}",
        "tooltip-search-go": "Dem ci xët wi tudd ni nga wax, su dee am na.",
-       "tooltip-search-fulltext": "Seet xët yi ëmb kàddu gi",
+       "tooltip-search-fulltext": "Seet mbind mi ci biir xët yi",
        "tooltip-p-logo": "Xët wu njëkk",
-       "tooltip-n-mainpage": "Nemmeeku xëtu njëlbéen",
-       "tooltip-n-mainpage-description": "Nemmeku xët wu njëkk wi",
-       "tooltip-n-portal": "Ngir xam dara ci mbiri sémb bi, noo ci mana jàppe",
+       "tooltip-n-mainpage": "Nemmeeku xët wu njëkk wi",
+       "tooltip-n-mainpage-description": "Nemmeeku xët wu njëkk wi",
+       "tooltip-n-portal": "Ngir xam dara ci mbiri sémb bi, noo ci man a jàppe",
        "tooltip-n-currentevents": "Xibaar ci xew-xew yu teew yi",
        "tooltip-n-recentchanges": "Limu coppite yi mujj ci wiki bi",
        "tooltip-n-randompage": "Wone aw xët ci mbetteel",
-       "tooltip-n-help": "Xëtu ndimbal wi",
-       "tooltip-t-whatlinkshere": "limu xët yi ci wiki bi yi lëkkalook wii",
+       "tooltip-n-help": "Xëtu ndimbal",
+       "tooltip-t-whatlinkshere": "Limu xët yi ci wiki bi te lëkkalook wii",
        "tooltip-t-recentchangeslinked": "Limu coppite yu mujj yu xët yi lëkkalook wii",
        "tooltip-feed-rss": "Walug RSS ngir wii xët",
        "tooltip-feed-atom": "Walug Atom ngir wii xët",
        "tooltip-t-emailuser": "Yónne ab m-bataaxal bii jëfandikukat",
        "tooltip-t-upload": "Yeb ay dencukaay",
        "tooltip-t-specialpages": "Limu xëti jagleel yépp",
-       "tooltip-t-print": "Sumb bu móolu bu xët wii",
+       "tooltip-t-print": "Sumb bu móolu bu wii xët",
        "tooltip-t-permalink": "Lëkkalekaay bu sax buy jëme ci bii sumb bu xët wi",
        "tooltip-ca-nstab-main": "Xool jukki bi",
        "tooltip-ca-nstab-user": "Xool xëtu jëfandikukat wi",
        "tooltip-ca-nstab-media": "Xool xëtu dencukaay wi",
-       "tooltip-ca-nstab-special": "Lii aw xëtu jagleel la, kenn manu kaa soppi.",
+       "tooltip-ca-nstab-special": "Lii aw xëtu jagleel la, kenn manu koo soppi.",
        "tooltip-ca-nstab-project": "Xool xëtu sémb wi",
        "tooltip-ca-nstab-image": "Xool xëtu dencukaay wi",
        "tooltip-ca-nstab-mediawiki": "Xool bataaxalu noste bi",
        "tooltip-watch": "Yokk xët wii ci sa limu toppte",
        "tooltip-recreate": "Sosaat xët wi donte dañ kaa faroon",
        "tooltip-upload": "Door yeb gi",
-       "tooltip-rollback": "\"Delloowaat\" dafay neenal coppitey cërukat bi mujj ci xët wii ci benn cuq.",
+       "tooltip-rollback": "\"Delloowaat\" dafay neenal coppitey cërukat bi mujj ci xët wii ci benn kilig.",
        "tooltip-undo": "\"Neenal\" dafay far coppite yi te ubbi palanteeru coppite bi ci anamug wonendi.\nDafay tax nga man a bind ngirte li ci boyotu tënk bi.",
        "tooltip-summary": "Def ci ab tënk",
        "common.css": "/* CSS yiñ def fii dañuy am ay njeexit ci col yépp  */",
index c7704ae..fbae18a 100644 (file)
@@ -23,6 +23,7 @@
        "tog-hideminor": "באַהאַלטן מינערדיקע רעדאַקטירונגען אין לעצטע ענדערונגען",
        "tog-hidepatrolled": "באַהאַלטן פאַטראלירטע רעדאַקטירונגען אין לעצטע ענדערונגען",
        "tog-newpageshidepatrolled": "באַהאַלטן פאַטראלירטע בלעטער פון דער ליסטע פון נײַע בלעטער",
+       "tog-hidecategorization": "באהאלטן קאעגאריזירן בלעטער",
        "tog-extendwatchlist": "פארברייטערן די אויפפאסן ליסטע צו צייגן אלע פאסנדע ענדערונגען (אנדערשט: בלויז די לעצטע ענדערונג פון יעדן בלאט)",
        "tog-usenewrc": "גרופירן ענדערונגען לויטן בלאט אין \"לעצטע ענדערונגען\" און אויפֿפאסן ליסטע",
        "tog-numberheadings": "נומערירן קעפּלעך אויטאָמאַטיש",
@@ -52,6 +53,7 @@
        "tog-watchlisthideliu": "באהאלטן רעדאקטירונגען פון איינלאגירטע באניצערס פון דער אויפֿפאסונג ליסטע",
        "tog-watchlisthideanons": "באהאלטן רעדאקטירונגען פון אנאנימע באניצערס פון דער אויפֿפאסונג ליסטע",
        "tog-watchlisthidepatrolled": "באַהאַלטן פאַטראלירטע רעדאַקטירונגען פֿון דער אויפֿפאַסונג ליסטע",
+       "tog-watchlisthidecategorization": "באהאלטן קאעגאריזירן בלעטער",
        "tog-ccmeonemails": "שיק מיר קאפיעס פון בליצבריוו וואס איך שיק צו אנדערע באַניצער",
        "tog-diffonly": "ווייז נישט אינהאלט אונטער די דיפערענץ",
        "tog-showhiddencats": "ווײַזן באהאלטענע קאטעגאריעס",
        "rcshowhidemine": "$1 מײַנע רעדאַקטירוננגען",
        "rcshowhidemine-show": "ווײַזן",
        "rcshowhidemine-hide": "באַהאַלטן",
+       "rcshowhidecategorization": "$1 בלאט קאטעגאריזירונג",
+       "rcshowhidecategorization-show": "ווײַזן",
+       "rcshowhidecategorization-hide": "באַהאַלטן",
        "rclinks": "װײַזן די לעצטע $1 ענדערונגען אין די לעצטע $2 טעג.<br />$3",
        "diff": "אונטערשייד",
        "hist": "היסטאריע",
index b64ff91..7b41fed 100644 (file)
        "showingresultsinrange": "下面显示区间#<strong>$2</strong>至#<strong>$3</strong>的<strong>$1</strong>条结果。",
        "search-showingresults": "{{PLURAL:$4|<strong>$3</strong>条结果中的<strong>$1</strong>条|<strong>$3</strong>条结果中的<strong>$1~$2</strong>条}}",
        "search-nonefound": "找不到和查询相匹配的结果。",
+       "search-nonefound-thiswiki": "在此网站找不到匹配查询的结果。",
        "powersearch-legend": "高级搜索",
        "powersearch-ns": "搜索名字空间:",
        "powersearch-togglelabel": "选择:",
index 22f62fe..e65b09e 100644 (file)
--- a/load.php
+++ b/load.php
@@ -1,6 +1,6 @@
 <?php
 /**
- * This file is the entry point for the resource loader.
+ * This file is the entry point for ResourceLoader.
  *
  * 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
index 9fa05d2..b0bbdd0 100644 (file)
@@ -1,4 +1,4 @@
--- Table for storing JSON message blobs for the resource loader
+-- Table for storing JSON message blobs for ResourceLoader
 CREATE TABLE /*_*/msg_resource (
   -- Resource name
   mr_resource varbinary(255) NOT NULL,
index 5b09ffd..a192db7 100644 (file)
@@ -1224,7 +1224,7 @@ CREATE TABLE /*_*/l10n_cache (
 );
 CREATE INDEX /*i*/lc_lang_key ON /*_*/l10n_cache (lc_lang, lc_key);
 
--- Table for caching JSON message texts for the resource loader
+-- Table for caching JSON message texts for ResourceLoader
 CREATE TABLE /*_*/msg_resource (
   -- Resource name
   mr_resource nvarchar(255) NOT NULL,
index 06e1449..ed16805 100644 (file)
@@ -242,13 +242,8 @@ class RefreshLinks extends Maintenance {
                        return;
                }
 
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
-
                $updates = $content->getSecondaryDataUpdates( $page->getTitle() );
                DataUpdate::runUpdates( $updates );
-
-               $dbw->commit( __METHOD__ );
        }
 
        /**
index 638b99d..35e7ec2 100644 (file)
@@ -1496,7 +1496,7 @@ CREATE TABLE /*_*/l10n_cache (
 ) /*$wgDBTableOptions*/;
 CREATE INDEX /*i*/lc_lang_key ON /*_*/l10n_cache (lc_lang, lc_key);
 
--- Table for caching JSON message blobs for the resource loader
+-- Table for caching JSON message blobs for ResourceLoader
 CREATE TABLE /*_*/msg_resource (
   -- Resource name
   mr_resource varbinary(255) NOT NULL,
index 59ce155..b1b1541 100644 (file)
@@ -1318,6 +1318,9 @@ return array(
                'scripts' => 'resources/src/mediawiki/mediawiki.experiments.js',
                'targets' => array( 'desktop', 'mobile' ),
        ),
+       'mediawiki.raggett' => array(
+               'styles' => 'resources/src/mediawiki/mediawiki.raggett.css'
+       ),
 
        /* MediaWiki Action */
 
index d6f61ae..162790e 100644 (file)
@@ -2,7 +2,8 @@
        "@metadata": {
                "authors": [
                        "DzWiki",
-                       "Semso98"
+                       "Semso98",
+                       "Srdjan m"
                ]
        },
        "ooui-outline-control-move-down": "Premjesti stavku dolje",
@@ -16,5 +17,6 @@
        "ooui-dialog-process-error": "Nešto je pošlo naopako",
        "ooui-dialog-process-dismiss": "Odbaci",
        "ooui-dialog-process-retry": "Pokušajte ponovo",
-       "ooui-dialog-process-continue": "Nastavi"
+       "ooui-dialog-process-continue": "Nastavi",
+       "ooui-selectfile-placeholder": "Nijedna datoteka nije izabrana"
 }
index 0d86aa6..fb93e3a 100644 (file)
@@ -26,6 +26,7 @@
        "ooui-dialog-process-dismiss": "Zavřít",
        "ooui-dialog-process-retry": "Zkusit znovu",
        "ooui-dialog-process-continue": "Pokračovat",
+       "ooui-selectfile-button-select": "Vybrat soubor",
        "ooui-selectfile-not-supported": "Výběr souboru není podporován",
        "ooui-selectfile-placeholder": "Nebyl vybrán žádný soubor"
 }
index bdf015f..0b9e149 100644 (file)
@@ -29,7 +29,8 @@
        "ooui-dialog-process-dismiss": "Hylkää",
        "ooui-dialog-process-retry": "Yritä uudelleen",
        "ooui-dialog-process-continue": "Jatka",
+       "ooui-selectfile-button-select": "Valitse tiedosto",
        "ooui-selectfile-not-supported": "Tiedoston valitsemista ei tueta",
        "ooui-selectfile-placeholder": "Tiedostoa ei ole valittu",
-       "ooui-selectfile-dragdrop-placeholder": "Pudota tiedosto (tai selaa tiedostoja napsauttamalla)"
+       "ooui-selectfile-dragdrop-placeholder": "Pudota tiedosto tähän"
 }
index 515eada..d222d1c 100644 (file)
@@ -32,6 +32,8 @@
        "ooui-dialog-process-dismiss": "Sluiten",
        "ooui-dialog-process-retry": "Opnieuw proberen",
        "ooui-dialog-process-continue": "Doorgaan",
+       "ooui-selectfile-button-select": "Selecteer een bestand",
        "ooui-selectfile-not-supported": "Selectie van een bestand wordt niet ondersteund",
-       "ooui-selectfile-placeholder": "Er is geen bestand geselecteerd"
+       "ooui-selectfile-placeholder": "Er is geen bestand geselecteerd",
+       "ooui-selectfile-dragdrop-placeholder": "Sleep hier een bestand heen"
 }
index 5012934..131413e 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.12.12
+ * OOjs UI v0.13.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-10-13T20:38:26Z
+ * Date: 2015-10-27T17:53:00Z
  */
 @-webkit-keyframes oo-ui-progressBarWidget-slide {
        from {
        width: 1.875em;
        height: 1.875em;
 }
-.oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
-       /* Don't animate opacities for now, causes wiggling in Chrome (bug 63020) */
-       /*.oo-ui-transition(opacity @medium-ease);*/
-}
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:hover,
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus {
        outline: none;
        border: 1px #c9c9c9 solid;
        -webkit-transition: border-color 100ms ease;
           -moz-transition: border-color 100ms ease;
-           -ms-transition: border-color 100ms ease;
-            -o-transition: border-color 100ms ease;
                transition: border-color 100ms ease;
        background: #eeeeee;
        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#ffffff', endColorstr='#dddddd');
        margin-right: 0.3em;
 }
 .oo-ui-buttonElement-framed.oo-ui-indicatorElement > .oo-ui-buttonElement-button > .oo-ui-indicatorElement-indicator {
-       /* -0.5 - 0.475 */
        margin-left: -0.005em;
        margin-right: -0.005em;
 }
 }
 .oo-ui-draggableElement {
        cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
-       /*
-        * HACK: In order to style horizontally, we must override
-        * OO.ui.OptionWidget's display rule that is currently set
-        * to be 'block'
-        */
 }
 .oo-ui-draggableElement-dragging {
        cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
        position: absolute;
        -webkit-transition: all 200ms ease;
           -moz-transition: all 200ms ease;
-           -ms-transition: all 200ms ease;
-            -o-transition: all 200ms ease;
                transition: all 200ms ease;
 }
 .oo-ui-menuLayout-menu {
        border: 1px solid transparent;
        -webkit-transition: border-color 250ms ease;
           -moz-transition: border-color 250ms ease;
-           -ms-transition: border-color 250ms ease;
-            -o-transition: border-color 250ms ease;
                transition: border-color 250ms ease;
 }
 .oo-ui-toolGroup-empty {
        background-image: -webkit-linear-gradient(transparent, transparent), /* @embed */ url("themes/apex/images/icons/check.svg");
        background-image:         linear-gradient(transparent, transparent), /* @embed */ url("themes/apex/images/icons/check.svg");
        background-image:      -o-linear-gradient(transparent, transparent), url("themes/apex/images/icons/check.png");
+       background-size: contain;
+       background-position: center center;
+       background-repeat: no-repeat;
 }
 .oo-ui-menuToolGroup .oo-ui-tool.oo-ui-widget-enabled:hover {
        background-color: #e1f3ff;
 .oo-ui-toolbar-actions > .oo-ui-buttonElement-frameless:last-child.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        margin: 0 1em;
        line-height: 3.40625em;
-       /* 43/12.8 */
 }
 .oo-ui-toolbar-shadow {
        background-image: /* @embed */ url(themes/apex/images/toolbar-shadow.png);
        opacity: 0.5;
        -webkit-transition: opacity 500ms ease;
           -moz-transition: opacity 500ms ease;
-           -ms-transition: opacity 500ms ease;
-            -o-transition: opacity 500ms ease;
                transition: opacity 500ms ease;
 }
 .oo-ui-optionWidget {
        -webkit-transform: translateZ(0px);
           -moz-transform: translateZ(0px);
            -ms-transform: translateZ(0px);
-            -o-transform: translateZ(0px);
                transform: translateZ(0px);
        height: 2em;
        width: 4em;
        border: 1px #c9c9c9 solid;
        -webkit-transition: left 250ms ease, margin-left 250ms ease;
           -moz-transition: left 250ms ease, margin-left 250ms ease;
-           -ms-transition: left 250ms ease, margin-left 250ms ease;
-            -o-transition: left 250ms ease, margin-left 250ms ease;
                transition: left 250ms ease, margin-left 250ms ease;
        background: #eeeeee;
        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#ffffff', endColorstr='#dddddd');
        box-shadow: inset 0 1px 4px 0 rgba(0, 0, 0, 0.07);
        -webkit-transition: opacity 250ms ease;
           -moz-transition: opacity 250ms ease;
-           -ms-transition: opacity 250ms ease;
-            -o-transition: opacity 250ms ease;
                transition: opacity 250ms ease;
        background: #cde7f4;
        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#b0d9ee', endColorstr='#eaf4fa');
        border-right: 1px solid #cccccc;
        -webkit-transition: width 250ms ease, margin-left 250ms ease;
           -moz-transition: width 250ms ease, margin-left 250ms ease;
-           -ms-transition: width 250ms ease, margin-left 250ms ease;
-            -o-transition: width 250ms ease, margin-left 250ms ease;
                transition: width 250ms ease, margin-left 250ms ease;
        background: #cde7f4;
        filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#eaf4fa', endColorstr='#b0d9ee');
 .oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
        -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
           -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-           -ms-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-            -o-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
                animation: oo-ui-progressBarWidget-slide 2s infinite linear;
        width: 40%;
        margin-left: -10%;
 .oo-ui-popupWidget-transitioning .oo-ui-popupWidget-popup {
        -webkit-transition: width 100ms ease, height 100ms ease, left 100ms ease;
           -moz-transition: width 100ms ease, height 100ms ease, left 100ms ease;
-           -ms-transition: width 100ms ease, height 100ms ease, left 100ms ease;
-            -o-transition: width 100ms ease, height 100ms ease, left 100ms ease;
                transition: width 100ms ease, height 100ms ease, left 100ms ease;
 }
 .oo-ui-popupWidget-head {
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
+.oo-ui-textInputWidget textarea {
+       overflow: auto;
+}
 .oo-ui-textInputWidget input[type="search"] {
        -webkit-appearance: none;
 }
        border-radius: 0.25em;
        -webkit-transition: border-color 250ms ease, box-shadow 250ms ease;
           -moz-transition: border-color 250ms ease, box-shadow 250ms ease;
-           -ms-transition: border-color 250ms ease, box-shadow 250ms ease;
-            -o-transition: border-color 250ms ease, box-shadow 250ms ease;
                transition: border-color 250ms ease, box-shadow 250ms ease;
 }
 .oo-ui-textInputWidget input.oo-ui-pendingElement-pending,
        opacity: 0;
        z-index: 1;
        cursor: pointer;
-       /* Push the button part of the native control out of view, as it changes the cursor */
        padding-top: 100px;
 }
 .oo-ui-selectFileWidget-selectButton.oo-ui-widget-disabled > .oo-ui-buttonElement-button > input[type="file"] {
 .oo-ui-processDialog-actions-other .oo-ui-actionWidget.oo-ui-buttonElement-framed .oo-ui-buttonElement-button {
        padding: 0 1em;
        vertical-align: middle;
-       /* Adjust for border so text aligns with title */
        margin: -1px;
 }
 .oo-ui-processDialog-actions-safe .oo-ui-actionWidget.oo-ui-buttonElement-frameless,
        opacity: 0;
        -webkit-transition: opacity 250ms ease;
           -moz-transition: opacity 250ms ease;
-           -ms-transition: opacity 250ms ease;
-            -o-transition: opacity 250ms ease;
                transition: opacity 250ms ease;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog > .oo-ui-window-frame {
        -webkit-transform: scale(0.5);
           -moz-transform: scale(0.5);
            -ms-transform: scale(0.5);
-            -o-transform: scale(0.5);
                transform: scale(0.5);
        -webkit-transition: all 250ms ease;
           -moz-transition: all 250ms ease;
-           -ms-transition: all 250ms ease;
-            -o-transition: all 250ms ease;
                transition: all 250ms ease;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready {
-       /* Fade window overlay */
        opacity: 1;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
-       /* Fade frame */
        opacity: 1;
        -webkit-transform: scale(1);
           -moz-transform: scale(1);
            -ms-transform: scale(1);
-            -o-transform: scale(1);
                transform: scale(1);
 }
 .oo-ui-windowManager-modal.oo-ui-windowManager-floating > .oo-ui-dialog > .oo-ui-window-frame {
index dab3c78..3158bfe 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.12.12
+ * OOjs UI v0.13.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-10-13T20:38:18Z
+ * Date: 2015-10-27T17:52:51Z
  */
 /**
  * @class
index a19ebea..96ba45c 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.12.12
+ * OOjs UI v0.13.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-10-13T20:38:26Z
+ * Date: 2015-10-27T17:53:00Z
  */
 @-webkit-keyframes oo-ui-progressBarWidget-slide {
        from {
        position: relative;
        -webkit-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
           -moz-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
-           -ms-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
-            -o-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
                transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
 }
 .oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:hover,
 }
 .oo-ui-draggableElement {
        cursor: -webkit-grab -moz-grab, url(images/grab.cur), move;
-       /*
-        * HACK: In order to style horizontally, we must override
-        * OO.ui.OptionWidget's display rule that is currently set
-        * to be 'block'
-        */
 }
 .oo-ui-draggableElement-dragging {
        cursor: -webkit-grabbing -moz-grabbing, url(images/grabbing.cur), move;
        position: absolute;
        -webkit-transition: all 200ms ease;
           -moz-transition: all 200ms ease;
-           -ms-transition: all 200ms ease;
-            -o-transition: all 200ms ease;
                transition: all 200ms ease;
 }
 .oo-ui-menuLayout-menu {
 .oo-ui-toolbar-actions > .oo-ui-buttonElement.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        margin: 0 1em;
        line-height: 3.125em;
-       /* 40/12.8 */
 }
 .oo-ui-optionWidget {
        position: relative;
        -webkit-transform: translateZ(0px);
           -moz-transform: translateZ(0px);
            -ms-transform: translateZ(0px);
-            -o-transform: translateZ(0px);
                transform: translateZ(0px);
        height: 2em;
        width: 3.5em;
+       border: 1px solid #777777;
        border-radius: 1em;
-       border: 1px #555555 solid;
-       background: #ffffff;
-       -webkit-transition: background-color 100ms ease;
-          -moz-transition: background-color 100ms ease;
-           -ms-transition: background-color 100ms ease;
-            -o-transition: background-color 100ms ease;
-               transition: background-color 100ms ease;
+       background-color: #ffffff;
        margin-right: 0.5em;
+       -webkit-transition: background-color 100ms ease, border-color 100ms ease;
+          -moz-transition: background-color 100ms ease, border-color 100ms ease;
+               transition: background-color 100ms ease, border-color 100ms ease;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-disabled {
        cursor: default;
 .oo-ui-toggleSwitchWidget:last-child {
        margin-right: 0;
 }
-.oo-ui-toggleSwitchWidget-grip {
-       top: 0.5em;
-       left: 0.5em;
-       width: 1em;
-       height: 1em;
-       margin-top: -1px;
+.oo-ui-toggleSwitchWidget:before {
+       content: "";
+       display: block;
+       position: absolute;
+       top: 0;
+       left: 0;
+       bottom: 0;
+       right: 0;
+       border: 1px solid transparent;
        border-radius: 1em;
-       background: #555555;
+       z-index: 1;
+}
+.oo-ui-toggleSwitchWidget-grip {
+       top: 0.35em;
+       width: 1.2em;
+       height: 1.2em;
+       border-radius: 1.2em;
+       background-color: #555555;
        -webkit-transition: left 100ms ease, margin-left 100ms ease;
           -moz-transition: left 100ms ease, margin-left 100ms ease;
-           -ms-transition: left 100ms ease, margin-left 100ms ease;
-            -o-transition: left 100ms ease, margin-left 100ms ease;
                transition: left 100ms ease, margin-left 100ms ease;
 }
 .oo-ui-toggleSwitchWidget-glow {
        display: none;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
-       left: 2em;
+       left: 1.9em;
        margin-left: -2px;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-toggleWidget-off .oo-ui-toggleSwitchWidget-grip {
-       left: 0.5em;
+       left: 0.4em;
        margin-left: 0;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on {
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
        background: #ffffff;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus {
-       outline: none;
-       border-color: #347bff;
-}
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus.oo-ui-toggleWidget-on {
-       border-color: #ffffff;
-       box-shadow: 0 0 0 1px #347bff;
+       box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover {
        border-color: #2962cc;
-       box-shadow: 0 0 0 1px #2962cc;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover.oo-ui-toggleWidget-on {
        background: #2962cc;
        border-color: #2962cc;
 }
-.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus {
+       border-color: #347bff;
+       outline: none;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus.oo-ui-toggleWidget-on {
+       border-color: #347bff;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus.oo-ui-toggleWidget-on:before {
+       border-color: #ffffff;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active,
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover {
+       background-color: #347bff;
+       border-color: #347bff;
+}
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active .oo-ui-toggleSwitchWidget-grip,
+.oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:active:hover .oo-ui-toggleSwitchWidget-grip {
        background: #ffffff;
+       box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-disabled {
        background: #dddddd;
        background: #dddddd;
        -webkit-transition: width 200ms, margin-left 200ms;
           -moz-transition: width 200ms, margin-left 200ms;
-           -ms-transition: width 200ms, margin-left 200ms;
-            -o-transition: width 200ms, margin-left 200ms;
                transition: width 200ms, margin-left 200ms;
 }
 .oo-ui-progressBarWidget-indeterminate .oo-ui-progressBarWidget-bar {
        -webkit-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
           -moz-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-           -ms-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
-            -o-animation: oo-ui-progressBarWidget-slide 2s infinite linear;
                animation: oo-ui-progressBarWidget-slide 2s infinite linear;
        width: 40%;
        margin-left: -10%;
 .oo-ui-popupWidget-transitioning .oo-ui-popupWidget-popup {
        -webkit-transition: width 100ms ease, height 100ms ease, left 100ms ease;
           -moz-transition: width 100ms ease, height 100ms ease, left 100ms ease;
-           -ms-transition: width 100ms ease, height 100ms ease, left 100ms ease;
-            -o-transition: width 100ms ease, height 100ms ease, left 100ms ease;
                transition: width 100ms ease, height 100ms ease, left 100ms ease;
 }
 .oo-ui-popupWidget-head {
 .oo-ui-checkboxInputWidget input[type="checkbox"] + span {
        -webkit-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
           -moz-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-           -ms-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-            -o-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
                transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
 .oo-ui-checkboxInputWidget input[type="checkbox"]:hover + span {
        border-bottom-width: 3px;
 }
-.oo-ui-checkboxInputWidget input[type="checkbox"]:disabled + span {
+.oo-ui-checkboxInputWidget input[type="checkbox"]:disabled {
        cursor: default;
+}
+.oo-ui-checkboxInputWidget input[type="checkbox"]:disabled + span {
        background-color: #eeeeee;
        border-color: #eeeeee;
 }
 .oo-ui-radioInputWidget input[type="radio"] + span {
        -webkit-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
           -moz-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-           -ms-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
-            -o-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
                transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
 .oo-ui-radioInputWidget input[type="radio"]:hover + span {
        border-bottom-width: 3px;
 }
-.oo-ui-radioInputWidget input[type="radio"]:disabled + span {
+.oo-ui-radioInputWidget input[type="radio"]:disabled {
        cursor: default;
+}
+.oo-ui-radioInputWidget input[type="radio"]:disabled + span {
        background-color: #eeeeee;
        border-color: #eeeeee;
 }
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
+.oo-ui-textInputWidget textarea {
+       overflow: auto;
+}
 .oo-ui-textInputWidget input[type="search"] {
        -webkit-appearance: none;
 }
        border-radius: 0.1em;
        -webkit-transition: box-shadow 100ms ease;
           -moz-transition: box-shadow 100ms ease;
-           -ms-transition: box-shadow 100ms ease;
-            -o-transition: box-shadow 100ms ease;
                transition: box-shadow 100ms ease;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
 .oo-ui-textInputWidget.oo-ui-widget-enabled textarea {
        -webkit-transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
           -moz-transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
-           -ms-transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
-            -o-transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
                transition: border 200ms cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 200ms cubic-bezier(0.39, 0.575, 0.565, 1);
 }
 .oo-ui-textInputWidget.oo-ui-widget-enabled input:focus,
        opacity: 0;
        z-index: 1;
        cursor: pointer;
-       /* Push the button part of the native control out of view, as it changes the cursor */
        padding-top: 100px;
 }
 .oo-ui-selectFileWidget-selectButton.oo-ui-widget-disabled > .oo-ui-buttonElement-button > input[type="file"] {
        opacity: 0;
        -webkit-transition: opacity 250ms ease;
           -moz-transition: opacity 250ms ease;
-           -ms-transition: opacity 250ms ease;
-            -o-transition: opacity 250ms ease;
                transition: opacity 250ms ease;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog > .oo-ui-window-frame {
        -webkit-transform: scale(0.5);
           -moz-transform: scale(0.5);
            -ms-transform: scale(0.5);
-            -o-transform: scale(0.5);
                transform: scale(0.5);
        -webkit-transition: all 250ms ease;
           -moz-transition: all 250ms ease;
-           -ms-transition: all 250ms ease;
-            -o-transition: all 250ms ease;
                transition: all 250ms ease;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready {
-       /* Fade window overlay */
        opacity: 1;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog.oo-ui-window-ready > .oo-ui-window-frame {
-       /* Fade frame */
        opacity: 1;
        -webkit-transform: scale(1);
           -moz-transform: scale(1);
            -ms-transform: scale(1);
-            -o-transform: scale(1);
                transform: scale(1);
 }
 .oo-ui-windowManager-modal.oo-ui-windowManager-floating > .oo-ui-dialog > .oo-ui-window-frame {
index e6344f2..95e8b47 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.12.12
+ * OOjs UI v0.13.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-10-13T20:38:18Z
+ * Date: 2015-10-27T17:52:51Z
  */
 /**
  * @class
index aeff69e..fbffe09 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.12.12
+ * OOjs UI v0.13.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-10-13T20:38:18Z
+ * Date: 2015-10-27T17:52:51Z
  */
 ( function ( OO ) {
 
@@ -1289,10 +1289,10 @@ OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) {
                        }
                }
        } );
+       // pick up dynamic state, like focus, value of form inputs, scroll position, etc.
+       state = cls.static.gatherPreInfuseState( $elem, data );
        // jscs:disable requireCapitalizedConstructors
        obj = new cls( data ); // rebuild widget
-       // pick up dynamic state, like focus, value of form inputs, scroll position, etc.
-       state = obj.gatherPreInfuseState( $elem );
        // now replace old DOM with this new DOM.
        if ( top ) {
                $elem.replaceWith( obj.$element );
@@ -1310,6 +1310,24 @@ OO.ui.Element.static.unsafeInfuse = function ( idOrNode, domPromise ) {
        return obj;
 };
 
+/**
+ * Gather the dynamic state (focus, value of form inputs, scroll position, etc.) of a HTML DOM node
+ * (and its children) that represent an Element of the same class and the given configuration,
+ * generated by the PHP implementation.
+ *
+ * This method is called just before `node` is detached from the DOM. The return value of this
+ * function will be passed to #restorePreInfuseState after the newly created widget's #$element
+ * is inserted into DOM to replace `node`.
+ *
+ * @protected
+ * @param {HTMLElement} node
+ * @param {Object} config
+ * @return {Object}
+ */
+OO.ui.Element.static.gatherPreInfuseState = function () {
+       return {};
+};
+
 /**
  * Get a jQuery function within a specific document.
  *
@@ -1888,23 +1906,6 @@ OO.ui.Element.prototype.scrollElementIntoView = function ( config ) {
        return OO.ui.Element.static.scrollIntoView( this.$element[ 0 ], config );
 };
 
-/**
- * Gather the dynamic state (focus, value of form inputs, scroll position, etc.) of a HTML DOM node
- * (and its children) that represent an Element of the same type and configuration as the current
- * one, generated by the PHP implementation.
- *
- * This method is called just before `node` is detached from the DOM. The return value of this
- * function will be passed to #restorePreInfuseState after this widget's #$element is inserted into
- * DOM to replace `node`.
- *
- * @protected
- * @param {HTMLElement} node
- * @return {Object}
- */
-OO.ui.Element.prototype.gatherPreInfuseState = function () {
-       return {};
-};
-
 /**
  * Restore the pre-infusion dynamic state for this widget.
  *
@@ -9730,6 +9731,8 @@ OO.ui.BookletLayout = function OoUiBookletLayout( config ) {
        this.stackLayout.connect( this, { set: 'onStackLayoutSet' } );
        if ( this.outlined ) {
                this.outlineSelectWidget.connect( this, { select: 'onOutlineSelectWidgetSelect' } );
+               this.scrolling = false;
+               this.stackLayout.connect( this, { visibleItemChange: 'onStackLayoutVisibleItemChange' } );
        }
        if ( this.autoFocus ) {
                // Event 'focus' does not bubble, but 'focusin' does
@@ -9801,6 +9804,22 @@ OO.ui.BookletLayout.prototype.onStackLayoutFocus = function ( e ) {
        }
 };
 
+/**
+ * Handle visibleItemChange events from the stackLayout
+ *
+ * The next visible page is set as the current page by selecting it
+ * in the outline
+ *
+ * @param {OO.ui.PageLayout} page The next visible page in the layout
+ */
+OO.ui.BookletLayout.prototype.onStackLayoutVisibleItemChange = function ( page ) {
+       // Set a flag to so that the resulting call to #onStackLayoutSet doesn't
+       // try and scroll the item into view again.
+       this.scrolling = true;
+       this.outlineSelectWidget.selectItemByData( page.getName() );
+       this.scrolling = false;
+};
+
 /**
  * Handle stack layout set events.
  *
@@ -9809,7 +9828,7 @@ OO.ui.BookletLayout.prototype.onStackLayoutFocus = function ( e ) {
  */
 OO.ui.BookletLayout.prototype.onStackLayoutSet = function ( page ) {
        var layout = this;
-       if ( page ) {
+       if ( !this.scrolling && page ) {
                page.scrollElementIntoView( { complete: function () {
                        if ( layout.autoFocus ) {
                                layout.focus();
@@ -11020,6 +11039,7 @@ OO.ui.StackLayout = function OoUiStackLayout( config ) {
        this.$element.addClass( 'oo-ui-stackLayout' );
        if ( this.continuous ) {
                this.$element.addClass( 'oo-ui-stackLayout-continuous' );
+               this.$element.on( 'scroll', OO.ui.debounce( this.onScroll.bind( this ), 250 ) );
        }
        if ( Array.isArray( config.items ) ) {
                this.addItems( config.items );
@@ -11041,8 +11061,66 @@ OO.mixinClass( OO.ui.StackLayout, OO.ui.mixin.GroupElement );
  * @param {OO.ui.Layout|null} item Current panel or `null` if no panel is shown
  */
 
+/**
+ * When used in continuous mode, this event is emitted when the user scrolls down
+ * far enough such that currentItem is no longer visible.
+ *
+ * @event visibleItemChange
+ * @param {OO.ui.PanelLayout} panel The next visible item in the layout
+ */
+
 /* Methods */
 
+/**
+ * Handle scroll events from the layout element
+ *
+ * @param {jQuery.Event} e
+ * @fires visibleItemChange
+ */
+OO.ui.StackLayout.prototype.onScroll = function () {
+       var currentRect,
+               len = this.items.length,
+               currentIndex = this.items.indexOf( this.currentItem ),
+               newIndex = currentIndex,
+               containerRect = this.$element[ 0 ].getBoundingClientRect();
+
+       if ( !containerRect || ( !containerRect.top && !containerRect.bottom ) ) {
+               // Can't get bounding rect, possibly not attached.
+               return;
+       }
+
+       function getRect( item ) {
+               return item.$element[ 0 ].getBoundingClientRect();
+       }
+
+       function isVisible( item ) {
+               var rect = getRect( item );
+               return rect.bottom > containerRect.top && rect.top < containerRect.bottom;
+       }
+
+       currentRect = getRect( this.currentItem );
+
+       if ( currentRect.bottom < containerRect.top ) {
+               // Scrolled down past current item
+               while ( ++newIndex < len ) {
+                       if ( isVisible( this.items[ newIndex ] ) ) {
+                               break;
+                       }
+               }
+       } else if ( currentRect.top > containerRect.bottom ) {
+               // Scrolled up past current item
+               while ( --newIndex >= 0 ) {
+                       if ( isVisible( this.items[ newIndex ] ) ) {
+                               break;
+                       }
+               }
+       }
+
+       if ( newIndex !== currentIndex ) {
+               this.emit( 'visibleItemChange', this.items[ newIndex ] );
+       }
+};
+
 /**
  * Get the current panel.
  *
@@ -14540,6 +14618,21 @@ OO.mixinClass( OO.ui.InputWidget, OO.ui.mixin.AccessKeyedElement );
 
 OO.ui.InputWidget.static.supportsSimpleLabel = true;
 
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.InputWidget.static.gatherPreInfuseState = function ( node, config ) {
+       var
+               state = OO.ui.InputWidget.parent.static.gatherPreInfuseState( node, config ),
+               $input = state.$input || $( node ).find( '.oo-ui-inputWidget-input' );
+       state.value = $input.val();
+       // Might be better in TabIndexedElement, but it's awkward to do there because mixins are awkward
+       state.focus = $input.is( ':focus' );
+       return state;
+};
+
 /* Events */
 
 /**
@@ -14718,19 +14811,6 @@ OO.ui.InputWidget.prototype.blur = function () {
        return this;
 };
 
-/**
- * @inheritdoc
- */
-OO.ui.InputWidget.prototype.gatherPreInfuseState = function ( node ) {
-       var
-               state = OO.ui.InputWidget.parent.prototype.gatherPreInfuseState.call( this, node ),
-               $input = state.$input || $( node ).find( '.oo-ui-inputWidget-input' );
-       state.value = $input.val();
-       // Might be better in TabIndexedElement, but it's awkward to do there because mixins are awkward
-       state.focus = $input.is( ':focus' );
-       return state;
-};
-
 /**
  * @inheritdoc
  */
@@ -14936,6 +15016,20 @@ OO.ui.CheckboxInputWidget = function OoUiCheckboxInputWidget( config ) {
 
 OO.inheritClass( OO.ui.CheckboxInputWidget, OO.ui.InputWidget );
 
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.CheckboxInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+       var
+               state = OO.ui.CheckboxInputWidget.parent.static.gatherPreInfuseState( node, config ),
+               $input = $( node ).find( '.oo-ui-inputWidget-input' );
+       state.$input = $input; // shortcut for performance, used in InputWidget
+       state.checked = $input.prop( 'checked' );
+       return state;
+};
+
 /* Methods */
 
 /**
@@ -14990,18 +15084,6 @@ OO.ui.CheckboxInputWidget.prototype.isSelected = function () {
        return this.selected;
 };
 
-/**
- * @inheritdoc
- */
-OO.ui.CheckboxInputWidget.prototype.gatherPreInfuseState = function ( node ) {
-       var
-               state = OO.ui.CheckboxInputWidget.parent.prototype.gatherPreInfuseState.call( this, node ),
-               $input = $( node ).find( '.oo-ui-inputWidget-input' );
-       state.$input = $input; // shortcut for performance, used in InputWidget
-       state.checked = $input.prop( 'checked' );
-       return state;
-};
-
 /**
  * @inheritdoc
  */
@@ -15225,6 +15307,20 @@ OO.ui.RadioInputWidget = function OoUiRadioInputWidget( config ) {
 
 OO.inheritClass( OO.ui.RadioInputWidget, OO.ui.InputWidget );
 
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+       var
+               state = OO.ui.RadioInputWidget.parent.static.gatherPreInfuseState( node, config ),
+               $input = $( node ).find( '.oo-ui-inputWidget-input' );
+       state.$input = $input; // shortcut for performance, used in InputWidget
+       state.checked = $input.prop( 'checked' );
+       return state;
+};
+
 /* Methods */
 
 /**
@@ -15263,18 +15359,6 @@ OO.ui.RadioInputWidget.prototype.isSelected = function () {
        return this.$input.prop( 'checked' );
 };
 
-/**
- * @inheritdoc
- */
-OO.ui.RadioInputWidget.prototype.gatherPreInfuseState = function ( node ) {
-       var
-               state = OO.ui.RadioInputWidget.parent.prototype.gatherPreInfuseState.call( this, node ),
-               $input = $( node ).find( '.oo-ui-inputWidget-input' );
-       state.$input = $input; // shortcut for performance, used in InputWidget
-       state.checked = $input.prop( 'checked' );
-       return state;
-};
-
 /**
  * @inheritdoc
  */
@@ -15341,6 +15425,17 @@ OO.inheritClass( OO.ui.RadioSelectInputWidget, OO.ui.InputWidget );
 
 OO.ui.RadioSelectInputWidget.static.supportsSimpleLabel = false;
 
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.RadioSelectInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+       var state = OO.ui.RadioSelectInputWidget.parent.static.gatherPreInfuseState( node, config );
+       state.value = $( node ).find( '.oo-ui-radioInputWidget .oo-ui-inputWidget-input:checked' ).val();
+       return state;
+};
+
 /* Methods */
 
 /**
@@ -15416,15 +15511,6 @@ OO.ui.RadioSelectInputWidget.prototype.setOptions = function ( options ) {
        return this;
 };
 
-/**
- * @inheritdoc
- */
-OO.ui.RadioSelectInputWidget.prototype.gatherPreInfuseState = function ( node ) {
-       var state = OO.ui.RadioSelectInputWidget.parent.prototype.gatherPreInfuseState.call( this, node );
-       state.value = $( node ).find( '.oo-ui-radioInputWidget .oo-ui-inputWidget-input:checked' ).val();
-       return state;
-};
-
 /**
  * TextInputWidgets, like HTML text inputs, can be configured with options that customize the
  * size of the field as well as its presentation. In addition, these widgets can be configured
@@ -15517,6 +15603,7 @@ OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
        this.minRows = config.rows !== undefined ? config.rows : '';
        this.maxRows = config.maxRows || Math.max( 2 * ( this.minRows || 0 ), 10 );
        this.validate = null;
+       this.styleHeight = null;
 
        // Clone for resizing
        if ( this.autosize ) {
@@ -15603,6 +15690,22 @@ OO.ui.TextInputWidget.static.validationPatterns = {
        integer: /^\d+$/
 };
 
+/* Static Methods */
+
+/**
+ * @inheritdoc
+ */
+OO.ui.TextInputWidget.static.gatherPreInfuseState = function ( node, config ) {
+       var
+               state = OO.ui.TextInputWidget.parent.static.gatherPreInfuseState( node, config ),
+               $input = $( node ).find( '.oo-ui-inputWidget-input' );
+       state.$input = $input; // shortcut for performance, used in InputWidget
+       if ( config.multiline ) {
+               state.scrollTop = $input.scrollTop();
+       }
+       return state;
+};
+
 /* Events */
 
 /**
@@ -15613,6 +15716,12 @@ OO.ui.TextInputWidget.static.validationPatterns = {
  * @event enter
  */
 
+/**
+ * A `resize` event is emitted when autosize is set and the widget resizes
+ *
+ * @event resize
+ */
+
 /* Methods */
 
 /**
@@ -15802,9 +15911,10 @@ OO.ui.TextInputWidget.prototype.installParentChangeDetector = function () {
  * This only affects #multiline inputs that are {@link #autosize autosized}.
  *
  * @chainable
+ * @fires resize
  */
 OO.ui.TextInputWidget.prototype.adjustSize = function () {
-       var scrollHeight, innerHeight, outerHeight, maxInnerHeight, measurementError, idealHeight;
+       var scrollHeight, innerHeight, outerHeight, maxInnerHeight, measurementError, idealHeight, newHeight;
 
        if ( this.multiline && this.autosize && this.$input.val() !== this.valCache ) {
                this.$clone
@@ -15839,11 +15949,12 @@ OO.ui.TextInputWidget.prototype.adjustSize = function () {
                this.$clone.addClass( 'oo-ui-element-hidden' );
 
                // Only apply inline height when expansion beyond natural height is needed
-               if ( idealHeight > innerHeight ) {
-                       // Use the difference between the inner and outer height as a buffer
-                       this.$input.css( 'height', idealHeight + ( outerHeight - innerHeight ) );
-               } else {
-                       this.$input.css( 'height', '' );
+               // Use the difference between the inner and outer height as a buffer
+               newHeight = idealHeight > innerHeight ? idealHeight + ( outerHeight - innerHeight ) : '';
+               if ( newHeight !== this.styleHeight ) {
+                       this.$input.css( 'height', newHeight );
+                       this.styleHeight = newHeight;
+                       this.emit( 'resize' );
                }
        }
        return this;
@@ -15892,30 +16003,74 @@ OO.ui.TextInputWidget.prototype.isAutosizing = function () {
 };
 
 /**
- * Select the entire text of the input.
+ * Focus the input and select a specified range within the text.
  *
+ * @param {number} from Select from offset
+ * @param {number} [to] Select to offset, defaults to from
  * @chainable
  */
-OO.ui.TextInputWidget.prototype.select = function () {
-       this.$input.select();
-       return this;
-};
-
-/**
- * Focus the input and move the cursor to the end.
- */
-OO.ui.TextInputWidget.prototype.moveCursorToEnd = function () {
-       var textRange,
+OO.ui.TextInputWidget.prototype.selectRange = function ( from, to ) {
+       var textRange, isBackwards, start, end,
                element = this.$input[ 0 ];
+
+       to = to || from;
+
+       isBackwards = to < from;
+       start = isBackwards ? to : from;
+       end = isBackwards ? from : to;
+
        this.focus();
-       if ( element.selectionStart !== undefined ) {
-               element.selectionStart = element.selectionEnd = element.value.length;
+
+       if ( element.setSelectionRange ) {
+               element.setSelectionRange( start, end, isBackwards ? 'backward' : 'forward' );
        } else if ( element.createTextRange ) {
                // IE 8 and below
                textRange = element.createTextRange();
-               textRange.collapse( false );
+               textRange.collapse( true );
+               textRange.moveStart( 'character', start );
+               textRange.moveEnd( 'character', end - start );
                textRange.select();
        }
+       return this;
+};
+
+/**
+ * Get the length of the text input value.
+ *
+ * This could differ from the length of #getValue if the
+ * value gets filtered
+ *
+ * @return {number} Input length
+ */
+OO.ui.TextInputWidget.prototype.getInputLength = function () {
+       return this.$input[ 0 ].value.length;
+};
+
+/**
+ * Focus the input and select the entire text.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.select = function () {
+       return this.selectRange( 0, this.getInputLength() );
+};
+
+/**
+ * Focus the input and move the cursor to the start.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.moveCursorToStart = function () {
+       return this.selectRange( 0 );
+};
+
+/**
+ * Focus the input and move the cursor to the end.
+ *
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.moveCursorToEnd = function () {
+       return this.selectRange( this.getInputLength() );
 };
 
 /**
@@ -16117,20 +16272,6 @@ OO.ui.TextInputWidget.prototype.positionLabel = function () {
        return this;
 };
 
-/**
- * @inheritdoc
- */
-OO.ui.TextInputWidget.prototype.gatherPreInfuseState = function ( node ) {
-       var
-               state = OO.ui.TextInputWidget.parent.prototype.gatherPreInfuseState.call( this, node ),
-               $input = $( node ).find( '.oo-ui-inputWidget-input' );
-       state.$input = $input; // shortcut for performance, used in InputWidget
-       if ( this.multiline ) {
-               state.scrollTop = $input.scrollTop();
-       }
-       return state;
-};
-
 /**
  * @inheritdoc
  */
@@ -19612,104 +19753,4 @@ OO.ui.ToggleSwitchWidget.prototype.onKeyPress = function ( e ) {
        }
 };
 
-/*!
- * Deprecated aliases for classes in the `OO.ui.mixin` namespace.
- */
-
-/**
- * @inheritdoc OO.ui.mixin.ButtonElement
- * @deprecated Use {@link OO.ui.mixin.ButtonElement} instead.
- */
-OO.ui.ButtonElement = OO.ui.mixin.ButtonElement;
-
-/**
- * @inheritdoc OO.ui.mixin.ClippableElement
- * @deprecated Use {@link OO.ui.mixin.ClippableElement} instead.
- */
-OO.ui.ClippableElement = OO.ui.mixin.ClippableElement;
-
-/**
- * @inheritdoc OO.ui.mixin.DraggableElement
- * @deprecated Use {@link OO.ui.mixin.DraggableElement} instead.
- */
-OO.ui.DraggableElement = OO.ui.mixin.DraggableElement;
-
-/**
- * @inheritdoc OO.ui.mixin.DraggableGroupElement
- * @deprecated Use {@link OO.ui.mixin.DraggableGroupElement} instead.
- */
-OO.ui.DraggableGroupElement = OO.ui.mixin.DraggableGroupElement;
-
-/**
- * @inheritdoc OO.ui.mixin.FlaggedElement
- * @deprecated Use {@link OO.ui.mixin.FlaggedElement} instead.
- */
-OO.ui.FlaggedElement = OO.ui.mixin.FlaggedElement;
-
-/**
- * @inheritdoc OO.ui.mixin.GroupElement
- * @deprecated Use {@link OO.ui.mixin.GroupElement} instead.
- */
-OO.ui.GroupElement = OO.ui.mixin.GroupElement;
-
-/**
- * @inheritdoc OO.ui.mixin.GroupWidget
- * @deprecated Use {@link OO.ui.mixin.GroupWidget} instead.
- */
-OO.ui.GroupWidget = OO.ui.mixin.GroupWidget;
-
-/**
- * @inheritdoc OO.ui.mixin.IconElement
- * @deprecated Use {@link OO.ui.mixin.IconElement} instead.
- */
-OO.ui.IconElement = OO.ui.mixin.IconElement;
-
-/**
- * @inheritdoc OO.ui.mixin.IndicatorElement
- * @deprecated Use {@link OO.ui.mixin.IndicatorElement} instead.
- */
-OO.ui.IndicatorElement = OO.ui.mixin.IndicatorElement;
-
-/**
- * @inheritdoc OO.ui.mixin.ItemWidget
- * @deprecated Use {@link OO.ui.mixin.ItemWidget} instead.
- */
-OO.ui.ItemWidget = OO.ui.mixin.ItemWidget;
-
-/**
- * @inheritdoc OO.ui.mixin.LabelElement
- * @deprecated Use {@link OO.ui.mixin.LabelElement} instead.
- */
-OO.ui.LabelElement = OO.ui.mixin.LabelElement;
-
-/**
- * @inheritdoc OO.ui.mixin.LookupElement
- * @deprecated Use {@link OO.ui.mixin.LookupElement} instead.
- */
-OO.ui.LookupElement = OO.ui.mixin.LookupElement;
-
-/**
- * @inheritdoc OO.ui.mixin.PendingElement
- * @deprecated Use {@link OO.ui.mixin.PendingElement} instead.
- */
-OO.ui.PendingElement = OO.ui.mixin.PendingElement;
-
-/**
- * @inheritdoc OO.ui.mixin.PopupElement
- * @deprecated Use {@link OO.ui.mixin.PopupElement} instead.
- */
-OO.ui.PopupElement = OO.ui.mixin.PopupElement;
-
-/**
- * @inheritdoc OO.ui.mixin.TabIndexedElement
- * @deprecated Use {@link OO.ui.mixin.TabIndexedElement} instead.
- */
-OO.ui.TabIndexedElement = OO.ui.mixin.TabIndexedElement;
-
-/**
- * @inheritdoc OO.ui.mixin.TitledElement
- * @deprecated Use {@link OO.ui.mixin.TitledElement} instead.
- */
-OO.ui.TitledElement = OO.ui.mixin.TitledElement;
-
 }( OO ) );
index d66973a..600b771 100644 (file)
@@ -12,9 +12,6 @@
 //
 // Styleguide 2.
 
-@transitionDuration: .1s;
-@transitionFunction: ease-in-out;
-
 // Neutral button styling
 //
 // These are the main actions on the page/workflow. The page should have only one of progressive, constructive and desctructive buttons, the rest being quiet.
@@ -66,8 +63,6 @@
                cursor: default;
        }
 
-       .transition(background @transitionDuration @transitionFunction, color @transitionDuration @transitionFunction, box-shadow @transitionDuration @transitionFunction;);
-
        // Styling for specific button types
        // -----------------------------------------
 
index ac5becb..bd5dd4a 100644 (file)
@@ -72,7 +72,6 @@
 
                // the pseudo before element of the label after the checkbox now looks like a checkbox
                & + label::before {
-                       .transition( 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275) );
                        content: '';
                        cursor: pointer;
                        .box-sizing(border-box);
index 2f76131..62f0e83 100644 (file)
@@ -39,7 +39,6 @@
        font-family: inherit;
        font-size: inherit;
        line-height: inherit;
-       .transition(~"border 0.2s cubic-bezier(0.39, 0.575, 0.565, 1), box-shadow 0.2s cubic-bezier(0.39, 0.575, 0.565, 1)");
 
        // Placeholder text styling must be set individually for each browser @winter
        &::-webkit-input-placeholder { // webkit
index 1928699..52effd6 100644 (file)
@@ -63,7 +63,6 @@
 
                // the pseudo before element of the label after the radio now looks like a radio
                & + label::before {
-                       .transition( 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275) );
                        content: '';
                        cursor: pointer;
                        .box-sizing(border-box);
index 0ef5095..f764513 100644 (file)
                                redirect: suggestionPage.redirect !== undefined,
                                disambiguation: OO.getProp( suggestionPage, 'pageprops', 'disambiguation' ) !== undefined,
                                imageUrl: OO.getProp( suggestionPage, 'thumbnail', 'source' ),
-                               description: OO.getProp( suggestionPage, 'terms', 'description' )
+                               description: OO.getProp( suggestionPage, 'terms', 'description' ),
+                               // sort index
+                               index: suggestionPage.index
                        };
 
                        // Throw away pages from wrong namespaces. This can happen when 'showRedirectTargets' is true
                        }
                }
 
+               titles.sort( function ( a, b ) {
+                       return pageData[ a ].index - pageData[ b ].index;
+               } );
+
                // If not found, run value through mw.Title to avoid treating a match as a
                // mismatch where normalisation would make them matching (bug 48476)
 
index 01917f8..717e2ec 100644 (file)
         * @inheritdoc
         */
        mw.ForeignStructuredUpload.BookletLayout.prototype.initialize = function () {
-               mw.ForeignStructuredUpload.BookletLayout.parent.prototype.initialize.call( this );
-               // Point the CategorySelector to the right wiki as soon as we know what the right wiki is
-               this.upload.apiPromise.done( function ( api ) {
-                       // If this is a ForeignApi, it will have a apiUrl, otherwise we don't need to do anything
-                       if ( api.apiUrl ) {
-                               // Can't reuse the same object, CategorySelector calls #abort on its mw.Api instance
-                               this.categoriesWidget.api = new mw.ForeignApi( api.apiUrl );
-                       }
-               }.bind( this ) );
+               var deferred = $.Deferred();
+               mw.ForeignStructuredUpload.BookletLayout.parent.prototype.initialize.call( this )
+                       .done( function () {
+                               // Point the CategorySelector to the right wiki
+                               this.upload.apiPromise.done( function ( api ) {
+                                       // If this is a ForeignApi, it will have a apiUrl, otherwise we don't need to do anything
+                                       if ( api.apiUrl ) {
+                                               // Can't reuse the same object, CategorySelector calls #abort on its mw.Api instance
+                                               this.categoriesWidget.api = new mw.ForeignApi( api.apiUrl );
+                                       }
+                                       deferred.resolve();
+                               }.bind( this ) );
+                       }.bind( this ) );
+               return deferred.promise();
        };
 
        /**
index dd199ce..c616829 100644 (file)
 
        /**
         * Initialize for a new upload
+        *
+        * @return {jQuery.Promise} Promise resolved when everything is initialized
         */
        mw.Upload.BookletLayout.prototype.initialize = function () {
                this.clear();
                this.upload = this.createUpload();
                this.setPage( 'upload' );
+               return $.Deferred().resolve().promise();
        };
 
        /**
index 220a3fe..53afca8 100644 (file)
        mw.Upload.Dialog.prototype.getSetupProcess = function ( data ) {
                return mw.Upload.Dialog.parent.prototype.getSetupProcess.call( this, data )
                        .next( function () {
-                               this.uploadBooklet.initialize();
+                               return this.uploadBooklet.initialize();
                        }, this );
        };
 
index de63a18..2e89f6b 100644 (file)
         * @return {string} return.return Rendered HTML.
         */
        mw.jqueryMsg.getMessageFunction = function ( options ) {
-               var failableParserFn = getFailableParserFn( options ),
-                       format;
+               var failableParserFn, format;
 
                if ( options && options.format !== undefined ) {
                        format = options.format;
                }
 
                return function () {
+                       if ( !failableParserFn ) {
+                               failableParserFn = getFailableParserFn( options );
+                       }
                        var failableResult = failableParserFn( arguments );
                        if ( format === 'text' || format === 'escaped' ) {
                                return failableResult.text();
         * @return {jQuery} return.return
         */
        mw.jqueryMsg.getPlugin = function ( options ) {
-               var failableParserFn = getFailableParserFn( options );
+               var failableParserFn;
 
                return function () {
+                       if ( !failableParserFn ) {
+                               failableParserFn = getFailableParserFn( options );
+                       }
                        var $target = this.empty();
                        appendWithoutParsing( $target, failableParserFn( arguments ) );
                        return $target;
        };
 
        mw.jqueryMsg.parser.prototype = {
-               /**
-                * Cache mapping MediaWiki message keys and the value onlyCurlyBraceTransform, to the AST of the message.
-                *
-                * In most cases, the message is a string so this is identical.
-                * (This is why we would like to move this functionality server-side).
-                *
-                * The two parts of the key are separated by colon.  For example:
-                *
-                *     "message-key:true": ast
-                *
-                * if they key is "message-key" and onlyCurlyBraceTransform is true.
-                *
-                * This cache is shared by all instances of mw.jqueryMsg.parser.
-                *
-                * NOTE: We promise, it's static - when you create this empty object
-                * in the prototype, each new instance of the class gets a reference
-                * to the same object.
-                *
-                * @static
-                * @property {Object}
-                */
-               astCache: {},
-
                /**
                 * Where the magic happens.
                 * Parses a message from the key, and swaps in replacements as necessary, wraps in jQuery
                 * @return {string|Array} string of '[key]' if message missing, simple string if possible, array of arrays if needs parsing
                 */
                getAst: function ( key ) {
-                       var wikiText,
-                               cacheKey = [ key, this.settings.onlyCurlyBraceTransform ].join( ':' );
-
-                       if ( this.astCache[ cacheKey ] === undefined ) {
-                               wikiText = this.settings.messages.get( key );
-                               if ( typeof wikiText !== 'string' ) {
-                                       wikiText = '\\[' + key + '\\]';
-                               }
-                               this.astCache[ cacheKey ] = this.wikiTextToAst( wikiText );
+                       var wikiText = this.settings.messages.get( key );
+                       if ( typeof wikiText !== 'string' ) {
+                               wikiText = '\\[' + key + '\\]';
                        }
-                       return this.astCache[ cacheKey ];
+                       return this.wikiTextToAst( wikiText );
                },
 
                /**
                        // I am deferring the work of turning it into prototypes & objects. It's quite fast enough
                        // finally let's do some actual work...
 
-                       // If you add another possible rootExpression, you must update the astCache key scheme.
                        result = start( this.settings.onlyCurlyBraceTransform ? curlyBraceTransformExpression : expression );
 
                        /*
diff --git a/resources/src/mediawiki/mediawiki.raggett.css b/resources/src/mediawiki/mediawiki.raggett.css
new file mode 100644 (file)
index 0000000..1e1e973
--- /dev/null
@@ -0,0 +1,3 @@
+.mw-empty-li {
+       display: none;
+}
index 093748d..1c48515 100644 (file)
@@ -77,9 +77,6 @@ $wgAutoloadClasses += array(
        'WikitextContentTest' => "$testDir/phpunit/includes/content/WikitextContentTest.php",
 
        # tests/phpunit/includes/db
-       'ORMRowTest' => "$testDir/phpunit/includes/db/ORMRowTest.php",
-       'ORMTableTest' => "$testDir/phpunit/includes/db/ORMTableTest.php",
-       'PageORMTableForTesting' => "$testDir/phpunit/includes/db/ORMTableTest.php",
        'DatabaseTestHelper' => "$testDir/phpunit/includes/db/DatabaseTestHelper.php",
 
        # tests/phpunit/includes/diff
index f245826..4e6c591 100644 (file)
@@ -26405,3 +26405,19 @@ B <ref group="X" name="b" />
 <ref name="b"><span id="Z">foo</span>bar</ref>
 </references>
 !! end
+
+!! test
+Empty LI (T49673)
+!! wikitext
+* a
+* 
+*
+* b
+!! html/php+tidy
+<ul>
+<li>a</li>
+<li class="mw-empty-li"></li>
+<li class="mw-empty-li"></li>
+<li>b</li>
+</ul>
+!! end
index 4ce51c6..a5b65f2 100644 (file)
@@ -149,9 +149,6 @@ class WfTimestampTest extends MediaWikiTestCase {
        }
 
        /**
-        * The Resource Loader uses wfTimestamp() to convert timestamps
-        * from If-Modified-Since header. Thus it must be able to parse all
-        * rfc2616 date formats
         * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1
         * @dataProvider provideHttpDates
         */
index 8ee2ad5..9c22430 100644 (file)
@@ -9,16 +9,8 @@
  */
 class ImportTest extends MediaWikiLangTestCase {
 
-       private function getInputStreamSource( $xml ) {
-               if ( ini_get( 'allow_url_fopen' ) != 1 ) {
-                       $this->markTestSkipped( 'bug 73283: this test needs allow_url_fopen to be enabled' );
-               }
-               $file = 'data:application/xml,' . $xml;
-               $status = ImportStreamSource::newFromFile( $file );
-               if ( !$status->isGood() ) {
-                       throw new MWException( "Cannot create InputStreamSource." );
-               }
-               return $status->value;
+       private function getDataSource( $xml ) {
+               return new ImportStringSource( $xml );
        }
 
        /**
@@ -28,7 +20,7 @@ class ImportTest extends MediaWikiLangTestCase {
         * @param string|null $redirectTitle
         */
        public function testHandlePageContainsRedirect( $xml, $redirectTitle ) {
-               $source = $this->getInputStreamSource( $xml );
+               $source = $this->getDataSource( $xml );
 
                $redirect = null;
                $callback = function ( Title $title, ForeignTitle $foreignTitle, $revCount,
@@ -114,7 +106,7 @@ EOF
         * @param array|null $namespaces
         */
        public function testSiteInfoContainsNamespaces( $xml, $namespaces ) {
-               $source = $this->getInputStreamSource( $xml );
+               $source = $this->getDataSource( $xml );
 
                $importNamespaces = null;
                $callback = function ( array $siteinfo, $innerImporter ) use ( &$importNamespaces ) {
diff --git a/tests/phpunit/includes/db/ORMRowTest.php b/tests/phpunit/includes/db/ORMRowTest.php
deleted file mode 100644 (file)
index 807bd14..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-<?php
-
-/**
- * Abstract class to construct tests for ORMRow deriving classes.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @since 1.20
- *
- * @ingroup Test
- *
- * @group ORM
- *
- * The database group has as a side effect that temporal database tables are created. This makes
- * it possible to test without poisoning a production database.
- * @group Database
- *
- * Some of the tests takes more time, and needs therefor longer time before they can be aborted
- * as non-functional. The reason why tests are aborted is assumed to be set up of temporal databases
- * that hold the first tests in a pending state awaiting access to the database.
- * @group medium
- *
- * @author Jeroen De Dauw < jeroendedauw@gmail.com >
- */
-abstract class ORMRowTest extends \MediaWikiTestCase {
-
-       /**
-        * @since 1.20
-        * @return string
-        */
-       abstract protected function getRowClass();
-
-       /**
-        * @since 1.20
-        * @return IORMTable
-        */
-       abstract protected function getTableInstance();
-
-       /**
-        * @since 1.20
-        * @return array
-        */
-       abstract public function constructorTestProvider();
-
-       /**
-        * @since 1.20
-        * @param IORMRow $row
-        * @param array $data
-        */
-       protected function verifyFields( IORMRow $row, array $data ) {
-               foreach ( array_keys( $data ) as $fieldName ) {
-                       $this->assertEquals( $data[$fieldName], $row->getField( $fieldName ) );
-               }
-       }
-
-       /**
-        * @since 1.20
-        * @param array $data
-        * @param bool $loadDefaults
-        * @return IORMRow
-        */
-       protected function getRowInstance( array $data, $loadDefaults ) {
-               $class = $this->getRowClass();
-
-               return new $class( $this->getTableInstance(), $data, $loadDefaults );
-       }
-
-       /**
-        * @since 1.20
-        * @return array
-        */
-       protected function getMockValues() {
-               return array(
-                       'id' => 1,
-                       'str' => 'foobar4645645',
-                       'int' => 42,
-                       'float' => 4.2,
-                       'bool' => true,
-                       'array' => array( 42, 'foobar' ),
-                       'blob' => new stdClass()
-               );
-       }
-
-       /**
-        * @since 1.20
-        * @return array
-        */
-       protected function getMockFields() {
-               $mockValues = $this->getMockValues();
-               $mockFields = array();
-
-               foreach ( $this->getTableInstance()->getFields() as $name => $type ) {
-                       if ( $name !== 'id' ) {
-                               $mockFields[$name] = $mockValues[$type];
-                       }
-               }
-
-               return $mockFields;
-       }
-
-       /**
-        * @since 1.20
-        * @return array Array of IORMRow
-        */
-       public function instanceProvider() {
-               $instances = array();
-
-               foreach ( $this->constructorTestProvider() as $arguments ) {
-                       $instances[] = array( call_user_func_array( array( $this, 'getRowInstance' ), $arguments ) );
-               }
-
-               return $instances;
-       }
-
-       /**
-        * @dataProvider constructorTestProvider
-        */
-       public function testConstructor( array $data, $loadDefaults ) {
-               $this->verifyFields( $this->getRowInstance( $data, $loadDefaults ), $data );
-       }
-
-       /**
-        * @dataProvider constructorTestProvider
-        */
-       public function testSaveAndRemove( array $data, $loadDefaults ) {
-               $item = $this->getRowInstance( $data, $loadDefaults );
-
-               $this->assertTrue( $item->save() );
-
-               $this->assertTrue( $item->hasIdField() );
-               $this->assertTrue( is_integer( $item->getId() ) );
-
-               $id = $item->getId();
-
-               $this->assertTrue( $item->save() );
-
-               $this->assertEquals( $id, $item->getId() );
-
-               $this->verifyFields( $item, $data );
-
-               $this->assertTrue( $item->remove() );
-
-               $this->assertFalse( $item->hasIdField() );
-
-               $this->assertTrue( $item->save() );
-
-               $this->verifyFields( $item, $data );
-
-               $this->assertTrue( $item->remove() );
-
-               $this->assertFalse( $item->hasIdField() );
-
-               $this->verifyFields( $item, $data );
-       }
-
-       /**
-        * @dataProvider instanceProvider
-        */
-       public function testSetField( IORMRow $item ) {
-               foreach ( $this->getMockFields() as $name => $value ) {
-                       $item->setField( $name, $value );
-                       $this->assertEquals( $value, $item->getField( $name ) );
-               }
-       }
-
-       /**
-        * @since 1.20
-        * @param array $expected
-        * @param IORMRow $item
-        */
-       protected function assertFieldValues( array $expected, IORMRow $item ) {
-               foreach ( $expected as $name => $type ) {
-                       if ( $name !== 'id' ) {
-                               $this->assertEquals( $expected[$name], $item->getField( $name ) );
-                       }
-               }
-       }
-
-       /**
-        * @dataProvider instanceProvider
-        */
-       public function testSetFields( IORMRow $item ) {
-               $originalValues = $item->getFields();
-
-               $item->setFields( array(), false );
-
-               foreach ( $item->getTable()->getFields() as $name => $type ) {
-                       $originalHas = array_key_exists( $name, $originalValues );
-                       $newHas = $item->hasField( $name );
-
-                       $this->assertEquals( $originalHas, $newHas );
-
-                       if ( $originalHas && $newHas ) {
-                               $this->assertEquals( $originalValues[$name], $item->getField( $name ) );
-                       }
-               }
-
-               $mockFields = $this->getMockFields();
-
-               $item->setFields( $mockFields, false );
-
-               $this->assertFieldValues( $originalValues, $item );
-
-               $item->setFields( $mockFields, true );
-
-               $this->assertFieldValues( $mockFields, $item );
-       }
-
-       // TODO: test all of the methods!
-
-}
diff --git a/tests/phpunit/includes/db/ORMTableTest.php b/tests/phpunit/includes/db/ORMTableTest.php
deleted file mode 100644 (file)
index 764560d..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-/**
- * Abstract class to construct tests for ORMTable deriving classes.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @since 1.21
- *
- * @ingroup Test
- *
- * @group ORM
- * @group Database
- *
- * @covers PageORMTableForTesting
- *
- * @author Jeroen De Dauw < jeroendedauw@gmail.com >
- * @author Daniel Kinzler
- */
-
-class ORMTableTest extends MediaWikiTestCase {
-
-       /**
-        * @since 1.21
-        * @return string
-        */
-       protected function getTableClass() {
-               return 'PageORMTableForTesting';
-       }
-
-       /**
-        * @since 1.21
-        * @return IORMTable
-        */
-       public function getTable() {
-               $class = $this->getTableClass();
-
-               return $class::singleton();
-       }
-
-       /**
-        * @since 1.21
-        * @return string
-        */
-       public function getRowClass() {
-               return $this->getTable()->getRowClass();
-       }
-
-       /**
-        * @since 1.21
-        */
-       public function testSingleton() {
-               $class = $this->getTableClass();
-
-               $this->assertInstanceOf( $class, $class::singleton() );
-               $this->assertTrue( $class::singleton() === $class::singleton() );
-       }
-}
-
-/**
- * Dummy ORM table for testing, reading Title objects from the page table.
- *
- * @since 1.21
- */
-
-class PageORMTableForTesting extends ORMTable {
-
-       public function __construct() {
-               $this->fieldPrefix = 'page_';
-       }
-
-       /**
-        * @see ORMTable::getName
-        *
-        * @return string
-        */
-       public function getName() {
-               return 'page';
-       }
-
-       /**
-        * @see ORMTable::getRowClass
-        *
-        * @return string
-        */
-       public function getRowClass() {
-               return 'Title';
-       }
-
-       /**
-        * @see ORMTable::newRow
-        *
-        * @return IORMRow
-        */
-       public function newRow( array $data, $loadDefaults = false ) {
-               return Title::makeTitle( $data['namespace'], $data['title'] );
-       }
-
-       /**
-        * @see ORMTable::getFields
-        *
-        * @return array
-        */
-       public function getFields() {
-               return array(
-                       'id' => 'int',
-                       'namespace' => 'int',
-                       'title' => 'str',
-               );
-       }
-}
diff --git a/tests/phpunit/includes/db/TestORMRowTest.php b/tests/phpunit/includes/db/TestORMRowTest.php
deleted file mode 100644 (file)
index 04bb9f3..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-<?php
-
-/**
- * Tests for the TestORMRow class.
- * TestORMRow is a dummy class to be able to test the abstract ORMRow class.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Test
- * @author Jeroen De Dauw < jeroendedauw@gmail.com >
- */
-
-/**
- * The database group has as a side effect that temporal database tables are created. This makes
- * it possible to test without poisoning a production database.
- *
- * Some of the tests takes more time, and needs therefor longer time before they can be aborted
- * as non-functional. The reason why tests are aborted is assumed to be set up of temporal databases
- * that hold the first tests in a pending state awaiting access to the database.
- *
- * @since 1.20
- *
- * @group ORM
- * @group Database
- * @group medium
- * @covers TestORMRow
- */
-class TestORMRowTest extends ORMRowTest {
-
-       /**
-        * @since 1.20
-        * @return string
-        */
-       protected function getRowClass() {
-               return 'TestORMRow';
-       }
-
-       /**
-        * @since 1.20
-        * @return IORMTable
-        */
-       protected function getTableInstance() {
-               return TestORMTable::singleton();
-       }
-
-       protected function setUp() {
-               parent::setUp();
-
-               $dbw = wfGetDB( DB_MASTER );
-
-               $isSqlite = $GLOBALS['wgDBtype'] === 'sqlite';
-               $isPostgres = $GLOBALS['wgDBtype'] === 'postgres';
-
-               $idField = $isSqlite ? 'INTEGER' : 'INT unsigned';
-               $primaryKey = $isSqlite ? 'PRIMARY KEY AUTOINCREMENT' : 'auto_increment PRIMARY KEY';
-
-               if ( $isPostgres ) {
-                       $dbw->query(
-                               'CREATE TABLE IF NOT EXISTS ' . $dbw->tableName( 'orm_test' ) . "(
-                                       test_id serial PRIMARY KEY,
-                                       test_name TEXT NOT NULL DEFAULT '',
-                                       test_age INTEGER NOT NULL DEFAULT 0,
-                                       test_height REAL NOT NULL DEFAULT 0,
-                                       test_awesome INTEGER NOT NULL DEFAULT 0,
-                                       test_stuff BYTEA,
-                                       test_moarstuff BYTEA,
-                                       test_time TIMESTAMPTZ
-                                       );",
-                                       __METHOD__
-                               );
-               } else {
-                       $dbw->query(
-                               'CREATE TABLE IF NOT EXISTS ' . $dbw->tableName( 'orm_test' ) . '(
-                                       test_id                    ' . $idField . '        NOT NULL ' . $primaryKey . ',
-                                       test_name                  VARCHAR(255)        NOT NULL,
-                                       test_age                   TINYINT unsigned    NOT NULL,
-                                       test_height                FLOAT               NOT NULL,
-                                       test_awesome               TINYINT unsigned    NOT NULL,
-                                       test_stuff                 BLOB                NOT NULL,
-                                       test_moarstuff             BLOB                NOT NULL,
-                                       test_time                  varbinary(14)       NOT NULL
-                               );',
-                               __METHOD__
-                       );
-               }
-       }
-
-       protected function tearDown() {
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->dropTable( 'orm_test', __METHOD__ );
-
-               parent::tearDown();
-       }
-
-       public function constructorTestProvider() {
-               $dbw = wfGetDB( DB_MASTER );
-               return array(
-                       array(
-                               array(
-                                       'name' => 'Foobar',
-                                       'time' => $dbw->timestamp( '20120101020202' ),
-                                       'age' => 42,
-                                       'height' => 9000.1,
-                                       'awesome' => true,
-                                       'stuff' => array( 13, 11, 7, 5, 3, 2 ),
-                                       'moarstuff' => (object)array( 'foo' => 'bar', 'bar' => array( 4, 2 ), 'baz' => true )
-                               ),
-                               true
-                       ),
-               );
-       }
-
-       /**
-        * @since 1.21
-        * @return array
-        */
-       protected function getMockValues() {
-               return array(
-                       'id' => 1,
-                       'str' => 'foobar4645645',
-                       'int' => 42,
-                       'float' => 4.2,
-                       'bool' => '',
-                       'array' => array( 42, 'foobar' ),
-                       'blob' => new stdClass()
-               );
-       }
-}
-
-class TestORMRow extends ORMRow {
-}
-
-class TestORMTable extends ORMTable {
-
-       public function __construct() {
-               $this->fieldPrefix = 'test_';
-       }
-
-       /**
-        * Returns the name of the database table objects of this type are stored in.
-        *
-        * @since 1.20
-        *
-        * @return string
-        */
-       public function getName() {
-               return 'orm_test';
-       }
-
-       /**
-        * Returns the name of a IORMRow implementing class that
-        * represents single rows in this table.
-        *
-        * @since 1.20
-        *
-        * @return string
-        */
-       public function getRowClass() {
-               return 'TestORMRow';
-       }
-
-       /**
-        * Returns an array with the fields and their types this object contains.
-        * This corresponds directly to the fields in the database, without prefix.
-        *
-        * field name => type
-        *
-        * Allowed types:
-        * * id
-        * * str
-        * * int
-        * * float
-        * * bool
-        * * array
-        * * blob
-        *
-        * @since 1.20
-        *
-        * @return array
-        */
-       public function getFields() {
-               return array(
-                       'id' => 'id',
-                       'name' => 'str',
-                       'age' => 'int',
-                       'height' => 'float',
-                       'awesome' => 'bool',
-                       'stuff' => 'array',
-                       'moarstuff' => 'blob',
-                       'time' => 'str', // TS_MW
-               );
-       }
-}
diff --git a/tests/phpunit/includes/libs/RunningStatTest.php b/tests/phpunit/includes/libs/RunningStatTest.php
deleted file mode 100644 (file)
index 35a8e4f..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-/**
- * PHP Unit tests for RunningStat class.
- * @covers RunningStat
- */
-class RunningStatTest extends PHPUnit_Framework_TestCase {
-
-       public $points = array(
-               49.7168, 74.3804,  7.0115, 96.5769, 34.9458,
-               36.9947, 33.8926, 89.0774, 23.7745, 73.5154,
-               86.1322, 53.2124, 16.2046, 73.5130, 10.4209,
-               42.7299, 49.3330, 47.0215, 34.9950, 18.2914,
-       );
-
-       /**
-        * Verify that the statistical moments and extrema computed by RunningStat
-        * match expected values.
-        * @covers RunningStat::push
-        * @covers RunningStat::count
-        * @covers RunningStat::getMean
-        * @covers RunningStat::getVariance
-        * @covers RunningStat::getStdDev
-        */
-       public function testRunningStatAccuracy() {
-               $rstat = new RunningStat();
-               foreach ( $this->points as $point ) {
-                       $rstat->push( $point );
-               }
-
-               $mean = array_sum( $this->points ) / count( $this->points );
-               $variance = array_sum( array_map( function ( $x ) use ( $mean ) {
-                       return pow( $mean - $x, 2 );
-               }, $this->points ) ) / ( count( $rstat ) - 1 );
-               $stddev = sqrt( $variance );
-
-               $this->assertEquals( count( $rstat ), count( $this->points ) );
-               $this->assertEquals( $rstat->min, min( $this->points ) );
-               $this->assertEquals( $rstat->max, max( $this->points ) );
-               $this->assertEquals( $rstat->getMean(), $mean );
-               $this->assertEquals( $rstat->getVariance(), $variance );
-               $this->assertEquals( $rstat->getStdDev(), $stddev );
-       }
-
-       /**
-        * When one RunningStat instance is merged into another, the state of the
-        * target RunningInstance should have the state that it would have had if
-        * all the data had been accumulated by it alone.
-        * @covers RunningStat::merge
-        * @covers RunningStat::count
-        */
-       public function testRunningStatMerge() {
-               $expected = new RunningStat();
-
-               foreach ( $this->points as $point ) {
-                       $expected->push( $point );
-               }
-
-               // Split the data into two sets
-               $sets = array_chunk( $this->points, floor( count( $this->points ) / 2 ) );
-
-               // Accumulate the first half into one RunningStat object
-               $first = new RunningStat();
-               foreach ( $sets[0] as $point ) {
-                       $first->push( $point );
-               }
-
-               // Accumulate the second half into another RunningStat object
-               $second = new RunningStat();
-               foreach ( $sets[1] as $point ) {
-                       $second->push( $point );
-               }
-
-               // Merge the second RunningStat object into the first
-               $first->merge( $second );
-
-               $this->assertEquals( count( $first ), count( $this->points ) );
-               $this->assertEquals( $first, $expected );
-       }
-}
index c3702c5..a1fdd91 100644 (file)
@@ -175,7 +175,6 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                        return $value;
                };
 
-               $cache->delete( $key );
                $ret = $cache->getWithSetCallback( $key, 30, $func, array( 'lockTSE' => 5 ) );
                $this->assertEquals( $value, $ret );
                $this->assertEquals( 1, $calls, 'Value was populated' );
@@ -187,6 +186,36 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $this->assertEquals( 1, $calls, 'Callback was not used' );
        }
 
+       /**
+        * @covers WANObjectCache::getWithSetCallback()
+        */
+       public function testLockTSESlow() {
+               $cache = $this->cache;
+               $key = wfRandomString();
+               $value = wfRandomString();
+
+               $calls = 0;
+               $func = function( $oldValue, &$ttl, &$setOpts ) use ( &$calls, $value ) {
+                       ++$calls;
+                       $setOpts['since'] = microtime( true ) - 10;
+                       return $value;
+               };
+
+               // Value should be marked as stale due to snapshot lag
+               $curTTL = null;
+               $ret = $cache->getWithSetCallback( $key, 30, $func, array( 'lockTSE' => 5 ) );
+               $this->assertEquals( $value, $ret );
+               $this->assertEquals( $value, $cache->get( $key, $curTTL ), 'Value was populated' );
+               $this->assertLessThan( 0, $curTTL, 'Value has negative curTTL' );
+               $this->assertEquals( 1, $calls, 'Value was generated' );
+
+               // Acquire a lock to verify that getWithSetCallback uses lockTSE properly
+               $this->internalCache->lock( $key, 0 );
+               $ret = $cache->getWithSetCallback( $key, 30, $func, array( 'lockTSE' => 5 ) );
+               $this->assertEquals( $value, $ret );
+               $this->assertEquals( 1, $calls, 'Callback was not used' );
+       }
+
        /**
         * @covers WANObjectCache::getMulti()
         */
@@ -313,4 +342,32 @@ class WANObjectCacheTest extends MediaWikiTestCase {
                $t6 = $this->cache->getCheckKeyTime( $key );
                $this->assertEquals( $t5, $t6, 'Check key time did not change' );
        }
+
+       public function testSetWithLag() {
+               $value = 1;
+
+               $key = wfRandomString();
+               $opts = array( 'lag' => 300, 'since' => microtime( true ) );
+               $this->cache->set( $key, $value, 30, $opts );
+               $this->assertEquals( $value, $this->cache->get( $key ), "Rep-lagged value written." );
+
+               $key = wfRandomString();
+               $opts = array( 'lag' => 0, 'since' => microtime( true ) - 300 );
+               $this->cache->set( $key, $value, 30, $opts );
+               $this->assertEquals( false, $this->cache->get( $key ), "Trx-lagged value not written." );
+
+               $key = wfRandomString();
+               $opts = array( 'lag' => 5, 'since' => microtime( true ) - 5 );
+               $this->cache->set( $key, $value, 30, $opts );
+               $this->assertEquals( false, $this->cache->get( $key ), "Lagged value not written." );
+       }
+
+       public function testWritePending() {
+               $value = 1;
+
+               $key = wfRandomString();
+               $opts = array( 'pending' => true );
+               $this->cache->set( $key, $value, 30, $opts );
+               $this->assertEquals( false, $this->cache->get( $key ), "Pending value not written." );
+       }
 }
index b0c9e4f..3e3bac3 100644 (file)
@@ -12,7 +12,7 @@ class MemcachedBagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers MemcachedBagOStuff::makeKeyInternal
+        * @covers MemcachedBagOStuff::makeKey
         */
        public function testKeyNormalization() {
                $this->assertEquals(
@@ -67,4 +67,39 @@ class MemcachedBagOStuffTest extends MediaWikiTestCase {
                        $this->cache->makeKey( 'long_key_part_hashed', str_repeat( 'y', 500 ) )
                );
        }
+
+       /**
+        * @dataProvider validKeyProvider
+        */
+       public function testValidateKeyEncoding( $key ) {
+               $this->assertSame( $key, $this->cache->validateKeyEncoding( $key ) );
+       }
+
+       public function validKeyProvider() {
+               return array(
+                       'empty' => array( '' ),
+                       'digits' => array( '09' ),
+                       'letters' => array( 'AZaz' ),
+                       'ASCII special characters' => array( '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' ),
+               );
+       }
+
+       /**
+        * @dataProvider invalidKeyProvider
+        */
+       public function testValidateKeyEncodingThrowsException( $key ) {
+               $this->setExpectedException( 'Exception' );
+               $this->cache->validateKeyEncoding( $key );
+       }
+
+       public function invalidKeyProvider() {
+               return array(
+                       array( "\x00" ),
+                       array( ' ' ),
+                       array( "\x1F" ),
+                       array( "\x7F" ),
+                       array( "\x80" ),
+                       array( "\xFF" ),
+               );
+       }
 }
index 4fca002..77c3c02 100644 (file)
@@ -261,6 +261,16 @@ class LanguageTest extends LanguageClassesTestCase {
                        $this->getLang()->truncate( "1234567890", 5, 'XXX', false ),
                        'truncate without adjustment'
                );
+               $this->assertEquals(
+                       "泰乐菌...",
+                       $this->getLang()->truncate( "泰乐菌素123456789", 11, '...', false ),
+                       'truncate does not chop Unicode characters in half'
+               );
+               $this->assertEquals(
+                       "\n泰乐菌...",
+                       $this->getLang()->truncate( "\n泰乐菌素123456789", 12, '...', false ),
+                       'truncate does not chop Unicode characters in half if there is a preceding newline'
+               );
        }
 
        /**
index 49b91c3..aaa7751 100755 (executable)
@@ -113,6 +113,11 @@ class PHPUnitMaintClass extends Maintenance {
                );
                // xdebug's default of 100 is too low for MediaWiki
                ini_set( 'xdebug.max_nesting_level', 1000 );
+
+               // Bug T116683 serialize_precision of 100
+               // may break testing against floating point values
+               // treated with PHP's serialize()
+               ini_set( 'serialize_precision', 17 );
        }
 
        public function execute() {
index ae0d325..3282665 100644 (file)
@@ -232,7 +232,6 @@ class ResourcesTest extends MediaWikiTestCase {
                        'lists' => array(
                                'scripts',
                                'debugScripts',
-                               'loaderScripts',
                                'styles',
                        ),
 
index 4f273bc..24528bb 100644 (file)
                assert.equal( logSpy.callCount, 2, 'mw.log.warn calls' );
        } );
 
+       QUnit.test( 'Integration', 4, function ( assert ) {
+               var expected, logSpy;
+
+               expected = '<b><a title="Bold" href="/wiki/Bold">Bold</a>!</b>';
+               mw.messages.set( 'integration-test', '<b>[[Bold]]!</b>' );
+
+               this.suppressWarnings();
+               logSpy = this.sandbox.spy( mw.log, 'warn' );
+               assert.equal(
+                       window.gM( 'integration-test' ),
+                       expected,
+                       'Global function gM() works correctly'
+               );
+               assert.equal( logSpy.callCount, 1, 'mw.log.warn called' );
+               this.restoreWarnings();
+
+               assert.equal(
+                       mw.message( 'integration-test' ).parse(),
+                       expected,
+                       'mw.message().parse() works correctly'
+               );
+
+               assert.equal(
+                       $( '<span>' ).msg( 'integration-test' ).html(),
+                       expected,
+                       'jQuery plugin $.fn.msg() works correctly'
+               );
+       } );
+
 }( mediaWiki, jQuery ) );
index bd14e41..c699bb1 100644 (file)
--- a/thumb.php
+++ b/thumb.php
@@ -376,23 +376,28 @@ function wfStreamThumb( array $params ) {
  * @return array (MediaTransformOutput|bool, string|bool error message HTML)
  */
 function wfGenerateThumbnail( File $file, array $params, $thumbName, $thumbPath ) {
-       global $wgMemc, $wgAttemptFailureEpoch;
+       global $wgAttemptFailureEpoch;
 
-       $key = wfMemcKey( 'attempt-failures', $wgAttemptFailureEpoch,
-               $file->getRepo()->getName(), $file->getSha1(), md5( $thumbName ) );
+       $cache = ObjectCache::getLocalClusterInstance();
+       $key = $cache->makeKey(
+               'attempt-failures',
+               $wgAttemptFailureEpoch,
+               $file->getRepo()->getName(),
+               $file->getSha1(),
+               md5( $thumbName )
+       );
 
        // Check if this file keeps failing to render
-       if ( $wgMemc->get( $key ) >= 4 ) {
+       if ( $cache->get( $key ) >= 4 ) {
                return array( false, wfMessage( 'thumbnail_image-failure-limit', 4 ) );
        }
 
        $done = false;
        // Record failures on PHP fatals in addition to caching exceptions
-       register_shutdown_function( function () use ( &$done, $key ) {
+       register_shutdown_function( function () use ( $cache, &$done, $key ) {
                if ( !$done ) { // transform() gave a fatal
-                       global $wgMemc;
                        // Randomize TTL to reduce stampedes
-                       $wgMemc->incrWithInit( $key, 3600 + mt_rand( 0, 300 ) );
+                       $cache->incrWithInit( $key, $cache::TTL_HOUR + mt_rand( 0, 300 ) );
                }
        } );
 
@@ -445,7 +450,7 @@ function wfGenerateThumbnail( File $file, array $params, $thumbName, $thumbPath
 
        if ( !$thumb || $thumb->isError() ) {
                // Randomize TTL to reduce stampedes
-               $wgMemc->incrWithInit( $key, 3600 + mt_rand( 0, 300 ) );
+               $cache->incrWithInit( $key, $cache::TTL_HOUR + mt_rand( 0, 300 ) );
        }
 
        return array( $thumb, $errorHtml );