Merge "Use MWLogger logging for wfLogProfilingData"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 4 Nov 2014 21:17:19 +0000 (21:17 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 4 Nov 2014 21:17:19 +0000 (21:17 +0000)
171 files changed:
.rubocop.yml [new file with mode: 0644]
.rubocop_todo.yml [new file with mode: 0644]
Gemfile [new file with mode: 0644]
Gemfile.lock [new file with mode: 0644]
INSTALL
RELEASE-NOTES-1.25
api.php
composer.json
docs/export-0.10.xsd [new file with mode: 0644]
docs/export-demo.xml
docs/hooks.txt
includes/AutoLoader.php
includes/DefaultSettings.php
includes/Export.php
includes/GlobalFunctions.php
includes/Import.php
includes/MediaWikiVersionFetcher.php
includes/MovePage.php
includes/PHPVersionError.php
includes/Title.php
includes/api/ApiBase.php
includes/api/ApiFormatBase.php
includes/api/ApiHelp.php
includes/api/ApiImport.php
includes/api/ApiMain.php
includes/api/ApiMove.php
includes/api/ApiPageSet.php
includes/api/ApiParamInfo.php
includes/api/ApiQuery.php
includes/api/ApiQueryAllDeletedRevisions.php [new file with mode: 0644]
includes/api/ApiQueryBase.php
includes/api/ApiQueryDeletedRevisions.php [new file with mode: 0644]
includes/api/ApiQueryDeletedrevs.php
includes/api/ApiQueryInfo.php
includes/api/ApiQueryRevisions.php
includes/api/ApiQueryRevisionsBase.php [new file with mode: 0644]
includes/api/ApiResult.php
includes/api/i18n/en.json
includes/api/i18n/fa.json
includes/api/i18n/fr.json
includes/api/i18n/he.json
includes/api/i18n/jam.json [new file with mode: 0644]
includes/api/i18n/lb.json
includes/api/i18n/mk.json
includes/api/i18n/ms.json
includes/api/i18n/nl.json
includes/api/i18n/qqq.json
includes/api/i18n/sv.json
includes/api/i18n/zh-hans.json
includes/api/i18n/zh-hant.json
includes/db/ORMTable.php
includes/debug/logger/Logger.php
includes/debug/logger/legacy/Logger.php
includes/debug/logger/legacy/Spi.php
includes/debug/logger/monolog/Handler.php
includes/exception/UserNotLoggedIn.php
includes/htmlform/HTMLTagFilter.php
includes/installer/i18n/nap.json
includes/limit.sh [changed mode: 0644->0755]
includes/logging/LogEventsList.php
includes/page/Article.php
includes/page/WikiPage.php
includes/parser/CoreTagHooks.php
includes/parser/Parser.php
includes/password/MWOldPassword.php
includes/password/ParameterizedPassword.php
includes/profiler/ProfilerMwprof.php [deleted file]
includes/profiler/ProfilerSimpleDB.php
includes/profiler/ProfilerSimpleText.php
includes/profiler/ProfilerSimpleTrace.php
includes/profiler/ProfilerSimpleUDP.php
includes/profiler/ProfilerStandard.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/skins/SkinApiTemplate.php
includes/specials/SpecialImport.php
includes/specials/SpecialLinkSearch.php
includes/specials/SpecialMovepage.php
includes/specials/SpecialNewpages.php
includes/specials/SpecialSearch.php
includes/specials/SpecialUpload.php
includes/specials/SpecialUserlogin.php
index.php
languages/i18n/azb.json
languages/i18n/be-tarask.json
languages/i18n/bn.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/cs.json
languages/i18n/de.json
languages/i18n/el.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/frr.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/jam.json
languages/i18n/km.json
languages/i18n/ko.json
languages/i18n/lrc.json
languages/i18n/lzh.json
languages/i18n/mk.json
languages/i18n/ml.json
languages/i18n/nan.json
languages/i18n/nap.json
languages/i18n/pl.json
languages/i18n/pms.json
languages/i18n/qqq.json
languages/i18n/rue.json
languages/i18n/sl.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/su.json
languages/i18n/sv.json
languages/i18n/uk.json
languages/i18n/vi.json
languages/i18n/yi.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
load.php
maintenance/Maintenance.php
maintenance/cleanupCaps.php
maintenance/dev/includes/php.sh [changed mode: 0644->0755]
maintenance/dev/includes/require-php.sh [changed mode: 0644->0755]
maintenance/dumpIterator.php
maintenance/importDump.php
maintenance/jsduck/CustomTags.rb
maintenance/moveBatch.php
maintenance/postgres/compare_schemas.pl [changed mode: 0644->0755]
maintenance/postgres/mediawiki_mysql2postgres.pl [changed mode: 0644->0755]
maintenance/renderDump.php
maintenance/storage/checkStorage.php
maintenance/storage/make-blobs [changed mode: 0644->0755]
maintenance/update.php
mw-config/index.php
resources/lib/oojs-ui/oojs-ui-apex.css
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-apex.svg.css
resources/lib/oojs-ui/oojs-ui-mediawiki.css
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui-mediawiki.svg.css
resources/lib/oojs-ui/oojs-ui.js
resources/src/jquery/jquery.getAttrs.js
resources/src/mediawiki.api/mediawiki.api.js
resources/src/mediawiki.skinning/elements.css [changed mode: 0755->0644]
resources/src/mediawiki.special/mediawiki.special.search.css
resources/src/mediawiki.ui/components/checkbox.less
resources/src/mediawiki/mediawiki.Title.js
resources/src/mediawiki/mediawiki.confirmCloseWindow.js
resources/src/mediawiki/mediawiki.js
resources/src/mediawiki/mediawiki.util.js
tests/TestsAutoLoader.php
tests/browser/Gemfile [deleted file]
tests/browser/Gemfile.lock [deleted file]
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/includes/ImportTest.php
tests/phpunit/includes/TestUser.php
tests/phpunit/includes/TitlePermissionTest.php
tests/phpunit/includes/WikiPageTest.php
tests/phpunit/includes/api/query/ApiQueryContinueTest.php
tests/phpunit/includes/json/FormatJsonTest.php
tests/phpunit/includes/specialpage/SpecialPageFactoryTest.php
tests/phpunit/includes/specialpage/SpecialPageTestHelper.php [new file with mode: 0644]
tests/phpunit/maintenance/DumpTestCase.php
tests/phpunit/maintenance/backupTextPassTest.php
tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js

diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644 (file)
index 0000000..cc32da4
--- /dev/null
@@ -0,0 +1 @@
+inherit_from: .rubocop_todo.yml
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
new file mode 100644 (file)
index 0000000..f0702ba
--- /dev/null
@@ -0,0 +1,84 @@
+# This configuration was generated by `rubocop --auto-gen-config`
+# on 2014-10-21 15:10:03 +0200 using RuboCop version 0.26.1.
+# The point is for the user to remove these configuration records
+# one by one as the offenses are removed from the code base.
+# Note that changes in the inspected code, or installation of new
+# versions of RuboCop, may require this file to be generated again.
+
+# Offense count: 1
+Lint/AmbiguousRegexpLiteral:
+  Enabled: false
+
+# Offense count: 2
+# Cop supports --auto-correct.
+Lint/UnusedMethodArgument:
+  Enabled: false
+
+# Offense count: 19
+# Configuration parameters: AllowURI, URISchemes.
+Metrics/LineLength:
+  Max: 94
+
+# Offense count: 10
+Style/Documentation:
+  Enabled: false
+
+# Offense count: 1
+# Cop supports --auto-correct.
+Style/EmptyLines:
+  Enabled: false
+
+# Offense count: 1
+# Cop supports --auto-correct.
+Style/EmptyLinesAroundBody:
+  Enabled: false
+
+# Offense count: 1
+# Configuration parameters: Exclude.
+Style/FileName:
+  Enabled: false
+
+# Offense count: 8
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+Style/HashSyntax:
+  Enabled: false
+
+# Offense count: 2
+# Cop supports --auto-correct.
+Style/LeadingCommentSpace:
+  Enabled: false
+
+# Offense count: 4
+# Cop supports --auto-correct.
+Style/PerlBackrefs:
+  Enabled: false
+
+# Offense count: 4
+# Cop supports --auto-correct.
+Style/SpaceAroundOperators:
+  Enabled: false
+
+# Offense count: 1
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
+Style/SpaceInsideBlockBraces:
+  Enabled: true
+
+# Offense count: 6
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SupportedStyles.
+Style/SpaceInsideHashLiteralBraces:
+  Enabled: false
+
+# Offense count: 89
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+Style/StringLiterals:
+  Enabled: false
+
+# Offense count: 11
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+Style/TrailingBlankLines:
+  Enabled: false
diff --git a/Gemfile b/Gemfile
new file mode 100644 (file)
index 0000000..1559d0e
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,8 @@
+#ruby=ruby-2.1.2
+#ruby-gemset=core
+
+source "https://rubygems.org"
+
+gem "mediawiki_api"
+gem "mediawiki_selenium"
+gem "rubocop", require: false
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644 (file)
index 0000000..0dc4500
--- /dev/null
@@ -0,0 +1,99 @@
+GEM
+  remote: https://rubygems.org/
+  specs:
+    ast (2.0.0)
+    astrolabe (1.3.0)
+      parser (>= 2.2.0.pre.3, < 3.0)
+    builder (3.2.2)
+    childprocess (0.5.3)
+      ffi (~> 1.0, >= 1.0.11)
+    cucumber (1.3.16)
+      builder (>= 2.1.2)
+      diff-lcs (>= 1.1.3)
+      gherkin (~> 2.12)
+      multi_json (>= 1.7.5, < 2.0)
+      multi_test (>= 0.1.1)
+    data_magic (0.19)
+      faker (>= 1.1.2)
+      yml_reader (>= 0.3)
+    diff-lcs (1.2.5)
+    domain_name (0.5.20)
+      unf (>= 0.0.5, < 1.0.0)
+    faker (1.4.3)
+      i18n (~> 0.5)
+    faraday (0.9.0)
+      multipart-post (>= 1.2, < 3)
+    faraday-cookie_jar (0.0.6)
+      faraday (>= 0.7.4)
+      http-cookie (~> 1.0.0)
+    ffi (1.9.3)
+    gherkin (2.12.2)
+      multi_json (~> 1.3)
+    headless (1.0.2)
+    http-cookie (1.0.2)
+      domain_name (~> 0.5)
+    i18n (0.6.11)
+    json (1.8.1)
+    mediawiki_api (0.2.1)
+      faraday (~> 0.9, >= 0.9.0)
+      faraday-cookie_jar (~> 0.0, >= 0.0.6)
+    mediawiki_selenium (0.3.2)
+      cucumber (~> 1.3, >= 1.3.10)
+      headless (~> 1.0, >= 1.0.1)
+      json (~> 1.8, >= 1.8.1)
+      mediawiki_api (~> 0.2, >= 0.2.1)
+      page-object (~> 1.0)
+      rest-client (~> 1.6, >= 1.6.7)
+      rspec-expectations (~> 2.14, >= 2.14.4)
+      syntax (~> 1.2, >= 1.2.0)
+    mime-types (2.3)
+    multi_json (1.10.1)
+    multi_test (0.1.1)
+    multipart-post (2.0.0)
+    netrc (0.7.7)
+    page-object (1.0.2)
+      page_navigation (>= 0.9)
+      selenium-webdriver (>= 2.42.0)
+      watir-webdriver (>= 0.6.9)
+    page_navigation (0.9)
+      data_magic (>= 0.14)
+    parser (2.2.0.pre.4)
+      ast (>= 1.1, < 3.0)
+      slop (~> 3.4, >= 3.4.5)
+    powerpack (0.0.9)
+    rainbow (2.0.0)
+    rest-client (1.7.2)
+      mime-types (>= 1.16, < 3.0)
+      netrc (~> 0.7)
+    rspec-expectations (2.99.2)
+      diff-lcs (>= 1.1.3, < 2.0)
+    rubocop (0.26.1)
+      astrolabe (~> 1.3)
+      parser (>= 2.2.0.pre.4, < 3.0)
+      powerpack (~> 0.0.6)
+      rainbow (>= 1.99.1, < 3.0)
+      ruby-progressbar (~> 1.4)
+    ruby-progressbar (1.6.0)
+    rubyzip (1.1.6)
+    selenium-webdriver (2.42.0)
+      childprocess (>= 0.5.0)
+      multi_json (~> 1.0)
+      rubyzip (~> 1.0)
+      websocket (~> 1.0.4)
+    slop (3.6.0)
+    syntax (1.2.0)
+    unf (0.1.4)
+      unf_ext
+    unf_ext (0.0.6)
+    watir-webdriver (0.6.10)
+      selenium-webdriver (>= 2.18.0)
+    websocket (1.0.7)
+    yml_reader (0.3)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  mediawiki_api
+  mediawiki_selenium
+  rubocop
diff --git a/INSTALL b/INSTALL
index e8731a1..70d8d53 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -6,7 +6,7 @@ Starting with MediaWiki 1.2.0, it's possible to install and configure the wiki
 "in-place", as long as you have the necessary prerequisites available.
 
 Required software:
-* Web server with PHP 5.3.2 or higher.
+* Web server with PHP 5.3.3 or higher.
 * A SQL server, the following types are supported
 ** MySQL 5.0.2 or higher
 ** PostgreSQL 8.3 or higher
index f67a9e9..7a088cf 100644 (file)
@@ -16,6 +16,9 @@ production.
   validity must be checked by passing the user-supplied token to
   User::matchEditToken rather than by testing for equality with a
   newly-generated token.
+* (bug 72951) The UserGetLanguageObject hook may be passed any IContextSource
+  for its $context parameter. Formerly it was documented as receiving a
+  RequestContext specifically.
 
 === New features in 1.25 ===
 * (bug 62861) Updated plural rules to CLDR 26. Includes incompatible changes
@@ -50,6 +53,16 @@ production.
   SVG images in Internet Explorer 6 and 7, as they don't support them anyway.
 * (bug 67021) On Special:BookSources, corrected validation of ISBNs (both
   10- and 13-digit forms) containing "X".
+* Page moving was refactored into a MovePage class. As part of that:
+** The AbortMove hook was removed.
+** MovePageIsValidMove is for extensions to specify whether a page
+   cannot be moved for technical reasons, and should not be overriden.
+** MovePageCheckPermissions is for checking whether the given user is
+   allowed to make the move.
+** Title::moveNoAuth() was deprecated. Use the MovePage class instead.
+** Title::moveTo() was deprecated. Use the MovePage class instead.
+** Title::isValidMoveOperation() broken down into MovePage::isValidMove()
+   and MovePage::checkPermissions().
 
 === Action API changes in 1.25 ===
 * (bug 65403) XML tag highlighting is now only performed for formats
@@ -72,6 +85,15 @@ production.
   when a batch of pages is complete.
 * Pretty-printed HTML output now has nicer formatting and (if available)
   better syntax highlighting.
+* Deprecated list=deletedrevs in favor of newly-added prop=deletedrevisions and
+  list=alldeletedrevisions.
+* prop=revisions will gracefully continue when given too many revids or titles,
+  rather than just ignoring the extras.
+* prop=revisions will no longer die if rvcontentformat doesn't match a
+  revision's content model; it will instead warn and omit the content.
+* If the user has the 'deletedhistory' right, action=query's revids parameter
+  will now recognize deleted revids.
+* prop=revisions may be used as a generator, generating revids.
 
 === Action API internal changes in 1.25 ===
 * ApiHelp has been rewritten to support i18n and paginated HTML output.
@@ -96,6 +118,11 @@ production.
   has been removed.
 * ApiFormatBase now always buffers. Output is done when
   ApiFormatBase::closePrinter is called.
+* Much of the logic in ApiQueryRevisions has been split into ApiQueryRevisionsBase.
+* The 'revids' parameter supplied by ApiPageSet will now count deleted
+  revisions as "good" if the user has the 'deletedhistory' right. New methods
+  ApiPageSet::getLiveRevisionIDs() and ApiPageSet::getDeletedRevisionIDs() are
+  provided to access just the live or just the deleted revids.
 * The following methods have been deprecated and may be removed in a future
   release:
   * ApiBase::getDescription
@@ -114,6 +141,9 @@ production.
   * ApiMain::reallyMakeHelpMsg
   * ApiMain::makeHelpMsgHeader
   * ApiQueryImageInfo::getPropertyDescriptions
+* The following classes have been deprecated and may be removed in a future
+  release:
+  * ApiQueryDeletedrevs
 
 === Languages updated in 1.25 ===
 
@@ -146,10 +176,15 @@ changes to languages because of Bugzilla reports.
 * The "temp" zone of the upload respository is now considered private. If it
   already exists (such as under the images/ directory), please make sure that
   the directory is not web readable (e.g. via a .htaccess file).
+* BREAKING CHANGE: In the XML dump format used by Special:Export and
+  dumpBackup.php, the <model> and <format> tags now apprear before the <text>
+  tag, instead of after the <text> and <sha1> tags.
+  The new schema version is 0.10, the new schema URI is
+  <https://www.mediawiki.org/xml/export-0.10.xsd>.
 
 == Compatibility ==
 
-MediaWiki 1.25 requires PHP 5.3.2 or later. There is experimental support for
+MediaWiki 1.25 requires PHP 5.3.3 or later. There is experimental support for
 HHVM 3.3.0.
 
 MySQL is the recommended DBMS. PostgreSQL or SQLite can also be used, but
diff --git a/api.php b/api.php
index 7974f15..74ee775 100644 (file)
--- a/api.php
+++ b/api.php
@@ -34,7 +34,7 @@
 define( 'MW_API', true );
 
 // Bail if PHP is too low
-if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.2' ) < 0 ) {
+if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.3' ) < 0 ) {
        // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
        require dirname( __FILE__ ) . '/includes/PHPVersionError.php';
        wfPHPVersionError( 'api.php' );
index 3b18933..851ad41 100644 (file)
@@ -16,7 +16,7 @@
                "wiki": "https://www.mediawiki.org/"
        },
        "require": {
-               "php": ">=5.3.2",
+               "php": ">=5.3.3",
                "psr/log": "1.0.0"
        },
        "require-dev": {
diff --git a/docs/export-0.10.xsd b/docs/export-0.10.xsd
new file mode 100644 (file)
index 0000000..9d5d49e
--- /dev/null
@@ -0,0 +1,294 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+       This is an XML Schema description of the format
+       output by MediaWiki's Special:Export system.
+
+       Version 0.2 adds optional basic file upload info support,
+       which is used by our OAI export/import submodule.
+
+       Version 0.3 adds some site configuration information such
+       as a list of defined namespaces.
+
+       Version 0.4 adds per-revision delete flags, log exports,
+       discussion threading data, a per-page redirect flag, and
+       per-namespace capitalization.
+
+       Version 0.5 adds byte count per revision.
+
+       Version 0.6 adds a separate namespace tag, and resolves the
+       redirect target and adds a separate sha1 tag for each revision.
+
+       Version 0.7 adds a unique identity constraint for both page and
+       revision identifiers. See also bug 4220.
+       Fix type for <ns> from "positiveInteger" to "nonNegativeInteger" to allow 0
+       Moves <logitem> to its right location.
+       Add parentid to revision.
+       Fix type for <id> within <contributor> to "nonNegativeInteger"
+
+       Version 0.8 adds support for a <model> and a <format> tag for
+       each revision. See contenthandler.txt.
+
+       Version 0.9 adds the database name to the site information.
+
+       Version 0.10 moved the <model> and <format> tags before the <text> tag.
+
+       The canonical URL to the schema document is:
+       http://www.mediawiki.org/xml/export-0.10.xsd
+
+       Use the namespace:
+       http://www.mediawiki.org/xml/export-0.10/
+-->
+<schema xmlns="http://www.w3.org/2001/XMLSchema"
+               xmlns:mw="http://www.mediawiki.org/xml/export-0.10/"
+               targetNamespace="http://www.mediawiki.org/xml/export-0.10/"
+               elementFormDefault="qualified">
+
+       <annotation>
+               <documentation xml:lang="en">
+                       MediaWiki's page export format
+               </documentation>
+       </annotation>
+
+       <!-- Need this to reference xml:lang -->
+       <import namespace="http://www.w3.org/XML/1998/namespace"
+                       schemaLocation="http://www.w3.org/2001/xml.xsd" />
+
+       <!-- Our root element -->
+       <element name="mediawiki" type="mw:MediaWikiType">
+               <!-- Page ID contraint, see bug 4220 -->
+               <unique name="PageIDConstraint">
+                       <selector xpath="mw:page" />
+                       <field xpath="mw:id" />
+               </unique>
+               <!-- Revision ID contraint, see bug 4220 -->
+               <unique name="RevIDConstraint">
+                       <selector xpath="mw:page/mw:revision" />
+                       <field xpath="mw:id" />
+               </unique>
+       </element>
+
+       <complexType name="MediaWikiType">
+               <sequence>
+                       <element name="siteinfo" type="mw:SiteInfoType"
+                                        minOccurs="0" maxOccurs="1" />
+                       <element name="page" type="mw:PageType"
+                                        minOccurs="0" maxOccurs="unbounded" />
+                       <element name="logitem" type="mw:LogItemType"
+                                        minOccurs="0" maxOccurs="unbounded" />
+               </sequence>
+               <attribute name="version" type="string" use="required" />
+               <attribute ref="xml:lang" use="required" />
+       </complexType>
+
+       <complexType name="SiteInfoType">
+               <sequence>
+                       <element name="sitename" type="string" minOccurs="0" />
+            <element name="dbname" type="string" minOccurs="0" />
+                       <element name="base" type="anyURI" minOccurs="0" />
+                       <element name="generator" type="string" minOccurs="0" />
+                       <element name="case" type="mw:CaseType" minOccurs="0" />
+                       <element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
+               </sequence>
+       </complexType>
+
+       <simpleType name="CaseType">
+               <restriction base="NMTOKEN">
+                       <!-- Cannot have two titles differing only by case of first letter. -->
+                       <!-- Default behavior through 1.5, $wgCapitalLinks = true -->
+                       <enumeration value="first-letter" />
+
+                       <!-- Complete title is case-sensitive -->
+                       <!-- Behavior when $wgCapitalLinks = false -->
+                       <enumeration value="case-sensitive" />
+
+                       <!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
+                       <!-- Not yet implemented as of MediaWiki 1.18 -->
+                       <enumeration value="case-insensitive" />
+               </restriction>
+       </simpleType>
+
+       <simpleType name="DeletedFlagType">
+               <restriction base="NMTOKEN">
+                       <enumeration value="deleted" />
+               </restriction>
+       </simpleType>
+
+       <complexType name="NamespacesType">
+               <sequence>
+                       <element name="namespace" type="mw:NamespaceType"
+                                        minOccurs="0" maxOccurs="unbounded" />
+               </sequence>
+       </complexType>
+
+       <complexType name="NamespaceType">
+               <simpleContent>
+                       <extension base="string">
+                               <attribute name="key" type="integer" />
+                               <attribute name="case" type="mw:CaseType" />
+                       </extension>
+               </simpleContent>
+       </complexType>
+
+       <complexType name="RedirectType">
+               <simpleContent>
+                       <extension base="string">
+                               <attribute name="title" type="string" />
+                       </extension>
+               </simpleContent>
+       </complexType>
+
+       <simpleType name="ContentModelType">
+               <restriction base="string">
+                       <pattern value="[a-zA-Z][-+./a-zA-Z0-9]*" />
+               </restriction>
+       </simpleType>
+
+       <simpleType name="ContentFormatType">
+               <restriction base="string">
+                       <pattern value="[a-zA-Z][-+.a-zA-Z0-9]*/[a-zA-Z][-+.a-zA-Z0-9]*" />
+               </restriction>
+       </simpleType>
+
+       <complexType name="PageType">
+               <sequence>
+                       <!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
+                       <element name="title" type="string" />
+
+                       <!-- Namespace in canonical form -->
+                       <element name="ns" type="nonNegativeInteger" />
+
+                       <!-- optional page ID number -->
+                       <element name="id" type="positiveInteger" />
+
+                       <!-- flag if the current revision is a redirect -->
+                       <element name="redirect" type="mw:RedirectType" minOccurs="0" maxOccurs="1" />
+
+                       <!-- comma-separated list of string tokens, if present -->
+                       <element name="restrictions" type="string" minOccurs="0" />
+
+                       <!-- Zero or more sets of revision or upload data -->
+                       <choice minOccurs="0" maxOccurs="unbounded">
+                               <element name="revision" type="mw:RevisionType" />
+                               <element name="upload" type="mw:UploadType" />
+                       </choice>
+
+                       <!-- Zero or One sets of discussion threading data -->
+                       <element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
+               </sequence>
+       </complexType>
+
+       <complexType name="RevisionType">
+               <sequence>
+                       <element name="id" type="positiveInteger" />
+                       <element name="parentid" type="positiveInteger" minOccurs="0" />
+                       <element name="timestamp" type="dateTime" />
+                       <element name="contributor" type="mw:ContributorType" />
+                       <element name="minor" minOccurs="0" maxOccurs="1" />
+                       <element name="comment" type="mw:CommentType" minOccurs="0" maxOccurs="1" />
+                       <element name="model" type="mw:ContentModelType" />
+                       <element name="format" type="mw:ContentFormatType" />
+                       <element name="text" type="mw:TextType" />
+                       <element name="sha1" type="string" />
+               </sequence>
+       </complexType>
+
+       <complexType name="LogItemType">
+               <sequence>
+                       <element name="id" type="positiveInteger" />
+                       <element name="timestamp" type="dateTime" />
+                       <element name="contributor" type="mw:ContributorType" />
+                       <element name="comment" type="mw:CommentType" minOccurs="0" />
+                       <element name="type" type="string" />
+                       <element name="action" type="string" />
+                       <element name="text" type="mw:LogTextType" minOccurs="0" maxOccurs="1" />
+                       <element name="logtitle" type="string" minOccurs="0" maxOccurs="1" />
+                       <element name="params" type="mw:LogParamsType" minOccurs="0" maxOccurs="1" />
+               </sequence>
+       </complexType>
+
+       <complexType name="CommentType">
+               <simpleContent>
+                       <extension base="string">
+                               <!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
+                               <attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
+                       </extension>
+               </simpleContent>
+       </complexType>
+
+       <complexType name="TextType">
+               <simpleContent>
+                       <extension base="string">
+                               <attribute ref="xml:space" use="optional" default="preserve" />
+                               <!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
+                               <attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
+                               <!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
+                               <!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
+                               <!-- usage ([0-9]+) and with the "ID" type. -->
+                               <attribute name="id" type="NMTOKEN" />
+                               <attribute name="bytes" use="optional" type="nonNegativeInteger" />
+                       </extension>
+               </simpleContent>
+       </complexType>
+
+       <complexType name="LogTextType">
+               <simpleContent>
+                       <extension base="string">
+                               <!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
+                               <attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
+                       </extension>
+               </simpleContent>
+       </complexType>
+
+       <complexType name="LogParamsType">
+               <simpleContent>
+                       <extension base="string">
+                               <attribute ref="xml:space" use="optional" default="preserve" />
+                       </extension>
+               </simpleContent>
+       </complexType>
+
+       <complexType name="ContributorType">
+               <sequence>
+                       <element name="username" type="string" minOccurs="0" />
+                       <element name="id" type="nonNegativeInteger" minOccurs="0" />
+
+                       <element name="ip" type="string" minOccurs="0" />
+               </sequence>
+               <!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
+               <attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
+       </complexType>
+
+       <complexType name="UploadType">
+               <sequence>
+                       <!-- Revision-style data... -->
+                       <element name="timestamp" type="dateTime" />
+                       <element name="contributor" type="mw:ContributorType" />
+                       <element name="comment" type="string" minOccurs="0" />
+
+                       <!-- Filename. (Using underscores, not spaces. No 'File:' namespace marker.) -->
+                       <element name="filename" type="string" />
+
+                       <!-- URI at which this resource can be obtained -->
+                       <element name="src" type="anyURI" />
+
+                       <element name="size" type="positiveInteger" />
+
+                       <!-- TODO: add other metadata fields -->
+               </sequence>
+       </complexType>
+
+       <!-- Discussion threading data for LiquidThreads -->
+       <complexType name="DiscussionThreadingInfo">
+               <sequence>
+                       <element name="ThreadSubject" type="string" />
+                       <element name="ThreadParent" type="positiveInteger" />
+                       <element name="ThreadAncestor" type="positiveInteger" />
+                       <element name="ThreadPage" type="string" />
+                       <element name="ThreadID" type="positiveInteger" />
+                       <element name="ThreadAuthor" type="string" />
+                       <element name="ThreadEditStatus" type="string" />
+                       <element name="ThreadType" type="string" />
+               </sequence>
+       </complexType>
+
+</schema>
index fd47d13..e96bd6c 100644 (file)
@@ -1,4 +1,4 @@
-<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.9/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.9/ http://www.mediawiki.org/xml/export-0.9.xsd" version="0.9" xml:lang="en">
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
   
   <!-- Optional global configuration info -->
   <siteinfo>
       </contributor>
       <minor />
       <comment>I have just one thing to say!</comment>
-      <text xml:space="preserve" bytes="25">A bunch of [[text]] here.</text>
-      <sha1>5x0ux8iwjrbmfzgv6pkketxgkcnpr7h</sha1>
       <model>wikitext</model>
       <format>text/x-wiki</format>
+      <text xml:space="preserve" bytes="25">A bunch of [[text]] here.</text>
+      <sha1>5x0ux8iwjrbmfzgv6pkketxgkcnpr7h</sha1>
     </revision>
     
     <revision>
         <ip>10.0.0.2</ip>
       </contributor>
       <comment>new!</comment>
-      <text xml:space="preserve" bytes="24">An earlier [[revision]].</text>
-      <sha1>etaxt3shcge6igz1biwy3d4um2pnle4</sha1>
       <model>wikitext</model>
       <format>text/x-wiki</format>
+      <text xml:space="preserve" bytes="24">An earlier [[revision]].</text>
+      <sha1>etaxt3shcge6igz1biwy3d4um2pnle4</sha1>
     </revision>
   </page>
   
       <timestamp>2001-01-15T14:03:00Z</timestamp>
       <contributor><ip>10.0.0.2</ip></contributor>
       <comment>hey</comment>
-      <text xml:space="preserve" bytes="47">WHYD YOU LOCK PAGE??!!! i was editing that jerk</text>
-      <sha1>ml80vmyjlixdstnywwihx003exfzq9j</sha1>
       <model>wikitext</model>
       <format>text/x-wiki</format>
+      <text xml:space="preserve" bytes="47">WHYD YOU LOCK PAGE??!!! i was editing that jerk</text>
+      <sha1>ml80vmyjlixdstnywwihx003exfzq9j</sha1>
     </revision>
   </page>
   
       <timestamp>2001-01-15T20:34:12Z</timestamp>
       <contributor><username>Foobar</username><id>42</id></contributor>
       <comment>My awesomeest image!</comment>
-      <text xml:space="preserve" bytes="52">This is an awesome little imgae. I lurves it. {{PD}}</text>
-      <sha1>mehom37npwkpzhaiwu3wyr0egalumki</sha1>
       <model>wikitext</model>
       <format>text/x-wiki</format>
+      <text xml:space="preserve" bytes="52">This is an awesome little imgae. I lurves it. {{PD}}</text>
+      <sha1>mehom37npwkpzhaiwu3wyr0egalumki</sha1>
     </revision>
     <upload>
       <timestamp>2001-01-15T20:34:12Z</timestamp>
index b71c347..18b7099 100644 (file)
@@ -260,13 +260,6 @@ $password: the password being submitted, not yet checked for validity
           a machine API rather than the HTML user interface.
 &$msg: the message identifier for abort reason (new in 1.18, not available before 1.18)
 
-'AbortMove': Allows to abort moving an article (title).
-$old: old title
-$nt: new title
-$user: user who is doing the move
-$err: error message
-$reason: the reason for the move (added in 1.13)
-
 'AbortNewAccount': Return false to cancel explicit account creation.
 $user: the User object about to be created (read-only, incomplete)
 &$msg: out parameter: HTML to display on abort
@@ -1835,6 +1828,18 @@ $db: The database object to be queried.
 &$opts: Options for the query.
 &$join_conds: Join conditions for the query.
 
+'MovePageCheckPermissions': Specify whether the user is allowed to move the page.
+$oldTitle: Title object of the current (old) location
+$newTitle: Title object of the new location
+$user: User making the move
+$reason: string of the reason provided by the user
+$status: Status object to pass error messages to
+
+'MovePageIsValidMove': Specify whether a page can be moved for technical reasons.
+$oldTitle: Title object of the current (old) location
+$newTitle: Title object of the new location
+$status: Status object to pass error messages to
+
 'BaseTemplateToolbox': Called by BaseTemplate when building the $toolbox array
 and returning it for the skin to output. You can add items to the toolbox while
 still letting the skin make final decisions on skin-specific markup conventions
@@ -2867,7 +2872,7 @@ $user: User object
 'UserGetLanguageObject': Called when getting user's interface language object.
 $user: User object
 &$code: Language code that will be used to create the object
-$context: RequestContext object
+$context: IContextSource object
 
 'UserGetReservedNames': Allows to modify $wgReservedUsernames at run time.
 &$reservedUsernames: $wgReservedUsernames
@@ -3000,6 +3005,11 @@ when UserMailer sends an email, with a bounce handling extension.
 $to: Array of MailAddress objects for the recipients
 &$returnPath: The return address string
 
+'LoginFormValidErrorMessages': Called in LoginForm when a function gets valid error
+messages. Allows to add additional error messages (except messages already in
+LoginForm::$validErrorMessages).
+&$messages Already added messages (inclusive messages from LoginForm::$validErrorMessages)
+
 'WantedPages::getQueryInfo': Called in WantedPagesPage::getQueryInfo(), can be
 used to alter the SQL query which gets the list of wanted pages.
 &$wantedPages: WantedPagesPage object
index 0895873..f0aa116 100644 (file)
@@ -262,6 +262,7 @@ $wgAutoloadLocalClasses = array(
        'ApiPurge' => 'includes/api/ApiPurge.php',
        'ApiQuery' => 'includes/api/ApiQuery.php',
        'ApiQueryAllCategories' => 'includes/api/ApiQueryAllCategories.php',
+       'ApiQueryAllDeletedRevisions' => 'includes/api/ApiQueryAllDeletedRevisions.php',
        'ApiQueryAllImages' => 'includes/api/ApiQueryAllImages.php',
        'ApiQueryAllLinks' => 'includes/api/ApiQueryAllLinks.php',
        'ApiQueryAllMessages' => 'includes/api/ApiQueryAllMessages.php',
@@ -276,6 +277,7 @@ $wgAutoloadLocalClasses = array(
        'ApiQueryCategoryMembers' => 'includes/api/ApiQueryCategoryMembers.php',
        'ApiQueryContributions' => 'includes/api/ApiQueryUserContributions.php',
        'ApiQueryContributors' => 'includes/api/ApiQueryContributors.php',
+       'ApiQueryDeletedRevisions' => 'includes/api/ApiQueryDeletedRevisions.php',
        'ApiQueryDeletedrevs' => 'includes/api/ApiQueryDeletedrevs.php',
        'ApiQueryDisabled' => 'includes/api/ApiQueryDisabled.php',
        'ApiQueryDuplicateFiles' => 'includes/api/ApiQueryDuplicateFiles.php',
@@ -303,6 +305,7 @@ $wgAutoloadLocalClasses = array(
        'ApiQueryRecentChanges' => 'includes/api/ApiQueryRecentChanges.php',
        'ApiQueryFileRepoInfo' => 'includes/api/ApiQueryFileRepoInfo.php',
        'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
+       'ApiQueryRevisionsBase' => 'includes/api/ApiQueryRevisionsBase.php',
        'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php',
        'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php',
        'ApiQueryStashImageInfo' => 'includes/api/ApiQueryStashImageInfo.php',
@@ -868,7 +871,6 @@ $wgAutoloadLocalClasses = array(
 
        # includes/profiler
        'Profiler' => 'includes/profiler/Profiler.php',
-       'ProfilerMwprof' => 'includes/profiler/ProfilerMwprof.php',
        'ProfilerSimpleDB' => 'includes/profiler/ProfilerSimpleDB.php',
        'ProfilerSimpleText' => 'includes/profiler/ProfilerSimpleText.php',
        'ProfilerSimpleTrace' => 'includes/profiler/ProfilerSimpleTrace.php',
index cfc8438..29d653d 100644 (file)
@@ -7297,13 +7297,21 @@ $wgPagePropsHaveSortkey = true;
 $wgHttpsPort = 443;
 
 /**
- * Secret and algorithm for hmac-based key derivation function (fast,
+ * Secret for hmac-based key derivation function (fast,
  * cryptographically secure random numbers).
  * This should be set in LocalSettings.php, otherwise wgSecretKey will
  * be used.
+ * See also: $wgHKDFAlgorithm
  * @since 1.24
  */
 $wgHKDFSecret = false;
+
+/**
+ * Algorithm for hmac-based key derivation function (fast,
+ * cryptographically secure random numbers).
+ * See also: $wgHKDFSecret
+ * @since 1.24
+ */
 $wgHKDFAlgorithm = 'sha256';
 
 /**
index dee0a86..840e723 100644 (file)
@@ -69,7 +69,7 @@ class WikiExporter {
         * @return string
         */
        public static function schemaVersion() {
-               return "0.9";
+               return "0.10";
        }
 
        /**
@@ -693,6 +693,9 @@ class XmlDumpWriter {
                        $content_format = $content_handler->getDefaultFormat();
                }
 
+               $out .= "      " . Xml::element( 'model', null, strval( $content_model ) ) . "\n";
+               $out .= "      " . Xml::element( 'format', null, strval( $content_format ) ) . "\n";
+
                $text = '';
                if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_TEXT ) ) {
                        $out .= "      " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
@@ -719,9 +722,6 @@ class XmlDumpWriter {
                        $out .= "      <sha1/>\n";
                }
 
-               $out .= "      " . Xml::element( 'model', null, strval( $content_model ) ) . "\n";
-               $out .= "      " . Xml::element( 'format', null, strval( $content_format ) ) . "\n";
-
                wfRunHooks( 'XmlDumpWriterWriteRevision', array( &$this, &$out, $row, $text ) );
 
                $out .= "    </revision>\n";
index 55e6e27..67c1586 100644 (file)
@@ -30,7 +30,7 @@ if ( !defined( 'MEDIAWIKI' ) ) {
 /**
  * Compatibility functions
  *
- * We support PHP 5.3.2 and up.
+ * We support PHP 5.3.3 and up.
  * Re-implementations of newer functions or functions in non-standard
  * PHP extensions may be included here.
  */
@@ -3710,7 +3710,7 @@ function wfWaitForSlaves(
        // Figure out which clusters need to be checked
        $lbs = array();
        if ( $cluster === '*' ) {
-               wfGetLBFactory()->forEachLB( function( LoadBalancer $lb ) use ( &$lbs ) {
+               wfGetLBFactory()->forEachLB( function ( LoadBalancer $lb ) use ( &$lbs ) {
                        $lbs[] = $lb;
                } );
        } elseif ( $cluster !== false ) {
@@ -3730,7 +3730,9 @@ function wfWaitForSlaves(
                        if ( $ifWritesSince && !$lb->hasMasterConnection() ) {
                                continue; // assume no writes done
                        }
-                       $dbw = $lb->getConnection( DB_MASTER, array(), $wiki );
+                       // Use the empty string to not trigger selectDB() since the connection
+                       // may have been to a server that does not have a DB for the current wiki.
+                       $dbw = $lb->getConnection( DB_MASTER, array(), '' );
                        if ( $ifWritesSince && $dbw->lastDoneWrites() < $ifWritesSince ) {
                                continue; // no writes since the last wait
                        }
index 5319076..4eb8e97 100644 (file)
@@ -37,13 +37,21 @@ class WikiImporter {
        private $mNoticeCallback, $mDebug;
        private $mImportUploads, $mImageBasePath;
        private $mNoUpdates = false;
+       /** @var Config */
+       private $config;
 
        /**
         * Creates an ImportXMLReader drawing from the source provided
         * @param ImportStreamSource $source
+        * @param Config $config
         */
-       function __construct( ImportStreamSource $source ) {
+       function __construct( ImportStreamSource $source, Config $config = null ) {
                $this->reader = new XMLReader();
+               if ( !$config ) {
+                       wfDeprecated( __METHOD__ . ' without a Config instance', '1.25' );
+                       $config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
+               }
+               $this->config = $config;
 
                if ( !in_array( 'uploadsource', stream_get_wrappers() ) ) {
                        stream_wrapper_register( 'uploadsource', 'UploadSourceAdapter' );
@@ -536,7 +544,7 @@ class WikiImporter {
         * @return bool|mixed
         */
        private function processLogItem( $logInfo ) {
-               $revision = new WikiRevision;
+               $revision = new WikiRevision( $this->config );
 
                $revision->setID( $logInfo['id'] );
                $revision->setType( $logInfo['type'] );
@@ -670,7 +678,7 @@ class WikiImporter {
         * @return bool|mixed
         */
        private function processRevision( $pageInfo, $revisionInfo ) {
-               $revision = new WikiRevision;
+               $revision = new WikiRevision( $this->config );
 
                if ( isset( $revisionInfo['id'] ) ) {
                        $revision->setID( $revisionInfo['id'] );
@@ -786,7 +794,7 @@ class WikiImporter {
         * @return mixed
         */
        private function processUpload( $pageInfo, $uploadInfo ) {
-               $revision = new WikiRevision;
+               $revision = new WikiRevision( $this->config );
                $text = isset( $uploadInfo['text'] ) ? $uploadInfo['text'] : '';
 
                $revision->setTitle( $pageInfo['_title'] );
@@ -847,8 +855,6 @@ class WikiImporter {
         * @return array|bool
         */
        private function processTitle( $text ) {
-               global $wgCommandLineMode;
-
                $workTitle = $text;
                $origTitle = Title::newFromText( $workTitle );
 
@@ -864,6 +870,7 @@ class WikiImporter {
                        $title = Title::newFromText( $workTitle );
                }
 
+               $commandLineMode = $this->config->get( 'CommandLineMode' );
                if ( is_null( $title ) ) {
                        # Invalid page title? Ignore the page
                        $this->notice( 'import-error-invalid', $workTitle );
@@ -874,11 +881,11 @@ class WikiImporter {
                } elseif ( !$title->canExist() ) {
                        $this->notice( 'import-error-special', $title->getPrefixedText() );
                        return false;
-               } elseif ( !$title->userCan( 'edit' ) && !$wgCommandLineMode ) {
+               } elseif ( !$title->userCan( 'edit' ) && !$commandLineMode ) {
                        # Do not import if the importing wiki user cannot edit this page
                        $this->notice( 'import-error-edit', $title->getPrefixedText() );
                        return false;
-               } elseif ( !$title->exists() && !$title->userCan( 'create' ) && !$wgCommandLineMode ) {
+               } elseif ( !$title->exists() && !$title->userCan( 'create' ) && !$commandLineMode ) {
                        # Do not import if the importing wiki user cannot create this page
                        $this->notice( 'import-error-create', $title->getPrefixedText() );
                        return false;
@@ -1093,6 +1100,13 @@ class WikiRevision {
        /** @var bool */
        private $mNoUpdates = false;
 
+       /** @var Config $config */
+       private $config;
+
+       public function __construct( Config $config ) {
+               $this->config = $config;
+       }
+
        /**
         * @param Title $title
         * @throws MWException
@@ -1608,8 +1622,7 @@ class WikiRevision {
         * @return bool|string
         */
        function downloadSource() {
-               global $wgEnableUploads;
-               if ( !$wgEnableUploads ) {
+               if ( !$this->config->get( 'EnableUploads' ) ) {
                        return false;
                }
 
index 17cb8aa..c3fb486 100644 (file)
@@ -19,7 +19,7 @@ class MediaWikiVersionFetcher {
                $defaultSettings = file_get_contents( __DIR__ . '/DefaultSettings.php' );
 
                $matches = array();
-               preg_match( "/wgVersion = '([0-9a-zA-Z\.]+)';/", $defaultSettings, $matches );
+               preg_match( "/wgVersion = '([0-9a-zA-Z\.\-]+)';/", $defaultSettings, $matches );
 
                if ( count( $matches ) !== 2 ) {
                        throw new RuntimeException( 'Could not extract the MediaWiki version from DefaultSettings.php' );
index 79095e9..994be91 100644 (file)
@@ -42,6 +42,52 @@ class MovePage {
                $this->newTitle = $newTitle;
        }
 
+       public function checkPermissions( User $user, $reason ) {
+               $status = new Status();
+
+               $errors = wfMergeErrorArrays(
+                       $this->oldTitle->getUserPermissionsErrors( 'move', $user ),
+                       $this->oldTitle->getUserPermissionsErrors( 'edit', $user ),
+                       $this->newTitle->getUserPermissionsErrors( 'move-target', $user ),
+                       $this->newTitle->getUserPermissionsErrors( 'edit', $user )
+               );
+
+               // Convert into a Status object
+               if ( $errors ) {
+                       foreach ( $errors as $error ) {
+                               call_user_func_array( array( $status, 'fatal' ), $error );
+                       }
+               }
+
+               if ( EditPage::matchSummarySpamRegex( $reason ) !== false ) {
+                       // This is kind of lame, won't display nice
+                       $status->fatal( 'spamprotectiontext' );
+               }
+
+               # The move is allowed only if (1) the target doesn't exist, or
+               # (2) the target is a redirect to the source, and has no history
+               # (so we can undo bad moves right after they're done).
+
+               if ( $this->newTitle->getArticleID() ) { # Target exists; check for validity
+                       if ( !$this->isValidMoveTarget() ) {
+                               $status->fatal( 'articleexists' );
+                       }
+               } else {
+                       $tp = $this->newTitle->getTitleProtection();
+                       if ( $tp !== false ) {
+                               if ( !$user->isAllowed( $tp['permission'] ) ) {
+                                       $status->fatal( 'cantmove-titleprotected' );
+                               }
+                       }
+               }
+
+               wfRunHooks( 'MovePageCheckPermissions',
+                       array( $this->oldTitle, $this->newTitle, $user, $reason, $status )
+               );
+
+               return $status;
+       }
+
        /**
         * Does various sanity checks that the move is
         * valid. Only things based on the two titles
@@ -99,6 +145,9 @@ class MovePage {
                        $status->fatal( 'nonfile-cannot-move-to-file' );
                }
 
+               // Hook for extensions to say a title can't be moved for technical reasons
+               wfRunHooks( 'MovePageIsValidMove', array( $this->oldTitle, $this->newTitle, $status ) );
+
                return $status;
        }
 
@@ -126,6 +175,53 @@ class MovePage {
                return $status;
        }
 
+       /**
+        * Checks if $this can be moved to a given Title
+        * - Selects for update, so don't call it unless you mean business
+        *
+        * @since 1.25
+        * @return bool
+        */
+       protected function isValidMoveTarget() {
+               # Is it an existing file?
+               if ( $this->newTitle->inNamespace( NS_FILE ) ) {
+                       $file = wfLocalFile( $this->newTitle );
+                       if ( $file->exists() ) {
+                               wfDebug( __METHOD__ . ": file exists\n" );
+                               return false;
+                       }
+               }
+               # Is it a redirect with no history?
+               if ( !$this->newTitle->isSingleRevRedirect() ) {
+                       wfDebug( __METHOD__ . ": not a one-rev redirect\n" );
+                       return false;
+               }
+               # Get the article text
+               $rev = Revision::newFromTitle( $this->newTitle, false, Revision::READ_LATEST );
+               if ( !is_object( $rev ) ) {
+                       return false;
+               }
+               $content = $rev->getContent();
+               # Does the redirect point to the source?
+               # Or is it a broken self-redirect, usually caused by namespace collisions?
+               $redirTitle = $content ? $content->getRedirectTarget() : null;
+
+               if ( $redirTitle ) {
+                       if ( $redirTitle->getPrefixedDBkey() !== $this->oldTitle->getPrefixedDBkey() &&
+                               $redirTitle->getPrefixedDBkey() !== $this->newTitle->getPrefixedDBkey() ) {
+                               wfDebug( __METHOD__ . ": redirect points to other page\n" );
+                               return false;
+                       } else {
+                               return true;
+                       }
+               } else {
+                       # Fail safe (not a redirect after all. strange.)
+                       wfDebug( __METHOD__ . ": failsafe: database says " . $this->newTitle->getPrefixedDBkey() .
+                               " is a redirect, but it doesn't contain a valid redirect.\n" );
+                       return false;
+               }
+       }
+
        /**
         * @param User $user
         * @param string $reason
@@ -135,6 +231,8 @@ class MovePage {
        public function move( User $user, $reason, $createRedirect ) {
                global $wgCategoryCollation;
 
+               wfRunHooks( 'TitleMove', array( $this->oldTitle, $this->newTitle, $user ) );
+
                // If it is a file, move it first.
                // It is done before all other moving stuff is done because it's hard to revert.
                $dbw = wfGetDB( DB_MASTER );
@@ -274,7 +372,6 @@ class MovePage {
 
                wfRunHooks( 'TitleMoveComplete', array( &$this->oldTitle, &$this->newTitle, &$user, $pageid, $redirid, $reason ) );
                return Status::newGood();
-
        }
 
        /**
@@ -342,6 +439,9 @@ class MovePage {
 
                $dbw = wfGetDB( DB_MASTER );
 
+               $oldpage = WikiPage::factory( $this->oldTitle );
+               $oldcountable = $oldpage->isCountable();
+
                $newpage = WikiPage::factory( $nt );
 
                if ( $moveOverRedirect ) {
@@ -389,7 +489,8 @@ class MovePage {
                wfRunHooks( 'NewRevisionFromEditComplete',
                        array( $newpage, $nullRevision, $nullRevision->getParentId(), $user ) );
 
-               $newpage->doEditUpdates( $nullRevision, $user, array( 'changed' => false ) );
+               $newpage->doEditUpdates( $nullRevision, $user,
+                       array( 'changed' => false, 'moved' => true, 'oldcountable' => $oldcountable ) );
 
                if ( !$moveOverRedirect ) {
                        WikiPage::onArticleCreate( $nt );
index 5af818f..b061990 100644 (file)
@@ -40,7 +40,7 @@
  */
 function wfPHPVersionError( $type ) {
        $mwVersion = '1.25';
-       $minimumVersionPHP = '5.3.2';
+       $minimumVersionPHP = '5.3.3';
 
        $phpVersion = PHP_VERSION;
        $protocol = isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
index 0efc94e..b97d36a 100644 (file)
@@ -2232,19 +2232,13 @@ class Title {
                } elseif ( $action == 'create' ) {
                        $title_protection = $this->getTitleProtection();
                        if ( $title_protection ) {
-                               if ( $title_protection['pt_create_perm'] == 'sysop' ) {
-                                       $title_protection['pt_create_perm'] = 'editprotected'; // B/C
-                               }
-                               if ( $title_protection['pt_create_perm'] == 'autoconfirmed' ) {
-                                       $title_protection['pt_create_perm'] = 'editsemiprotected'; // B/C
-                               }
-                               if ( $title_protection['pt_create_perm'] == ''
-                                       || !$user->isAllowed( $title_protection['pt_create_perm'] )
+                               if ( $title_protection['permission'] == ''
+                                       || !$user->isAllowed( $title_protection['permission'] )
                                ) {
                                        $errors[] = array(
                                                'titleprotected',
-                                               User::whoIs( $title_protection['pt_user'] ),
-                                               $title_protection['pt_reason']
+                                               User::whoIs( $title_protection['user'] ),
+                                               $title_protection['reason']
                                        );
                                }
                        }
@@ -2536,7 +2530,7 @@ class Title {
         * @return array|bool An associative array representing any existent title
         *   protection, or false if there's none.
         */
-       private function getTitleProtection() {
+       public function getTitleProtection() {
                // Can't protect pages in special namespaces
                if ( $this->getNamespace() < 0 ) {
                        return false;
@@ -2551,13 +2545,27 @@ class Title {
                        $dbr = wfGetDB( DB_SLAVE );
                        $res = $dbr->select(
                                'protected_titles',
-                               array( 'pt_user', 'pt_reason', 'pt_expiry', 'pt_create_perm' ),
+                               array(
+                                       'user' => 'pt_user',
+                                       'reason' => 'pt_reason',
+                                       'expiry' => 'pt_expiry',
+                                       'permission' => 'pt_create_perm'
+                               ),
                                array( 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ),
                                __METHOD__
                        );
 
                        // fetchRow returns false if there are no rows.
-                       $this->mTitleProtection = $dbr->fetchRow( $res );
+                       $row = $dbr->fetchRow( $res );
+                       if ( $row ) {
+                               if ( $row['permission'] == 'sysop' ) {
+                                       $row['permission'] = 'editprotected'; // B/C
+                               }
+                               if ( $row['permission'] == 'autoconfirmed' ) {
+                                       $row['permission'] = 'editsemiprotected'; // B/C
+                               }
+                       }
+                       $this->mTitleProtection = $row;
                }
                return $this->mTitleProtection;
        }
@@ -2978,12 +2986,12 @@ class Title {
 
                                if ( $title_protection ) {
                                        $now = wfTimestampNow();
-                                       $expiry = $wgContLang->formatExpiry( $title_protection['pt_expiry'], TS_MW );
+                                       $expiry = $wgContLang->formatExpiry( $title_protection['expiry'], TS_MW );
 
                                        if ( !$expiry || $expiry > $now ) {
                                                // Apply the restrictions
                                                $this->mRestrictionsExpiry['create'] = $expiry;
-                                               $this->mRestrictions['create'] = explode( ',', trim( $title_protection['pt_create_perm'] ) );
+                                               $this->mRestrictions['create'] = explode( ',', trim( $title_protection['permission'] ) );
                                        } else { // Get rid of the old restrictions
                                                Title::purgeExpiredRestrictions();
                                                $this->mTitleProtection = false;
@@ -3579,10 +3587,12 @@ class Title {
        /**
         * Move this page without authentication
         *
+        * @deprecated since 1.25 use MovePage class instead
         * @param Title $nt The new page Title
         * @return array|bool True on success, getUserPermissionsErrors()-like array on failure
         */
        public function moveNoAuth( &$nt ) {
+               wfDeprecated( __METHOD__, '1.25' );
                return $this->moveTo( $nt, false );
        }
 
@@ -3590,10 +3600,9 @@ class Title {
         * Check whether a given move operation would be valid.
         * Returns true if ok, or a getUserPermissionsErrors()-like array otherwise
         *
-        * @todo finish moving this into MovePage
+        * @deprecated since 1.25, use MovePage's methods instead
         * @param Title $nt The new title
-        * @param bool $auth Indicates whether $wgUser's permissions
-        *  should be checked
+        * @param bool $auth Ignored
         * @param string $reason Is the log summary of the move, used for spam checking
         * @return array|bool True on success, getUserPermissionsErrors()-like array on failure
         */
@@ -3607,54 +3616,12 @@ class Title {
                }
 
                $mp = new MovePage( $this, $nt );
-               $errors = $mp->isValidMove()->getErrorsArray();
-
-               $newid = $nt->getArticleID();
-
-               if ( $auth ) {
-                       $errors = wfMergeErrorArrays( $errors,
-                               $this->getUserPermissionsErrors( 'move', $wgUser ),
-                               $this->getUserPermissionsErrors( 'edit', $wgUser ),
-                               $nt->getUserPermissionsErrors( 'move-target', $wgUser ),
-                               $nt->getUserPermissionsErrors( 'edit', $wgUser ) );
-               }
-
-               $match = EditPage::matchSummarySpamRegex( $reason );
-               if ( $match !== false ) {
-                       // This is kind of lame, won't display nice
-                       $errors[] = array( 'spamprotectiontext' );
-               }
-
-               $err = null;
-               if ( !wfRunHooks( 'AbortMove', array( $this, $nt, $wgUser, &$err, $reason ) ) ) {
-                       $errors[] = array( 'hookaborted', $err );
-               }
-
-               # The move is allowed only if (1) the target doesn't exist, or
-               # (2) the target is a redirect to the source, and has no history
-               # (so we can undo bad moves right after they're done).
+               $errors = wfMergeErrorArrays(
+                       $mp->isValidMove()->getErrorsArray(),
+                       $mp->checkPermissions( $wgUser, $reason )->getErrorsArray()
+               );
 
-               if ( 0 != $newid ) { # Target exists; check for validity
-                       if ( !$this->isValidMoveTarget( $nt ) ) {
-                               $errors[] = array( 'articleexists' );
-                       }
-               } else {
-                       $tp = $nt->getTitleProtection();
-                       $right = $tp['pt_create_perm'];
-                       if ( $right == 'sysop' ) {
-                               $right = 'editprotected'; // B/C
-                       }
-                       if ( $right == 'autoconfirmed' ) {
-                               $right = 'editsemiprotected'; // B/C
-                       }
-                       if ( $tp && !$wgUser->isAllowed( $right ) ) {
-                               $errors[] = array( 'cantmove-titleprotected' );
-                       }
-               }
-               if ( empty( $errors ) ) {
-                       return true;
-               }
-               return $errors;
+               return $errors ? : true;
        }
 
        /**
@@ -3679,7 +3646,7 @@ class Title {
        /**
         * Move a title to a new location
         *
-        * @todo Deprecate this in favor of MovePage
+        * @deprecated since 1.25, use the MovePage class instead
         * @param Title $nt The new title
         * @param bool $auth Indicates whether $wgUser's permissions
         *  should be checked
@@ -3701,8 +3668,6 @@ class Title {
                        $createRedirect = true;
                }
 
-               wfRunHooks( 'TitleMove', array( $this, $nt, $wgUser ) );
-
                $mp = new MovePage( $this, $nt );
                $status = $mp->move( $wgUser, $reason, $createRedirect );
                if ( $status->isOK() ) {
@@ -3838,7 +3803,7 @@ class Title {
         * Checks if $this can be moved to a given Title
         * - Selects for update, so don't call it unless you mean business
         *
-        * @todo move to MovePage
+        * @deprecated since 1.25, use MovePage's methods instead
         * @param Title $nt The new title to check
         * @return bool
         */
index 58bd68d..3f84f2a 100644 (file)
@@ -79,7 +79,7 @@ abstract class ApiBase extends ContextSource {
        // of arrays, with the first member being the 'tag' for the info and the
        // remaining members being the values. In the help, this is formatted using
        // apihelp-{$path}-paraminfo-{$tag}, which is passed $1 = count, $2 =
-       // comma-joined list of values.
+       // comma-joined list of values, $3 = module prefix.
        const PARAM_HELP_MSG_INFO = 12;
        /// @since 1.25
        // When PARAM_DFLT is an array, this may be an array mapping those values
index 230a340..913b8eb 100644 (file)
@@ -164,8 +164,8 @@ abstract class ApiFormatBase extends ApiBase {
                        $out->setPageTitle( $context->msg( 'api-format-title' ) );
 
                        $header = $context->msg( 'api-format-prettyprint-header' )
-                          ->params( $format, strtolower( $format ) )
-                          ->parseAsBlock();
+                               ->params( $format, strtolower( $format ) )
+                               ->parseAsBlock();
                        $out->addHTML(
                                Html::rawElement( 'div', array( 'class' => 'api-pretty-header' ),
                                        ApiHelp::fixHelpLinks( $header )
index 2ac246b..b33b087 100644 (file)
@@ -376,6 +376,7 @@ class ApiHelp extends ApiBase {
                                                        $info[] = $context->msg( "apihelp-{$path}-paraminfo-{$tag}" )
                                                                ->numParams( count( $i ) )
                                                                ->params( $context->getLanguage()->commaList( $i ) )
+                                                               ->params( $module->getModulePrefix() )
                                                                ->parse();
                                                }
                                        }
@@ -528,11 +529,11 @@ class ApiHelp extends ApiBase {
                                                        ->parse();
                                        }
 
-                                       if ( !$description && !$info ) {
-                                               $description[] = self::wrap(
+                                       if ( !array_filter( $description ) ) {
+                                               $description = array( self::wrap(
                                                        $context->msg( 'api-help-param-no-description' ),
                                                        'apihelp-empty'
-                                               );
+                                               ) );
                                        }
 
                                        // Add "deprecated" flag
index b2febde..a134074 100644 (file)
@@ -60,7 +60,7 @@ class ApiImport extends ApiBase {
                        $this->dieStatus( $source );
                }
 
-               $importer = new WikiImporter( $source->value );
+               $importer = new WikiImporter( $source->value, $this->getConfig() );
                if ( isset( $params['namespace'] ) ) {
                        $importer->setTargetNamespace( $params['namespace'] );
                }
index 472f753..f7588a3 100644 (file)
@@ -310,7 +310,7 @@ class ApiMain extends ApiBase {
                        // then there's an appropriate Vary header set by whatever set
                        // their non-default language.
                        wfDebug( __METHOD__ . ": downgrading cache mode 'public' to " .
-                          "'anon-public-user-private' due to uselang=user\n" );
+                               "'anon-public-user-private' due to uselang=user\n" );
                        $mode = 'anon-public-user-private';
                }
 
index 864582d..c7f40c7 100644 (file)
@@ -72,9 +72,9 @@ class ApiMove extends ApiBase {
 
                // Move the page
                $toTitleExists = $toTitle->exists();
-               $retval = $fromTitle->moveTo( $toTitle, true, $params['reason'], !$params['noredirect'] );
-               if ( $retval !== true ) {
-                       $this->dieUsageMsg( reset( $retval ) );
+               $status = $this->movePage( $fromTitle, $toTitle, $params['reason'], !$params['noredirect'] );
+               if ( !$status->isOK() ) {
+                       $this->dieStatus( $status );
                }
 
                $r = array(
@@ -99,8 +99,8 @@ class ApiMove extends ApiBase {
                // Move the talk page
                if ( $params['movetalk'] && $fromTalk->exists() && !$fromTitle->isTalkPage() ) {
                        $toTalkExists = $toTalk->exists();
-                       $retval = $fromTalk->moveTo( $toTalk, true, $params['reason'], !$params['noredirect'] );
-                       if ( $retval === true ) {
+                       $status = $this->movePage( $fromTalk, $toTalk, $params['reason'], !$params['noredirect'] );
+                       if ( $status->isOK() ) {
                                $r['talkfrom'] = $fromTalk->getPrefixedText();
                                $r['talkto'] = $toTalk->getPrefixedText();
                                if ( $toTalkExists ) {
@@ -108,9 +108,9 @@ class ApiMove extends ApiBase {
                                }
                        } else {
                                // We're not gonna dieUsage() on failure, since we already changed something
-                               $parsed = $this->parseMsg( reset( $retval ) );
-                               $r['talkmove-error-code'] = $parsed['code'];
-                               $r['talkmove-error-info'] = $parsed['info'];
+                               $error = $this->getErrorFromStatus( $status );
+                               $r['talkmove-error-code'] = $error[0];
+                               $r['talkmove-error-info'] = $error[1];
                        }
                }
 
@@ -147,6 +147,28 @@ class ApiMove extends ApiBase {
                $result->addValue( null, $this->getModuleName(), $r );
        }
 
+       /**
+        * @param Title $from
+        * @param Title $to
+        * @param string $reason
+        * @param bool $createRedirect
+        * @return Status
+        */
+       protected function movePage( Title $from, Title $to, $reason, $createRedirect ) {
+               $mp = new MovePage( $from, $to );
+               $valid = $mp->isValidMove();
+               if ( !$valid->isOK() ) {
+                       return $valid;
+               }
+
+               $permStatus = $mp->checkPermissions( $this->getUser(), $reason );
+               if ( !$permStatus->isOK() ) {
+                       return $permStatus;
+               }
+
+               return $mp->move( $this->getUser(), $reason, $createRedirect );
+       }
+
        /**
         * @param Title $fromTitle
         * @param Title $toTitle
index ab705b2..ea85cac 100644 (file)
@@ -68,6 +68,8 @@ class ApiPageSet extends ApiBase {
        private $mPendingRedirectIDs = array();
        private $mConvertedTitles = array();
        private $mGoodRevIDs = array();
+       private $mLiveRevIDs = array();
+       private $mDeletedRevIDs = array();
        private $mMissingRevIDs = array();
        private $mFakePageId = -1;
        private $mCacheMode = 'public';
@@ -596,13 +598,29 @@ class ApiPageSet extends ApiBase {
        }
 
        /**
-        * Get the list of revision IDs (requested with the revids= parameter)
+        * Get the list of valid revision IDs (requested with the revids= parameter)
         * @return array Array of revID (int) => pageID (int)
         */
        public function getRevisionIDs() {
                return $this->mGoodRevIDs;
        }
 
+       /**
+        * Get the list of non-deleted revision IDs (requested with the revids= parameter)
+        * @return array Array of revID (int) => pageID (int)
+        */
+       public function getLiveRevisionIDs() {
+               return $this->mLiveRevIDs;
+       }
+
+       /**
+        * Get the list of revision IDs that were associated with deleted titles.
+        * @return array Array of revID (int) => pageID (int)
+        */
+       public function getDeletedRevisionIDs() {
+               return $this->mDeletedRevIDs;
+       }
+
        /**
         * Revision IDs that were not found in the database
         * @return array Array of revision IDs
@@ -669,6 +687,10 @@ class ApiPageSet extends ApiBase {
 
        /**
         * Populate this PageSet from a rowset returned from the database
+        *
+        * Note that the query result must include the columns returned by
+        * $this->getPageTableFields().
+        *
         * @param DatabaseBase $db
         * @param ResultWrapper $queryResult Query result object
         */
@@ -897,6 +919,7 @@ class ApiPageSet extends ApiBase {
                                $revid = intval( $row->rev_id );
                                $pageid = intval( $row->rev_page );
                                $this->mGoodRevIDs[$revid] = $pageid;
+                               $this->mLiveRevIDs[$revid] = $pageid;
                                $pageids[$pageid] = '';
                                unset( $remaining[$revid] );
                        }
@@ -907,6 +930,51 @@ class ApiPageSet extends ApiBase {
 
                // Populate all the page information
                $this->initFromPageIds( array_keys( $pageids ) );
+
+               // If the user can see deleted revisions, pull out the corresponding
+               // titles from the archive table and include them too. We ignore
+               // ar_page_id because deleted revisions are tied by title, not page_id.
+               if ( !empty( $this->mMissingRevIDs ) && $this->getUser()->isAllowed( 'deletedhistory' ) ) {
+                       $remaining = array_flip( $this->mMissingRevIDs );
+                       $tables = array( 'archive' );
+                       $fields = array( 'ar_rev_id', 'ar_namespace', 'ar_title' );
+                       $where = array( 'ar_rev_id' => $this->mMissingRevIDs );
+
+                       $this->profileDBIn();
+                       $res = $db->select( $tables, $fields, $where, __METHOD__ );
+                       $titles = array();
+                       foreach ( $res as $row ) {
+                               $revid = intval( $row->ar_rev_id );
+                               $titles[$revid] = Title::makeTitle( $row->ar_namespace, $row->ar_title );
+                               unset( $remaining[$revid] );
+                       }
+                       $this->profileDBOut();
+
+                       $this->initFromTitles( $titles );
+
+                       foreach ( $titles as $revid => $title ) {
+                               $ns = $title->getNamespace();
+                               $dbkey = $title->getDBkey();
+
+                               // Handle converted titles
+                               if ( !isset( $this->mAllPages[$ns][$dbkey] ) &&
+                                       isset( $this->mConvertedTitles[$title->getPrefixedText()] )
+                               ) {
+                                       $title = Title::newFromText( $this->mConvertedTitles[$title->getPrefixedText()] );
+                                       $ns = $title->getNamespace();
+                                       $dbkey = $title->getDBkey();
+                               }
+
+                               if ( isset( $this->mAllPages[$ns][$dbkey] ) ) {
+                                       $this->mGoodRevIDs[$revid] = $this->mAllPages[$ns][$dbkey];
+                                       $this->mDeletedRevIDs[$revid] = $this->mAllPages[$ns][$dbkey];
+                               } else {
+                                       $remaining[$revid] = true;
+                               }
+                       }
+
+                       $this->mMissingRevIDs = array_keys( $remaining );
+               }
        }
 
        /**
index 07670f6..b1c092e 100644 (file)
@@ -331,6 +331,7 @@ class ApiParamInfo extends ApiBase {
                                                $this->context->msg( "apihelp-{$path}-paraminfo-{$tag}" )
                                                        ->numParams( count( $i ) )
                                                        ->params( $this->context->getLanguage()->commaList( $i ) )
+                                                       ->params( $module->getModulePrefix() )
                                        ) );
                                        $result->setSubelements( $info, 'text' );
                                        $item['info'][] = $info;
index a091663..5a0491a 100644 (file)
@@ -45,6 +45,7 @@ class ApiQuery extends ApiBase {
                'categories' => 'ApiQueryCategories',
                'categoryinfo' => 'ApiQueryCategoryInfo',
                'contributors' => 'ApiQueryContributors',
+               'deletedrevisions' => 'ApiQueryDeletedRevisions',
                'duplicatefiles' => 'ApiQueryDuplicateFiles',
                'extlinks' => 'ApiQueryExternalLinks',
                'fileusage' => 'ApiQueryBacklinksprop',
@@ -69,6 +70,7 @@ class ApiQuery extends ApiBase {
         */
        private static $QueryListModules = array(
                'allcategories' => 'ApiQueryAllCategories',
+               'alldeletedrevisions' => 'ApiQueryAllDeletedRevisions',
                'allfileusages' => 'ApiQueryAllLinks',
                'allimages' => 'ApiQueryAllImages',
                'alllinks' => 'ApiQueryAllLinks',
diff --git a/includes/api/ApiQueryAllDeletedRevisions.php b/includes/api/ApiQueryAllDeletedRevisions.php
new file mode 100644 (file)
index 0000000..0292e9a
--- /dev/null
@@ -0,0 +1,384 @@
+<?php
+/**
+ * Created on Oct 3, 2014
+ *
+ * Copyright © 2014 Brad Jorsch "bjorsch@wikimedia.org"
+ *
+ * Heavily based on ApiQueryDeletedrevs,
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
+ *
+ * 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
+ */
+
+/**
+ * Query module to enumerate all deleted revisions.
+ *
+ * @ingroup API
+ */
+class ApiQueryAllDeletedRevisions extends ApiQueryRevisionsBase {
+
+       public function __construct( ApiQuery $query, $moduleName ) {
+               parent::__construct( $query, $moduleName, 'adr' );
+       }
+
+       /**
+        * @param ApiPageSet $resultPageSet
+        * @return void
+        */
+       protected function run( ApiPageSet $resultPageSet = null ) {
+               $user = $this->getUser();
+               // Before doing anything at all, let's check permissions
+               if ( !$user->isAllowed( 'deletedhistory' ) ) {
+                       $this->dieUsage(
+                               'You don\'t have permission to view deleted revision information',
+                               'permissiondenied'
+                       );
+               }
+
+               $db = $this->getDB();
+               $params = $this->extractRequestParams( false );
+
+               $result = $this->getResult();
+               $pageSet = $this->getPageSet();
+               $titles = $pageSet->getTitles();
+
+               // This module operates in two modes:
+               // 'user': List deleted revs by a certain user
+               // 'all': List all deleted revs in NS
+               $mode = 'all';
+               if ( !is_null( $params['user'] ) ) {
+                       $mode = 'user';
+               }
+
+               if ( $mode == 'user' ) {
+                       foreach ( array( 'from', 'to', 'prefix', 'excludeuser' ) as $param ) {
+                               if ( !is_null( $params[$param] ) ) {
+                                       $p = $this->getModulePrefix();
+                                       $this->dieUsage( "The '{$p}{$param}' parameter cannot be used with '{$p}user'",
+                                               'badparams' );
+                               }
+                       }
+               } else {
+                       foreach ( array( 'start', 'end' ) as $param ) {
+                               if ( !is_null( $params[$param] ) ) {
+                                       $p = $this->getModulePrefix();
+                                       $this->dieUsage( "The '{$p}{$param}' parameter may only be used with '{$p}user'",
+                                               'badparams' );
+                               }
+                       }
+               }
+
+               $this->addTables( 'archive' );
+               if ( $resultPageSet === null ) {
+                       $this->parseParameters( $params );
+                       $this->addFields( Revision::selectArchiveFields() );
+                       $this->addFields( array( 'ar_title', 'ar_namespace' ) );
+               } else {
+                       $this->limit = $this->getParameter( 'limit' ) ?: 10;
+                       $this->addFields( array( 'ar_title', 'ar_namespace', 'ar_timestamp', 'ar_rev_id', 'ar_id' ) );
+               }
+
+               if ( $this->fld_tags ) {
+                       $this->addTables( 'tag_summary' );
+                       $this->addJoinConds(
+                               array( 'tag_summary' => array( 'LEFT JOIN', array( 'ar_rev_id=ts_rev_id' ) ) )
+                       );
+                       $this->addFields( 'ts_tags' );
+               }
+
+               if ( !is_null( $params['tag'] ) ) {
+                       $this->addTables( 'change_tag' );
+                       $this->addJoinConds(
+                               array( 'change_tag' => array( 'INNER JOIN', array( 'ar_rev_id=ct_rev_id' ) ) )
+                       );
+                       $this->addWhereFld( 'ct_tag', $params['tag'] );
+               }
+
+               if ( $this->fld_content || !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) {
+                       // Modern MediaWiki has the content for deleted revs in the 'text'
+                       // table using fields old_text and old_flags. But revisions deleted
+                       // pre-1.5 store the content in the 'archive' table directly using
+                       // fields ar_text and ar_flags, and no corresponding 'text' row. So
+                       // we have to LEFT JOIN and fetch all four fields.
+                       $this->addTables( 'text' );
+                       $this->addJoinConds(
+                               array( 'text' => array( 'LEFT JOIN', array( 'ar_text_id=old_id' ) ) )
+                       );
+                       $this->addFields( array( 'ar_text', 'ar_flags', 'old_text', 'old_flags' ) );
+
+                       // This also means stricter restrictions
+                       if ( !$user->isAllowedAny( 'undelete', 'deletedtext' ) ) {
+                               $this->dieUsage(
+                                       'You don\'t have permission to view deleted revision content',
+                                       'permissiondenied'
+                               );
+                       }
+               }
+
+               $dir = $params['dir'];
+               $miser_ns = null;
+
+               if ( $mode == 'all' ) {
+                       if ( $params['namespace'] !== null ) {
+                               $this->addWhereFld( 'ar_namespace', $params['namespace'] );
+                       }
+
+                       $from = $params['from'] === null
+                               ? null
+                               : $this->titlePartToKey( $params['from'], $params['namespace'] );
+                       $to = $params['to'] === null
+                               ? null
+                               : $this->titlePartToKey( $params['to'], $params['namespace'] );
+                       $this->addWhereRange( 'ar_title', $dir, $from, $to );
+
+                       if ( isset( $params['prefix'] ) ) {
+                               $this->addWhere( 'ar_title' . $db->buildLike(
+                                       $this->titlePartToKey( $params['prefix'], $params['namespace'] ),
+                                       $db->anyString() ) );
+                       }
+               } else {
+                       if ( $this->getConfig()->get( 'MiserMode' ) ) {
+                               $miser_ns = $params['namespace'];
+                       } else {
+                               $this->addWhereFld( 'ar_namespace', $params['namespace'] );
+                       }
+                       $this->addTimestampWhereRange( 'ar_timestamp', $dir, $params['start'], $params['end'] );
+               }
+
+               if ( !is_null( $params['user'] ) ) {
+                       $this->addWhereFld( 'ar_user_text', $params['user'] );
+               } elseif ( !is_null( $params['excludeuser'] ) ) {
+                       $this->addWhere( 'ar_user_text != ' .
+                               $db->addQuotes( $params['excludeuser'] ) );
+               }
+
+               if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
+                       // Paranoia: avoid brute force searches (bug 17342)
+                       // (shouldn't be able to get here without 'deletedhistory', but
+                       // check it again just in case)
+                       if ( !$user->isAllowed( 'deletedhistory' ) ) {
+                               $bitmask = Revision::DELETED_USER;
+                       } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
+                               $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
+                       } else {
+                               $bitmask = 0;
+                       }
+                       if ( $bitmask ) {
+                               $this->addWhere( $db->bitAnd( 'ar_deleted', $bitmask ) . " != $bitmask" );
+                       }
+               }
+
+               if ( !is_null( $params['continue'] ) ) {
+                       $cont = explode( '|', $params['continue'] );
+                       $op = ( $dir == 'newer' ? '>' : '<' );
+                       if ( $mode == 'all' ) {
+                               $this->dieContinueUsageIf( count( $cont ) != 4 );
+                               $ns = intval( $cont[0] );
+                               $this->dieContinueUsageIf( strval( $ns ) !== $cont[0] );
+                               $title = $db->addQuotes( $cont[1] );
+                               $ts = $db->addQuotes( $db->timestamp( $cont[2] ) );
+                               $ar_id = (int)$cont[3];
+                               $this->dieContinueUsageIf( strval( $ar_id ) !== $cont[3] );
+                               $this->addWhere( "ar_namespace $op $ns OR " .
+                                       "(ar_namespace = $ns AND " .
+                                       "(ar_title $op $title OR " .
+                                       "(ar_title = $title AND " .
+                                       "(ar_timestamp $op $ts OR " .
+                                       "(ar_timestamp = $ts AND " .
+                                       "ar_id $op= $ar_id)))))" );
+                       } else {
+                               $this->dieContinueUsageIf( count( $cont ) != 2 );
+                               $ts = $db->addQuotes( $db->timestamp( $cont[0] ) );
+                               $ar_id = (int)$cont[1];
+                               $this->dieContinueUsageIf( strval( $ar_id ) !== $cont[1] );
+                               $this->addWhere( "ar_timestamp $op $ts OR " .
+                                       "(ar_timestamp = $ts AND " .
+                                       "ar_id $op= $ar_id)" );
+                       }
+               }
+
+               $this->addOption( 'LIMIT', $this->limit + 1 );
+
+               $sort = ( $dir == 'newer' ? '' : ' DESC' );
+               $orderby = array();
+               if ( $mode == 'all' ) {
+                       // Targeting index name_title_timestamp
+                       if ( $params['namespace'] === null || count( array_unique( $params['namespace'] ) ) > 1 ) {
+                               $orderby[] = "ar_namespace $sort";
+                       }
+                       $orderby[] = "ar_title $sort";
+                       $orderby[] = "ar_timestamp $sort";
+                       $orderby[] = "ar_id $sort";
+               } else {
+                       // Targeting index usertext_timestamp
+                       // 'user' is always constant.
+                       $orderby[] = "ar_timestamp $sort";
+                       $orderby[] = "ar_id $sort";
+               }
+               $this->addOption( 'ORDER BY', $orderby );
+
+               $res = $this->select( __METHOD__ );
+               $pageMap = array(); // Maps ns&title to array index
+               $count = 0;
+               $nextIndex = 0;
+               $generated = array();
+               foreach ( $res as $row ) {
+                       if ( ++$count > $this->limit ) {
+                               // We've had enough
+                               if ( $mode == 'all' ) {
+                                       $this->setContinueEnumParameter( 'continue',
+                                               "$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id"
+                                       );
+                               } else {
+                                       $this->setContinueEnumParameter( 'continue', "$row->ar_timestamp|$row->ar_id" );
+                               }
+                               break;
+                       }
+
+                       // Miser mode namespace check
+                       if ( $miser_ns !== null && !in_array( $row->ar_namespace, $miser_ns ) ) {
+                               continue;
+                       }
+
+                       if ( $resultPageSet !== null ) {
+                               if ( $params['generatetitles'] ) {
+                                       $key = "{$row->ar_namespace}:{$row->ar_title}";
+                                       if ( !isset( $generated[$key] ) ) {
+                                               $generated[$key] = Title::makeTitle( $row->ar_namespace, $row->ar_title );
+                                       }
+                               } else {
+                                       $generated[] = $row->ar_rev_id;
+                               }
+                       } else {
+                               $revision = Revision::newFromArchiveRow( $row );
+                               $rev = $this->extractRevisionInfo( $revision, $row );
+
+                               if ( !isset( $pageMap[$row->ar_namespace][$row->ar_title] ) ) {
+                                       $index = $nextIndex++;
+                                       $pageMap[$row->ar_namespace][$row->ar_title] = $index;
+                                       $title = $revision->getTitle();
+                                       $a = array(
+                                               'pageid' => $title->getArticleID(),
+                                               'revisions' => array( $rev ),
+                                       );
+                                       $result->setIndexedTagName( $a['revisions'], 'rev' );
+                                       ApiQueryBase::addTitleInfo( $a, $title );
+                                       $fit = $result->addValue( array( 'query', $this->getModuleName() ), $index, $a );
+                               } else {
+                                       $index = $pageMap[$row->ar_namespace][$row->ar_title];
+                                       $fit = $result->addValue(
+                                               array( 'query', $this->getModuleName(), $index, 'revisions' ),
+                                               null, $rev );
+                               }
+                               if ( !$fit ) {
+                                       if ( $mode == 'all' ) {
+                                               $this->setContinueEnumParameter( 'continue',
+                                                       "$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id"
+                                               );
+                                       } else {
+                                               $this->setContinueEnumParameter( 'continue', "$row->ar_timestamp|$row->ar_id" );
+                                       }
+                                       break;
+                               }
+                       }
+               }
+
+               if ( $resultPageSet !== null ) {
+                       if ( $params['generatetitles'] ) {
+                               $resultPageSet->populateFromTitles( $generated );
+                       } else {
+                               $resultPageSet->populateFromRevisionIDs( $generated );
+                       }
+               } else {
+                       $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'page' );
+               }
+       }
+
+       public function getAllowedParams() {
+               $ret = parent::getAllowedParams() + array(
+                       'user' => array(
+                               ApiBase::PARAM_TYPE => 'user'
+                       ),
+                       'namespace' => array(
+                               ApiBase::PARAM_ISMULTI => true,
+                               ApiBase::PARAM_TYPE => 'namespace',
+                               ApiBase::PARAM_DFLT => null,
+                       ),
+                       'start' => array(
+                               ApiBase::PARAM_TYPE => 'timestamp',
+                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'useronly' ) ),
+                       ),
+                       'end' => array(
+                               ApiBase::PARAM_TYPE => 'timestamp',
+                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'useronly' ) ),
+                       ),
+                       'dir' => array(
+                               ApiBase::PARAM_TYPE => array(
+                                       'newer',
+                                       'older'
+                               ),
+                               ApiBase::PARAM_DFLT => 'older',
+                               ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
+                       ),
+                       'from' => array(
+                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'nonuseronly' ) ),
+                       ),
+                       'to' => array(
+                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'nonuseronly' ) ),
+                       ),
+                       'prefix' => array(
+                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'nonuseronly' ) ),
+                       ),
+                       'excludeuser' => array(
+                               ApiBase::PARAM_TYPE => 'user',
+                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'nonuseronly' ) ),
+                       ),
+                       'tag' => null,
+                       'continue' => array(
+                               ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
+                       ),
+                       'generatetitles' => array(
+                               ApiBase::PARAM_DFLT => false
+                       ),
+               );
+
+               if ( $this->getConfig()->get( 'MiserMode' ) ) {
+                       $ret['user'][ApiBase::PARAM_HELP_MSG_APPEND] = array(
+                               'apihelp-query+alldeletedrevisions-param-miser-user-namespace',
+                       );
+                       $ret['namespace'][ApiBase::PARAM_HELP_MSG_APPEND] = array(
+                               'apihelp-query+alldeletedrevisions-param-miser-user-namespace',
+                       );
+               }
+
+               return $ret;
+       }
+
+       protected function getExamplesMessages() {
+               return array(
+                       'action=query&list=alldeletedrevisions&adruser=Example&adrlimit=50'
+                               => 'apihelp-query+alldeletedrevisions-example-user',
+                       'action=query&list=alldeletedrevisions&adrdir=newer&adrlimit=50'
+                               => 'apihelp-query+alldeletedrevisions-example-ns-main',
+               );
+       }
+
+       public function getHelpUrls() {
+               return 'https://www.mediawiki.org/wiki/API:Alldeletedrevisions';
+       }
+}
index b1581f3..998cc91 100644 (file)
@@ -70,6 +70,10 @@ abstract class ApiQueryBase extends ApiBase {
        /**
         * Override this method to request extra fields from the pageSet
         * using $pageSet->requestField('fieldName')
+        *
+        * Note this only makes sense for 'prop' modules, as 'list' and 'meta'
+        * modules should not be using the pageset.
+        *
         * @param ApiPageSet $pageSet
         */
        public function requestExtraData( $pageSet ) {
diff --git a/includes/api/ApiQueryDeletedRevisions.php b/includes/api/ApiQueryDeletedRevisions.php
new file mode 100644 (file)
index 0000000..0271037
--- /dev/null
@@ -0,0 +1,304 @@
+<?php
+/**
+ * Created on Oct 3, 2014
+ *
+ * Copyright © 2014 Brad Jorsch "bjorsch@wikimedia.org"
+ *
+ * Heavily based on ApiQueryDeletedrevs,
+ * Copyright © 2007 Roan Kattouw "<Firstname>.<Lastname>@gmail.com"
+ *
+ * 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
+ */
+
+/**
+ * Query module to enumerate deleted revisions for pages.
+ *
+ * @ingroup API
+ */
+class ApiQueryDeletedRevisions extends ApiQueryRevisionsBase {
+
+       public function __construct( ApiQuery $query, $moduleName ) {
+               parent::__construct( $query, $moduleName, 'drv' );
+       }
+
+       protected function run( ApiPageSet $resultPageSet = null ) {
+               $user = $this->getUser();
+               // Before doing anything at all, let's check permissions
+               if ( !$user->isAllowed( 'deletedhistory' ) ) {
+                       $this->dieUsage(
+                               'You don\'t have permission to view deleted revision information',
+                               'permissiondenied'
+                       );
+               }
+
+               $result = $this->getResult();
+               $pageSet = $this->getPageSet();
+               $pageMap = $pageSet->getGoodAndMissingTitlesByNamespace();
+               $pageCount = count( $pageSet->getGoodAndMissingTitles() );
+               $revCount = $pageSet->getRevisionCount();
+               if ( $revCount === 0 && $pageCount === 0 ) {
+                       // Nothing to do
+                       return;
+               }
+               if ( $revCount !== 0 && count( $pageSet->getDeletedRevisionIDs() ) === 0 ) {
+                       // Nothing to do, revisions were supplied but none are deleted
+                       return;
+               }
+
+               $params = $this->extractRequestParams( false );
+
+               $db = $this->getDB();
+
+               if ( !is_null( $params['user'] ) && !is_null( $params['excludeuser'] ) ) {
+                       $this->dieUsage( 'user and excludeuser cannot be used together', 'badparams' );
+               }
+
+               $this->addTables( 'archive' );
+               if ( $resultPageSet === null ) {
+                       $this->parseParameters( $params );
+                       $this->addFields( Revision::selectArchiveFields() );
+                       $this->addFields( array( 'ar_title', 'ar_namespace' ) );
+               } else {
+                       $this->limit = $this->getParameter( 'limit' ) ?: 10;
+                       $this->addFields( array( 'ar_title', 'ar_namespace', 'ar_timestamp', 'ar_rev_id', 'ar_id' ) );
+               }
+
+               if ( $this->fld_tags ) {
+                       $this->addTables( 'tag_summary' );
+                       $this->addJoinConds(
+                               array( 'tag_summary' => array( 'LEFT JOIN', array( 'ar_rev_id=ts_rev_id' ) ) )
+                       );
+                       $this->addFields( 'ts_tags' );
+               }
+
+               if ( !is_null( $params['tag'] ) ) {
+                       $this->addTables( 'change_tag' );
+                       $this->addJoinConds(
+                               array( 'change_tag' => array( 'INNER JOIN', array( 'ar_rev_id=ct_rev_id' ) ) )
+                       );
+                       $this->addWhereFld( 'ct_tag', $params['tag'] );
+               }
+
+               if ( $this->fld_content || !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) {
+                       // Modern MediaWiki has the content for deleted revs in the 'text'
+                       // table using fields old_text and old_flags. But revisions deleted
+                       // pre-1.5 store the content in the 'archive' table directly using
+                       // fields ar_text and ar_flags, and no corresponding 'text' row. So
+                       // we have to LEFT JOIN and fetch all four fields.
+                       $this->addTables( 'text' );
+                       $this->addJoinConds(
+                               array( 'text' => array( 'LEFT JOIN', array( 'ar_text_id=old_id' ) ) )
+                       );
+                       $this->addFields( array( 'ar_text', 'ar_flags', 'old_text', 'old_flags' ) );
+
+                       // This also means stricter restrictions
+                       if ( !$user->isAllowedAny( 'undelete', 'deletedtext' ) ) {
+                               $this->dieUsage(
+                                       'You don\'t have permission to view deleted revision content',
+                                       'permissiondenied'
+                               );
+                       }
+               }
+
+               $dir = $params['dir'];
+
+               if ( $revCount !== 0 ) {
+                       $this->addWhere( array(
+                               'ar_rev_id' => array_keys( $pageSet->getDeletedRevisionIDs() )
+                       ) );
+               } else {
+                       // We need a custom WHERE clause that matches all titles.
+                       $lb = new LinkBatch( $pageSet->getGoodAndMissingTitles() );
+                       $where = $lb->constructSet( 'ar', $db );
+                       $this->addWhere( $where );
+               }
+
+               if ( !is_null( $params['user'] ) ) {
+                       $this->addWhereFld( 'ar_user_text', $params['user'] );
+               } elseif ( !is_null( $params['excludeuser'] ) ) {
+                       $this->addWhere( 'ar_user_text != ' .
+                               $db->addQuotes( $params['excludeuser'] ) );
+               }
+
+               if ( !is_null( $params['user'] ) || !is_null( $params['excludeuser'] ) ) {
+                       // Paranoia: avoid brute force searches (bug 17342)
+                       // (shouldn't be able to get here without 'deletedhistory', but
+                       // check it again just in case)
+                       if ( !$user->isAllowed( 'deletedhistory' ) ) {
+                               $bitmask = Revision::DELETED_USER;
+                       } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
+                               $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
+                       } else {
+                               $bitmask = 0;
+                       }
+                       if ( $bitmask ) {
+                               $this->addWhere( $db->bitAnd( 'ar_deleted', $bitmask ) . " != $bitmask" );
+                       }
+               }
+
+               if ( !is_null( $params['continue'] ) ) {
+                       $cont = explode( '|', $params['continue'] );
+                       $op = ( $dir == 'newer' ? '>' : '<' );
+                       if ( $revCount !== 0 ) {
+                               $this->dieContinueUsageIf( count( $cont ) != 2 );
+                               $rev = intval( $cont[0] );
+                               $this->dieContinueUsageIf( strval( $rev ) !== $cont[0] );
+                               $ar_id = (int)$cont[1];
+                               $this->dieContinueUsageIf( strval( $ar_id ) !== $cont[1] );
+                               $this->addWhere( "ar_rev_id $op $rev OR " .
+                                       "(ar_rev_id = $rev AND " .
+                                       "ar_id $op= $ar_id)" );
+                       } else {
+                               $this->dieContinueUsageIf( count( $cont ) != 4 );
+                               $ns = intval( $cont[0] );
+                               $this->dieContinueUsageIf( strval( $ns ) !== $cont[0] );
+                               $title = $db->addQuotes( $cont[1] );
+                               $ts = $db->addQuotes( $db->timestamp( $cont[2] ) );
+                               $ar_id = (int)$cont[3];
+                               $this->dieContinueUsageIf( strval( $ar_id ) !== $cont[3] );
+                               $this->addWhere( "ar_namespace $op $ns OR " .
+                                       "(ar_namespace = $ns AND " .
+                                       "(ar_title $op $title OR " .
+                                       "(ar_title = $title AND " .
+                                       "(ar_timestamp $op $ts OR " .
+                                       "(ar_timestamp = $ts AND " .
+                                       "ar_id $op= $ar_id)))))" );
+                       }
+               }
+
+               $this->addOption( 'LIMIT', $this->limit + 1 );
+
+               if ( $revCount !== 0 ) {
+                       // Sort by ar_rev_id when querying by ar_rev_id
+                       $this->addWhereRange( 'ar_rev_id', $dir, null, null );
+               } else {
+                       // Sort by ns and title in the same order as timestamp for efficiency
+                       // But only when not already unique in the query
+                       if ( count( $pageMap ) > 1 ) {
+                               $this->addWhereRange( 'ar_namespace', $dir, null, null );
+                       }
+                       $oneTitle = key( reset( $pageMap ) );
+                       foreach ( $pageMap as $pages ) {
+                               if ( count( $pages ) > 1 || key( $pages ) !== $oneTitle ) {
+                                       $this->addWhereRange( 'ar_title', $dir, null, null );
+                                       break;
+                               }
+                       }
+                       $this->addTimestampWhereRange( 'ar_timestamp', $dir, $params['start'], $params['end'] );
+               }
+               // Include in ORDER BY for uniqueness
+               $this->addWhereRange( 'ar_id', $dir, null, null );
+
+               $res = $this->select( __METHOD__ );
+               $count = 0;
+               $generated = array();
+               foreach ( $res as $row ) {
+                       if ( ++$count > $this->limit ) {
+                               // We've had enough
+                               $this->setContinueEnumParameter( 'continue',
+                                       $revCount
+                                               ? "$row->ar_rev_id|$row->ar_id"
+                                               : "$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id"
+                               );
+                               break;
+                       }
+
+                       if ( $resultPageSet !== null ) {
+                               $generated[] = $row->ar_rev_id;
+                       } else {
+                               if ( !isset( $pageMap[$row->ar_namespace][$row->ar_title] ) ) {
+                                       // Was it converted?
+                                       $title = Title::makeTitle( $row->ar_namespace, $row->ar_title );
+                                       $converted = $pageSet->getConvertedTitles();
+                                       if ( $title && isset( $converted[$title->getPrefixedText()] ) ) {
+                                               $title = Title::newFromText( $converted[$title->getPrefixedText()] );
+                                               if ( $title && isset( $pageMap[$title->getNamespace()][$title->getDBkey()] ) ) {
+                                                       $pageMap[$row->ar_namespace][$row->ar_title] =
+                                                               $pageMap[$title->getNamespace()][$title->getDBkey()];
+                                               }
+                                       }
+                               }
+                               if ( !isset( $pageMap[$row->ar_namespace][$row->ar_title] ) ) {
+                                       ApiBase::dieDebug( "Found row in archive (ar_id={$row->ar_id}) that didn't " .
+                                               "get processed by ApiPageSet" );
+                               }
+
+                               $fit = $this->addPageSubItem(
+                                       $pageMap[$row->ar_namespace][$row->ar_title],
+                                       $this->extractRevisionInfo( Revision::newFromArchiveRow( $row ), $row ),
+                                       'rev'
+                               );
+                               if ( !$fit ) {
+                                       $this->setContinueEnumParameter( 'continue',
+                                               $revCount
+                                                       ? "$row->ar_rev_id|$row->ar_id"
+                                                       : "$row->ar_namespace|$row->ar_title|$row->ar_timestamp|$row->ar_id"
+                                       );
+                                       break;
+                               }
+                       }
+               }
+
+               if ( $resultPageSet !== null ) {
+                       $resultPageSet->populateFromRevisionIDs( $generated );
+               }
+       }
+
+       public function getAllowedParams() {
+               return parent::getAllowedParams() + array(
+                       'start' => array(
+                               ApiBase::PARAM_TYPE => 'timestamp',
+                       ),
+                       'end' => array(
+                               ApiBase::PARAM_TYPE => 'timestamp',
+                       ),
+                       'dir' => array(
+                               ApiBase::PARAM_TYPE => array(
+                                       'newer',
+                                       'older'
+                               ),
+                               ApiBase::PARAM_DFLT => 'older',
+                               ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
+                       ),
+                       'tag' => null,
+                       'user' => array(
+                               ApiBase::PARAM_TYPE => 'user'
+                       ),
+                       'excludeuser' => array(
+                               ApiBase::PARAM_TYPE => 'user'
+                       ),
+                       'continue' => array(
+                               ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
+                       ),
+               );
+       }
+
+       protected function getExamplesMessages() {
+               return array(
+                       'action=query&prop=deletedrevisions&titles=Main%20Page|Talk:Main%20Page&' .
+                               'drvprop=user|comment|content'
+                               => 'apihelp-query+deletedrevisions-example-titles',
+                       'action=query&prop=deletedrevisions&revids=123456'
+                               => 'apihelp-query+deletedrevisions-example-revids',
+               );
+       }
+
+       public function getHelpUrls() {
+               return 'https://www.mediawiki.org/wiki/API:Properties#deletedrevisions_.2F_drv';
+       }
+}
index 4a5f5fd..f828255 100644 (file)
@@ -28,6 +28,7 @@
  * Query module to enumerate all deleted revisions.
  *
  * @ingroup API
+ * @deprecated since 1.25
  */
 class ApiQueryDeletedrevs extends ApiQueryBase {
 
@@ -45,6 +46,12 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                        );
                }
 
+               $this->setWarning(
+                       'list=deletedrevs has been deprecated. Please use prop=deletedrevisions or ' .
+                       'list=alldeletedrevisions instead.'
+               );
+               $this->logFeatureUsage( 'action=query&list=deletedrevs' );
+
                $db = $this->getDB();
                $params = $this->extractRequestParams( false );
                $prop = array_flip( $params['prop'] );
@@ -420,6 +427,10 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'page' );
        }
 
+       public function isDeprecated() {
+               return true;
+       }
+
        public function getAllowedParams() {
                return array(
                        'start' => array(
index 7ecb429..5e61ed1 100644 (file)
@@ -70,6 +70,9 @@ class ApiQueryInfo extends ApiQueryBase {
                if ( $config->get( 'ContentHandlerUseDB' ) ) {
                        $pageSet->requestField( 'page_content_model' );
                }
+               if ( $config->get( 'PageLanguageUseDB' ) ) {
+                       $pageSet->requestField( 'page_lang' );
+               }
        }
 
        /**
index f5ad9d0..2e980f3 100644 (file)
  *
  * @ingroup API
  */
-class ApiQueryRevisions extends ApiQueryBase {
+class ApiQueryRevisions extends ApiQueryRevisionsBase {
 
-       private $diffto, $difftotext, $expandTemplates, $generateXML, $section,
-               $token, $parseContent, $contentFormat;
+       private $token = null;
 
        public function __construct( ApiQuery $query, $moduleName ) {
                parent::__construct( $query, $moduleName, 'rv' );
        }
 
-       private $fld_ids = false, $fld_flags = false, $fld_timestamp = false,
-               $fld_size = false, $fld_sha1 = false, $fld_comment = false,
-               $fld_parsedcomment = false, $fld_user = false, $fld_userid = false,
-               $fld_content = false, $fld_tags = false, $fld_contentmodel = false;
-
        private $tokenFunctions;
 
        /** @deprecated since 1.24 */
@@ -89,7 +83,7 @@ class ApiQueryRevisions extends ApiQueryBase {
                        array( $title->getPrefixedText(), $rev->getUserText() ) );
        }
 
-       public function execute() {
+       protected function run( ApiPageSet $resultPageSet = null ) {
                $params = $this->extractRequestParams( false );
 
                // If any of those parameters are used, work in 'enumeration' mode.
@@ -107,6 +101,11 @@ class ApiQueryRevisions extends ApiQueryBase {
 
                // Optimization -- nothing to do
                if ( $revCount === 0 && $pageCount === 0 ) {
+                       // Nothing to do
+                       return;
+               }
+               if ( $revCount > 0 && count( $pageSet->getLiveRevisionIDs() ) === 0 ) {
+                       // We're in revisions mode but all given revisions are deleted
                        return;
                }
 
@@ -127,75 +126,32 @@ class ApiQueryRevisions extends ApiQueryBase {
                        );
                }
 
-               if ( !is_null( $params['difftotext'] ) ) {
-                       $this->difftotext = $params['difftotext'];
-               } elseif ( !is_null( $params['diffto'] ) ) {
-                       if ( $params['diffto'] == 'cur' ) {
-                               $params['diffto'] = 0;
-                       }
-                       if ( ( !ctype_digit( $params['diffto'] ) || $params['diffto'] < 0 )
-                               && $params['diffto'] != 'prev' && $params['diffto'] != 'next'
-                       ) {
-                               $this->dieUsage(
-                                       'rvdiffto must be set to a non-negative number, "prev", "next" or "cur"',
-                                       'diffto'
-                               );
-                       }
-                       // Check whether the revision exists and is readable,
-                       // DifferenceEngine returns a rather ambiguous empty
-                       // string if that's not the case
-                       if ( $params['diffto'] != 0 ) {
-                               $difftoRev = Revision::newFromID( $params['diffto'] );
-                               if ( !$difftoRev ) {
-                                       $this->dieUsageMsg( array( 'nosuchrevid', $params['diffto'] ) );
-                               }
-                               if ( !$difftoRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
-                                       $this->setWarning( "Couldn't diff to r{$difftoRev->getID()}: content is hidden" );
-                                       $params['diffto'] = null;
-                               }
-                       }
-                       $this->diffto = $params['diffto'];
+               // In non-enum mode, rvlimit can't be directly used. Use the maximum
+               // allowed value.
+               if ( !$enumRevMode ) {
+                       $this->setParsedLimit = false;
+                       $params['limit'] = 'max';
                }
 
                $db = $this->getDB();
-               $this->addTables( 'page' );
-               $this->addFields( Revision::selectFields() );
-               $this->addWhere( 'page_id = rev_page' );
-
-               $prop = array_flip( $params['prop'] );
-
-               // Optional fields
-               $this->fld_ids = isset( $prop['ids'] );
-               // $this->addFieldsIf('rev_text_id', $this->fld_ids); // should this be exposed?
-               $this->fld_flags = isset( $prop['flags'] );
-               $this->fld_timestamp = isset( $prop['timestamp'] );
-               $this->fld_comment = isset( $prop['comment'] );
-               $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
-               $this->fld_size = isset( $prop['size'] );
-               $this->fld_sha1 = isset( $prop['sha1'] );
-               $this->fld_contentmodel = isset( $prop['contentmodel'] );
-               $this->fld_userid = isset( $prop['userid'] );
-               $this->fld_user = isset( $prop['user'] );
-               $this->token = $params['token'];
-
-               if ( !empty( $params['contentformat'] ) ) {
-                       $this->contentFormat = $params['contentformat'];
-               }
-
-               $userMax = ( $this->fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 );
-               $botMax = ( $this->fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 );
-               $limit = $params['limit'];
-               if ( $limit == 'max' ) {
-                       $limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
-                       $this->getResult()->setParsedLimit( $this->getModuleName(), $limit );
-               }
+               $this->addTables( array( 'revision', 'page' ) );
+               $this->addJoinConds(
+                       array( 'page' => array( 'INNER JOIN', array( 'page_id = rev_page' ) ) )
+               );
 
-               if ( !is_null( $this->token ) || $pageCount > 0 ) {
-                       $this->addFields( Revision::selectPageFields() );
+               if ( $resultPageSet === null ) {
+                       $this->parseParameters( $params );
+                       $this->token = $params['token'];
+                       $this->addFields( Revision::selectFields() );
+                       if ( $this->token !== null || $pageCount > 0 ) {
+                               $this->addFields( Revision::selectPageFields() );
+                       }
+               } else {
+                       $this->limit = $this->getParameter( 'limit' ) ?: 10;
+                       $this->addFields( array( 'rev_id', 'rev_page' ) );
                }
 
-               if ( isset( $prop['tags'] ) ) {
-                       $this->fld_tags = true;
+               if ( $this->fld_tags ) {
                        $this->addTables( 'tag_summary' );
                        $this->addJoinConds(
                                array( 'tag_summary' => array( 'LEFT JOIN', array( 'rev_id=ts_rev_id' ) ) )
@@ -211,7 +167,7 @@ class ApiQueryRevisions extends ApiQueryBase {
                        $this->addWhereFld( 'ct_tag', $params['tag'] );
                }
 
-               if ( isset( $prop['content'] ) || !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) {
+               if ( $this->fld_content || !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) {
                        // For each page we will request, the user must have read rights for that page
                        $user = $this->getUser();
                        /** @var $title Title */
@@ -224,28 +180,11 @@ class ApiQueryRevisions extends ApiQueryBase {
                        }
 
                        $this->addTables( 'text' );
-                       $this->addWhere( 'rev_text_id=old_id' );
+                       $this->addJoinConds(
+                               array( 'text' => array( 'INNER JOIN', array( 'rev_text_id=old_id' ) ) )
+                       );
                        $this->addFields( 'old_id' );
                        $this->addFields( Revision::selectTextFields() );
-
-                       $this->fld_content = isset( $prop['content'] );
-
-                       $this->expandTemplates = $params['expandtemplates'];
-                       $this->generateXML = $params['generatexml'];
-                       $this->parseContent = $params['parse'];
-                       if ( $this->parseContent ) {
-                               // Must manually initialize unset limit
-                               if ( is_null( $limit ) ) {
-                                       $limit = 1;
-                               }
-                               // We are only going to parse 1 revision per request
-                               $this->validateLimit( 'limit', $limit, 1, 1, 1 );
-                       }
-                       if ( isset( $params['section'] ) ) {
-                               $this->section = $params['section'];
-                       } else {
-                               $this->section = false;
-                       }
                }
 
                // add user name, if needed
@@ -255,9 +194,6 @@ class ApiQueryRevisions extends ApiQueryBase {
                        $this->addFields( Revision::selectUserFields() );
                }
 
-               // Bug 24166 - API error when using rvprop=tags
-               $this->addTables( 'revision' );
-
                if ( $enumRevMode ) {
                        // This is mostly to prevent parameter errors (and optimize SQL?)
                        if ( !is_null( $params['startid'] ) && !is_null( $params['start'] ) ) {
@@ -300,12 +236,6 @@ class ApiQueryRevisions extends ApiQueryBase {
                                        $params['start'], $params['end'], false );
                        }
 
-                       // must manually initialize unset limit
-                       if ( is_null( $limit ) ) {
-                               $limit = 10;
-                       }
-                       $this->validateLimit( 'limit', $limit, 1, $userMax, $botMax );
-
                        // There is only one ID, use it
                        $ids = array_keys( $pageSet->getGoodTitles() );
                        $this->addWhereFld( 'rev_page', reset( $ids ) );
@@ -330,11 +260,7 @@ class ApiQueryRevisions extends ApiQueryBase {
                                }
                        }
                } elseif ( $revCount > 0 ) {
-                       $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
-                       $revs = $pageSet->getRevisionIDs();
-                       if ( self::truncateArray( $revs, $max ) ) {
-                               $this->setWarning( "Too many values supplied for parameter 'revids': the limit is $max" );
-                       }
+                       $revs = $pageSet->getLiveRevisionIDs();
 
                        // Get all revision IDs
                        $this->addWhereFld( 'rev_id', array_keys( $revs ) );
@@ -343,19 +269,11 @@ class ApiQueryRevisions extends ApiQueryBase {
                                $this->addWhere( 'rev_id >= ' . intval( $params['continue'] ) );
                        }
                        $this->addOption( 'ORDER BY', 'rev_id' );
-
-                       // assumption testing -- we should never get more then $revCount rows.
-                       $limit = $revCount;
                } elseif ( $pageCount > 0 ) {
-                       $max = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
                        $titles = $pageSet->getGoodTitles();
-                       if ( self::truncateArray( $titles, $max ) ) {
-                               $this->setWarning( "Too many values supplied for parameter 'titles': the limit is $max" );
-                       }
 
                        // When working in multi-page non-enumeration mode,
                        // limit to the latest revision only
-                       $this->addWhere( 'page_id=rev_page' );
                        $this->addWhere( 'page_latest=rev_id' );
 
                        // Get all page IDs
@@ -378,31 +296,20 @@ class ApiQueryRevisions extends ApiQueryBase {
                                'rev_page',
                                'rev_id'
                        ) );
-
-                       // assumption testing -- we should never get more then $pageCount rows.
-                       $limit = $pageCount;
                } else {
                        ApiBase::dieDebug( __METHOD__, 'param validation?' );
                }
 
-               $this->addOption( 'LIMIT', $limit + 1 );
+               $this->addOption( 'LIMIT', $this->limit + 1 );
 
                $count = 0;
+               $generated = array();
                $res = $this->select( __METHOD__ );
 
                foreach ( $res as $row ) {
-                       if ( ++$count > $limit ) {
+                       if ( ++$count > $this->limit ) {
                                // We've reached the one extra which shows that there are
                                // additional pages to be had. Stop here...
-                               if ( !$enumRevMode ) {
-                                       ApiBase::dieDebug( __METHOD__, 'Got more rows then expected' ); // bug report
-                               }
-                               $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
-                               break;
-                       }
-
-                       $fit = $this->addPageSubItem( $row->rev_page, $this->extractRowInfo( $row ), 'rev' );
-                       if ( !$fit ) {
                                if ( $enumRevMode ) {
                                        $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
                                } elseif ( $revCount > 0 ) {
@@ -413,313 +320,55 @@ class ApiQueryRevisions extends ApiQueryBase {
                                }
                                break;
                        }
-               }
-       }
-
-       private function extractRowInfo( $row ) {
-               $revision = new Revision( $row );
-               $title = $revision->getTitle();
-               $user = $this->getUser();
-               $vals = array();
-               $anyHidden = false;
-
-               if ( $this->fld_ids ) {
-                       $vals['revid'] = intval( $revision->getId() );
-                       // $vals['oldid'] = intval( $row->rev_text_id ); // todo: should this be exposed?
-                       if ( !is_null( $revision->getParentId() ) ) {
-                               $vals['parentid'] = intval( $revision->getParentId() );
-                       }
-               }
-
-               if ( $this->fld_flags && $revision->isMinor() ) {
-                       $vals['minor'] = '';
-               }
-
-               if ( $this->fld_user || $this->fld_userid ) {
-                       if ( $revision->isDeleted( Revision::DELETED_USER ) ) {
-                               $vals['userhidden'] = '';
-                               $anyHidden = true;
-                       }
-                       if ( $revision->userCan( Revision::DELETED_USER, $user ) ) {
-                               if ( $this->fld_user ) {
-                                       $vals['user'] = $revision->getRawUserText();
-                               }
-                               $userid = $revision->getRawUser();
-                               if ( !$userid ) {
-                                       $vals['anon'] = '';
-                               }
-
-                               if ( $this->fld_userid ) {
-                                       $vals['userid'] = $userid;
-                               }
-                       }
-               }
-
-               if ( $this->fld_timestamp ) {
-                       $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $revision->getTimestamp() );
-               }
-
-               if ( $this->fld_size ) {
-                       if ( !is_null( $revision->getSize() ) ) {
-                               $vals['size'] = intval( $revision->getSize() );
-                       } else {
-                               $vals['size'] = 0;
-                       }
-               }
-
-               if ( $this->fld_sha1 ) {
-                       if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
-                               $vals['sha1hidden'] = '';
-                               $anyHidden = true;
-                       }
-                       if ( $revision->userCan( Revision::DELETED_TEXT, $user ) ) {
-                               if ( $revision->getSha1() != '' ) {
-                                       $vals['sha1'] = wfBaseConvert( $revision->getSha1(), 36, 16, 40 );
-                               } else {
-                                       $vals['sha1'] = '';
-                               }
-                       }
-               }
-
-               if ( $this->fld_contentmodel ) {
-                       $vals['contentmodel'] = $revision->getContentModel();
-               }
-
-               if ( $this->fld_comment || $this->fld_parsedcomment ) {
-                       if ( $revision->isDeleted( Revision::DELETED_COMMENT ) ) {
-                               $vals['commenthidden'] = '';
-                               $anyHidden = true;
-                       }
-                       if ( $revision->userCan( Revision::DELETED_COMMENT, $user ) ) {
-                               $comment = $revision->getRawComment();
 
-                               if ( $this->fld_comment ) {
-                                       $vals['comment'] = $comment;
-                               }
-
-                               if ( $this->fld_parsedcomment ) {
-                                       $vals['parsedcomment'] = Linker::formatComment( $comment, $title );
-                               }
-                       }
-               }
-
-               if ( $this->fld_tags ) {
-                       if ( $row->ts_tags ) {
-                               $tags = explode( ',', $row->ts_tags );
-                               $this->getResult()->setIndexedTagName( $tags, 'tag' );
-                               $vals['tags'] = $tags;
+                       if ( $resultPageSet !== null ) {
+                               $generated[] = $row->rev_id;
                        } else {
-                               $vals['tags'] = array();
-                       }
-               }
-
-               if ( !is_null( $this->token ) ) {
-                       $tokenFunctions = $this->getTokenFunctions();
-                       foreach ( $this->token as $t ) {
-                               $val = call_user_func( $tokenFunctions[$t], $title->getArticleID(), $title, $revision );
-                               if ( $val === false ) {
-                                       $this->setWarning( "Action '$t' is not allowed for the current user" );
-                               } else {
-                                       $vals[$t . 'token'] = $val;
-                               }
-                       }
-               }
-
-               $content = null;
-               global $wgParser;
-               if ( $this->fld_content || !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) {
-                       $content = $revision->getContent( Revision::FOR_THIS_USER, $this->getUser() );
-                       // Expand templates after getting section content because
-                       // template-added sections don't count and Parser::preprocess()
-                       // will have less input
-                       if ( $content && $this->section !== false ) {
-                               $content = $content->getSection( $this->section, false );
-                               if ( !$content ) {
-                                       $this->dieUsage(
-                                               "There is no section {$this->section} in r" . $revision->getId(),
-                                               'nosuchsection'
-                                       );
-                               }
-                       }
-                       if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
-                               $vals['texthidden'] = '';
-                               $anyHidden = true;
-                       } elseif ( !$content ) {
-                               $vals['textmissing'] = '';
-                       }
-               }
-               if ( $this->fld_content && $content ) {
-                       $text = null;
-
-                       if ( $this->generateXML ) {
-                               if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
-                                       $t = $content->getNativeData(); # note: don't set $text
-
-                                       $wgParser->startExternalParse(
-                                               $title,
-                                               ParserOptions::newFromContext( $this->getContext() ),
-                                               Parser::OT_PREPROCESS
-                                       );
-                                       $dom = $wgParser->preprocessToDom( $t );
-                                       if ( is_callable( array( $dom, 'saveXML' ) ) ) {
-                                               $xml = $dom->saveXML();
-                                       } else {
-                                               $xml = $dom->__toString();
+                               $revision = new Revision( $row );
+                               $rev = $this->extractRevisionInfo( $revision, $row );
+
+                               if ( $this->token !== null ) {
+                                       $title = $revision->getTitle();
+                                       $tokenFunctions = $this->getTokenFunctions();
+                                       foreach ( $this->token as $t ) {
+                                               $val = call_user_func( $tokenFunctions[$t], $title->getArticleID(), $title, $revision );
+                                               if ( $val === false ) {
+                                                       $this->setWarning( "Action '$t' is not allowed for the current user" );
+                                               } else {
+                                                       $rev[$t . 'token'] = $val;
+                                               }
                                        }
-                                       $vals['parsetree'] = $xml;
-                               } else {
-                                       $this->setWarning( "Conversion to XML is supported for wikitext only, " .
-                                               $title->getPrefixedDBkey() .
-                                               " uses content model " . $content->getModel() );
                                }
-                       }
-
-                       if ( $this->expandTemplates && !$this->parseContent ) {
-                               #XXX: implement template expansion for all content types in ContentHandler?
-                               if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
-                                       $text = $content->getNativeData();
-
-                                       $text = $wgParser->preprocess(
-                                               $text,
-                                               $title,
-                                               ParserOptions::newFromContext( $this->getContext() )
-                                       );
-                               } else {
-                                       $this->setWarning( "Template expansion is supported for wikitext only, " .
-                                               $title->getPrefixedDBkey() .
-                                               " uses content model " . $content->getModel() );
-
-                                       $text = false;
-                               }
-                       }
-                       if ( $this->parseContent ) {
-                               $po = $content->getParserOutput(
-                                       $title,
-                                       $revision->getId(),
-                                       ParserOptions::newFromContext( $this->getContext() )
-                               );
-                               $text = $po->getText();
-                       }
-
-                       if ( $text === null ) {
-                               $format = $this->contentFormat ? $this->contentFormat : $content->getDefaultFormat();
-                               $model = $content->getModel();
-
-                               if ( !$content->isSupportedFormat( $format ) ) {
-                                       $name = $title->getPrefixedDBkey();
 
-                                       $this->dieUsage( "The requested format {$this->contentFormat} is not supported " .
-                                               "for content model $model used by $name", 'badformat' );
-                               }
-
-                               $text = $content->serialize( $format );
-
-                               // always include format and model.
-                               // Format is needed to deserialize, model is needed to interpret.
-                               $vals['contentformat'] = $format;
-                               $vals['contentmodel'] = $model;
-                       }
-
-                       if ( $text !== false ) {
-                               ApiResult::setContent( $vals, $text );
-                       }
-               }
-
-               if ( $content && ( !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) ) {
-                       static $n = 0; // Number of uncached diffs we've had
-
-                       if ( $n < $this->getConfig()->get( 'APIMaxUncachedDiffs' ) ) {
-                               $vals['diff'] = array();
-                               $context = new DerivativeContext( $this->getContext() );
-                               $context->setTitle( $title );
-                               $handler = $revision->getContentHandler();
-
-                               if ( !is_null( $this->difftotext ) ) {
-                                       $model = $title->getContentModel();
-
-                                       if ( $this->contentFormat
-                                               && !ContentHandler::getForModelID( $model )->isSupportedFormat( $this->contentFormat )
-                                       ) {
-
-                                               $name = $title->getPrefixedDBkey();
-
-                                               $this->dieUsage( "The requested format {$this->contentFormat} is not supported for " .
-                                                       "content model $model used by $name", 'badformat' );
+                               $fit = $this->addPageSubItem( $row->rev_page, $rev, 'rev' );
+                               if ( !$fit ) {
+                                       if ( $enumRevMode ) {
+                                               $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
+                                       } elseif ( $revCount > 0 ) {
+                                               $this->setContinueEnumParameter( 'continue', intval( $row->rev_id ) );
+                                       } else {
+                                               $this->setContinueEnumParameter( 'continue', intval( $row->rev_page ) .
+                                                       '|' . intval( $row->rev_id ) );
                                        }
-
-                                       $difftocontent = ContentHandler::makeContent(
-                                               $this->difftotext,
-                                               $title,
-                                               $model,
-                                               $this->contentFormat
-                                       );
-
-                                       $engine = $handler->createDifferenceEngine( $context );
-                                       $engine->setContent( $content, $difftocontent );
-                               } else {
-                                       $engine = $handler->createDifferenceEngine( $context, $revision->getID(), $this->diffto );
-                                       $vals['diff']['from'] = $engine->getOldid();
-                                       $vals['diff']['to'] = $engine->getNewid();
+                                       break;
                                }
-                               $difftext = $engine->getDiffBody();
-                               ApiResult::setContent( $vals['diff'], $difftext );
-                               if ( !$engine->wasCacheHit() ) {
-                                       $n++;
-                               }
-                       } else {
-                               $vals['diff']['notcached'] = '';
                        }
                }
 
-               if ( $anyHidden && $revision->isDeleted( Revision::DELETED_RESTRICTED ) ) {
-                       $vals['suppressed'] = '';
+               if ( $resultPageSet !== null ) {
+                       $resultPageSet->populateFromRevisionIDs( $generated );
                }
-
-               return $vals;
        }
 
        public function getCacheMode( $params ) {
                if ( isset( $params['token'] ) ) {
                        return 'private';
                }
-               if ( $this->userCanSeeRevDel() ) {
-                       return 'private';
-               }
-               if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) {
-                       // formatComment() calls wfMessage() among other things
-                       return 'anon-public-user-private';
-               }
-
-               return 'public';
+               return parent::getCacheMode( $params );
        }
 
        public function getAllowedParams() {
-               return array(
-                       'prop' => array(
-                               ApiBase::PARAM_ISMULTI => true,
-                               ApiBase::PARAM_DFLT => 'ids|timestamp|flags|comment|user',
-                               ApiBase::PARAM_TYPE => array(
-                                       'ids',
-                                       'flags',
-                                       'timestamp',
-                                       'user',
-                                       'userid',
-                                       'size',
-                                       'sha1',
-                                       'contentmodel',
-                                       'comment',
-                                       'parsedcomment',
-                                       'content',
-                                       'tags'
-                               )
-                       ),
-                       'limit' => array(
-                               ApiBase::PARAM_TYPE => 'limit',
-                               ApiBase::PARAM_MIN => 1,
-                               ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
-                               ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2,
-                               ApiBase::PARAM_HELP_MSG_INFO => array( array( 'singlepageonly' ) ),
-                       ),
+               $ret = parent::getAllowedParams() + array(
                        'startid' => array(
                                ApiBase::PARAM_TYPE => 'integer',
                                ApiBase::PARAM_HELP_MSG_INFO => array( array( 'singlepageonly' ) ),
@@ -742,7 +391,7 @@ class ApiQueryRevisions extends ApiQueryBase {
                                        'newer',
                                        'older'
                                ),
-                               ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
+                               ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
                                ApiBase::PARAM_HELP_MSG_INFO => array( array( 'singlepageonly' ) ),
                        ),
                        'user' => array(
@@ -754,10 +403,6 @@ class ApiQueryRevisions extends ApiQueryBase {
                                ApiBase::PARAM_HELP_MSG_INFO => array( array( 'singlepageonly' ) ),
                        ),
                        'tag' => null,
-                       'expandtemplates' => false,
-                       'generatexml' => false,
-                       'parse' => false,
-                       'section' => null,
                        'token' => array(
                                ApiBase::PARAM_DEPRECATED => true,
                                ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ),
@@ -766,13 +411,11 @@ class ApiQueryRevisions extends ApiQueryBase {
                        'continue' => array(
                                ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
                        ),
-                       'diffto' => null,
-                       'difftotext' => null,
-                       'contentformat' => array(
-                               ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
-                               ApiBase::PARAM_DFLT => null
-                       ),
                );
+
+               $ret['limit'][ApiBase::PARAM_HELP_MSG_INFO] = array( array( 'singlepageonly' ) );
+
+               return $ret;
        }
 
        protected function getExamplesMessages() {
diff --git a/includes/api/ApiQueryRevisionsBase.php b/includes/api/ApiQueryRevisionsBase.php
new file mode 100644 (file)
index 0000000..3879d7b
--- /dev/null
@@ -0,0 +1,474 @@
+<?php
+/**
+ *
+ *
+ * Created on Oct 3, 2014 as a split from ApiQueryRevisions
+ *
+ * Copyright © 2006 Yuri Astrakhan "<Firstname><Lastname>@gmail.com"
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * A base class for functions common to producing a list of revisions.
+ *
+ * @ingroup API
+ */
+abstract class ApiQueryRevisionsBase extends ApiQueryGeneratorBase {
+
+       protected $limit, $diffto, $difftotext, $expandTemplates, $generateXML, $section,
+               $parseContent, $contentFormat, $setParsedLimit = true;
+
+       protected $fld_ids = false, $fld_flags = false, $fld_timestamp = false,
+               $fld_size = false, $fld_sha1 = false, $fld_comment = false,
+               $fld_parsedcomment = false, $fld_user = false, $fld_userid = false,
+               $fld_content = false, $fld_tags = false, $fld_contentmodel = false;
+
+       public function execute() {
+               $this->run();
+       }
+
+       public function executeGenerator( $resultPageSet ) {
+               $this->run( $resultPageSet );
+       }
+
+       /**
+        * @param ApiPageSet $resultPageSet
+        * @return void
+        */
+       abstract protected function run( ApiPageSet $resultPageSet = null );
+
+       /**
+        * Parse the parameters into the various instance fields.
+        *
+        * @param array $params
+        */
+       protected function parseParameters( $params ) {
+               if ( !is_null( $params['difftotext'] ) ) {
+                       $this->difftotext = $params['difftotext'];
+               } elseif ( !is_null( $params['diffto'] ) ) {
+                       if ( $params['diffto'] == 'cur' ) {
+                               $params['diffto'] = 0;
+                       }
+                       if ( ( !ctype_digit( $params['diffto'] ) || $params['diffto'] < 0 )
+                               && $params['diffto'] != 'prev' && $params['diffto'] != 'next'
+                       ) {
+                               $p = $this->getModulePrefix();
+                               $this->dieUsage(
+                                       "{$p}diffto must be set to a non-negative number, \"prev\", \"next\" or \"cur\"",
+                                       'diffto'
+                               );
+                       }
+                       // Check whether the revision exists and is readable,
+                       // DifferenceEngine returns a rather ambiguous empty
+                       // string if that's not the case
+                       if ( $params['diffto'] != 0 ) {
+                               $difftoRev = Revision::newFromID( $params['diffto'] );
+                               if ( !$difftoRev ) {
+                                       $this->dieUsageMsg( array( 'nosuchrevid', $params['diffto'] ) );
+                               }
+                               if ( !$difftoRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
+                                       $this->setWarning( "Couldn't diff to r{$difftoRev->getID()}: content is hidden" );
+                                       $params['diffto'] = null;
+                               }
+                       }
+                       $this->diffto = $params['diffto'];
+               }
+
+               $prop = array_flip( $params['prop'] );
+
+               $this->fld_ids = isset( $prop['ids'] );
+               $this->fld_flags = isset( $prop['flags'] );
+               $this->fld_timestamp = isset( $prop['timestamp'] );
+               $this->fld_comment = isset( $prop['comment'] );
+               $this->fld_parsedcomment = isset( $prop['parsedcomment'] );
+               $this->fld_size = isset( $prop['size'] );
+               $this->fld_sha1 = isset( $prop['sha1'] );
+               $this->fld_content = isset( $prop['content'] );
+               $this->fld_contentmodel = isset( $prop['contentmodel'] );
+               $this->fld_userid = isset( $prop['userid'] );
+               $this->fld_user = isset( $prop['user'] );
+               $this->fld_tags = isset( $prop['tags'] );
+
+               if ( !empty( $params['contentformat'] ) ) {
+                       $this->contentFormat = $params['contentformat'];
+               }
+
+               $this->limit = $params['limit'];
+
+               $smallLimit = false;
+               if ( $this->fld_content || !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) {
+                       $smallLimit = true;
+                       $this->expandTemplates = $params['expandtemplates'];
+                       $this->generateXML = $params['generatexml'];
+                       $this->parseContent = $params['parse'];
+                       if ( $this->parseContent ) {
+                               // Must manually initialize unset limit
+                               if ( is_null( $this->limit ) ) {
+                                       $this->limit = 1;
+                               }
+                       }
+                       if ( isset( $params['section'] ) ) {
+                               $this->section = $params['section'];
+                       } else {
+                               $this->section = false;
+                       }
+               }
+
+               $userMax = $this->parseContent ? 1 : ( $smallLimit ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 );
+               $botMax = $this->parseContent ? 1 : ( $smallLimit ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 );
+               if ( $this->limit == 'max' ) {
+                       $this->limit = $this->getMain()->canApiHighLimits() ? $botMax : $userMax;
+                       if ( $this->setParsedLimit ) {
+                               $this->getResult()->setParsedLimit( $this->getModuleName(), $this->limit );
+                       }
+               }
+
+               if ( is_null( $this->limit ) ) {
+                       $this->limit = 10;
+               }
+               $this->validateLimit( 'limit', $this->limit, 1, $userMax, $botMax );
+       }
+
+       /**
+        * Extract information from the Revision
+        *
+        * @param Revision $revision
+        * @param object $row Should have a field 'ts_tags' if $this->fld_tags is set
+        * @return array
+        */
+       protected function extractRevisionInfo( Revision $revision, $row ) {
+               $title = $revision->getTitle();
+               $user = $this->getUser();
+               $vals = array();
+               $anyHidden = false;
+
+               if ( $this->fld_ids ) {
+                       $vals['revid'] = intval( $revision->getId() );
+                       if ( !is_null( $revision->getParentId() ) ) {
+                               $vals['parentid'] = intval( $revision->getParentId() );
+                       }
+               }
+
+               if ( $this->fld_flags && $revision->isMinor() ) {
+                       $vals['minor'] = '';
+               }
+
+               if ( $this->fld_user || $this->fld_userid ) {
+                       if ( $revision->isDeleted( Revision::DELETED_USER ) ) {
+                               $vals['userhidden'] = '';
+                               $anyHidden = true;
+                       }
+                       if ( $revision->userCan( Revision::DELETED_USER, $user ) ) {
+                               if ( $this->fld_user ) {
+                                       $vals['user'] = $revision->getRawUserText();
+                               }
+                               $userid = $revision->getRawUser();
+                               if ( !$userid ) {
+                                       $vals['anon'] = '';
+                               }
+
+                               if ( $this->fld_userid ) {
+                                       $vals['userid'] = $userid;
+                               }
+                       }
+               }
+
+               if ( $this->fld_timestamp ) {
+                       $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $revision->getTimestamp() );
+               }
+
+               if ( $this->fld_size ) {
+                       if ( !is_null( $revision->getSize() ) ) {
+                               $vals['size'] = intval( $revision->getSize() );
+                       } else {
+                               $vals['size'] = 0;
+                       }
+               }
+
+               if ( $this->fld_sha1 ) {
+                       if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
+                               $vals['sha1hidden'] = '';
+                               $anyHidden = true;
+                       }
+                       if ( $revision->userCan( Revision::DELETED_TEXT, $user ) ) {
+                               if ( $revision->getSha1() != '' ) {
+                                       $vals['sha1'] = wfBaseConvert( $revision->getSha1(), 36, 16, 40 );
+                               } else {
+                                       $vals['sha1'] = '';
+                               }
+                       }
+               }
+
+               if ( $this->fld_contentmodel ) {
+                       $vals['contentmodel'] = $revision->getContentModel();
+               }
+
+               if ( $this->fld_comment || $this->fld_parsedcomment ) {
+                       if ( $revision->isDeleted( Revision::DELETED_COMMENT ) ) {
+                               $vals['commenthidden'] = '';
+                               $anyHidden = true;
+                       }
+                       if ( $revision->userCan( Revision::DELETED_COMMENT, $user ) ) {
+                               $comment = $revision->getRawComment();
+
+                               if ( $this->fld_comment ) {
+                                       $vals['comment'] = $comment;
+                               }
+
+                               if ( $this->fld_parsedcomment ) {
+                                       $vals['parsedcomment'] = Linker::formatComment( $comment, $title );
+                               }
+                       }
+               }
+
+               if ( $this->fld_tags ) {
+                       if ( $row->ts_tags ) {
+                               $tags = explode( ',', $row->ts_tags );
+                               $this->getResult()->setIndexedTagName( $tags, 'tag' );
+                               $vals['tags'] = $tags;
+                       } else {
+                               $vals['tags'] = array();
+                       }
+               }
+
+               $content = null;
+               global $wgParser;
+               if ( $this->fld_content || !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) {
+                       $content = $revision->getContent( Revision::FOR_THIS_USER, $this->getUser() );
+                       // Expand templates after getting section content because
+                       // template-added sections don't count and Parser::preprocess()
+                       // will have less input
+                       if ( $content && $this->section !== false ) {
+                               $content = $content->getSection( $this->section, false );
+                               if ( !$content ) {
+                                       $this->dieUsage(
+                                               "There is no section {$this->section} in r" . $revision->getId(),
+                                               'nosuchsection'
+                                       );
+                               }
+                       }
+                       if ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
+                               $vals['texthidden'] = '';
+                               $anyHidden = true;
+                       } elseif ( !$content ) {
+                               $vals['textmissing'] = '';
+                       }
+               }
+               if ( $this->fld_content && $content ) {
+                       $text = null;
+
+                       if ( $this->generateXML ) {
+                               if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
+                                       $t = $content->getNativeData(); # note: don't set $text
+
+                                       $wgParser->startExternalParse(
+                                               $title,
+                                               ParserOptions::newFromContext( $this->getContext() ),
+                                               Parser::OT_PREPROCESS
+                                       );
+                                       $dom = $wgParser->preprocessToDom( $t );
+                                       if ( is_callable( array( $dom, 'saveXML' ) ) ) {
+                                               $xml = $dom->saveXML();
+                                       } else {
+                                               $xml = $dom->__toString();
+                                       }
+                                       $vals['parsetree'] = $xml;
+                               } else {
+                                       $vals['badcontentformatforparsetree'] = '';
+                                       $this->setWarning( "Conversion to XML is supported for wikitext only, " .
+                                               $title->getPrefixedDBkey() .
+                                               " uses content model " . $content->getModel() );
+                               }
+                       }
+
+                       if ( $this->expandTemplates && !$this->parseContent ) {
+                               #XXX: implement template expansion for all content types in ContentHandler?
+                               if ( $content->getModel() === CONTENT_MODEL_WIKITEXT ) {
+                                       $text = $content->getNativeData();
+
+                                       $text = $wgParser->preprocess(
+                                               $text,
+                                               $title,
+                                               ParserOptions::newFromContext( $this->getContext() )
+                                       );
+                               } else {
+                                       $this->setWarning( "Template expansion is supported for wikitext only, " .
+                                               $title->getPrefixedDBkey() .
+                                               " uses content model " . $content->getModel() );
+                                       $vals['badcontentformat'] = '';
+                                       $text = false;
+                               }
+                       }
+                       if ( $this->parseContent ) {
+                               $po = $content->getParserOutput(
+                                       $title,
+                                       $revision->getId(),
+                                       ParserOptions::newFromContext( $this->getContext() )
+                               );
+                               $text = $po->getText();
+                       }
+
+                       if ( $text === null ) {
+                               $format = $this->contentFormat ? $this->contentFormat : $content->getDefaultFormat();
+                               $model = $content->getModel();
+
+                               if ( !$content->isSupportedFormat( $format ) ) {
+                                       $name = $title->getPrefixedDBkey();
+                                       $this->setWarning( "The requested format {$this->contentFormat} is not " .
+                                               "supported for content model $model used by $name" );
+                                       $vals['badcontentformat'] = '';
+                                       $text = false;
+                               } else {
+                                       $text = $content->serialize( $format );
+                                       // always include format and model.
+                                       // Format is needed to deserialize, model is needed to interpret.
+                                       $vals['contentformat'] = $format;
+                                       $vals['contentmodel'] = $model;
+                               }
+                       }
+
+                       if ( $text !== false ) {
+                               ApiResult::setContent( $vals, $text );
+                       }
+               }
+
+               if ( $content && ( !is_null( $this->diffto ) || !is_null( $this->difftotext ) ) ) {
+                       static $n = 0; // Number of uncached diffs we've had
+
+                       if ( $n < $this->getConfig()->get( 'APIMaxUncachedDiffs' ) ) {
+                               $vals['diff'] = array();
+                               $context = new DerivativeContext( $this->getContext() );
+                               $context->setTitle( $title );
+                               $handler = $revision->getContentHandler();
+
+                               if ( !is_null( $this->difftotext ) ) {
+                                       $model = $title->getContentModel();
+
+                                       if ( $this->contentFormat
+                                               && !ContentHandler::getForModelID( $model )->isSupportedFormat( $this->contentFormat )
+                                       ) {
+                                               $name = $title->getPrefixedDBkey();
+                                               $this->setWarning( "The requested format {$this->contentFormat} is not " .
+                                                       "supported for content model $model used by $name" );
+                                               $vals['diff']['badcontentformat'] = '';
+                                               $engine = null;
+                                       } else {
+                                               $difftocontent = ContentHandler::makeContent(
+                                                       $this->difftotext,
+                                                       $title,
+                                                       $model,
+                                                       $this->contentFormat
+                                               );
+
+                                               $engine = $handler->createDifferenceEngine( $context );
+                                               $engine->setContent( $content, $difftocontent );
+                                       }
+                               } else {
+                                       $engine = $handler->createDifferenceEngine( $context, $revision->getID(), $this->diffto );
+                                       $vals['diff']['from'] = $engine->getOldid();
+                                       $vals['diff']['to'] = $engine->getNewid();
+                               }
+                               if ( $engine ) {
+                                       $difftext = $engine->getDiffBody();
+                                       ApiResult::setContent( $vals['diff'], $difftext );
+                                       if ( !$engine->wasCacheHit() ) {
+                                               $n++;
+                                       }
+                               }
+                       } else {
+                               $vals['diff']['notcached'] = '';
+                       }
+               }
+
+               if ( $anyHidden && $revision->isDeleted( Revision::DELETED_RESTRICTED ) ) {
+                       $vals['suppressed'] = '';
+               }
+
+               return $vals;
+       }
+
+       public function getCacheMode( $params ) {
+               if ( $this->userCanSeeRevDel() ) {
+                       return 'private';
+               }
+
+               return 'public';
+       }
+
+       public function getAllowedParams() {
+               return array(
+                       'prop' => array(
+                               ApiBase::PARAM_ISMULTI => true,
+                               ApiBase::PARAM_DFLT => 'ids|timestamp|flags|comment|user',
+                               ApiBase::PARAM_TYPE => array(
+                                       'ids',
+                                       'flags',
+                                       'timestamp',
+                                       'user',
+                                       'userid',
+                                       'size',
+                                       'sha1',
+                                       'contentmodel',
+                                       'comment',
+                                       'parsedcomment',
+                                       'content',
+                                       'tags'
+                               ),
+                               ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-prop',
+                       ),
+                       'limit' => array(
+                               ApiBase::PARAM_TYPE => 'limit',
+                               ApiBase::PARAM_MIN => 1,
+                               ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
+                               ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2,
+                               ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-limit',
+                       ),
+                       'expandtemplates' => array(
+                               ApiBase::PARAM_DFLT => false,
+                               ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-expandtemplates',
+                       ),
+                       'generatexml' => array(
+                               ApiBase::PARAM_DFLT => false,
+                               ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-generatexml',
+                       ),
+                       'parse' => array(
+                               ApiBase::PARAM_DFLT => false,
+                               ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-parse',
+                       ),
+                       'section' => array(
+                               ApiBase::PARAM_DFLT => null,
+                               ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-section',
+                       ),
+                       'diffto' => array(
+                               ApiBase::PARAM_DFLT => null,
+                               ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-diffto',
+                       ),
+                       'difftotext' => array(
+                               ApiBase::PARAM_DFLT => null,
+                               ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-difftotext',
+                       ),
+                       'contentformat' => array(
+                               ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
+                               ApiBase::PARAM_DFLT => null,
+                               ApiBase::PARAM_HELP_MSG => 'apihelp-query+revisions+base-param-contentformat',
+                       ),
+               );
+       }
+
+}
index 946977d..5bc2efb 100644 (file)
@@ -481,10 +481,14 @@ class ApiResult extends ApiBase {
                        $continue = explode( '||', $continue );
                        $this->dieContinueUsageIf( count( $continue ) !== 2 );
                        $this->generatorDone = ( $continue[0] === '-' );
+                       $skip = explode( '|', $continue[1] );
                        if ( !$this->generatorDone ) {
                                $this->generatorParams = explode( '|', $continue[0] );
+                       } else {
+                               // When the generator is complete, don't run any modules that
+                               // depend on it.
+                               $skip += $this->continueGeneratedModules;
                        }
-                       $skip = explode( '|', $continue[1] );
                }
 
                $this->continueAllModules = array();
index 2bec759..485c776 100644 (file)
        "apihelp-query+allcategories-example-size": "List categories with information on the number of pages in each",
        "apihelp-query+allcategories-example-generator": "Retrieve info about the category page itself for categories beginning \"List\"",
 
+       "apihelp-query+alldeletedrevisions-description": "List all deleted revisions by a user or in a namespace.",
+       "apihelp-query+alldeletedrevisions-paraminfo-useronly": "May only be used with $3user.",
+       "apihelp-query+alldeletedrevisions-paraminfo-nonuseronly": "Cannot be used with $3user.",
+       "apihelp-query+alldeletedrevisions-param-start": "The timestamp to start enumerating from.",
+       "apihelp-query+alldeletedrevisions-param-end": "The timestamp to stop enumerating at.",
+       "apihelp-query+alldeletedrevisions-param-from": "Start listing at this title.",
+       "apihelp-query+alldeletedrevisions-param-to": "Stop listing at this title.",
+       "apihelp-query+alldeletedrevisions-param-prefix": "Search for all page titles that begin with this value.",
+       "apihelp-query+alldeletedrevisions-param-tag": "Only list revisions tagged with this tag.",
+       "apihelp-query+alldeletedrevisions-param-user": "Only list revisions by this user.",
+       "apihelp-query+alldeletedrevisions-param-excludeuser": "Don't list revisions by this user.",
+       "apihelp-query+alldeletedrevisions-param-namespace": "Only list pages in this namespace.",
+       "apihelp-query+alldeletedrevisions-param-miser-user-namespace": "'''NOTE:''' Due to [https://www.mediawiki.org/wiki/Manual:$wgMiserMode miser mode], using $1user and $1namespace together may result in fewer than \"$1limit\" results returned before continuing; in extreme cases, zero results may be returned.",
+       "apihelp-query+alldeletedrevisions-param-generatetitles": "When being used as a generator, generate titles rather than revision IDs.",
+       "apihelp-query+alldeletedrevisions-example-user": "List the last 50 deleted contributions by User:Example",
+       "apihelp-query+alldeletedrevisions-example-ns-main": "List the first 50 deleted revisions in the main namespace",
+
        "apihelp-query+allfileusages-description": "List all file usages, including non-existing.",
        "apihelp-query+allfileusages-param-from": "The title of the file to start enumerating from.",
        "apihelp-query+allfileusages-param-to": "The title of the file to stop enumerating at.",
        "apihelp-query+contributors-param-limit": "How many contributors to return.",
        "apihelp-query+contributors-example-simple": "Show contributors to the [[Main Page]]",
 
+       "apihelp-query+deletedrevisions-description": "Get deleted revision information.\n\nMay be used in several ways:\n# Get deleted revisions for a set of pages, by setting titles or pageids. Ordered by title and timestamp.\n# Get data about a set of deleted revisions by setting their IDs with revids. Ordered by revision ID.",
+       "apihelp-query+deletedrevisions-param-start": "The timestamp to start enumerating from. Ignored when processing a list of revision IDs.",
+       "apihelp-query+deletedrevisions-param-end": "The timestamp to stop enumerating at. Ignored when processing a list of revision IDs.",
+       "apihelp-query+deletedrevisions-param-tag": "Only list revisions tagged with this tag.",
+       "apihelp-query+deletedrevisions-param-user": "Only list revisions by this user.",
+       "apihelp-query+deletedrevisions-param-excludeuser": "Don't list revisions by this user.",
+       "apihelp-query+deletedrevisions-param-limit": "The maximum amount of revisions to list.",
+       "apihelp-query+deletedrevisions-param-prop": "Which properties to get:\n;revid:Adds the revision ID of the deleted revision.\n;parentid:Adds the revision ID of the previous revision to the page.\n;user:Adds the user who made the revision.\n;userid:Adds the user ID who made the revision.\n;comment:Adds the comment of the revision.\n;parsedcomment:Adds the parsed comment of the revision.\n;minor:Tags if the revision is minor.\n;len:Adds the length (bytes) of the revision.\n;sha1:Adds the SHA-1 (base 16) of the revision.\n;content:Adds the content of the revision.\n;tags:Tags for the revision.",
+       "apihelp-query+deletedrevisions-example-titles": "List the deleted revisions of [[Main Page]] and [[Talk:Main Page]], with content",
+       "apihelp-query+deletedrevisions-example-revids": "List the information for deleted revision 123456",
+
        "apihelp-query+deletedrevs-description": "List deleted revisions.\n\nOperates in three modes:\n# List deleted revisions for the given titles, sorted by timestamp.\n# List deleted contributions for the given user, sorted by timestamp (no titles specified).\n# List all deleted revisions in the given namespace, sorted by title and timestamp (no titles specified, $1user not set).\n\nCertain parameters only apply to some modes and are ignored in others.",
        "apihelp-query+deletedrevs-paraminfo-modes": "{{PLURAL:$1|Mode|Modes}}: $2",
        "apihelp-query+deletedrevs-param-start": "The timestamp to start enumerating from.",
 
        "apihelp-query+revisions-description": "Get revision information.\n\nMay be used in several ways:\n# Get data about a set of pages (last revision), by setting titles or pageids.\n# Get revisions for one given page, by using titles or pageids with start, end, or limit.\n# Get data about a set of revisions by setting their IDs with revids.",
        "apihelp-query+revisions-paraminfo-singlepageonly": "May only be used with a single page (mode #2).",
-       "apihelp-query+revisions-param-prop": "Which properties to get for each revision:\n;ids:The ID of the revision.\n;flags:Revision flags (minor).\n;timestamp:The timestamp of the revision.\n;user:User that made the revision.\n;userid:User ID of revision creator.\n;size:Length (bytes) of the revision.\n;sha1:SHA-1 (base 16) of the revision.\n;contentmodel:Content model ID.\n;comment:Comment by the user for revision.\n;parsedcomment:Parsed comment by the user for the revision.\n;content:Text of the revision.\n;tags:Tags for the revision.",
-       "apihelp-query+revisions-param-limit": "Limit how many revisions will be returned.",
        "apihelp-query+revisions-param-startid": "From which revision ID to start enumeration.",
        "apihelp-query+revisions-param-endid": "Stop revision enumeration on this revision ID.",
        "apihelp-query+revisions-param-start": "From which revision timestamp to start enumeration.",
        "apihelp-query+revisions-param-user": "Only include revisions made by user.",
        "apihelp-query+revisions-param-excludeuser": "Exclude revisions made by user.",
        "apihelp-query+revisions-param-tag": "Only list revisions tagged with this tag.",
-       "apihelp-query+revisions-param-expandtemplates": "Expand templates in revision content (requires $1prop=content).",
-       "apihelp-query+revisions-param-generatexml": "Generate XML parse tree for revision content (requires $1prop=content).",
-       "apihelp-query+revisions-param-parse": "Parse revision content (requires $1prop=content). For performance reasons, if this option is used, $1limit is enforced to 1.",
-       "apihelp-query+revisions-param-section": "Only retrieve the content of this section number.",
        "apihelp-query+revisions-param-token": "Which tokens to obtain for each revision.",
-       "apihelp-query+revisions-param-diffto": "Revision ID to diff each revision to. Use \"prev\", \"next\" and \"cur\" for the previous, next and current revision respectively.",
-       "apihelp-query+revisions-param-difftotext": "Text to diff each revision to. Only diffs a limited number of revisions. Overrides $1diffto. If $1section is set, only that section will be diffed against this text.",
-       "apihelp-query+revisions-param-contentformat": "Serialization format used for $1difftotext and expected for output of content.",
        "apihelp-query+revisions-example-content": "Get data with content for the last revision of titles \"API\" and \"Main Page\"",
        "apihelp-query+revisions-example-last5": "Get last 5 revisions of the \"Main Page\"",
        "apihelp-query+revisions-example-first5": "Get first 5 revisions of the \"Main Page\"",
        "apihelp-query+revisions-example-first5-not-localhost": "Get first 5 revisions of the \"Main Page\" that were not made made by anonymous user \"127.0.0.1\"",
        "apihelp-query+revisions-example-first5-user": "Get first 5 revisions of the \"Main Page\" that were made by the user \"MediaWiki default\"",
 
+       "apihelp-query+revisions+base-param-prop": "Which properties to get for each revision:\n;ids:The ID of the revision.\n;flags:Revision flags (minor).\n;timestamp:The timestamp of the revision.\n;user:User that made the revision.\n;userid:User ID of the revision creator.\n;size:Length (bytes) of the revision.\n;sha1:SHA-1 (base 16) of the revision.\n;contentmodel:Content model ID of the revision.\n;comment:Comment by the user for the revision.\n;parsedcomment:Parsed comment by the user for the revision.\n;content:Text of the revision.\n;tags:Tags for the revision.",
+       "apihelp-query+revisions+base-param-limit": "Limit how many revisions will be returned.",
+       "apihelp-query+revisions+base-param-expandtemplates": "Expand templates in revision content (requires $1prop=content).",
+       "apihelp-query+revisions+base-param-generatexml": "Generate XML parse tree for revision content (requires $1prop=content).",
+       "apihelp-query+revisions+base-param-parse": "Parse revision content (requires $1prop=content). For performance reasons, if this option is used, $1limit is enforced to 1.",
+       "apihelp-query+revisions+base-param-section": "Only retrieve the content of this section number.",
+       "apihelp-query+revisions+base-param-diffto": "Revision ID to diff each revision to. Use \"prev\", \"next\" and \"cur\" for the previous, next and current revision respectively.",
+       "apihelp-query+revisions+base-param-difftotext": "Text to diff each revision to. Only diffs a limited number of revisions. Overrides $1diffto. If $1section is set, only that section will be diffed against this text",
+       "apihelp-query+revisions+base-param-contentformat": "Serialization format used for $1difftotext and expected for output of content.",
+
        "apihelp-query+search-description": "Perform a full text search.",
        "apihelp-query+search-param-search": "Search for all page titles (or content) that have this value.",
        "apihelp-query+search-param-namespace": "Search only within these namespaces.",
index fbfe7d4..7e51a3f 100644 (file)
@@ -4,7 +4,8 @@
                        "Alirezaaa",
                        "Arash.pt",
                        "Fatemi127",
-                       "Reza1615"
+                       "Reza1615",
+                       "KhabarNegar"
                ]
        },
        "apihelp-main-param-action": "کدام عملیات را انجام دهد.",
        "apihelp-parse-example-page": "تجزیه یک صفحه.",
        "apihelp-parse-example-text": "تجزیه متن ویکی.",
        "apihelp-parse-example-summary": "تجزیه خلاصه.",
+       "apihelp-patrol-description": "گشت‌زنی یک صفحه یا نسخهٔ ویرایشی.",
+       "apihelp-patrol-example-rcid": "گشت‌زنی یک تغییر اخیر",
        "apihelp-patrol-example-revid": "گشت‌زدن یک نسخه",
        "apihelp-protect-description": "تغییر سطح محافظت صفحه",
        "apihelp-protect-param-reason": "دلیل برای (عدم) حفاظت.",
        "apihelp-protect-example-protect": "محافظت از صفحه",
+       "apihelp-protect-example-unprotect": "خارج ساختن صفحه از حفاظت با تغییر سطح حفاظتی به \"همگان\"",
+       "apihelp-protect-example-unprotect2": "خارج ساختن صفحه از حفاظت با قراردادن هیچ‌گونه محدودیت‌حفاظتی",
        "apihelp-purge-param-forcelinkupdate": "به‌روزرسانی جداول پیوندها.",
+       "apihelp-query+allfileusages-param-limit": "تعداد آیتم‌ها برای بازگرداندن.",
+       "apihelp-query+allfileusages-param-dir": "جهتی که باید فهرست شود.",
+       "apihelp-query+allfileusages-example-unique": "فهرست پرونده‌های با عنوان یکتا",
+       "apihelp-query+allfileusages-example-unique-generator": "گرفتن عنوان همهٔ پرونده‌ها، برچسب زدن موارد گم شده",
+       "apihelp-query+allfileusages-example-generator": "گرفتن صفحاتی که دارای پرونده هستند",
+       "apihelp-query+allimages-description": "متوالی شمردن همهٔ تصاویر.",
+       "apihelp-query+allimages-param-sort": "خصوصیت برای مرتب‌سازی بر پایه آن",
+       "apihelp-query+allimages-param-dir": "جهتی که باید فهرست شود.",
+       "apihelp-query+allimages-param-minsize": "محدودکردن به صفحه‌هایی که دست کم این تعداد بایت دارند.",
+       "apihelp-query+allimages-param-maxsize": "محدودکردن به صفحه‌هایی که حداکثر این تعداد بایت دارند.",
+       "apihelp-query+alllinks-param-namespace": "فضای نامی که باید شمرده شود.",
+       "apihelp-query+alllinks-param-limit": "تعداد آیتم‌ها برای بازگرداندن.",
+       "apihelp-query+alllinks-param-dir": "جهتی که باید فهرست شود.",
        "apihelp-query+allpages-param-filterredir": "صفحه‌هایی که باید فهرست شوند.",
        "apihelp-query+allpages-param-minsize": "محدودکردن به صفحه‌هایی که همراه دست کم این تعداد بایت است.",
        "apihelp-query+allredirects-param-limit": "تعداد آیتم‌ها برای بازگرداندن.",
index 2040094..5821c4a 100644 (file)
@@ -1,7 +1,8 @@
 {
        "@metadata": {
                "authors": [
-                       "Gomoko"
+                       "Gomoko",
+                       "Windes"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [https://www.mediawiki.org/wiki/API:Main_page Documentation]\n* [https://www.mediawiki.org/wiki/API:FAQ FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Liste de diffusion]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annonces de l’API]\n* [https://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts Bogues et demandes]\n</div>\n<strong>État :</strong> Toutes les fonctionnalités affichées sur cette page devraient fonctionner, mais l’API est encore en  cours de développement, et peut changer à tout moment. Inscrivez-vous à [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ la liste de diffusion mediawiki-api-announce] pour être informé des mises à jour.\n\n<strong>Demandes erronées :</strong> Qaund des demandes erronés sont envoyées à l’API, un entête HTTP sera renvoyé avec la clé « MediaWiki-API-Error » et à la fois la valeur de l’entête et le code d’erreur retourné prendront la même valeur. Pour plus d’information, voyez https://www.mediawiki.org/wiki/API:Errors_and_warnings.",
        "apihelp-query+allfileusages-description": "Lister toutes les utilisations de fichier, y compris ceux n’existant pas.",
        "apihelp-query+allfileusages-param-from": "Le titre du fichier depuis lequel commencer l’énumération.",
        "apihelp-query+allfileusages-param-to": "Le titre du fichier auquel arrêter l’énumération.",
-       "apihelp-query+allfileusages-param-prefix": "Rechercher tous les titres de fichier qui commencent par cette valeur.",
+       "apihelp-query+allfileusages-param-prefix": "Rechercher tous les fichiers dont le titre commence par cette valeur.",
        "apihelp-query+allfileusages-param-unique": "Afficher uniquement les titres de fichier distincts. Impossible à utiliser avec $1prop=ids.\nQuand utilisé comme générateur, produit les pages cibles au lieu des sources.",
        "apihelp-query+allfileusages-param-prop": "Quelles informations inclure :\n;ids:Ajoute l’ID de la page utilisatrice (impossible à utiliser avec $1unique).\n;title:Ajoute le titre du fichier.",
        "apihelp-query+allfileusages-param-limit": "Combien d’éléments renvoyer au total.",
        "apihelp-query+allimages-param-start": "L’horodatage depuis lequel énumérer. Ne peut être utilisé qu’avec $1sort=timestamp.",
        "apihelp-query+allimages-param-end": "L’horodatage de fin de l’énumération. Ne peut être utilisé qu’avec $1sort=timestamp.",
        "apihelp-query+allimages-param-prop": "Quelle information obtenir sur l’image :\n;timestamp:Ajoute l’horodatage de la version téléchargée.\n;user:Ajoute l’utilisateur qui a téléchargé la version de l’image.\n;userid:Ajoute l’ID de l’utilisateur qui a téléchargé la version de l’image.\n;comment:Commentaire sur la version.\n;parsedcomment:Analyser le commentaire de la version.\n;canonicaltitle:Ajoute le titre canonique du fichier image.\n;url:Fournit l’URL vers l’image et la page de description.\n;size:Ajoute la taille de l’image en octets et sa hauteur et largeur, et le numéro de page (si applicable).\n;dimensions:Alias de la taille.\n;sha1:Ajoute le hachage SHA-1 de l’image.\n;mime:Ajoute le type MIME de l’image.\n;mediatype:Ajoute le type de média de l’image.\n;metadata:Liste les métadonnées Exif de la version de l’image.\n;commonmetadata:Liste les métadonnées génériques du format de fichier pour la version de l’image.\n;extmetadata:Liste les métadonnées mises en forme regroupées depuis différentes sources. Les résultats sont au format HTML.\n;bitdepth:Ajoute la profondeur de couleur de la version.",
-       "apihelp-query+allimages-param-prefix": "Rechercher tous les titres d’image commençant par cette valeur. Utilisable uniquement avec $1sort=name.",
+       "apihelp-query+allimages-param-prefix": "Rechercher toutes les images dont le titre commence par cette valeur. Utilisable uniquement avec $1sort=name.",
        "apihelp-query+allimages-param-minsize": "Restreindre aux images avec au moins ce nombre d’octets.",
        "apihelp-query+allimages-param-maxsize": "Restreindre aux images avec au plus ce nombre d’octets.",
        "apihelp-query+allimages-param-sha1": "Hachage SHA1 de l’image. Écrase $1sha1base36.",
        "apihelp-query+allimages-param-mime": "Quel type MIME rechercher, par ex. image/jpeg.",
        "apihelp-query+allimages-param-limit": "Combien d’images renvoyer au total.",
        "apihelp-query+allimages-example-B": "Afficher une liste des fichiers commençant par la lettre « B »",
+       "apihelp-query+allimages-example-recent": "Afficher une liste des fichiers récemment téléchargés semblable à [[Special:NewFiles]]",
+       "apihelp-query+allimages-example-generator": "Afficher l’information sur 4 fichiers commençant par la lettre « T »",
+       "apihelp-query+alllinks-description": "Énumérer tous les liens pointant vers un espace de noms donné.",
+       "apihelp-query+alllinks-param-from": "Le titre du lien auquel démarrer l’énumération.",
+       "apihelp-query+alllinks-param-to": "Le titre du lien auquel arrêter l’énumération.",
+       "apihelp-query+alllinks-param-prefix": "Rechercher tous les titres liés commençant par cette valeur.",
+       "apihelp-query+alllinks-param-unique": "Afficher uniquement les titres liés distincts. Impossible à utiliser avec $1prop=ids.\nUtilisé avec un générateur, produit les pages cible au lieu des pages source.",
+       "apihelp-query+alllinks-param-prop": "Quelles informations inclure :\n;ids:Ajoute l’ID de la page de liaison (impossible à utiliser avec $1unique).\n;title:Ajoute le titre du lien.",
        "apihelp-query+alllinks-param-namespace": "L’espace de noms à énumérer.",
        "apihelp-query+alllinks-param-limit": "Combien d’éléments renvoyer au total.",
        "apihelp-query+alllinks-param-dir": "La direction dans laquelle lister.",
        "apihelp-query+allpages-param-limit": "Combien de pages renvoyer au total.",
        "apihelp-query+allpages-param-dir": "La direction dans laquelle lister.",
        "apihelp-query+allpages-param-filterlanglinks": "Filtrer si une page a des liens de langue. Noter que cela ne prend pas en compte les liens de langue ajoutés par des extensions.",
+       "apihelp-query+allpages-param-prexpiry": "Quelle expiration de protection sur laquelle filtrer la page :\n;indefinite:N’obtenir que les pages avec une expiration de protection infinie.\n;definite:N’obtenir que les pages avec une expiration de protection définie (spécifique).\n;all:Obtenir toutes les pages avec une expiration de protection.",
+       "apihelp-query+allpages-example-B": "Afficher une liste des pages commençant par la lettre « B »",
+       "apihelp-query+allpages-example-generator": "Afficher l’information sur 4 pages commençant par la lettre « T »",
+       "apihelp-query+allpages-example-generator-revisions": "Afficher le contenu des 2 premières pages hors redirections commençant par « Re »",
+       "apihelp-query+allredirects-description": "Lister toutes les redirections vers un espace de noms.",
+       "apihelp-query+allredirects-param-from": "Le titre de la redirection auquel démarrer l’énumération.",
+       "apihelp-query+allredirects-param-to": "Le titre de la redirection auquel arrêter l’énumération.",
+       "apihelp-query+allredirects-param-prefix": "Rechercher toutes les pages cible commençant par cette valeur.",
+       "apihelp-query+allredirects-param-unique": "Afficher uniquement les pages cibles distinctes. Impossible à utiliser avec $1prop=ids|fragment|interwiki.\nUtilisé avec un générateur, produit les pages cible au lieu des pages source.",
+       "apihelp-query+allredirects-param-prop": "Quelles informations inclure :\n;ids:Ajoute l’ID de la page de redirection (impossible à utiliser avec $1unique).\n;title:Ajoute le titre de la redirection.\n;fragment:Ajoute le fragment de la redirection, s’il y en a un (impossible à utiliser avec $1unique).\n;interwiki:Ajoute le préfixe interwiki de la redirection, s’il y en a un (impossible à utiliser avec $1unique).",
+       "apihelp-query+allredirects-param-namespace": "L’espace de noms à énumérer.",
+       "apihelp-query+allredirects-param-limit": "Combien d’éléments renvoyer au total.",
+       "apihelp-query+allredirects-param-dir": "La direction dans laquelle lister.",
+       "apihelp-query+allredirects-example-B": "Lister les pages cible, y compris les manquantes, avec les IDs de page d’où ils proviennent, en commençant à B",
+       "apihelp-query+allredirects-example-unique": "Lister les pages cible unique",
+       "apihelp-query+allredirects-example-unique-generator": "Obtient toutes les pages cible, en marquant les manquantes",
+       "apihelp-query+allredirects-example-generator": "Obtient les pages contenant les redirections",
+       "apihelp-query+alltransclusions-description": "Lister toutes les transclusions (pages intégrées en utilisant &#123;&#123;x&#125;&#125;), y compris les inexistantes.",
+       "apihelp-query+alltransclusions-param-from": "Le titre de la transclusion depuis lequel commencer l’énumération.",
+       "apihelp-query+alltransclusions-param-to": "Le titre de la transclusion auquel arrêter l’énumération.",
+       "apihelp-query+alltransclusions-param-prefix": "Rechercher tous les titres inclus qui commencent par cette valeur.",
+       "apihelp-query+alltransclusions-param-unique": "Afficher uniquement les titres inclus. Impossible à utiliser avec $1prop=ids.\nUtilisé avec un générateur, produit les pages cible plutôt que les pages source.",
+       "apihelp-query+alltransclusions-param-prop": "Quelles informations inclure :\n;ids:Ajoute l’ID de la page incluse (impossible à utiliser avec $1unique).\n;title:Ajoute le titre de la transclusion.",
+       "apihelp-query+alltransclusions-param-namespace": "L’espace de noms à énumérer.",
+       "apihelp-query+alltransclusions-param-limit": "Combien d’éléments renvoyer au total.",
+       "apihelp-query+alltransclusions-param-dir": "La direction dans laquelle lister.",
+       "apihelp-query+alltransclusions-example-B": "Lister les titres inclus, y compris les manquants, avec les IDs des pages d’où ils viennent, en commençant à B",
+       "apihelp-query+alltransclusions-example-unique": "Lister les titres inclus uniques",
+       "apihelp-query+alltransclusions-example-unique-generator": "Obtient tous les titres inclus, en marquant les manquants",
+       "apihelp-query+alltransclusions-example-generator": "Obtient les pages contenant des transclusions",
+       "apihelp-query+allusers-description": "Énumérer tous les utilisateurs enregistrés.",
+       "apihelp-query+allusers-param-from": "Le nom d’utilisateur auquel démarrer l’énumération.",
+       "apihelp-query+allusers-param-to": "Le nom d’utilisateur auquel stopper l’énumération.",
+       "apihelp-query+allusers-param-prefix": "Rechercher tous les utilisateurs commençant par cette valeur.",
+       "apihelp-query+allusers-param-dir": "Direction du tri.",
+       "apihelp-query+allusers-param-group": "Inclure uniquement les utilisateurs dans les groupes donnés.",
+       "apihelp-query+allusers-param-excludegroup": "Exclure les utilisateurs dans les groupes donnés.",
+       "apihelp-query+allusers-param-rights": "Inclure uniquement les utilisateurs avec les droits indiqués. Ne comprend pas les droits accordés par des groupes implicites ou auto-promus comme *, user ou autoconfirmed.",
+       "apihelp-query+allusers-param-prop": "Quelles informations inclure :\n;blockinfo:Ajoute l’information sur le bloc actuel d’un utilisateur.\n;groups:Liste des groupes auxquels appartient l’utilisateur. Cela utilise beaucoup de ressources du serveur et peut renvoyer moins de résultats que la limite.\n;implicitgroups:Liste tous les groupes auxquels l’utilisateur est affecté automatiquement.\n;rights:Liste les droits qu’à l’utilisateur.\n;editcount:Ajoute le compteur de modifications de l’utilisateur.\n;registration:Ajoute l’horodatage de l’inscription de l’utilisateur, s’il est disponible (peut être vide).",
+       "apihelp-query+allusers-param-limit": "Combien de noms d’utilisateur renvoyer au total.",
+       "apihelp-query+allusers-param-witheditsonly": "Ne lister que les utilisateurs qui ont fait des modifications.",
+       "apihelp-query+allusers-param-activeusers": "Lister uniquement les utilisateurs actifs durant {{PLURAL:$1|le dernier jour|les $1 derniers jours}}.",
+       "apihelp-query+allusers-example-Y": "Lister les utilisateurs en commençant à Y",
+       "apihelp-query+backlinks-description": "Trouver toutes les pages qui ont un lien vers la page donnée.",
+       "apihelp-query+backlinks-param-title": "Titre à rechercher. Impossible à utiliser avec $1pageid.",
+       "apihelp-query+backlinks-param-pageid": "ID de la page à chercher. Impossible à utiliser avec $1title.",
+       "apihelp-query+backlinks-param-namespace": "L’espace de noms à énumérer.",
+       "apihelp-query+backlinks-param-dir": "La direction dans laquelle lister.",
+       "apihelp-query+backlinks-param-filterredir": "Comment filtrer les redirections. Si positionné à nonredirects quand $1redirect est activé, cela ne s’applique qu’au second niveau.",
+       "apihelp-query+backlinks-param-limit": "Combien de pages renvoyer au total. Si $1redirect est activé, la limite s’applique à chaque niveau séparément (ce qui signifie que vous pouvez obtenir jusqu’à 2 * limite résultats).",
+       "apihelp-query+backlinks-param-redirect": "Si le lien vers une page est une redirection, trouver toutes les pages qui ont un lien vers cette redirection aussi. La limite maximale est divisée par deux.",
+       "apihelp-query+backlinks-example-simple": "Afficher les liens vers [[Main page]]",
+       "apihelp-query+backlinks-example-generator": "Obtenir des informations sur les pages ayant un lien vers [[Main page]]",
+       "apihelp-query+blocks-description": "Lister tous les utilisateurs et les adresses IP bloqués.",
+       "apihelp-query+blocks-param-start": "L’horodatage auquel démarrer l’énumération.",
+       "apihelp-query+blocks-param-end": "L’horodatage auquel arrêter l’énumération.",
+       "apihelp-query+blocks-param-ids": "Liste des IDs de bloc à lister (facultatif).",
+       "apihelp-query+blocks-param-users": "Liste des utilisateurs à rechercher (facultatif).",
+       "apihelp-query+blocks-param-ip": "Obtenir tous les blocs s’appliquant à cette adresse IP ou à cette plage CIDR, y compris les blocs de plage.\nImpossible à utiliser avec $3users. Les plages CIDR plus larges que IPv4/$1 ou IPv6/$2 ne sont pas acceptées.",
+       "apihelp-query+blocks-param-limit": "Le nombre maximal de blocs à lister.",
+       "apihelp-query+blocks-param-prop": "Quelles propriétés obtenir :\n;id:Ajoute l’ID du blocage.\n;user:Ajoute le nom de l’utilisateur bloqué.\n;userid:Ajoute l’ID de l’utilisateur bloqué.\n;by:Ajoute le nom de l’utilisateur ayant bloqué.\n;byid:Ajoute l’ID de l’utilisateur ayant bloqué.\n;timestamp:Ajoute l’horodatage du blocage.\n;expiry:Ajoute l’horodatage d’expiration du blocage.\n;reason:Ajoute le motif du blocage.\n;range:Ajoute la plage d&adresses IP affectée par le blocage.\n;flags:Marque le bannissement avec (autoblock, anononly, etc.).",
+       "apihelp-query+blocks-param-show": "Afficher uniquement les éléments correspondant à ces critères.\nPar exemple, pour voir uniquement les blocages infinis sur les adresses IP, mettre $1show=ip|!temp.",
+       "apihelp-query+blocks-example-simple": "Lister les blocages",
+       "apihelp-query+blocks-example-users": "Lister les blocages des utilisateurs Alice et Bob",
+       "apihelp-query+categories-description": "Lister toutes les catégories auxquelles les pages appartiennent.",
+       "apihelp-query+categories-param-prop": "Quelles propriétés supplémentaires obtenir de chaque catégorie :\n;sortkey:Ajoute la clé de tri (chaîne hexadécimale) et son préfixe (partie lisible) de la catégorie.\n;timestamp:Ajoute l’horodatage de l’ajout de la catégorie.\n;hidden:Marque els catégories cachées avec _&#95;HIDDENCAT_&#95;.",
+       "apihelp-query+categories-param-show": "Quelle sorte de catégories afficher.",
+       "apihelp-query+categories-param-limit": "Combien de catégories renvoyer.",
+       "apihelp-query+categories-param-categories": "Lister uniquement ces catégories. Utile pour vérifier si une certaine page est dans une certaine catégorie.",
+       "apihelp-query+categories-param-dir": "La direction dans laquelle lister.",
+       "apihelp-query+categories-example-simple": "Obtenir une liste des catégories auxquelles appartient [[Albert Einstein]]",
+       "apihelp-query+categories-example-generator": "Obtenir des informations sur toutes les catégories utilisées dans [[Albert Einstein]]",
+       "apihelp-query+categoryinfo-description": "Renvoie les informations sur les catégories données.",
+       "apihelp-query+categoryinfo-example-simple": "Obtenir des informations sur [[:Category:Foo]] et [[:Category:Bar]]",
+       "apihelp-query+categorymembers-description": "Lister toutes les pages d’une catégorie donnée.",
+       "apihelp-query+categorymembers-param-title": "Quelle catégorie énumérer (obligatoire). Doit comprendre le préfixe « Category: ». Impossible à utiliser avec $1pageid.",
+       "apihelp-query+categorymembers-param-pageid": "ID de la page de la catégorie à énumérer. Impossible à utiliser avec $1title.",
+       "apihelp-query+categorymembers-param-prop": "Quelles informations inclure :\n;ids:Ajoute l’ID de la page.\n;title:Ajoute le titre et l’ID de l’espace de noms de la page.\n;sortkey:Ajoute la clé de tri utilisée pour trier dans la catégorie (chaîne hexadécimale).\n;sortkeyprefix:Ajoute le préfixe de la clé de tri utilisé pour trier dans la catégorie (partie lisible de la clé de tri).\n;type:Ajoute le type dans lequel a été catégorisée la page (page, sous-catégorie ou fichier).\n;timestamp:Ajoute l’horodatage de l’inclusion de la page.",
+       "apihelp-query+categorymembers-param-namespace": "Inclure uniquement les pages dans ces espaces de nom. Remarquez que $1type=subcat ou $1type=file peuvent  être utilisés à la place de $1namespace=14 or 6.",
+       "apihelp-query+categorymembers-param-type": "Quel type de membres de la catégorie inclure. Ignoré quand $1sort=timestamp est positionné.",
+       "apihelp-query+categorymembers-param-limit": "Le nombre maximal de pages à renvoyer.",
+       "apihelp-query+categorymembers-param-sort": "Propriété par laquelle trier.",
+       "apihelp-query+categorymembers-param-dir": "Dans quelle direction trier.",
+       "apihelp-query+categorymembers-param-start": "Horodatage auquel démarrer la liste. Peut être utilisé uniquement avec $1sort=timestamp.",
+       "apihelp-query+categorymembers-param-end": "Horodatage auquel terminer la liste. Peut être utilisé uniquement avec $1sort=timestamp.",
        "apihelp-format-example-generic": "Mettre en forme le résultat de la requête dans le format $1",
        "apihelp-dbg-description": "Extraire les données au format de var_export() de PHP.",
        "apihelp-dbgfm-description": "Extraire les données au format de var_export() de PHP (affiché proprement en HTML).",
index 0170f23..8363d0d 100644 (file)
@@ -1,7 +1,8 @@
 {
        "@metadata": {
                "authors": [
-                       "Guycn2"
+                       "Guycn2",
+                       "Amire80"
                ]
        },
        "apihelp-main-param-action": "איזו פעולה לבצע.",
@@ -11,7 +12,7 @@
        "apihelp-query+categories-param-limit": "כמה קטגוריות להחזיר.",
        "apihelp-query+tokens-example-types": "אחזור אסימון של רשימת המעקב ואסימון של ניטור",
        "apihelp-xml-param-xslt": "אם צוין, מוסיף &lt;xslt&gt; כגליון סגנונות. זה צריך להיות דף ויקי במרחב השם מדיה ויקי ששמו מסתיים ב\".xsl\".",
-       "api-help-title": "×¢×\96ר×\94 ×\91Ö¾MediaWiki API",
+       "api-help-title": "×¢×\96ר×\94 ×©×\9c MediaWiki API",
        "api-help-lead": "זהו דף תיעוד של API שנוצר באופן אוטומטי.\n\nתיעוד ודוגמאות: https://www.mediawiki.org/wiki/API",
        "api-help-main-header": "יחידה ראשית",
        "api-help-flag-deprecated": "יחידה זו אינה מומלצת לשימוש.",
        "api-help-param-required": "פרמטר זה נדרש.",
        "api-help-param-list": "{{PLURAL:$1|1=ערך אחד|2=ערכים (מופרדים באמצעות \"{{!}}\")}}: $2",
        "api-help-param-limit": "מספר הפרמטרים לא יכול להיות גדול מ־$1.",
-       "api-help-param-limit2": "×\94×\9eספר ×\94×\9e×\99ר×\91×\99 ×\94×\9e×\95תר ×\94×\95×\90 $1 ($2 ×¢×\91×\95ר ×\91×\95×\98×\99×\9d).",
+       "api-help-param-limit2": "×\94×\9eספר ×\94×\9eר×\91×\99 ×\94×\9e×\95תר ×\94×\95×\90 $1 (×¢×\91×\95ר ×\91×\95×\98×\99×\9d â\80\93 $2).",
        "api-help-param-integer-min": "ה{{PLURAL:$1|1=ערך|2=ערכים}} לא יכולים להיות קטנים מ־$2.",
        "api-help-param-integer-max": "ה{{PLURAL:$1|1=ערך לא יכול להיות גדול|2=ערכים לא יכולים להיות גדולים}} מ־$3.",
        "api-help-param-integer-minmax": "ה{{PLURAL:$1|1=ערך חייב|2=ערכים חייבים}} להיות בין $2 ל־$3.",
        "api-help-param-multi-separate": "הפרדה בין ערכים נעשית באמצעות \"|\".",
-       "api-help-param-multi-max": "×\9eספר ×\94ער×\9b×\99×\9d ×\94×\9e×\99ר×\91×\99 ×\94×\95×\90 {{PLURAL:$1|$1}} ({{PLURAL:$2|$2}} ×¢×\91×\95ר ×\91×\95×\98×\99×\9d).",
+       "api-help-param-multi-max": "×\9eספר ×\94ער×\9b×\99×\9d ×\94×\9eר×\91×\99 ×\94×\95×\90 {{PLURAL:$1|$1}} (×¢×\91×\95ר ×\91×\95×\98×\99×\9d â\80\93 {{PLURAL:$2|$2}}).",
        "api-help-param-default": "ברירת מחדל: $1",
        "api-help-param-default-empty": "ברירת מחדל: <span class=\"apihelp-empty\">(ריק)</span>",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(ללא תיאור)</span>",
diff --git a/includes/api/i18n/jam.json b/includes/api/i18n/jam.json
new file mode 100644 (file)
index 0000000..3c44fd2
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Chabi1"
+               ]
+       },
+       "api-help-main-header": "Mien madyuul"
+}
index 1d08bf5..4040c1f 100644 (file)
@@ -53,6 +53,7 @@
        "apihelp-query+images-example-simple": "Eng Lëscht vun de Fichiere kréien déi op der [[Main Page|Haaptsäit]] benotzt ginn",
        "apihelp-query+imageusage-example-simple": "Säite weisen déi [[:File:Albert Einstein Head.jpg]] benotzen",
        "apihelp-query+langlinks-param-lang": "Nëmme Sproochlinke mat dësem Sproochcode zréckginn.",
+       "apihelp-query+protectedtitles-param-namespace": "Nëmmen Titelen aus dësen Nummraim opzielen.",
        "apihelp-query+recentchanges-param-user": "Nëmmen Ännerunge vun dësem Benotzer opzielen.",
        "apihelp-query+recentchanges-example-simple": "Rezent Ännerunge weisen",
        "apihelp-query+revisions-example-last5": "Déi lescht 5 Versioune vun der \"Haaptsäit\" kréien",
index 6ef1d31..c6fcec7 100644 (file)
        "apihelp-protect-param-pageid": "Назнака на страница што се (од)заштитува. Не може да се користи заедно со $1title.",
        "apihelp-protect-param-reason": "Причиина за (од)заштитување",
        "apihelp-protect-example-protect": "Заштити страница",
+       "apihelp-purge-param-forcelinkupdate": "Поднови ги табелите со врски.",
        "apihelp-purge-example-simple": "Превчитај ги „Главна страница“ и „Прилог“",
        "apihelp-query-param-list": "Кои списоци да се набават.",
        "apihelp-query-param-meta": "Кои метаподатоци да се набават.",
+       "apihelp-query+allcategories-description": "Наброј ги сите категории.",
+       "apihelp-query+allcategories-param-from": "Од која категорија да почне набројувањето.",
+       "apihelp-query+allcategories-param-to": "На која категорија да запре набројувањето.",
+       "apihelp-query+allcategories-param-dir": "Насока на подредувањето.",
        "apihelp-query+backlinks-example-simple": "Прикажи врски до [[Главна страница|Главната страница]]",
        "apihelp-query+backlinks-example-generator": "Дава информации за страниците што водат до [[Главна страница|Главната страница]]",
        "apihelp-query+blocks-description": "Список на сите блокирани корисници и IP-адреси",
        "apihelp-query+blocks-param-ids": "Список на назнаки на блоковите за испис (незадолжително)",
        "apihelp-query+blocks-param-users": "Список на корисници што ќе се пребаруваат (незадолжително)",
        "apihelp-query+imageinfo-param-urlheight": "Слично на $1urlwidth.",
+       "apihelp-query+revisions-example-last5": "Дај ги последните 5 преработки на „Главна страница“",
+       "apihelp-query+revisions-example-first5": "Дај ги првите 5 преработки на „Главна страница“",
+       "apihelp-query+revisions-example-first5-after": "Дај ги првите 5 преработки на „Главна страница“ направени по 2006-05-01 (1 мај 2006 г.)",
+       "apihelp-query+revisions-example-first5-not-localhost": "Дај ги првите 5 преработки на „Главна страница“ кои не се направени од анонимниот корисник „127.0.0.1“",
+       "apihelp-query+revisions-example-first5-user": "Дај ги првите 5 преработки на „Главна страница“ кои се направени од корисникот „зададен од МедијаВики“ (MediaWiki default)",
+       "apihelp-query+search-example-simple": "Побарај „meaning“",
+       "apihelp-query+search-example-text": "Побарај го „meaning“ по текстовите",
+       "apihelp-query+search-example-generator": "Дај информации за страниците што излегуваат во резултатите од пребарувањето на „meaning“",
+       "apihelp-query+siteinfo-description": "Дај општи информации за мрежното место.",
+       "apihelp-upload-param-filename": "Целно име на податотеката.",
+       "apihelp-upload-param-comment": "Коментар при подигање. Се користи и како првичен текст на страницата за нови податотеки ако не е укажано „$1text“.",
+       "apihelp-upload-param-text": "Првичен текст на страницата за нови податотеки.",
+       "apihelp-upload-param-watch": "Набљудувај ја страницата.",
+       "apihelp-upload-param-watchlist": "Безусловно додај или отстрани ја страницата од набљудуваните, користете ги нагодувањата или не ги менувајте набљудуваните.",
+       "apihelp-upload-param-ignorewarnings": "Занемари предупредувања.",
+       "apihelp-upload-param-file": "Содржина на податотеката.",
+       "apihelp-upload-param-url": "Од која URL-адреса да се преземе податотеката.",
+       "apihelp-upload-param-filekey": "Клуч на претходното подигање кое е привремено складирано.",
+       "apihelp-upload-param-sessionkey": "Исто што и $1filekey. Се одржува за назадна складност.",
+       "apihelp-upload-param-stash": "Ако е зададено, опслужувачот нема да ја стави податотеката во складиштето за привремено чување.",
+       "apihelp-upload-param-filesize": "Големина на целото подигање.",
+       "apihelp-upload-param-offset": "Зафатнина на делот во бајти.",
+       "apihelp-upload-param-chunk": "Содржина на делот.",
+       "apihelp-upload-param-async": "Направи ги работите со потенцијално големи податотеки неусогласени, кога е можно.",
+       "apihelp-upload-param-asyncdownload": "Направи го добивањето на URL-адреса неусогласено.",
+       "apihelp-upload-param-leavemessage": "Ако се користи неусогласено преземање, остави порака на страницата за разговор на корисникот ако е готово.",
+       "apihelp-upload-param-statuskey": "Дај ја состојбата на подигнатост за овој податотечен клуч (подигање по URL-адреса).",
+       "apihelp-upload-param-checkstatus": "Дај ја состојбата на подигнатост само за дадениот податотечен клуч.",
+       "apihelp-upload-example-url": "Подигни од URL-адреса",
+       "apihelp-userrights-param-userid": "Корисничка назнака.",
+       "apihelp-userrights-param-add": "Стави го корисникот во следниве групи.",
+       "apihelp-userrights-param-remove": "Отстрани го корисникот од следниве групи.",
+       "apihelp-userrights-param-reason": "Причина за промената.",
+       "apihelp-watch-example-watch": "Набљудувај ја страницата „Главна страница“",
+       "apihelp-watch-example-unwatch": "Отстрани ја страницата „Главна страница“ од набљудуваните",
+       "apihelp-watch-example-generator": "Набљудувај ги првите неколку страници во главниот именски простор",
        "apihelp-format-example-generic": "Форматирај го резултатот од барањето во $1-формат",
        "apihelp-dbg-description": "Давај го изводот во PHP-форматот var_export().",
        "apihelp-dbgfm-description": "Давај го изводот во PHP-форматот var_export() (подобрен испис во HTML).",
        "apihelp-yamlfm-description": "Давај го изводот во YAML-формат (подобрен испис во HTML).",
        "api-format-title": "Резултат од Прилогот на МедијаВики",
        "api-format-prettyprint-header": "Ја гледате HTML-претставата на форматот $1. HTML е добар за отстранување на грешки, но не е погоден за употреб во прилог.\n\nУкажете го параметарот за формат за да го смените изводниот формат. За да ги видите претставите на форматот $1 вон HTML, задајте format=$2.\n\nПовеќе информации ќе најдете на [https://www.mediawiki.org/wiki/API целосната документација], или пак [[Special:ApiHelp/main|помош со прилогот]].",
+       "api-orm-param-props": "Полиња за пребарување.",
+       "api-orm-param-limit": "Макс. број на редови во изводот.",
+       "api-pageset-param-titles": "Список на наслови на кои ќе се работи",
+       "api-pageset-param-pageids": "Список на назнаки за страници на кои ќе се работи",
+       "api-pageset-param-revids": "Список на назнаки на преработки на кои ќе се работи",
+       "api-pageset-param-generator": "Дај го списокот на страници на кои ќе се работи исполнувајќи го укажаниот модул за барање.\n\n'''НАПОМЕНА:''' називите на создавачките параметри мора да ја имаат претставката „g“. Погледајте ги примерите.",
        "api-help-title": "Помош со Прилогот на МедијаВики",
        "api-help-lead": "Ова е самосоздадена документациска страница за Прилогот на МедијаВики.\n\nDocumentation and examples: https://www.mediawiki.org/wiki/API",
        "api-help-main-header": "Главен модул",
        "api-help-param-default": "По основно: $1",
        "api-help-param-default-empty": "По основно: <span class=\"apihelp-empty\">(празно)</span>",
        "api-help-param-token": "Шифра „$1“ добиена од [[Special:ApiHelp/query+tokens|action=query&meta=tokens]]",
+       "api-help-param-token-webui": "За складност, се прифаќа и шифрата што се користи за обичниот кориснички посредник.",
+       "api-help-param-disabled-in-miser-mode": "Исклучено поради [https://www.mediawiki.org/wiki/Manual:$wgMiserMode скржавиот режим].",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(нема опис)</span>",
        "api-help-examples": "{{PLURAL:$1|Пример|Примери}}:",
        "api-help-permissions": "{{PLURAL:$1|Дозвола|Дозволи}}:",
index 95dd194..6b10584 100644 (file)
@@ -7,6 +7,7 @@
        "apihelp-main-param-action": "Tindakan mana untuk dilakukan.",
        "apihelp-main-param-format": "Format output.",
        "apihelp-main-param-uselang": "Bahasa yang hendak digunakan untuk penterjemahan mesej. Senarai kod boleh diperoleh dari [[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo&siprop=languages]], ataupun menyatakan \"user\" untuk menggunakan bahasa kegemaran pengguna semasa.",
+       "apihelp-expandtemplates-example-simple": "Perluaskan \"<nowiki>{{Project:Sandbox}}</nowiki>\" wikiteks",
        "apihelp-help-param-helpformat": "Format output bantuan.",
        "apihelp-help-example-main": "Bantuan untuk modul utama",
        "apihelp-help-example-recursive": "Segala bantuan dalam satu halaman",
index 1dabdb9..8ead891 100644 (file)
@@ -3,15 +3,16 @@
                "authors": [
                        "Siebrand",
                        "Sjoerddebruin",
-                       "Robin0van0der0vliet"
+                       "Robin0van0der0vliet",
+                       "Mar(c)"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [https://www.mediawiki.org/wiki/API:Main_page Documentatie]\n* [https://www.mediawiki.org/wiki/API:FAQ FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api E-maillijst]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-aankondigingen]\n* [https://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts Bugs & verzoeken]\n</div>\n<strong>Status:</strong> Alle funties die op deze pagina worden weergegeven horen te werken. Aan de API wordt actief gewerkt, en deze kan gewijzigd worden. Abonneer u op  de [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ e-maillijst mediawiki-api-announce] voor meldingen over aanpassingen.\n\n<strong>Foutieve verzoeken:</strong> als de API foutieve verzoeken ontvangt, wordt er geantwoord met een HTTP-header met de sleutel \"MediaWiki-API-Error\" en daarna worden de waarde van de header en de foutcode op dezelfde waarde ingesteld. Zie https://www.mediawiki.org/wiki/API:Errors_and_warnings voor meer informatie.",
        "apihelp-main-param-action": "Welke handeling uit te voeren.",
        "apihelp-main-param-format": "De opmaak van de uitvoer.",
        "apihelp-main-param-maxlag": "De maximale vertraging kan gebruikt worden als MediaWiki is geïnstalleerd op een databasecluster die gebruik maakt van replicatie. Om te voorkomen dat handelingen nog meer databasereplicatievertraging veroorzaken, kan deze parameter er voor zorgen dat de client wacht totdat de replicatievertraging lager is dan de aangegeven waarde. In het geval van buitensporige vertraging, wordt de foutcode \"maxlag\" teruggegeven met een bericht als \"Waiting for $host: $lag seconds lagged\".<br />Zie https://www.mediawiki.org/wiki/Manual:Maxlag_parameter voor mee informatie.",
-       "apihelp-main-param-smaxage": "Stelt de header \"s-maxage\" in op het aangegeven aantal seocnden. Foutmeldingen komen nooit in de cache.",
-       "apihelp-main-param-maxage": "Stelt de header \"max-age\" in op het aangegeven aantal seocnden. Foutmeldingen komen nooit in de cache.",
+       "apihelp-main-param-smaxage": "Stelt de header \"s-maxage\" in op het aangegeven aantal seconden. Foutmeldingen komen nooit in de cache.",
+       "apihelp-main-param-maxage": "Stelt de header \"max-age\" in op het aangegeven aantal seconden. Foutmeldingen komen nooit in de cache.",
        "apihelp-block-description": "Gebruiker blokkeren.",
        "apihelp-block-param-reason": "Reden voor blokkade.",
        "apihelp-edit-example-edit": "Pagina bewerken",
index 25ba0b2..ce058e9 100644 (file)
        "apihelp-query+allcategories-param-prop": "{{doc-apihelp-param|query+allcategories|prop}}",
        "apihelp-query+allcategories-example-size": "{{doc-apihelp-example|query+allcategories}}",
        "apihelp-query+allcategories-example-generator": "{{doc-apihelp-example|query+allcategories}}",
+
+       "apihelp-query+alldeletedrevisions-description": "{{doc-apihelp-description|query+alldeletedrevisions}}",
+       "apihelp-query+alldeletedrevisions-paraminfo-nonuseronly": "{{doc-apihelp-paraminfo|query+alldeletedrevisions|nonuseronly}}",
+       "apihelp-query+alldeletedrevisions-paraminfo-useronly": "{{doc-apihelp-paraminfo|query+alldeletedrevisions|useronly}}",
+       "apihelp-query+alldeletedrevisions-param-end": "{{doc-apihelp-param|query+alldeletedrevisions|end}}",
+       "apihelp-query+alldeletedrevisions-param-excludeuser": "{{doc-apihelp-param|query+alldeletedrevisions|excludeuser}}",
+       "apihelp-query+alldeletedrevisions-param-from": "{{doc-apihelp-param|query+alldeletedrevisions|from}}",
+       "apihelp-query+alldeletedrevisions-param-generatetitles": "{{doc-apihelp-param|query+alldeletedrevisions|generatetitles}}",
+       "apihelp-query+alldeletedrevisions-param-miser-user-namespace": "{{doc-apihelp-param|query+alldeletedrevisions|miser-user-namespace}}",
+       "apihelp-query+alldeletedrevisions-param-namespace": "{{doc-apihelp-param|query+alldeletedrevisions|namespace}}",
+       "apihelp-query+alldeletedrevisions-param-prefix": "{{doc-apihelp-param|query+alldeletedrevisions|prefix}}",
+       "apihelp-query+alldeletedrevisions-param-start": "{{doc-apihelp-param|query+alldeletedrevisions|start}}",
+       "apihelp-query+alldeletedrevisions-param-tag": "{{doc-apihelp-param|query+alldeletedrevisions|tag}}",
+       "apihelp-query+alldeletedrevisions-param-to": "{{doc-apihelp-param|query+alldeletedrevisions|to}}",
+       "apihelp-query+alldeletedrevisions-param-user": "{{doc-apihelp-param|query+alldeletedrevisions|user}}",
+       "apihelp-query+alldeletedrevisions-example-ns-main": "{{doc-apihelp-example|query+alldeletedrevisions}}",
+       "apihelp-query+alldeletedrevisions-example-user": "{{doc-apihelp-example|query+alldeletedrevisions}}",
+
        "apihelp-query+allfileusages-description": "{{doc-apihelp-description|query+allfileusages}}",
        "apihelp-query+allfileusages-param-from": "{{doc-apihelp-param|query+allfileusages|from}}",
        "apihelp-query+allfileusages-param-to": "{{doc-apihelp-param|query+allfileusages|to}}",
        "apihelp-query+contributors-param-excluderights": "{{doc-apihelp-param|query+contributors|excluderights}}",
        "apihelp-query+contributors-param-limit": "{{doc-apihelp-param|query+contributors|limit}}",
        "apihelp-query+contributors-example-simple": "{{doc-apihelp-example|query+contributors}}",
+
+       "apihelp-query+deletedrevisions-description": "{{doc-apihelp-description|query+deletedrevisions}}",
+       "apihelp-query+deletedrevisions-param-end": "{{doc-apihelp-param|query+deletedrevisions|end}}",
+       "apihelp-query+deletedrevisions-param-excludeuser": "{{doc-apihelp-param|query+deletedrevisions|excludeuser}}",
+       "apihelp-query+deletedrevisions-param-limit": "{{doc-apihelp-param|query+deletedrevisions|limit}}",
+       "apihelp-query+deletedrevisions-param-prop": "{{doc-apihelp-param|query+deletedrevisions|prop}}",
+       "apihelp-query+deletedrevisions-param-start": "{{doc-apihelp-param|query+deletedrevisions|start}}",
+       "apihelp-query+deletedrevisions-param-tag": "{{doc-apihelp-param|query+deletedrevisions|tag}}",
+       "apihelp-query+deletedrevisions-param-user": "{{doc-apihelp-param|query+deletedrevisions|user}}",
+       "apihelp-query+deletedrevisions-example-revids": "{{doc-apihelp-example|query+deletedrevisions}}",
+       "apihelp-query+deletedrevisions-example-titles": "{{doc-apihelp-example|query+deletedrevisions}}",
+
        "apihelp-query+deletedrevs-description": "{{doc-apihelp-description|query+deletedrevs}}",
        "apihelp-query+deletedrevs-paraminfo-modes": "{{doc-apihelp-paraminfo|query+deletedrevs|modes}}\n{{Identical|Mode}}",
        "apihelp-query+deletedrevs-param-start": "{{doc-apihelp-param|query+deletedrevs|start}}",
        "apihelp-query+redirects-example-generator": "{{doc-apihelp-example|query+redirects}}",
        "apihelp-query+revisions-description": "{{doc-apihelp-description|query+revisions}}",
        "apihelp-query+revisions-paraminfo-singlepageonly": "{{doc-apihelp-paraminfo|query+revisions|singlepageonly}}",
-       "apihelp-query+revisions-param-prop": "{{doc-apihelp-param|query+revisions|prop}}",
-       "apihelp-query+revisions-param-limit": "{{doc-apihelp-param|query+revisions|limit}}",
        "apihelp-query+revisions-param-startid": "{{doc-apihelp-param|query+revisions|startid}}",
        "apihelp-query+revisions-param-endid": "{{doc-apihelp-param|query+revisions|endid}}",
        "apihelp-query+revisions-param-start": "{{doc-apihelp-param|query+revisions|start}}",
        "apihelp-query+revisions-param-user": "{{doc-apihelp-param|query+revisions|user}}",
        "apihelp-query+revisions-param-excludeuser": "{{doc-apihelp-param|query+revisions|excludeuser}}",
        "apihelp-query+revisions-param-tag": "{{doc-apihelp-param|query+revisions|tag}}",
-       "apihelp-query+revisions-param-expandtemplates": "{{doc-apihelp-param|query+revisions|expandtemplates}}",
-       "apihelp-query+revisions-param-generatexml": "{{doc-apihelp-param|query+revisions|generatexml}}",
-       "apihelp-query+revisions-param-parse": "{{doc-apihelp-param|query+revisions|parse}}",
-       "apihelp-query+revisions-param-section": "{{doc-apihelp-param|query+revisions|section}}",
        "apihelp-query+revisions-param-token": "{{doc-apihelp-param|query+revisions|token}}",
-       "apihelp-query+revisions-param-diffto": "{{doc-apihelp-param|query+revisions|diffto}}",
-       "apihelp-query+revisions-param-difftotext": "{{doc-apihelp-param|query+revisions|difftotext}}",
-       "apihelp-query+revisions-param-contentformat": "{{doc-apihelp-param|query+revisions|contentformat}}",
        "apihelp-query+revisions-example-content": "{{doc-apihelp-example|query+revisions}}",
        "apihelp-query+revisions-example-last5": "{{doc-apihelp-example|query+revisions}}",
        "apihelp-query+revisions-example-first5": "{{doc-apihelp-example|query+revisions}}",
        "apihelp-query+revisions-example-first5-after": "{{doc-apihelp-example|query+revisions}}",
        "apihelp-query+revisions-example-first5-not-localhost": "{{doc-apihelp-example|query+revisions}}",
        "apihelp-query+revisions-example-first5-user": "{{doc-apihelp-example|query+revisions}}",
+
+       "apihelp-query+revisions+base-param-contentformat": "{{doc-apihelp-param|query+revisions+base|contentformat|description=the \"contentformat\" parameter to revision querying modules|noseealso=1}}",
+       "apihelp-query+revisions+base-param-diffto": "{{doc-apihelp-param|query+revisions+base|diffto|description=the \"diffto\" parameter to revision querying modules|noseealso=1}}",
+       "apihelp-query+revisions+base-param-difftotext": "{{doc-apihelp-param|query+revisions+base|difftotext|description=the \"difftotext\" parameter to revision querying modules|noseealso=1}}",
+       "apihelp-query+revisions+base-param-expandtemplates": "{{doc-apihelp-param|query+revisions+base|expandtemplates|description=the \"expandtemplates\" parameter to revision querying modules|noseealso=1}}",
+       "apihelp-query+revisions+base-param-generatexml": "{{doc-apihelp-param|query+revisions+base|generatexml|description=the \"generatexml\" parameter to revision querying modules|noseealso=1}}",
+       "apihelp-query+revisions+base-param-limit": "{{doc-apihelp-param|query+revisions+base|limit|description=the \"limit\" parameter to revision querying modules|noseealso=1}}",
+       "apihelp-query+revisions+base-param-parse": "{{doc-apihelp-param|query+revisions+base|parse|description=the \"parse\" parameter to revision querying modules|noseealso=1}}",
+       "apihelp-query+revisions+base-param-prop": "{{doc-apihelp-param|query+revisions+base|prop|description=the \"prop\" parameter to revision querying modules|noseealso=1}}",
+       "apihelp-query+revisions+base-param-section": "{{doc-apihelp-param|query+revisions+base|section|description=the \"section\" parameter to revision querying modules|noseealso=1}}",
+
        "apihelp-query+search-description": "{{doc-apihelp-description|query+search}}",
        "apihelp-query+search-param-search": "{{doc-apihelp-param|query+search|search}}",
        "apihelp-query+search-param-namespace": "{{doc-apihelp-param|query+search|namespace}}",
index 9ace145..ae40cd9 100644 (file)
@@ -1,9 +1,52 @@
 {
        "@metadata": {
                "authors": [
-                       "Jopparn"
+                       "Jopparn",
+                       "Lokal Profil",
+                       "WikiPhoenix"
                ]
        },
+       "apihelp-main-param-curtimestamp": "Inkludera den aktuella tidsstämpeln i resultatet.",
+       "apihelp-block-description": "Blockera en användare.",
+       "apihelp-block-param-user": "Användare, IP-adress eller IP-intervall du vill blockera.",
+       "apihelp-block-param-reason": "Orsak till blockering.",
+       "apihelp-block-param-anononly": "Blockera endast anonyma användare (t.ex. inaktivera anonyma redigeringar för denna IP-adress).",
+       "apihelp-block-param-nocreate": "Förhindra registrering av användarkonton.",
+       "apihelp-createaccount-description": "Skapa ett nytt användarkonto.",
+       "apihelp-createaccount-param-name": "Användarnamn.",
+       "apihelp-createaccount-param-password": "Lösenord (ignoreras om $1mailpassword angetts).",
+       "apihelp-createaccount-param-email": "Användarens e-postadress (valfritt).",
+       "apihelp-createaccount-param-realname": "Användarens riktiga namn (valfritt).",
+       "apihelp-createaccount-example-pass": "Skapa användaren \"testuser\" med lösenordet \"test123\"",
+       "apihelp-delete-description": "Radera en sida.",
+       "apihelp-delete-param-watch": "Lägg till sidan i din bevakningslista.",
+       "apihelp-delete-param-unwatch": "Ta bort sidan från din bevakningslista.",
+       "apihelp-delete-example-simple": "Radera huvudsidan",
+       "apihelp-delete-example-reason": "Raderar huvudsidan med orsaken \"Förbereder flyttning\"",
+       "apihelp-disabled-description": "Denna modul har inaktiverats.",
+       "apihelp-edit-description": "Skapa och redigera sidor.",
+       "apihelp-edit-param-text": "Sidans innehåll.",
+       "apihelp-edit-param-summary": "Redigeringssammanfattning. Även avsnittets rubrik när $1section=new och $1sectiontitle inte anges.",
+       "apihelp-edit-param-minor": "Mindre redigering.",
+       "apihelp-edit-param-watch": "Lägg till sidan i din bevakningslista.",
+       "apihelp-edit-param-unwatch": "Ta bort sidan från din bevakningslista.",
+       "apihelp-edit-example-edit": "Redigera en sida",
+       "apihelp-emailuser-description": "Skicka e-post till en användare.",
+       "apihelp-expandtemplates-param-title": "Sidans rubrik.",
+       "apihelp-expandtemplates-param-text": "Wikitext att konvertera.",
+       "apihelp-feedcontributions-param-year": "Från år (och tidigare).",
+       "apihelp-feedcontributions-param-month": "Från månad (och tidigare).",
+       "apihelp-feedrecentchanges-param-hideminor": "Dölj mindre ändringar.",
+       "apihelp-feedrecentchanges-param-hidebots": "Dölj robotändringar.",
+       "apihelp-feedrecentchanges-param-hideanons": "Dölj ändringar av oinloggade användare.",
+       "apihelp-feedrecentchanges-param-hideliu": "Dölj ändringar av inloggade användare.",
+       "apihelp-feedrecentchanges-param-hidepatrolled": "Dölj patrullerade ändringar.",
+       "apihelp-feedrecentchanges-param-hidemyself": "Dölj mina ändringar.",
+       "apihelp-feedrecentchanges-param-tagfilter": "Filtrera efter tagg.",
+       "apihelp-feedrecentchanges-param-target": "Visa endast ändringarna av sidor som den här sidan länkar till.",
+       "apihelp-feedrecentchanges-example-simple": "Visa senaste ändringar",
+       "apihelp-feedrecentchanges-example-30days": "Visa senaste ändringar för 30 dygn",
+       "apihelp-filerevert-param-comment": "Ladda upp kommentar.",
        "apihelp-help-example-recursive": "All hjälp på en sida",
        "apihelp-help-example-help": "Hjälp för själva hjälpmodulen",
        "api-help-main-header": "Huvudmodul",
index d52c9c0..32175ed 100644 (file)
        "apihelp-edit-param-unwatch": "将页面从您的监视列表移除。",
        "apihelp-edit-param-redirect": "自动解析重定向。",
        "apihelp-edit-example-edit": "编辑一个页面",
+       "apihelp-edit-example-prepend": "页面中预置_&#95;NOTOC_&#95;",
        "apihelp-emailuser-description": "电子邮件联系一位用户。",
        "apihelp-emailuser-param-target": "电子邮件的目标用户。",
+       "apihelp-emailuser-param-subject": "主题页眉。",
        "apihelp-emailuser-param-text": "邮件正文。",
        "apihelp-emailuser-param-ccme": "给我发送一份该邮件的副本。",
+       "apihelp-emailuser-example-email": "向用户“WikiSysop”发送邮件,带文字“Content”",
        "apihelp-expandtemplates-description": "展开维基文本中的所有模板。",
        "apihelp-expandtemplates-param-title": "页面标题。",
        "apihelp-expandtemplates-param-text": "要转换的wiki文本。",
        "apihelp-help-example-query": "两个查询子模块的帮助",
        "apihelp-import-param-summary": "导入摘要。",
        "apihelp-import-param-xml": "上传的XML文件。",
+       "apihelp-import-param-interwikipage": "用于跨wiki导入:导入的页面。",
        "apihelp-import-param-rootpage": "导入作为此页面的子页面。",
+       "apihelp-import-example-import": "将页面[[meta:Help:Parserfunctions]]连带完整历史导入至100名字空间。",
        "apihelp-login-param-name": "用户名。",
        "apihelp-login-param-password": "密码。",
        "apihelp-login-param-domain": "域名(可选)。",
+       "apihelp-login-example-gettoken": "检索登录令牌",
        "apihelp-login-example-login": "登录",
        "apihelp-move-description": "移动一个页面。",
        "apihelp-move-param-reason": "移动原因。",
        "apihelp-opensearch-param-search": "搜索字符串。",
        "apihelp-opensearch-param-namespace": "搜索的名字空间。",
        "apihelp-opensearch-param-suggest": "如果[https://www.mediawiki.org/wiki/Manual:$wgEnableOpenSearchSuggest $wgEnableOpenSearchSuggest]设置为false则不做任何事情。",
+       "apihelp-opensearch-param-format": "输出格式。",
+       "apihelp-opensearch-example-te": "查找以“Te”开头的页面",
        "apihelp-options-example-reset": "重置所有用户设置",
+       "apihelp-options-example-change": "更改“皮肤”和“hideminot”设置",
+       "apihelp-options-example-complex": "重置所有设置,之后设置“皮肤”和“昵称”",
        "apihelp-paraminfo-description": "获取关于 API 模块的信息。",
+       "apihelp-paraminfo-param-modules": "模块名称列表(action=和format=参数值,或“主”)。可通过“+”指定子模块。",
        "apihelp-paraminfo-param-helpformat": "帮助字符串的格式。",
+       "apihelp-paraminfo-example-1": "显示[[Special:ApiHelp/parse|action=parse]]、[[Special:ApiHelp/jsonfm|format=jsonfm]]、[[Special:ApiHelp/query+allpages|action=query&list=allpages]]和[[Special:ApiHelp/query+siteinfo|action=query&meta=siteinfo]]的信息",
        "apihelp-parse-param-summary": "所要解析的摘要。",
        "apihelp-parse-param-page": "解析此页的内容。不能与$1text和$1title一起使用。",
        "apihelp-parse-param-pageid": "解析此页的内容。覆盖 $1 页。",
        "apihelp-query+allpages-param-maxsize": "限于至多这么多字节的页面。",
        "apihelp-query+allpages-param-prtype": "仅限于受保护页面。",
        "apihelp-query+allredirects-description": "列出至一个名字空间的重定向。",
+       "apihelp-query+allredirects-param-namespace": "要列举的名字空间。",
        "apihelp-query+allusers-param-witheditsonly": "只列出有编辑的用户。",
        "apihelp-query+allusers-param-activeusers": "只列出最近$1天内活跃的用户。",
        "apihelp-query+allusers-example-Y": "列出以Y开头的用户",
        "apihelp-query+backlinks-example-generator": "获取关于链接至[[首页]]的页面的信息",
        "apihelp-query+blocks-description": "列出所有被封禁的用户和IP地址。",
        "apihelp-query+blocks-example-simple": "封禁列表",
+       "apihelp-query+categories-param-show": "显示何种分类。",
+       "apihelp-query+categories-param-limit": "返回多少分类。",
        "apihelp-query+categoryinfo-example-simple": "获取有关[[:Category:Foo]]和[[:Category:Bar]]的信息",
        "apihelp-query+categorymembers-param-sort": "要作为排序方式的属性。",
        "apihelp-query+categorymembers-param-startsortkey": "请改用$1starthexsortkey。",
        "apihelp-query+contributors-example-simple": "显示[[首页]]的贡献",
        "apihelp-query+deletedrevs-paraminfo-modes": "{{PLURAL:$1|模式}}:$2",
        "apihelp-query+deletedrevs-param-namespace": "只列出此名字空间的页面。",
+       "apihelp-query+deletedrevs-example-mode1": "列出最近已删除的对首页和Talk:首页的贡献,带内容(模式1)",
+       "apihelp-query+deletedrevs-example-mode2": "列出由Bob作出的最近50次已删除贡献(模式2)",
+       "apihelp-query+deletedrevs-example-mode3-main": "列出前50次主名字空间已删除贡献(模式3)",
+       "apihelp-query+deletedrevs-example-mode3-talk": "列出前50次讨论名字空间已删除页面(模式3):",
+       "apihelp-query+disabled-description": "此查询模块已被禁用。",
+       "apihelp-query+duplicatefiles-param-limit": "返回多少重复文件。",
        "apihelp-query+duplicatefiles-example-simple": "查找与[[:File:Albert Einstein Head.jpg]]重复的文件",
        "apihelp-query+duplicatefiles-example-generated": "查找所有文件的重复文件",
        "apihelp-query+embeddedin-param-title": "要搜索的标题。不能与$1pageid一起使用。",
        "apihelp-query+embeddedin-param-namespace": "列举的名字空间。",
        "apihelp-query+embeddedin-example-simple": "显示嵌入[[Template:Stub]]的页面",
        "apihelp-query+embeddedin-example-generator": "获取有关显示嵌入[[Template:Stub]]的页面的信息",
+       "apihelp-query+extlinks-param-limit": "返回多少链接。",
        "apihelp-query+exturlusage-param-limit": "返回多少页面。",
        "apihelp-query+exturlusage-example-simple": "显示链接至http://www.mediawiki.org的页面",
+       "apihelp-query+filearchive-example-simple": "显示已删除文件列表",
        "apihelp-query+fileusage-param-limit": "返回多少。",
+       "apihelp-query+fileusage-example-simple": "获取使用[[:File:Example.jpg]]的页面列表",
+       "apihelp-query+fileusage-example-generator": "获取有关使用[[:File:Example.jpg]]的页面的信息",
+       "apihelp-query+imageinfo-description": "返回文件信息和上传历史。",
        "apihelp-query+imageinfo-param-urlheight": "与$1urlwidth类似。",
        "apihelp-query+images-param-limit": "返回多少文件。",
        "apihelp-query+images-example-simple": "获取[[首页]]使用的文件列表",
        "apihelp-query+iwbacklinks-param-prefix": "跨维基前缀。",
        "apihelp-query+iwbacklinks-example-simple": "获取链接至[[wikibooks:Test]]的页面",
        "apihelp-query+iwbacklinks-example-generator": "获取有关链接至[[wikibooks:Test]]的页面的信息",
+       "apihelp-query+iwlinks-param-limit": "返回多少跨wiki链接。",
+       "apihelp-query+iwlinks-param-prefix": "只返回此前缀的跨wiki链接。",
        "apihelp-query+iwlinks-param-title": "用于搜索的跨wiki链接。必须与$1prefix一起使用。",
        "apihelp-query+iwlinks-example-simple": "从[[首页]]获取跨wiki链接",
        "apihelp-query+langbacklinks-param-lang": "用于语言链接的语言。",
        "apihelp-query+linkshere-example-simple": "获取链接至[[首页]]的页面列表",
        "apihelp-query+linkshere-example-generator": "获取有关链接至[[首页]]的页面的信息",
        "apihelp-query+logevents-description": "从日志获取事件。",
+       "apihelp-query+pagepropnames-example-simple": "获取前10个常用名称",
        "apihelp-query+pageprops-example-simple": "获取用于[[:Category:Foo]]的属性",
+       "apihelp-query+pageswithprop-example-simple": "列出前10个使用&#123;&#123;DISPLAYTITLE:&#125;&#125;的页面",
+       "apihelp-query+pageswithprop-example-generator": "获取有关前10个使用_&#95;NOTOC_&#95;的页面的信息",
        "apihelp-query+prefixsearch-param-search": "搜索字符串。",
        "apihelp-query+prefixsearch-param-namespace": "搜索的名字空间。",
        "apihelp-query+protectedtitles-example-simple": "受保护标题列表",
+       "apihelp-query+querypage-param-limit": "返回的结果数。",
        "apihelp-query+querypage-example-ancientpages": "返回[[Special:Ancientpages]]的结果。",
+       "apihelp-query+random-param-namespace": "只返回这些名字空间的页面。",
        "apihelp-query+recentchanges-description": "枚举最近更改。",
        "apihelp-query+recentchanges-param-user": "只列出此用户的更改。",
        "apihelp-query+recentchanges-param-excludeuser": "不要列出此用户的更改。",
        "apihelp-query+revisions-example-last5": "获取“首页”的最近5次修订",
        "apihelp-query+revisions-example-first5": "获取“首页”的前5次修订版本",
        "apihelp-query+revisions-example-first5-after": "获取“首页”于2006年05月01日之后做出的前5次修订版本",
+       "apihelp-query+search-param-info": "要返回的元数据。",
+       "apihelp-query+search-param-interwiki": "搜索结果中包含跨wiki结果,如果可用。",
        "apihelp-query+search-example-simple": "搜索“意义”",
        "apihelp-query+search-example-text": "搜索文本“意义”",
        "apihelp-query+siteinfo-example-simple": "获取网站信息",
        "apihelp-query+siteinfo-example-interwiki": "获取本地跨wiki前缀列表",
        "apihelp-query+tags-description": "列出更改标签。",
        "apihelp-query+tags-example-simple": "可用标签列表",
+       "apihelp-query+templates-param-limit": "返回多少模板。",
+       "apihelp-query+templates-param-templates": "只列出这些模板。对于检查某一页面使用某一模板很有用。",
        "apihelp-query+templates-example-simple": "从[[首页]]获取模板",
+       "apihelp-query+templates-example-generator": "获取有关[[首页]]中的模板页面的信息",
+       "apihelp-query+templates-example-namespaces": "从[[首页]]获取用户和模板名字空间中的模板",
+       "apihelp-query+transcludedin-param-namespace": "至包含这些名字空间的页面。",
+       "apihelp-query+transcludedin-param-limit": "返回多少。",
+       "apihelp-query+transcludedin-example-simple": "获取嵌入[[首页]]的页面列表",
+       "apihelp-query+transcludedin-example-generator": "获取有关嵌入[[首页]]的页面的信息",
+       "apihelp-query+usercontribs-description": "获取一位用户的所有编辑。",
+       "apihelp-query+usercontribs-param-namespace": "只列出这些名字空间的贡献。",
        "apihelp-query+usercontribs-example-user": "显示[[User:Example]]的贡献",
+       "apihelp-query+usercontribs-example-ipprefix": "显示来自“192.0.2.”前缀所有IP地址的贡献",
        "apihelp-query+userinfo-description": "获取有关当前用户的信息。",
        "apihelp-query+userinfo-example-simple": "获取有关当前用户的信息",
        "apihelp-query+userinfo-example-data": "获取有关当前用户的额外信息",
        "apihelp-query+users-description": "获取有关列出用户的信息。",
        "apihelp-query+users-param-token": "请改用[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]。",
        "apihelp-query+users-example-simple": "返回[[User:Example]]的信息",
+       "apihelp-query+watchlist-param-user": "只列出此用户的更改。",
+       "apihelp-query+watchlist-param-excludeuser": "不要列出此用户的更改。",
        "apihelp-query+watchlistraw-description": "获取登录用户的监视列表的所有页面。",
        "apihelp-query+watchlistraw-param-namespace": "只列出指定名字空间的页面。",
        "apihelp-revisiondelete-param-reason": "删除或恢复的原因。",
+       "apihelp-rollback-example-simple": "回退由用户Example对[[首页]]做出的最近编辑",
+       "apihelp-rollback-example-summary": "回退由IP用户192.0.2.5对[[首页]]做出的最近编辑,带编辑摘要“回退破坏”,并将这些编辑和回退标记为“机器人”",
        "apihelp-rsd-description": "导出一个RSD(Really Simple Discovery)架构",
        "apihelp-rsd-example-simple": "导出RSD架构",
        "apihelp-unblock-description": "解封一位用户。",
+       "apihelp-unblock-param-id": "解封时需要的封禁ID(通过list=blocks获得)。不能与$1user一起使用。",
        "apihelp-unblock-param-user": "要解封的用户名、IP地址或IP段。不能与$1id一起使用。",
        "apihelp-unblock-param-reason": "解封的原因。",
        "apihelp-unblock-example-id": "解封封禁ID#105",
        "apihelp-watch-example-watch": "监视页面“首页”",
        "apihelp-watch-example-unwatch": "取消监视页面“首页”",
        "apihelp-none-description": "不输出任何东西。",
+       "apihelp-yaml-description": "以YAML格式输出数据。",
        "api-format-title": "MediaWiki API 结果",
        "api-orm-param-props": "要查询的字段。",
        "api-help-title": "MediaWiki API 帮助",
index 9abc22f..1038f71 100644 (file)
@@ -7,6 +7,8 @@
        },
        "apihelp-main-param-action": "要執行的動作。",
        "apihelp-main-param-format": "輸出的格式。",
+       "apihelp-login-param-name": "使用者名稱。",
+       "apihelp-userrights-param-user": "使用者名稱。",
        "apihelp-format-example-generic": "格式化查詢結果為 $1 格式",
        "apihelp-dbg-description": "使用 PHP 的 var_export() 格式輸出資料。",
        "apihelp-dbgfm-description": "使用 PHP 的 var_export() 格式輸出資料 (使用 HTML 格式顯示)。",
index 1868073..b22df39 100644 (file)
@@ -800,7 +800,7 @@ class ORMTable extends DBAccessBase implements IORMTable {
        private function stripFieldPrefix( array $fieldNames ) {
                $start = strlen( $this->fieldPrefix );
 
-               return array_map( function( $fieldName ) use ( $start ) {
+               return array_map( function ( $fieldName ) use ( $start ) {
                        return substr( $fieldName, $start );
                }, $fieldNames );
        }
index 7164bfa..f5d2445 100644 (file)
  * @file
  */
 
+if ( !interface_exists( '\Psr\Log\LoggerInterface' ) ) {
+       $message = <<<TXT
+MediaWiki requires the <a href="https://github.com/php-fig/log">PSR-3 logging library</a> to be present. This library is not embedded directly in MediaWiki's git repository and must be installed separately by the end user.
+
+Please see <a href="https://www.mediawiki.org/wiki/Download_from_Git#Fetch_external_libraries">mediawiki.org</a> for help on installing the required components.
+TXT;
+       echo $message;
+       trigger_error( $message, E_USER_ERROR );
+       die( 1 );
+}
+
 /**
  * PSR-3 logging service.
  *
index f994da3..daf3f51 100644 (file)
@@ -83,7 +83,7 @@ class MWLoggerLegacyLogger extends \Psr\Log\AbstractLogger {
                if ( $channel === 'wfLogDBError' ) {
                        // wfLogDBError messages are emitted if a database log location is
                        // specfied.
-                       $shouldEmit = (bool) $wgDBerrorLog;
+                       $shouldEmit = (bool)$wgDBerrorLog;
 
                } elseif ( $channel === 'wfErrorLog' ) {
                        // All messages on the wfErrorLog channel should be emitted.
@@ -216,7 +216,7 @@ class MWLoggerLegacyLogger extends \Psr\Log\AbstractLogger {
                }
 
                // Workaround for https://bugs.php.net/bug.php?id=52063
-               // Can be removed when min PHP > 5.3.2
+               // Can be removed when min PHP > 5.3.6
                if ( $cachedTimezone === null ) {
                        $d = date_create( 'now' );
                } else {
index 51e14a2..a3d34fa 100644 (file)
@@ -50,7 +50,7 @@ class MWLoggerLegacySpi implements MWLoggerSpi {
         */
        public function getLogger( $channel ) {
                if ( !isset( $this->singletons[$channel] ) ) {
-                       $this->singletons[$channel] = new MWLoggerLegacyLogger($channel);
+                       $this->singletons[$channel] = new MWLoggerLegacyLogger( $channel );
                }
                return $this->singletons[$channel];
        }
index 1472459..02ab309 100644 (file)
@@ -168,7 +168,7 @@ class MWLoggerMonologHandler extends \Monolog\Handler\AbstractProcessingHandler
                        $this->openSink();
                }
 
-               $text = (string) $record['formatted'];
+               $text = (string)$record['formatted'];
                if ( $this->useUdp() ) {
 
                        // Clean it up for the multiplexer
index 03ba0b2..02fca3d 100644 (file)
@@ -25,8 +25,9 @@
  * 'exception-nologin' as a title and 'exception-nologin-text' for the message.
  *
  * @note In order for this exception to redirect, the error message passed to the
- * constructor has to be explicitly added to LoginForm::validErrorMessages. Otherwise,
- * the user will just be shown the message rather than redirected.
+ * constructor has to be explicitly added to LoginForm::validErrorMessages or with
+ * the LoginFormValidErrorMessages hook. Otherwise, the user will just be shown the message
+ * rather than redirected.
  *
  * @par Example:
  * @code
@@ -52,7 +53,8 @@
 class UserNotLoggedIn extends ErrorPageError {
 
        /**
-        * @note The value of the $reasonMsg parameter must be put into LoginForm::validErrorMessages
+        * @note The value of the $reasonMsg parameter must be put into LoginForm::validErrorMessages or
+        * set with the LoginFormValidErrorMessages Hook.
         * if you want the user to be automatically redirected to the login form.
         *
         * @param string $reasonMsg A message key containing the reason for the error.
@@ -77,7 +79,7 @@ class UserNotLoggedIn extends ErrorPageError {
        public function report() {
                // If an unsupported message is used, don't try redirecting to Special:Userlogin,
                // since the message may not be compatible.
-               if ( !in_array( $this->msg, LoginForm::$validErrorMessages ) ) {
+               if ( !in_array( $this->msg, LoginForm::getValidErrorMessages() ) ) {
                        parent::report();
                }
 
index da5e85c..eac9423 100644 (file)
@@ -10,6 +10,6 @@ class HTMLTagFilter extends HTMLFormField {
                        // we only need the select field, HTMLForm should handle the label
                        return $tagFilterSelector;
                }
-               return;
+               return '';
        }
 }
index 1cbe7d5..cfe4d83 100644 (file)
        "config-localsettings-cli-upgrade": "È stato scummigliato nu file <code>LocalSettings.php</code>.\nPe l'agghiurnà sta installazione, secutate <code>update.php</code>",
        "config-localsettings-key": "Chiave d'agghiurnamiento:",
        "config-localsettings-badkey": "'A chiave c'avete dato nun è curretta.",
+       "config-upgrade-key-missing": "S'è scummigliata n'installazione 'e MediaWiki ch'esisteva già.\nPe' ll'agghiurnà, nzertate pe' piacere sta riga ccà abbascio dint' 'a parta vascia d' 'o <code>LocalSettings.php</code> vuosto:\n\n$1",
+       "config-localsettings-incomplete": "'O file <code>LocalSettings.php</code> esistente pare ca fosse cumpleto a metà.\n'A variabbele $1 nun è mpustata.\nCagnate <code>LocalSettings.php</code> in modo ca sta variabbele fosse mpustata e facite clic ncopp'a \"{{int:Config-continue}}\".",
+       "config-localsettings-connection-error": "S'è truvato n'errore pe' tramente ca se faceva 'a connessione a 'o database ausanno 'e mpustaziune specificate dint'a <code>LocalSettings.php</code>. Pe' piacere curriggite sti mpustaziuni e provate n'ata vota.\n\n$1",
+       "config-session-error": "Errore facenno accumincià 'a sessione: $1",
+       "config-session-expired": "'E date d' 'a sessione pareno ammaturate.\n'E sessiune so' configurate pe na durata 'e $1.\n'A putite aumentà pe' bbìa 'e na mpustazione  <code>session.gc_maxlifetime</code> dint' 'o file php.ini.\nRiabbìa 'o prucesso 'e installazione.",
+       "config-no-session": "'E date d' 'a sessione so' state perdute!\nCuntrullate 'o file php.ini vuosto e assicurateve ca 'a <code>session.save_path</code> è stata mpustata ncopp'a na cartella appropriata.",
+       "config-your-language": "'A lengua vosta:",
+       "config-your-language-help": "Scigliete na lengua pe' l'ausà pe' tramente ca se fa 'o prucesso 'installazione.",
+       "config-wiki-language": "Lengua d' 'o wiki:",
+       "config-wiki-language-help": "Scigliete 'a lengua ca sarrà ausàta prevalentemente ncopp' 'a wiki.",
+       "config-back": "← Arreto",
+       "config-continue": "Annanze →",
+       "config-page-language": "Lengua",
+       "config-page-welcome": "Bemmenute a MediaWiki!",
+       "config-page-dbconnect": "Connessione a 'o database",
+       "config-page-upgrade": "Agghiuorna l'istallazione esistente",
+       "config-page-dbsettings": "Mpustaziune d' 'o database",
+       "config-page-name": "Nomme",
+       "config-page-options": "Opziune",
+       "config-page-install": "Installa",
+       "config-page-complete": "Cumpreta!",
+       "config-page-restart": "Riabbìa l'installazione",
+       "config-page-readme": "Lieggeme",
+       "config-page-releasenotes": "Note 'e verziona",
+       "config-page-copying": "Copia",
+       "config-page-upgradedoc": "Agghiurnanno",
+       "config-page-existingwiki": "Wiki esistente",
+       "config-help-restart": "Vulite scancellà tutt' 'e date astipate c'avite nzertato e riabbià 'o prucesso d'installazione?",
+       "config-restart": "Sì, riabbìa",
+       "config-welcome": "=== Cuntrollo 'e ll'ambiente ===\nSarranno eseguite 'e cuntrolle bbase pe' putè vedè si st'ambiente è adatto pe' ne ffà l'installazione 'e MediaWiki.\nArricurdateve d'includere sti nfurmaziune si spiate assistenza ncopp' 'a maniera 'e cumpletà l'installazione.",
+       "config-copyright": "=== Copyright e termine ===\n\n$1\n\nChistu programma è nu software libbero; vuje 'o putite redestribbuì e/o cagnà sott' 'e termine d' 'a licienza GNU GPL ('a Licienza Pubbreca Generale) comme pubbrecata d' 'a Free Software Foundation; o pure 'a verziona 2 d' 'a Licienza, o pure (comme vulite vuje) 'a n'ata verziona cchiù nnova.\n\nChistu programma è destribbuito c' 'a speranza d'essere utile, ma SENZA NISCIUNA GARANZIA; senza manco 'a garanzia p' 'a CUMMERCIABBELETÀ O IDONIETÀ PE' NU SCOPO PARTICOLARE.\nIate a vedé 'a GNU GPL pe' n'avé cchiù nfurmaziune.\n\nCu stu programma avísseve 'a ricevere <doclink href=Copying>na copia d' 'a Licienza GNU GPL</doclink> cu stu prugramma; si nò, scrivete â Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA o [http://www.gnu.org/copyleft/gpl.html liggite sta paggena ncopp' 'a l'Internet].",
+       "config-sidebar": "* [//www.mediawiki.org Paggina prencepale MediaWiki]\n* [//www.mediawiki.org/wiki/Aiuto:Guida a 'e cuntenute pe' l'utente]\n* [//www.mediawiki.org/wiki/Manuale:Guida a 'e cuntenute pe l'ammenistrature]\n* [//www.mediawiki.org/wiki/Manuale:FAQ FAQ]\n----\n* <doclink href=Readme>Lieggeme</doclink>\n* <doclink href=ReleaseNotes>Note 'e verziona</doclink>\n* <doclink href=Copying>Copie</doclink>\n* <doclink href=UpgradeDoc>Agghiurnamento</doclink>",
+       "config-env-good": "L'ambiente è stato cuntrullato.\nÈ pussibbele installare MediaWiki.",
+       "config-env-bad": "L'ambiente è stato cuntrullato.\nNun se può installà MediaWiki.",
+       "config-env-php": "PHP $1 è installato.",
+       "config-env-hhvm": "HHVM $1 è installato.",
+       "config-unicode-using-utf8": "Aúsa Brion Vibber's utf8_normalize.so pe' ne fà 'a normalizzazione Unicode.",
+       "config-unicode-using-intl": "Aúsa [http://pecl.php.net/intl l'estensione PECL intl] pe' ne fà 'a normalizzazione Unicode.",
+       "config-unicode-pure-php-warning": "<strong>Attenziò:</strong> L' [http://pecl.php.net/intl estensione intl PECL] nun è disponibbele pe' gestire 'a normalizzazione Unicode, accussì se ausasse n'imprementazziona llenta 'n puro PHP.\nSi state a gestire nu pizzo ad alto traffico, avisseve a lieggere cocche considerazione ncopp' 'a [//www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations normalizzaziona Unicode].",
+       "config-unicode-update-warning": "<strong>Attenziò:</strong> 'A verziona installata 'e normalizzazione Unicode aùsa 'a verziona viecchia d' 'o [http://site.icu-project.org/ pruggetto ICU].\nV'avite 'a [//www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations agghiurnà] si state a penzà ncopp' 'o fatto d'ausà Unicode.",
+       "config-no-db": "Nun se può truvà nu driver adatto p' 'o database! È necessario installare nu driver p' 'o PHP.\n'E furmatte 'e database ccà annanze songo suppurtate: $1.\n\nSi cumpilate PHP autonomamente, riaccunciatevello attivando nu client database, p'esempio ausannoo <code>./configure --with-mysqli</code>.\nQuanno fosse installato PHP pe' bbìa 'e nu pacchetto Debian o Ubuntu, allora avite 'a installà pure 'o pacchetto <code>php5-mysql</code>.",
+       "config-outdated-sqlite": "'''Attenziò''': tenite 'o SQLite $1 pe' tramente ca ce vulesse 'a verziona $2, SQLite nun sarrà disponibbele.",
+       "config-memory-raised": "'O valore 'e PHP <code>memory_limit</code> è $1, aumentato a $2.",
+       "config-memory-bad": "<strong>Attenziò:</strong> 'o valore 'e PHP <code>memory_limit</code> è $1.\nProbabbilmente troppo basso.\n'A installazione se putesse scassà!",
+       "config-ctype": "'''Errore''': 'o PHP s'adda ghienchere c' 'o supporto pe' l'[http://www.php.net/manual/it/ctype.installation.php estensione Ctype].",
+       "config-iconv": "<strong>Fatale:</strong> PHP s'adda ghienchere c' 'o supporto pe' l'[http://www.php.net/manual/en/iconv.installation.php estensione iconv].",
+       "config-diff3-bad": "GNU diff3 nun truvato.",
+       "config-git": "Truvato software 'e cuntrollo d' 'a verziona Git: <code>$1</code>.",
+       "config-git-bad": "Software 'e cuntrollo d' 'a verziona Git nun truvato.",
+       "config-imagemagick": "Truvato ImageMagick: <code>$1</code>.\n'E miniature d' 'e fiùre sarranno prisente si l'upload song'abbiàte.",
+       "config-gd": "Truvata 'a bibblioteca ntegrata GD Graphics.\n'E miniature 'e ll'immaggene sarranno prisente si l'upload se song'abbiàte.",
+       "config-no-scaling": "Nun se può truvà 'a bibblioteca GD o ImageMagick.\n'E miniature 'e l'immaggene sarranno stutate.",
+       "config-no-uri": "<strong>Errore:</strong> Nun se può determina l'URI 'e mmò.\nInstallazione interrotta.",
+       "config-db-port": "Porta d' 'o database:",
+       "config-db-schema": "Schema pe' MediaWiki:",
+       "config-db-schema-help": "Stu schema 'n genere sarrà buono.\nSi 'o vulite cagnà facite sulamente si ne tenite abbesuogno.",
+       "config-pg-test-error": "Nun se può connettà a 'o database <strong>$1</strong>: $2",
+       "config-sqlite-dir": "Cartella 'e data 'e SQLite:",
+       "config-oracle-def-ts": "Tablespace 'e default:",
+       "config-oracle-temp-ts": "Tablespace temporaneo:",
+       "config-type-mysql": "MySQL (o compatibbele)",
+       "config-type-mssql": "Microsoft SQL Server",
+       "config-header-mysql": "Mpustaziune MySQL",
+       "config-header-postgres": "Mpustaziune PostgreSQL",
+       "config-header-sqlite": "Mpustaziune SQLite",
+       "config-header-oracle": "Mpustaziune Oracle",
+       "config-header-mssql": "Mpustaziune 'e Microsoft SQL Server",
+       "config-invalid-db-type": "'O tipo 'e database nun è buono.",
        "config-mssql-install-auth": "Sceglie 'o tipo d'autenticazziona ca s'ausarrà pe cunnettà â database, durante ll'operazziona d'istallazziona. Si piglie \"{{int:config-mssql-windowsauth}}\", 'e credenziale 'e qualunque fosse ll'utenza ca 'o webserver sta pruciessanno sarranno ausate.",
        "config-mssql-web-auth": "Sceglie 'o tipo d'autenticazziona ca 'o web server pigliarrà pe se cunnettà a 'o server 'e bbase 'e dati, durante ll'operazziona nurmale d&#39;'a wiki.\nSi piglie \"{{int:config-mssql-windowsauth}}\", 'e credenziale 'e qualunque fosse ll'utenza ca 'o webserver sta pruciessanno sarranno ausate."
 }
old mode 100644 (file)
new mode 100755 (executable)
index 70f1f1a..8421672 100644 (file)
@@ -535,7 +535,23 @@ class LogEventsList extends ContextSource {
                        $pager->mLimit = $lim;
                }
 
-               $logBody = $pager->getBody();
+               $logBody = null;
+               // Check if we can avoid the DB query all together
+               if ( $page !== '' && !$param['useMaster'] ) {
+                       $title = ( $page instanceof Title ) ? $page : Title::newFromText( $page );
+                       if ( $title ) {
+                               $member = $title->getNamespace() . ':' . $title->getDBkey();
+                               if ( !BloomCache::get( 'main' )->check( wfWikiId(), 'TitleHasLogs', $member ) ) {
+                                       $logBody = '';
+                               }
+                       } else {
+                               $logBody = '';
+                       }
+               }
+
+               // Fetch the log rows and build the HTML if needed
+               $logBody = ( $logBody === null ) ? $pager->getBody() : $logBody;
+
                $s = '';
 
                if ( $logBody ) {
index 54fab29..7900b2c 100644 (file)
@@ -1248,16 +1248,12 @@ class Article implements Page {
                wfRunHooks( 'Article::MissingArticleConditions', array( &$conds, $logTypes ) );
 
                # Show delete and move logs
-               $member = $title->getNamespace() . ':' . $title->getDBkey();
-               // @todo: move optimization to showLogExtract()?
-               if ( BloomCache::get( 'main' )->check( wfWikiId(), 'TitleHasLogs', $member ) ) {
-                       LogEventsList::showLogExtract( $outputPage, $logTypes, $title, '',
-                               array( 'lim' => 10,
-                                       'conds' => $conds,
-                                       'showIfEmpty' => false,
-                                       'msgKey' => array( 'moveddeleted-notice' ) )
-                       );
-               }
+               LogEventsList::showLogExtract( $outputPage, $logTypes, $title, '',
+                       array( 'lim' => 10,
+                               'conds' => $conds,
+                               'showIfEmpty' => false,
+                               'msgKey' => array( 'moveddeleted-notice' ) )
+               );
 
                if ( !$this->mPage->hasViewableContent() && $wgSend404Code && !$validUserPage ) {
                        // If there's no backing content, send a 404 Not Found
index 81c93a1..61f0504 100644 (file)
@@ -278,7 +278,6 @@ class WikiPage implements Page, IDBAccessObject {
                        'page_namespace',
                        'page_title',
                        'page_restrictions',
-                       'page_counter',
                        'page_is_redirect',
                        'page_is_new',
                        'page_random',
@@ -346,8 +345,7 @@ class WikiPage implements Page, IDBAccessObject {
        }
 
        /**
-        * Set the general counter, title etc data loaded from
-        * some source.
+        * Load the object from a given source by title
         *
         * @param object|string|int $from One of the following:
         *   - A DB query result object.
@@ -2142,6 +2140,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @param array $options Array of options, following indexes are used:
         * - changed: boolean, whether the revision changed the content (default true)
         * - created: boolean, whether the revision created the page (default false)
+        * - moved: boolean, whether the page was moved (default false)
         * - oldcountable: boolean or null (default null):
         *   - boolean: whether the page was counted as an article before that
         *     revision, only used in changed is true and created is false
@@ -2152,7 +2151,12 @@ class WikiPage implements Page, IDBAccessObject {
 
                wfProfileIn( __METHOD__ );
 
-               $options += array( 'changed' => true, 'created' => false, 'oldcountable' => null );
+               $options += array(
+                       'changed' => true,
+                       'created' => false,
+                       'moved' => false,
+                       'oldcountable' => null
+               );
                $content = $revision->getContent();
 
                // Parse the text
@@ -2201,7 +2205,7 @@ class WikiPage implements Page, IDBAccessObject {
                $title = $this->mTitle->getPrefixedDBkey();
                $shortTitle = $this->mTitle->getDBkey();
 
-               if ( !$options['changed'] ) {
+               if ( !$options['changed'] && !$options['moved'] ) {
                        $good = 0;
                } elseif ( $options['created'] ) {
                        $good = (int)$this->isCountable( $editInfo );
index df868ea..9755ea9 100644 (file)
@@ -141,7 +141,7 @@ class CoreTagHooks {
 
                $parser->getOutput()->setIndicator(
                        trim( $attributes['name'] ),
-                       $parser->recursiveTagParse( $content, $frame )
+                       Parser::stripOuterParagraph( $parser->recursiveTagParseFully( $content, $frame ) )
                );
 
                return '';
index fe0c81f..e6486ff 100644 (file)
@@ -389,7 +389,7 @@ class Parser {
                 * to internalParse() which does all the real work.
                 */
 
-               global $wgUseTidy, $wgAlwaysUseTidy, $wgShowHostnames;
+               global $wgShowHostnames;
                $fname = __METHOD__ . '-' . wfGetCaller();
                wfProfileIn( __METHOD__ );
                wfProfileIn( $fname );
@@ -430,40 +430,7 @@ class Parser {
                $text = $this->internalParse( $text );
                wfRunHooks( 'ParserAfterParse', array( &$this, &$text, &$this->mStripState ) );
 
-               $text = $this->mStripState->unstripGeneral( $text );
-
-               # Clean up special characters, only run once, next-to-last before doBlockLevels
-               $fixtags = array(
-                       # french spaces, last one Guillemet-left
-                       # only if there is something before the space
-                       '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1&#160;',
-                       # french spaces, Guillemet-right
-                       '/(\\302\\253) /' => '\\1&#160;',
-                       '/&#160;(!\s*important)/' => ' \\1', # Beware of CSS magic word !important, bug #11874.
-               );
-               $text = preg_replace( array_keys( $fixtags ), array_values( $fixtags ), $text );
-
-               $text = $this->doBlockLevels( $text, $linestart );
-
-               $this->replaceLinkHolders( $text );
-
-               /**
-                * The input doesn't get language converted if
-                * a) It's disabled
-                * b) Content isn't converted
-                * c) It's a conversion table
-                * d) it is an interface message (which is in the user language)
-                */
-               if ( !( $options->getDisableContentConversion()
-                       || isset( $this->mDoubleUnderscores['nocontentconvert'] ) )
-               ) {
-                       if ( !$this->mOptions->getInterfaceMessage() ) {
-                               # The position of the convert() call should not be changed. it
-                               # assumes that the links are all replaced and the only thing left
-                               # is the <nowiki> mark.
-                               $text = $this->getConverterLanguage()->convert( $text );
-                       }
-               }
+               $text = $this->internalParseHalfParsed( $text, true, $linestart );
 
                /**
                 * A converted title will be provided in the output object if title and
@@ -486,45 +453,6 @@ class Parser {
                        }
                }
 
-               $text = $this->mStripState->unstripNoWiki( $text );
-
-               wfRunHooks( 'ParserBeforeTidy', array( &$this, &$text ) );
-
-               $text = $this->replaceTransparentTags( $text );
-               $text = $this->mStripState->unstripGeneral( $text );
-
-               $text = Sanitizer::normalizeCharReferences( $text );
-
-               if ( ( $wgUseTidy && $this->mOptions->getTidy() ) || $wgAlwaysUseTidy ) {
-                       $text = MWTidy::tidy( $text );
-               } else {
-                       # attempt to sanitize at least some nesting problems
-                       # (bug #2702 and quite a few others)
-                       $tidyregs = array(
-                               # ''Something [http://www.cool.com cool''] -->
-                               # <i>Something</i><a href="http://www.cool.com"..><i>cool></i></a>
-                               '/(<([bi])>)(<([bi])>)?([^<]*)(<\/?a[^<]*>)([^<]*)(<\/\\4>)?(<\/\\2>)/' =>
-                               '\\1\\3\\5\\8\\9\\6\\1\\3\\7\\8\\9',
-                               # fix up an anchor inside another anchor, only
-                               # at least for a single single nested link (bug 3695)
-                               '/(<a[^>]+>)([^<]*)(<a[^>]+>[^<]*)<\/a>(.*)<\/a>/' =>
-                               '\\1\\2</a>\\3</a>\\1\\4</a>',
-                               # fix div inside inline elements- doBlockLevels won't wrap a line which
-                               # contains a div, so fix it up here; replace
-                               # div with escaped text
-                               '/(<([aib]) [^>]+>)([^<]*)(<div([^>]*)>)(.*)(<\/div>)([^<]*)(<\/\\2>)/' =>
-                               '\\1\\3&lt;div\\5&gt;\\6&lt;/div&gt;\\8\\9',
-                               # remove empty italic or bold tag pairs, some
-                               # introduced by rules above
-                               '/<([bi])><\/\\1>/' => '',
-                       );
-
-                       $text = preg_replace(
-                               array_keys( $tidyregs ),
-                               array_values( $tidyregs ),
-                               $text );
-               }
-
                if ( $this->mExpensiveFunctionCount > $this->mOptions->getExpensiveParserFunctionLimit() ) {
                        $this->limitationWarn( 'expensive-parserfunction',
                                $this->mExpensiveFunctionCount,
@@ -532,8 +460,6 @@ class Parser {
                        );
                }
 
-               wfRunHooks( 'ParserAfterTidy', array( &$this, &$text ) );
-
                # Information on include size limits, for the benefit of users who try to skirt them
                if ( $this->mOptions->getEnableLimitReport() ) {
                        $max = $this->mOptions->getMaxIncludeSize();
@@ -621,15 +547,26 @@ class Parser {
        }
 
        /**
-        * Recursive parser entry point that can be called from an extension tag
-        * hook.
+        * Half-parse wikitext to half-parsed HTML. This recursive parser entry point
+        * can be called from an extension tag hook.
         *
-        * If $frame is not provided, then template variables (e.g., {{{1}}}) within $text are not expanded
+        * The output of this function IS NOT SAFE PARSED HTML; it is "half-parsed"
+        * instead, which means that lists and links have not been fully parsed yet,
+        * and strip markers are still present.
+        *
+        * Use recursiveTagParseFully() to fully parse wikitext to output-safe HTML.
+        *
+        * Use this function if you're a parser tag hook and you want to parse
+        * wikitext before or after applying additional transformations, and you
+        * intend to *return the result as hook output*, which will cause it to go
+        * through the rest of parsing process automatically.
+        *
+        * If $frame is not provided, then template variables (e.g., {{{1}}}) within
+        * $text are not expanded
         *
         * @param string $text Text extension wants to have parsed
         * @param bool|PPFrame $frame The frame to use for expanding any template variables
-        *
-        * @return string
+        * @return string UNSAFE half-parsed HTML
         */
        public function recursiveTagParse( $text, $frame = false ) {
                wfProfileIn( __METHOD__ );
@@ -640,6 +577,31 @@ class Parser {
                return $text;
        }
 
+       /**
+        * Fully parse wikitext to fully parsed HTML. This recursive parser entry
+        * point can be called from an extension tag hook.
+        *
+        * The output of this function is fully-parsed HTML that is safe for output.
+        * If you're a parser tag hook, you might want to use recursiveTagParse()
+        * instead.
+        *
+        * If $frame is not provided, then template variables (e.g., {{{1}}}) within
+        * $text are not expanded
+        *
+        * @since 1.25
+        *
+        * @param string $text Text extension wants to have parsed
+        * @param bool|PPFrame $frame The frame to use for expanding any template variables
+        * @return string Fully parsed HTML
+        */
+       public function recursiveTagParseFully( $text, $frame = false ) {
+               wfProfileIn( __METHOD__ );
+               $text = $this->recursiveTagParse( $text, $frame );
+               $text = $this->internalParseHalfParsed( $text, false );
+               wfProfileOut( __METHOD__ );
+               return $text;
+       }
+
        /**
         * Expand templates and variables in the text, producing valid, static wikitext.
         * Also removes comments.
@@ -1227,7 +1189,7 @@ class Parser {
        }
 
        /**
-        * Helper function for parse() that transforms wiki markup into
+        * Helper function for parse() that transforms wiki markup into half-parsed
         * HTML. Only called for $mOutputType == self::OT_HTML.
         *
         * @private
@@ -1300,6 +1262,101 @@ class Parser {
                return $text;
        }
 
+       /**
+        * Helper function for parse() that transforms half-parsed HTML into fully
+        * parsed HTML.
+        *
+        * @param string $text
+        * @param bool $isMain
+        * @param bool $linestart
+        * @return string
+        */
+       private function internalParseHalfParsed( $text, $isMain = true, $linestart = true ) {
+               global $wgUseTidy, $wgAlwaysUseTidy;
+
+               $text = $this->mStripState->unstripGeneral( $text );
+
+               # Clean up special characters, only run once, next-to-last before doBlockLevels
+               $fixtags = array(
+                       # french spaces, last one Guillemet-left
+                       # only if there is something before the space
+                       '/(.) (?=\\?|:|;|!|%|\\302\\273)/' => '\\1&#160;',
+                       # french spaces, Guillemet-right
+                       '/(\\302\\253) /' => '\\1&#160;',
+                       '/&#160;(!\s*important)/' => ' \\1', # Beware of CSS magic word !important, bug #11874.
+               );
+               $text = preg_replace( array_keys( $fixtags ), array_values( $fixtags ), $text );
+
+               $text = $this->doBlockLevels( $text, $linestart );
+
+               $this->replaceLinkHolders( $text );
+
+               /**
+                * The input doesn't get language converted if
+                * a) It's disabled
+                * b) Content isn't converted
+                * c) It's a conversion table
+                * d) it is an interface message (which is in the user language)
+                */
+               if ( !( $this->mOptions->getDisableContentConversion()
+                       || isset( $this->mDoubleUnderscores['nocontentconvert'] ) )
+               ) {
+                       if ( !$this->mOptions->getInterfaceMessage() ) {
+                               # The position of the convert() call should not be changed. it
+                               # assumes that the links are all replaced and the only thing left
+                               # is the <nowiki> mark.
+                               $text = $this->getConverterLanguage()->convert( $text );
+                       }
+               }
+
+               $text = $this->mStripState->unstripNoWiki( $text );
+
+               if ( $isMain ) {
+                       wfRunHooks( 'ParserBeforeTidy', array( &$this, &$text ) );
+               }
+
+               $text = $this->replaceTransparentTags( $text );
+               $text = $this->mStripState->unstripGeneral( $text );
+
+               $text = Sanitizer::normalizeCharReferences( $text );
+
+               if ( ( $wgUseTidy && $this->mOptions->getTidy() ) || $wgAlwaysUseTidy ) {
+                       $text = MWTidy::tidy( $text );
+               } else {
+                       # attempt to sanitize at least some nesting problems
+                       # (bug #2702 and quite a few others)
+                       $tidyregs = array(
+                               # ''Something [http://www.cool.com cool''] -->
+                               # <i>Something</i><a href="http://www.cool.com"..><i>cool></i></a>
+                               '/(<([bi])>)(<([bi])>)?([^<]*)(<\/?a[^<]*>)([^<]*)(<\/\\4>)?(<\/\\2>)/' =>
+                               '\\1\\3\\5\\8\\9\\6\\1\\3\\7\\8\\9',
+                               # fix up an anchor inside another anchor, only
+                               # at least for a single single nested link (bug 3695)
+                               '/(<a[^>]+>)([^<]*)(<a[^>]+>[^<]*)<\/a>(.*)<\/a>/' =>
+                               '\\1\\2</a>\\3</a>\\1\\4</a>',
+                               # fix div inside inline elements- doBlockLevels won't wrap a line which
+                               # contains a div, so fix it up here; replace
+                               # div with escaped text
+                               '/(<([aib]) [^>]+>)([^<]*)(<div([^>]*)>)(.*)(<\/div>)([^<]*)(<\/\\2>)/' =>
+                               '\\1\\3&lt;div\\5&gt;\\6&lt;/div&gt;\\8\\9',
+                               # remove empty italic or bold tag pairs, some
+                               # introduced by rules above
+                               '/<([bi])><\/\\1>/' => '',
+                       );
+
+                       $text = preg_replace(
+                               array_keys( $tidyregs ),
+                               array_values( $tidyregs ),
+                               $text );
+               }
+
+               if ( $isMain ) {
+                       wfRunHooks( 'ParserAfterTidy', array( &$this, &$text ) );
+               }
+
+               return $text;
+       }
+
        /**
         * Replace special strings like "ISBN xxx" and "RFC xxx" with
         * magic external links.
index 0ba407f..afa5cac 100644 (file)
@@ -38,7 +38,7 @@ class MWOldPassword extends ParameterizedPassword {
        public function crypt( $plaintext ) {
                global $wgPasswordSalt;
 
-               if ( $wgPasswordSalt && count( $this->args ) == 1 ) {
+               if ( $wgPasswordSalt && count( $this->args ) === 1 ) {
                        $this->hash = md5( $this->args[0] . '-' . md5( $plaintext ) );
                } else {
                        $this->args = array();
index 4d6e415..187f895 100644 (file)
@@ -83,10 +83,14 @@ abstract class ParameterizedPassword extends Password {
        }
 
        public function toString() {
-               return
-                       ':' . $this->config['type'] . ':' .
-                       implode( $this->getDelimiter(), array_merge( $this->params, $this->args ) ) .
-                       $this->getDelimiter() . $this->hash;
+               $str = ':' . $this->config['type'] . ':';
+
+               if ( count( $this->params ) || count( $this->args ) ) {
+                       $str .= implode( $this->getDelimiter(), array_merge( $this->params, $this->args ) );
+                       $str .= $this->getDelimiter();
+               }
+
+               return $str . $this->hash;
        }
 
        /**
diff --git a/includes/profiler/ProfilerMwprof.php b/includes/profiler/ProfilerMwprof.php
deleted file mode 100644 (file)
index 83fed28..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-<?php
-/**
- * Profiler class for Mwprof.
- *
- * 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
- */
-
-/**
- * Profiler class for Mwprof.
- *
- * Mwprof is a high-performance MediaWiki profiling data collector, designed to
- * collect profiling data from multiple hosts running in tandem. This class
- * serializes profiling samples into MessagePack arrays and sends them to an
- * Mwprof instance via UDP.
- *
- * @see https://github.com/wikimedia/operations-software-mwprof
- * @since 1.23
- */
-class ProfilerMwprof extends Profiler {
-       /** @var array Queue of open profile calls with start data */
-       protected $mWorkStack = array();
-
-       /** @var array Map of (function name => aggregate data array) */
-       protected $mCollated = array();
-       /** @var array Cache of a standard broken collation entry */
-       protected $mErrorEntry;
-
-       // Message types
-       const TYPE_SINGLE = 1;
-       const TYPE_RUNNING = 2;
-
-       public function isStub() {
-               return false;
-       }
-
-       public function isPersistent() {
-               return true;
-       }
-
-       /**
-        * Start a profiling section.
-        *
-        * Marks the beginning of the function or code-block that should be time
-        * and logged under some specific name.
-        *
-        * @param string $inName Section to start
-        */
-       public function profileIn( $inName ) {
-               $this->mWorkStack[] = array( $inName, count( $this->mWorkStack ),
-                       $this->getTime(), $this->getTime( 'cpu' ), 0 );
-       }
-
-       /**
-        * Close a profiling section.
-        *
-        * Marks the end of the function or code-block that should be timed and
-        * logged under some specific name.
-        *
-        * @param string $outName Section to close
-        */
-       public function profileOut( $outName ) {
-               list( $inName, , $inWall, $inCpu ) = array_pop( $this->mWorkStack );
-
-               // Check for unbalanced profileIn / profileOut calls.
-               // Bad entries are logged but not sent.
-               if ( $inName !== $outName ) {
-                       $this->debugGroup( 'ProfilerUnbalanced', json_encode( array( $inName, $outName ) ) );
-                       return;
-               }
-
-               $elapsedCpu = $this->getTime( 'cpu' ) - $inCpu;
-               $elapsedWall = $this->getTime() - $inWall;
-               $this->updateRunningEntry( $outName, $elapsedCpu, $elapsedWall );
-               $this->trxProfiler->recordFunctionCompletion( $outName, $elapsedWall );
-       }
-
-       /**
-        * Update an entry with timing data.
-        *
-        * @param string $name Section name
-        * @param float $elapsedCpu Elapsed CPU time
-        * @param float $elapsedWall Elapsed wall-clock time
-        */
-       public function updateRunningEntry( $name, $elapsedCpu, $elapsedWall ) {
-               // If this is the first measurement for this entry, store plain values.
-               // Many profiled functions will only be called once per request.
-               if ( !isset( $this->mCollated[$name] ) ) {
-                       $this->mCollated[$name] = array(
-                               'cpu'   => $elapsedCpu,
-                               'wall'  => $elapsedWall,
-                               'count' => 1,
-                       );
-                       return;
-               }
-
-               $entry = &$this->mCollated[$name];
-
-               // If it's the second measurement, convert the plain values to
-               // RunningStat instances, so we can push the incoming values on top.
-               if ( $entry['count'] === 1 ) {
-                       $cpu = new RunningStat();
-                       $cpu->push( $entry['cpu'] );
-                       $entry['cpu'] = $cpu;
-
-                       $wall = new RunningStat();
-                       $wall->push( $entry['wall'] );
-                       $entry['wall'] = $wall;
-               }
-
-               $entry['count']++;
-               $entry['cpu']->push( $elapsedCpu );
-               $entry['wall']->push( $elapsedWall );
-       }
-
-       /**
-        * @return array
-        */
-       public function getRawData() {
-               // This method is called before shutdown in the footer method on Skins.
-               // If some outer methods have not yet called wfProfileOut(), work around
-               // that by clearing anything in the work stack to just the "-total" entry.
-               if ( count( $this->mWorkStack ) > 1 ) {
-                       $oldWorkStack = $this->mWorkStack;
-                       $this->mWorkStack = array( $this->mWorkStack[0] ); // just the "-total" one
-               } else {
-                       $oldWorkStack = null;
-               }
-               $this->close();
-               // If this trick is used, then the old work stack is swapped back afterwards.
-               // This means that logData() will still make use of all the method data since
-               // the missing wfProfileOut() calls should be made by the time it is called.
-               if ( $oldWorkStack ) {
-                       $this->mWorkStack = $oldWorkStack;
-               }
-
-               $totalWall = 0.0;
-               $profile = array();
-               foreach ( $this->mCollated as $fname => $data ) {
-                       if ( $data['count'] == 1 ) {
-                               $profile[] = array(
-                                       'name' => $fname,
-                                       'calls' => $data['count'],
-                                       'elapsed' => $data['wall'] * 1000,
-                                       'memory' => 0, // not supported
-                                       'min' => $data['wall'] * 1000,
-                                       'max' => $data['wall'] * 1000,
-                                       'overhead' => 0, // not supported
-                                       'periods' => array() // not supported
-                               );
-                               $totalWall += $data['wall'];
-                       } else {
-                               $profile[] = array(
-                                       'name' => $fname,
-                                       'calls' => $data['count'],
-                                       'elapsed' => $data['wall']->n * $data['wall']->getMean() * 1000,
-                                       'memory' => 0, // not supported
-                                       'min' => $data['wall']->min * 1000,
-                                       'max' => $data['wall']->max * 1000,
-                                       'overhead' => 0, // not supported
-                                       'periods' => array() // not supported
-                               );
-                               $totalWall += $data['wall']->n * $data['wall']->getMean();
-                       }
-               }
-               $totalWall = $totalWall * 1000;
-
-               foreach ( $profile as &$item ) {
-                       $item['percent'] = $totalWall ? 100 * $item['elapsed'] / $totalWall : 0;
-               }
-
-               return $profile;
-       }
-
-       /**
-        * Serialize profiling data and send to a profiling data aggregator.
-        *
-        * Individual entries are represented as arrays and then encoded using
-        * MessagePack, an efficient binary data-interchange format. Encoded
-        * entries are accumulated into a buffer and sent in batch via UDP to the
-        * profiling data aggregator.
-        */
-       public function logData() {
-               global $wgUDPProfilerHost, $wgUDPProfilerPort;
-
-               $this->close();
-
-               if ( !function_exists( 'socket_create' ) ) {
-                       return; // avoid fatal
-               }
-
-               $sock = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
-               socket_connect( $sock, $wgUDPProfilerHost, $wgUDPProfilerPort );
-               $bufferLength = 0;
-               $buffer = '';
-               foreach ( $this->mCollated as $name => $entry ) {
-                       $count = $entry['count'];
-                       $cpu = $entry['cpu'];
-                       $wall = $entry['wall'];
-
-                       if ( $count === 1 ) {
-                               $data = array( self::TYPE_SINGLE, $name, $cpu, $wall );
-                       } else {
-                               $data = array( self::TYPE_RUNNING, $name, $count,
-                                       $cpu->m1, $cpu->m2, $cpu->min, $cpu->max,
-                                       $wall->m1, $wall->m2, $wall->min, $wall->max );
-                       }
-
-                       $encoded = MWMessagePack::pack( $data );
-                       $length = strlen( $encoded );
-
-                       // If adding this entry would cause the size of the buffer to
-                       // exceed the standard ethernet MTU size less the UDP header,
-                       // send all pending data and reset the buffer. Otherwise, continue
-                       // accumulating entries into the current buffer.
-                       if ( $length + $bufferLength > 1450 ) {
-                               socket_send( $sock, $buffer, $bufferLength, 0 );
-                               $buffer = '';
-                               $bufferLength = 0;
-                       }
-                       $buffer .= $encoded;
-                       $bufferLength += $length;
-               }
-               if ( $bufferLength !== 0 ) {
-                       socket_send( $sock, $buffer, $bufferLength, 0 );
-               }
-       }
-
-       /**
-        * Close opened profiling sections
-        */
-       public function close() {
-               while ( count( $this->mWorkStack ) ) {
-                       $this->profileOut( 'close' );
-               }
-       }
-
-       public function getOutput() {
-               return ''; // no report
-       }
-}
index 7ef0ad0..5e62f7c 100644 (file)
  * @ingroup Profiler
  */
 class ProfilerSimpleDB extends ProfilerStandard {
-       protected function collateOnly() {
-               return true;
-       }
-
        public function isPersistent() {
                return true;
        }
index 0ee7aad..cc18890 100644 (file)
@@ -42,10 +42,6 @@ class ProfilerSimpleText extends ProfilerStandard {
                parent::__construct( $profileConfig );
        }
 
-       protected function collateOnly() {
-               return true;
-       }
-
        public function logData() {
                if ( $this->mTemplated ) {
                        $this->close();
@@ -54,17 +50,18 @@ class ProfilerSimpleText extends ProfilerStandard {
                                : 0; // profiling mismatch error?
                        uasort( $this->mCollated, array( 'self', 'sort' ) );
                        array_walk( $this->mCollated, array( 'self', 'format' ), $totalReal );
+                       $contentType = $this->getContentType();
                        if ( PHP_SAPI === 'cli' ) {
                                print "<!--\n" . self::$out . "\n-->\n";
-                       } elseif ( $this->getContentType() === 'text/html' ) {
+                       } elseif ( $contentType === 'text/html' ) {
                                if ( $this->visible ) {
                                        print '<pre>' . self::$out . '</pre>';
                                } else {
                                        print "<!--\n" . self::$out . "\n-->\n";
                                }
-                       } elseif ( $this->getContentType() === 'text/javascript' ) {
+                       } elseif ( $contentType === 'text/javascript' ) {
                                print "\n/*\n" . self::$out . "*/\n";
-                       } elseif ( $this->getContentType() === 'text/css' ) {
+                       } elseif ( $contentType === 'text/css' ) {
                                print "\n/*\n" . self::$out . "*/\n";
                        }
                }
index 2a44494..0dd4067 100644 (file)
@@ -30,10 +30,6 @@ class ProfilerSimpleTrace extends ProfilerStandard {
        protected $trace = "Beginning trace: \n";
        protected $memory = 0;
 
-       protected function collateOnly() {
-               return true;
-       }
-
        public function profileIn( $functionname ) {
                parent::profileIn( $functionname );
 
@@ -71,13 +67,14 @@ class ProfilerSimpleTrace extends ProfilerStandard {
 
        public function logData() {
                if ( $this->mTemplated ) {
+                       $contentType = $this->getContentType();
                        if ( PHP_SAPI === 'cli' ) {
                                print "<!-- \n {$this->trace} \n -->";
-                       } elseif ( $this->getContentType() === 'text/html' ) {
+                       } elseif ( $contentType === 'text/html' ) {
                                print "<!-- \n {$this->trace} \n -->";
-                       } elseif ( $this->getContentType() === 'text/javascript' ) {
+                       } elseif ( $contentType === 'text/javascript' ) {
                                print "\n/*\n {$this->trace}\n*/";
-                       } elseif ( $this->getContentType() === 'text/css' ) {
+                       } elseif ( $contentType === 'text/css' ) {
                                print "\n/*\n {$this->trace}\n*/";
                        }
                }
index 627b4de..02405af 100644 (file)
  * @ingroup Profiler
  */
 class ProfilerSimpleUDP extends ProfilerStandard {
-       protected function collateOnly() {
-               return true;
-       }
-
        public function isPersistent() {
                return true;
        }
index cc13416..a65e53e 100644 (file)
@@ -40,8 +40,8 @@ class ProfilerStandard extends Profiler {
        protected $mCollated = array();
        /** @var bool */
        protected $mCollateDone = false;
-       /** @var bool */
-       protected $mCollateOnly = false;
+       /** @var bool Whether to collect the full stack trace or just aggregates */
+       protected $mCollateOnly = true;
        /** @var array Cache of a standard broken collation entry */
        protected $mErrorEntry;
 
@@ -51,8 +51,6 @@ class ProfilerStandard extends Profiler {
        public function __construct( array $params ) {
                parent::__construct( $params );
 
-               $this->mCollateOnly = $this->collateOnly();
-
                $this->addInitialStack();
        }
 
@@ -75,18 +73,6 @@ class ProfilerStandard extends Profiler {
                return false;
        }
 
-       /**
-        * Whether to internally just track aggregates and ignore the full stack trace
-        *
-        * Only doing collation saves memory overhead but limits the use of certain
-        * features like that of graph generation for the debug toolbar.
-        *
-        * @return bool
-        */
-       protected function collateOnly() {
-               return false;
-       }
-
        /**
         * Add the inital item in the stack.
         */
index d4e8159..bcef149 100644 (file)
@@ -993,7 +993,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                        $localPath = $this->getLocalPath( $templatePath );
                        if ( file_exists( $localPath ) ) {
                                $content = file_get_contents( $localPath );
-                               $templates[ $alias ] = $content;
+                               $templates[$alias] = $content;
                        } else {
                                $msg = __METHOD__ . ": template file not found: \"$localPath\"";
                                wfDebugLog( 'resourceloader', $msg );
index 2a1736d..e195cf2 100644 (file)
@@ -70,7 +70,7 @@ abstract class ResourceLoaderWikiModule extends ResourceLoaderModule {
         * In particular, it doesn't work for getting the content of JS and CSS pages. That functionality
         * will use the local DB irrespective of the return value of this method.
         *
-        * @return DatabaseBase|null
+        * @return IDatabase|null
         */
        protected function getDB() {
                return wfGetDB( DB_SLAVE );
index d5c57eb..2062da8 100644 (file)
@@ -46,8 +46,7 @@ class SkinApiTemplate extends BaseTemplate {
 
                <?php $this->printTrail() ?>
                </body></html>
-
-       <?php
+<?php
        }
 
        // Skip work and hooks for stuff we don't use
index 3d762aa..0831c20 100644 (file)
@@ -157,7 +157,7 @@ class SpecialImport extends SpecialPage {
                                array( 'importfailed', $source->getWikiText() )
                        );
                } else {
-                       $importer = new WikiImporter( $source->value );
+                       $importer = new WikiImporter( $source->value, $this->getConfig() );
                        if ( !is_null( $this->namespace ) ) {
                                $importer->setTargetNamespace( $this->namespace );
                        }
@@ -251,7 +251,7 @@ class SpecialImport extends SpecialPage {
                                        Xml::label( $this->msg( 'import-comment' )->text(), 'mw-import-comment' ) .
                                        "</td>
                                        <td class='mw-input'>" .
-                                       Xml::input( 'log-comment', 50, 
+                                       Xml::input( 'log-comment', 50,
                                                ( $this->sourceName == 'upload' ? $this->logcomment : '' ),
                                                array( 'id' => 'mw-import-comment', 'type' => 'text' ) ) . ' ' .
                                        "</td>
index 37edc0f..2d6213a 100644 (file)
@@ -251,6 +251,25 @@ class LinkSearchPage extends QueryPage {
                return $retval;
        }
 
+       /**
+        * Pre-fill the link cache
+        *
+        * @param DatabaseBase $db
+        * @param ResultWrapper $res
+        */
+       function preprocessResults( $db, $res ) {
+               if ( $res->numRows() > 0 ) {
+                       $linkBatch = new LinkBatch();
+
+                       foreach ( $res as $row ) {
+                               $linkBatch->add( $row->namespace, $row->title );
+                       }
+
+                       $res->seek( 0 );
+                       $linkBatch->execute();
+               }
+       }
+
        /**
         * @param Skin $skin
         * @param object $result Result row
index ec9593f..4cbf584 100644 (file)
@@ -549,10 +549,22 @@ class MovePageForm extends UnlistedSpecialPage {
                }
 
                # Do the actual move.
-               $error = $ot->moveTo( $nt, true, $this->reason, $createRedirect );
-               if ( $error !== true ) {
-                       $this->showForm( $error );
+               $mp = new MovePage( $ot, $nt );
+               $valid = $mp->isValidMove();
+               if ( !$valid->isOK() ) {
+                       $this->showForm( $valid->getErrorsArray() );
+                       return;
+               }
+
+               $permStatus = $mp->checkPermissions( $user, $this->reason );
+               if ( !$permStatus->isOK() ) {
+                       $this->showForm( $permStatus->getErrorsArray() );
+                       return;
+               }
 
+               $status = $mp->move( $user, $this->reason, $createRedirect );
+               if ( !$status->isOK() ) {
+                       $this->showForm( $status->getErrorsArray() );
                        return;
                }
 
index 5b34297..ad1f051 100644 (file)
@@ -255,7 +255,7 @@ class SpecialNewpages extends IncludableSpecialPage {
                // The form should be visible on each request (inclusive requests with submitted forms), so
                // return always false here.
                $htmlForm->setSubmitCallback(
-                       function() {
+                       function () {
                                return false;
                        }
                );
index 895f1e8..b1baf67 100644 (file)
@@ -895,10 +895,7 @@ class SpecialSearch extends SpecialPage {
                // be arranged nicely while still accommodating different screen widths
                $namespaceTables = '';
                for ( $i = 0; $i < $numRows; $i += 4 ) {
-                       $namespaceTables .= Xml::openElement(
-                               'table',
-                               array( 'cellpadding' => 0, 'cellspacing' => 0 )
-                       );
+                       $namespaceTables .= Xml::openElement( 'table' );
 
                        for ( $j = $i; $j < $i + 4 && $j < $numRows; $j++ ) {
                                $namespaceTables .= Xml::tags( 'tr', null, $rows[$j] );
index 51dd7bd..77ad9aa 100644 (file)
@@ -746,7 +746,7 @@ class SpecialUpload extends SpecialPage {
         *
         * @todo What about non-BitmapHandler handled files?
         */
-       static public function rotationEnabled() {
+       public static function rotationEnabled() {
                $bitmapHandler = new BitmapHandler();
                return $bitmapHandler->autoRotateEnabled();
        }
@@ -903,7 +903,7 @@ class UploadForm extends HTMLForm {
                                        $this->getContext()->getLanguage()->formatSize( $this->mMaxUploadSize['url'] )
                                )->parse() .
                                        $this->msg( 'word-separator' )->escaped() .
-                                       $this->msg( 'upload_source_url' )->escaped(),
+                                       $this->msg( 'upload_source_url' )->parse(),
                                'checked' => $selectedSourceType == 'url',
                        );
                }
index bdd6751..b6a3be2 100644 (file)
@@ -113,6 +113,21 @@ class LoginForm extends SpecialPage {
                $wgUseMediaWikiUIEverywhere = true;
        }
 
+       /**
+        * Returns an array of all valid error messages.
+        *
+        * @return array
+        */
+       public static function getValidErrorMessages() {
+               static $messages = null;
+               if ( !$messages ) {
+                       $messages = self::$validErrorMessages;
+                       wfRunHooks( 'LoginFormValidErrorMessages', array( &$messages ) );
+               }
+
+               return $messages;
+       }
+
        /**
         * Loader
         */
@@ -175,13 +190,13 @@ class LoginForm extends SpecialPage {
 
                // Only show valid error or warning messages.
                if ( $entryError->exists()
-                       && in_array( $entryError->getKey(), self::$validErrorMessages )
+                       && in_array( $entryError->getKey(), self::getValidErrorMessages() )
                ) {
                        $this->mEntryErrorType = 'error';
                        $this->mEntryError = $entryError->rawParams( $loginreqlink )->escaped();
 
                } elseif ( $entryWarning->exists()
-                       && in_array( $entryWarning->getKey(), self::$validErrorMessages )
+                       && in_array( $entryWarning->getKey(), self::getValidErrorMessages() )
                ) {
                        $this->mEntryErrorType = 'warning';
                        $this->mEntryError = $entryWarning->rawParams( $loginreqlink )->escaped();
index b35967b..c32bd3a 100644 (file)
--- a/index.php
+++ b/index.php
@@ -34,7 +34,7 @@
 # has structures (try/catch, foo()->bar(), etc etc) which throw parse errors in
 # PHP 4. Setup.php and ObjectCache.php have structures invalid in PHP 5.0 and
 # 5.1, respectively.
-if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.2' ) < 0 ) {
+if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.3' ) < 0 ) {
        // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
        require dirname( __FILE__ ) . '/includes/PHPVersionError.php';
        wfPHPVersionError( 'index.php' );
index e55f79c..493f818 100644 (file)
@@ -11,7 +11,8 @@
                        "Mousa",
                        "Shirayuki",
                        "Microchip08",
-                       "아라"
+                       "아라",
+                       "Koroğlu"
                ]
        },
        "tog-underline": "باغلانتیلارین آلتینی خطله:",
        "cancel": "وازگئچ",
        "moredotdotdot": "داها...",
        "morenotlisted": "بۇ لیست کامل دئییل.",
-       "mypage": "صحیفه",
+       "mypage": "صفحه",
        "mytalk": "دانیشیق",
        "anontalk": "بو آی‌پی آدرسینه دانیشیق",
        "navigation": "دوْلانماق",
        "qbfind": "تاپ",
        "qbbrowse": "گؤزدن گئچیرت",
        "qbedit": "دَییشدیر",
-       "qbpageoptions": "بۇ صحیفه‌‌",
-       "qbmyoptions": "صحیفه‌‌لریم",
+       "qbpageoptions": "بۇ صفحه‌‌",
+       "qbmyoptions": "صفحه‌‌لریم",
        "faq": "چوْخ سوْروشولان سوْال‌لار",
        "faqpage": "Project:چوْخ سوْروشولان سوْال‌لار",
        "actions": "چالیشمالار",
        "searchbutton": "آختار",
        "go": "گئت",
        "searcharticle": "گئت",
-       "history": "صحیفه‌‌نین گئچمیشی",
+       "history": "صفحه‌‌نین گئچمیشی",
        "history_short": "گئچمیش",
        "updatedmarker": "سوْن باخیشیمدان سوْنرا یئنی‌لنیب‌دیر",
        "printableversion": "یازدیریرا بیلن نۆسخه",
        "newpage": "يئنی صحیفه‌‌",
        "talkpage": "بۇ صحیفه‌نی دانیش",
        "talkpagelinktext": "دانیشیق",
-       "specialpage": "اؤزل صحیفه",
+       "specialpage": "اؤزل صفحه",
        "personaltools": "شخصی آراجلار",
        "articlepage": "ایچری‌لی صحیفه‌یه باخ",
        "talk": "دانیشیق",
        "disclaimers": "رد ائتمک",
        "disclaimerpage": "Project:عمومی رد ائتمک",
        "edithelp": "ديَیشتیرمک یاردیمی",
-       "mainpage": "آنا صحیفه",
-       "mainpage-description": "آنا صحیفه",
+       "mainpage": "آنا صفحه",
+       "mainpage-description": "آنا صفحه",
        "policy-url": "Project:قایدالار",
        "portal": "توْپلوم پوْرتالی",
        "portal-url": "Project:توْپلوم پوْرتالی",
        "red-link-title": "$1 (صحیفه یوْخدور)",
        "sort-descending": "آزالان سیرالاماق",
        "sort-ascending": "چوْخالان سیرالاماق",
-       "nstab-main": "صحیفه",
+       "nstab-main": "صفحه",
        "nstab-user": "ایستیفاده‌چی صحیفه‌سی",
        "nstab-media": "مئدیا صحیفه‌سی",
        "nstab-special": "اؤزل صحیفه",
        "tooltip-search": "{{SITENAME}}-دا آختار",
        "tooltip-search-go": "اولورسا بو آددا بیر صحیفه‌یه گئت",
        "tooltip-search-fulltext": "بو یازی اولان صحیفه‌لری آختار",
-       "tooltip-p-logo": "آنا صحیفه‌یه باخ",
+       "tooltip-p-logo": "آنا صفحه‌یه باخ",
        "tooltip-n-mainpage": "آنا صحیفه‌‌یه باخین",
-       "tooltip-n-mainpage-description": "آنا صحیفه‌یه باخین",
+       "tooltip-n-mainpage-description": "آنا صفحه‌یه باخ",
        "tooltip-n-portal": "پروژه‌ یه گؤره، سیز نه ایش گوره بیلرسیز، هاردا نه‌لری تاپا بیلرسیز",
        "tooltip-n-currentevents": "ایندیکی اولایلارا ایلگیلی بیلگیلر تاپ",
        "tooltip-n-recentchanges": "بو ویکی‌ده سون دَییشیکلرین لیستی",
index 5074125..24cbe04 100644 (file)
        "api-error-stashfailed": "Унутраная памылка: сэрвэр ня змог захаваць часовы файл.",
        "api-error-publishfailed": "Унутраная памылка: сэрвэр ня змог захаваць часловы файл.",
        "api-error-stasherror": "Падчас загрузкі файла ў сховішча адбылася памылка.",
+       "api-error-stashedfilenotfound": "Схаваны файл ня быў знойдзены пры спробе загрузіць яго з схованкі.",
        "api-error-timeout": "Сэрвэр не адказаў у чаканы тэрмін.",
        "api-error-unclassified": "Узьнікла невядомая памылка",
        "api-error-unknown-code": "Невядомая памылка: «$1».",
index 4172144..1b52e6e 100644 (file)
        "viewsourcetext": "এ পাতাটি আপনি দেখতে এবং উৎসের অনুলিপি নিতে পারবেন:",
        "viewyourtext": "আপনি ' ' ' আপনার সম্পাদনা ' ' ' এই পাতায় দেখতে এবং কপি করতে পারেন:",
        "protectedinterface": "এই পাতার বিষয়বস্তু এই উইকি সফটওয়্যারের একটি ইন্টারফেস বার্তা প্রদান করে, তাই এটিকে সুরক্ষিত করে রাখা হয়েছে।\nসকল উইকির অনুবাদে কোনো ধরনের সংযোজন বা পরিবর্তন করতে, অনুগ্রহ করে মিডিয়াউইকি স্থানীয়করন প্রকল্প [//translatewiki.net/ translatewiki.net] ব্যবহার করুন।",
-       "editinginterface": "'''সতর্কীকরণ:''' আপনি এমন একটি পাতা সম্পাদনা করছেন যা সফটওয়্যারের জন্য ইন্টারফেস টেক্সট প্রদান করে।\nএই পাতাতে সংঘটিত পরিবর্তন এই উইকির ব্যবহারকারীদের ইন্টারফেসে প্রভাব ফেলবে, যা অন্য ব্যবহারকারীরা দেখতে পাবেন।\nসকল উইকির অনুবাদে কোনো ধরনের সংযোজন বা পরিবর্তন করতে, অনুগ্রহ করে মিডিয়াউইকি স্থানীয়করন প্রকল্প [//translatewiki.net/ translatewiki.net] ব্যবহার করুন।",
+       "editinginterface": "<strong>সতর্কীকরণ:</strong> আপনি এমন একটি পাতা সম্পাদনা করছেন যা সফটওয়্যারের জন্য ইন্টারফেস টেক্সট প্রদান করে।\nএই পাতাতে সংঘটিত পরিবর্তন এই উইকির ব্যবহারকারীদের ইন্টারফেসে প্রভাব ফেলবে, যা অন্য ব্যবহারকারীরা দেখতে পাবেন।",
+       "translateinterface": "সকল উইকির জন্য অনুবাদ যোগ বা পরিবর্তন করতে, দয়া করে [//translatewiki.net/ translatewiki.net], মিডিয়াউইকি স্থানীয়করণ প্রকল্প ব্যবহার করুন।",
        "cascadeprotected": "এই পাতাটি সম্পাদনা থেকে সুরক্ষিত, কারণ পাতাটি নিচের {{PLURAL:$1|টি পাতার|টি পাতার}} অন্তর্গত, যে পাতা(গুলি) \"প্রপাতাকার\" (cascading) বৈশিষ্ট্য সহযোগে সুরক্ষিত করা হয়েছে:\n$2",
        "namespaceprotected": "'''$1''' নামস্থানে কোন পাতা আপনার সম্পাদনা করার অনুমতি নেই।",
        "customcssprotected": "আপনার এই সিএসএস পাতাটি সম্পাদনা করার অনুমতি নেই, কারণ এ পাতায় অন্য ব্যবহারকারীর নিজস্ব সেটিংস রয়েছে।",
        "content-model-javascript": "জাভাস্ক্রিপ্ট",
        "content-model-css": "সিএসএস",
        "duplicate-args-category": "টেমপ্লেট আহ্বানে সদৃশ আর্গুমেন্ট ব্যবহার করা পাতা",
+       "duplicate-args-category-desc": "এই পাতায় টেমপ্লেট আহ্বান উপস্থিত রয়েছে যা সদৃশ আর্গুমেন্ট ব্যবহার করেছে, যেমন <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> বা <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>।",
        "expensive-parserfunction-warning": "'''সতর্ক হোন:''' এই পাতাটি অনেক বেশি পরিমাণে এক্সপেনসিভ পার্সার ফাংশন কল রয়েছে।\n\nএটি $2-এর চেয়ে কম পরিমাণ {{PLURAL:$2|কল|কল}} থাকা উচিত, যেখানে মোট কলের সংখ্যা {{PLURAL:$1|বর্তমানে $1|বর্তমানে $1}}।",
        "expensive-parserfunction-category": "অনেক বেশি পরিমাণে এক্সপেনসিভ পার্সার ফাংশন কল থাকা পাতাসমূহ",
        "post-expand-template-inclusion-warning": "'''সতর্ক হোন:''' টেমপ্লেটের ইনক্লুড আকার অনেক বেশি।\nকিছু টেমপ্লেট সংযুক্ত করা নাও যেতে পারে।",
        "gender-female": "তিনি (মহিলা) উইকি পাতা সম্পাদনা করেন",
        "prefs-help-gender": "সেটিংসের এই পরিবর্তন ঐচ্ছিক।\nসফটওয়্যারে মাধ্যমে লিঙ্গ অনুযায়ী সম্বধনের ক্ষেত্রে এটি ব্যবহৃত হয়।\nএই তথ্য সকলের জন্য উন্মুক্ত থাকেবে।",
        "email": "ই-মেইল",
-       "prefs-help-realname": "আসল নাম দেওয়া অনাবশ্যক। যদি আসল নাম দেন, তবে আপনার কাজের স্বীকৃতি দানে তা ব্যবহার করা হবে।",
+       "prefs-help-realname": "আসল নাম দেওয়া অনাবশ্যক।\nযদি আসল নাম দেন, তবে আপনার কাজের স্বীকৃতি দানে তা ব্যবহার করা হবে।",
        "prefs-help-email": "ইমেইল ঠিকানা ঐচ্ছিক, তবে পাসওয়ার্ড ভুলে গেলে নতুন করে পাসওয়ার্ড নিতে এটির প্রয়োজন হবে।",
        "prefs-help-email-others": "আপনি আপনার পরিচয় প্রকাশ না করেও আপনার ব্যবহারকারী অথবা আলাপ পাতাটির মাধ্যমে অন্যদেরকে আপনার সাথে যোগাযোগ করতে দিতে পারেন।",
        "prefs-help-email-required": "ই-মেইল ঠিকানা আবশ্যক।",
        "pager-older-n": "{{PLURAL:$1|আরও পুরনো ১টি|আরও পুরনো $1টি}}",
        "suppress": "ওভারসাইট",
        "querypage-disabled": "কারিগরি কারণে এই বিশেষ পাতাটি আপাতত বন্ধ রয়েছে।",
+       "apihelp": "এপিআই সাহায্য",
        "apihelp-no-such-module": "মডিউল \"$1\" পাওয়া যায়নি।",
        "booksources": "বইয়ের উৎস",
        "booksources-search-legend": "বইয়ের উৎসের জন্য অনুসন্ধান করা হোক",
        "tooltip-preferences-save": "পছন্দ সংরক্ষণ",
        "tooltip-summary": "একটি সংক্ষিপ্ত সারাংশ দিন",
        "interlanguage-link-title": "$1 - $2",
+       "common.css": "/* এখানে সিএসএস নিবেশিত করা হলে তা সব স্কিনে প্রয়োগ করা হবে */",
        "anonymous": "{{SITENAME}} এর বেনামী {{PLURAL:$1|ব্যবহারকারী|ব্যবহারকারীবৃন্দ}}",
        "siteuser": "{{SITENAME}} ব্যবহারকারী $1",
        "anonuser": "{{SITENAME}} বেনামী ব্যবহারকারী $1",
        "version-parser-function-hooks": "পার্সার ফাংশন হুক",
        "version-hook-name": "হুকের নাম",
        "version-hook-subscribedby": "সাবস্ক্রাইব করেছেন",
-       "version-version": "(সংস্করণ $1)",
+       "version-version": "($1)",
        "version-no-ext-name": "[নাম নেই]",
        "version-license": "মিডিয়াউইকি লাইসেন্স",
        "version-ext-license": "লাইসেন্স",
        "mediastatistics-header-audio": "অডিও",
        "mediastatistics-header-video": "ভিডিও",
        "mediastatistics-header-office": "অফিস",
+       "mediastatistics-header-archive": "সংকুচিত বিন্যাস",
        "json-error-unknown": "JSON-এ একটি সমস্যা রয়েছে। ত্রুটি: $1",
        "json-error-syntax": "সিনট্যাক্স ত্রুটি"
 }
index ab26b65..880f255 100644 (file)
        "badipaddress": "L'adreça IP no té el format correcte.",
        "blockipsuccesssub": "S'ha blocat amb èxit",
        "blockipsuccesstext": "S'ha {{GENDER:$1|blocat|blocada}} [[Special:Contributions/$1|$1]] .<br />\nVegeu la [[Special:BlockList|llista de bloqueigs]] per revisar-los.",
-       "ipb-blockingself": "Esteu a punt de blocar-vos a vós mateix! Esteu segurs de voler-ho fer?",
-       "ipb-confirmhideuser": "Esteu a punt de bloquejar un usuari que està marcat amb l'opció «amaga l'usuari». Això suprimirà el seu nom a totes les llistes i registres. Esteu segurs de voler-ho fer?",
+       "ipb-blockingself": "Esteu a punt de blocar el vostre propi compte! Esteu segur de voler-ho fer?",
+       "ipb-confirmhideuser": "Esteu a punt de blocar un usuari amb l'opció d'amagar el seu nom. Això suprimirà el seu nom a totes les llistes i registres. Esteu segur de voler-ho fer?",
        "ipb-confirmaction": "Si esteu segur que voleu fer-ho, marqueu el camp «{{int:ipb-confirm}}» a la part inferior.",
        "ipb-edit-dropdown": "Edita les raons per a blocar",
        "ipb-unblock-addr": "Desbloca $1",
index 15f778d..8c9c020 100644 (file)
        "content-model-text": "цхьалхе йоза",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
-       "duplicate-args-category": "Ð\9aепийн Ð´ÐµÑ\85аÑ\80аÑ\88каÑ\85Ñ\8c Ñ\8eÑ\85 Ñ\8eÑ\85а Ð°Ñ\80гÑ\83менÑ\82аш йолу агӀонаш",
+       "duplicate-args-category": "Ð\9aепийн ÐºÑ\85айкÑ\85амаÑ\88каÑ\85Ñ\8c Ð°Ñ\80гÑ\83менÑ\82аÑ\88 Ñ\8eÑ\85\8eÑ\85а Ð»ÐµÐ»Ð¾ш йолу агӀонаш",
        "expensive-parserfunction-warning": "'''Тидам бе!''' Ресурсийн функцийн дехарш сов даьлла агӀонаш .\n\nДукху хилла ца деза {{PLURAL:$2|$2 дехар|$2 дехарш|1=цхьана дехар}}, хӀинца $1 {{PLURAL:$1|дехар}} ду.",
        "expensive-parserfunction-category": "Ресурсийн функцийн дехарш сов даьлла агӀонаш",
        "post-expand-template-inclusion-warning": "ДӀахьедар: юкъа тоьхна кепашан жамӀан барам тӀех бокха бу. Цхьайолу кепаш юкъа тухур яц.",
index 5ce0443..f23330b 100644 (file)
        "api-error-stashfailed": "Vnitřní chyba: Serveru se nepodařilo uložit dočasný soubor.",
        "api-error-publishfailed": "Vnitřní chyba: Serveru se nepodařilo zveřejnit dočasný soubor.",
        "api-error-stasherror": "Při načítání souboru do skrýše došlo k chybě.",
+       "api-error-stashedfilenotfound": "Při pokusu o načtení souboru ze skrýše nebyl uložený soubor nalezen.",
+       "api-error-stashpathinvalid": "Cesta, na které měl být soubor uložen ve skrýši, je neplatná.",
+       "api-error-stashfilestorage": "Při ukládání souboru do skrýše došlo k chybě.",
+       "api-error-stashzerolength": "Server nemohl soubor uložit do skrýše, protože má nulovou délku.",
+       "api-error-stashnotloggedin": "Pro ukládání souboru do skrýše musíte být přihlášeni.",
+       "api-error-stashwrongowner": "Soubor, ke kterému se ve skrýši pokoušíte přistoupit, vám nepatří.",
+       "api-error-stashnosuchfilekey": "Klíč souboru, ke kterém se ve skrýši pokoušíte přistoupit, neexistuje.",
        "api-error-timeout": "Server neodpověděl v očekávaném čase.",
        "api-error-unclassified": "Došlo k neznámé chybě.",
        "api-error-unknown-code": "Neznámá chyba: „$1“.",
index cacfe0f..eecd71f 100644 (file)
        "content-model-text": "Klartext",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
-       "duplicate-args-category": "Seiten, die doppelte Argumente in Vorlagenaufrufe verwenden",
+       "duplicate-args-category": "Seiten, die doppelte Argumente in Vorlagenaufrufen verwenden",
        "duplicate-args-category-desc": "Die Seite enthält Vorlagenaufrufe, die Duplikate von Argumenten verwenden, wie <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> oder <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "'''Achtung:''' Diese Seite enthält zu viele Aufrufe aufwändiger Parserfunktionen.\n\nSie darf nicht mehr als $2 {{PLURAL:$2|Aufruf|Aufrufe}} haben, es {{PLURAL:$1|ist aktuell $1 Aufruf|sind aktuell $1 Aufrufe}}.",
        "expensive-parserfunction-category": "Seiten, die aufwändige Parserfunktionen zu oft aufrufen",
index a291810..bc9d6ff 100644 (file)
        "emailccsubject": "Αντίγραφο του μηνυματός σας στο $1: $2",
        "emailsent": "Το μήνυμα έχει σταλεί",
        "emailsenttext": "Το μήνυμά σας έχει σταλεί.",
-       "emailuserfooter": "Î\91Ï\85Ï\84Ï\8c Ï\84ο Î·Î»ÎµÎºÏ\84Ï\81ονικÏ\8c Î¼Î®Î½Ï\85μα Ï\83Ï\84άλθηκε Î±Ï\80Ï\8c Ï\84ον/Ï\84ην \"$1\" Ï\83Ï\84ον Ï\84ον/Ï\84ην \"$2\" Î¼Î­Ï\83Ï\89 Ï\84ηÏ\82 Î»ÎµÎ¹Ï\84οÏ\85Ï\81γίαÏ\82 \"αÏ\80οÏ\83Ï\84ολήÏ\82 Î¼Î·Î½Ï\85μάÏ\84Ï\89ν\" στο {{SITENAME}}.",
+       "emailuserfooter": "Î\91Ï\85Ï\84Ï\8c Ï\84ο Î¼Î®Î½Ï\85μα Î·Î»ÎµÎºÏ\84Ï\81ονικοÏ\8d Ï\84αÏ\87Ï\85δÏ\81ομείοÏ\85 Ï\83Ï\84άλθηκε Î±Ï\80Ï\8c {{GENDER:$1|Ï\84ον Ï\87Ï\81ήÏ\83Ï\84η|Ï\84ην Ï\87Ï\81ήÏ\83Ï\84Ï\81ια}} $1 Ï\83{{GENDER:$2|Ï\84ον Ï\87Ï\81ήÏ\83Ï\84η|Ï\84ην Ï\87Ï\81ήÏ\83Ï\84Ï\81ια}} $2 Î¼Î­Ï\83Ï\89 Ï\84ηÏ\82 Î»ÎµÎ¹Ï\84οÏ\85Ï\81γίαÏ\82 Â«Î±Ï\80οÏ\83Ï\84ολήÏ\82 Î¼Î·Î½Ï\8dμαÏ\84οÏ\82 Î·Î»ÎµÎºÏ\84Ï\81ονικοÏ\8d Ï\84αÏ\87Ï\85δÏ\81ομείοÏ\85 Ï\83ε Ï\87Ï\81ήÏ\83Ï\84η» στο {{SITENAME}}.",
        "usermessage-summary": "Φεύγετε από τις ειδοποιήσεις συστήματος.",
        "usermessage-editor": "Μηνύματα συστήματος",
        "usermessage-template": "MediaWiki:UserMessage",
index 8fddc08..319d4f8 100644 (file)
                        "Amitie 10g",
                        "Eurodyne",
                        "Gleki",
-                       "Jonathan rrr"
+                       "Jonathan rrr",
+                       "Paynekiller92"
                ]
        },
        "tog-underline": "Subrayar los enlaces:",
        "gender-female": "Femenino",
        "prefs-help-gender": "Opcional: el software utiliza esta preferencia para dirigirse a ti con el género gramatical apropiado. Esta información es pública.",
        "email": "Correo electrónico",
-       "prefs-help-realname": "El nombre real es opcional. Si decides proporcionarlo, se usará para dar atribución a tu trabajo.",
+       "prefs-help-realname": "El nombre real es opcional. Si lo proporcionas, se usará para dar atribución a tu trabajo.",
        "prefs-help-email": "La dirección de correo electrónico es opcional, pero es necesaria para el restablecimiento de tu contraseña, en caso de que la olvides.",
        "prefs-help-email-others": "También puedes permitir que otros usuarios te contacten por correo a través de un enlace en tus páginas de usuario y de discusión.\nTu dirección de correo no se revela cuando otros usuarios te contactan.",
        "prefs-help-email-required": "Es necesario proporcionar una dirección de correo electrónico.",
index 9bbc6b4..e5804b1 100644 (file)
        "api-error-stashfailed": "Sisetõrge: Serveril ei õnnestunud ajutist faili talletada.",
        "api-error-publishfailed": "Sisetõrge: Serveril ebaõnnestus ajutise faili avaldamine.",
        "api-error-stasherror": "Selle faili hoidlasse üleslaadimisel ilmnes tõrge.",
+       "api-error-stashedfilenotfound": "Algses hoidlas talletatud faili ei leitud, kui seda sealt üles püüti laadida.",
+       "api-error-stashpathinvalid": "Failitee, kus algse hoidla fail pidanuks leiduma, oli vigane.",
+       "api-error-stashfilestorage": "Faili algsesse hoidlasse talletamisel esines tõrge.",
+       "api-error-stashzerolength": "Server ei saanud faili algses hoidlas talletada, sest fail on tühi.",
+       "api-error-stashnotloggedin": "Pead olema sisse logitud, et salvestada faile üleslaadimise algsesse hoidlasse.",
+       "api-error-stashwrongowner": "Fail, mille juurde algses hoidlas üritasid pääseda, ei kuulu sulle.",
+       "api-error-stashnosuchfilekey": "Failivõtit, mille juurde algses hoidlas üritasid pääseda, pole olemas.",
        "api-error-timeout": "Server ei vastanud oodatud aja sees.",
        "api-error-unclassified": "Ilmnes teadmata tõrge.",
        "api-error-unknown-code": "Teadmata tõrge: \"$1\"",
index 883ed0d..f79f369 100644 (file)
        "api-error-stashfailed": "خطای داخلی: کارساز نمی‌تواند پرونده موقت را ذخیره کند.",
        "api-error-publishfailed": "خطای داخلی: کارساز نمی‌تواند پرونده موقت را ذخیره کند.",
        "api-error-stasherror": "هنگام انتقال پوشه برای ذخیره خطایی بود.",
+       "api-error-stashedfilenotfound": "زمانی که تلاش برای بارگذاری فایل استش بود، فایل استش یافت نشد.",
+       "api-error-stashpathinvalid": "مسیری که فایل استش در آن باید یافت می‌شد اشتباه است.",
+       "api-error-stashfilestorage": "برای ذخیرهٔ فایل استش خطایی رخ داده است.",
+       "api-error-stashzerolength": "سرور نمی‌تواند فایل استش را ذخیره کند چون حجم آن صفر است.",
+       "api-error-stashnotloggedin": "برای ذخیرهٔ فایل‌ها در بارگذاری استش باید وارد سامانه شده‌باشید.",
+       "api-error-stashwrongowner": "فایلی که در استش قصد داشتید به آن دسترسی داشته‌باشید متعلق به شما نیست.",
+       "api-error-stashnosuchfilekey": "کلیدی که در فایل استش می خواستید به آن دسترسی داشته‌باشید، وجود ندارد.",
        "api-error-timeout": "کارساز در زمان انتظار هیچ پاسخی نداد.",
        "api-error-unclassified": "یک خطای ناشناخته رخ داد.",
        "api-error-unknown-code": "خطای ناشناخته: \" $1 \"",
index 6e83498..6cccd40 100644 (file)
        "invert": "Käänteinen valinta",
        "tooltip-invert": "Valitse tämä kohta, jos haluat piilottaa muutokset sivuihin valitussa nimiavaruudessa (ja liittyviin nimiavaruuksiin, jos valittu)",
        "namespace_association": "Liittyvä nimiavaruus",
-       "tooltip-namespace_association": "Valitse tämä kohta, jos haluat sisällyttää ne keskustelu- tai aihe-nimiavaruudet, jotka liittyvät valittuun nimiavaruuteen",
+       "tooltip-namespace_association": "Valitse tämä kohta, jos haluat haun sisältävän myös sen keskustelu- tai aihe-nimiavaruuden, joka liittyy valittuun nimiavaruuteen",
        "blanknamespace": "(sivut)",
        "contributions": "{{GENDER:$1|Käyttäjän}} muokkaukset",
        "contributions-title": "Käyttäjän $1 muokkaukset",
index b8975fb..15eeca8 100644 (file)
        "viewsourcetext": "Vous pouvez voir et copier le contenu de la page :",
        "viewyourtext": "Vous pouvez voir et copier le contenu de '''vos modifications''' à cette page :",
        "protectedinterface": "Cette page fournit du texte d'interface pour le logiciel sur ce wiki, et est protégée pour éviter les abus.\nPour ajouter ou modifier des traductions sur tous les wikis, veuillez utiliser [//translatewiki.net/ translatewiki.net], le projet de localisation de MediaWiki.",
-       "editinginterface": "<strong>Attention</strong> vous êtes en train de modifier une page utilisée pour créer le texte de l’interface du logiciel.\nLes changements sur cette page se répercuteront sur l’apparence de l’interface utilisateur pour les autres utilisateurs de ce wiki.",
+       "editinginterface": "<strong>Attention :</strong> vous êtes en train de modifier une page utilisée pour créer le texte de l’interface du logiciel.\nLes changements sur cette page se répercuteront sur l’apparence de l’interface utilisateur pour les autres utilisateurs de ce wiki.",
        "translateinterface": "Pour ajouter ou modifier des traductions pour tous les wikis, veuillez utiliser [//translatewiki.net/ translatewiki.net], le projet de localisation de MédiaWiki.",
        "cascadeprotected": "Cette page est protégée car elle est incluse par {{PLURAL:$1|la page suivante, qui a été protégée|les pages suivantes, qui ont été protégées}} avec l’option « protection en cascade » activée :\n$2",
        "namespaceprotected": "Vous n'avez pas la permission de modifier les pages de l'espace de noms « '''$1''' ».",
        "api-error-stashfailed": "Erreur interne : le serveur n'a pas pu enregistrer le fichier temporaire.",
        "api-error-publishfailed": "Erreur interne: Le serveur n'a pas pu publier le fichier temporaire.",
        "api-error-stasherror": "Une erreur s'est produite lors du téléchargement du fichier pour le dissimuler.",
+       "api-error-stashedfilenotfound": "Le fichier caché n’a pas été trouvé lors de la tentative pour le télécharger depuis sa cachette.",
+       "api-error-stashpathinvalid": "Le chemin où aurait dû se trouver le fichier caché n’est pas valide.",
+       "api-error-stashfilestorage": "Une erreur s’est produite en stockant le fichier dans la cachette.",
+       "api-error-stashzerolength": "Le serveur n’a pas pu cacher le fichier, car il a une taille de zéro.",
+       "api-error-stashnotloggedin": "Vous devez être connecté pour enregistrer des fichiers dans la cachette de téléchargement.",
+       "api-error-stashwrongowner": "Le fichier auquel vous essayez d’accéder dans la cachette ne vous appartient pas.",
+       "api-error-stashnosuchfilekey": "La clé du fichier auquel vous essayez d’accéder dans la cachette n’existe pas.",
        "api-error-timeout": "Le serveur n'a pas répondu dans le délai imparti.",
        "api-error-unclassified": "Une erreur inconnue s'est produite",
        "api-error-unknown-code": "Erreur inconnue : « $1 »",
index 430c6fc..743f91f 100644 (file)
        "log-name-pagelang": "Logbuk för spriak-feranrangen",
        "log-description-pagelang": "Det as en logbuk för sidjenspriak-feranrangen",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|hää}} det sidjenspriak för $3 faan $4 tu $5 feranert.",
-       "default-skin-not-found": "Hoppla! Die in <code dir=\"ltr\">$wgDefaultSkin</code> als <code>$1</code> definierte Standardbenutzeroberfläche für dein Wiki ist nicht verfügbar.\n\nDeine Installation scheint die folgenden Benutzeroberflächen zu enthalten. Siehe [https://www.mediawiki.org/wiki/Manual:Skin_configuration/de das Benutzerhandbuch] zur Aktivierung und Auswahl des Standards.\n\n$2\n\n; Falls du gerade MediaWiki installiert hast:\n: Du hast vermutlich von Git oder direkt vom Quellcode mithilfe einer anderen Methode installiert. Dies wird erwartet. Versuche einige Benutzeroberflächen aus dem  [https://www.mediawiki.org/wiki/Category:All_skins MediaWiki.org-Benutzeroberflächenverzeichnis] zu installieren, indem du:\n:* Den [https://www.mediawiki.org/wiki/Download/de Tarball-Installer] herunterlädst, der mit verschiedenen Benutzeroberflächen und Erweiterungen kommt. Du kannst das Verzeichnis <code>skins/</code> kopieren und einfügen.\n:* Eine der <code>mediawiki/skins/*</code>-Repositorien über Git in das <code dir=\"ltr\">skins/</code>-Verzeichnis deiner MediaWiki-Installation klonst.\n: Dies sollte nicht dein Git-Repositorium beeinträchtigen, falls du ein MediaWiki-Entwickler bist.\n\n; Falls du gerade MediaWiki aktualisiert hast:\n: MediaWiki 1.24 und neuere Versionen aktivieren nicht mehr automatisch installierte Benutzeroberflächen (siehe das [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Benutzerhandbuch]). Du kannst die folgenden Zeilen in die Datei <code>LocalSettings.php</code> einfügen, um alle derzeit installierten Benutzeroberflächen zu aktivieren:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Falls du gerade <code>LocalSettings.php</code> geändert hast:\n: Überprüfe die Namen der Benutzeroberflächen auf Tippfehler.",
-       "default-skin-not-found-no-skins": "Hoppla! Die in <code>$wgDefaultSkin</code> als <code>$1</code> definierte Standardbenutzeroberfläche für dein Wiki ist nicht verfügbar.\n\nDu hast keine installierten Benutzeroberflächen.\n\n; Falls du gerade MediaWiki installiert oder aktualisiert hast:\n: Du hast vermutlich von Git oder direkt vom Quellcode mithilfe einer anderen Methode installiert. Dies wird erwartet. MediaWiki 1.24 und neuere Versionen enthalten keine Benutzeroberflächen im Haupt-Repositorium. Versuche einige Benutzeroberflächen aus dem [https://www.mediawiki.org/wiki/Category:All_skins MediaWiki.org-Benutzeroberflächenverzeichnis] zu installieren, indem du:\n:* Den [https://www.mediawiki.org/wiki/Download/de Tarball-Installer] herunterlädst, der mit verschiedenen Benutzeroberflächen und Erweiterungen kommt. Du kannst das  <code>skins/</code>-Verzeichnis kopieren und einfügen.\n:* Eine der <code>mediawiki/skins/*</code>-Repositorien über Git in das <code dir=\"ltr\">skins/</code>-Verzeichnis deiner MediaWiki-Installation klonst.\n: Dies sollte nicht dein Git-Repositorium beeinträchtigen, falls du ein MediaWiki-Entwickler bist. Siehe das [https://www.mediawiki.org/wiki/Manual:Skin_configuration/de Benutzerhandbuch] zur Aktivierung von Benutzeroberflächen und Auswahl des Standards.",
+       "default-skin-not-found": "Uuha! Uun <code dir=\"ltr\">$wgDefaultSkin</code> as fäästlaanj, dat <code>$1</code> dan standard-skak as. Man hi as ei diar!\n\nDin instalatjuun hää wel jodiar skaker. Luke uk uun't [https://www.mediawiki.org/wiki/Manual:Skin_configuration/de brüker-hoonbuk], am skaker tu aktiwiarin an standards iinturachten.\n\n$2\n\n; Wan dü MediaWiki jüst instaliaret heest:\n: Dü heest was faan Git of direkt faan a kwelcode instaliaret. Do as det nian woner. Dü könst skaker ütj det [https://www.mediawiki.org/wiki/Category:All_skins MediaWiki.org-Skakfertiaknis] instaliare. Diarför skel dü:\n:* Di [https://www.mediawiki.org/wiki/Download/de Tarball-Installer] deelloose, hi komt mä ünlik skaker an ütjwidjangen. Dü könst det  <code>skins/</code>-fertiaknis kopiare an iinsaat.\n:* Ian faan a <code>mediawiki/skins/*</code>-fertiaknissen auer Git iin uun det <code dir=\"ltr\">skins/</code>-fertiaknis faan din MediaWiki-Instalatjuun auerskriiw.\n: Det skul din Git-fertiaknis ei uunstaken maage, wan dü en MediaWiki-ütjwerker beest.\n\n; Wan dü jüst MediaWiki aktualisiaret heest:\n: MediaWiki 1.24 an neier werjuunen aktiwiare instaliaret skaker ei muar faan salew (luke uk iin uun det [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery brüker-hoonbuk]). Dü könst jodiar räen uun det datei <code>LocalSettings.php</code> iinsaat, am aal a instaliaret skaker tu aktiwiarin:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Wan dü jüst <code>LocalSettings.php</code> feranert heest:\n: Heest dü a skaker uk aaltumaal rocht skrewen?",
+       "default-skin-not-found-no-skins": "Uuha! Uun <code>$wgDefaultSkin</code> as fäästlaanj, dat <code>$1</code> dan standard-skak as. Man hi as ei diar!\n\nDü heest goor nian skak instaliaret.\n\n; Wan dü MediaWiki jüst instaliaret of aktualisiaret heest:\n: Dü heest was faan Git of direkt faan a kwelcode instaliaret. Do as det nian woner. MediaWiki 1.24 an neier werjuunen haa nian skak uun't hood-fertiaknis. Dü könst skaker ütj det [https://www.mediawiki.org/wiki/Category:All_skins MediaWiki.org-Skakfertiaknis] instaliare. Diarför skel dü:\n:* Di [https://www.mediawiki.org/wiki/Download/de Tarball-Installer] deelloose, hi komt mä ünlik skaker an ütjwidjangen. Dü könst det  <code>skins/</code>-fertiaknis kopiare an iinsaat.\n:* Ian faan a <code>mediawiki/skins/*</code>-fertiaknissen auer Git iin uun det <code dir=\"ltr\">skins/</code>-fertiaknis faan din MediaWiki-Instalatjuun auerskriiw.\n: Det skul din Git-fertiaknis ei uunstaken maage, wan dü en MediaWiki-ütjwerker beest. Luke uk uun't [https://www.mediawiki.org/wiki/Manual:Skin_configuration/de brüker-hoonbuk], am skaker tu aktiwiarin an standards iinturachten.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (aktiwiaret)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''ufsteld''')",
        "mediastatistics": "Statistik faan meedien",
index 839bc22..ed1c57e 100644 (file)
        "api-error-stashfailed": "Errore interno: il server non è riuscito a memorizzare il documento temporaneo.",
        "api-error-publishfailed": "Errore interno: il server non è riuscito a pubblicare il documento temporaneo.",
        "api-error-stasherror": "Si è verificato un errore durante il caricamento del file in stash.",
+       "api-error-stashedfilenotfound": "Il file in stash non è stato trovato durante il tentativo di caricamento dallo stash.",
+       "api-error-stashpathinvalid": "Il percorso in cui il file in stash dovrebbe trovarsi non è valido.",
+       "api-error-stashfilestorage": "Si è verificato un errore durante la memorizzazione del file in stash.",
+       "api-error-stashzerolength": "Il server non può inserire il file in stash, poiché ha lunghezza zero.",
+       "api-error-stashnotloggedin": "Devi aver effettuato l'accesso per poter salvare il file nel caricamento in stash.",
+       "api-error-stashwrongowner": "Il file a cui stai tentando di accedere nello stash non appartiene a te.",
+       "api-error-stashnosuchfilekey": "La chiave del file a cui stai tentando di accedere nello stash non esiste.",
        "api-error-timeout": "Il server non ha risposto entro il tempo previsto.",
        "api-error-unclassified": "Si è verificato un errore sconosciuto.",
        "api-error-unknown-code": "Errore sconosciuto: \"$1\"",
index 2dabf8b..e95eeea 100644 (file)
        "revdelete-uname-unhid": "利用者名の可視化",
        "revdelete-restricted": "管理者に対する制限の適用",
        "revdelete-unrestricted": "管理者に対する制限の除去",
-       "logentry-merge-merge": "$1 {{GENDER:$2|統合元}} と$3 を $4 に統合(改訂版を$5に掲載)",
+       "logentry-merge-merge": "$1{{GENDER:$2|統合元}} と$3を$4に統合(改訂版を$5に掲載)",
        "logentry-move-move": "$1 がページ「$3」を「$4」に{{GENDER:$2|移動しました}}",
        "logentry-move-move-noredirect": "$1 がページ「$3」を「$4」に、リダイレクトを残さずに{{GENDER:$2|移動しました}}",
        "logentry-move-move_redir": "$1 がページ「$3」をリダイレクトの「$4」に{{GENDER:$2|移動しました}}",
index d9fa6f7..ac1921e 100644 (file)
@@ -5,7 +5,8 @@
                        "Ukabia",
                        "Yocahuna",
                        "គីមស៊្រុន",
-                       "아라"
+                       "아라",
+                       "Chabi1"
                ]
        },
        "tog-underline": "Lingk andalainin",
        "powersearch-ns": "Saach ina niemspies:",
        "preferences": "Prefrens",
        "mypreferences": "Mi prefrans",
+       "prefs-help-realname": "Riil niem apshanal. Ef yu giit, imaita yuuz az achribyuushan fi yu wok.",
        "group-sysop": "Adminischrieta",
        "grouppage-sysop": "{{ns:project}}:Adminischrieta",
        "newuserlogpage": "Yuuza krieshan lag",
        "tooltip-feed-atom": "Atom fiid fi dis piej",
        "tooltip-t-contributions": "Vyuu di lis a kanchribyuushan a dis yuuza",
        "tooltip-t-emailuser": "Sen e-miel tu dis yuuza",
+       "tooltip-t-info": "Muo infamieshan bout da piej ya",
        "tooltip-t-upload": "Opluod fail",
        "tooltip-t-specialpages": "Lis a aal peshal piej",
        "tooltip-t-print": "Printobl voerjan a dis piej",
index 8ebd626..0d10bec 100644 (file)
        "permalink": "តំណភ្ជាប់អចិន្ត្រៃយ៍",
        "print": "បោះពុម្ព",
        "view": "មើល",
+       "view-foreign": "មើលលើ $1",
        "edit": "កែប្រែ",
        "create": "បង្កើត",
        "editthispage": "កែប្រែទំព័រនេះ",
        "otherlanguages": "ជាភាសាដទៃទៀត",
        "redirectedfrom": "(ត្រូវបានបញ្ជូនបន្តពី $1)",
        "redirectpagesub": "ទំព័របញ្ជូនបន្ត",
+       "redirectto": "បញ្ជូនបន្តទៅ៖",
        "lastmodifiedat": "ទំព័រនេះត្រូវបានកែចុងក្រោយនៅ$2 $1",
        "viewcount": "ទំព័រនេះ​ត្រូវបានចូលមើល​ចំនួន'''{{PLURAL:$1|ម្ដង|$1ដង}}'''",
        "protectedpage": "ទំព័រដែលត្រូវបានការពារ",
        "hidetoc": "លាក់",
        "collapsible-collapse": "បង្រួម",
        "collapsible-expand": "ពន្លាត",
+       "confirmable-confirm": "តើ {{GENDER:$1|អ្នក}} ប្រាកដហើយ?",
+       "confirmable-yes": "បាទ/ចាស",
+       "confirmable-no": "ទេ",
        "thisisdeleted": "មើល ឬ​ ស្ដារ $1 ឡើងវិញ?",
        "viewdeleted": "មើល $1?",
        "restorelink": "{{PLURAL:$1|កំណែប្រែមួយត្រូវបានលុបចោល|កំណែប្រែចំនួន $1 ត្រូវបានលុបចោល}}",
        "viewsourcetext": "អ្នកអាចមើលនិងចម្លងកូដរបស់ទំព័រនេះ៖",
        "viewyourtext": "អ្នកអាចមើលនិងចម្លងកូដរបស់'''ការកែប្រែរបស់អ្នក'''មកកាន់ទំព័រនេះ៖",
        "protectedinterface": "ទំព័រនេះផ្ដល់នូវអត្ថបទអន្តរមុខសម្រាប់សូហ្វវែរនៅក្នុងវិគីនេះ និងត្រូវបានចាក់សោដើម្បីចៀសវាងការបំពាន។\nដើម្បីបន្ថែមឬផ្លាស់ប្ដូរការបកប្រែសំរាប់វិគីទាំងអស់ សូមប្រើប្រាស់ [//translatewiki.net/ translatewiki.net] ដែលជាគំរោងបកប្រែរបស់MediaWiki។",
-       "editinginterface": "'''ប្រយ័ត្ន៖''' អ្នកកំពុងតែកែប្រែទំព័រដែលបានប្រើប្រាស់​ដើម្បីផ្ដល់ជូនអន្តរមុខសម្រាប់សូហ្វវែរ។ បំលាស់ប្ដូរចំពោះទំព័រនេះ​នឹងប៉ះពាល់ដល់ទ្រង់ទ្រាយរបស់ទំព័រអន្តរមុខសំរាប់អ្នកប្រើប្រាស់​ជាច្រើន ដែលប្រើប្រាស់វិគីនេះ។ ដើម្បីបន្ថែមឬផ្លាស់ប្ដូរការបកប្រែ​សំរាប់វិគីទាំងអស់ សូបប្រើប្រាស់  [//translatewiki.net/wiki/Main_Page?setlang=km translatewiki.net] គម្រោង​បកប្រែរបស់មេឌាវិគី ។",
+       "editinginterface": "'''ប្រយ័ត្ន៖''' អ្នកកំពុងតែកែប្រែទំព័រដែលបានប្រើប្រាស់​ដើម្បីផ្ដល់ជូនអន្តរមុខសម្រាប់សូហ្វវែរ។ បំលាស់ប្ដូរចំពោះទំព័រនេះ​នឹងប៉ះពាល់ដល់ទ្រង់ទ្រាយរបស់ទំព័រអន្តរមុខសំរាប់អ្នកប្រើប្រាស់​ជាច្រើន ដែលប្រើប្រាស់វិគីនេះ។",
+       "translateinterface": "ដើម្បីបន្ថែមឬកែប្រែការបកប្រែសម្រាប់វិគីទាំងអស់ សូមប្រើប្រាស់ [//translatewiki.net/ translatewiki.net] ដែលជាគម្រោងបកប្រែវិគីមេឌា។",
        "cascadeprotected": "ទំព័រនេះត្រូវបានការពារពីការការប្រែដោយសារវាមាន{{PLURAL:$1|ទំព័រ, ដែលមាន}} ដែលត្រូវបានការពារជាមួយជំរើស\"ជាបណ្ដាក់\"៖\n$2",
        "namespaceprotected": "អ្នកមិនមានសិទ្ធិកែប្រែទំព័រក្នុងប្រភេទ'''$1'''ទេ។",
        "customcssprotected": "អ្នកមិនមាន​ការអនុញ្ញាត​ក្នុងការកែប្រែទំព័រ CSS នេះទេ ព្រោះវាផ្ទុកការកំណត់ផ្ទាល់ខ្លួនផ្សេងៗរបស់អ្នកប្រើប្រាស់ម្នាក់ផ្សេងទៀត។",
        "invalidtitle-knownnamespace": "ចំណងជើងមិនត្រឹមត្រូវដែលមានលំហឈ្មោះ \"$2\" និងអត្ថបទ \"$3\"",
        "invalidtitle-unknownnamespace": "ចំណងជើងមិនត្រឹមត្រូវដែលមានលំហឈ្មោះមិនស្គាល់លេខ $1 និងអត្ថបទ \"$2\"",
        "exception-nologin": "មិនទាន់កត់ឈ្មោះចូលទេ",
-       "exception-nologin-text": "សូម[[Special:Userlogin|lកត់ឈ្មោះចូល]]ដើម្បីចូលអានទំព័រឬធ្វើសកម្មភាពនេះ។",
+       "exception-nologin-text": "សូមកត់ឈ្មោះចូលដើម្បីចូលអានទំព័រឬធ្វើសកម្មភាពនេះ។",
        "exception-nologin-text-manual": "សូម $1 ដើម្បីអាចចូលមើលទំព័រនេះឬធ្វើសកម្មភាពអ្វីមួយ។",
        "virus-badscanner": "ការ​កំណត់​រចនា​សម្ព័ន្ធ​មិន​ល្អ​៖ កម្មវិធី​ស្កេន​មេរោគមិន​ស្គាល់​៖ ''$1''",
        "virus-scanfailed": "ស្កេនមិនបានសំរេច (កូដ $1)",
        "gotaccountlink": "កត់ឈ្មោះចូល",
        "userlogin-resetlink": "តើអ្នកភ្លេចព័ត៌មានលម្អិតសម្រាប់កត់ឈ្មោះចូលហើយ?",
        "userlogin-resetpassword-link": "អ្នកភ្លេចពាក្យសម្ងាត់ហើយ?",
+       "userlogin-helplink2": "ជំនួយលើការកត់ឈ្មោះចូល",
        "userlogin-loggedin": "អ្នកបានកត់ឈ្មោះចូលជា {{GENDER:$1|$1}} ហើយ។\nប្រើសំណុំបែបបទខាងក្រោមដើម្បីកត់ឈ្មោះចូលជាអ្នកប្រើប្រាស់ផ្សេងម្នាក់ទៀត។",
        "userlogin-createanother": "បង្កើតគណនីមួយទៀត",
        "createacct-emailrequired": "អាសយដ្ឋានអ៊ីមែល",
        "loginlanguagelabel": "ភាសា៖ $1",
        "createacct-another-realname-tip": "អ្នកអាចផ្ដល់ឈ្មោះពិតរបស់អ្នកក៏បានមិនផ្ដល់ក៏បាន។ បើអ្នកផ្ដល់ឱ្យ វានឹងត្រូវបានប្រើប្រាស់់ដើម្បីបញ្ជាក់ភាពជាម្ចាស់​លើការរួមចំណែក​នានា​របស់អ្នក។",
        "pt-login": "កត់ឈ្មោះចូល",
+       "pt-login-button": "កត់ឈ្មោះចូល",
        "pt-createaccount": "បង្កើតគណនី",
        "pt-userlogout": "កត់ឈ្មោះចេញ",
        "php-mail-error-unknown": "កំហុសមិនស្គាល់នៅក្នុងអនុគមន៍ mail() របស់ PHP",
        "resetpass-temp-emailed": "អ្នកបានកត់ឈ្មោះចូលដោយប្រើលេខកូដបណ្ដោះអាសន្នផ្ញើតាមអ៊ីមែល។\nដើម្បីបញ្ចប់ការកត់ឈ្មោះចូល អ្នកចាំបាច់ត្រូវតែកំណត់ពាក្យសម្ងាត់ថ្មីនៅទីនេះ៖",
        "resetpass-temp-password": "ពាក្យសម្ងាត់បណ្តោះអាសន្ន:",
        "resetpass-expired": "ពាក្យសម្ងាត់របស់អ្នកហួសសុពលភាពហើយ។ សូមកំណត់ពាក្យសម្ងាត់ថ្មីដើម្បីកត់ឈ្មោះចូល។",
-       "resetpass-expired-soft": "ពាក្យសម្ងាត់របស់អ្នកហួសសុពលភាពហើយ ហើយអ្នកត្រូវតែកំណត់វាឡើងវិញ។ សូមជ្រើសរើសពាក្យសម្ងាត់ថ្មីឥឡូវនេះ ឬចុចបោះបង់ដើម្បីកំណត់វានៅពេលក្រោយ។",
-       "passwordreset": "កំណត់​ពាក្យសម្ងាត់​សាឡើងវិញ",
+       "resetpass-expired-soft": "ពាក្យសម្ងាត់របស់អ្នកហួសសុពលភាពហើយ ហើយអ្នកត្រូវតែកំណត់វាឡើងវិញ។ សូមជ្រើសរើសពាក្យសម្ងាត់ថ្មីឥឡូវនេះ ឬចុច\"{{int:resetpass-submit-cancel}}\"ដើម្បីកំណត់វានៅពេលក្រោយ។",
+       "resetpass-validity-soft": "ពាក្យសម្ងាត់របស់អ្នកមិនត្រឹមត្រូវទេ៖ $1\n\nសូមជ្រើសរើសពាក្យសម្ងាត់ថ្មីមួយឥឡូវនេះ ឬក៏ចុច\"{{int:resetpass-submit-cancel}}\" ដើម្បីកំណត់វាឡើងវិញនៅពេលក្រោយ។",
+       "passwordreset": "កំណត់​ពាក្យសម្ងាត់​ឡើងវិញ",
        "passwordreset-text-one": "បំពេញសំណុំបែបបទនេះដើម្បីស្ដារពាក្យសម្ងាត់របស់អ្នក។",
        "passwordreset-text-many": "{{PLURAL:$1|សូមបំពេញក្នុងប្រអប់មួយក្នុងចំណោមប្រអប់ខាងក្រោមដើម្បីទទួលពាក្យសម្ងាត់បណ្ដោះអាសន្នមួយតាមរយៈអ៊ីមែល។}}",
-       "passwordreset-legend": "á\9e\80á\9f\86á\9e\8eá\9e\8fá\9f\8bâ\80\8bá\9e\96á\9e¶á\9e\80á\9f\92á\9e\99á\9e\9fá\9e\98á\9f\92á\9e\84á\9e¶á\9e\8fá\9f\8bâ\80\8bá\9e\9fá\9e¶á\9e¡á\9e¾á\9e\84á\9e\9cá\9e·á\9e\89",
+       "passwordreset-legend": "កំណត់​ពាក្យសម្ងាត់​ឡើងវិញ",
        "passwordreset-disabled": "មុខងារប្ដូរទៅពាក្យសម្ងាត់ដើមត្រូវបានបិទមិនអោយប្រើនៅលើវិគីនេះ។",
        "passwordreset-emaildisabled": "មុខងារអ៊ីមែលត្រូវបានបិទមិនអោយប្រើនៅលើវិគីនេះ។",
        "passwordreset-username": "អត្តនាម៖",
        "preview": "មើលជាមុន",
        "showpreview": "បង្ហាញ​ការមើលជាមុន",
        "showdiff": "បង្ហាញ​បន្លាស់ប្ដូរ",
-       "anoneditwarning": "'''ប្រយ័ត្ន ៖''' អ្នកមិនបានកត់ឈ្មោះចូល​ទេ។ អាសយដ្ឋានIPរបស់អ្នក​នឹងត្រូវបាន​កត់ត្រាទុក​ក្នុងប្រវត្តិកែប្រែ​នៃទំព័រ​នេះ។",
-       "anonpreviewwarning": "''អ្នកមិនបានកត់ឈ្មោះចូល​ទេ។ ប្រសិនបើអ្នកធ្វើការរក្សាទុក នោះអាសយដ្ឋានIPរបស់អ្នក​នឹងត្រូវបាន​កត់ត្រាទុក​ក្នុងប្រវត្តិកែប្រែ​នៃទំព័រ​នេះ។''",
+       "anoneditwarning": "'''ប្រយ័ត្ន ៖''' អ្នកមិនបានកត់ឈ្មោះចូល​ទេ។ អាសយដ្ឋានIPរបស់អ្នក​នឹងត្រូវបាន​កត់ត្រាទុក​ក្នុងប្រវត្តិកែប្រែ​នៃទំព័រ​នេះ។ បើសិនជាអ្នក <strong>[$1 កត់ឈ្មោះចូល]</strong> ឬ <strong>[$2 បង្កើតគណនី]</strong> នោះការកែប្រែរបស់អ្នកនឹងត្រូវភ្ចាប់ជាមួយអត្តនាមរបស់អ្នកផង នឹងមានផលប្រយោជន៍ផ្សេងទៀតផង។",
+       "anonpreviewwarning": "<em>អ្នកមិនបានកត់ឈ្មោះចូល​ទេ។ ប្រសិនបើអ្នកធ្វើការរក្សាទុក នោះអាសយដ្ឋានIPរបស់អ្នក​នឹងត្រូវបាន​កត់ត្រាទុក​ក្នុងប្រវត្តិកែប្រែ​នៃទំព័រ​នេះ។</em>",
        "missingsummary": "'''រំលឹក៖''' អ្នកមិនទាន់បានផ្ដល់ចំណារពន្យល់អំពីកំណែប្រែនេះទេ។\n\nបើសិនជាអ្នកចុច '''រក្សាទុក''' ម្ដងទៀតនោះកំណែប្រែរបស់អ្នកនឹងត្រូវរក្សាទុកដោយគ្មានចំណារពន្យល់។",
        "missingcommenttext": "សូមវាយបញ្ចូលយោបល់មួយនៅខាងក្រោម។",
        "missingcommentheader": "'''រំលឹក៖''' អ្នកមិនទាន់បានផ្ដល់ឱ្យនូវ ប្រធានបទ/ចំណងជើង របស់មតិយោបល់នេះទេ។\nបើសិនជាអ្នកចុច \"{{int:savearticle}}\" ម្ដងទៀតនោះកំណែប្រែរបស់អ្នកនឹងត្រូវរក្សាទុកដោយគ្មានវា។",
        "edit-gone-missing": "មិនអាចបន្ទាន់សម័យទំព័រនេះទេ។\n\nទំព័រនេះហាក់ដូចជាត្រូវបានលុបចោលហើយ។",
        "edit-conflict": "កែប្រែ​ភាពឆ្គង​។",
        "edit-no-change": "ការកែប្រែរបស់អ្នកត្រូវបានមិនទុកជាការទេ ព្រោះគ្មានការផ្លាស់ប្ដូរណាមួយត្រូវបានធ្វើនៅលើអត្ថបទនេះទេ។",
+       "postedit-confirmation-created": "បានបង្កើតទំព័ររូចហើយ។",
+       "postedit-confirmation-restored": "បានស្ដារទំព័រឡើងវិញរួចហើយ។",
        "postedit-confirmation-saved": "កំណែប្រែរបស់អ្នកត្រូវបានរក្សាទុកហើយ។",
        "edit-already-exists": "មិនអាចបង្កើតទំព័រថ្មីមួយទេ។\n\nទំព័រនេះមានរួចហើយ។",
        "defaultmessagetext": "អត្ថបទសារតាមលំនាំដើម",
        "currentrev": "កំណែបច្ចុប្បន្ន",
        "currentrev-asof": "កំណែប្រែបច្ចុប្បន្ន $1",
        "revisionasof": "កំណែ​របស់ $1",
-       "revision-info": "á\9e\80á\9f\86á\9e\8eá\9f\82â\80\8bá\9e\9aá\9e\94á\9e\9fá\9f\8b $1 á\9e\8aá\9f\84á\9e\99 $2",
+       "revision-info": "á\9e\80á\9f\86á\9e\8eá\9f\82â\80\8bá\9e\93á\9f\85 $1 á\9e\8aá\9f\84á\9e\99 {{GENDER:$6|$2}}$7",
        "previousrevision": "← កំណែ​មុន",
        "nextrevision": "កំណែបន្ទាប់ →",
        "currentrevisionlink": "កំណែបច្ចុប្បន្ន",
        "shown-title": "បង្ហាញ $1 {{PLURAL:$1|លទ្ធផល|លទ្ធផល}}ក្នុងមួយទំព័រ",
        "viewprevnext": "មើល ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "* ទំព័រ '''[[$1]]'''",
-       "searchmenu-new": "'''បង្កើតទំព័រ \"[[:$1]]\" នៅ​លើ​វិគី​នេះ!'''",
+       "searchmenu-new": "<strong>បង្កើតទំព័រ \"[[:$1]]\" នៅ​លើ​វិគី​នេះ!</strong> {{PLURAL:$2|0=|សូមមើលផងដែរទំព័រដែលរកឃើញជាមួយពាក្យដែលអ្នកស្វែងរក។|សូមមើលផងដែរលទ្ធផលដែលរកឃើញ។}}",
        "searchprofile-articles": "ទំព័រ​មាតិកា",
        "searchprofile-images": "ពហុ​ព័ត៌មាន",
        "searchprofile-everything": "ទាំងអស់",
        "search-result-category-size": "{{PLURAL:$1|សមាជិកម្នាក់|សមាជិក$1នាក់}} ({{PLURAL:$2|កូនចំណាត់ថ្នាក់ក្រុម១|$2 កូនចំណាត់ថ្នាក់ក្រុម}}, {{PLURAL:$3|1 ឯកសារ|$3 ឯកសារ}})",
        "search-redirect": "(បញ្ជូនបន្ត $1)",
        "search-section": "(ផ្នែក $1)",
+       "search-category": "(ចំណាត់ថ្នាក់ក្រុម $1)",
+       "search-file-match": "(ខ្លឹមសារឯកសារត្រូវគ្នា)",
        "search-suggest": "ប្រហែលជាអ្នកចង់រក៖ $1",
        "search-interwiki-caption": "គម្រោងជាបងប្អូន",
        "search-interwiki-default": "លទ្ធផលពី$1៖",
        "preferences": "ចំណង់ចំណូលចិត្ត",
        "mypreferences": "ចំណង់ចំណូលចិត្ត​",
        "prefs-edits": "ចំនួនកំណែប្រែ៖",
+       "prefsnologintext2": "សូមកត់ឈ្មោះចូលដើម្បីផ្លាស់ប្ដូរចំណង់ចំណូលចិត្តរបស់អ្នក។",
        "prefs-skin": "សំបក",
        "skin-preview": "មើលជាមុន",
        "datedefault": "គ្មានចំណូលចិត្ត",
        "gender-female": "ស្រី",
        "prefs-help-gender": "ចំណង់ចំណូលចិត្តនេះកំណត់ក៏បានមិនកំណត់ក៏បាន៖ ប្រើសំរាប់អោយសូហ្វវែរហៅតាមភេទអោយបាមត្រឹមត្រូវ។ ព័ត៌មាននេះនឹងត្រូវបង្ហាញជាសាធារណៈ។",
        "email": "អ៊ីមែល",
-       "prefs-help-realname": "á\9e¢á\9f\92á\9e\93á\9e\80á\9e¢á\9e¶á\9e\85á\9e\95á\9f\92á\9e\8aá\9e\9bá\9f\8bá\9e\88á\9f\92á\9e\98á\9f\84á\9f\87á\9e\96á\9e·á\9e\8fá\9e\9aá\9e\94á\9e\9fá\9f\8bá\9e¢á\9f\92á\9e\93á\9e\80á\9e\80á\9f\8fá\9e\94á\9e¶á\9e\93á\9e\98á\9e·á\9e\93á\9e\95á\9f\92á\9e\8aá\9e\9bá\9f\8bá\9e\80á\9f\8fá\9e\94á\9e¶á\9e\93á\9f\94 á\9e\94á\9e¾á\9e¢á\9f\92á\9e\93á\9e\80á\9e\95á\9f\92á\9e\8aá\9e\9bá\9f\8bá\9e±á\9f\92á\9e\99 á\9e\9cá\9e¶á\9e\93á\9e¹á\9e\84á\9e\8fá\9f\92á\9e\9aá\9e¼á\9e\9cá\9e\94á\9e¶á\9e\93á\9e\94á\9f\92á\9e\9aá\9e¾á\9e\94á\9f\92á\9e\9aá\9e¶á\9e\9fá\9f\8bá\9f\8bá\9e\8aá\9e¾á\9e\98á\9f\92á\9e\94á\9e¸á\9e\94á\9e\89á\9f\92á\9e\87á\9e¶á\9e\80á\9f\8bá\9e\97á\9e¶á\9e\96á\9e\87á\9e¶á\9e\98á\9f\92á\9e\85á\9e¶á\9e\9fá\9f\8bâ\80\8bá\9e\9bá\9e¾á\9e\80á\9e¶á\9e\9aá\9e\9aá\9e½á\9e\98á\9e\85á\9f\86á\9e\8eá\9f\82á\9e\80â\80\8bá\9e\93á\9e¶á\9e\93á\9e​របស់អ្នក។",
+       "prefs-help-realname": "á\9e¢á\9f\92á\9e\93á\9e\80á\9e¢á\9e¶á\9e\85á\9e\95á\9f\92á\9e\8aá\9e\9bá\9f\8bá\9e\88á\9f\92á\9e\98á\9f\84á\9f\87á\9e\96á\9e·á\9e\8fá\9e\9aá\9e\94á\9e\9fá\9f\8bá\9e¢á\9f\92á\9e\93á\9e\80á\9e\80á\9f\8fá\9e\94á\9e¶á\9e\93á\9e\98á\9e·á\9e\93á\9e\95á\9f\92á\9e\8aá\9e\9bá\9f\8bá\9e\80á\9f\8fá\9e\94á\9e¶á\9e\93á\9f\94 á\9e\94á\9e¾á\9e¢á\9f\92á\9e\93á\9e\80á\9e\95á\9f\92á\9e\8aá\9e\9bá\9f\8bá\9e±á\9f\92á\9e\99 á\9e\9cá\9e¶á\9e\93á\9e¹á\9e\84á\9e\8fá\9f\92á\9e\9aá\9e¼á\9e\9cá\9e\94á\9e¶á\9e\93á\9e\94á\9f\92á\9e\9aá\9e¾á\9e\94á\9f\92á\9e\9aá\9e¶á\9e\9fá\9f\8bá\9f\8bá\9e\8aá\9e¾á\9e\98á\9f\92á\9e\94á\9e¸á\9e\94á\9e\89á\9f\92á\9e\87á\9e¶á\9e\80á\9f\8bá\9e\97á\9e¶á\9e\96á\9e\87á\9e¶á\9e\98á\9f\92á\9e\85á\9e¶á\9e\9fá\9f\8bâ\80\8bá\9e\9bá\9e¾á\9e\9fá\9f\92á\9e\93á\9e¶á\9e\8aá\9f\83​របស់អ្នក។",
        "prefs-help-email": "អ្នកអាចផ្ដល់អាសយដ្ឋានអ៊ីមែលរបស់អ្នកក៏បានមិនផ្ដល់ក៏បាន។ ប៉ុន្ដែអាសយដ្ឋានអ៊ីមែលដែលផ្ដល់អោយនឹងមានប្រយោជន៍ក្នុងការប្ដូរពាក្យសម្ងាត់ ពេលដែលអ្នកភ្លេចវា។",
        "prefs-help-email-others": "អ្នកក៏អាចជ្រើសរើស​ការផ្ដល់លទ្ឋភាព​​ឱ្យអ្នកដទៃទាក់ទងអ្នក​តាមរយៈ​​ទំព័រអ្នកប្រើប្រាស់​​ឬទំព័រពិភាក្សារបស់អ្នក​​ដោយមិនចាំបាច់ឱ្យគេដឹងពីអត្តសញ្ញាណរបស់អ្នកផងដែរ។",
        "prefs-help-email-required": "អាសយដ្ឋានអ៊ីមែលត្រូវការជាចាំបាច់។",
        "right-move": "ប្ដូរទីតាំងទំព័រ",
        "right-move-subpages": "ប្ដូរទីតាំងទំព័ររួមជាមួយទំព័ររងរបស់វា",
        "right-move-rootuserpages": "ប្ដូរទីតាំងឫសទំព័រអ្នកប្រើប្រាស់",
+       "right-move-categorypages": "ប្ដូរទីតាំងទំព័រចំណាត់ថ្នាក់ក្រុម",
        "right-movefile": "ប្ដូរទីតាំងឯកសារ",
        "right-suppressredirect": "មិនបង្កើតការបញ្ជូនបន្តពីទំព័រប្រភពនៅពេលប្ដូរទីតាំងទំព័រ",
        "right-upload": "ផ្ទុកឡើងឯកសារ",
        "action-move": "ប្ដូរទីតាំងទំព័រនេះ",
        "action-move-subpages": "ប្ដូរទីតាំងទំព័រនេះព្រមទាំងអនុទំព័ររបស់វា",
        "action-move-rootuserpages": "ប្ដូរទីតាំងឫសទំព័រអ្នកប្រើប្រាស់",
+       "action-move-categorypages": "ប្ដូរទីតាំងទំព័រចំណាត់ថ្នាក់ក្រុម",
        "action-movefile": "ប្ដូរទីតាំងឯកសារនេះ",
        "action-upload": "ផ្ទុកឡើងឯកសារនេះ",
        "action-reupload": "ផ្ទុកជាន់ពីលើឯកសារដែលមានស្រាប់ហើយនេះ",
        "license-nopreview": "(មិនទាន់មានការបង្ហាញការមើលជាមុនទេ)",
        "upload_source_url": "(URL ត្រឹមត្រូវនិងបើកចំហជាសាធារណៈ)",
        "upload_source_file": "(ឯកសារក្នុងកុំព្យូទ័ររបស់អ្នក)",
+       "listfiles-delete": "លុបចោល",
        "listfiles-summary": "ទំព័រពិសេស​នេះ​បង្ហាញ​គ្រប់​ឯកសារ​ដែល​បានផ្ទុកឡើង។",
        "listfiles_search_for": "ស្វែងរកឈ្មោះមេឌា៖",
        "imgfile": "ឯកសារ",
        "booksources": "ប្រភពសៀវភៅ",
        "booksources-search-legend": "ស្វែងរកប្រភពសៀវភៅ",
        "booksources-isbn": "លេខ​កូដ​សៀវ​ភៅ​ ISBN ៖",
+       "booksources-search": "ស្វែងរក",
        "booksources-text": "ខាងក្រោមនេះជាបញ្ជីនៃតំណភ្ជាប់ទៅវិបសាយនានាដែលលក់​សៀវភៅថ្មីនិងជជុះ ហើយអាចផ្ដល់ព័ត៌មានបន្ថែមផ្សេងទៀតអំពីសៀវភៅដែលអ្នកកំពុងស្វែងរក៖",
        "booksources-invalid-isbn": "លេខISBNដែលអ្នកផ្ដល់អោយហាក់ដូចជាមិនត្រឹមត្រូវទេ។ សូមពិនិត្យក្រែងលោមានកំហុសក្នុងការចម្លងចេញពីប្រភពដើម។",
        "specialloguserlabel": "អ្នកប្រព្រឹត្តិ៖",
        "listgrouprights-removegroup-self": "យក​ចេញ​{{PLURAL:$2|ក្រុម}}ពី​​គណនី​ផ្ទាល់ខ្លួន​៖ $1",
        "listgrouprights-addgroup-self-all": "បន្ថែម​ក្រុម​ទាំងអស់​ទៅ​គណនី​ផ្ទាល់ខ្លួន​",
        "listgrouprights-removegroup-self-all": "យក​ចេញ​​ក្រុម​ទាំងអស់​ពី​​គណនី​ផ្ទាល់ខ្លួន​",
+       "trackingcategories-name": "ឈ្មោះសារ",
        "mailnologin": "គ្មានអាសយដ្ឋានផ្ញើទេ",
        "mailnologintext": "អ្នកត្រូវតែ [[Special:UserLogin|កត់ឈ្មោះចូល]] និង មានអាសយដ្ឋានអ៊ីមែលមានសុពលភាពមួយ ក្នុង[[Special:Preferences|ចំណង់ចំណូលចិត្ត]]របស់អ្នក ដើម្បីមានសិទ្ធិផ្ញើអ៊ីមែលទៅអ្នកប្រើប្រាស់ដទៃទៀត។",
        "emailuser": "ផ្ញើអ៊ីមែល​ទៅកាន់​អ្នក​ប្រើប្រាស់នេះ",
        "mywatchlist": "បញ្ជីតាមដាន​",
        "watchlistfor2": "សម្រាប់ $1 $2",
        "nowatchlist": "គ្មានអ្វីនៅក្នុងបញ្ជីតាមដានរបស់អ្នកទេ។",
-       "watchlistanontext": "សូម $1 ដើម្បី​មើល​ឬ​កែប្រែ​របស់​ក្នុង​បញ្ជីតាមដាន​របស់អ្នក។",
+       "watchlistanontext": "សូមកត់ឈ្មោះចូលដើម្បី​មើល​ឬ​កែប្រែ​របស់​ក្នុង​បញ្ជីតាមដាន​របស់អ្នក។",
        "watchnologin": "មិនទាន់កត់ឈ្មោះចូលទេ",
        "addwatch": "បន្ថែមទៅបញ្ជីតាមដាន",
        "addedwatchtext": "ទំព័រ \"[[:$1]]\" ត្រូវបានដាក់បញ្ចូលទៅក្នុង​[[Special:Watchlist|បញ្ជីតាមដាន]]របស់លោកអ្នកហើយ ។ រាល់ការផ្លាស់ប្ដូរនៃទំព័រនេះ រួមទាំងទំព័រពិភាក្សារបស់វាផងដែរ នឹងត្រូវបានដាក់បញ្ចូលក្នុងបញ្ជីនៅទីនោះ។",
+       "addedwatchtext-short": "ទំព័រ \"$1\" ត្រូវបានបន្ថែមទៅក្នុងបញ្ជីតាមដានរបស់អ្នកហើយ។",
        "removewatch": "ដកចេញពីបញ្ជីតាមដាន",
-       "removedwatchtext": "ទំព័រ \"[[:$1]]\" ត្រូវបានដកចេញពី[[Special:Watchlist|បញ្ជីតាមដាន]]របស់លោកអ្នកហើយ ។",
+       "removedwatchtext": "ទំព័រ \"[[:$1]]\" ត្រូវបានដកចេញពី[[Special:Watchlist|បញ្ជីតាមដានរបស់លោកអ្នក]]ហើយ ។",
+       "removedwatchtext-short": "ទំព័រ \"$1\" ត្រូវបានដកចេញបញ្ជីតាមដានរបស់អ្នកហើយ។",
        "watch": "តាមដាន",
        "watchthispage": "តាមដានទំព័រនេះ",
        "unwatch": "ឈប់​តាមដាន",
        "wlheader-enotif": "បើកប្រើការផ្ដល់ដំណឹងតាមរយៈអ៊ីមែល។",
        "wlheader-showupdated": "ទំព័រដែលត្រូវបានផ្លាស់ប្តូរតាំងពីពេលចូលមើលចុងក្រោយរបស់អ្នក ត្រូវបានបង្ហាញជា '''អក្សរដិត'''។",
        "wlnote": "ខាងក្រោមនេះជា {{PLURAL:$1|បំលាស់ប្តូរចុងក្រោយ|'''$1'''បំលាស់ប្តូរចុងក្រោយ}}ក្នុងរយះពេល{{PLURAL:$2|'''$2'''ម៉ោង}}ចុងក្រោយ គិតចាប់ពី $3, $4។",
-       "wlshowlast": "បង្ហាញ $1ម៉ោងចុងក្រោយ $2ថ្ងៃចុងក្រោយ ឬ",
+       "wlshowlast": "បង្ហាញ $1ម៉ោងចុងក្រោយ $2ថ្ងៃចុងក្រោយ",
        "watchlist-options": "ជម្រើសនានាក្នុងបញ្ជីតាមដាន",
        "watching": "កំពុង​តាមដាន...",
        "unwatching": "ឈប់​តាមដាន...",
        "delete-edit-reasonlist": "ពិនិត្យផ្ទៀងផ្ទាត់ហេតុផលនៃការលុប",
        "delete-toobig": "ទំព័រនេះមានប្រវត្តិកែប្រែធំលើសពី $1 {{PLURAL:$1|កំណែ|កំណែ}}។\n\nការលុបទំព័របែបនេះចោលត្រូវបានហាមឃាត់ ដើម្បីបង្ការកុំអោយមានការរអាក់រអួលក្នុង{{SITENAME}}។",
        "delete-warning-toobig": "ទំព័រនេះមានប្រវត្តិកែប្រែធំលើសពី $1 {{PLURAL:$1|កំណែ|កំណែ}}។\n\nការលុបទំព័របែបនេះចោលអាចធ្វើអោយមានការរអាក់រអួលប្រតិបត្តិការរបស់មូលដ្ឋានទិន្នន័យក្នុង{{SITENAME}}។\n\nសូមបន្តសកម្មភាពនេះដោយប្រុងប្រយ័ត្ន។",
+       "deleteprotected": "អ្នកមិនអាចលុបចោលទំព័រនេះបានទេព្រោះវាត្រូវបានការពារហើយ។",
        "rollback": "មូលត្រឡប់កំណែប្រែ",
        "rollback_short": "មូលត្រឡប់",
        "rollbacklink": "មូលត្រឡប់",
        "protect-othertime": "រយៈពេលផុតកំណត់ផ្សេងទៀត៖",
        "protect-othertime-op": "រយៈពេលផុតកំណត់ផ្សេងទៀត",
        "protect-existing-expiry": "រយៈពេលផុតកំណត់មានស្រាប់៖ $3, $2",
+       "protect-existing-expiry-infinity": "រយៈពេលផុតកំណត់មានស្រាប់៖ ជារៀងរហូត",
        "protect-otherreason": "មូលហេតុបន្ថែមផ្សេងៗទៀត៖",
        "protect-otherreason-op": "មូលហេតុផ្សេងទៀត",
        "protect-dropdown": "*មូលហេតុការពារជាទូទៅ\n** ទទួលការបំផ្លិចបំផ្លាញយ៉ាងសំបើមក្រៃលែង\n** ស្ព៊ែមយ៉ាងសំបើមក្រៃលែង\n** សង្រ្គាមនៃការកែប្រែដែលនាំឲខូចប្រយោជន៍\n** ទំព័រដែលមានចរាចរកម្រិតខ្ពស់",
        "contributions-title": "ការរួមចំណែករបស់អ្នកប្រើប្រាស់ $1",
        "mycontris": "ការរួមចំណែក",
        "contribsub2": "សម្រាប់{{GENDER:$3|$1}} ($2)",
+       "contributions-userdoesnotexist": "គណនីអ្នកប្រើប្រាស់ដែលមានឈ្មោះ \"$1\"មិនទាន់បានចុះឈ្មោះទេ។",
        "nocontribs": "គ្មានការផ្លាស់ប្តូរត្រូវបានឃើញដូចនឹងលក្ខណៈវិនិច្ឆ័យទាំងនេះ។",
        "uctop": "(បច្ចុប្បន្ន)",
        "month": "ខែ៖",
        "autoblockid": "ដាក់ការហាមឃាត់ជាស្វ័យប្រវត្តិលើ #$1",
        "block": "ដាក់ការហាមឃាត់លើអ្នកប្រើប្រាស់",
        "unblock": "ដកការហាមឃាត់លើអ្នកប្រើប្រាស់",
-       "blockip": "á\9e\8aá\9e¶á\9e\80á\9f\8bá\9e\80á\9e¶á\9e\9aá\9e á\9e¶á\9e\98á\9e\83á\9e¶á\9e\8fá\9f\8bá\9e\9bá\9e¾á\9e¢á\9f\92á\9e\93á\9e\80á\9e\94á\9f\92á\9e\9aá\9e¾á\9e\94á\9f\92á\9e\9aá\9e¶á\9e\9fá\9f\8b",
+       "blockip": "á\9e á\9e¶á\9e\98á\9e\83á\9e¶á\9e\8fá\9f\8b{{GENDER:$1|á\9e¢á\9f\92á\9e\93á\9e\80á\9e\94á\9f\92á\9e\9aá\9e¾á\9e\94á\9f\92á\9e\9aá\9e¶á\9e\9fá\9f\8b}}",
        "blockip-legend": "ដាក់ការហាមឃាត់លើអ្នកប្រើប្រាស់",
        "blockiptext": "សូម​ប្រើប្រាស់​សំណុំ​បែបបទ​ខាងក្រោម​ដើម្បី​ហាមឃាត់ការសរសេរ​ពី​អាសយដ្ឋាន IP ឬ​ឈ្មោះ​អ្នកប្រើប្រាស់ណាមួយ​។\nការ​ធ្វើ​បែបនេះ​គួរតែ​ធ្វើឡើង​ក្នុង​គោលបំណង​បង្ការ​ការប៉ុនប៉ង​បំផ្លាញ(vandalism)ដូច​ដែល​មាន​ចែង​ក្នុង[[{{MediaWiki:Policy-url}}|គោលការណ៍]]។\nសូមបំពេញមូលហេតុច្បាស់លាស់មួយខាងក្រោម (ឧទាហរណ៍៖ រាយឈ្មោះទំព័រនានាដែលត្រូវបានគេបំផ្លាញ)។",
        "ipaddressorusername": "អាសយដ្ឋានIP ឬអត្តនាម៖",
        "ipb-unblock-addr": "ដកការហាមឃាត់លើ $1",
        "ipb-unblock": "ដកការហាមឃាត់លើអ្នកប្រើប្រាស់ ឬ អាសយដ្ឋាន IP",
        "ipb-blocklist": "មើលការហាមឃាត់ដែលមានហើយ",
-       "ipb-blocklist-contribs": "ការរួមចំណែកសម្រាប់ $1",
+       "ipb-blocklist-contribs": "ការរួមចំណែកសម្រាប់ {{GENDER:$1|$1}}",
        "unblockip": "ដកការហាមឃាត់លើអ្នកប្រើប្រាស់",
        "unblockiptext": "សូម​ប្រើប្រាស់​ទម្រង់​បែបបទ​ខាងក្រោម​នេះ ដើម្បី​បើក​សិទ្ឋិ​សរសេរ​ឡើងវិញ សម្រាប់​អាសយដ្ឋាន​IP​ឬ​អ្នកប្រើប្រាស់​ដែល​ត្រូវ​បាន​ហាមឃាត់ពីមុន​។",
        "ipusubmit": "ដក​ការហាមឃាត់នេះ​ចេញ",
        "thumbnail_image-missing": "ឯកសារហាក់ដូចជាកំពុងបាត់ខ្លួន៖$1",
        "import": "ការនាំចូលទំព័រ",
        "importinterwiki": "ការនាំចូលអន្តរវិគី",
+       "import-interwiki-sourcewiki": "វិគីប្រភព៖",
+       "import-interwiki-sourcepage": "ទំព័រប្រភព៖",
        "import-interwiki-history": "ចម្លង គ្រប់កំណែចាស់ នៃទំព័រនេះ",
        "import-interwiki-templates": "រាប់​បញ្ចូល​ទំព័រគំរូ​ទាំងអស់​",
        "import-interwiki-submit": "នាំចូល",
        "tooltip-feed-atom": "បម្រែបម្រួល Atom ចំពោះទំព័រនេះ",
        "tooltip-t-contributions": "បង្ហាញបញ្ជីរួមចំណែករបស់អ្នកប្រើប្រាស់នេះ",
        "tooltip-t-emailuser": "ផ្ញើអ៊ីមែលទៅកាន់អ្នកប្រើប្រាស់នេះ",
+       "tooltip-t-info": "ព័ត៌មានបន្ថែមអំពីទំព័រនេះ",
        "tooltip-t-upload": "ឯកសារផ្ទុកឡើង",
        "tooltip-t-specialpages": "បញ្ជីទំព័រពិសេសៗទាំងមូល",
        "tooltip-t-print": "ទម្រង់សម្រាប់បោះពុម្ភរបស់ទំព័រនេះ",
        "autosumm-replace": "ជំនួសខ្លឹមសារនៃទំព័រដោយ '$1'",
        "autoredircomment": "បញ្ជូនបន្តទៅ [[$1]]",
        "autosumm-new": "បានបង្កើតទំព័រដែលផ្ដើមដោយ $1",
+       "autosumm-newblank": "បានបង្កើតទំព័រទទេ",
        "size-bytes": "$1បៃ",
        "size-kilobytes": "$1គីឡូបៃ",
        "size-megabytes": "$1មេកាបៃ",
        "watchlistedit-raw-done": "បញ្ជីតាមដានរបស់អ្នកត្រូវបានធ្វើឱ្យទាន់សម័យហើយ។",
        "watchlistedit-raw-added": "{{PLURAL:$1| ចំណងជើង១បានត្រូវ|$1 ចំណងជើងបានត្រូវ}}ដាក់បន្ថែម៖",
        "watchlistedit-raw-removed": "{{PLURAL:$1|១ចំណងជើងបានត្រូវ|$1ចំណងជើងបានត្រូវ}}ដកចេញ៖",
+       "watchlistedit-clear-title": "បានសម្អាតបញ្ចីតាមដាន",
+       "watchlistedit-clear-legend": "សម្អាតបញ្ជីតាមដាន",
        "watchlisttools-view": "មើលបន្លាស់ប្ដូរពាក់ព័ន្ធ",
        "watchlisttools-edit": "មើលនិងកែប្រែបញ្ជីតាមដាន",
        "watchlisttools-raw": "កែប្រែបញ្ជីតាមដានឆៅ",
        "version-hook-name": "ឈ្មោះ​ Hook",
        "version-hook-subscribedby": "បានជាវ ជាប្រចាំ ដោយ",
        "version-version": "(កំណែ $1)",
+       "version-no-ext-name": "[គ្មានឈ្មោះ]",
        "version-license": "អាជ្ញាបណ្ណ​មេឌាវិគី",
        "version-ext-license": "អាជ្ញាបណ្ណ",
+       "version-skin-colheader-name": "សំបក",
        "version-ext-colheader-version": "កំណែ",
        "version-ext-colheader-license": "អាជ្ញាបណ្ណ",
        "version-ext-colheader-description": "ការពិពណ៌នា",
        "fileduplicatesearch-result-n": "មាន {{PLURAL:$2|1 ឯកសារដូចគ្នាបេះបិទ|$2 ឯកសារដូចគ្នាបេះបិទ}}ទៅនឹងឯកសារ \"$1\"។",
        "fileduplicatesearch-noresults": "រកមិនឃើញឯកសារដែលមានឈ្មោះ \"$1\" ទេ។",
        "specialpages": "ទំព័រ​ពិសេស​ៗ",
+       "specialpages-note-top": "កំណត់សម្គាល់",
        "specialpages-note": "* ទំព័រពិសេសៗធម្មតា។\n* <span class=\"mw-specialpagerestricted\">ទំព័រពិសេសៗដែលមានការដាក់កំហិត។</span>",
        "specialpages-group-maintenance": "របាយការណ៍នានាអំពីតំហែទាំ",
        "specialpages-group-other": "ទំព័រពិសេសៗផ្សេងៗទៀត",
        "htmlform-no": "ទេ",
        "htmlform-yes": "ព្រម",
        "htmlform-chosen-placeholder": "ជ្រើសយកជម្រើសមួយ",
+       "htmlform-cloner-create": "បន្ថែមទៀត",
+       "htmlform-cloner-delete": "ដកចេញ",
        "logentry-delete-delete": "$1 {{GENDER:$2|បានលុប}} ទំព័រ $3 ចោល",
        "logentry-delete-restore": "$1 {{GENDER:$2|បានស្ដារ}} ទំព័រ $3 ឡើងវិញ",
        "revdelete-content-hid": "ខ្លឹមសារត្រូវបានលាក់",
index 2485ed8..60eeb0b 100644 (file)
        "parser-template-loop-warning": "재귀적인 틀이 발견되었습니다: [[$1]]",
        "parser-template-recursion-depth-warning": "틀 반복 깊이 제한을 초과함 ($1)",
        "language-converter-depth-warning": "언어 변환기 실행 제한 초과($1)",
-       "node-count-exceeded-category": "문ì\84\9cê°\80 ë\85¸ë\93\9c í\9a\9fì\88\98를 ì´\88ê³¼í\95\98ì\98\80ì\8aµë\8b\88ë\8b¤.",
+       "node-count-exceeded-category": "ë\85¸ë\93\9c í\9a\9fì\88\98를 ì´\88ê³¼í\95\9c ë¬¸ì\84\9c",
        "node-count-exceeded-category-desc": "문서가 최대 노드 수를 초과합니다.",
        "node-count-exceeded-warning": "문서가 노드 수를 초과하였습니다.",
-       "expansion-depth-exceeded-category": "문서가 확장 깊이를 초과하였습니다.",
+       "expansion-depth-exceeded-category": "확장 깊이를 초과한 문서",
        "expansion-depth-exceeded-category-desc": "최대 확장 깊이를 초과하는 문서입니다.",
        "expansion-depth-exceeded-warning": "문서가 확장 깊이를 초과하였습니다",
        "parser-unstrip-loop-warning": "Unstrip의 반복을 감지했습니다",
index 5c82cef..b1a590b 100644 (file)
        "search-result-category-size": "{{جمی:$1|1 اندوم|$1 اندومیا}} ({{جمی:$2|1 زیردسه|$2 زیردسه یا}}, {{جمی:$3|1 جانیا|$3 جانیایا}}",
        "search-redirect": "(ورگشتن $1)",
        "search-section": "(بشق $1)",
+       "search-category": "(دسه $1)",
        "search-file-match": "(یکی کردن مینونه جانیا)",
        "search-suggest": "منظورت يه بی:$1",
        "search-interwiki-caption": "پروجه یا خوئر",
        "exif-cameraownername": "مالک دیربین",
        "exif-label": "ری دیار",
        "exif-nickname": "نوم نامرتوط سی عسگ",
+       "exif-rating": "امتیاز(د 5)",
        "exif-copyrighted": "حال و بال کپی رایت",
        "exif-copyrightowner": "مالک کپی رایت",
        "exif-usageterms": "کلیمه یا وه کار گرتن",
        "exif-pngfilecomment": "ویر و باور فایل جی پی ان جی",
+       "exif-disclaimer": "کذو نومه",
        "exif-giffilecomment": "ویر و باور فایل جی آی اف",
+       "exif-intellectualgenre": "نوع مورد",
        "exif-contact-value": "$1\n\n$2\n<div class=\"adr\">\n$3\n\n$4, $5, $6 $7\n</div>\n$8",
        "exif-unknowndate": "گات نادیار",
        "exif-orientation-1": "عادی",
        "exif-meteringmode-3": "نقطه جا",
        "exif-meteringmode-4": "چن جاگه ای",
        "exif-meteringmode-5": "الگو",
+       "exif-meteringmode-6": "جزئی",
        "exif-meteringmode-255": "هنی",
        "exif-lightsource-0": "نادیار",
        "exif-lightsource-1": "روشنایی روز",
        "exif-gpsdestdistance-k": "کلومتر",
        "exif-gpsdestdistance-m": "مایل",
        "exif-gpsdop-good": "خو ($1)",
+       "exif-gpsdop-fair": "د ری انصاف ($1)",
        "exif-dc-contributor": "هومیارا",
        "exif-dc-publisher": "درتیجن",
        "exif-dc-relation": "وارسگر مرتوط",
        "version-variables": "آلشت ونا",
        "version-antispam": "نهاگرتن هرزنومه",
        "version-other": "هنی",
+       "version-hook-name": "نوم قلاو",
        "version-no-ext-name": "[بی نوم]",
        "version-ext-license": "ليسانس",
        "version-skin-colheader-name": "پوسه",
        "version-ext-colheader-license": "ليسانس",
        "version-ext-colheader-description": "شرح",
        "version-ext-colheader-credits": "نیسنه یا",
+       "version-license-title": "لیسانس سی $1",
        "version-poweredby-others": "دیه رون",
        "version-software-product": "نتجه",
        "version-software-version": "نسقه",
        "htmlform-selectorother-other": "هنی",
        "htmlform-no": "نه",
        "htmlform-yes": "هری",
+       "htmlform-chosen-placeholder": "یه گل گزینه انتخاو بکیت",
        "htmlform-cloner-create": "هنی اضاف بکیت",
        "htmlform-cloner-delete": "ؤرداشتن",
        "revdelete-content-hid": "مینونه قام بیه",
index 8a0f942..589613f 100644 (file)
        "wlheader-enotif": "*准報信。",
        "wlheader-showupdated": "*易者'''粗體'''。",
        "wlnote": "近<b>$2</b>時有$1者易。",
-       "wlshowlast": "見近$1時、$2天、時易",
+       "wlshowlast": "見近$1時、$2天易",
        "watchlist-options": "哨項",
        "watching": "出陣…",
        "unwatching": "收兵…",
index 294d821..a931417 100644 (file)
        "api-error-stashfailed": "Внатрешна грешка: Опслужувачот не успеа да ја складира привремената податотека.",
        "api-error-publishfailed": "Внатрешна грешка: Опслужувачот не успеа да ја објави привремената податотека.",
        "api-error-stasherror": "Се јави грешка при подигањето на податотеката во складот.",
+       "api-error-stashedfilenotfound": "Не ја најдов наплстената податотека при обидот да ја подигнам од пластот.",
+       "api-error-stashpathinvalid": "Патеката кајшто требаше да се најде складираната податотека е неважечка.",
+       "api-error-stashfilestorage": "Се јави грешка при ставањето на податотеката во складот.",
+       "api-error-stashzerolength": "Опслужувачот не можеше да ја складира податотеката бидејќи има нулта должина.",
+       "api-error-stashnotloggedin": "Мора да се најавени за да зачувувате податотеки во складот на подигнати.",
+       "api-error-stashwrongowner": "Податотеката во складот до која сакате да дојдете не ви припаѓа вам.",
+       "api-error-stashnosuchfilekey": "Клучот на податотеката во складот до кој сакате да дојдете не постои.",
        "api-error-timeout": "Опслужувачот не одговори во очекуваното време.",
        "api-error-unclassified": "Се појави непозната грешка.",
        "api-error-unknown-code": "Непозната грешка: „$1“",
index 2bd618b..f3cea21 100644 (file)
        "search-result-category-size": "{{PLURAL:$1|ഒരു അംഗം|$1 അംഗങ്ങൾ}} ({{PLURAL:$2|ഒരു ഉപവർഗ്ഗം|$2 ഉപവർഗ്ഗങ്ങൾ}}, {{PLURAL:$3|ഒരു പ്രമാണം|$3 പ്രമാണങ്ങൾ}})",
        "search-redirect": "(തിരിച്ചുവിടൽ താൾ $1)",
        "search-section": "(വിഭാഗം $1)",
+       "search-category": "(വർഗ്ഗം $1)",
        "search-file-match": "(പ്രമാണ ഉള്ളടക്കവുമായി ഒത്തുപോകുന്നുണ്ട്)",
        "search-suggest": "താങ്കൾ ഉദ്ദേശിച്ചത് $1 എന്നാണോ",
        "search-interwiki-caption": "സഹോദര സംരംഭങ്ങൾ",
        "import-error-interwiki": "ബാഹ്യ കണ്ണിചേർക്കലിനു (അന്തർവിക്കി) കരുതിവെച്ചിരിക്കുന്ന പേര് ആയതിനാൽ, \"$1\" എന്ന താൾ ഇറക്കുമതി ചെയ്തില്ല.",
        "import-error-special": "താളുകൾ അനുവദിക്കാത്ത പ്രത്യേക നാമമേഖലയിൽ പെടുന്നതായതിനാൽ \"$1\" എന്ന താൾ ഇറക്കുമതി ചെയ്തില്ല.",
        "import-error-invalid": "ഇറക്കുമതി ചെയ്യപ്പെട്ടാൽ പേര് ഈ വിക്കിയിൽ അസാധുവാകുമെന്നതിനാൽ \"$1\" എന്ന താൾ ഇറക്കുമതി ചെയ്തില്ല.",
+       "import-error-bad-location": "ഉള്ളടക്ക മാതൃക $3 ഉപയോഗിക്കുന്ന $2 നാൾപ്പതിപ്പ്, ഈ വിക്കിയിലെ \"$1\" എന്ന താളിൽ ആ മാതൃക പിന്തുണയ്ക്കുന്നില്ലാത്തതിനാൽ ഉപയോഗിക്കാനാവില്ല.",
        "import-options-wrong": "തെറ്റായ {{PLURAL:$2|ഐച്ഛികം|ഐച്ഛികങ്ങൾ}}: <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "നൽകിയ മൂലതാൾ അസാധുവാണ്.",
        "import-rootpage-nosubpage": "മൂലതാളിന്റെ നാമമേഖലയായ \"$1\" ഉപതാളുകൾ അനുവദിക്കുന്നില്ല.",
index b338f83..d5ea8f7 100644 (file)
        "otherlanguages": "Kî-thaⁿ ê gí-giân",
        "redirectedfrom": "(Tùi $1 choán--lâi)",
        "redirectpagesub": "Choán-ia̍h",
+       "redirectto": "跳去:",
        "lastmodifiedat": "Chit ia̍h tī $1,  $2 ū kái--koè",
        "viewcount": "Chit ia̍h kàu taⁿ, hō͘ lâng khoàⁿ $1 pái.",
        "protectedpage": "Siū pó-hō͘ ê ia̍h",
        "hidetoc": "siu",
        "collapsible-collapse": "Siu",
        "collapsible-expand": "Khui",
+       "confirmable-confirm": "{{GENDER:$1|你}}敢確定唅?",
+       "confirmable-yes": "著啦",
+       "confirmable-no": "無啦!毋是!",
        "thisisdeleted": "Khoàⁿ a̍h-sī kiù $1?",
        "viewdeleted": "Beh khoàⁿ $1?",
        "restorelink": "{{PLURAL:$1|chi̍t ê thâi-tiàu ê pian-chi̍p|$1 thâi-tiàu ê pian-chi̍p}}",
        "viewyourtext": "你會使看<strong>你改的</strong>原始碼,並且khop去這頁:",
        "protectedinterface": "Chit ia̍h thê-kiong nńg-thé kài-bīn ēng ê bûn-jī. Ūi beh ī-hông lâng chau-that, só͘-í ū siū tio̍h pó-hō͘. Nā beh kái hoan-e̍k, chhiaⁿ khì Ûi-ki Mûi-thé chāi-tē-hoà sū-kang [//translatewiki.net/ translatewiki.net] hiâ.",
        "editinginterface": "'''Sè-jī:''' Lí tng teh siu-kái 1 bīn thê-kiong nńg-thé kài-bīn bûn-jī ê ia̍h. Jīn-hô kái-piàn to ē éng-hióng tio̍h kî-thaⁿ iōng-chiá ê sú-iōng kài-bīn. Nā beh kái hoan-e̍k, chhiaⁿ khì Ûi-ki Mûi-thé chāi-tē-hoà sū-kang [//translatewiki.net/ translatewiki.net] hiâ.",
+       "translateinterface": "欲改抑是加維基的翻譯,請去維基媒體的在地化專案:[//translatewiki.net/ 翻譯維基網]。",
        "cascadeprotected": "Chit-ê ia̍h í-keng hông pó-hō͘ bē kái tit. In-ūi i tī ē-bīn {{PLURAL:$1|ê|ê}} liân-só pó-hō͘ lāi-té:\n$2",
        "namespaceprotected": "Lí bô khoân-lī kái '''$1'''  miâ-khong-kan ê ia̍h",
        "customcssprotected": "你無受權去改這个 CSS頁,因為這个頁有包括別个用者的個人設定。",
        "ns-specialprotected": "特殊頁袂得改。",
        "titleprotected": "這个標題已經予[[User:$1|$1]]保護起來,袂得提來用。\n原因是 \"<em>$2</em>。",
        "filereadonlyerror": "因為檔案庫這馬只會使看,所以袂得改 \"$1\"這个檔案。\n鎖檔案庫的管理員講是因為:\"$3\"。",
+       "invalidtitle-knownnamespace": "佇名空間 \"$2\"佮文字\"$3\"的標題袂使得。",
+       "invalidtitle-unknownnamespace": "名空間編號 $1(毋知名)的\"$2\"文字標題袂使用。",
        "exception-nologin": "Bô teng-ji̍p",
-       "exception-nologin-text": "請先[[Special:Userlogin|登入]]了才有法度看這頁抑對這頁做動作。",
+       "exception-nologin-text": "請先登入,才有法度看這頁抑對這頁做動作。",
        "exception-nologin-text-manual": "請先$1,才有法度看這頁抑對這頁做動作。",
        "virus-badscanner": "設定毋著:你的病毒掃描程式阮毋知:<em>$1</em>",
        "virus-scanfailed": "掃病毒無成功(代碼$1)",
        "createaccountmail": "Iōng chi̍t-ê lîm-sî loān-sò͘ sán-seng ê bi̍t-bé , kià khì goá chí-tēng ê tiān-chú-phoe tē-chí.",
        "createacct-realname": "真正的名",
        "createaccountreason": "Lí-iû:",
+       "createacct-reason": "理由:",
        "createacct-reason-ph": "為啥物你欲開一另外一个口座?",
        "createacct-captcha": "安全檢驗",
        "createacct-imgcaptcha-ph": "共下跤你看著的字拍入來",
        "createaccounterror": "Bô hoat-tō͘ khui kháu-chō: $1",
        "nocookiesnew": "你的用者口座已經開好矣,毋過你猶未登入,{{SITENAME}}有用Cookies做記錄登入的用者,你無允准用Cookies,請先共阻擋提掉,才閣用你的用者名稱佮密碼登入。",
        "nocookieslogin": "{{SITENAME}}有用cookies做記錄用者,毋過你無允準用cookies,等你改做會當了後,才閣試。",
+       "nocookiesfornew": "因為不明的原因,阮無法度建立用者的口座。\n請先確定你的cookie會使用,閣重進入這頁,閣試一擺。",
+       "noname": "你提供的用者名稱袂使用。",
        "loginsuccesstitle": "Teng-ji̍p sêng-kong",
        "loginsuccess": "Lí hiān-chhú-sî í-keng teng-ji̍p {{SITENAME}} chò \"$1\".",
        "nosuchuser": "Chia bô iōng-chiá hō-chò \"$1\".\nIiōng-chiá hō-chò ū hun toā-jī sè-jī.\nChhiáⁿ kiám-cha lí ê phèng-im, a̍h-sī  [[Special:UserLogin/signup|khui sin iōng-chiá ê kháu-chō.]]",
        "nosuchusershort": "Bô \"$1\" chit ê iōng-chiá miâ.\nTùi khoàⁿ-māi,  lí phah--ê.",
        "nouserspecified": "Lí ài chí-tēng chi̍t ê iōng-chiá miâ.",
+       "login-userblocked": "這個用者已經予人封鎖,袂使登入。",
        "wrongpassword": "Lí su-ji̍p ê bi̍t-bé ū têng-tâⁿ. Chhiáⁿ têng chhì.",
        "wrongpasswordempty": "Bi̍t-bé keh-á khang-khang. Chhiáⁿ têng chhì.",
+       "passwordtooshort": "密碼上少愛{{PLURAL:$1|1字|$1字}}",
+       "password-name-match": "你的密碼愛佮你的用者名稱無仝。",
+       "password-login-forbidden": "這个用者名稱佮密碼已經禁止用。",
        "mailmypassword": "Têng siat bi̍t-bé",
        "passwordremindertitle": "{{SITENAME}} the-chheN li e bit-be",
        "noemail": "Kì-lo̍k bô iōng-chiá \"$1\" ê e-mail chū-chí.",
index 0b7c627..411d86f 100644 (file)
        "unexpected": "Valore imprevisto: \"$1\"=\"$2\".",
        "formerror": "Sbàglio: nun se può mannà 'o modulo",
        "badarticleerror": "Chest'azione nun se può fà int'a sta paggena.",
-       "cannotdelete": "Nun è possibbele scassà 'a paggena o 'a fiura $1 addamannata. Putria éssere stato già scancellato.",
+       "cannotdelete": "Nun è pussibbele scassà 'a paggena o 'a fiura $1 addamannata. Putria éssere stato già scancellato.",
        "cannotdelete-title": "Nun se può scancellà 'a paggena \"$1\"",
        "delete-hook-aborted": "'O scancellamiento è stato annullato 'a 'o «hook».\nNun dette spiegazione nisciuna.",
        "no-null-revision": "Nun se può crià na nnova revisione nulla p' 'a paggena \"$1\"",
        "revdelete-hide-user": "Nomme o indirizzo IP 'e ll'autore",
        "revdelete-hide-restricted": "Annascunne 'e nfurmaziune 'nnecate pure a l'ammenistrature",
        "revdelete-radio-same": "(nun cagnà)",
-       "revdelete-radio-set": "Nasconde",
+       "revdelete-radio-set": "Annascunnuto",
        "revdelete-radio-unset": "Faje vedé",
        "revdelete-suppress": "Annascunne 'e nfurmaziune pure a l'ammenistrature",
        "revdelete-unsuppress": "Scancella 'e limmete ncopp' 'e verziune arripigliate",
        "protect-expiring-local": "ammatura 'o $1",
        "protect-expiry-indefinite": "indefinite",
        "protect-cascade": "Prutegge paggene appennute dint'a sta paggena (spanne 'a prutezione a tutt' 'e paggene appennute ccà).",
-       "protect-cantedit": "Nun è possibbele cagnà 'e livelle e prutezione 'e sta paggena, pecchè nun tenite deritto o permesse pe' ne fà 'o cagnamiento.",
+       "protect-cantedit": "Nun è pussibbele cagnà 'e livelle e prutezione 'e sta paggena, pecchè nun tenite deritto o permesse pe' ne fà 'o cagnamiento.",
        "protect-othertime": "N'ata durata:",
        "protect-othertime-op": "ati durate",
        "protect-existing-expiry": "'O tiempo d'ammaturamiento esistente: $3, $2",
        "version-poweredby-others": "ati",
        "version-poweredby-translators": "tradutture 'e translatewiki.net",
        "version-credits-summary": "Nuje vulessemo tené a mmente 'e perzune ccà abbascio pe' purtà rispetto a 'e cuntribbute 'e lloro ncopp'a [[Special:Version|MediaWiki]].",
-       "version-license-info": "MediaWiki è nu software libbero; vuje 'o putite redestribbuì e/o cagnà sott' 'e termine d' 'a licienza GNU GPL ('a Licienza Pubbreca Generale) comme pubbrecata d' 'a Free Software Foundation; o pure 'a verziona 2 d' 'a Licienza, o pure (comme vulite vuje) 'a n'ata verziona cchiù nnova.\n\nMediaWiki è destribbuita c' 'a speranza d'essere utile, ma SENZA NISCIUNA GARANZIA; senza manco 'a garanzia p' 'a CUMMERCIABBELETÀ O IDONIETÀ PE' NU SCOPO PARTICOLARE. Iate a vedé 'a GNU GPL pe' n'avé cchiù nfurmaziune.\n\nAvísseve 'a ricevere [{{SERVER}}{{SCRIPTPATH}}/COPYING na copia d' 'a Licienza GNU GPL] cu stu prugramma; si nò, scrivete â Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA o [//www.gnu.org/licenses/old-licenses/gpl-2.0.html liggete sta paggena ncopp' 'a l'Internet].",
+       "version-license-info": "MediaWiki è nu software libbero; vuje 'o putite redestribbuì e/o cagnà sott' 'e termine d' 'a licienza GNU GPL ('a Licienza Pubbreca Generale) comme pubbrecata d' 'a Free Software Foundation; o pure 'a verziona 2 d' 'a Licienza, o pure (comme vulite vuje) 'a n'ata verziona cchiù nnova.\n\nMediaWiki è destribbuita c' 'a speranza d'essere utile, ma SENZA NISCIUNA GARANZIA; senza manco 'a garanzia p' 'a CUMMERCIABBELETÀ O IDONIETÀ PE' NU SCOPO PARTICOLARE. Iate a vedé 'a GNU GPL pe' n'avé cchiù nfurmaziune.\n\nAvísseve 'a ricevere [{{SERVER}}{{SCRIPTPATH}}/COPYING na copia d' 'a Licienza GNU GPL] cu stu prugramma; si nò, scrivete â Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA o [//www.gnu.org/licenses/old-licenses/gpl-2.0.html liggite sta paggena ncopp' 'a l'Internet].",
        "version-software": "Software installato",
        "version-software-product": "Prodotto",
        "version-software-version": "Verziona",
        "api-error-stashfailed": "Errore interno: 'O server nun ngarraje a s'astipà 'o file temporaneo.",
        "api-error-publishfailed": "Errore interno: 'O server nun ngarraje a pubbrecà 'o file temporaneo.",
        "api-error-stasherror": "'A carreca d' 'o file 'n stash è asciuta male, ce sta n'errore.",
+       "api-error-stashedfilenotfound": "'O file 'n stash nun è stato truvato pe' tramente ca se faceva 'a prova 'e carreca d' 'o stash.",
+       "api-error-stashpathinvalid": "'O cullegamento a 'o pizzo addò avesse stà 'o file 'e stash nun è bbuono.",
+       "api-error-stashfilestorage": "L'astipamento d' 'o file 'n stash è asciuto male, ce sta n'errore.",
+       "api-error-stashzerolength": "'O server nun può nzertà 'o file dint'a 'o stash, pecché è luongo zero zero.",
+       "api-error-stashnotloggedin": "Avisseve 'a trasì pe' ne putè astipà 'e file din' 'o stash 'e càrreca.",
+       "api-error-stashwrongowner": "'O file addò stavate a trasì dint' 'o stash nun v'appartene.",
+       "api-error-stashnosuchfilekey": "'A chiave d' 'o file addò stavate a trasì dint' 'o stash nun esiste.",
        "api-error-timeout": "'O server nun rispunnette dint'a 'o tiempo stabbelito.",
        "api-error-unclassified": "È capitato n'errore scanusciuto.",
        "api-error-unknown-code": "Errore scanusciuto: \"$1\"",
        "mediastatistics-summary": "Statistiche ncopp' 'e tipe d' 'e file carrecate. Ce truvate azzeccata sulamente 'a verziona cchiù recente d' 'o file. Verziune viecchie o scancellate se so' luvate.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte}} ($2; $3%)",
        "mediastatistics-table-mimetype": "Tipo 'e MIME",
-       "mediastatistics-table-extensions": "Estenziune possibbele",
+       "mediastatistics-table-extensions": "Estenziune pussibbele",
        "mediastatistics-table-count": "Nummero 'e file",
        "mediastatistics-table-totalbytes": "Dimenziona cumbinata",
        "mediastatistics-header-unknown": "Scanusciuto",
index b45a628..220b395 100644 (file)
        "powersearch-remember": "Zapamiętaj wybór dla kolejnych wyszukiwań",
        "search-external": "Wyszukiwanie zewnętrzne",
        "searchdisabled": "Wyszukiwanie w {{GRAMMAR:MS.lp|{{SITENAME}}}} zostało wyłączone.\nW międzyczasie możesz skorzystać z wyszukiwania Google.\nJednak informacje o treści {{GRAMMAR:D.lp|{{SITENAME}}}} mogą być w Google nieaktualne.",
-       "search-error": "Wystąpił błąd podczas wyszukiwania:$1",
+       "search-error": "Wystąpił błąd podczas wyszukiwania: $1",
        "preferences": "Preferencje",
        "mypreferences": "Preferencje",
        "prefs-edits": "Liczba edycji:",
index f64e9bf..0ef1b48 100644 (file)
        "api-error-stashfailed": "Eror antern: ël servent a l'ha pa podù memorisé l'archivi a temp.",
        "api-error-publishfailed": "Eror antern: Ël servent a l'ha pa podù publiché l'archivi provisòri.",
        "api-error-stasherror": "A-i é staje n'eror durant ël cariament dl'archivi da stërmé.",
+       "api-error-stashedfilenotfound": "L'archivi stërmà a l'é nen trovasse durant ël tentativ ëd carielo da sò strem.",
+       "api-error-stashpathinvalid": "Ël përcors anté ch'a l'avrìa dovù trovesse l'archivi stërmà a l'era nen bon.",
+       "api-error-stashfilestorage": "A-ié staje n'eror an rangiand l'archivi an sò strem.",
+       "api-error-stashzerolength": "Ël servent a l'ha nen podù stërmé l'archivi, përchè a l'era ëd taja nula.",
+       "api-error-stashnotloggedin": "A dev esse rintrà ant ël sistema për argistré dj'archivi ant lë strem ëd cariament.",
+       "api-error-stashwrongowner": "L'archivi al qual a sërcava d'acede ant lë strem a l'é nen sò.",
+       "api-error-stashnosuchfilekey": "La ciav dl'archivi al qual a sërcava d'acede ant lë strem a esist pa.",
        "api-error-timeout": "Ël servent a l'ha pa rëspondù ant ël temp ëspetà.",
        "api-error-unclassified": "A l'é capitaje n'eror nen conossù.",
        "api-error-unknown-code": "Eror sconossù: «$1».",
        "mediastatistics-table-count": "Nùmer d'archivi",
        "mediastatistics-table-totalbytes": "Taja combinà",
        "mediastatistics-header-unknown": "Nen conossù",
-       "mediastatistics-header-bitmap": "Plance bitmap"
+       "mediastatistics-header-bitmap": "Plance bitmap",
+       "mediastatistics-header-drawing": "Dissegn (plance vetoriaj)",
+       "mediastatistics-header-audio": "Sonor",
+       "mediastatistics-header-video": "Filmà",
+       "mediastatistics-header-multimedia": "Mojen rich",
+       "mediastatistics-header-office": "Ufissi",
+       "mediastatistics-header-text": "Testual",
+       "mediastatistics-header-executable": "Eseguìbij",
+       "mediastatistics-header-archive": "Formà compress",
+       "json-warn-trailing-comma": "$1 {{PLURAL:$1|coma final a l'é stàita|còme finaj a son ëstàite}} gavà dal JSON",
+       "json-error-unknown": "A-i é staje un problema con ël JSON. Eror: $1"
 }
index d02a4dd..b9d586c 100644 (file)
        "action-userrights": "{{Doc-action|userrights}}\n\nThis action allows editing of all of the \"user rights\", not just the rights of the group \"all users\".",
        "action-userrights-interwiki": "{{Doc-action|userrights-interwiki}}",
        "action-siteadmin": "{{Doc-action|siteadmin}}",
-       "action-sendemail": "{{doc-action|sendemail}}",
+       "action-sendemail": "{{doc-action|sendemail}}\n{{Identical|E-mail}}",
        "action-editmywatchlist": "{{doc-action|editmywatchlist}}\n{{Identical|Edit your watchlist}}",
        "action-viewmywatchlist": "{{doc-action|viewmywatchlist}}\n{{Identical|View your watchlist}}",
        "action-viewmyprivateinfo": "{{doc-action|viewmyprivateinfo}}",
        "pubmedurl": "{{notranslate}}\nParameters:\n* $1 - Pubmed number\nSee also:\n* {{msg-mw|Rfcurl}}",
        "specialloguserlabel": "Used in [[Special:Log]] as a label for an input field with which the log can be filtered for entries describing actions ''performed'' by the specified user.  \"Carried out\" and \"done\" are possible alternatives for \"performed\".",
        "speciallogtitlelabel": "Used in [[Special:Log]] as a label for an input field with which the log can be filtered.  This filter selects for pages or users on which a log action was performed.",
-       "log": "{{doc-special|Log}}",
+       "log": "{{doc-special|Log}}\n{{Identical|Log}}",
        "all-logs-page": "{{doc-logpage}}\nTitle of [[Special:Log]].",
        "alllogstext": "Header of [[Special:Log]]",
        "logempty": "Used as warning when there are no items to show.",
        "show-big-image-preview": "Message shown under the image description page thumbnail.\n\nCan be followed by {{msg-mw|Show-big-image-other}}.\n\nParameters:\n* $1 - a link which points to the thumbnail. Its text is {{msg-mw|Show-big-image-size}}",
        "show-big-image-other": "Message shown under the image description page thumbnail.\n\nPreceded by {{msg-mw|Show-big-image-preview}}, if the image is in high resolution.\n\nParameters:\n* $1 - list of resolutions (pipe-separated)\n* $2 - number of resolutions",
        "show-big-image-size": "Parameters:\n* $1 - the width of the image(s) in pixels\n* $2 - the height of the image(s) in pixels",
-       "file-info-gif-looped": "Part of the information provided about a [[w:Gif|.gif file]] on its file description page. Looped means repeating in the context of an animated gif. It is a sequence of images, each displayed after the other, and the first one displayed after the last, in a never ending loop. For example of message in use see [[:File:Mouse10.gif]].",
-       "file-info-gif-frames": "Part of the information provided about a [[w:Gif|.gif file]] on its file description page.\n\nParameters:\n* $1 - number of frames",
-       "file-info-png-looped": "Part of the information provided about a [[w:APNG|.apng file]] on its file description page. Looped means repeating indefinetly in the context of an animated png. It is a sequence of images, each displayed after the other, and the first one displayed after the last, in a never ending loop.",
+       "file-info-gif-looped": "Part of the information provided about a [[w:Gif|.gif file]] on its file description page. Looped means repeating in the context of an animated gif. It is a sequence of images, each displayed after the other, and the first one displayed after the last, in a never ending loop. For example of message in use see [[:File:Mouse10.gif]].\n{{Identical|Looped}}",
+       "file-info-gif-frames": "Part of the information provided about a [[w:Gif|.gif file]] on its file description page.\n\nParameters:\n* $1 - number of frames\n{{Identical|Frame}}",
+       "file-info-png-looped": "Part of the information provided about a [[w:APNG|.apng file]] on its file description page. Looped means repeating indefinetly in the context of an animated png. It is a sequence of images, each displayed after the other, and the first one displayed after the last, in a never ending loop.\n{{Identical|Looped}}",
        "file-info-png-repeat": "Part of the information provided about a [[w:APNG|.apng file]] on its file description page. The sequence of images is repeating a limited amount of time. It is a sequence of images, each displayed after the other, and the first one displayed after the last, for $1 times.",
-       "file-info-png-frames": "Part of the information provided about a [[w:APNG|.apng file]] on its file description page.\n\nThe variable $1 is the number of individual frames in an animated gif file.\n\nFor example of message in use see [[:File:Mouse10.gif]].",
+       "file-info-png-frames": "Part of the information provided about a [[w:APNG|.apng file]] on its file description page.\n\nThe variable $1 is the number of individual frames in an animated gif file.\n\nFor example of message in use see [[:File:Mouse10.gif]].\n{{Identical|Frame}}",
        "file-no-thumb-animation": "We cannot animate thumbnails of this file.\n\nThis notice is shown on the image description page on animated svg files just below {{msg-mw|File-info-size}}.\n\nThis message may be overridden by a more specific message:\n* {{msg-mw|File-no-thumb-animation-gif}}.",
        "file-no-thumb-animation-gif": "Cannot animate thumbnails of this gif file, because it has too big a resolution. The cut off resolution can vary between wikis ([[mw:manual:$wgMaxAnimatedGifArea|$wgMaxAnimatedGifArea]]). Note that resolution is calculated as width times height times number of frames. See {{msg-mw|file-no-thumb-animation}}.",
        "newimages": "Page title of [[Special:NewImages]].",
index d25367d..c723ae6 100644 (file)
        "delete-edit-reasonlist": "Едітовати причіны вымазаня",
        "delete-toobig": "Тота сторінка має велику історію едітованя, через $1 {{PLURAL:$1|верзії|верзій|верзій}}. Мазаня такых сторінок є обмеджено, жебы ся заборонило нехоченому нарушіню {{grammar:2sg|{{SITENAME}}}}.",
        "delete-warning-toobig": "Тота сторінка має велику історію едітацій, через $1 {{PLURAL:$1|верзії|верзій|верзій}}. Мазаня такых сторінок може нарушыти датабазовы операцім {{grammar:2sg|{{SITENAME}}}}; мерькуйте.",
+       "deleting-backlinks-warning": "'''Позірь:'''  Сторінка, котру ся рыхтуєте вымазати, є одказована на [[Special:WhatLinksHere/{{FULLPAGENAME}}|іншых сторінках]] ці вложена до них.",
        "rollback": "Вернути назад едітованя",
        "rollback_short": "Вернути назад",
        "rollbacklink": "вернути назад",
index 95dbe93..6e39183 100644 (file)
        "api-error-stashfailed": "Notranja napaka: strežnik ni uspel shraniti začasne datoteke.",
        "api-error-publishfailed": "Notranja napaka: strežnik ni uspel objaviti začasne datoteke.",
        "api-error-stasherror": "Pri nalaganju datoteke v hrambo je prišlo do napake.",
+       "api-error-stashedfilenotfound": "Datoteke iz skrite shrambe med poskusom nalaganja iz skrite shrambe nismo našli.",
+       "api-error-stashpathinvalid": "Pot, na kateri naj bi bila datoteka iz skrite shrambe, ni veljavna.",
+       "api-error-stashfilestorage": "Pri shranjevanju datoteke v skrito shrambo je prišlo do napake.",
+       "api-error-stashzerolength": "Strežnik ni mogel dati datoteke v skrito shrambo, ker ima ničelno dolžino.",
+       "api-error-stashnotloggedin": "Da lahko shranite datoteke v skrito shrambo nalaganja, morate biti prijavljeni.",
+       "api-error-stashwrongowner": "Datoteka, do katere ste poskušali dostopati v skriti shrambi, ne pripada vam.",
+       "api-error-stashnosuchfilekey": "Datoteka, do katere ste poskušali dostopati v skriti shrambi, ne obstaja.",
        "api-error-timeout": "Strežnik se ni odzval v pričakovanem času.",
        "api-error-unclassified": "Prišlo je do neznane napake",
        "api-error-unknown-code": "Neznana napaka: »$1«",
index bc7a915..bf93633 100644 (file)
        "parser-unstrip-loop-warning": "Утврђена је петља",
        "parser-unstrip-recursion-limit": "Прекорачено је ограничење рекурзије ($1)",
        "converter-manual-rule-error": "Пронађена је грешка у правилу за ручно претварање језика",
-       "undo-success": "Измена се може вратити.\nПроверите разлике испод па сачувајте измене.",
+       "undo-success": "Измена се може вратити.\nПроверите разлике испод, па сачувајте измене.",
        "undo-failure": "Ова измена се не може поништити због сукоба измена.",
        "undo-norev": "Не могу да вратим измену јер не постоји или је обрисана.",
        "undo-nochange": "Изгледа да је измена већ поништена.",
        "movepage-moved": "'''„$1“ је премештена на „$2“'''",
        "movepage-moved-redirect": "Преусмерење је направљено.",
        "movepage-moved-noredirect": "Стварање преусмерења је онемогућено.",
-       "articleexists": "Страница с тим именом већ постоји или је име неисправно.\nИзаберите друго име.",
+       "articleexists": "Страница с тим именом већ постоји, или је име неисправно.\nИзаберите друго име.",
        "cantmove-titleprotected": "Не можете да преместите страницу на то место јер је жељени наслов заштићен од стварања",
        "movetalk": "Премести и страницу за разговор",
        "move-subpages": "Премести и подстранице (до $1)",
index c656998..fe87203 100644 (file)
        "parser-unstrip-loop-warning": "Utvrđena je petlja",
        "parser-unstrip-recursion-limit": "Prekoračeno je ograničenje rekurzije ($1)",
        "converter-manual-rule-error": "Pronađena je greška u pravilu za ručno pretvaranje jezika",
-       "undo-success": "Izmena se može vratiti.\nProverite razlike ispod pa sačuvajte izmene.",
+       "undo-success": "Izmena se može vratiti.\nProverite razlike ispod, pa sačuvajte izmene.",
        "undo-failure": "Ova izmena se ne može poništiti zbog sukoba izmena.",
        "undo-norev": "Ne mogu da vratim izmenu jer ne postoji ili je obrisana.",
        "undo-nochange": "Izgleda da je izmena već poništena.",
        "movepage-moved": "'''„$1“ je premeštena na „$2“'''",
        "movepage-moved-redirect": "Preusmerenje je napravljeno.",
        "movepage-moved-noredirect": "Stvaranje preusmerenja je onemogućeno.",
-       "articleexists": "Stranica s tim imenom već postoji ili je ime neispravno.\nIzaberite drugo ime.",
+       "articleexists": "Stranica s tim imenom već postoji, ili je ime neispravno.\nIzaberite drugo ime.",
        "cantmove-titleprotected": "Ne možete da premestite stranicu na to mesto jer je željeni naslov zaštićen od stvaranja",
        "movetalk": "Premesti i stranicu za razgovor",
        "move-subpages": "Premesti i podstranice (do $1)",
index bb86cf9..012b957 100644 (file)
        "viewyourtext": "Anjeun bisa némbongkeun sarta nyalin sumber '''éditan anjeun''' ka ieu kaca:",
        "protectedinterface": "Kaca ieu eusina teks antarmuka pikeun dipaké ku pakakas beyé sarta geus dikunci pikeun ngahindar ti kasalahan.",
        "editinginterface": "'''Awas:''' Anjeun keur ngédit kaca nu dipaké pikeun nyadiakeun téks antarmuka pikeun sopwérna.\nParobahan kana ieu kaca bakal mangaruhan pidangan antarmuka pikeun pamaké séjén.\nPikeun alihbasa, mangga sumping ka [//translatewiki.net/wiki/Main_Page?setlang=en translatewiki.net], proyék lokalisasi MediaWiki.",
+       "translateinterface": "Pikeun nambahkeun atawa ngarobah tarjamah keur sakabéh wiki, paké [//translatewiki.net/ translatewiki.net], proyék lokalisasi MediaWiki.",
        "cascadeprotected": "Kaca ieu geus dikonci ti éditan alatan disartakeun di {{PLURAL:$1|kaca|kaca-kaca}} katut anu geus dikonci kalawan pilihan \"runtun\": $2",
        "namespaceprotected": "Anjeun teu ngabogaan hak pikeun ngédit kaca di ngaranspasi '''$1'''.",
        "customcssprotected": "Anjeun teu teu diwenangkeun pikeun ngédit ieu kaca CSS, sabab ngandung setélan pribadi kontributor séjén.",
        "resetpass-temp-password": "Sandi samentara:",
        "resetpass-expired": "Kecap sandi anjeun geus kadaluwarsa. Mangga jieun anu anyar pikeun asup log.",
        "passwordreset": "Setél ulang sandi",
+       "passwordreset-text-one": "Eusian formulir ieu pikeun ngirimkeun kecap sandi saheulaanan kana surélék.",
        "passwordreset-legend": "Setél ulang sandi",
        "passwordreset-disabled": "Dina ieu wiki, sandi teu bisa disetél ulang.",
+       "passwordreset-emaildisabled": "Fitur surélék ditumpurkeun di ieu wiki.",
        "passwordreset-username": "Sandiasma:",
        "passwordreset-domain": "Domain:",
        "passwordreset-capture": "Témbongkeun surat-é hasilna?",
        "filehist-comment": "Kamandang",
        "imagelinks": "Pamakéan berkas",
        "linkstoimage": "Kaca ieu  {{PLURAL:$1|numbu|$1 numbu}} ka gambar ieu :",
-       "nolinkstoimage": "Teu aya kaca nu numbu ka gambar ieu.",
+       "nolinkstoimage": "Teu aya kaca anu nutumbu ka ieu berkas.",
        "sharedupload": "Ieu koropak téh ti $1 nu bisa jadi dipaké ku proyék-proyék lianna.",
        "sharedupload-desc-here": "Ieu berkas asalna ti $1 anu bisa jadi dipaké ku proyék séjén. \nPedaran ti [$2 kaca pedaranana] dipidangkeun di handap.",
        "uploadnewversion-linktext": "ngamuatkeun vérsi anyar koropak ieu",
        "sp-contributions-username": "Alamat IP atawa sandiasma:",
        "sp-contributions-toponly": "Témbongkeun éditan anu révisi panungtung wungkul",
        "sp-contributions-submit": "Paluruh",
-       "whatlinkshere": "Nu numbu ka dieu",
-       "whatlinkshere-title": "Kaca-kaca nu numbu ka \"$1\"",
+       "whatlinkshere": "Anu nutumbu ka dieu",
+       "whatlinkshere-title": "Kaca anu nutumbu ka \"$1\"",
        "whatlinkshere-page": "Kaca:",
        "linkshere": "Kaca di handap ieu numbu ka '''[[:$1]]''':",
-       "nolinkshere": "Euweuh kaca nu numbu ka '''[[:$1]]'''.",
-       "nolinkshere-ns": "Euweuh kaca nu numbu ka '''[[:$1]]''' dina namespace nu dipilih.",
+       "nolinkshere": "Euweuh kaca anu nutumbu ka <strong>[[:$1]]</strong>.",
+       "nolinkshere-ns": "Euweuh kaca anu nutumbu ka <strong>[[:$1]]</strong> dina ruang-nama anu dipilih.",
        "isredirect": "Kaca alihan",
        "istemplate": "ku citakan",
        "isimage": "tutumbu berkas",
        "tooltip-n-recentchanges": "Béréndélan nu anyar robah dina wiki",
        "tooltip-n-randompage": "Muatkeun kaca naon baé",
        "tooltip-n-help": "Tempat pikeun néangan pitulung",
-       "tooltip-t-whatlinkshere": "Daptar kaca-kaca wiki nu numbu ka dieu",
-       "tooltip-t-recentchangeslinked": "Nu anyar robah na kaca-kaca nu numbu ka dieu",
+       "tooltip-t-whatlinkshere": "Béréndélan sakabéh kaca wiki anu nutumbu ka dieu",
+       "tooltip-t-recentchangeslinked": "Anu anyar robah dina kaca-kaca anu nutumbu ti dieu",
        "tooltip-feed-rss": "Asupan RSS pikeun kaca ieu",
        "tooltip-feed-atom": "Asupan atom pikeun kaca ieu",
        "tooltip-t-contributions": "Témbongkeun béréndélan kontribusi ti ieu kontributor",
index a855fc6..6b0bac8 100644 (file)
        "search-result-category-size": "{{PLURAL:$1|1 medlem|$1 medlemmar}} ({{PLURAL:$2|1 underkategori|$2 underkategorier}}, {{PLURAL:$3|1 fil|$3 filer}})",
        "search-redirect": "(omdirigering $1)",
        "search-section": "(avsnitt $1)",
+       "search-category": "(kategorin $1)",
        "search-file-match": "(överensstämmer filens innehåll)",
        "search-suggest": "Menade du: $1",
        "search-interwiki-caption": "Systerprojekt",
        "ipboptions": "2 timmar:2 hours,1 dygn:1 day,3 dygn:3 days,1 vecka:1 week,2 veckor:2 weeks,1 månad:1 month,3 månader:3 months,6 månader:6 months,1 år:1 year,oändlig:infinite",
        "ipbhidename": "Dölj användarnamnet från redigeringar och listor",
        "ipbwatchuser": "Bevaka användarens användarsida och diskussionssida",
-       "ipb-disableusertalk": "Hindra användaren från att redigera sina egna diskussionssida under blockeringen",
+       "ipb-disableusertalk": "Hindra användaren från att redigera sin egen diskussionssida under blockeringen",
        "ipb-change-block": "Återblockera användaren med de här inställningarna",
        "ipb-confirm": "Bekräfta blockering",
        "badipaddress": "Du har inte skrivit IP-adressen korrekt.",
index 36257a8..4d3384b 100644 (file)
        "content-model-text": "звичайний текст",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
-       "duplicate-args-category": "СÑ\82оÑ\80Ñ\96нки, Ñ\89о Ð²ÐºÐ»Ñ\8eÑ\87аÑ\8eÑ\82Ñ\8c Ñ\88аблони Ð· Ð´Ñ\83блÑ\96каÑ\82ним Ð²Ð¸Ð·Ð½Ð°Ñ\87еннÑ\8fм Ð°Ñ\80гÑ\83менÑ\82Ñ\83",
+       "duplicate-args-category": "СÑ\82оÑ\80Ñ\96нки, Ñ\89о Ð¼Ñ\96Ñ\81Ñ\82Ñ\8fÑ\82Ñ\8c Ñ\88аблон Ñ\96з ÐºÑ\96лÑ\8cкома Ð·Ð½Ð°Ñ\87еннÑ\8fми Ð¾Ð´Ð½Ð¾Ð³Ð¾ Ð¹ Ñ\82ого Ð¶ Ð¿Ð°Ñ\80амеÑ\82Ñ\80а",
        "duplicate-args-category-desc": "Тут перелічено сторінки, що містять дублікатне визначення аргументу при включенні шаблону, приміром, <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> або <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "'''Увага:''' Ця сторінка містить дуже багато викликів ресурсомістких функцій.\n\nКількість викликів не повинна перевищувати $2, зараз потрібно зробити $1 {{PLURAL:$1|виклик|виклики|викликів}}.",
        "expensive-parserfunction-category": "Сторінки з дуже великою кількістю викликів ресурсомістких функцій",
        "nolicense": "Відсутнє",
        "licenses-edit": "Редагувати параметри ліцензії",
        "license-nopreview": "(Попередній перегляд недоступний)",
-       "upload_source_url": " (вірна, публічно доступна інтернет-адреса)",
+       "upload_source_url": "(ви вибрали правильну, публічно доступну інтернет-адресу)",
        "upload_source_file": " (файл на вашому комп'ютері)",
        "listfiles-delete": "видалити",
        "listfiles-summary": "Ця спеціальна сторінка показує всі завантажені файли.",
index 1068a66..b83706e 100644 (file)
        "api-error-stashfailed": "Lỗi nội bộ: Máy chủ bị thất bại trong việc lưu giữ tập tin tạm.",
        "api-error-publishfailed": "Lỗi nội bộ: Máy chủ bị thất bại trong việc xuất bản tập tin tạm.",
        "api-error-stasherror": "Đã xuất hiện lỗi khi tải tập tin lên hàng đợi.",
+       "api-error-stashedfilenotfound": "Không tìm thấy tập tin khi thử tải nó lên từ hàng đợi.",
+       "api-error-stashpathinvalid": "Đường dẫn mong đợi đến tập tin đợi tải lên là không hợp lệ.",
+       "api-error-stashfilestorage": "Đã xuất hiện lỗi khi tải tập tin lên từ hàng đợi.",
+       "api-error-stashzerolength": "Máy chủ không thể lưu tập tin vào hàng đợi vì nó không có nội dung.",
+       "api-error-stashnotloggedin": "Bạn phải đăng nhập để lưu tập tin vào hàng đợi tải lên.",
+       "api-error-stashwrongowner": "Không thể truy cập một tập tin không phải của bạn trong hàng đợi tải lên.",
+       "api-error-stashnosuchfilekey": "Bạn không thể truy cập chìa khóa tập tin đợi tải lên vì chìa khóa này không tồn tại.",
        "api-error-timeout": "Máy chủ không đáp ứng trong thời gian dự kiến.",
        "api-error-unclassified": "Gặp lỗi không ngờ",
        "api-error-unknown-code": "Lỗi không rõ: “$1”",
index 5179e74..b0c2824 100644 (file)
@@ -11,7 +11,8 @@
                        "ווארצגאנג",
                        "לערי ריינהארט",
                        "פוילישער",
-                       "아라"
+                       "아라",
+                       "Har-wradim"
                ]
        },
        "tog-underline": "שטרייכט אונטער לינקען",
        "go": "גיין",
        "searcharticle": "גיין",
        "history": "בלאט היסטאריע",
-       "history_short": "היסטאריע",
+       "history_short": "היסטאָריע",
        "updatedmarker": "דערהיינטיגט זינט מיין לעצטע וויזיט",
        "printableversion": "דרוק ווערסיע",
        "permalink": "שטענדיגער לינק",
        "subject": "טעמע/קעפל:",
        "minoredit": "דאס איז א מינערדיגע ענדערונג",
        "watchthis": "טוט אױפֿפּאַסן דעם בלאט",
-       "savearticle": "×\90ױפֿהיטן בלאַט",
+       "savearticle": "×\90×\95×\99פהיטן בלאַט",
        "preview": "פֿאראויסקוק",
-       "showpreview": "×\95×\95ײַ×\96×\9f ×¤Ö¿אָרױסקוק",
+       "showpreview": "×\95×\95×\99×\99×\96×\9f ×¤אָרױסקוק",
        "showdiff": "ווײַז די ענדערונגען",
        "blankarticle": "<strong>אזהרה:</strong> דער בלאט איר גייט שאפן איז ליידיק.\nטאמער איר וועט דריקן אויף \"{{int:savearticle}}\" נאכאמאל, וועט דער בלאט ווערן געשאפן אן קיין אינהאלט.",
        "anoneditwarning": "<strong>ווארענונג:</strong> איר זענט נישט אריינלאגירט. אייער איי פי אדרעס וועט ווערן עפנטלעך זעבאר ווען איר פירט דורך  ענדערונגען . אז איר <strong>[$1 לאגירט ארײַן]</strong> אדער <strong>[$2 שאפט א קאנטע]</strong>, וועלן אײַערע רעדאקטירונגען ווערן צוגעשריבן צו אײַער באניצער-נאמען, ווי אויך אנדערע טובות.",
        "userinvalidcssjstitle": "'''ווארענונג:''' סאיז נישטא קיין סקין \"$1\". גדענקט אז קאסטעם .css און .js בלעטער נוצען לאוער קעיס טיטול, e.g. {{ns:user}}:Foo/vector.css ווי אנדערשט צו {{ns:user}}:Foo/Vector.css.",
        "updated": "(דערהיינטיגט)",
        "note": "'''באמערקונג:'''",
-       "previewnote": "'''געדענקט אז דאס איז נאָר אין אַ פֿאָרויסיקע ווייזונג.'''\nאייערע ענדערונגען זענען נאָך נישט געהיט!",
+       "previewnote": "'''געדענקט אַז דאָס איז נאָר אַ פאָרויסקוק.'''\nאייערע ענדערונגען זענען נאָך נישט געהיט!",
        "continue-editing": "אריבער צום רעדאקטירן פֿעלד",
        "previewconflict": "די פֿאראויסיגע ווייזונג רעפלעקטירט דעם טעקסט און דער אויבערשטע טעקסט ענדערונג אָפטיילונג וויאזוי דאס וועט אויסזען אויב וועט איר דאס אָפהיטן.",
        "session_fail_preview": "'''אנטשולדיגט! מען האט נישט געקענט פראצעסירן אייער ענדערונג צוליב א פארלוסט פון סעסיע דאטע. ביטע פרובירט נאכאמאל. אויב ס'ארבעט נאך אלס ניט, פרובירט [[Special:UserLogout|ארויסלאגירן]] און זיך צוריק אריינלאגירן.",
        "longpageerror": "'''פעלער: דער טעקסט וואס איר האט ארײַנגעשטעלט איז לאנג {{PLURAL:$1|איין קילאבייט|$1 קילאבייטן}}, וואס איז לענגער פון דעם מאקסימום פון {{PLURAL:$2|איין קילאבייט|$2 קילאבייטן}}.\nער קען נישט ווערן אפגעהיטן.'''",
        "readonlywarning": "'''ווארענונג: די דאטנבאזע איז געווארן פארשלאסן פאר אויפהאלטונג, ממילא וועט איר נישט קענען אפהיטן אייערע ענדערונגען אצינד. '''\nאיר קענט קאפירן און ארײַנלייגן דעם טעקסט אריין צו א טעקסט טעקע און דאס דארטן אפהיטן אויף שפעטער.\n\nדער אדמיניסטראטאר וואס האט זי פארשלאסן האט מסביר געווען אזוי: $1",
        "protectedpagewarning": "'''ווארענונג:  דער בלאט איז געווארן פארשפארט אז בלויז באניצערס מיט סיסאפ פריווילעגיעס קענען אים ענדערן.'''\nדי פארגאנגענע לאגבוך באשרײַבונג ווערט געוויזן דא:",
-       "semiprotectedpagewarning": "'''באמערקונג:''' דער דאזיגער בלאַט איז פֿאַרשפאַרט אז בלויז איינגעשריבענע באניצערס קענען אים ענדערן.\nדי פֿאַרגאַנגענע לאגבוך באשרײַבונג ווערט געוויזן דאָ:",
+       "semiprotectedpagewarning": "'''באמערקונג:''' דער דאָזיקער בלאַט איז פאַרשפּאַרט, אַזוי אַז בלויז איינגעשריבענע באַניצער קענען אים ענדערן.\nדאָס פאַרגאַנגענע לאָגבוך באַשרייבונג ווערט געוויזן דאָ:",
        "cascadeprotectedwarning": "'''ווארענונג:''' דער בלאט איז פארשפארט אז בלויז סיסאפן קענען אים ענדערן, וויבאלד ער איז איינגעשלאסן אין {{PLURAL:$1| דעם פאלגנדן בלאט, וואס איז|די פאלגנדע בלעטער, וואס זענען}} קאסקאד באשיצט:",
        "titleprotectedwarning": "'''אזהרה: דער בלאט איז פֿארשפאַרט טא דארף מען [[Special:ListGroupRights|ספעציפֿישע רעכטן]] צו שאפֿן אים.'''\nדי פֿאַרגאַנגענע לאגבוך באשרײַבונג ווערט געוויזן דאָ:",
        "templatesused": "{{PLURAL:$1|מוסטער|מוסטערן}} באנוצט אויף דעם בלאט:",
        "search-result-category-size": "{{PLURAL:$1|1 מיטגליד|$1 מיטגלידער}} ({{PLURAL:$2|1 אונטער־קאַטעגאריע|$2 אונטער־קאַטעגאריעס}}, {{PLURAL:$3|1 טעקע|$3 טעקעס}})",
        "search-redirect": "(ווײַטערפֿירן $1)",
        "search-section": "(אפטיילונג $1)",
+       "search-category": "(קאטעגאריע $1)",
        "search-file-match": "(פאסט צו טעקע אינהאלט)",
        "search-suggest": "צי האט איר געמיינט: $1",
        "search-interwiki-caption": "שוועסטער פראיעקטן",
        "searchall": "אלץ",
        "showingresults": "ווייזן ביז {{PLURAL:$1|רעזולטאט '''איינס'''|'''$1''' רעזולטאטן}} אנגעפאנגן פון נומער #'''$2''':",
        "showingresultsinrange": "ווײַזן אונטן ביז {{PLURAL:$1|<strong>1</strong> רעזולטאט|<strong>$1</strong> רעזולטאטן}} אין גרייך #<strong>$2</strong> ביז #<strong>$3</strong>.",
+       "search-showingresults": "{{PLURAL:$4|רעזולטאַט <strong>$1</strong> פֿון <strong>$3</strong>|רעזולטאַטן\n<strong>$1 - $2</strong> פֿון <strong>$3</strong>}}",
        "search-nonefound": "נישטא קיין רעזולטאטן פֿאַר דער שאלה.",
        "powersearch-legend": "ווײַטהאלטן זוכן",
        "powersearch-ns": "זוכן אין נאמענטיילן:",
        "prefs-tokenwatchlist": "טאקן",
        "prefs-diffs": "צווישנשיידן",
        "prefs-help-prefershttps": "דער פרעפערענץ וועט ארבעטן ביי אײַער נעקסטער ארײַנלאגירונג.",
+       "prefswarning-warning": "איר האט געמאכט ענדערונגען צו אײַערע פרעפערענצן וואס זענען נאך נישט אויפגעהיטן.\nאז איר פארלאזט דעם בלאט אן קליקן ״$1״ וועלן אײַערע פרעפערענצן נישט ווערן דערהײַנטיקט.",
        "prefs-tabs-navigation-hint": "טיפ: איר קענט ניצן די רעכטס און לינקס פייל־קלאווישן צו נאוויגירן צווישן די צינגלעך אין דער צינגלעך־ליסטע.",
        "email-address-validity-valid": "ע-פּאָסט אַדרעס זעט אויס גילטיק",
        "email-address-validity-invalid": "לייגט אַרײַן א גילטיקן ע־פאסט אַדרעס",
        "newpages": "נייע בלעטער",
        "newpages-username": "באַניצער נאָמען:",
        "ancientpages": "עלטסטע בלעטער",
-       "move": "×\91×\90Ö·×°עגן",
+       "move": "×\91×\90Ö·×\95×\95עגן",
        "movethispage": "באוועג דעם בלאט",
        "unusedimagestext": "די פֿאלגנדע טעקעס עקזיסטירן אבער ווערן נישט גענוצט אין קיין שום בלאַט.\nגיט אַכט אז אנדערע וועבערטער קענען פֿארבינדן צו א טעקע מיט א דירעקטן URL, און קענען דעריבער באווײַזן זיך דאָ כאטש זיי זענען אין אקטיוון באניץ.",
        "unusedcategoriestext": "די פֿאלגנדע קאטעגאריעס עקסיסטירן, אבער קיין בלאט אדער קאטעגאריע ניצט זיי נישט.",
        "pager-older-n": "{{PLURAL:$1|עלטערע|$1 עלטערע}}",
        "suppress": "אויפֿזען",
        "querypage-disabled": "דער באַזונדער־בלאַט איז אומאַקטיווירט צוליב אויספֿירונג סיבות.",
+       "apihelp": "API־הילף",
+       "apihelp-no-such-module": "מאָדול \"$1\" נישט געפונען.",
        "booksources": "דרויסנדיגע ליטעראַטור ISBN",
        "booksources-search-legend": "זוכן פאר דרויסנדע ביכער מקורות",
        "booksources-search": "זוכן",
        "removedwatchtext-short": "מ'האט אראפגענומען בלאט \"$1\" פון אײַער אויפפאסן ליסטע.",
        "watch": "אױפֿפּאַסן",
        "watchthispage": "טוט אױפֿפּאַסן דעם בלאט",
-       "unwatch": "אויפֿהערן אויפֿפּאַסן",
+       "unwatch": "אויפהערן אויפפּאַסן",
        "unwatchthispage": "ענדיגן אויפֿפאַסן",
        "notanarticle": "דאס איז נישט קיין אינהאלט בלאט",
        "notvisiblerev": "די באארבעטונג איז געווארן אויסגעמעקט",
        "protect-othertime": "אנדער צייט:",
        "protect-othertime-op": "אנדער צײַט",
        "protect-existing-expiry": "עקזיסטירנדע אויסלאז צײַט: $3, $2",
+       "protect-existing-expiry-infinity": "עקזיסטירנדיקע אויסגיין צייט: אומענדלעך",
        "protect-otherreason": "אנדער/ווײַטערדיקע סיבה:",
        "protect-otherreason-op": "אַנדער סיבה",
        "protect-dropdown": "* געוויינטלעכע סיבות פאר שיצן\n** אסאך וואנדאליזם\n** אסאך ספאם\n** אומנוציקער רעדאקטירונג קריג\n** שטארק געניצטער בלאט",
        "tooltip-ca-undelete": "צוריק דרייען די ענדערונגען פון דעם בלאט פארן מעקן",
        "tooltip-ca-move": "באַוועגן דעם בלאַט",
        "tooltip-ca-watch": "לייגט צו דעם בלאט אויפצופאסן",
-       "tooltip-ca-unwatch": "נעמט אראפ דעם בלאט פון אויפפאסן",
+       "tooltip-ca-unwatch": "נעמט אַראָפּ דעם בלאַט פון נאָכפאָלג־ליסטע",
        "tooltip-search": "זוכט אינעם סייט",
        "tooltip-search-go": "גייט צו א בלאט מיט אט דעם נאמען, אויב ער עקסיסטירט",
        "tooltip-search-fulltext": "זוכט דעם טעקסט אין די בלעטער",
        "tooltip-feed-atom": "לייג צו אן אטאמאטישער אפדעיט דורך אטאם Atom",
        "tooltip-t-contributions": "אלע בײַשטײַערונגען פון דעם באניצער",
        "tooltip-t-emailuser": "שיקן א בליצבריוו צו דעם בַאניצער",
+       "tooltip-t-info": "נאָך אינפאָרמאַציע וועגן דעם בלאַט",
        "tooltip-t-upload": "ארויפלאדן טעקעס",
        "tooltip-t-specialpages": "אלע ספעציעלע בלעטער",
        "tooltip-t-print": "דרוק ווערסיע פון דעם בלאט",
index bd746e4..12154de 100644 (file)
        "category_header": "分类“$1”中的页面",
        "subcategories": "子分类",
        "category-media-header": "分类“$1”中的媒体文件",
-       "category-empty": "<em>本分类目前没有包含页面或媒体文件。</em>",
-       "hidden-categories": "{{PLURAL:$1|隐藏分类|$1个隐藏分类}}",
+       "category-empty": "<em>本分类目前没有包含任何页面或媒体文件。</em>",
+       "hidden-categories": "{{PLURAL:$1|隐藏分类}}",
        "hidden-category-category": "隐藏分类",
        "category-subcat-count": "{{PLURAL:$2|本分类只有以下子分类。|本分类有以下$1个子分类,共有$2个子分类。}}",
        "category-subcat-count-limited": "本分类有以下{{PLURAL:$1|子分类|$1个子分类}}。",
        "variants": "变种",
        "navigation-heading": "导航菜单",
        "errorpagetitle": "错误",
-       "returnto": "返回$1。",
+       "returnto": "返回$1。",
        "tagline": "来自{{SITENAME}}",
        "help": "帮助",
        "search": "搜索",
        "jumpto": "跳转至:",
        "jumptonavigation": "导航",
        "jumptosearch": "搜索",
-       "view-pool-error": "对不起,服务器当前正超负荷运转。过多用户正尝试查看本页面。请在再次尝试访问本页面前稍等片刻。\n\n$1",
-       "generic-pool-error": "对不起,服务器目前超负荷运转。太多用户尝试查看本页面。请稍等片刻再重新尝试。",
+       "view-pool-error": "对不起,服务器当前正超负荷运转。正在尝试查看本页面的用户过多。在重新尝试访问本页面之前,请您稍等片刻。\n\n$1",
+       "generic-pool-error": "对不起,服务器当前正超负荷运转。正在尝试查看本资源的用户过多。在重新尝试访问本资源之前,请您稍等片刻。",
        "pool-timeout": "等待锁超时",
        "pool-queuefull": "请求队列已满",
        "pool-errorunknown": "未知错误",
        "aboutsite": "关于{{SITENAME}}",
        "aboutpage": "Project:关于",
        "copyright": "除非另有声明,本网站内容采用$1授权。",
-       "copyrightpage": "{{ns:project}}:著作权",
+       "copyrightpage": "{{ns:project}}:权",
        "currentevents": "新闻动态",
        "currentevents-url": "Project:新闻动态",
        "disclaimers": "免责声明",
        "mainpage": "首页",
        "mainpage-description": "首页",
        "policy-url": "Project:方针",
-       "portal": "社区主页",
-       "portal-url": "Project:社区专页",
+       "portal": "社区门户",
+       "portal-url": "Project:社区门户",
        "privacy": "隐私政策",
        "privacypage": "Project:隐私权政策",
        "badaccess": "权限错误",
-       "badaccess-group0": "你被ç¦\81æ­¢æ\89§è¡\8cä½ å\88\9aæ\89\8d请求的操作。",
-       "badaccess-groups": "您请求的操作仅限属于{{PLURAL:$2|该用户组|这些用户组}}的用户执行:$1",
+       "badaccess-group0": "ä¸\8då\85\81许æ\82¨æ\89§è¡\8cæ\82¨æ\89\80请求的操作。",
+       "badaccess-groups": "您所请求的操作仅限于{{PLURAL:$2|该|这些}}用户组的用户使用:$1",
        "versionrequired": "需要$1版本的MediaWiki",
-       "versionrequiredtext": "使用本页需要$1版本的MediaWiki。请见[[Special:Version|版本页面]]。",
+       "versionrequiredtext": "使用本页需要$1版本的MediaWiki。请见[[Special:Version|版本页面]]。",
        "ok": "确定",
        "backlinksubtitle": "←$1",
        "retrievedfrom": "取自“$1”",
        "viewsourceold": "查看源代码",
        "editlink": "编辑",
        "viewsourcelink": "查看源代码",
-       "editsectionhint": "编辑段落:$1",
+       "editsectionhint": "编辑小节:$1",
        "toc": "目录",
        "showtoc": "显示",
        "hidetoc": "隐藏",
        "sort-descending": "降序",
        "sort-ascending": "升序",
        "nstab-main": "页面",
-       "nstab-user": "用户页",
+       "nstab-user": "用户页",
        "nstab-media": "媒体文件页面",
        "nstab-special": "特殊页面",
        "nstab-project": "项目页面",
        "nstab-image": "文件",
-       "nstab-mediawiki": "息",
+       "nstab-mediawiki": "息",
        "nstab-template": "模板",
        "nstab-help": "帮助页面",
        "nstab-category": "分类",
-       "nosuchaction": "无该命令",
-       "nosuchactiontext": "URLæ\8c\87å®\9aç\9a\84æ\93\8dä½\9cæ\97 æ\95\88ã\80\82ä½ å\8f¯è\83½è¾\93å\85¥äº\86é\94\99误ç\9a\84URLå\9c°å\9d\80ï¼\8cæ\88\96æ\98¯ç\82¹å\87»äº\86é\94\99误ç\9a\84é\93¾æ\8e¥ã\80\82è¿\99ä¹\9få\8f¯è\83½è¡¨æ\98\8e{{SITENAME}}使ç\94¨ç\9a\84软件ç\9a\84å­\98å\9c¨æ¼\8fæ´\9eï¼\88bugï¼\89。",
+       "nosuchaction": "无此操作",
+       "nosuchactiontext": "URLæ\89\80æ\8c\87å®\9aç\9a\84æ\93\8dä½\9cæ\97 æ\95\88ã\80\82ä½ æ\89\80è¾\93å\85¥ç\9a\84URLå\9c°å\9d\80å\8f¯è\83½æ\9c\89误ï¼\8cæ\88\96æ\98¯ä½¿ç\94¨äº\86é\94\99误ç\9a\84é\93¾æ\8e¥ã\80\82è¿\99ä¹\9få\8f¯è\83½è¡¨ç¤º{{SITENAME}}æ\89\80使ç\94¨è½¯ä»¶ä¹\8b中å­\98å\9c¨æ¼\8fæ´\9e。",
        "nosuchspecialpage": "此特殊页面不存在",
-       "nospecialpagetext": "<strong>您请求了一个无效的特殊页面。</strong>\n\n有效的特殊页面的列表可以在[[Special:SpecialPages|{{int:specialpages}}]]找到。",
-       "error": "出错",
+       "nospecialpagetext": "<strong>您请求了一个无效的特殊页面。</strong>\n\n在[[Special:SpecialPages|{{int:specialpages}}]可以]找到有效的特殊页面的列表。",
+       "error": "错误",
        "databaseerror": "数据库错误",
-       "databaseerror-text": "数据库查询出错。这可能表明软件中存在漏洞(bug)。",
-       "databaseerror-textcl": "数据库查询出错。",
+       "databaseerror-text": "出现数据库查询错误。这可能表示软件中存在漏洞。",
+       "databaseerror-textcl": "出现数据库查询错误。",
        "databaseerror-query": "查询:$1",
        "databaseerror-function": "函数:$1",
-       "databaseerror-error": "出错:$1",
-       "laggedslavemode": "'''警告:'''页面可能没有包含最近的更新。",
-       "readonly": "数据库锁定",
-       "enterlockreason": "请输入锁定的原因(包括预计解锁的时间)",
-       "readonlytext": "数据库当前被锁定,不能添加新条目或进行其他修改,锁定可能是因为例行的数据库维护,完成后即可恢复正常。\n\n锁定数据库的管理员提供的解释:$1",
+       "databaseerror-error": "错误:$1",
+       "laggedslavemode": "'''警告:'''页面可能没有包含最近的更新。",
+       "readonly": "数据库锁定",
+       "enterlockreason": "请输入锁定原因,包括预计解锁的时间",
+       "readonlytext": "数据库当前被锁定,不能添加新条目或进行其他修改,锁定可能是因为例行的数据库维护,完成后即可恢复正常。\n\n锁定数据库的管理员提供的解释:$1",
        "missing-article": "数据库找不到预期的页面文字:“$1”$2。\n\n这通常是由于点击了链向旧有差异或历史的链接,而原有版本已被删除导致的。\n\n如果情况不是这样,您可能找到了软件的一个内部错误。请记录下URL地址,并向[[Special:ListUsers/sysop|管理员]]报告。",
        "missingarticle-rev": "(版本#:$1)",
        "missingarticle-diff": "(差异:$1,$2)",
        "content-model-text": "纯文本",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
-       "duplicate-args-category": "调用重复模参数的页面",
+       "duplicate-args-category": "调用重复模参数的页面",
        "duplicate-args-category-desc": "页面包含使用重复参数的模板调用,例如<code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code>或<code><nowiki>{{foo|bar|1=baz}}</nowiki></code>。",
        "expensive-parserfunction-warning": "<strong>警告:</strong>这个页面有太多高昂的语法功能调用。\n\n它应该少过$2次呼叫,现在有$1次呼叫。",
        "expensive-parserfunction-category": "页面中有太多耗费的语法功能呼叫",
        "action-userrights": "编辑所有用户的权限",
        "action-userrights-interwiki": "编辑其他wiki用户的用户权限",
        "action-siteadmin": "锁定或解锁数据库",
-       "action-sendemail": "电邮联系其他用户",
+       "action-sendemail": "发送电子邮件",
        "action-editmywatchlist": "编辑你的监视列表",
        "action-viewmywatchlist": "查看你的监视列表",
        "action-viewmyprivateinfo": "查看您的私人信息",
        "trackingcategories-msg": "追踪分类",
        "trackingcategories-name": "信息名",
        "trackingcategories-desc": "分类收录标准",
-       "noindex-category-desc": "因为页面上有魔术字<code><nowiki>__NOINDEX__</nowiki></code>并位于允许该标记的名字空间,而不被网络爬虫索引的页面。",
-       "index-category-desc": "页面上有魔术字<code><nowiki>__INDEX__</nowiki></code>(并位于允许该标记的名字空间),并因此被网络爬虫索引(这些页面通常不会被索引)。",
+       "noindex-category-desc": "因为页面上有魔术字<code><nowiki>__NOINDEX__</nowiki></code>并位于允许该标记的名字空间,而不被机器人索引的页面。",
+       "index-category-desc": "页面上有魔术字<code><nowiki>__INDEX__</nowiki></code>(并位于允许该标记的名字空间),并因此被机器人索引,但通常不应被索引。",
        "post-expand-template-inclusion-category-desc": "在展开所有模板后,页面大小大于<code>$wgMaxArticleSize</code>,所以某些模板未展开。",
        "post-expand-template-argument-category-desc": "在展开模板参数(以三对花括号包含的东西,如<code>{{{Foo}}}</code>)后,页面大于<code>$wgMaxArticleSize</code>。",
        "expensive-parserfunction-category-desc": "页面使用过多高开销解析器函数(如<code>#ifexist</code>)。请见[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgExpensiveParserFunctionLimit MediaWiki官网手册]。",
        "api-error-stashfailed": "内部错误:服务器保存临时文件失败。",
        "api-error-publishfailed": "内部错误:服务器发布临时文件失败。",
        "api-error-stasherror": "上传文件存档时出现错误。",
+       "api-error-stashedfilenotfound": "试图从藏匿处上传时找不到藏匿的文件。",
+       "api-error-stashpathinvalid": "找到的藏匿文件的路径是无效的。",
+       "api-error-stashfilestorage": "存储文件至藏匿处时出错。",
+       "api-error-stashzerolength": "服务器不能藏匿文件,因为它已经没有藏匿空间。",
+       "api-error-stashnotloggedin": "您必须登录以保存文件至上传藏匿处。",
+       "api-error-stashwrongowner": "您试图在藏匿处访问的文件不属于您。",
+       "api-error-stashnosuchfilekey": "您试图在藏匿处访问的文件密钥不存在。",
        "api-error-timeout": "服务器没有在预期内响应。",
        "api-error-unclassified": "出现未知错误。",
        "api-error-unknown-code": "未知错误:$1",
        "log-description-pagelang": "这是页面语言更改的日志。",
        "logentry-pagelang-pagelang": "$1{{GENDER:$2|更改}}$3的页面语言:从$4改为$5。",
        "default-skin-not-found": "天哪!您在<code dir=\"ltr\">$wgDefaultSkin</code>定义的wiki默认皮肤<code>$1</code>不可用。您的安装版本看起来需要包含以下皮肤。参见MediaWiki官网手册[https://www.mediawiki.org/wiki/Manual:Skin_configuration “皮肤配置”]获取如何启用他们并设置为默认。\n\n$2\n\n; 如果您刚刚安装完了MediaWiki的话:\n: 您可能是从git库安装的,或者使用其他方法直接从源代码安装的。希望如此。尝试通过以下方法从[https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org的皮肤存储库]安装一些皮肤:\n:* 下载[https://www.mediawiki.org/wiki/Download/zh-hans 打包安装器],这会预装一些皮肤和扩展。您可在此处复制粘贴<code>skins/</code>。\n:* 通过git直接克隆<code>mediawiki/skins/*</code>存储库中的一个至您的MediaWiki副本的<code dir=\"ltr\">skins/</code>。\n: 做这些事应该不会打扰您的git存储库如果你是MediaWiki开发人员的话。\n\n; 如果您升级了您的MediaWiki的话:\n: MediaWiki 1.24版本起不再自动启用已安装皮肤(参见[https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery 此手册])。您可复制粘贴以下文本至您wiki的<code>LocalSettings.php</code>以启用安装的皮肤:\n\n<pre dir=\"ltr\">$3</pre>\n\n; 如果您已经修改了<code>LocalSettings.php</code>:\n: 请再次检查皮肤名以确保不存在错误拼写。",
-       "default-skin-not-found-no-skins": "天哪!您在<code>$wgDefaultSkin</code>定义的wiki默认皮肤<code>$1</code>不可用。而且您没有安装任何皮肤。\n\n; 如果您刚刚安装完了MediaWiki的话:\n: 您可能是从git库安装的,或者使用其他方法直接从源代码安装的,希望如此。这是因为MediaWiki 1.24版本起主代码库不再包含任何皮肤。尝试通过以下方法从[https://www.mediawiki.org/wiki/Special:MyLanguage/Category:All_skins mediawiki.org的皮肤存储库]安装一些皮肤:\n:* 下载[https://www.mediawiki.org/wiki/Download/zh-hans 打包安装器],这会预装一些皮肤和扩展。您可在此处复制粘贴<code>skins/</code>。\n:* 通过git直接克隆<code>mediawiki/skins/*</code>存储库中的一个至您的MediaWiki副本的<code dir=\"ltr\">skins/</code>。\n: 做这些事应该不会打扰您的git存储库如果你是MediaWiki开发人员的话。参见MediaWiki官网手册[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Skin_configuration “皮肤配置”]获取如何启用他们并设置为默认。",
+       "default-skin-not-found-no-skins": "天哪!您在<code>$wgDefaultSkin</code>定义的wiki默认皮肤<code>$1</code>不可用。而且您没有安装任何皮肤。\n\n; 如果您刚刚安装完了MediaWiki的话:\n: 您可能是从git库安装的,或者使用其他方法直接从源代码安装的,这是预期的。这是因为MediaWiki 1.24版本起主代码库不再包含任何皮肤。尝试通过以下方法从[https://www.mediawiki.org/wiki/Special:MyLanguage/Category:All_skins mediawiki.org的皮肤存储库]安装一些皮肤:\n:* 下载[https://www.mediawiki.org/wiki/Download/zh-hans 打包安装器],这会预装一些皮肤和扩展。您可在此处复制粘贴<code>skins/</code>。\n:* 通过git直接克隆<code>mediawiki/skins/*</code>存储库中的一个至您的MediaWiki副本的<code dir=\"ltr\">skins/</code>。\n: 做这些事应该不会打扰您的git存储库如果你是MediaWiki开发人员的话。参见MediaWiki官网手册[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Skin_configuration “皮肤配置”]获取如何启用他们并设置为默认。",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2(已启用)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2('''已禁用''')",
        "mediastatistics": "媒体统计",
index fe17eeb..68de984 100644 (file)
@@ -86,7 +86,7 @@
        "tog-shownumberswatching": "顯示正在監視的使用者數",
        "tog-oldsig": "現有簽名:",
        "tog-fancysig": "將簽名視為 Wikitext 語言 (不自動產生連結)",
-       "tog-uselivepreview": "使用即時預覽 (測試中)",
+       "tog-uselivepreview": "使用即時預覽 (實驗中)",
        "tog-forceeditsummary": "未填寫編輯摘要時提示我",
        "tog-watchlisthideown": "隱藏監視清單中我自己的編輯",
        "tog-watchlisthidebots": "隱藏監視清單中機器人的編輯",
        "policy-url": "Project:Policy",
        "portal": "社群入口",
        "portal-url": "Project:Community portal",
-       "privacy": "私隱政策",
+       "privacy": "隱私政策",
        "privacypage": "Project:Privacy policy",
        "badaccess": "權限錯誤",
        "badaccess-group0": "系統不允許您執行這項操作。",
        "right-editmyuserjs": "編輯自己的使用者 JavaScript 檔",
        "right-viewmywatchlist": "檢視自己的監視清單",
        "right-editmywatchlist": "編輯自己的監視清單。注意,即使無此權限,某些操作仍會新增頁面至監視清單。",
-       "right-viewmyprivateinfo": "檢視自己的私隱資料 (如:電子郵件位址及真實姓名)",
-       "right-editmyprivateinfo": "編輯自己的私隱資料 (如:電子郵件位址及真實姓名)",
+       "right-viewmyprivateinfo": "檢視自己的私隱資料(如:電子郵件位址及真實姓名)",
+       "right-editmyprivateinfo": "編輯自己的隱私資料(如:電子郵件位址及真實姓名)",
        "right-editmyoptions": "編輯自己的偏好設定",
        "right-rollback": "快速還原最後一位使用者對某一頁面的編輯",
        "right-markbotedits": "標示還原編輯為機械人編輯",
        "unknown_extension_tag": "不明的擴充標籤 \"$1\"",
        "duplicate-defaultsort": "<strong>警告:</strong>預設的排序鍵 \"$2\" 會覆蓋先前預設的排序鍵 \"$1\"。",
        "duplicate-displaytitle": "<strong>警告:</strong> 顯示標題 \"$2\" 覆蓋之前的顯示標題 \"$1\"。",
+       "invalid-indicator-name": "<strong>錯誤:</strong>頁面狀態指示器的<code>name</code> 屬性不能為空。",
        "version": "版本",
        "version-extensions": "已安裝的擴充套件",
        "version-skins": "已安裝的外觀",
        "revdelete-uname-unhid": "取消隱藏使用者名稱",
        "revdelete-restricted": "已套用對管理員的限制",
        "revdelete-unrestricted": "已移除對管理員的限制",
+       "logentry-merge-merge": "$1將$3{{GENDER:$2|合併}}至$4(修訂版本至$5)",
        "logentry-move-move": "$1 {{GENDER:$2|已移動}}頁面 $3 至 $4",
        "logentry-move-move-noredirect": "$1 已移動頁面 $3 至 $4,不留重新導向頁面",
        "logentry-move-move_redir": "$1 已移動頁面 $3 至 $4 並覆蓋原有重新導向",
        "api-error-stashfailed": "內部錯誤:伺服器儲存暫存檔案失敗。",
        "api-error-publishfailed": "內部錯誤:伺服器發佈暫存檔案失敗。",
        "api-error-stasherror": "上傳檔案至儲存庫時發生錯誤。",
+       "api-error-stashedfilenotfound": "嘗試從藏匿處上傳時找不到藏匿的檔案。",
+       "api-error-stashpathinvalid": "找到的藏匿檔案的路徑是無效的。",
+       "api-error-stashfilestorage": "儲存檔案至藏匿處時出錯。",
+       "api-error-stashzerolength": "伺服器不能藏匿檔案,因爲它已經沒有藏匿空間了。",
+       "api-error-stashnotloggedin": "您必須登入以儲存檔案至上傳藏匿處。",
+       "api-error-stashwrongowner": "您嘗試在藏匿處存取的檔案不屬於您。",
+       "api-error-stashnosuchfilekey": "您嘗試在藏匿處存取的檔案金鑰不存在。",
        "api-error-timeout": "伺服器沒有在預期的時間內回應。",
        "api-error-unclassified": "發生不明錯誤。",
        "api-error-unknown-code": "不明錯誤:\"$1\"。",
index 655f309..8b23909 100644 (file)
--- a/load.php
+++ b/load.php
@@ -23,7 +23,7 @@
  */
 
 // Bail if PHP is too low
-if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.2' ) < 0 ) {
+if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.3' ) < 0 ) {
        // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
        require dirname( __FILE__ ) . '/includes/PHPVersionError.php';
        wfPHPVersionError( 'load.php' );
@@ -38,16 +38,15 @@ if ( !$wgRequest->checkUrlExtension() ) {
        return;
 }
 
-// Respond to resource loading request
-$resourceLoader = new ResourceLoader(
-       ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
-);
+// Respond to resource loading request.
+// foo()->bar() syntax is not supported in PHP4, and this file needs to *parse* in PHP4.
+$configFactory = ConfigFactory::getDefaultInstance();
+$resourceLoader = new ResourceLoader( $configFactory->makeConfig( 'main' ) );
 $resourceLoader->respond( new ResourceLoaderContext( $resourceLoader, $wgRequest ) );
 
 wfProfileOut( 'load.php' );
 wfLogProfilingData();
 
-// Shut down the database.  foo()->bar() syntax is not supported in PHP4, and this file
-// needs to *parse* in PHP4, although we'll never get down here to worry about = vs =&
+// Shut down the database.
 $lb = wfGetLBFactory();
 $lb->shutdown();
index 8d30df4..d740f56 100644 (file)
@@ -20,8 +20,8 @@
  * @defgroup Maintenance Maintenance
  */
 
-// Make sure we're on PHP5.3.2 or better
-if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.2' ) < 0 ) {
+// Make sure we're on PHP5.3.3 or better
+if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.3' ) < 0 ) {
        // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
        require_once dirname( __FILE__ ) . '/../includes/PHPVersionError.php';
        wfPHPVersionError( 'cli' );
index 9e88c13..6234db4 100644 (file)
@@ -37,6 +37,9 @@ require_once __DIR__ . '/cleanupTable.inc';
  * @ingroup Maintenance
  */
 class CapsCleanup extends TableCleanup {
+
+       private $user;
+
        public function __construct() {
                parent::__construct();
                $this->mDescription = "Script to cleanup capitalization";
@@ -44,13 +47,13 @@ class CapsCleanup extends TableCleanup {
        }
 
        public function execute() {
-               global $wgCapitalLinks, $wgUser;
+               global $wgCapitalLinks;
 
                if ( $wgCapitalLinks ) {
                        $this->error( "\$wgCapitalLinks is on -- no need for caps links cleanup.", true );
                }
 
-               $wgUser = User::newFromName( 'Conversion script' );
+               $this->user = User::newFromName( 'Conversion script' );
 
                $this->namespace = intval( $this->getOption( 'namespace', 0 ) );
                $this->dryrun = $this->hasOption( 'dry-run' );
@@ -87,7 +90,9 @@ class CapsCleanup extends TableCleanup {
                        $this->output( "\"$display\" -> \"$targetDisplay\": DRY RUN, NOT MOVED\n" );
                        $ok = true;
                } else {
-                       $ok = $current->moveTo( $target, false, 'Converting page titles to lowercase' );
+                       $mp = new MovePage( $current, $target );
+                       $status = $mp->move( $this->user, 'Converting page titles to lowercase', true );
+                       $ok = $status->isOK() ? 'OK' : $status->getWikiText();
                        $this->output( "\"$display\" -> \"$targetDisplay\": $ok\n" );
                }
                if ( $ok === true ) {
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index 4b2ff71..d8bc3a4 100644 (file)
@@ -54,7 +54,7 @@ abstract class DumpIterator extends Maintenance {
                $this->checkOptions();
 
                if ( $this->hasOption( 'file' ) ) {
-                       $revision = new WikiRevision;
+                       $revision = new WikiRevision( $this->getConfig() );
 
                        $revision->setText( file_get_contents( $this->getOption( 'file' ) ) );
                        $revision->setTitle( Title::newFromText(
@@ -73,7 +73,7 @@ abstract class DumpIterator extends Maintenance {
                        $this->error( "Sorry, I don't support dump filenames yet. "
                                . "Use - and provide it on stdin on the meantime.", true );
                }
-               $importer = new WikiImporter( $source );
+               $importer = new WikiImporter( $source, $this->getConfig() );
 
                $importer->setRevisionCallback(
                        array( &$this, 'handleRevision' ) );
index 1f75bcc..ea8c84b 100644 (file)
@@ -270,7 +270,7 @@ TEXT;
                $this->startTime = microtime( true );
 
                $source = new ImportStreamSource( $handle );
-               $importer = new WikiImporter( $source );
+               $importer = new WikiImporter( $source, $this->getConfig() );
 
                if ( $this->hasOption( 'debug' ) ) {
                        $importer->setDebug( true );
index 2aff988..bf0e07f 100644 (file)
@@ -66,7 +66,7 @@ class SeeTag < CommonTag
     <<-EOHTML
       <h3 class="pa">Related</h3>
       <ul>
-      #{ context[@tagname].map {|tag| tag[:doc] }.join("\n") }
+      #{ context[@tagname].map { |tag| tag[:doc] }.join("\n") }
       </ul>
     EOHTML
   end
index 713753f..a27a772 100644 (file)
@@ -103,10 +103,10 @@ class MoveBatch extends Maintenance {
 
                        $this->output( $source->getPrefixedText() . ' --> ' . $dest->getPrefixedText() );
                        $dbw->begin( __METHOD__ );
-                       $err = $source->moveTo( $dest, false, $reason, !$noredirects );
-                       if ( $err !== true ) {
-                               $msg = array_shift( $err[0] );
-                               $this->output( "\nFAILED: " . wfMessage( $msg, $err[0] )->text() );
+                       $mp = new MovePage( $source, $dest );
+                       $status = $mp->move( $wgUser, $reason, !$noredirects );
+                       if ( !$status->isOK() ) {
+                               $this->output( "\nFAILED: " . $status->getWikiText() );
                        }
                        $dbw->commit( __METHOD__ );
                        $this->output( "\n" );
old mode 100644 (file)
new mode 100755 (executable)
index 169f512..2218a5e 100644 (file)
@@ -61,7 +61,7 @@ class DumpRenderer extends Maintenance {
                }
 
                $source = new ImportStreamSource( $this->getStdin() );
-               $importer = new WikiImporter( $source );
+               $importer = new WikiImporter( $source, $this->getConfig() );
 
                $importer->setRevisionCallback(
                        array( &$this, 'handleRevision' ) );
index 0f99662..c0f6c7b 100644 (file)
@@ -39,6 +39,7 @@ if ( !defined( 'MEDIAWIKI' ) ) {
 /**
  * Maintenance script to do various checks on external storage.
  *
+ * @fixme this should extend the base Maintenance class
  * @ingroup Maintenance ExternalStorage
  */
 class CheckStorage {
@@ -466,7 +467,10 @@ class CheckStorage {
                $dbw->ping();
 
                $source = new ImportStreamSource( $file );
-               $importer = new WikiImporter( $source );
+               $importer = new WikiImporter(
+                       $source,
+                       ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
+               );
                $importer->setRevisionCallback( array( &$this, 'importRevision' ) );
                $importer->doImport();
        }
old mode 100644 (file)
new mode 100755 (executable)
index 046d73c..657a2fc 100755 (executable)
@@ -26,7 +26,7 @@
  * @ingroup Maintenance
  */
 
-if ( !function_exists( 'version_compare' ) || ( version_compare( PHP_VERSION, '5.3.2' ) < 0 ) ) {
+if ( !function_exists( 'version_compare' ) || ( version_compare( PHP_VERSION, '5.3.3' ) < 0 ) ) {
        require dirname( __FILE__ ) . '/../includes/PHPVersionError.php';
        wfPHPVersionError( 'cli' );
 }
index a6cebc3..ed3e7f4 100644 (file)
@@ -21,7 +21,7 @@
  */
 
 // Bail if PHP is too low
-if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.2' ) < 0 ) {
+if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.3' ) < 0 ) {
        // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
        require dirname( dirname( __FILE__ ) ) . '/includes/PHPVersionError.php';
        wfPHPVersionError( 'mw-config/index.php' );
index be2a24f..1056abc 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.1.0-pre (deccd11549)
+ * OOjs UI v0.1.0-pre (571f26d0ab)
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-10-28T16:52:27Z
+ * Date: 2014-11-03T21:02:16Z
  */
 /* @noflip */
 .oo-ui-rtl {
@@ -20,6 +20,7 @@
        cursor: pointer;
        display: inline-block;
        vertical-align: middle;
+       font-family: inherit;
        -webkit-touch-callout: none;
        -webkit-user-select: none;
           -moz-user-select: none;
        box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
 }
 .oo-ui-fieldLayout {
+       display: block;
        margin-bottom: 1em;
 }
 .oo-ui-fieldLayout:before,
        margin-left: 0;
 }
 .oo-ui-progressBarWidget {
-       width: 20em;
+       width: 100%;
+       max-width: 50em;
        border: solid 1px #a6cee1;
        border-radius: 0.25em;
 }
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
-       width: 20em;
+       width: 100%;
+       max-width: 50em;
 }
 .oo-ui-textInputWidget input,
 .oo-ui-textInputWidget textarea {
        padding: 0.33em 0.75em;
        color: #888888;
 }
-.oo-ui-inlineMenuWidget {
+.oo-ui-dropdownWidget {
        position: relative;
        display: inline-block;
        margin: 0.25em 0;
-       min-width: 20em;
+       width: 100%;
+       max-width: 50em;
 }
-.oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget-handle {
        width: 100%;
        display: inline-block;
        cursor: pointer;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        position: absolute;
        background-position: center center;
        background-repeat: no-repeat;
 }
-.oo-ui-inlineMenuWidget .oo-ui-menuWidget {
+.oo-ui-dropdownWidget .oo-ui-menuWidget {
        z-index: 1;
        width: 100%;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
        cursor: default;
 }
-.oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget-handle {
        height: 2.5em;
        border: solid 1px rgba(0, 0, 0, 0.1);
        border-radius: 0.25em;
 }
-.oo-ui-inlineMenuWidget-handle:hover {
+.oo-ui-dropdownWidget-handle:hover {
        border-color: rgba(0, 0, 0, 0.2);
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
        right: 0;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        left: 0.25em;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        line-height: 2.5em;
        margin: 0 0.5em;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        top: 0;
        width: 2.5em;
        height: 2.5em;
        opacity: 0.8;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
        color: #cccccc;
        text-shadow: 0 1px 1px #ffffff;
        border-color: #dddddd;
        background-color: #f3f3f3;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
        opacity: 0.2;
 }
-.oo-ui-inlineMenuWidget.oo-ui-iconElement .oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin-left: 3em;
 }
-.oo-ui-inlineMenuWidget.oo-ui-indicatorElement .oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin-right: 2em;
 }
 .oo-ui-outlineItemWidget {
 .oo-ui-comboBoxWidget {
        display: inline-block;
        position: relative;
-       min-width: 20em;
+       width: 100%;
+       max-width: 50em;
 }
 .oo-ui-comboBoxWidget > .oo-ui-selectWidget {
        width: 100%;
index 3fa88c6..fb6b342 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.1.0-pre (deccd11549)
+ * OOjs UI v0.1.0-pre (571f26d0ab)
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-10-28T16:52:18Z
+ * Date: 2014-11-03T21:02:06Z
  */
 /* Instantiation */
 
index b5fc881..5182a5f 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.1.0-pre (deccd11549)
+ * OOjs UI v0.1.0-pre (571f26d0ab)
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-10-28T16:52:27Z
+ * Date: 2014-11-03T21:02:16Z
  */
 /* @noflip */
 .oo-ui-rtl {
@@ -20,6 +20,7 @@
        cursor: pointer;
        display: inline-block;
        vertical-align: middle;
+       font-family: inherit;
        -webkit-touch-callout: none;
        -webkit-user-select: none;
           -moz-user-select: none;
        box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
 }
 .oo-ui-fieldLayout {
+       display: block;
        margin-bottom: 1em;
 }
 .oo-ui-fieldLayout:before,
        margin-left: 0;
 }
 .oo-ui-progressBarWidget {
-       width: 20em;
+       width: 100%;
+       max-width: 50em;
        border: solid 1px #a6cee1;
        border-radius: 0.25em;
 }
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
-       width: 20em;
+       width: 100%;
+       max-width: 50em;
 }
 .oo-ui-textInputWidget input,
 .oo-ui-textInputWidget textarea {
        padding: 0.33em 0.75em;
        color: #888888;
 }
-.oo-ui-inlineMenuWidget {
+.oo-ui-dropdownWidget {
        position: relative;
        display: inline-block;
        margin: 0.25em 0;
-       min-width: 20em;
+       width: 100%;
+       max-width: 50em;
 }
-.oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget-handle {
        width: 100%;
        display: inline-block;
        cursor: pointer;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        position: absolute;
        background-position: center center;
        background-repeat: no-repeat;
 }
-.oo-ui-inlineMenuWidget .oo-ui-menuWidget {
+.oo-ui-dropdownWidget .oo-ui-menuWidget {
        z-index: 1;
        width: 100%;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
        cursor: default;
 }
-.oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget-handle {
        height: 2.5em;
        border: solid 1px rgba(0, 0, 0, 0.1);
        border-radius: 0.25em;
 }
-.oo-ui-inlineMenuWidget-handle:hover {
+.oo-ui-dropdownWidget-handle:hover {
        border-color: rgba(0, 0, 0, 0.2);
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
        right: 0;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        left: 0.25em;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        line-height: 2.5em;
        margin: 0 0.5em;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        top: 0;
        width: 2.5em;
        height: 2.5em;
        opacity: 0.8;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
        color: #cccccc;
        text-shadow: 0 1px 1px #ffffff;
        border-color: #dddddd;
        background-color: #f3f3f3;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
        opacity: 0.2;
 }
-.oo-ui-inlineMenuWidget.oo-ui-iconElement .oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin-left: 3em;
 }
-.oo-ui-inlineMenuWidget.oo-ui-indicatorElement .oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin-right: 2em;
 }
 .oo-ui-outlineItemWidget {
 .oo-ui-comboBoxWidget {
        display: inline-block;
        position: relative;
-       min-width: 20em;
+       width: 100%;
+       max-width: 50em;
 }
 .oo-ui-comboBoxWidget > .oo-ui-selectWidget {
        width: 100%;
index e06261d..5266918 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.1.0-pre (deccd11549)
+ * OOjs UI v0.1.0-pre (571f26d0ab)
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-10-28T16:52:27Z
+ * Date: 2014-11-03T21:02:16Z
  */
 /* @noflip */
 .oo-ui-rtl {
@@ -20,6 +20,7 @@
        cursor: pointer;
        display: inline-block;
        vertical-align: middle;
+       font-family: inherit;
        -webkit-touch-callout: none;
        -webkit-user-select: none;
           -moz-user-select: none;
        box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
 }
 .oo-ui-fieldLayout {
+       display: block;
        margin-bottom: 1em;
 }
 .oo-ui-fieldLayout:before,
        background-color: #ffffff;
 }
 .oo-ui-progressBarWidget {
-       width: 20em;
+       width: 100%;
+       max-width: 50em;
        border: solid 1px #0274ff;
        border-radius: 0.1em;
 }
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
-       width: 20em;
+       width: 100%;
+       max-width: 50em;
 }
 .oo-ui-textInputWidget input,
 .oo-ui-textInputWidget textarea {
        padding: 0.33em 0.75em;
        color: #888888;
 }
-.oo-ui-inlineMenuWidget {
+.oo-ui-dropdownWidget {
        position: relative;
        display: inline-block;
        margin: 0.25em 0;
-       min-width: 20em;
+       width: 100%;
+       max-width: 50em;
 }
-.oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget-handle {
        width: 100%;
        display: inline-block;
        cursor: pointer;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        position: absolute;
        background-position: center center;
        background-repeat: no-repeat;
 }
-.oo-ui-inlineMenuWidget .oo-ui-menuWidget {
+.oo-ui-dropdownWidget .oo-ui-menuWidget {
        z-index: 1;
        width: 100%;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
        cursor: default;
 }
-.oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget-handle {
        height: 2.5em;
        border: solid 1px #cccccc;
        border-radius: 0.1em;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
        right: 0;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        left: 0.25em;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        line-height: 2.5em;
        margin: 0 1em;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        top: 0;
        width: 2.5em;
        height: 2.5em;
 }
-.oo-ui-inlineMenuWidget:hover .oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget:hover .oo-ui-dropdownWidget-handle {
        border-color: #aaaaaa;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
        color: #cccccc;
        text-shadow: 0 1px 1px #ffffff;
        border-color: #dddddd;
        background-color: #f3f3f3;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
        opacity: 0.2;
 }
-.oo-ui-inlineMenuWidget.oo-ui-iconElement .oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin-left: 3em;
 }
-.oo-ui-inlineMenuWidget.oo-ui-indicatorElement .oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin-right: 2em;
 }
-.oo-ui-inlineMenuWidget .oo-ui-selectWidget {
+.oo-ui-dropdownWidget .oo-ui-selectWidget {
        border-top-color: #ffffff;
 }
 .oo-ui-outlineItemWidget {
 .oo-ui-comboBoxWidget {
        display: inline-block;
        position: relative;
+       width: 100%;
+       max-width: 50em;
 }
 .oo-ui-comboBoxWidget > .oo-ui-selectWidget {
        width: 100%;
index 6632aae..5264487 100644 (file)
@@ -1,16 +1,16 @@
 /*!
- * OOjs UI v0.1.0-pre (deccd11549)
+ * OOjs UI v0.1.0-pre (571f26d0ab)
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-10-28T16:52:18Z
+ * Date: 2014-11-03T21:02:06Z
  */
 /**
  * @class
- * @extends {OO.ui.Theme}
+ * @extends OO.ui.Theme
  *
  * @constructor
  */
index c876309..9125945 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.1.0-pre (deccd11549)
+ * OOjs UI v0.1.0-pre (571f26d0ab)
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-10-28T16:52:27Z
+ * Date: 2014-11-03T21:02:16Z
  */
 /* @noflip */
 .oo-ui-rtl {
@@ -20,6 +20,7 @@
        cursor: pointer;
        display: inline-block;
        vertical-align: middle;
+       font-family: inherit;
        -webkit-touch-callout: none;
        -webkit-user-select: none;
           -moz-user-select: none;
        box-shadow: 0 0 0.25em rgba(0, 0, 0, 0.25);
 }
 .oo-ui-fieldLayout {
+       display: block;
        margin-bottom: 1em;
 }
 .oo-ui-fieldLayout:before,
        background-color: #ffffff;
 }
 .oo-ui-progressBarWidget {
-       width: 20em;
+       width: 100%;
+       max-width: 50em;
        border: solid 1px #0274ff;
        border-radius: 0.1em;
 }
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
-       width: 20em;
+       width: 100%;
+       max-width: 50em;
 }
 .oo-ui-textInputWidget input,
 .oo-ui-textInputWidget textarea {
        padding: 0.33em 0.75em;
        color: #888888;
 }
-.oo-ui-inlineMenuWidget {
+.oo-ui-dropdownWidget {
        position: relative;
        display: inline-block;
        margin: 0.25em 0;
-       min-width: 20em;
+       width: 100%;
+       max-width: 50em;
 }
-.oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget-handle {
        width: 100%;
        display: inline-block;
        cursor: pointer;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        position: absolute;
        background-position: center center;
        background-repeat: no-repeat;
 }
-.oo-ui-inlineMenuWidget .oo-ui-menuWidget {
+.oo-ui-dropdownWidget .oo-ui-menuWidget {
        z-index: 1;
        width: 100%;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
        cursor: default;
 }
-.oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget-handle {
        height: 2.5em;
        border: solid 1px #cccccc;
        border-radius: 0.1em;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
        right: 0;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        left: 0.25em;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        line-height: 2.5em;
        margin: 0 1em;
 }
-.oo-ui-inlineMenuWidget-handle .oo-ui-indicatorElement-indicator,
-.oo-ui-inlineMenuWidget-handle .oo-ui-iconElement-icon {
+.oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator,
+.oo-ui-dropdownWidget-handle .oo-ui-iconElement-icon {
        top: 0;
        width: 2.5em;
        height: 2.5em;
 }
-.oo-ui-inlineMenuWidget:hover .oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget:hover .oo-ui-dropdownWidget-handle {
        border-color: #aaaaaa;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-inlineMenuWidget-handle {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-dropdownWidget-handle {
        color: #cccccc;
        text-shadow: 0 1px 1px #ffffff;
        border-color: #dddddd;
        background-color: #f3f3f3;
 }
-.oo-ui-inlineMenuWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
+.oo-ui-dropdownWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
        opacity: 0.2;
 }
-.oo-ui-inlineMenuWidget.oo-ui-iconElement .oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget.oo-ui-iconElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin-left: 3em;
 }
-.oo-ui-inlineMenuWidget.oo-ui-indicatorElement .oo-ui-inlineMenuWidget-handle .oo-ui-labelElement-label {
+.oo-ui-dropdownWidget.oo-ui-indicatorElement .oo-ui-dropdownWidget-handle .oo-ui-labelElement-label {
        margin-right: 2em;
 }
-.oo-ui-inlineMenuWidget .oo-ui-selectWidget {
+.oo-ui-dropdownWidget .oo-ui-selectWidget {
        border-top-color: #ffffff;
 }
 .oo-ui-outlineItemWidget {
 .oo-ui-comboBoxWidget {
        display: inline-block;
        position: relative;
+       width: 100%;
+       max-width: 50em;
 }
 .oo-ui-comboBoxWidget > .oo-ui-selectWidget {
        width: 100%;
index 96befc5..9e9aa5b 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.1.0-pre (deccd11549)
+ * OOjs UI v0.1.0-pre (571f26d0ab)
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2014 OOjs Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2014-10-28T16:52:18Z
+ * Date: 2014-11-03T21:02:06Z
  */
 ( function ( OO ) {
 
@@ -693,7 +693,7 @@ OO.ui.ActionSet.prototype.organize = function () {
  * @constructor
  * @param {Object} [config] Configuration options
  * @cfg {Function} [$] jQuery for the frame the widget is in
- * @cfg {string[]} [classes] CSS class names
+ * @cfg {string[]} [classes] CSS class names to add
  * @cfg {string} [text] Text to insert
  * @cfg {jQuery} [$content] Content elements to append (after text)
  */
@@ -800,7 +800,7 @@ OO.ui.Element.getWindow = function ( obj ) {
  *
  * @static
  * @param {jQuery|HTMLElement|HTMLDocument|Window} obj Context to get the direction for
- * @return {string} Text direction, either `ltr` or `rtl`
+ * @return {string} Text direction, either 'ltr' or 'rtl'
  */
 OO.ui.Element.getDir = function ( obj ) {
        var isDoc, isWin;
@@ -1011,7 +1011,7 @@ OO.ui.Element.getClosestScrollableContainer = function ( el, dimension ) {
  *
  * @static
  * @param {HTMLElement} el Element to scroll into view
- * @param {Object} [config={}] Configuration config
+ * @param {Object} [config] Configuration options
  * @param {string} [config.duration] jQuery animation duration value
  * @param {string} [config.direction] Scroll in only one direction, e.g. 'x' or 'y', omit
  *  to scroll in both directions
@@ -1112,7 +1112,7 @@ OO.ui.Element.offDOMEvent = function ( el, event, callback ) {
  * Check if element supports one or more methods.
  *
  * @param {string|string[]} methods Method or list of methods to check
- * @return boolean All methods are supported
+ * @return {boolean} All methods are supported
  */
 OO.ui.Element.prototype.supports = function ( methods ) {
        var i, len,
@@ -1217,7 +1217,7 @@ OO.ui.Element.prototype.setElementGroup = function ( group ) {
 /**
  * Scroll element into view.
  *
- * @param {Object} [config={}]
+ * @param {Object} [config] Configuration options
  */
 OO.ui.Element.prototype.scrollElementIntoView = function ( config ) {
        return OO.ui.Element.scrollIntoView( this.$element[0], config );
@@ -3545,8 +3545,8 @@ OO.initClass( OO.ui.Theme );
 /**
  * Get a list of classes to be applied to a widget.
  *
- * @localdoc The 'on' and 'off' lists combined MUST contain keys for all classes the theme adds or
- *   removes, otherwise state transitions will not work properly.
+ * The 'on' and 'off' lists combined MUST contain keys for all classes the theme adds or removes,
+ * otherwise state transitions will not work properly.
  *
  * @param {OO.ui.Element} element Element for which to get classes
  * @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
@@ -3560,7 +3560,7 @@ OO.ui.Theme.prototype.getElementClasses = function ( /* element */ ) {
  *
  * For elements with theme logic hooks, this should be called anytime there's a state change.
  *
- * @param {OO.ui.Element} Element for which to update classes
+ * @param {OO.ui.Element} element Element for which to update classes
  * @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
  */
 OO.ui.Theme.prototype.updateElementClasses = function ( element ) {
@@ -3584,7 +3584,8 @@ OO.ui.Theme.prototype.updateElementClasses = function ( element ) {
  * @param {Object} [config] Configuration options
  * @cfg {jQuery} [$button] Button node, assigned to #$button, omit to use a generated `<a>`
  * @cfg {boolean} [framed=true] Render button with a frame
- * @cfg {number} [tabIndex=0] Button's tab index, use null to have no tabIndex
+ * @cfg {number} [tabIndex=0] Button's tab index. Use 0 to use default ordering, use -1 to prevent
+ *   tab focusing.
  * @cfg {string} [accessKey] Button's access key
  */
 OO.ui.ButtonElement = function OoUiButtonElement( config ) {
@@ -3883,7 +3884,7 @@ OO.ui.GroupElement.prototype.aggregate = function ( events ) {
 /**
  * Add items.
  *
- * Adding an existing item (by value) will move it.
+ * Adding an existing item will move it.
  *
  * @param {OO.ui.Element[]} items Items
  * @param {number} [index] Index to insert items at
@@ -4091,7 +4092,7 @@ OO.ui.IconElement.prototype.setIconElement = function ( $icon ) {
 };
 
 /**
- * Set icon.
+ * Set icon name.
  *
  * @param {Object|string|null} icon Symbolic icon name, or map of icon names keyed by language ID;
  *  use the 'default' key to specify the icon to be used when there is no icon in the user's
@@ -4147,9 +4148,9 @@ OO.ui.IconElement.prototype.setIconTitle = function ( iconTitle ) {
 };
 
 /**
- * Get icon.
+ * Get icon name.
  *
- * @return {string} Icon
+ * @return {string} Icon name
  */
 OO.ui.IconElement.prototype.getIcon = function () {
        return this.icon;
@@ -4199,7 +4200,7 @@ OO.initClass( OO.ui.IndicatorElement );
  *
  * @static
  * @inheritable
- * @property {string|null} Symbolic indicator name or null for no indicator
+ * @property {string|null} Symbolic indicator name
  */
 OO.ui.IndicatorElement.static.indicator = null;
 
@@ -4238,7 +4239,7 @@ OO.ui.IndicatorElement.prototype.setIndicatorElement = function ( $indicator ) {
 };
 
 /**
- * Set indicator.
+ * Set indicator name.
  *
  * @param {string|null} indicator Symbolic name of indicator to use or null for no indicator
  * @chainable
@@ -4291,9 +4292,9 @@ OO.ui.IndicatorElement.prototype.setIndicatorTitle = function ( indicatorTitle )
 };
 
 /**
- * Get indicator.
+ * Get indicator name.
  *
- * @return {string} title Symbolic name of indicator
+ * @return {string} Symbolic name of indicator
  */
 OO.ui.IndicatorElement.prototype.getIndicator = function () {
        return this.indicator;
@@ -4372,7 +4373,7 @@ OO.ui.LabelElement.prototype.setLabelElement = function ( $label ) {
  * Set the label.
  *
  * An empty string will result in the label being hidden. A string containing only whitespace will
- * be converted to a single &nbsp;
+ * be converted to a single `&nbsp;`.
  *
  * @param {jQuery|string|Function|null} label Label nodes; text; a function that returns nodes or
  *  text; or null for no label
@@ -4397,7 +4398,7 @@ OO.ui.LabelElement.prototype.setLabel = function ( label ) {
 /**
  * Get the label.
  *
- * @return {jQuery|string|Function|null} label Label nodes; text; a function that returns nodes or
+ * @return {jQuery|string|Function|null} Label nodes; text; a function that returns nodes or
  *  text; or null for no label
  */
 OO.ui.LabelElement.prototype.getLabel = function () {
@@ -4487,7 +4488,7 @@ OO.ui.PopupElement.prototype.getPopup = function () {
  *
  * @constructor
  * @param {Object} [config] Configuration options
- * @cfg {string[]} [flags=[]] Styling flags, e.g. 'primary', 'destructive' or 'constructive'
+ * @cfg {string|string[]} [flags] Styling flags, e.g. 'primary', 'destructive' or 'constructive'
  * @cfg {jQuery} [$flagged] Flagged node, assigned to #$flagged, omit to use #$element
  */
 OO.ui.FlaggedElement = function OoUiFlaggedElement( config ) {
@@ -4545,7 +4546,7 @@ OO.ui.FlaggedElement.prototype.hasFlag = function ( flag ) {
 /**
  * Get the names of all flags set.
  *
- * @return {string[]} flags Flag names
+ * @return {string[]} Flag names
  */
 OO.ui.FlaggedElement.prototype.getFlags = function () {
        return Object.keys( this.flags );
@@ -4658,7 +4659,8 @@ OO.ui.FlaggedElement.prototype.setFlags = function ( flags ) {
  * @constructor
  * @param {Object} [config] Configuration options
  * @cfg {jQuery} [$titled] Titled node, assigned to #$titled, omit to use #$element
- * @cfg {string|Function} [title] Title text or a function that returns text
+ * @cfg {string|Function} [title] Title text or a function that returns text. If not provided, the
+ *    static property 'title' is used.
  */
 OO.ui.TitledElement = function OoUiTitledElement( config ) {
        // Config intialization
@@ -6596,10 +6598,6 @@ OO.ui.BookletLayout.prototype.updateOutlineWidget = function () {
 /**
  * Layout made of a field and optional label.
  *
- * @class
- * @extends OO.ui.Layout
- * @mixins OO.ui.LabelElement
- *
  * Available label alignment modes include:
  *  - left: Label is before the field and aligned away from it, best for when the user will be
  *    scanning for a specific label in a form with many fields
@@ -6610,6 +6608,10 @@ OO.ui.BookletLayout.prototype.updateOutlineWidget = function () {
  *  - inline: Label is after the field and aligned toward it, best for small boolean fields like
  *    checkboxes or radio buttons
  *
+ * @class
+ * @extends OO.ui.Layout
+ * @mixins OO.ui.LabelElement
+ *
  * @constructor
  * @param {OO.ui.Widget} fieldWidget Field widget
  * @param {Object} [config] Configuration options
@@ -6668,6 +6670,10 @@ OO.ui.FieldLayout = function OoUiFieldLayout( fieldWidget, config ) {
 OO.inheritClass( OO.ui.FieldLayout, OO.ui.Layout );
 OO.mixinClass( OO.ui.FieldLayout, OO.ui.LabelElement );
 
+/* Static Properties */
+
+OO.ui.FieldLayout.static.tagName = 'label';
+
 /* Methods */
 
 /**
@@ -6701,6 +6707,7 @@ OO.ui.FieldLayout.prototype.getField = function () {
 /**
  * Set the field alignment mode.
  *
+ * @private
  * @param {string} value Alignment mode, either 'left', 'right', 'top' or 'inline'
  * @chainable
  */
@@ -6738,8 +6745,8 @@ OO.ui.FieldLayout.prototype.setAlignment = function ( value ) {
  *
  * @class
  * @extends OO.ui.Layout
- * @mixins OO.ui.LabelElement
  * @mixins OO.ui.IconElement
+ * @mixins OO.ui.LabelElement
  * @mixins OO.ui.GroupElement
  *
  * @constructor
@@ -7855,6 +7862,10 @@ OO.ui.ItemWidget.prototype.setElementGroup = function ( group ) {
  *
  * Subclasses must handle `select` and `choose` events on #lookupMenu to make use of selections.
  *
+ * Subclasses that set the value of #lookupInput from their `choose` or `select` handler should
+ * be aware that this will cause new suggestions to be looked up for the new value. If this is
+ * not desired, disable lookups with #setLookupsDisabled, then set the value, then re-enable lookups.
+ *
  * @class
  * @abstract
  *
@@ -7878,7 +7889,8 @@ OO.ui.LookupInputWidget = function OoUiLookupInputWidget( input, config ) {
        this.lookupCache = {};
        this.lookupQuery = null;
        this.lookupRequest = null;
-       this.populating = false;
+       this.lookupsDisabled = false;
+       this.lookupInputFocused = false;
 
        // Events
        this.lookupInput.$input.on( {
@@ -7887,6 +7899,7 @@ OO.ui.LookupInputWidget = function OoUiLookupInputWidget( input, config ) {
                mousedown: this.onLookupInputMouseDown.bind( this )
        } );
        this.lookupInput.connect( this, { change: 'onLookupInputChange' } );
+       this.lookupMenu.connect( this, { toggle: 'onLookupMenuToggle' } );
 
        // Initialization
        this.$element.addClass( 'oo-ui-lookupWidget' );
@@ -7902,7 +7915,8 @@ OO.ui.LookupInputWidget = function OoUiLookupInputWidget( input, config ) {
  * @param {jQuery.Event} e Input focus event
  */
 OO.ui.LookupInputWidget.prototype.onLookupInputFocus = function () {
-       this.openLookupMenu();
+       this.lookupInputFocused = true;
+       this.populateLookupMenu();
 };
 
 /**
@@ -7911,7 +7925,8 @@ OO.ui.LookupInputWidget.prototype.onLookupInputFocus = function () {
  * @param {jQuery.Event} e Input blur event
  */
 OO.ui.LookupInputWidget.prototype.onLookupInputBlur = function () {
-       this.lookupMenu.toggle( false );
+       this.closeLookupMenu();
+       this.lookupInputFocused = false;
 };
 
 /**
@@ -7920,7 +7935,13 @@ OO.ui.LookupInputWidget.prototype.onLookupInputBlur = function () {
  * @param {jQuery.Event} e Input mouse down event
  */
 OO.ui.LookupInputWidget.prototype.onLookupInputMouseDown = function () {
-       this.openLookupMenu();
+       // Only open the menu if the input was already focused.
+       // This way we allow the user to open the menu again after closing it with Esc
+       // by clicking in the input. Opening (and populating) the menu when initially
+       // clicking into the input is handled by the focus handler.
+       if ( this.lookupInputFocused ) {
+               this.openLookupMenu();
+       }
 };
 
 /**
@@ -7929,7 +7950,23 @@ OO.ui.LookupInputWidget.prototype.onLookupInputMouseDown = function () {
  * @param {string} value New input value
  */
 OO.ui.LookupInputWidget.prototype.onLookupInputChange = function () {
-       this.openLookupMenu();
+       if ( this.lookupInputFocused ) {
+               this.populateLookupMenu();
+       }
+};
+
+/**
+ * Handle the lookup menu being shown/hidden.
+ * @param {boolean} visible Whether the lookup menu is now visible.
+ */
+OO.ui.LookupInputWidget.prototype.onLookupMenuToggle = function ( visible ) {
+       if ( !visible ) {
+               // When the menu is hidden, abort any active request and clear the menu.
+               // This has to be done here in addition to closeLookupMenu(), because
+               // MenuWidget will close itself when the user presses Esc.
+               this.abortLookupRequest();
+               this.lookupMenu.clearItems();
+       }
 };
 
 /**
@@ -7942,35 +7979,61 @@ OO.ui.LookupInputWidget.prototype.getLookupMenu = function () {
 };
 
 /**
- * Open the menu.
+ * Disable or re-enable lookups.
+ *
+ * When lookups are disabled, calls to #populateLookupMenu will be ignored.
+ *
+ * @param {boolean} disabled Disable lookups
+ */
+OO.ui.LookupInputWidget.prototype.setLookupsDisabled = function ( disabled ) {
+       this.lookupsDisabled = !!disabled;
+};
+
+/**
+ * Open the menu. If there are no entries in the menu, this does nothing.
  *
  * @chainable
  */
 OO.ui.LookupInputWidget.prototype.openLookupMenu = function () {
-       var value = this.lookupInput.getValue();
-
-       if ( this.lookupMenu.$input.is( ':focus' ) && $.trim( value ) !== '' ) {
-               this.populateLookupMenu();
+       if ( !this.lookupMenu.isEmpty() ) {
                this.lookupMenu.toggle( true );
-       } else {
-               this.lookupMenu
-                       .clearItems()
-                       .toggle( false );
        }
+       return this;
+};
 
+/**
+ * Close the menu, empty it, and abort any pending request.
+ *
+ * @chainable
+ */
+OO.ui.LookupInputWidget.prototype.closeLookupMenu = function () {
+       this.lookupMenu.toggle( false );
+       this.abortLookupRequest();
+       this.lookupMenu.clearItems();
        return this;
 };
 
 /**
- * Populate lookup menu with current information.
+ * Request menu items based on the input's current value, and when they arrive,
+ * populate the menu with these items and show the menu.
+ *
+ * If lookups have been disabled with #setLookupsDisabled, this function does nothing.
  *
  * @chainable
  */
 OO.ui.LookupInputWidget.prototype.populateLookupMenu = function () {
-       var widget = this;
+       var widget = this,
+               value = this.lookupInput.getValue();
+
+       if ( this.lookupsDisabled ) {
+               return;
+       }
 
-       if ( !this.populating ) {
-               this.populating = true;
+       // If the input is empty, clear the menu
+       if ( value === '' ) {
+               this.closeLookupMenu();
+       // Skip population if there is already a request pending for the current value
+       } else if ( value !== this.lookupQuery ) {
                this.getLookupMenuItems()
                        .done( function ( items ) {
                                widget.lookupMenu.clearItems();
@@ -7979,15 +8042,12 @@ OO.ui.LookupInputWidget.prototype.populateLookupMenu = function () {
                                                .addItems( items )
                                                .toggle( true );
                                        widget.initializeLookupMenuSelection();
-                                       widget.openLookupMenu();
                                } else {
-                                       widget.lookupMenu.toggle( true );
+                                       widget.lookupMenu.toggle( false );
                                }
-                               widget.populating = false;
                        } )
                        .fail( function () {
                                widget.lookupMenu.clearItems();
-                               widget.populating = false;
                        } );
        }
 
@@ -7995,7 +8055,7 @@ OO.ui.LookupInputWidget.prototype.populateLookupMenu = function () {
 };
 
 /**
- * Set selection in the lookup menu with current information.
+ * Select and highlight the first selectable item in the menu.
  *
  * @chainable
  */
@@ -8010,50 +8070,74 @@ OO.ui.LookupInputWidget.prototype.initializeLookupMenuSelection = function () {
  * Get lookup menu items for the current query.
  *
  * @return {jQuery.Promise} Promise object which will be passed menu items as the first argument
- * of the done event
+ * of the done event. If the request was aborted to make way for a subsequent request,
+ * this promise will not be rejected: it will remain pending forever.
  */
 OO.ui.LookupInputWidget.prototype.getLookupMenuItems = function () {
        var widget = this,
                value = this.lookupInput.getValue(),
-               deferred = $.Deferred();
+               deferred = $.Deferred(),
+               ourRequest;
 
-       if ( value && value !== this.lookupQuery ) {
-               // Abort current request if query has changed
-               if ( this.lookupRequest ) {
-                       this.lookupRequest.abort();
-                       this.lookupQuery = null;
-                       this.lookupRequest = null;
-               }
-               if ( value in this.lookupCache ) {
-                       deferred.resolve( this.getLookupMenuItemsFromData( this.lookupCache[value] ) );
-               } else {
-                       this.lookupQuery = value;
-                       this.lookupRequest = this.getLookupRequest()
-                               .always( function () {
+       this.abortLookupRequest();
+       if ( value in this.lookupCache ) {
+               deferred.resolve( this.getLookupMenuItemsFromData( this.lookupCache[value] ) );
+       } else {
+               this.lookupInput.pushPending();
+               this.lookupQuery = value;
+               ourRequest = this.lookupRequest = this.getLookupRequest();
+               ourRequest
+                       .always( function () {
+                               // We need to pop pending even if this is an old request, otherwise
+                               // the widget will remain pending forever.
+                               // TODO: this assumes that an aborted request will fail or succeed soon after
+                               // being aborted, or at least eventually. It would be nice if we could popPending()
+                               // at abort time, but only if we knew that we hadn't already called popPending()
+                               // for that request.
+                               widget.lookupInput.popPending();
+                       } )
+                       .done( function ( data ) {
+                               // If this is an old request (and aborting it somehow caused it to still succeed),
+                               // ignore its success completely
+                               if ( ourRequest === widget.lookupRequest ) {
                                        widget.lookupQuery = null;
                                        widget.lookupRequest = null;
-                               } )
-                               .done( function ( data ) {
                                        widget.lookupCache[value] = widget.getLookupCacheItemFromData( data );
                                        deferred.resolve( widget.getLookupMenuItemsFromData( widget.lookupCache[value] ) );
-                               } )
-                               .fail( function () {
+                               }
+                       } )
+                       .fail( function () {
+                               // If this is an old request (or a request failing because it's being aborted),
+                               // ignore its failure completely
+                               if ( ourRequest === widget.lookupRequest ) {
+                                       widget.lookupQuery = null;
+                                       widget.lookupRequest = null;
                                        deferred.reject();
-                               } );
-                       this.pushPending();
-                       this.lookupRequest.always( function () {
-                               widget.popPending();
+                               }
                        } );
-               }
        }
        return deferred.promise();
 };
 
+/**
+ * Abort the currently pending lookup request, if any.
+ */
+OO.ui.LookupInputWidget.prototype.abortLookupRequest = function () {
+       var oldRequest = this.lookupRequest;
+       if ( oldRequest ) {
+               // First unset this.lookupRequest to the fail handler will notice
+               // that the request is no longer current
+               this.lookupRequest = null;
+               this.lookupQuery = null;
+               oldRequest.abort();
+       }
+};
+
 /**
  * Get a new request object of the current lookup query value.
  *
  * @abstract
- * @return {jqXHR} jQuery AJAX object, or promise object with an .abort() method
+ * @return {jQuery.Promise} jQuery AJAX object, or promise object with an .abort() method
  */
 OO.ui.LookupInputWidget.prototype.getLookupRequest = function () {
        // Stub, implemented in subclass
@@ -8061,26 +8145,25 @@ OO.ui.LookupInputWidget.prototype.getLookupRequest = function () {
 };
 
 /**
- * Handle successful lookup request.
- *
- * Overriding methods should call #populateLookupMenu when results are available and cache results
- * for future lookups in #lookupCache as an array of #OO.ui.MenuItemWidget objects.
+ * Get a list of menu item widgets from the data stored by the lookup request's done handler.
  *
  * @abstract
- * @param {Mixed} data Response from server
+ * @param {Mixed} data Cached result data, usually an array
+ * @return {OO.ui.MenuItemWidget[]} Menu items
  */
-OO.ui.LookupInputWidget.prototype.onLookupRequestDone = function () {
+OO.ui.LookupInputWidget.prototype.getLookupMenuItemsFromData = function () {
        // Stub, implemented in subclass
+       return [];
 };
 
 /**
- * Get a list of menu item widgets from the data stored by the lookup request's done handler.
+ * Get lookup cache item from server response data.
  *
  * @abstract
- * @param {Mixed} data Cached result data, usually an array
- * @return {OO.ui.MenuItemWidget[]} Menu items
+ * @param {Mixed} data Response from server
+ * @return {Mixed} Cached result data
  */
-OO.ui.LookupInputWidget.prototype.getLookupMenuItemsFromData = function () {
+OO.ui.LookupInputWidget.prototype.getLookupCacheItemFromData = function () {
        // Stub, implemented in subclass
        return [];
 };
@@ -8271,7 +8354,7 @@ OO.ui.ToggleWidget.prototype.setValue = function ( value ) {
  *
  * @constructor
  * @param {Object} [config] Configuration options
- * @cfg {OO.ui.ButtonWidget} [items] Buttons to add
+ * @cfg {OO.ui.ButtonWidget[]} [items] Buttons to add
  */
 OO.ui.ButtonGroupWidget = function OoUiButtonGroupWidget( config ) {
        // Parent constructor
@@ -8717,85 +8800,9 @@ OO.ui.ToggleButtonWidget.prototype.setValue = function ( value ) {
 };
 
 /**
- * Icon widget.
- *
- * See OO.ui.IconElement for more information.
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.IconElement
- * @mixins OO.ui.TitledElement
+ * Dropdown menu of options.
  *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.IconWidget = function OoUiIconWidget( config ) {
-       // Config intialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.IconWidget.super.call( this, config );
-
-       // Mixin constructors
-       OO.ui.IconElement.call( this, $.extend( {}, config, { $icon: this.$element } ) );
-       OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-iconWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.IconWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.IconWidget, OO.ui.IconElement );
-OO.mixinClass( OO.ui.IconWidget, OO.ui.TitledElement );
-
-/* Static Properties */
-
-OO.ui.IconWidget.static.tagName = 'span';
-
-/**
- * Indicator widget.
- *
- * See OO.ui.IndicatorElement for more information.
- *
- * @class
- * @extends OO.ui.Widget
- * @mixins OO.ui.IndicatorElement
- * @mixins OO.ui.TitledElement
- *
- * @constructor
- * @param {Object} [config] Configuration options
- */
-OO.ui.IndicatorWidget = function OoUiIndicatorWidget( config ) {
-       // Config intialization
-       config = config || {};
-
-       // Parent constructor
-       OO.ui.IndicatorWidget.super.call( this, config );
-
-       // Mixin constructors
-       OO.ui.IndicatorElement.call( this, $.extend( {}, config, { $indicator: this.$element } ) );
-       OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
-
-       // Initialization
-       this.$element.addClass( 'oo-ui-indicatorWidget' );
-};
-
-/* Setup */
-
-OO.inheritClass( OO.ui.IndicatorWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.IndicatorElement );
-OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.TitledElement );
-
-/* Static Properties */
-
-OO.ui.IndicatorWidget.static.tagName = 'span';
-
-/**
- * Inline menu of options.
- *
- * Inline menus provide a control for accessing a menu and compose a menu within the widget, which
+ * Dropdown menus provide a control for accessing a menu and compose a menu within the widget, which
  * can be accessed using the #getMenu method.
  *
  * Use with OO.ui.MenuItemWidget.
@@ -8811,12 +8818,12 @@ OO.ui.IndicatorWidget.static.tagName = 'span';
  * @param {Object} [config] Configuration options
  * @cfg {Object} [menu] Configuration options to pass to menu widget
  */
-OO.ui.InlineMenuWidget = function OoUiInlineMenuWidget( config ) {
+OO.ui.DropdownWidget = function OoUiDropdownWidget( config ) {
        // Configuration initialization
        config = $.extend( { indicator: 'down' }, config );
 
        // Parent constructor
-       OO.ui.InlineMenuWidget.super.call( this, config );
+       OO.ui.DropdownWidget.super.call( this, config );
 
        // Mixin constructors
        OO.ui.IconElement.call( this, config );
@@ -8834,20 +8841,20 @@ OO.ui.InlineMenuWidget = function OoUiInlineMenuWidget( config ) {
 
        // Initialization
        this.$handle
-               .addClass( 'oo-ui-inlineMenuWidget-handle' )
+               .addClass( 'oo-ui-dropdownWidget-handle' )
                .append( this.$icon, this.$label, this.$indicator );
        this.$element
-               .addClass( 'oo-ui-inlineMenuWidget' )
+               .addClass( 'oo-ui-dropdownWidget' )
                .append( this.$handle, this.menu.$element );
 };
 
 /* Setup */
 
-OO.inheritClass( OO.ui.InlineMenuWidget, OO.ui.Widget );
-OO.mixinClass( OO.ui.InlineMenuWidget, OO.ui.IconElement );
-OO.mixinClass( OO.ui.InlineMenuWidget, OO.ui.IndicatorElement );
-OO.mixinClass( OO.ui.InlineMenuWidget, OO.ui.LabelElement );
-OO.mixinClass( OO.ui.InlineMenuWidget, OO.ui.TitledElement );
+OO.inheritClass( OO.ui.DropdownWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.IconElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.IndicatorElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.LabelElement );
+OO.mixinClass( OO.ui.DropdownWidget, OO.ui.TitledElement );
 
 /* Methods */
 
@@ -8856,7 +8863,7 @@ OO.mixinClass( OO.ui.InlineMenuWidget, OO.ui.TitledElement );
  *
  * @return {OO.ui.MenuWidget} Menu of widget
  */
-OO.ui.InlineMenuWidget.prototype.getMenu = function () {
+OO.ui.DropdownWidget.prototype.getMenu = function () {
        return this.menu;
 };
 
@@ -8865,7 +8872,7 @@ OO.ui.InlineMenuWidget.prototype.getMenu = function () {
  *
  * @param {OO.ui.MenuItemWidget} item Selected menu item
  */
-OO.ui.InlineMenuWidget.prototype.onMenuSelect = function ( item ) {
+OO.ui.DropdownWidget.prototype.onMenuSelect = function ( item ) {
        var selectedLabel;
 
        if ( !item ) {
@@ -8887,7 +8894,7 @@ OO.ui.InlineMenuWidget.prototype.onMenuSelect = function ( item ) {
  *
  * @param {jQuery.Event} e Mouse click event
  */
-OO.ui.InlineMenuWidget.prototype.onClick = function ( e ) {
+OO.ui.DropdownWidget.prototype.onClick = function ( e ) {
        // Skip clicks within the menu
        if ( $.contains( this.menu.$element[0], e.target ) ) {
                return;
@@ -8903,6 +8910,82 @@ OO.ui.InlineMenuWidget.prototype.onClick = function ( e ) {
        return false;
 };
 
+/**
+ * Icon widget.
+ *
+ * See OO.ui.IconElement for more information.
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.IconElement
+ * @mixins OO.ui.TitledElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.IconWidget = function OoUiIconWidget( config ) {
+       // Config intialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.IconWidget.super.call( this, config );
+
+       // Mixin constructors
+       OO.ui.IconElement.call( this, $.extend( {}, config, { $icon: this.$element } ) );
+       OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-iconWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.IconWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.IconWidget, OO.ui.IconElement );
+OO.mixinClass( OO.ui.IconWidget, OO.ui.TitledElement );
+
+/* Static Properties */
+
+OO.ui.IconWidget.static.tagName = 'span';
+
+/**
+ * Indicator widget.
+ *
+ * See OO.ui.IndicatorElement for more information.
+ *
+ * @class
+ * @extends OO.ui.Widget
+ * @mixins OO.ui.IndicatorElement
+ * @mixins OO.ui.TitledElement
+ *
+ * @constructor
+ * @param {Object} [config] Configuration options
+ */
+OO.ui.IndicatorWidget = function OoUiIndicatorWidget( config ) {
+       // Config intialization
+       config = config || {};
+
+       // Parent constructor
+       OO.ui.IndicatorWidget.super.call( this, config );
+
+       // Mixin constructors
+       OO.ui.IndicatorElement.call( this, $.extend( {}, config, { $indicator: this.$element } ) );
+       OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$element } ) );
+
+       // Initialization
+       this.$element.addClass( 'oo-ui-indicatorWidget' );
+};
+
+/* Setup */
+
+OO.inheritClass( OO.ui.IndicatorWidget, OO.ui.Widget );
+OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.IndicatorElement );
+OO.mixinClass( OO.ui.IndicatorWidget, OO.ui.TitledElement );
+
+/* Static Properties */
+
+OO.ui.IndicatorWidget.static.tagName = 'span';
+
 /**
  * Base class for input widgets.
  *
@@ -8915,12 +8998,11 @@ OO.ui.InlineMenuWidget.prototype.onClick = function ( e ) {
  * @param {Object} [config] Configuration options
  * @cfg {string} [name=''] HTML input name
  * @cfg {string} [value=''] Input value
- * @cfg {boolean} [readOnly=false] Prevent changes
  * @cfg {Function} [inputFilter] Filter function to apply to the input. Takes a string argument and returns a string.
  */
 OO.ui.InputWidget = function OoUiInputWidget( config ) {
-       // Config intialization
-       config = $.extend( { readOnly: false }, config );
+       // Configuration initialization
+       config = config || {};
 
        // Parent constructor
        OO.ui.InputWidget.super.call( this, config );
@@ -8931,7 +9013,6 @@ OO.ui.InputWidget = function OoUiInputWidget( config ) {
        // Properties
        this.$input = this.getInputElement( config );
        this.value = '';
-       this.readOnly = false;
        this.inputFilter = config.inputFilter;
 
        // Events
@@ -8941,7 +9022,6 @@ OO.ui.InputWidget = function OoUiInputWidget( config ) {
        this.$input
                .attr( 'name', config.name )
                .prop( 'disabled', this.isDisabled() );
-       this.setReadOnly( config.readOnly );
        this.$element.addClass( 'oo-ui-inputWidget' ).append( this.$input );
        this.setValue( config.value );
 };
@@ -8963,6 +9043,7 @@ OO.mixinClass( OO.ui.InputWidget, OO.ui.FlaggedElement );
 /**
  * Get input element.
  *
+ * @private
  * @param {Object} [config] Configuration options
  * @return {jQuery} Input element
  */
@@ -9033,8 +9114,9 @@ OO.ui.InputWidget.prototype.setValue = function ( value ) {
 /**
  * Sanitize incoming value.
  *
- * Ensures value is a string, and converts undefined and null to empty strings.
+ * Ensures value is a string, and converts undefined and null to empty string.
  *
+ * @private
  * @param {string} value Original value
  * @return {string} Sanitized value
  */
@@ -9061,29 +9143,6 @@ OO.ui.InputWidget.prototype.simulateLabelClick = function () {
        }
 };
 
-/**
- * Check if the widget is read-only.
- *
- * @return {boolean}
- */
-OO.ui.InputWidget.prototype.isReadOnly = function () {
-       return this.readOnly;
-};
-
-/**
- * Set the read-only state of the widget.
- *
- * This should probably change the widgets's appearance and prevent it from being used.
- *
- * @param {boolean} state Make input read-only
- * @chainable
- */
-OO.ui.InputWidget.prototype.setReadOnly = function ( state ) {
-       this.readOnly = !!state;
-       this.$input.prop( 'readOnly', this.readOnly );
-       return this;
-};
-
 /**
  * @inheritdoc
  */
@@ -9116,7 +9175,7 @@ OO.ui.InputWidget.prototype.blur = function () {
 };
 
 /**
- * A button that is an input widget. Intended to be used within FormLayouts.
+ * A button that is an input widget. Intended to be used within a OO.ui.FormLayout.
  *
  * @class
  * @extends OO.ui.InputWidget
@@ -9131,14 +9190,17 @@ OO.ui.InputWidget.prototype.blur = function () {
  * @param {Object} [config] Configuration options
  * @cfg {string} [type='button'] HTML tag `type` attribute, may be 'button', 'submit' or 'reset'
  * @cfg {boolean} [useInputTag=false] Whether to use `<input/>` rather than `<button/>`. Only useful
- *  if you need IE 6 support in a form with multiple buttons. By using this option, you sacrifice
- *  icons and indicators, as well as the ability to have non-plaintext label or a label different
- *  from the value.
+ *  if you need IE 6 support in a form with multiple buttons. If you use this option, icons and
+ *  indicators will not be displayed, it won't be possible to have a non-plaintext label, and it
+ *  won't be possible to set a value (which will internally become identical to the label).
  */
 OO.ui.ButtonInputWidget = function OoUiButtonInputWidget( config ) {
        // Configuration initialization
        config = $.extend( { type: 'button', useInputTag: false }, config );
 
+       // Properties (must be set before parent constructor, which calls #setValue)
+       this.useInputTag = config.useInputTag;
+
        // Parent constructor
        OO.ui.ButtonInputWidget.super.call( this, config );
 
@@ -9150,9 +9212,6 @@ OO.ui.ButtonInputWidget = function OoUiButtonInputWidget( config ) {
        OO.ui.TitledElement.call( this, $.extend( {}, config, { $titled: this.$input } ) );
        OO.ui.FlaggedElement.call( this, config );
 
-       // Properties
-       this.useInputTag = config.useInputTag;
-
        // Events
        this.$input.on( {
                click: this.onClick.bind( this ),
@@ -9187,6 +9246,7 @@ OO.mixinClass( OO.ui.ButtonInputWidget, OO.ui.FlaggedElement );
 /**
  * Get input element.
  *
+ * @private
  * @param {Object} [config] Configuration options
  * @return {jQuery} Input element
  */
@@ -9196,7 +9256,7 @@ OO.ui.ButtonInputWidget.prototype.getInputElement = function ( config ) {
 };
 
 /**
- * Set the label.
+ * Set label value.
  *
  * Overridden to support setting the 'value' of `<input/>` elements.
  *
@@ -9223,6 +9283,21 @@ OO.ui.ButtonInputWidget.prototype.setLabel = function ( label ) {
        return this;
 };
 
+/**
+ * Set the value of the input.
+ *
+ * Overridden to disable for `<input/>` elements, which have value identical to the label.
+ *
+ * @param {string} value New value
+ * @chainable
+ */
+OO.ui.ButtonInputWidget.prototype.setValue = function ( value ) {
+       if ( !this.useInputTag ) {
+               OO.ui.ButtonInputWidget.super.prototype.setValue.call( this, value );
+       }
+       return this;
+};
+
 /**
  * Handles mouse click events.
  *
@@ -9275,6 +9350,7 @@ OO.inheritClass( OO.ui.CheckboxInputWidget, OO.ui.InputWidget );
 /**
  * Get input element.
  *
+ * @private
  * @return {jQuery} Input element
  */
 OO.ui.CheckboxInputWidget.prototype.getInputElement = function () {
@@ -9330,6 +9406,7 @@ OO.ui.CheckboxInputWidget.prototype.onEdit = function () {
  * @param {Object} [config] Configuration options
  * @cfg {string} [type='text'] HTML tag `type` attribute
  * @cfg {string} [placeholder] Placeholder text
+ * @cfg {boolean} [readOnly=false] Prevent changes
  * @cfg {boolean} [multiline=false] Allow multiple lines of text
  * @cfg {boolean} [autosize=false] Automatically resize to fit content
  * @cfg {boolean} [maxRows=10] Maximum number of rows to make visible when autosizing
@@ -9338,7 +9415,7 @@ OO.ui.CheckboxInputWidget.prototype.onEdit = function () {
  */
 OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
        // Configuration initialization
-       config = config || {};
+       config = $.extend( { readOnly: false }, config );
 
        // Parent constructor
        OO.ui.TextInputWidget.super.call( this, config );
@@ -9349,6 +9426,7 @@ OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
        OO.ui.PendingElement.call( this, config );
 
        // Properties
+       this.readOnly = false;
        this.multiline = !!config.multiline;
        this.autosize = !!config.autosize;
        this.maxRows = config.maxRows !== undefined ? config.maxRows : 10;
@@ -9369,6 +9447,7 @@ OO.ui.TextInputWidget = function OoUiTextInputWidget( config ) {
        this.$element
                .addClass( 'oo-ui-textInputWidget' )
                .append( this.$icon, this.$indicator );
+       this.setReadOnly( config.readOnly );
        if ( config.placeholder ) {
                this.$input.attr( 'placeholder', config.placeholder );
        }
@@ -9484,6 +9563,29 @@ OO.ui.TextInputWidget.prototype.setValue = function ( value ) {
        return this;
 };
 
+/**
+ * Check if the widget is read-only.
+ *
+ * @return {boolean}
+ */
+OO.ui.TextInputWidget.prototype.isReadOnly = function () {
+       return this.readOnly;
+};
+
+/**
+ * Set the read-only state of the widget.
+ *
+ * This should probably change the widgets's appearance and prevent it from being used.
+ *
+ * @param {boolean} state Make input read-only
+ * @chainable
+ */
+OO.ui.TextInputWidget.prototype.setReadOnly = function ( state ) {
+       this.readOnly = !!state;
+       this.$input.prop( 'readOnly', this.readOnly );
+       return this;
+};
+
 /**
  * Automatically adjust the size of the text input.
  *
@@ -9527,6 +9629,7 @@ OO.ui.TextInputWidget.prototype.adjustSize = function () {
 /**
  * Get input element.
  *
+ * @private
  * @param {Object} [config] Configuration options
  * @return {jQuery} Input element
  */
@@ -9703,6 +9806,8 @@ OO.ui.ComboBoxWidget.prototype.onMenuChoose = function ( item ) {
  * Handle menu item change events.
  */
 OO.ui.ComboBoxWidget.prototype.onMenuItemsChange = function () {
+       var match = this.menu.getItemFromData( this.input.getValue() );
+       this.menu.selectItem( match );
        this.$element.toggleClass( 'oo-ui-comboBoxWidget-empty', this.menu.isEmpty() );
 };
 
index c44831c..5d6a1d4 100644 (file)
@@ -5,32 +5,19 @@
 /**
  * Get the attributes of an element directy as a plain object.
  *
- * If there are more elements in the collection, like most jQuery get/read methods,
- * this method will use the first element in the collection.
+ * If there is more than one element in the collection, similar to most other jQuery getter methods,
+ * this will use the first element in the collection.
  *
- * In IE6, the `attributes` map of a node includes *all* allowed attributes
- * for an element (including those not set). Those will have values like
- * `undefined`, `null`, `0`, `false`, `""` or `"inherit"`.
- *
- * However there may be attributes genuinely set to one of those values, and there
- * is no way to distinguish between attributes set to that and those not set and
- * it being the default. If you need them, set `all` to `true`. They are filtered out
- * by default.
- *
- * @param {boolean} [all=false]
  * @return {Object}
  */
-jQuery.fn.getAttrs = function ( all ) {
-       var map = this[0].attributes,
+jQuery.fn.getAttrs = function () {
+       var i,
+               map = this[0].attributes,
                attrs = {},
-               len = map.length,
-               i, v;
+               len = map.length;
 
        for ( i = 0; i < len; i++ ) {
-               v = map[i].nodeValue;
-               if ( all || ( v && v !== 'inherit' ) ) {
-                       attrs[ map[i].nodeName ] = v;
-               }
+               attrs[ map[i].name ] = map[i].value;
        }
 
        return attrs;
index fb6982c..3a19e02 100644 (file)
         *         console.log( data );
         *     } );
         *
+        * Multiple values for a parameter can be specified using an array (since MW 1.25):
+        *
+        *     var api = new mw.Api();
+        *     api.get( {
+        *         action: 'query',
+        *         meta: [ 'userinfo', 'siteinfo' ] // same effect as 'userinfo|siteinfo'
+        *     } ).done ( function ( data ) {
+        *         console.log( data );
+        *     } );
+        *
         * @class
         *
         * @constructor
                                delete parameters.token;
                        }
 
+                       for ( key in parameters ) {
+                               if ( $.isArray( parameters[key] ) ) {
+                                       parameters[key] = parameters[key].join( '|' );
+                               }
+                       }
+
                        // If multipart/form-data has been requested and emulation is possible, emulate it
                        if (
                                ajaxOptions.type === 'POST' &&
old mode 100755 (executable)
new mode 100644 (file)
index ef95507..4b6c14e 100644 (file)
@@ -106,9 +106,10 @@ fieldset#mw-searchoptions div#mw-search-togglebox input {
 fieldset#mw-searchoptions table {
        float: left;
        margin-right: 3em;
+       border-collapse: collapse;
 }
 fieldset#mw-searchoptions table td {
-       padding-right: 1em;
+       padding: 0 1em 0 0;
        white-space: nowrap;
 }
 fieldset#mw-searchoptions div.divider {
index 4204c29..6868ab6 100644 (file)
                // the pseudo before element of the label after the checkbox now looks like a checkbox
                & + label {
                        cursor: pointer;
-                       margin: 0 .4em;
+                       margin: 0 0.4em;
 
                        &::before {
-                                               content: '';
-                                               position: absolute;
-                                               left: 0;
-                                               display: inline-block;
-                                               border-radius: @borderRadius;
-                                               margin-right: 18px;
-                                               width: @checkboxSize;
-                                               height: @checkboxSize;
-                                               background-color: #fff;
-                                               border: 1px solid grey;
-                                       }
+                               content: '';
+                               position: absolute;
+                               left: 0;
+                               border-radius: @borderRadius;
+                               width: @checkboxSize;
+                               height: @checkboxSize;
+                               background-color: #fff;
+                               border: 1px solid grey;
+                       }
                }
 
                // when the input is checked, style the label pseudo before element that followed as a checked checkbox
index 95b18a8..7ced42f 100644 (file)
         */
        NS_SPECIAL = -1,
 
+       /**
+        * @private
+        * @static
+        * @property NS_MEDIA
+        */
+       NS_MEDIA = -2,
+
+       /**
+        * @private
+        * @static
+        * @property NS_FILE
+        */
+       NS_FILE = 6,
+
+       /**
+        * @private
+        * @static
+        * @property FILENAME_MAX_BYTES
+        */
+       FILENAME_MAX_BYTES = 240,
+
+       /**
+        * @private
+        * @static
+        * @property TITLE_MAX_BYTES
+        */
+       TITLE_MAX_BYTES = 255,
+
        /**
         * Get the namespace id from a namespace name (either from the localized, canonical or alias
         * name).
                '|&#x[0-9A-Fa-f]+;'
        ),
 
+       // From MediaWikiTitleCodec.php#L225 @26fcab1f18c568a41
+       // "Clean up whitespace" in function MediaWikiTitleCodec::splitTitleString()
+       rWhitespace = /[ _\u0009\u00A0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\s]+/g,
+
+       /**
+        * Slightly modified from Flinfo. Credit goes to Lupo and Flominator.
+        * @private
+        * @static
+        * @property sanitationRules
+        */
+       sanitationRules = [
+               // "signature"
+               {
+                       pattern: /~{3}/g,
+                       replace: '',
+                       generalRule: true
+               },
+               // Space, underscore, tab, NBSP and other unusual spaces
+               {
+                       pattern: rWhitespace,
+                       replace: ' ',
+                       generalRule: true
+               },
+               // unicode bidi override characters: Implicit, Embeds, Overrides
+               {
+                       pattern: /[\u200E\u200F\u202A-\u202E]/g,
+                       replace: '',
+                       generalRule: true
+               },
+               // control characters
+               {
+                       pattern: /[\x00-\x1f\x7f]/g,
+                       replace: '',
+                       generalRule: true
+               },
+               // URL encoding (possibly)
+               {
+                       pattern: /%([0-9A-Fa-f]{2})/g,
+                       replace: '% $1',
+                       generalRule: true
+               },
+               // HTML-character-entities
+               {
+                       pattern: /&(([A-Za-z0-9\x80-\xff]+|#[0-9]+|#x[0-9A-Fa-f]+);)/g,
+                       replace: '& $1',
+                       generalRule: true
+               },
+               // slash, colon (not supported by file systems like NTFS/Windows, Mac OS 9 [:], ext4 [/])
+               {
+                       pattern: /[:\/#]/g,
+                       replace: '-',
+                       fileRule: true
+               },
+               // brackets, greater than
+               {
+                       pattern: /[\]\}>]/g,
+                       replace: ')',
+                       generalRule: true
+               },
+               // brackets, lower than
+               {
+                       pattern: /[\[\{<]/g,
+                       replace: '(',
+                       generalRule: true
+               },
+               // everything that wasn't covered yet
+               {
+                       pattern: new RegExp( rInvalid.source, 'g' ),
+                       replace: '-',
+                       generalRule: true
+               },
+               // directory structures
+               {
+                       pattern: /^(\.|\.\.|\.\/.*|\.\.\/.*|.*\/\.\/.*|.*\/\.\.\/.*|.*\/\.|.*\/\.\.)$/g,
+                       replace: '',
+                       generalRule: true
+               }
+       ],
+
        /**
         * Internal helper for #constructor and #newFromtext.
         *
                        return false;
                }
 
-               // Disallow titles exceeding the 255 byte size limit (size of underlying database field)
+               // Disallow titles exceeding the TITLE_MAX_BYTES byte size limit (size of underlying database field)
                // Except for special pages, e.g. [[Special:Block/Long name]]
                // Note: The PHP implementation also asserts that even in NS_SPECIAL, the title should
                // be less than 512 bytes.
-               if ( namespace !== NS_SPECIAL && $.byteLength( title ) > 255 ) {
+               if ( namespace !== NS_SPECIAL && $.byteLength( title ) > TITLE_MAX_BYTES ) {
                        return false;
                }
 
                }
        },
 
+       /**
+        * Sanitizes a string based on a rule set and a filter
+        *
+        * @private
+        * @static
+        * @method sanitize
+        * @param {string} s
+        * @param {Array} filter
+        * @return {string}
+        */
+       sanitize = function ( s, filter ) {
+               var i, ruleLength, rule, m, filterLength,
+                       rules = sanitationRules;
+
+               for ( i = 0, ruleLength = rules.length; i < ruleLength; ++i ) {
+                       rule = rules[i];
+                       for ( m = 0, filterLength = filter.length; m < filterLength; ++m ) {
+                               if ( rule[filter[m]] ) {
+                                       s = s.replace( rule.pattern, rule.replace );
+                               }
+                       }
+               }
+               return s;
+       },
+
+       /**
+        * Cuts a string to a specific byte length, assuming UTF-8
+        * or less, if the last character is a multi-byte one
+        *
+        * @private
+        * @static
+        * @method trimToByteLength
+        * @param {string} s
+        * @param {number} length
+        * @return {string}
+        */
+       trimToByteLength = function ( s, length ) {
+               var byteLength, chopOffChars, chopOffBytes;
+
+               // bytelength is always greater or equal to the length in characters
+               s = s.substr( 0, length );
+               while ( ( byteLength = $.byteLength( s ) ) > length ) {
+                       // Calculate how many characters can be safely removed
+                       // First, we need to know how many bytes the string exceeds the threshold
+                       chopOffBytes = byteLength - length;
+                       // A character in UTF-8 is at most 4 bytes
+                       // One character must be removed in any case because the
+                       // string is too long
+                       chopOffChars = Math.max( 1, Math.floor( chopOffBytes / 4 ) );
+                       s = s.substr( 0, s.length - chopOffChars );
+               }
+               return s;
+       },
+
+       /**
+        * Cuts a file name to a specific byte length
+        *
+        * @private
+        * @static
+        * @method trimFileNameToByteLength
+        * @param {string} name without extension
+        * @param {string} extension file extension
+        * @return {string} The full name, including extension
+        */
+       trimFileNameToByteLength = function ( name, extension ) {
+               // There is a special byte limit for file names and ... remember the dot
+               return trimToByteLength( name, FILENAME_MAX_BYTES - extension.length - 1 ) + '.' + extension;
+       },
+
        // Polyfill for ES5 Object.create
        createObject = Object.create || ( function () {
                return function ( o ) {
         * Constructor for Title objects with a null return instead of an exception for invalid titles.
         *
         * @static
-        * @method
         * @param {string} title
         * @param {number} [namespace=NS_MAIN] Default namespace
         * @return {mw.Title|null} A valid Title object or null if the title is invalid
                return t;
        };
 
+       /**
+        * Constructor for Title objects from user input altering that input to
+        * produce a title that MediaWiki will accept as legal
+        *
+        * @static
+        * @param {string} title
+        * @param {number} [defaultNamespace=NS_MAIN]
+        *  If given, will used as default namespace for the given title.
+        * @param {Object} [options] additional options
+        * @param {string} [options.fileExtension='']
+        *  If the title is about to be created for the Media or File namespace,
+        *  ensures the resulting Title has the correct extension. Useful, for example
+        *  on systems that predict the type by content-sniffing, not by file extension.
+        *  If different from empty string, `forUploading` is assumed.
+        * @param {boolean} [options.forUploading=true]
+        *  Makes sure that a file is uploadable under the title returned.
+        *  There are pages in the file namespace under which file upload is impossible.
+        *  Automatically assumed if the title is created in the Media namespace.
+        * @return {mw.Title|null} A valid Title object or null if the input cannot be turned into a valid title
+        */
+       Title.newFromUserInput = function ( title, defaultNamespace, options ) {
+               var namespace, m, id, ext, parts, normalizeExtension;
+
+               // defaultNamespace is optional; check whether options moves up
+               if ( arguments.length < 3 && $.type( defaultNamespace ) === 'object' ) {
+                       options = defaultNamespace;
+                       defaultNamespace = undefined;
+               }
+
+               // merge options into defaults
+               options = $.extend( {
+                       fileExtension: '',
+                       forUploading: true
+               }, options );
+
+               normalizeExtension = function ( extension ) {
+                       // Remove only trailing space (that is removed by MW anyway)
+                       extension = extension.toLowerCase().replace(/\s*$/, '');
+                       return extension;
+               };
+
+               namespace = defaultNamespace === undefined ? NS_MAIN : defaultNamespace;
+
+               // Normalise whitespace and remove duplicates
+               title = $.trim( title.replace( rWhitespace, ' ' ) );
+
+               // Process initial colon
+               if ( title !== '' && title.charAt( 0 ) === ':' ) {
+                       // Initial colon means main namespace instead of specified default
+                       namespace = NS_MAIN;
+                       title = title
+                               // Strip colon
+                               .substr( 1 )
+                               // Trim underscores
+                               .replace( rUnderscoreTrim, '' );
+               }
+
+               // Process namespace prefix (if any)
+               m = title.match( rSplit );
+               if ( m ) {
+                       id = getNsIdByName( m[1] );
+                       if ( id !== false ) {
+                               // Ordinary namespace
+                               namespace = id;
+                               title = m[2];
+                       }
+               }
+
+               if ( namespace === NS_MEDIA
+                       || ( ( options.forUploading || options.fileExtension ) && ( namespace === NS_FILE ) )
+               ) {
+
+                       title = sanitize( title, [ 'generalRule', 'fileRule' ] );
+
+                       // Operate on the file extension
+                       // Although it is possible having spaces between the name and the ".ext" this isn't nice for
+                       // operating systems hiding file extensions -> strip them later on
+                       parts = title.split( '.' );
+
+                       if ( parts.length > 1 ) {
+
+                               // Get the last part, which is supposed to be the file extension
+                               ext = parts.pop();
+
+                               // Does the supplied file name carry the desired file extension?
+                               if ( options.fileExtension
+                                       && normalizeExtension( ext ) !== normalizeExtension( options.fileExtension )
+                               ) {
+
+                                       // No, push back, whatever there was after the dot
+                                       parts.push( ext );
+
+                                       // And add the desired file extension later
+                                       ext = options.fileExtension;
+                               }
+
+                               // Remove whitespace of the name part (that W/O extension)
+                               title = $.trim( parts.join( '.' ) );
+
+                               // Cut, if too long and append file extension
+                               title = trimFileNameToByteLength( title, ext );
+
+                       } else {
+
+                               // Missing file extension
+                               title = $.trim( parts.join( '.' ) );
+
+                               if ( options.fileExtension ) {
+
+                                       // Cut, if too long and append the desired file extension
+                                       title = trimFileNameToByteLength( title, options.fileExtension );
+
+                               } else {
+
+                                       // Name has no file extension and a fallback wasn't provided either
+                                       return null;
+                               }
+                       }
+               } else {
+
+                       title = sanitize( title, [ 'generalRule' ] );
+
+                       // Cut titles exceeding the TITLE_MAX_BYTES byte size limit
+                       // (size of underlying database field)
+                       if ( namespace !== NS_SPECIAL ) {
+                               title = trimToByteLength( title, TITLE_MAX_BYTES );
+                       }
+               }
+
+               // Any remaining initial :s are illegal.
+               title = title.replace( /^\:+/, '' );
+
+               return Title.newFromText( title, namespace );
+       };
+
+       /**
+        * Sanitizes a file name as supplied by the user, originating in the user's file system
+        * so it is most likely a valid MediaWiki title and file name after processing.
+        * Returns null on fatal errors.
+        *
+        * @static
+        * @param {string} uncleanName The unclean file name including file extension but
+        *   without namespace
+        * @param {string} [fileExtension] the desired file extension
+        * @return {mw.Title|null} A valid Title object or null if the title is invalid
+        */
+       Title.newFromFileName = function ( uncleanName, fileExtension ) {
+
+               return Title.newFromUserInput( 'File:' + uncleanName, {
+                       fileExtension: fileExtension,
+                       forUploading: true
+               } );
+       };
+
        /**
         * Get the file title from an image element
         *
index 177367d..7fc5c42 100644 (file)
                                }, 1 );
 
                                // show an alert with this message
-                               return options.message;
+                               if ( $.isFunction( options.message ) ) {
+                                       return options.message();
+                               } else {
+                                       return options.message;
+                               }
                        }
                } ).on( showEventName, function () {
                        // Re-add onbeforeunload handler
index a709fe5..cfdb5a7 100644 (file)
                                 * @param {Function} [ready] Callback to execute when all dependencies are ready
                                 * @param {Function} [error] Callback to execute if one or more dependencies failed
                                 * @return {jQuery.Promise}
+                                * @since 1.23 this returns a promise
                                 */
                                using: function ( dependencies, ready, error ) {
                                        var deferred = $.Deferred();
index 3a06a02..a53cbcb 100644 (file)
                addPortletLink: function ( portlet, href, text, id, tooltip, accesskey, nextnode ) {
                        var $item, $link, $portlet, $ul;
 
-                       // Check if there's atleast 3 arguments to prevent a TypeError
+                       // Check if there's at least 3 arguments to prevent a TypeError
                        if ( arguments.length < 3 ) {
                                return null;
                        }
index 2e8fed4..7391afd 100644 (file)
@@ -112,4 +112,7 @@ $wgAutoloadClasses += array(
        # tests/phpunit/includes/site
        'SiteTest' => "$testDir/phpunit/includes/site/SiteTest.php",
        'TestSites' => "$testDir/phpunit/includes/site/TestSites.php",
+
+       # tests/phpunit/includes/specialpage
+       'SpecialPageTestHelper' => "$testDir/phpunit/includes/specialpage/SpecialPageTestHelper.php",
 );
diff --git a/tests/browser/Gemfile b/tests/browser/Gemfile
deleted file mode 100644 (file)
index 3be33b7..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ruby=ruby-2.1.2
-#ruby-gemset=core
-
-source "https://rubygems.org"
-
-gem "mediawiki_api"
-gem "mediawiki_selenium"
diff --git a/tests/browser/Gemfile.lock b/tests/browser/Gemfile.lock
deleted file mode 100644 (file)
index 1ea4eb5..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-GEM
-  remote: https://rubygems.org/
-  specs:
-    builder (3.2.2)
-    childprocess (0.5.3)
-      ffi (~> 1.0, >= 1.0.11)
-    cucumber (1.3.16)
-      builder (>= 2.1.2)
-      diff-lcs (>= 1.1.3)
-      gherkin (~> 2.12)
-      multi_json (>= 1.7.5, < 2.0)
-      multi_test (>= 0.1.1)
-    data_magic (0.19)
-      faker (>= 1.1.2)
-      yml_reader (>= 0.3)
-    diff-lcs (1.2.5)
-    domain_name (0.5.20)
-      unf (>= 0.0.5, < 1.0.0)
-    faker (1.4.3)
-      i18n (~> 0.5)
-    faraday (0.9.0)
-      multipart-post (>= 1.2, < 3)
-    faraday-cookie_jar (0.0.6)
-      faraday (>= 0.7.4)
-      http-cookie (~> 1.0.0)
-    ffi (1.9.3)
-    gherkin (2.12.2)
-      multi_json (~> 1.3)
-    headless (1.0.2)
-    http-cookie (1.0.2)
-      domain_name (~> 0.5)
-    i18n (0.6.11)
-    json (1.8.1)
-    mediawiki_api (0.2.1)
-      faraday (~> 0.9, >= 0.9.0)
-      faraday-cookie_jar (~> 0.0, >= 0.0.6)
-    mediawiki_selenium (0.3.2)
-      cucumber (~> 1.3, >= 1.3.10)
-      headless (~> 1.0, >= 1.0.1)
-      json (~> 1.8, >= 1.8.1)
-      mediawiki_api (~> 0.2, >= 0.2.1)
-      page-object (~> 1.0)
-      rest-client (~> 1.6, >= 1.6.7)
-      rspec-expectations (~> 2.14, >= 2.14.4)
-      syntax (~> 1.2, >= 1.2.0)
-    mime-types (2.3)
-    multi_json (1.10.1)
-    multi_test (0.1.1)
-    multipart-post (2.0.0)
-    netrc (0.7.7)
-    page-object (1.0.2)
-      page_navigation (>= 0.9)
-      selenium-webdriver (>= 2.42.0)
-      watir-webdriver (>= 0.6.9)
-    page_navigation (0.9)
-      data_magic (>= 0.14)
-    rest-client (1.7.2)
-      mime-types (>= 1.16, < 3.0)
-      netrc (~> 0.7)
-    rspec-expectations (2.99.2)
-      diff-lcs (>= 1.1.3, < 2.0)
-    rubyzip (1.1.6)
-    selenium-webdriver (2.42.0)
-      childprocess (>= 0.5.0)
-      multi_json (~> 1.0)
-      rubyzip (~> 1.0)
-      websocket (~> 1.0.4)
-    syntax (1.2.0)
-    unf (0.1.4)
-      unf_ext
-    unf_ext (0.0.6)
-    watir-webdriver (0.6.10)
-      selenium-webdriver (>= 2.18.0)
-    websocket (1.0.7)
-    yml_reader (0.3)
-
-PLATFORMS
-  ruby
-
-DEPENDENCIES
-  mediawiki_api
-  mediawiki_selenium
index 8bcfee0..95d3c40 100644 (file)
@@ -223,6 +223,8 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                        $this->db->ignoreErrors( false );
                }
 
+               DeferredUpdates::clearPendingUpdates();
+
                wfProfileOut( __METHOD__ );
        }
 
index 2fce6bf..678c89b 100644 (file)
@@ -34,7 +34,7 @@ class ImportTest extends MediaWikiLangTestCase {
                        }
                };
 
-               $importer = new WikiImporter( $source );
+               $importer = new WikiImporter( $source, ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) );
                $importer->setPageOutCallback( $callback );
                $importer->doImport();
 
@@ -45,7 +45,7 @@ class ImportTest extends MediaWikiLangTestCase {
                return array(
                        array(
                                <<< EOF
-<mediawiki>
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
        <page>
                <title>Test</title>
                <ns>0</ns>
@@ -59,10 +59,10 @@ class ImportTest extends MediaWikiLangTestCase {
                                <id>10</id>
                        </contributor>
                        <comment>Admin moved page [[Test]] to [[Test22]]</comment>
-                       <text xml:space="preserve" bytes="20">#REDIRECT [[Test22]]</text>
-                       <sha1>tq456o9x3abm7r9ozi6km8yrbbc56o6</sha1>
                        <model>wikitext</model>
                        <format>text/x-wiki</format>
+                       <text xml:space="preserve" bytes="20">#REDIRECT [[Test22]]</text>
+                       <sha1>tq456o9x3abm7r9ozi6km8yrbbc56o6</sha1>
                </revision>
        </page>
 </mediawiki>
@@ -72,7 +72,7 @@ EOF
                        ),
                        array(
                                <<< EOF
-<mediawiki>
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.9/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.9/ http://www.mediawiki.org/xml/export-0.9.xsd" version="0.9" xml:lang="en">
        <page>
                <title>Test</title>
                <ns>0</ns>
index 39822dc..754568d 100644 (file)
@@ -28,7 +28,7 @@ class TestUser {
 
        private function assertNotReal() {
                global $wgDBprefix;
-               if( $wgDBprefix !== MediaWikiTestCase::DB_PREFIX && $wgDBprefix !== MediaWikiTestCase::ORA_DB_PREFIX ) {
+               if ( $wgDBprefix !== MediaWikiTestCase::DB_PREFIX && $wgDBprefix !== MediaWikiTestCase::ORA_DB_PREFIX ) {
                        throw new MWException( "Can't create user on real database" );
                }
        }
@@ -114,9 +114,9 @@ class TestUser {
                $passwordFactory = $this->user->getPasswordFactory();
                $oldDefaultType = $passwordFactory->getDefaultType();
 
-                // B is salted MD5 (thus fast) ... we don't care about security here, this is test only
-               $passwordFactory->setDefaultType( 'B' ); // @TODO: Change this to A once that is fixed: https://gerrit.wikimedia.org/r/167523
-               $newPassword = $passwordFactory->newFromPlaintext( $password , $this->user->getPassword() );
+               // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
+               $passwordFactory->setDefaultType( 'A' );
+               $newPassword = $passwordFactory->newFromPlaintext( $password, $this->user->getPassword() );
 
                $change = false;
                if ( !$this->user->getPassword()->equals( $newPassword ) ) {
index 49c0108..6af1862 100644 (file)
@@ -650,10 +650,10 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
        public function testActionPermissions() {
                $this->setUserPerm( array( "createpage" ) );
                $this->setTitle( NS_MAIN, "test page" );
-               $this->title->mTitleProtection['pt_create_perm'] = '';
-               $this->title->mTitleProtection['pt_user'] = $this->user->getID();
-               $this->title->mTitleProtection['pt_expiry'] = wfGetDB( DB_SLAVE )->getInfinity();
-               $this->title->mTitleProtection['pt_reason'] = 'test';
+               $this->title->mTitleProtection['permission'] = '';
+               $this->title->mTitleProtection['user'] = $this->user->getID();
+               $this->title->mTitleProtection['expiry'] = wfGetDB( DB_SLAVE )->getInfinity();
+               $this->title->mTitleProtection['reason'] = 'test';
                $this->title->mCascadeRestriction = false;
 
                $this->assertEquals( array( array( 'titleprotected', 'Useruser', 'test' ) ),
@@ -661,7 +661,7 @@ class TitlePermissionTest extends MediaWikiLangTestCase {
                $this->assertEquals( false,
                        $this->title->userCan( 'create', $this->user ) );
 
-               $this->title->mTitleProtection['pt_create_perm'] = 'sysop';
+               $this->title->mTitleProtection['permission'] = 'editprotected';
                $this->setUserPerm( array( 'createpage', 'protect' ) );
                $this->assertEquals( array( array( 'titleprotected', 'Useruser', 'test' ) ),
                        $this->title->getUserPermissionsErrors( 'create', $this->user ) );
index 7f7945b..c011e9a 100644 (file)
@@ -55,8 +55,8 @@ class WikiPageTest extends MediaWikiLangTestCase {
        }
 
        /**
-        * @param Title $title
-        * @param string $model
+        * @param Title|string $title
+        * @param string|null $model
         * @return WikiPage
         */
        protected function newPage( $title, $model = null ) {
index 328d839..56130f1 100644 (file)
@@ -148,7 +148,7 @@ class ApiQueryContinueTest extends ApiQueryContinueTestBase {
                };
                // generator + 2 props
                $data = $this->query( $mk( 99, 99, 99 ), 1, 'G2P', false ) +
-                       array( 'batchcomplete' => '' );;
+                       array( 'batchcomplete' => '' );
                $this->checkC( $data, $mk( 1, 1, 1 ), 16, 'G2P-111' );
                $this->checkC( $data, $mk( 2, 2, 2 ), 9, 'G2P-222' );
                $this->checkC( $data, $mk( 3, 3, 3 ), 6, 'G2P-333' );
index 456266f..7657633 100644 (file)
@@ -169,12 +169,28 @@ class FormatJsonTest extends MediaWikiTestCase {
                $this->assertEquals( $value, $st->getValue() );
        }
 
+       /**
+        * Test data for testParseTryFixing.
+        *
+        * HHVM has a lenient json parser (yeah great idea right?) which allows
+        * trailing commas for array and object delarations among other things, so
+        * our JSON_ERROR_SYNTAX rescue block is not always triggered. It however
+        * isn't lenient in exactly the same ways as our TRY_FIXING mode, so the
+        * assertions in this test are a bit more complicated than they ideally
+        * would be:
+        *
+        * Optional third argument: true if hhvm parses the value without
+        * intervention, false otherwise. Defaults to true.
+        *
+        * Optional fourth argument: expected cannonical JSON serialization of HHVM
+        * parsed result. Defaults to the second argument's value.
+        */
        public static function provideParseTryFixing() {
                return array(
-                       array( "[,]", '[]' ),
-                       array( "[ , ]", '[]' ),
+                       array( "[,]", '[]', false ),
+                       array( "[ , ]", '[]', false ),
                        array( "[ , }", false ),
-                       array( '[1],', false ),
+                       array( '[1],', false, true, '[1]' ),
                        array( "[1,]", '[1]' ),
                        array( "[1\n,]", '[1]' ),
                        array( "[1,\n]", '[1]' ),
@@ -182,24 +198,43 @@ class FormatJsonTest extends MediaWikiTestCase {
                        array( "[1\n,\n]\n", '[1]' ),
                        array( '["a,",]', '["a,"]' ),
                        array( "[[1,]\n,[2,\n],[3\n,]]", '[[1],[2],[3]]' ),
-                       array( '[[1,],[2,],[3,]]', false ), // I wish we could parse this, but would need quote parsing
-                       array( '[1,,]', false ),
+                       // I wish we could parse this, but would need quote parsing
+                       array( '[[1,],[2,],[3,]]', false, true, '[[1],[2],[3]]' ),
+                       array( '[1,,]', false, false, '[1]' ),
                );
        }
 
        /**
         * @dataProvider provideParseTryFixing
         * @param string $value
-        * @param string|bool $expected
+        * @param string|bool $expected Expected result with Zend PHP
+        * @param bool $hhvmParses Will HHVM parse this value without TRY_FIXING?
+        * @param string|bool $expectedHHVM Expected result with HHVM if different
+        * from the PHP5 expectation
         */
-       public function testParseTryFixing( $value, $expected ) {
+       public function testParseTryFixing(
+               $value, $expected,
+               $hhvmParses = true, $expectedHHVM = null
+       ) {
+               // PHP5 results are always expected to have isGood() === false
+               $expectedGoodStatus = false;
+
+               if ( wfIsHHVM() ) {
+                       // Use HHVM specific expected result if provided
+                       $expected = ( $expectedHHVM === null ) ? $expected : $expectedHHVM;
+                       // If HHVM parses the value natively, expect isGood() === true
+                       $expectedGoodStatus = $hhvmParses;
+               }
+
                $st = FormatJson::parse( $value, FormatJson::TRY_FIXING );
                $this->assertType( 'Status', $st );
                if ( $expected === false ) {
-                       $this->assertFalse( $st->isOK() );
+                       $this->assertFalse( $st->isOK(), 'Expected isOK() == false' );
                } else {
-                       $this->assertFalse( $st->isGood() );
-                       $this->assertTrue( $st->isOK() );
+                       $this->assertSame( $expectedGoodStatus, $st->isGood(),
+                               'Expected isGood() == ' . ( $expectedGoodStatus ? 'true' : 'false' )
+                       );
+                       $this->assertTrue( $st->isOK(), 'Expected isOK == true' );
                        $val = FormatJson::encode( $st->getValue(), false, FormatJson::ALL_OK );
                        $this->assertEquals( $expected, $val );
                }
index cb12273..fd6911f 100644 (file)
@@ -52,12 +52,23 @@ class SpecialPageFactoryTest extends MediaWikiTestCase {
        }
 
        public function specialPageProvider() {
+               $specialPageTestHelper = new SpecialPageTestHelper();
+
                return array(
                        'class name' => array( 'SpecialAllPages', false ),
                        'closure' => array( function () {
                                return new SpecialAllPages();
                        }, false ),
                        'function' => array( array( $this, 'newSpecialAllPages' ), false ),
+                       'callback string' => array( 'SpecialPageTestHelper::newSpecialAllPages', false ),
+                       'callback with object' => array(
+                               array( $specialPageTestHelper, 'newSpecialAllPages' ),
+                               false
+                       ),
+                       'callback array' => array(
+                               array( 'SpecialPageTestHelper', 'newSpecialAllPages' ),
+                               false
+                       )
                );
        }
 
diff --git a/tests/phpunit/includes/specialpage/SpecialPageTestHelper.php b/tests/phpunit/includes/specialpage/SpecialPageTestHelper.php
new file mode 100644 (file)
index 0000000..37e29dc
--- /dev/null
@@ -0,0 +1,24 @@
+<?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
+ */
+class SpecialPageTestHelper {
+
+       public static function newSpecialAllPages() {
+               return new SpecialAllPages();
+       }
+
+}
index 8b6aef5..415e11b 100644 (file)
@@ -330,6 +330,12 @@ abstract class DumpTestCase extends MediaWikiLangTestCase {
                $this->assertTextNode( "comment", $summary );
                $this->skipWhitespace();
 
+               $this->assertTextNode( "model", $model );
+               $this->skipWhitespace();
+
+               $this->assertTextNode( "format", $format );
+               $this->skipWhitespace();
+
                if ( $this->xml->name == "text" ) {
                        // note: <text> tag may occur here or at the very end.
                        $text_found = true;
@@ -340,12 +346,6 @@ abstract class DumpTestCase extends MediaWikiLangTestCase {
 
                $this->assertTextNode( "sha1", $text_sha1 );
 
-               $this->assertTextNode( "model", $model );
-               $this->skipWhitespace();
-
-               $this->assertTextNode( "format", $format );
-               $this->skipWhitespace();
-
                if ( !$text_found ) {
                        $this->assertText( $id, $text_id, $text_bytes, $text );
                }
index a37a97c..e620b08 100644 (file)
@@ -440,10 +440,10 @@ class TextPassDumperTest extends DumpTestCase {
                if ( $fname === null ) {
                        $fname = $this->getNewTempFile();
                }
-               $header = '<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.7/" '
+               $header = '<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" '
                        . 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
-                       . 'xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.7/ '
-                       . 'http://www.mediawiki.org/xml/export-0.7.xsd" version="0.7" xml:lang="en">
+                       . 'xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ '
+                       . 'http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
   <siteinfo>
     <sitename>wikisvn</sitename>
     <base>http://localhost/wiki-svn/index.php/Main_Page</base>
@@ -489,10 +489,10 @@ class TextPassDumperTest extends DumpTestCase {
         <ip>127.0.0.1</ip>
       </contributor>
       <comment>BackupDumperTestP1Summary1</comment>
-      <sha1>0bolhl6ol7i6x0e7yq91gxgaan39j87</sha1>
       <model>wikitext</model>
       <format>text/x-wiki</format>
       <text id="' . $this->textId1_1 . '" bytes="23" />
+      <sha1>0bolhl6ol7i6x0e7yq91gxgaan39j87</sha1>
     </revision>
   </page>
 ';
@@ -507,10 +507,10 @@ class TextPassDumperTest extends DumpTestCase {
         <ip>127.0.0.1</ip>
       </contributor>
       <comment>BackupDumperTestP2Summary1</comment>
-      <sha1>jprywrymfhysqllua29tj3sc7z39dl2</sha1>
       <model>wikitext</model>
       <format>text/x-wiki</format>
       <text id="' . $this->textId2_1 . '" bytes="23" />
+      <sha1>jprywrymfhysqllua29tj3sc7z39dl2</sha1>
     </revision>
     <revision>
       <id>' . ( $this->revId2_2 + $i * self::$numOfRevs ) . '</id>
@@ -520,10 +520,10 @@ class TextPassDumperTest extends DumpTestCase {
         <ip>127.0.0.1</ip>
       </contributor>
       <comment>BackupDumperTestP2Summary2</comment>
-      <sha1>b7vj5ks32po5m1z1t1br4o7scdwwy95</sha1>
       <model>wikitext</model>
       <format>text/x-wiki</format>
       <text id="' . $this->textId2_2 . '" bytes="23" />
+      <sha1>b7vj5ks32po5m1z1t1br4o7scdwwy95</sha1>
     </revision>
     <revision>
       <id>' . ( $this->revId2_3 + $i * self::$numOfRevs ) . '</id>
@@ -533,10 +533,10 @@ class TextPassDumperTest extends DumpTestCase {
         <ip>127.0.0.1</ip>
       </contributor>
       <comment>BackupDumperTestP2Summary3</comment>
-      <sha1>jfunqmh1ssfb8rs43r19w98k28gg56r</sha1>
       <model>wikitext</model>
       <format>text/x-wiki</format>
       <text id="' . $this->textId2_3 . '" bytes="23" />
+      <sha1>jfunqmh1ssfb8rs43r19w98k28gg56r</sha1>
     </revision>
     <revision>
       <id>' . ( $this->revId2_4 + $i * self::$numOfRevs ) . '</id>
@@ -546,10 +546,10 @@ class TextPassDumperTest extends DumpTestCase {
         <ip>127.0.0.1</ip>
       </contributor>
       <comment>BackupDumperTestP2Summary4 extra</comment>
-      <sha1>6o1ciaxa6pybnqprmungwofc4lv00wv</sha1>
       <model>wikitext</model>
       <format>text/x-wiki</format>
       <text id="' . $this->textId2_4 . '" bytes="44" />
+      <sha1>6o1ciaxa6pybnqprmungwofc4lv00wv</sha1>
     </revision>
   </page>
 ';
@@ -566,10 +566,10 @@ class TextPassDumperTest extends DumpTestCase {
         <ip>127.0.0.1</ip>
       </contributor>
       <comment>Talk BackupDumperTestP1 Summary1</comment>
-      <sha1>nktofwzd0tl192k3zfepmlzxoax1lpe</sha1>
       <model>wikitext</model>
       <format>text/x-wiki</format>
       <text id="' . $this->textId4_1 . '" bytes="35" />
+      <sha1>nktofwzd0tl192k3zfepmlzxoax1lpe</sha1>
     </revision>
   </page>
 ';
index 0b7e87e..ca3f418 100644 (file)
@@ -1,13 +1,14 @@
 ( function ( $ ) {
        QUnit.module( 'jquery.getAttrs', QUnit.newMwEnvironment() );
 
-       QUnit.test( 'Check', 1, function ( assert ) {
+       QUnit.test( 'getAttrs()', 1, function ( assert ) {
                var attrs = {
                                foo: 'bar',
-                               'class': 'lorem'
+                               'class': 'lorem',
+                               'data-foo': 'data value'
                        },
                        $el = $( '<div>' ).attr( attrs );
 
-               assert.deepEqual( $el.getAttrs(), attrs, 'getAttrs() return object should match the attributes set, no more, no less' );
+               assert.propEqual( $el.getAttrs(), attrs, 'keys and values match' );
        } );
 }( jQuery ) );
index 1d5656e..b89526f 100644 (file)
 
                this.server.respond( function ( request ) {
                        if ( window.FormData ) {
-                               assert.ok( !request.url.match( /action=/), 'Request has no query string' );
+                               assert.ok( !request.url.match( /action=/ ), 'Request has no query string' );
                                assert.ok( request.requestBody instanceof FormData, 'Request uses FormData body' );
                        } else {
-                               assert.ok( !request.url.match( /action=test/), 'Request has no query string' );
+                               assert.ok( !request.url.match( /action=test/ ), 'Request has no query string' );
                                assert.equal( request.requestBody, 'action=test&format=json', 'Request uses query string body' );
                        }
                        request.respond( 200, { 'Content-Type': 'application/json' }, '[]' );
                } );
        } );
 
+       QUnit.test( 'Converting arrays to pipe-separated', function ( assert ) {
+               QUnit.expect( 1 );
+
+               var api = new mw.Api();
+               api.get( { test: [ 'foo', 'bar', 'baz' ] } );
+
+               this.server.respond( function ( request ) {
+                       assert.ok( request.url.match( /test=foo%7Cbar%7Cbaz/ ), 'Pipe-separated value was submitted' );
+                       request.respond( 200, { 'Content-Type': 'application/json' }, '[]' );
+               } );
+       } );
+
        QUnit.test( 'getToken( pre-populated )', function ( assert ) {
                QUnit.expect( 2 );
 
index 5ece31b..7ab309a 100644 (file)
                        assert.equal( title.getRelativeText( thisCase.relativeTo ), thisCase.expectedResult );
                }
        } );
+
+       QUnit.test( 'newFromUserInput', 8, function ( assert ) {
+               var title, i, thisCase, prefix,
+                       cases = [
+                               {
+                                       title: 'DCS0001557854455.JPG',
+                                       defaultNamespace: 0,
+                                       options: {
+                                               fileExtension: 'PNG'
+                                       },
+                                       expected: 'DCS0001557854455.JPG',
+                                       description: 'Title in normal namespace without anything invalid but with "file extension"'
+                               },
+                               {
+                                       title: 'MediaWiki:Msg-awesome',
+                                       defaultNamespace: undefined,
+                                       expected: 'MediaWiki:Msg-awesome',
+                                       description: 'Full title (page in MediaWiki namespace) supplied as string'
+                               },
+                               {
+                                       title: 'The/Mw/Sound.flac',
+                                       defaultNamespace: -2,
+                                       expected: 'Media:The-Mw-Sound.flac',
+                                       description: 'Page in Media-namespace without explicit options'
+                               },
+                               {
+                                       title: 'File:The/Mw/Sound.kml',
+                                       defaultNamespace: 6,
+                                       options: {
+                                               forUploading: false
+                                       },
+                                       expected: 'File:The/Mw/Sound.kml',
+                                       description: 'Page in File-namespace without explicit options'
+                               }
+                       ];
+
+               for ( i = 0; i < cases.length; i++ ) {
+                       thisCase = cases[i];
+                       title = mw.Title.newFromUserInput( thisCase.title, thisCase.defaultNamespace, thisCase.options );
+
+                       if ( thisCase.expected !== undefined ) {
+                               prefix = '[' + thisCase.description + '] ';
+
+                               assert.notStrictEqual( title, null, prefix + 'Parses successfully' );
+                               assert.equal( title.toText(), thisCase.expected, prefix + 'Title as expected' );
+                       } else {
+                               assert.strictEqual( title, null, thisCase.description + ', should not produce an mw.Title object' );
+                       }
+               }
+       } );
+
+       QUnit.test( 'newFromFileName', 62, function ( assert ) {
+               var title, i, thisCase, prefix,
+                       cases = [
+                               {
+                                       fileName: 'DCS0001557854455.JPG',
+                                       typeOfName: 'Standard camera output',
+                                       nameText: 'DCS0001557854455',
+                                       prefixedText: 'File:DCS0001557854455.JPG',
+                                       extensionDesired: 'jpg'
+                               },
+                               {
+                                       fileName: 'File:Sample.png',
+                                       typeOfName: 'Carrying namespace',
+                                       nameText: 'File-Sample',
+                                       prefixedText: 'File:File-Sample.png'
+                               },
+                               {
+                                       fileName: 'Treppe 2222 Test upload.jpg',
+                                       typeOfName: 'File name with spaces in it and lower case file extension',
+                                       nameText: 'Treppe 2222 Test upload',
+                                       prefixedText: 'File:Treppe 2222 Test upload.jpg',
+                                       extensionDesired: 'JPG'
+                               },
+                               {
+                                       fileName: 'I contain a \ttab.jpg',
+                                       typeOfName: 'Name containing a tab character',
+                                       nameText: 'I contain a tab',
+                                       prefixedText: 'File:I contain a tab.jpg'
+                               },
+                               {
+                                       fileName: 'I_contain multiple__ ___ _underscores.jpg',
+                                       typeOfName: 'Name containing multiple underscores',
+                                       nameText: 'I contain multiple underscores',
+                                       prefixedText: 'File:I contain multiple underscores.jpg'
+                               },
+                               {
+                                       fileName: 'I like ~~~~~~~~es.jpg',
+                                       typeOfName: 'Name containing more than three consecutive tilde characters',
+                                       nameText: 'I like ~~es',
+                                       prefixedText: 'File:I like ~~es.jpg'
+                               },
+                               {
+                                       fileName: 'BI\u200EDI.jpg',
+                                       typeOfName: 'Name containing BIDI overrides',
+                                       nameText: 'BIDI',
+                                       prefixedText: 'File:BIDI.jpg'
+                               },
+                               {
+                                       fileName: '100%ab progress.jpg',
+                                       typeOfName: 'File name with URL encoding',
+                                       nameText: '100% ab progress',
+                                       prefixedText: 'File:100% ab progress.jpg'
+                               },
+                               {
+                                       fileName: '<([>]):/#.jpg',
+                                       typeOfName: 'File name with characters not permitted in titles that are replaced',
+                                       nameText: '((()))---',
+                                       prefixedText: 'File:((()))---.jpg'
+                               },
+                               {
+                                       fileName: 'spaces\u0009\u2000\u200A\u200Bx.djvu',
+                                       typeOfName: 'File name with different kind of spaces',
+                                       nameText: 'Spaces \u200Bx',
+                                       prefixedText: 'File:Spaces \u200Bx.djvu'
+                               },
+                               {
+                                       fileName: 'dot.dot.dot.dot.dotdot',
+                                       typeOfName: 'File name with a lot of dots',
+                                       nameText: 'Dot.dot.dot.dot',
+                                       prefixedText: 'File:Dot.dot.dot.dot.dotdot'
+                               },
+                               {
+                                       fileName: 'dot. dot ._dot',
+                                       typeOfName: 'File name with multiple dots and spaces',
+                                       nameText: 'Dot. dot',
+                                       prefixedText: 'File:Dot. dot. dot'
+                               },
+                               {
+                                       fileName: 'dot. dot ._dot',
+                                       typeOfName: 'File name with different file extension desired',
+                                       nameText: 'Dot. dot . dot',
+                                       prefixedText: 'File:Dot. dot . dot.png',
+                                       extensionDesired: 'png'
+                               },
+                               {
+                                       fileName: 'fileWOExt',
+                                       typeOfName: 'File W/O extension with extension desired',
+                                       nameText: 'FileWOExt',
+                                       prefixedText: 'File:FileWOExt.png',
+                                       extensionDesired: 'png'
+                               },
+                               {
+                                       fileName: '𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼𠵿𠸎𠸏𠹷𠺝𠺢𠻗𠻹𠻺𠼭𠼮𠽌𠾴𠾼𠿪𡁜𡁯𡁵𡁶𡁻𡃁𡃉𡇙𢃇𢞵𢫕𢭃𢯊𢱑𢱕𢳂𠻹𠻺𠼭𠼮𠽌𠾴𠾼𠿪𡁜𡁯𡁵𡁶𡁻𡃁𡃉𡇙𢃇𢞵𢫕𢭃𢯊𢱑𢱕𢳂.png',
+                                       typeOfName: 'File name longer than 240 bytes',
+                                       nameText: '𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼𠵿𠸎𠸏𠹷𠺝𠺢𠻗𠻹𠻺𠼭𠼮𠽌𠾴𠾼𠿪𡁜𡁯𡁵𡁶𡁻𡃁𡃉𡇙𢃇𢞵𢫕𢭃𢯊𢱑𢱕𢳂𠻹𠻺𠼭𠼮𠽌𠾴𠾼𠿪𡁜𡁯𡁵𡁶𡁻𡃁𡃉𡇙𢃇𢞵',
+                                       prefixedText: 'File:𠜎𠜱𠝹𠱓𠱸𠲖𠳏𠳕𠴕𠵼𠵿𠸎𠸏𠹷𠺝𠺢𠻗𠻹𠻺𠼭𠼮𠽌𠾴𠾼𠿪𡁜𡁯𡁵𡁶𡁻𡃁𡃉𡇙𢃇𢞵𢫕𢭃𢯊𢱑𢱕𢳂𠻹𠻺𠼭𠼮𠽌𠾴𠾼𠿪𡁜𡁯𡁵𡁶𡁻𡃁𡃉𡇙𢃇𢞵.png'
+                               },
+                               {
+                                       fileName: '',
+                                       typeOfName: 'Empty string'
+                               },
+                               {
+                                       fileName: 'foo',
+                                       typeOfName: 'String with only alphabet characters'
+                               }
+                       ];
+
+               for ( i = 0; i < cases.length; i++ ) {
+                       thisCase = cases[i];
+                       title = mw.Title.newFromFileName( thisCase.fileName, thisCase.extensionDesired );
+
+                       if ( thisCase.nameText !== undefined ) {
+                               prefix = '[' + thisCase.typeOfName + '] ';
+
+                               assert.notStrictEqual( title, null, prefix + 'Parses successfully' );
+                               assert.equal( title.getNameText(), thisCase.nameText, prefix + 'Filename matches original' );
+                               assert.equal( title.getPrefixedText(), thisCase.prefixedText, prefix + 'File page title matches original' );
+                               assert.equal( title.getNamespaceId(), 6, prefix + 'Namespace ID matches File namespace' );
+                       } else {
+                               assert.strictEqual( title, null, thisCase.typeOfName + ', should not produce an mw.Title object' );
+                       }
+               }
+       } );
+
 }( mediaWiki, jQuery ) );