Merge "Fix some FSFileBackend IDEA errors"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Thu, 31 Dec 2015 01:18:46 +0000 (01:18 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Thu, 31 Dec 2015 01:18:46 +0000 (01:18 +0000)
104 files changed:
autoload.php
docs/extension.schema.json
includes/AjaxResponse.php
includes/GlobalFunctions.php
includes/Import.php [deleted file]
includes/OutputPage.php
includes/api/i18n/ce.json
includes/api/i18n/de.json
includes/api/i18n/hu.json
includes/api/i18n/qqq.json
includes/api/i18n/tt-cyrl.json [new file with mode: 0644]
includes/cache/HTMLFileCache.php
includes/db/Database.php
includes/debug/MWDebug.php
includes/debug/logger/LegacyLogger.php
includes/export/Dump7ZipOutput.php
includes/export/DumpBZip2Output.php
includes/export/DumpFileOutput.php
includes/export/DumpFilter.php
includes/export/DumpGZipOutput.php
includes/export/DumpLatestFilter.php
includes/export/DumpMultiWriter.php
includes/export/DumpNamespaceFilter.php
includes/export/DumpNotalkFilter.php
includes/export/DumpOutput.php
includes/export/DumpPipeOutput.php
includes/export/WikiExporter.php
includes/filebackend/FileOpBatch.php
includes/import/ImportSource.php [new file with mode: 0644]
includes/import/ImportStreamSource.php [new file with mode: 0644]
includes/import/ImportStringSource.php [new file with mode: 0644]
includes/import/UploadSourceAdapter.php [new file with mode: 0644]
includes/import/WikiImporter.php [new file with mode: 0644]
includes/import/WikiRevision.php [new file with mode: 0644]
includes/installer/i18n/ce.json
includes/installer/i18n/wuu.json
includes/registration/ExtensionProcessor.php
includes/registration/ExtensionRegistry.php
includes/registration/Processor.php
includes/specials/SpecialBlock.php
includes/specials/SpecialContributions.php
includes/specials/SpecialDeletedContributions.php
includes/specials/SpecialEmailuser.php
includes/specials/SpecialListfiles.php
includes/specials/SpecialUnblock.php
includes/specials/SpecialUserrights.php
includes/user/UserNamePrefixSearch.php [new file with mode: 0644]
languages/i18n/azb.json
languages/i18n/be-tarask.json
languages/i18n/ca.json
languages/i18n/ce.json
languages/i18n/cs.json
languages/i18n/de.json
languages/i18n/es.json
languages/i18n/fa.json
languages/i18n/he.json
languages/i18n/hu.json
languages/i18n/is.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/lb.json
languages/i18n/pam.json
languages/i18n/pl.json
languages/i18n/sa.json
languages/i18n/sd.json
languages/i18n/sl.json
languages/i18n/sv.json
languages/i18n/tt-cyrl.json
languages/i18n/ur.json
languages/i18n/wuu.json
maintenance/Maintenance.php
maintenance/cleanupImages.php
maintenance/cleanupPreferences.php
maintenance/cleanupSpam.php
maintenance/deleteArchivedFiles.php
maintenance/deleteOldRevisions.php
maintenance/deleteOrphanedRevisions.php
maintenance/deleteSelfExternals.php
maintenance/migrateUserGroup.php
maintenance/moveBatch.php
maintenance/nukeNS.php
maintenance/nukePage.php
maintenance/populateLogUsertext.php
maintenance/populateRevisionLength.php
maintenance/populateRevisionSha1.php
maintenance/reassignEdits.php
maintenance/rebuildFileCache.php
maintenance/storage/compressOld.php
maintenance/storage/fixBug20757.php
maintenance/storage/recompressTracked.php
maintenance/updateCollation.php
maintenance/wrapOldPasswords.php
resources/src/mediawiki.less/mediawiki.ui/variables.less
resources/src/mediawiki.ui/components/anchors.less
resources/src/mediawiki.ui/components/buttons.less
resources/src/mediawiki.ui/components/checkbox.less
resources/src/mediawiki.ui/components/forms.less
resources/src/mediawiki.ui/components/icons.less
resources/src/mediawiki.ui/components/inputs.less
resources/src/mediawiki.ui/components/radio.less
resources/src/mediawiki.ui/components/text.less
tests/parser/parserTests.txt
tests/phpunit/includes/registration/ExtensionRegistryTest.php
tests/phpunit/maintenance/MaintenanceTest.php

index 4d7ed5b..ac38fa5 100644 (file)
@@ -571,9 +571,9 @@ $wgAutoloadLocalClasses = array(
        'ImportReporter' => __DIR__ . '/includes/specials/SpecialImport.php',
        'ImportSiteScripts' => __DIR__ . '/maintenance/importSiteScripts.php',
        'ImportSites' => __DIR__ . '/maintenance/importSites.php',
-       'ImportSource' => __DIR__ . '/includes/Import.php',
-       'ImportStreamSource' => __DIR__ . '/includes/Import.php',
-       'ImportStringSource' => __DIR__ . '/includes/Import.php',
+       'ImportSource' => __DIR__ . '/includes/import/ImportSource.php',
+       'ImportStreamSource' => __DIR__ . '/includes/import/ImportStreamSource.php',
+       'ImportStringSource' => __DIR__ . '/includes/import/ImportStringSource.php',
        'ImportTitleFactory' => __DIR__ . '/includes/title/ImportTitleFactory.php',
        'IncludableSpecialPage' => __DIR__ . '/includes/specialpage/IncludableSpecialPage.php',
        'IndexPager' => __DIR__ . '/includes/pager/IndexPager.php',
@@ -1314,7 +1314,7 @@ $wgAutoloadLocalClasses = array(
        'UploadFromUrl' => __DIR__ . '/includes/upload/UploadFromUrl.php',
        'UploadFromUrlJob' => __DIR__ . '/includes/jobqueue/jobs/UploadFromUrlJob.php',
        'UploadLogFormatter' => __DIR__ . '/includes/logging/UploadLogFormatter.php',
-       'UploadSourceAdapter' => __DIR__ . '/includes/Import.php',
+       'UploadSourceAdapter' => __DIR__ . '/includes/import/UploadSourceAdapter.php',
        'UploadSourceField' => __DIR__ . '/includes/specials/SpecialUpload.php',
        'UploadStash' => __DIR__ . '/includes/upload/UploadStash.php',
        'UploadStashBadPathException' => __DIR__ . '/includes/upload/UploadStash.php',
@@ -1336,6 +1336,7 @@ $wgAutoloadLocalClasses = array(
        'UserCache' => __DIR__ . '/includes/cache/UserCache.php',
        'UserDupes' => __DIR__ . '/maintenance/userDupes.inc',
        'UserMailer' => __DIR__ . '/includes/mail/UserMailer.php',
+       'UserNamePrefixSearch' => __DIR__ . '/includes/user/UserNamePrefixSearch.php',
        'UserNotLoggedIn' => __DIR__ . '/includes/exception/UserNotLoggedIn.php',
        'UserOptions' => __DIR__ . '/maintenance/userOptions.inc',
        'UserPasswordPolicy' => __DIR__ . '/includes/password/UserPasswordPolicy.php',
@@ -1388,11 +1389,11 @@ $wgAutoloadLocalClasses = array(
        'WikiDiff3' => __DIR__ . '/includes/diff/WikiDiff3.php',
        'WikiExporter' => __DIR__ . '/includes/export/WikiExporter.php',
        'WikiFilePage' => __DIR__ . '/includes/page/WikiFilePage.php',
-       'WikiImporter' => __DIR__ . '/includes/Import.php',
+       'WikiImporter' => __DIR__ . '/includes/import/WikiImporter.php',
        'WikiMap' => __DIR__ . '/includes/WikiMap.php',
        'WikiPage' => __DIR__ . '/includes/page/WikiPage.php',
        'WikiReference' => __DIR__ . '/includes/WikiMap.php',
-       'WikiRevision' => __DIR__ . '/includes/Import.php',
+       'WikiRevision' => __DIR__ . '/includes/import/WikiRevision.php',
        'WikiStatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
        'WikitextContent' => __DIR__ . '/includes/content/WikitextContent.php',
        'WikitextContentHandler' => __DIR__ . '/includes/content/WikitextContentHandler.php',
index b635467..8c760cc 100644 (file)
                "ParserTestFiles": {
                        "type": "array",
                        "description": "Parser test suite files to be run by parserTests.php when no specific filename is passed to it"
+               },
+               "load_composer_autoloader": {
+                       "type": "boolean",
+                       "description": "Load the composer autoloader for this extension, if one is present"
                }
        }
 }
index db989a4..6c2782c 100644 (file)
@@ -223,12 +223,12 @@ class AjaxResponse {
                $fname = 'AjaxResponse::checkLastModified';
 
                if ( !$timestamp || $timestamp == '19700101000000' ) {
-                       wfDebug( "$fname: CACHE DISABLED, NO TIMESTAMP\n", 'log' );
+                       wfDebug( "$fname: CACHE DISABLED, NO TIMESTAMP", 'private' );
                        return false;
                }
 
                if ( !$wgCachePages ) {
-                       wfDebug( "$fname: CACHE DISABLED\n", 'log' );
+                       wfDebug( "$fname: CACHE DISABLED", 'private' );
                        return false;
                }
 
@@ -242,8 +242,8 @@ class AjaxResponse {
                        $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
                        $modsinceTime = strtotime( $modsince );
                        $ismodsince = wfTimestamp( TS_MW, $modsinceTime ? $modsinceTime : 1 );
-                       wfDebug( "$fname: -- client send If-Modified-Since: " . $modsince . "\n", 'log' );
-                       wfDebug( "$fname: --  we might send Last-Modified : $lastmod\n", 'log' );
+                       wfDebug( "$fname: -- client send If-Modified-Since: $modsince", 'private' );
+                       wfDebug( "$fname: --  we might send Last-Modified : $lastmod", 'private' );
 
                        if ( ( $ismodsince >= $timestamp )
                                && $wgUser->validateCache( $ismodsince ) &&
@@ -255,16 +255,16 @@ class AjaxResponse {
                                $this->mLastModified = $lastmod;
 
                                wfDebug( "$fname: CACHED client: $ismodsince ; user: {$wgUser->getTouched()} ; " .
-                                       "page: $timestamp ; site $wgCacheEpoch\n", 'log' );
+                                       "page: $timestamp ; site $wgCacheEpoch", 'private' );
 
                                return true;
                        } else {
                                wfDebug( "$fname: READY  client: $ismodsince ; user: {$wgUser->getTouched()} ; " .
-                                       "page: $timestamp ; site $wgCacheEpoch\n", 'log' );
+                                       "page: $timestamp ; site $wgCacheEpoch", 'private' );
                                $this->mLastModified = $lastmod;
                        }
                } else {
-                       wfDebug( "$fname: client did not send If-Modified-Since header\n", 'log' );
+                       wfDebug( "$fname: client did not send If-Modified-Since header", 'private' );
                        $this->mLastModified = $lastmod;
                }
                return false;
@@ -284,12 +284,12 @@ class AjaxResponse {
                if ( $mcvalue ) {
                        # Check to see if the value has been invalidated
                        if ( $touched <= $mcvalue['timestamp'] ) {
-                               wfDebug( "Got $mckey from cache\n" );
+                               wfDebug( "Got $mckey from cache" );
                                $this->mText = $mcvalue['value'];
 
                                return true;
                        } else {
-                               wfDebug( "$mckey has expired\n" );
+                               wfDebug( "$mckey has expired" );
                        }
                }
 
index e30b371..47d086b 100644 (file)
@@ -1038,7 +1038,12 @@ function wfMatchesDomainList( $url, $domains ) {
  * @since 1.25 support for additional context data
  *
  * @param string $text
- * @param string|bool $dest Unused
+ * @param string|bool $dest Destination of the message:
+ *     - 'all': both to the log and HTML (debug toolbar or HTML comments)
+ *     - 'private': excluded from HTML output
+ *   For backward compatibility, it can also take a boolean:
+ *     - true: same as 'all'
+ *     - false: same as 'private'
  * @param array $context Additional logging context data
  */
 function wfDebug( $text, $dest = 'all', array $context = array() ) {
@@ -1065,6 +1070,7 @@ function wfDebug( $text, $dest = 'all', array $context = array() ) {
        if ( $wgDebugLogPrefix !== '' ) {
                $context['prefix'] = $wgDebugLogPrefix;
        }
+       $context['private'] = ( $dest === false || $dest === 'private' );
 
        $logger = LoggerFactory::getInstance( 'wfDebug' );
        $logger->debug( $text, $context );
@@ -1126,7 +1132,6 @@ function wfDebugMem( $exact = false ) {
  * @param string $text
  * @param string|bool $dest Destination of the message:
  *     - 'all': both to the log and HTML (debug toolbar or HTML comments)
- *     - 'log': only to the log and not in HTML
  *     - 'private': only to the specific log if set in $wgDebugLogGroups and
  *       discarded otherwise
  *   For backward compatibility, it can also take a boolean:
@@ -1137,17 +1142,10 @@ function wfDebugMem( $exact = false ) {
 function wfDebugLog(
        $logGroup, $text, $dest = 'all', array $context = array()
 ) {
-       // Turn $dest into a string if it's a boolean (for b/c)
-       if ( $dest === true ) {
-               $dest = 'all';
-       } elseif ( $dest === false ) {
-               $dest = 'private';
-       }
-
        $text = trim( $text );
 
        $logger = LoggerFactory::getInstance( $logGroup );
-       $context['private'] = ( $dest === 'private' );
+       $context['private'] = ( $dest === false || $dest === 'private' );
        $logger->info( $text, $context );
 }
 
diff --git a/includes/Import.php b/includes/Import.php
deleted file mode 100644 (file)
index f59cf47..0000000
+++ /dev/null
@@ -1,2054 +0,0 @@
-<?php
-/**
- * MediaWiki page data importer.
- *
- * Copyright © 2003,2005 Brion Vibber <brion@pobox.com>
- * https://www.mediawiki.org/
- *
- * 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 SpecialPage
- */
-
-/**
- * XML file reader for the page data importer
- *
- * implements Special:Import
- * @ingroup SpecialPage
- */
-class WikiImporter {
-       private $reader = null;
-       private $foreignNamespaces = null;
-       private $mLogItemCallback, $mUploadCallback, $mRevisionCallback, $mPageCallback;
-       private $mSiteInfoCallback, $mPageOutCallback;
-       private $mNoticeCallback, $mDebug;
-       private $mImportUploads, $mImageBasePath;
-       private $mNoUpdates = false;
-       /** @var Config */
-       private $config;
-       /** @var ImportTitleFactory */
-       private $importTitleFactory;
-       /** @var array */
-       private $countableCache = array();
-
-       /**
-        * Creates an ImportXMLReader drawing from the source provided
-        * @param ImportSource $source
-        * @param Config $config
-        * @throws Exception
-        */
-       function __construct( ImportSource $source, Config $config = null ) {
-               if ( !class_exists( 'XMLReader' ) ) {
-                       throw new Exception( 'Import requires PHP to have been compiled with libxml support' );
-               }
-
-               $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' );
-               }
-               $id = UploadSourceAdapter::registerSource( $source );
-
-               // Enable the entity loader, as it is needed for loading external URLs via
-               // XMLReader::open (T86036)
-               $oldDisable = libxml_disable_entity_loader( false );
-               if ( defined( 'LIBXML_PARSEHUGE' ) ) {
-                       $status = $this->reader->open( "uploadsource://$id", null, LIBXML_PARSEHUGE );
-               } else {
-                       $status = $this->reader->open( "uploadsource://$id" );
-               }
-               if ( !$status ) {
-                       $error = libxml_get_last_error();
-                       libxml_disable_entity_loader( $oldDisable );
-                       throw new MWException( 'Encountered an internal error while initializing WikiImporter object: ' .
-                               $error->message );
-               }
-               libxml_disable_entity_loader( $oldDisable );
-
-               // Default callbacks
-               $this->setPageCallback( array( $this, 'beforeImportPage' ) );
-               $this->setRevisionCallback( array( $this, "importRevision" ) );
-               $this->setUploadCallback( array( $this, 'importUpload' ) );
-               $this->setLogItemCallback( array( $this, 'importLogItem' ) );
-               $this->setPageOutCallback( array( $this, 'finishImportPage' ) );
-
-               $this->importTitleFactory = new NaiveImportTitleFactory();
-       }
-
-       /**
-        * @return null|XMLReader
-        */
-       public function getReader() {
-               return $this->reader;
-       }
-
-       public function throwXmlError( $err ) {
-               $this->debug( "FAILURE: $err" );
-               wfDebug( "WikiImporter XML error: $err\n" );
-       }
-
-       public function debug( $data ) {
-               if ( $this->mDebug ) {
-                       wfDebug( "IMPORT: $data\n" );
-               }
-       }
-
-       public function warn( $data ) {
-               wfDebug( "IMPORT: $data\n" );
-       }
-
-       public function notice( $msg /*, $param, ...*/ ) {
-               $params = func_get_args();
-               array_shift( $params );
-
-               if ( is_callable( $this->mNoticeCallback ) ) {
-                       call_user_func( $this->mNoticeCallback, $msg, $params );
-               } else { # No ImportReporter -> CLI
-                       echo wfMessage( $msg, $params )->text() . "\n";
-               }
-       }
-
-       /**
-        * Set debug mode...
-        * @param bool $debug
-        */
-       function setDebug( $debug ) {
-               $this->mDebug = $debug;
-       }
-
-       /**
-        * Set 'no updates' mode. In this mode, the link tables will not be updated by the importer
-        * @param bool $noupdates
-        */
-       function setNoUpdates( $noupdates ) {
-               $this->mNoUpdates = $noupdates;
-       }
-
-       /**
-        * Set a callback that displays notice messages
-        *
-        * @param callable $callback
-        * @return callable
-        */
-       public function setNoticeCallback( $callback ) {
-               return wfSetVar( $this->mNoticeCallback, $callback );
-       }
-
-       /**
-        * Sets the action to perform as each new page in the stream is reached.
-        * @param callable $callback
-        * @return callable
-        */
-       public function setPageCallback( $callback ) {
-               $previous = $this->mPageCallback;
-               $this->mPageCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform as each page in the stream is completed.
-        * Callback accepts the page title (as a Title object), a second object
-        * with the original title form (in case it's been overridden into a
-        * local namespace), and a count of revisions.
-        *
-        * @param callable $callback
-        * @return callable
-        */
-       public function setPageOutCallback( $callback ) {
-               $previous = $this->mPageOutCallback;
-               $this->mPageOutCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform as each page revision is reached.
-        * @param callable $callback
-        * @return callable
-        */
-       public function setRevisionCallback( $callback ) {
-               $previous = $this->mRevisionCallback;
-               $this->mRevisionCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform as each file upload version is reached.
-        * @param callable $callback
-        * @return callable
-        */
-       public function setUploadCallback( $callback ) {
-               $previous = $this->mUploadCallback;
-               $this->mUploadCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform as each log item reached.
-        * @param callable $callback
-        * @return callable
-        */
-       public function setLogItemCallback( $callback ) {
-               $previous = $this->mLogItemCallback;
-               $this->mLogItemCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform when site info is encountered
-        * @param callable $callback
-        * @return callable
-        */
-       public function setSiteInfoCallback( $callback ) {
-               $previous = $this->mSiteInfoCallback;
-               $this->mSiteInfoCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the factory object to use to convert ForeignTitle objects into local
-        * Title objects
-        * @param ImportTitleFactory $factory
-        */
-       public function setImportTitleFactory( $factory ) {
-               $this->importTitleFactory = $factory;
-       }
-
-       /**
-        * Set a target namespace to override the defaults
-        * @param null|int $namespace
-        * @return bool
-        */
-       public function setTargetNamespace( $namespace ) {
-               if ( is_null( $namespace ) ) {
-                       // Don't override namespaces
-                       $this->setImportTitleFactory( new NaiveImportTitleFactory() );
-                       return true;
-               } elseif (
-                       $namespace >= 0 &&
-                       MWNamespace::exists( intval( $namespace ) )
-               ) {
-                       $namespace = intval( $namespace );
-                       $this->setImportTitleFactory( new NamespaceImportTitleFactory( $namespace ) );
-                       return true;
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Set a target root page under which all pages are imported
-        * @param null|string $rootpage
-        * @return Status
-        */
-       public function setTargetRootPage( $rootpage ) {
-               $status = Status::newGood();
-               if ( is_null( $rootpage ) ) {
-                       // No rootpage
-                       $this->setImportTitleFactory( new NaiveImportTitleFactory() );
-               } elseif ( $rootpage !== '' ) {
-                       $rootpage = rtrim( $rootpage, '/' ); // avoid double slashes
-                       $title = Title::newFromText( $rootpage );
-
-                       if ( !$title || $title->isExternal() ) {
-                               $status->fatal( 'import-rootpage-invalid' );
-                       } else {
-                               if ( !MWNamespace::hasSubpages( $title->getNamespace() ) ) {
-                                       global $wgContLang;
-
-                                       $displayNSText = $title->getNamespace() == NS_MAIN
-                                               ? wfMessage( 'blanknamespace' )->text()
-                                               : $wgContLang->getNsText( $title->getNamespace() );
-                                       $status->fatal( 'import-rootpage-nosubpage', $displayNSText );
-                               } else {
-                                       // set namespace to 'all', so the namespace check in processTitle() can pass
-                                       $this->setTargetNamespace( null );
-                                       $this->setImportTitleFactory( new SubpageImportTitleFactory( $title ) );
-                               }
-                       }
-               }
-               return $status;
-       }
-
-       /**
-        * @param string $dir
-        */
-       public function setImageBasePath( $dir ) {
-               $this->mImageBasePath = $dir;
-       }
-
-       /**
-        * @param bool $import
-        */
-       public function setImportUploads( $import ) {
-               $this->mImportUploads = $import;
-       }
-
-       /**
-        * Default per-page callback. Sets up some things related to site statistics
-        * @param array $titleAndForeignTitle Two-element array, with Title object at
-        * index 0 and ForeignTitle object at index 1
-        * @return bool
-        */
-       public function beforeImportPage( $titleAndForeignTitle ) {
-               $title = $titleAndForeignTitle[0];
-               $page = WikiPage::factory( $title );
-               $this->countableCache['title_' . $title->getPrefixedText()] = $page->isCountable();
-               return true;
-       }
-
-       /**
-        * Default per-revision callback, performs the import.
-        * @param WikiRevision $revision
-        * @return bool
-        */
-       public function importRevision( $revision ) {
-               if ( !$revision->getContentHandler()->canBeUsedOn( $revision->getTitle() ) ) {
-                       $this->notice( 'import-error-bad-location',
-                               $revision->getTitle()->getPrefixedText(),
-                               $revision->getID(),
-                               $revision->getModel(),
-                               $revision->getFormat() );
-
-                       return false;
-               }
-
-               try {
-                       $dbw = wfGetDB( DB_MASTER );
-                       return $dbw->deadlockLoop( array( $revision, 'importOldRevision' ) );
-               } catch ( MWContentSerializationException $ex ) {
-                       $this->notice( 'import-error-unserialize',
-                               $revision->getTitle()->getPrefixedText(),
-                               $revision->getID(),
-                               $revision->getModel(),
-                               $revision->getFormat() );
-               }
-
-               return false;
-       }
-
-       /**
-        * Default per-revision callback, performs the import.
-        * @param WikiRevision $revision
-        * @return bool
-        */
-       public function importLogItem( $revision ) {
-               $dbw = wfGetDB( DB_MASTER );
-               return $dbw->deadlockLoop( array( $revision, 'importLogItem' ) );
-       }
-
-       /**
-        * Dummy for now...
-        * @param WikiRevision $revision
-        * @return bool
-        */
-       public function importUpload( $revision ) {
-               $dbw = wfGetDB( DB_MASTER );
-               return $dbw->deadlockLoop( array( $revision, 'importUpload' ) );
-       }
-
-       /**
-        * Mostly for hook use
-        * @param Title $title
-        * @param ForeignTitle $foreignTitle
-        * @param int $revCount
-        * @param int $sRevCount
-        * @param array $pageInfo
-        * @return bool
-        */
-       public function finishImportPage( $title, $foreignTitle, $revCount,
-                       $sRevCount, $pageInfo ) {
-
-               // Update article count statistics (T42009)
-               // The normal counting logic in WikiPage->doEditUpdates() is designed for
-               // one-revision-at-a-time editing, not bulk imports. In this situation it
-               // suffers from issues of slave lag. We let WikiPage handle the total page
-               // and revision count, and we implement our own custom logic for the
-               // article (content page) count.
-               $page = WikiPage::factory( $title );
-               $page->loadPageData( 'fromdbmaster' );
-               $content = $page->getContent();
-               if ( $content === null ) {
-                       wfDebug( __METHOD__ . ': Skipping article count adjustment for ' . $title .
-                               ' because WikiPage::getContent() returned null' );
-               } else {
-                       $editInfo = $page->prepareContentForEdit( $content );
-                       $countKey = 'title_' . $title->getPrefixedText();
-                       $countable = $page->isCountable( $editInfo );
-                       if ( array_key_exists( $countKey, $this->countableCache ) &&
-                               $countable != $this->countableCache[$countKey] ) {
-                               DeferredUpdates::addUpdate( SiteStatsUpdate::factory( array(
-                                       'articles' => ( (int)$countable - (int)$this->countableCache[$countKey] )
-                               ) ) );
-                       }
-               }
-
-               $args = func_get_args();
-               return Hooks::run( 'AfterImportPage', $args );
-       }
-
-       /**
-        * Alternate per-revision callback, for debugging.
-        * @param WikiRevision $revision
-        */
-       public function debugRevisionHandler( &$revision ) {
-               $this->debug( "Got revision:" );
-               if ( is_object( $revision->title ) ) {
-                       $this->debug( "-- Title: " . $revision->title->getPrefixedText() );
-               } else {
-                       $this->debug( "-- Title: <invalid>" );
-               }
-               $this->debug( "-- User: " . $revision->user_text );
-               $this->debug( "-- Timestamp: " . $revision->timestamp );
-               $this->debug( "-- Comment: " . $revision->comment );
-               $this->debug( "-- Text: " . $revision->text );
-       }
-
-       /**
-        * Notify the callback function of site info
-        * @param array $siteInfo
-        * @return bool|mixed
-        */
-       private function siteInfoCallback( $siteInfo ) {
-               if ( isset( $this->mSiteInfoCallback ) ) {
-                       return call_user_func_array( $this->mSiteInfoCallback,
-                                       array( $siteInfo, $this ) );
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Notify the callback function when a new "<page>" is reached.
-        * @param Title $title
-        */
-       function pageCallback( $title ) {
-               if ( isset( $this->mPageCallback ) ) {
-                       call_user_func( $this->mPageCallback, $title );
-               }
-       }
-
-       /**
-        * Notify the callback function when a "</page>" is closed.
-        * @param Title $title
-        * @param ForeignTitle $foreignTitle
-        * @param int $revCount
-        * @param int $sucCount Number of revisions for which callback returned true
-        * @param array $pageInfo Associative array of page information
-        */
-       private function pageOutCallback( $title, $foreignTitle, $revCount,
-                       $sucCount, $pageInfo ) {
-               if ( isset( $this->mPageOutCallback ) ) {
-                       $args = func_get_args();
-                       call_user_func_array( $this->mPageOutCallback, $args );
-               }
-       }
-
-       /**
-        * Notify the callback function of a revision
-        * @param WikiRevision $revision
-        * @return bool|mixed
-        */
-       private function revisionCallback( $revision ) {
-               if ( isset( $this->mRevisionCallback ) ) {
-                       return call_user_func_array( $this->mRevisionCallback,
-                                       array( $revision, $this ) );
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Notify the callback function of a new log item
-        * @param WikiRevision $revision
-        * @return bool|mixed
-        */
-       private function logItemCallback( $revision ) {
-               if ( isset( $this->mLogItemCallback ) ) {
-                       return call_user_func_array( $this->mLogItemCallback,
-                                       array( $revision, $this ) );
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Retrieves the contents of the named attribute of the current element.
-        * @param string $attr The name of the attribute
-        * @return string The value of the attribute or an empty string if it is not set in the current
-        * element.
-        */
-       public function nodeAttribute( $attr ) {
-               return $this->reader->getAttribute( $attr );
-       }
-
-       /**
-        * Shouldn't something like this be built-in to XMLReader?
-        * Fetches text contents of the current element, assuming
-        * no sub-elements or such scary things.
-        * @return string
-        * @access private
-        */
-       public function nodeContents() {
-               if ( $this->reader->isEmptyElement ) {
-                       return "";
-               }
-               $buffer = "";
-               while ( $this->reader->read() ) {
-                       switch ( $this->reader->nodeType ) {
-                       case XMLReader::TEXT:
-                       case XMLReader::CDATA:
-                       case XMLReader::SIGNIFICANT_WHITESPACE:
-                               $buffer .= $this->reader->value;
-                               break;
-                       case XMLReader::END_ELEMENT:
-                               return $buffer;
-                       }
-               }
-
-               $this->reader->close();
-               return '';
-       }
-
-       /**
-        * Primary entry point
-        * @throws MWException
-        * @return bool
-        */
-       public function doImport() {
-               // Calls to reader->read need to be wrapped in calls to
-               // libxml_disable_entity_loader() to avoid local file
-               // inclusion attacks (bug 46932).
-               $oldDisable = libxml_disable_entity_loader( true );
-               $this->reader->read();
-
-               if ( $this->reader->localName != 'mediawiki' ) {
-                       libxml_disable_entity_loader( $oldDisable );
-                       throw new MWException( "Expected <mediawiki> tag, got " .
-                               $this->reader->localName );
-               }
-               $this->debug( "<mediawiki> tag is correct." );
-
-               $this->debug( "Starting primary dump processing loop." );
-
-               $keepReading = $this->reader->read();
-               $skip = false;
-               $rethrow = null;
-               try {
-                       while ( $keepReading ) {
-                               $tag = $this->reader->localName;
-                               $type = $this->reader->nodeType;
-
-                               if ( !Hooks::run( 'ImportHandleToplevelXMLTag', array( $this ) ) ) {
-                                       // Do nothing
-                               } elseif ( $tag == 'mediawiki' && $type == XMLReader::END_ELEMENT ) {
-                                       break;
-                               } elseif ( $tag == 'siteinfo' ) {
-                                       $this->handleSiteInfo();
-                               } elseif ( $tag == 'page' ) {
-                                       $this->handlePage();
-                               } elseif ( $tag == 'logitem' ) {
-                                       $this->handleLogItem();
-                               } elseif ( $tag != '#text' ) {
-                                       $this->warn( "Unhandled top-level XML tag $tag" );
-
-                                       $skip = true;
-                               }
-
-                               if ( $skip ) {
-                                       $keepReading = $this->reader->next();
-                                       $skip = false;
-                                       $this->debug( "Skip" );
-                               } else {
-                                       $keepReading = $this->reader->read();
-                               }
-                       }
-               } catch ( Exception $ex ) {
-                       $rethrow = $ex;
-               }
-
-               // finally
-               libxml_disable_entity_loader( $oldDisable );
-               $this->reader->close();
-
-               if ( $rethrow ) {
-                       throw $rethrow;
-               }
-
-               return true;
-       }
-
-       private function handleSiteInfo() {
-               $this->debug( "Enter site info handler." );
-               $siteInfo = array();
-
-               // Fields that can just be stuffed in the siteInfo object
-               $normalFields = array( 'sitename', 'base', 'generator', 'case' );
-
-               while ( $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XmlReader::END_ELEMENT &&
-                                       $this->reader->localName == 'siteinfo' ) {
-                               break;
-                       }
-
-                       $tag = $this->reader->localName;
-
-                       if ( $tag == 'namespace' ) {
-                               $this->foreignNamespaces[$this->nodeAttribute( 'key' )] =
-                                       $this->nodeContents();
-                       } elseif ( in_array( $tag, $normalFields ) ) {
-                               $siteInfo[$tag] = $this->nodeContents();
-                       }
-               }
-
-               $siteInfo['_namespaces'] = $this->foreignNamespaces;
-               $this->siteInfoCallback( $siteInfo );
-       }
-
-       private function handleLogItem() {
-               $this->debug( "Enter log item handler." );
-               $logInfo = array();
-
-               // Fields that can just be stuffed in the pageInfo object
-               $normalFields = array( 'id', 'comment', 'type', 'action', 'timestamp',
-                                       'logtitle', 'params' );
-
-               while ( $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
-                                       $this->reader->localName == 'logitem' ) {
-                               break;
-                       }
-
-                       $tag = $this->reader->localName;
-
-                       if ( !Hooks::run( 'ImportHandleLogItemXMLTag', array(
-                               $this, $logInfo
-                       ) ) ) {
-                               // Do nothing
-                       } elseif ( in_array( $tag, $normalFields ) ) {
-                               $logInfo[$tag] = $this->nodeContents();
-                       } elseif ( $tag == 'contributor' ) {
-                               $logInfo['contributor'] = $this->handleContributor();
-                       } elseif ( $tag != '#text' ) {
-                               $this->warn( "Unhandled log-item XML tag $tag" );
-                       }
-               }
-
-               $this->processLogItem( $logInfo );
-       }
-
-       /**
-        * @param array $logInfo
-        * @return bool|mixed
-        */
-       private function processLogItem( $logInfo ) {
-
-               $revision = new WikiRevision( $this->config );
-
-               if ( isset( $logInfo['id'] ) ) {
-                       $revision->setID( $logInfo['id'] );
-               }
-               $revision->setType( $logInfo['type'] );
-               $revision->setAction( $logInfo['action'] );
-               if ( isset( $logInfo['timestamp'] ) ) {
-                       $revision->setTimestamp( $logInfo['timestamp'] );
-               }
-               if ( isset( $logInfo['params'] ) ) {
-                       $revision->setParams( $logInfo['params'] );
-               }
-               if ( isset( $logInfo['logtitle'] ) ) {
-                       // @todo Using Title for non-local titles is a recipe for disaster.
-                       // We should use ForeignTitle here instead.
-                       $revision->setTitle( Title::newFromText( $logInfo['logtitle'] ) );
-               }
-
-               $revision->setNoUpdates( $this->mNoUpdates );
-
-               if ( isset( $logInfo['comment'] ) ) {
-                       $revision->setComment( $logInfo['comment'] );
-               }
-
-               if ( isset( $logInfo['contributor']['ip'] ) ) {
-                       $revision->setUserIP( $logInfo['contributor']['ip'] );
-               }
-
-               if ( !isset( $logInfo['contributor']['username'] ) ) {
-                       $revision->setUsername( 'Unknown user' );
-               } else {
-                       $revision->setUserName( $logInfo['contributor']['username'] );
-               }
-
-               return $this->logItemCallback( $revision );
-       }
-
-       private function handlePage() {
-               // Handle page data.
-               $this->debug( "Enter page handler." );
-               $pageInfo = array( 'revisionCount' => 0, 'successfulRevisionCount' => 0 );
-
-               // Fields that can just be stuffed in the pageInfo object
-               $normalFields = array( 'title', 'ns', 'id', 'redirect', 'restrictions' );
-
-               $skip = false;
-               $badTitle = false;
-
-               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
-                                       $this->reader->localName == 'page' ) {
-                               break;
-                       }
-
-                       $skip = false;
-
-                       $tag = $this->reader->localName;
-
-                       if ( $badTitle ) {
-                               // The title is invalid, bail out of this page
-                               $skip = true;
-                       } elseif ( !Hooks::run( 'ImportHandlePageXMLTag', array( $this,
-                                               &$pageInfo ) ) ) {
-                               // Do nothing
-                       } elseif ( in_array( $tag, $normalFields ) ) {
-                               // An XML snippet:
-                               // <page>
-                               //     <id>123</id>
-                               //     <title>Page</title>
-                               //     <redirect title="NewTitle"/>
-                               //     ...
-                               // Because the redirect tag is built differently, we need special handling for that case.
-                               if ( $tag == 'redirect' ) {
-                                       $pageInfo[$tag] = $this->nodeAttribute( 'title' );
-                               } else {
-                                       $pageInfo[$tag] = $this->nodeContents();
-                               }
-                       } elseif ( $tag == 'revision' || $tag == 'upload' ) {
-                               if ( !isset( $title ) ) {
-                                       $title = $this->processTitle( $pageInfo['title'],
-                                               isset( $pageInfo['ns'] ) ? $pageInfo['ns'] : null );
-
-                                       // $title is either an array of two titles or false.
-                                       if ( is_array( $title ) ) {
-                                               $this->pageCallback( $title );
-                                               list( $pageInfo['_title'], $foreignTitle ) = $title;
-                                       } else {
-                                               $badTitle = true;
-                                               $skip = true;
-                                       }
-                               }
-
-                               if ( $title ) {
-                                       if ( $tag == 'revision' ) {
-                                               $this->handleRevision( $pageInfo );
-                                       } else {
-                                               $this->handleUpload( $pageInfo );
-                                       }
-                               }
-                       } elseif ( $tag != '#text' ) {
-                               $this->warn( "Unhandled page XML tag $tag" );
-                               $skip = true;
-                       }
-               }
-
-               // @note $pageInfo is only set if a valid $title is processed above with
-               //       no error. If we have a valid $title, then pageCallback is called
-               //       above, $pageInfo['title'] is set and we do pageOutCallback here.
-               //       If $pageInfo['_title'] is not set, then $foreignTitle is also not
-               //       set since they both come from $title above.
-               if ( array_key_exists( '_title', $pageInfo ) ) {
-                       $this->pageOutCallback( $pageInfo['_title'], $foreignTitle,
-                                       $pageInfo['revisionCount'],
-                                       $pageInfo['successfulRevisionCount'],
-                                       $pageInfo );
-               }
-       }
-
-       /**
-        * @param array $pageInfo
-        */
-       private function handleRevision( &$pageInfo ) {
-               $this->debug( "Enter revision handler" );
-               $revisionInfo = array();
-
-               $normalFields = array( 'id', 'timestamp', 'comment', 'minor', 'model', 'format', 'text' );
-
-               $skip = false;
-
-               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
-                                       $this->reader->localName == 'revision' ) {
-                               break;
-                       }
-
-                       $tag = $this->reader->localName;
-
-                       if ( !Hooks::run( 'ImportHandleRevisionXMLTag', array(
-                               $this, $pageInfo, $revisionInfo
-                       ) ) ) {
-                               // Do nothing
-                       } elseif ( in_array( $tag, $normalFields ) ) {
-                               $revisionInfo[$tag] = $this->nodeContents();
-                       } elseif ( $tag == 'contributor' ) {
-                               $revisionInfo['contributor'] = $this->handleContributor();
-                       } elseif ( $tag != '#text' ) {
-                               $this->warn( "Unhandled revision XML tag $tag" );
-                               $skip = true;
-                       }
-               }
-
-               $pageInfo['revisionCount']++;
-               if ( $this->processRevision( $pageInfo, $revisionInfo ) ) {
-                       $pageInfo['successfulRevisionCount']++;
-               }
-       }
-
-       /**
-        * @param array $pageInfo
-        * @param array $revisionInfo
-        * @return bool|mixed
-        */
-       private function processRevision( $pageInfo, $revisionInfo ) {
-               global $wgMaxArticleSize;
-
-               // Make sure revisions won't violate $wgMaxArticleSize, which could lead to
-               // database errors and instability. Testing for revisions with only listed
-               // content models, as other content models might use serialization formats
-               // which aren't checked against $wgMaxArticleSize.
-               if ( ( !isset( $revisionInfo['model'] ) ||
-                       in_array( $revisionInfo['model'], array(
-                               'wikitext',
-                               'css',
-                               'json',
-                               'javascript',
-                               'text',
-                               ''
-                       ) ) ) &&
-                       (int)( strlen( $revisionInfo['text'] ) / 1024 ) > $wgMaxArticleSize
-               ) {
-                       throw new MWException( 'The text of ' .
-                               ( isset( $revisionInfo['id'] ) ?
-                                       "the revision with ID $revisionInfo[id]" :
-                                       'a revision'
-                               ) . " exceeds the maximum allowable size ($wgMaxArticleSize KB)" );
-               }
-
-               $revision = new WikiRevision( $this->config );
-
-               if ( isset( $revisionInfo['id'] ) ) {
-                       $revision->setID( $revisionInfo['id'] );
-               }
-               if ( isset( $revisionInfo['model'] ) ) {
-                       $revision->setModel( $revisionInfo['model'] );
-               }
-               if ( isset( $revisionInfo['format'] ) ) {
-                       $revision->setFormat( $revisionInfo['format'] );
-               }
-               $revision->setTitle( $pageInfo['_title'] );
-
-               if ( isset( $revisionInfo['text'] ) ) {
-                       $handler = $revision->getContentHandler();
-                       $text = $handler->importTransform(
-                               $revisionInfo['text'],
-                               $revision->getFormat() );
-
-                       $revision->setText( $text );
-               }
-               if ( isset( $revisionInfo['timestamp'] ) ) {
-                       $revision->setTimestamp( $revisionInfo['timestamp'] );
-               } else {
-                       $revision->setTimestamp( wfTimestampNow() );
-               }
-
-               if ( isset( $revisionInfo['comment'] ) ) {
-                       $revision->setComment( $revisionInfo['comment'] );
-               }
-
-               if ( isset( $revisionInfo['minor'] ) ) {
-                       $revision->setMinor( true );
-               }
-               if ( isset( $revisionInfo['contributor']['ip'] ) ) {
-                       $revision->setUserIP( $revisionInfo['contributor']['ip'] );
-               } elseif ( isset( $revisionInfo['contributor']['username'] ) ) {
-                       $revision->setUserName( $revisionInfo['contributor']['username'] );
-               } else {
-                       $revision->setUserName( 'Unknown user' );
-               }
-               $revision->setNoUpdates( $this->mNoUpdates );
-
-               return $this->revisionCallback( $revision );
-       }
-
-       /**
-        * @param array $pageInfo
-        * @return mixed
-        */
-       private function handleUpload( &$pageInfo ) {
-               $this->debug( "Enter upload handler" );
-               $uploadInfo = array();
-
-               $normalFields = array( 'timestamp', 'comment', 'filename', 'text',
-                                       'src', 'size', 'sha1base36', 'archivename', 'rel' );
-
-               $skip = false;
-
-               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
-                                       $this->reader->localName == 'upload' ) {
-                               break;
-                       }
-
-                       $tag = $this->reader->localName;
-
-                       if ( !Hooks::run( 'ImportHandleUploadXMLTag', array(
-                               $this, $pageInfo
-                       ) ) ) {
-                               // Do nothing
-                       } elseif ( in_array( $tag, $normalFields ) ) {
-                               $uploadInfo[$tag] = $this->nodeContents();
-                       } elseif ( $tag == 'contributor' ) {
-                               $uploadInfo['contributor'] = $this->handleContributor();
-                       } elseif ( $tag == 'contents' ) {
-                               $contents = $this->nodeContents();
-                               $encoding = $this->reader->getAttribute( 'encoding' );
-                               if ( $encoding === 'base64' ) {
-                                       $uploadInfo['fileSrc'] = $this->dumpTemp( base64_decode( $contents ) );
-                                       $uploadInfo['isTempSrc'] = true;
-                               }
-                       } elseif ( $tag != '#text' ) {
-                               $this->warn( "Unhandled upload XML tag $tag" );
-                               $skip = true;
-                       }
-               }
-
-               if ( $this->mImageBasePath && isset( $uploadInfo['rel'] ) ) {
-                       $path = "{$this->mImageBasePath}/{$uploadInfo['rel']}";
-                       if ( file_exists( $path ) ) {
-                               $uploadInfo['fileSrc'] = $path;
-                               $uploadInfo['isTempSrc'] = false;
-                       }
-               }
-
-               if ( $this->mImportUploads ) {
-                       return $this->processUpload( $pageInfo, $uploadInfo );
-               }
-       }
-
-       /**
-        * @param string $contents
-        * @return string
-        */
-       private function dumpTemp( $contents ) {
-               $filename = tempnam( wfTempDir(), 'importupload' );
-               file_put_contents( $filename, $contents );
-               return $filename;
-       }
-
-       /**
-        * @param array $pageInfo
-        * @param array $uploadInfo
-        * @return mixed
-        */
-       private function processUpload( $pageInfo, $uploadInfo ) {
-               $revision = new WikiRevision( $this->config );
-               $text = isset( $uploadInfo['text'] ) ? $uploadInfo['text'] : '';
-
-               $revision->setTitle( $pageInfo['_title'] );
-               $revision->setID( $pageInfo['id'] );
-               $revision->setTimestamp( $uploadInfo['timestamp'] );
-               $revision->setText( $text );
-               $revision->setFilename( $uploadInfo['filename'] );
-               if ( isset( $uploadInfo['archivename'] ) ) {
-                       $revision->setArchiveName( $uploadInfo['archivename'] );
-               }
-               $revision->setSrc( $uploadInfo['src'] );
-               if ( isset( $uploadInfo['fileSrc'] ) ) {
-                       $revision->setFileSrc( $uploadInfo['fileSrc'],
-                               !empty( $uploadInfo['isTempSrc'] ) );
-               }
-               if ( isset( $uploadInfo['sha1base36'] ) ) {
-                       $revision->setSha1Base36( $uploadInfo['sha1base36'] );
-               }
-               $revision->setSize( intval( $uploadInfo['size'] ) );
-               $revision->setComment( $uploadInfo['comment'] );
-
-               if ( isset( $uploadInfo['contributor']['ip'] ) ) {
-                       $revision->setUserIP( $uploadInfo['contributor']['ip'] );
-               }
-               if ( isset( $uploadInfo['contributor']['username'] ) ) {
-                       $revision->setUserName( $uploadInfo['contributor']['username'] );
-               }
-               $revision->setNoUpdates( $this->mNoUpdates );
-
-               return call_user_func( $this->mUploadCallback, $revision );
-       }
-
-       /**
-        * @return array
-        */
-       private function handleContributor() {
-               $fields = array( 'id', 'ip', 'username' );
-               $info = array();
-
-               if ( $this->reader->isEmptyElement ) {
-                       return $info;
-               }
-               while ( $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
-                                       $this->reader->localName == 'contributor' ) {
-                               break;
-                       }
-
-                       $tag = $this->reader->localName;
-
-                       if ( in_array( $tag, $fields ) ) {
-                               $info[$tag] = $this->nodeContents();
-                       }
-               }
-
-               return $info;
-       }
-
-       /**
-        * @param string $text
-        * @param string|null $ns
-        * @return array|bool
-        */
-       private function processTitle( $text, $ns = null ) {
-               if ( is_null( $this->foreignNamespaces ) ) {
-                       $foreignTitleFactory = new NaiveForeignTitleFactory();
-               } else {
-                       $foreignTitleFactory = new NamespaceAwareForeignTitleFactory(
-                               $this->foreignNamespaces );
-               }
-
-               $foreignTitle = $foreignTitleFactory->createForeignTitle( $text,
-                       intval( $ns ) );
-
-               $title = $this->importTitleFactory->createTitleFromForeignTitle(
-                       $foreignTitle );
-
-               $commandLineMode = $this->config->get( 'CommandLineMode' );
-               if ( is_null( $title ) ) {
-                       # Invalid page title? Ignore the page
-                       $this->notice( 'import-error-invalid', $foreignTitle->getFullText() );
-                       return false;
-               } elseif ( $title->isExternal() ) {
-                       $this->notice( 'import-error-interwiki', $title->getPrefixedText() );
-                       return false;
-               } elseif ( !$title->canExist() ) {
-                       $this->notice( 'import-error-special', $title->getPrefixedText() );
-                       return false;
-               } 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' ) && !$commandLineMode ) {
-                       # Do not import if the importing wiki user cannot create this page
-                       $this->notice( 'import-error-create', $title->getPrefixedText() );
-                       return false;
-               }
-
-               return array( $title, $foreignTitle );
-       }
-}
-
-/** This is a horrible hack used to keep source compatibility */
-class UploadSourceAdapter {
-       /** @var array */
-       public static $sourceRegistrations = array();
-
-       /** @var string */
-       private $mSource;
-
-       /** @var string */
-       private $mBuffer;
-
-       /** @var int */
-       private $mPosition;
-
-       /**
-        * @param ImportSource $source
-        * @return string
-        */
-       static function registerSource( ImportSource $source ) {
-               $id = wfRandomString();
-
-               self::$sourceRegistrations[$id] = $source;
-
-               return $id;
-       }
-
-       /**
-        * @param string $path
-        * @param string $mode
-        * @param array $options
-        * @param string $opened_path
-        * @return bool
-        */
-       function stream_open( $path, $mode, $options, &$opened_path ) {
-               $url = parse_url( $path );
-               $id = $url['host'];
-
-               if ( !isset( self::$sourceRegistrations[$id] ) ) {
-                       return false;
-               }
-
-               $this->mSource = self::$sourceRegistrations[$id];
-
-               return true;
-       }
-
-       /**
-        * @param int $count
-        * @return string
-        */
-       function stream_read( $count ) {
-               $return = '';
-               $leave = false;
-
-               while ( !$leave && !$this->mSource->atEnd() &&
-                               strlen( $this->mBuffer ) < $count ) {
-                       $read = $this->mSource->readChunk();
-
-                       if ( !strlen( $read ) ) {
-                               $leave = true;
-                       }
-
-                       $this->mBuffer .= $read;
-               }
-
-               if ( strlen( $this->mBuffer ) ) {
-                       $return = substr( $this->mBuffer, 0, $count );
-                       $this->mBuffer = substr( $this->mBuffer, $count );
-               }
-
-               $this->mPosition += strlen( $return );
-
-               return $return;
-       }
-
-       /**
-        * @param string $data
-        * @return bool
-        */
-       function stream_write( $data ) {
-               return false;
-       }
-
-       /**
-        * @return mixed
-        */
-       function stream_tell() {
-               return $this->mPosition;
-       }
-
-       /**
-        * @return bool
-        */
-       function stream_eof() {
-               return $this->mSource->atEnd();
-       }
-
-       /**
-        * @return array
-        */
-       function url_stat() {
-               $result = array();
-
-               $result['dev'] = $result[0] = 0;
-               $result['ino'] = $result[1] = 0;
-               $result['mode'] = $result[2] = 0;
-               $result['nlink'] = $result[3] = 0;
-               $result['uid'] = $result[4] = 0;
-               $result['gid'] = $result[5] = 0;
-               $result['rdev'] = $result[6] = 0;
-               $result['size'] = $result[7] = 0;
-               $result['atime'] = $result[8] = 0;
-               $result['mtime'] = $result[9] = 0;
-               $result['ctime'] = $result[10] = 0;
-               $result['blksize'] = $result[11] = 0;
-               $result['blocks'] = $result[12] = 0;
-
-               return $result;
-       }
-}
-
-/**
- * @todo document (e.g. one-sentence class description).
- * @ingroup SpecialPage
- */
-class WikiRevision {
-       /** @todo Unused? */
-       public $importer = null;
-
-       /** @var Title */
-       public $title = null;
-
-       /** @var int */
-       public $id = 0;
-
-       /** @var string */
-       public $timestamp = "20010115000000";
-
-       /**
-        * @var int
-        * @todo Can't find any uses. Public, because that's suspicious. Get clarity. */
-       public $user = 0;
-
-       /** @var string */
-       public $user_text = "";
-
-       /** @var string */
-       public $model = null;
-
-       /** @var string */
-       public $format = null;
-
-       /** @var string */
-       public $text = "";
-
-       /** @var int */
-       protected $size;
-
-       /** @var Content */
-       public $content = null;
-
-       /** @var ContentHandler */
-       protected $contentHandler = null;
-
-       /** @var string */
-       public $comment = "";
-
-       /** @var bool */
-       public $minor = false;
-
-       /** @var string */
-       public $type = "";
-
-       /** @var string */
-       public $action = "";
-
-       /** @var string */
-       public $params = "";
-
-       /** @var string */
-       public $fileSrc = '';
-
-       /** @var bool|string */
-       public $sha1base36 = false;
-
-       /**
-        * @var bool
-        * @todo Unused?
-        */
-       public $isTemp = false;
-
-       /** @var string */
-       public $archiveName = '';
-
-       protected $filename;
-
-       /** @var mixed */
-       protected $src;
-
-       /** @todo Unused? */
-       public $fileIsTemp;
-
-       /** @var bool */
-       private $mNoUpdates = false;
-
-       /** @var Config $config */
-       private $config;
-
-       public function __construct( Config $config ) {
-               $this->config = $config;
-       }
-
-       /**
-        * @param Title $title
-        * @throws MWException
-        */
-       function setTitle( $title ) {
-               if ( is_object( $title ) ) {
-                       $this->title = $title;
-               } elseif ( is_null( $title ) ) {
-                       throw new MWException( "WikiRevision given a null title in import. "
-                               . "You may need to adjust \$wgLegalTitleChars." );
-               } else {
-                       throw new MWException( "WikiRevision given non-object title in import." );
-               }
-       }
-
-       /**
-        * @param int $id
-        */
-       function setID( $id ) {
-               $this->id = $id;
-       }
-
-       /**
-        * @param string $ts
-        */
-       function setTimestamp( $ts ) {
-               # 2003-08-05T18:30:02Z
-               $this->timestamp = wfTimestamp( TS_MW, $ts );
-       }
-
-       /**
-        * @param string $user
-        */
-       function setUsername( $user ) {
-               $this->user_text = $user;
-       }
-
-       /**
-        * @param string $ip
-        */
-       function setUserIP( $ip ) {
-               $this->user_text = $ip;
-       }
-
-       /**
-        * @param string $model
-        */
-       function setModel( $model ) {
-               $this->model = $model;
-       }
-
-       /**
-        * @param string $format
-        */
-       function setFormat( $format ) {
-               $this->format = $format;
-       }
-
-       /**
-        * @param string $text
-        */
-       function setText( $text ) {
-               $this->text = $text;
-       }
-
-       /**
-        * @param string $text
-        */
-       function setComment( $text ) {
-               $this->comment = $text;
-       }
-
-       /**
-        * @param bool $minor
-        */
-       function setMinor( $minor ) {
-               $this->minor = (bool)$minor;
-       }
-
-       /**
-        * @param mixed $src
-        */
-       function setSrc( $src ) {
-               $this->src = $src;
-       }
-
-       /**
-        * @param string $src
-        * @param bool $isTemp
-        */
-       function setFileSrc( $src, $isTemp ) {
-               $this->fileSrc = $src;
-               $this->fileIsTemp = $isTemp;
-       }
-
-       /**
-        * @param string $sha1base36
-        */
-       function setSha1Base36( $sha1base36 ) {
-               $this->sha1base36 = $sha1base36;
-       }
-
-       /**
-        * @param string $filename
-        */
-       function setFilename( $filename ) {
-               $this->filename = $filename;
-       }
-
-       /**
-        * @param string $archiveName
-        */
-       function setArchiveName( $archiveName ) {
-               $this->archiveName = $archiveName;
-       }
-
-       /**
-        * @param int $size
-        */
-       function setSize( $size ) {
-               $this->size = intval( $size );
-       }
-
-       /**
-        * @param string $type
-        */
-       function setType( $type ) {
-               $this->type = $type;
-       }
-
-       /**
-        * @param string $action
-        */
-       function setAction( $action ) {
-               $this->action = $action;
-       }
-
-       /**
-        * @param array $params
-        */
-       function setParams( $params ) {
-               $this->params = $params;
-       }
-
-       /**
-        * @param bool $noupdates
-        */
-       public function setNoUpdates( $noupdates ) {
-               $this->mNoUpdates = $noupdates;
-       }
-
-       /**
-        * @return Title
-        */
-       function getTitle() {
-               return $this->title;
-       }
-
-       /**
-        * @return int
-        */
-       function getID() {
-               return $this->id;
-       }
-
-       /**
-        * @return string
-        */
-       function getTimestamp() {
-               return $this->timestamp;
-       }
-
-       /**
-        * @return string
-        */
-       function getUser() {
-               return $this->user_text;
-       }
-
-       /**
-        * @return string
-        *
-        * @deprecated Since 1.21, use getContent() instead.
-        */
-       function getText() {
-               ContentHandler::deprecated( __METHOD__, '1.21' );
-
-               return $this->text;
-       }
-
-       /**
-        * @return ContentHandler
-        */
-       function getContentHandler() {
-               if ( is_null( $this->contentHandler ) ) {
-                       $this->contentHandler = ContentHandler::getForModelID( $this->getModel() );
-               }
-
-               return $this->contentHandler;
-       }
-
-       /**
-        * @return Content
-        */
-       function getContent() {
-               if ( is_null( $this->content ) ) {
-                       $handler = $this->getContentHandler();
-                       $this->content = $handler->unserializeContent( $this->text, $this->getFormat() );
-               }
-
-               return $this->content;
-       }
-
-       /**
-        * @return string
-        */
-       function getModel() {
-               if ( is_null( $this->model ) ) {
-                       $this->model = $this->getTitle()->getContentModel();
-               }
-
-               return $this->model;
-       }
-
-       /**
-        * @return string
-        */
-       function getFormat() {
-               if ( is_null( $this->format ) ) {
-                       $this->format = $this->getContentHandler()->getDefaultFormat();
-               }
-
-               return $this->format;
-       }
-
-       /**
-        * @return string
-        */
-       function getComment() {
-               return $this->comment;
-       }
-
-       /**
-        * @return bool
-        */
-       function getMinor() {
-               return $this->minor;
-       }
-
-       /**
-        * @return mixed
-        */
-       function getSrc() {
-               return $this->src;
-       }
-
-       /**
-        * @return bool|string
-        */
-       function getSha1() {
-               if ( $this->sha1base36 ) {
-                       return Wikimedia\base_convert( $this->sha1base36, 36, 16 );
-               }
-               return false;
-       }
-
-       /**
-        * @return string
-        */
-       function getFileSrc() {
-               return $this->fileSrc;
-       }
-
-       /**
-        * @return bool
-        */
-       function isTempSrc() {
-               return $this->isTemp;
-       }
-
-       /**
-        * @return mixed
-        */
-       function getFilename() {
-               return $this->filename;
-       }
-
-       /**
-        * @return string
-        */
-       function getArchiveName() {
-               return $this->archiveName;
-       }
-
-       /**
-        * @return mixed
-        */
-       function getSize() {
-               return $this->size;
-       }
-
-       /**
-        * @return string
-        */
-       function getType() {
-               return $this->type;
-       }
-
-       /**
-        * @return string
-        */
-       function getAction() {
-               return $this->action;
-       }
-
-       /**
-        * @return string
-        */
-       function getParams() {
-               return $this->params;
-       }
-
-       /**
-        * @return bool
-        */
-       function importOldRevision() {
-               $dbw = wfGetDB( DB_MASTER );
-
-               # Sneak a single revision into place
-               $user = User::newFromName( $this->getUser() );
-               if ( $user ) {
-                       $userId = intval( $user->getId() );
-                       $userText = $user->getName();
-                       $userObj = $user;
-               } else {
-                       $userId = 0;
-                       $userText = $this->getUser();
-                       $userObj = new User;
-               }
-
-               // avoid memory leak...?
-               Title::clearCaches();
-
-               $page = WikiPage::factory( $this->title );
-               $page->loadPageData( 'fromdbmaster' );
-               if ( !$page->exists() ) {
-                       # must create the page...
-                       $pageId = $page->insertOn( $dbw );
-                       $created = true;
-                       $oldcountable = null;
-               } else {
-                       $pageId = $page->getId();
-                       $created = false;
-
-                       $prior = $dbw->selectField( 'revision', '1',
-                               array( 'rev_page' => $pageId,
-                                       'rev_timestamp' => $dbw->timestamp( $this->timestamp ),
-                                       'rev_user_text' => $userText,
-                                       'rev_comment' => $this->getComment() ),
-                               __METHOD__
-                       );
-                       if ( $prior ) {
-                               // @todo FIXME: This could fail slightly for multiple matches :P
-                               wfDebug( __METHOD__ . ": skipping existing revision for [[" .
-                                       $this->title->getPrefixedText() . "]], timestamp " . $this->timestamp . "\n" );
-                               return false;
-                       }
-               }
-
-               // Select previous version to make size diffs correct
-               $prevId = $dbw->selectField( 'revision', 'rev_id',
-                       array(
-                               'rev_page' => $pageId,
-                               'rev_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $this->timestamp ) ),
-                       ),
-                       __METHOD__,
-                       array( 'ORDER BY' => array(
-                                       'rev_timestamp DESC',
-                                       'rev_id DESC', // timestamp is not unique per page
-                               )
-                       )
-               );
-
-               # @todo FIXME: Use original rev_id optionally (better for backups)
-               # Insert the row
-               $revision = new Revision( array(
-                       'title' => $this->title,
-                       'page' => $pageId,
-                       'content_model' => $this->getModel(),
-                       'content_format' => $this->getFormat(),
-                       // XXX: just set 'content' => $this->getContent()?
-                       'text' => $this->getContent()->serialize( $this->getFormat() ),
-                       'comment' => $this->getComment(),
-                       'user' => $userId,
-                       'user_text' => $userText,
-                       'timestamp' => $this->timestamp,
-                       'minor_edit' => $this->minor,
-                       'parent_id' => $prevId,
-                       ) );
-               $revision->insertOn( $dbw );
-               $changed = $page->updateIfNewerOn( $dbw, $revision );
-
-               if ( $changed !== false && !$this->mNoUpdates ) {
-                       wfDebug( __METHOD__ . ": running updates\n" );
-                       // countable/oldcountable stuff is handled in WikiImporter::finishImportPage
-                       $page->doEditUpdates(
-                               $revision,
-                               $userObj,
-                               array( 'created' => $created, 'oldcountable' => 'no-change' )
-                       );
-               }
-
-               return true;
-       }
-
-       function importLogItem() {
-               $dbw = wfGetDB( DB_MASTER );
-
-               $user = User::newFromName( $this->getUser() );
-               if ( $user ) {
-                       $userId = intval( $user->getId() );
-                       $userText = $user->getName();
-               } else {
-                       $userId = 0;
-                       $userText = $this->getUser();
-               }
-
-               # @todo FIXME: This will not record autoblocks
-               if ( !$this->getTitle() ) {
-                       wfDebug( __METHOD__ . ": skipping invalid {$this->type}/{$this->action} log time, timestamp " .
-                               $this->timestamp . "\n" );
-                       return;
-               }
-               # Check if it exists already
-               // @todo FIXME: Use original log ID (better for backups)
-               $prior = $dbw->selectField( 'logging', '1',
-                       array( 'log_type' => $this->getType(),
-                               'log_action' => $this->getAction(),
-                               'log_timestamp' => $dbw->timestamp( $this->timestamp ),
-                               'log_namespace' => $this->getTitle()->getNamespace(),
-                               'log_title' => $this->getTitle()->getDBkey(),
-                               'log_comment' => $this->getComment(),
-                               # 'log_user_text' => $this->user_text,
-                               'log_params' => $this->params ),
-                       __METHOD__
-               );
-               // @todo FIXME: This could fail slightly for multiple matches :P
-               if ( $prior ) {
-                       wfDebug( __METHOD__
-                               . ": skipping existing item for Log:{$this->type}/{$this->action}, timestamp "
-                               . $this->timestamp . "\n" );
-                       return;
-               }
-               $log_id = $dbw->nextSequenceValue( 'logging_log_id_seq' );
-               $data = array(
-                       'log_id' => $log_id,
-                       'log_type' => $this->type,
-                       'log_action' => $this->action,
-                       'log_timestamp' => $dbw->timestamp( $this->timestamp ),
-                       'log_user' =>  $userId,
-                       'log_user_text' => $userText,
-                       'log_namespace' => $this->getTitle()->getNamespace(),
-                       'log_title' => $this->getTitle()->getDBkey(),
-                       'log_comment' => $this->getComment(),
-                       'log_params' => $this->params
-               );
-               $dbw->insert( 'logging', $data, __METHOD__ );
-       }
-
-       /**
-        * @return bool
-        */
-       function importUpload() {
-               # Construct a file
-               $archiveName = $this->getArchiveName();
-               if ( $archiveName ) {
-                       wfDebug( __METHOD__ . "Importing archived file as $archiveName\n" );
-                       $file = OldLocalFile::newFromArchiveName( $this->getTitle(),
-                               RepoGroup::singleton()->getLocalRepo(), $archiveName );
-               } else {
-                       $file = wfLocalFile( $this->getTitle() );
-                       $file->load( File::READ_LATEST );
-                       wfDebug( __METHOD__ . 'Importing new file as ' . $file->getName() . "\n" );
-                       if ( $file->exists() && $file->getTimestamp() > $this->getTimestamp() ) {
-                               $archiveName = $file->getTimestamp() . '!' . $file->getName();
-                               $file = OldLocalFile::newFromArchiveName( $this->getTitle(),
-                                       RepoGroup::singleton()->getLocalRepo(), $archiveName );
-                               wfDebug( __METHOD__ . "File already exists; importing as $archiveName\n" );
-                       }
-               }
-               if ( !$file ) {
-                       wfDebug( __METHOD__ . ': Bad file for ' . $this->getTitle() . "\n" );
-                       return false;
-               }
-
-               # Get the file source or download if necessary
-               $source = $this->getFileSrc();
-               $flags = $this->isTempSrc() ? File::DELETE_SOURCE : 0;
-               if ( !$source ) {
-                       $source = $this->downloadSource();
-                       $flags |= File::DELETE_SOURCE;
-               }
-               if ( !$source ) {
-                       wfDebug( __METHOD__ . ": Could not fetch remote file.\n" );
-                       return false;
-               }
-               $sha1 = $this->getSha1();
-               if ( $sha1 && ( $sha1 !== sha1_file( $source ) ) ) {
-                       if ( $flags & File::DELETE_SOURCE ) {
-                               # Broken file; delete it if it is a temporary file
-                               unlink( $source );
-                       }
-                       wfDebug( __METHOD__ . ": Corrupt file $source.\n" );
-                       return false;
-               }
-
-               $user = User::newFromName( $this->user_text );
-
-               # Do the actual upload
-               if ( $archiveName ) {
-                       $status = $file->uploadOld( $source, $archiveName,
-                               $this->getTimestamp(), $this->getComment(), $user, $flags );
-               } else {
-                       $status = $file->upload( $source, $this->getComment(), $this->getComment(),
-                               $flags, false, $this->getTimestamp(), $user );
-               }
-
-               if ( $status->isGood() ) {
-                       wfDebug( __METHOD__ . ": Successful\n" );
-                       return true;
-               } else {
-                       wfDebug( __METHOD__ . ': failed: ' . $status->getHTML() . "\n" );
-                       return false;
-               }
-       }
-
-       /**
-        * @return bool|string
-        */
-       function downloadSource() {
-               if ( !$this->config->get( 'EnableUploads' ) ) {
-                       return false;
-               }
-
-               $tempo = tempnam( wfTempDir(), 'download' );
-               $f = fopen( $tempo, 'wb' );
-               if ( !$f ) {
-                       wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
-                       return false;
-               }
-
-               // @todo FIXME!
-               $src = $this->getSrc();
-               $data = Http::get( $src, array(), __METHOD__ );
-               if ( !$data ) {
-                       wfDebug( "IMPORT: couldn't fetch source $src\n" );
-                       fclose( $f );
-                       unlink( $tempo );
-                       return false;
-               }
-
-               fwrite( $f, $data );
-               fclose( $f );
-
-               return $tempo;
-       }
-
-}
-
-/**
- * Source interface for XML import.
- */
-interface ImportSource {
-
-       /**
-        * Indicates whether the end of the input has been reached.
-        * Will return true after a finite number of calls to readChunk.
-        *
-        * @return bool true if there is no more input, false otherwise.
-        */
-       function atEnd();
-
-       /**
-        * Return a chunk of the input, as a (possibly empty) string.
-        * When the end of input is reached, readChunk() returns false.
-        * If atEnd() returns false, readChunk() will return a string.
-        * If atEnd() returns true, readChunk() will return false.
-        *
-        * @return bool|string
-        */
-       function readChunk();
-}
-
-/**
- * Used for importing XML dumps where the content of the dump is in a string.
- * This class is ineffecient, and should only be used for small dumps.
- * For larger dumps, ImportStreamSource should be used instead.
- *
- * @ingroup SpecialPage
- */
-class ImportStringSource implements ImportSource {
-       function __construct( $string ) {
-               $this->mString = $string;
-               $this->mRead = false;
-       }
-
-       /**
-        * @return bool
-        */
-       function atEnd() {
-               return $this->mRead;
-       }
-
-       /**
-        * @return bool|string
-        */
-       function readChunk() {
-               if ( $this->atEnd() ) {
-                       return false;
-               }
-               $this->mRead = true;
-               return $this->mString;
-       }
-}
-
-/**
- * Imports a XML dump from a file (either from file upload, files on disk, or HTTP)
- * @ingroup SpecialPage
- */
-class ImportStreamSource implements ImportSource {
-       function __construct( $handle ) {
-               $this->mHandle = $handle;
-       }
-
-       /**
-        * @return bool
-        */
-       function atEnd() {
-               return feof( $this->mHandle );
-       }
-
-       /**
-        * @return string
-        */
-       function readChunk() {
-               return fread( $this->mHandle, 32768 );
-       }
-
-       /**
-        * @param string $filename
-        * @return Status
-        */
-       static function newFromFile( $filename ) {
-               MediaWiki\suppressWarnings();
-               $file = fopen( $filename, 'rt' );
-               MediaWiki\restoreWarnings();
-               if ( !$file ) {
-                       return Status::newFatal( "importcantopen" );
-               }
-               return Status::newGood( new ImportStreamSource( $file ) );
-       }
-
-       /**
-        * @param string $fieldname
-        * @return Status
-        */
-       static function newFromUpload( $fieldname = "xmlimport" ) {
-               $upload =& $_FILES[$fieldname];
-
-               if ( $upload === null || !$upload['name'] ) {
-                       return Status::newFatal( 'importnofile' );
-               }
-               if ( !empty( $upload['error'] ) ) {
-                       switch ( $upload['error'] ) {
-                               case 1:
-                                       # The uploaded file exceeds the upload_max_filesize directive in php.ini.
-                                       return Status::newFatal( 'importuploaderrorsize' );
-                               case 2:
-                                       # The uploaded file exceeds the MAX_FILE_SIZE directive that
-                                       # was specified in the HTML form.
-                                       return Status::newFatal( 'importuploaderrorsize' );
-                               case 3:
-                                       # The uploaded file was only partially uploaded
-                                       return Status::newFatal( 'importuploaderrorpartial' );
-                               case 6:
-                                       # Missing a temporary folder.
-                                       return Status::newFatal( 'importuploaderrortemp' );
-                               # case else: # Currently impossible
-                       }
-
-               }
-               $fname = $upload['tmp_name'];
-               if ( is_uploaded_file( $fname ) ) {
-                       return ImportStreamSource::newFromFile( $fname );
-               } else {
-                       return Status::newFatal( 'importnofile' );
-               }
-       }
-
-       /**
-        * @param string $url
-        * @param string $method
-        * @return Status
-        */
-       static function newFromURL( $url, $method = 'GET' ) {
-               wfDebug( __METHOD__ . ": opening $url\n" );
-               # Use the standard HTTP fetch function; it times out
-               # quicker and sorts out user-agent problems which might
-               # otherwise prevent importing from large sites, such
-               # as the Wikimedia cluster, etc.
-               $data = Http::request( $method, $url, array( 'followRedirects' => true ), __METHOD__ );
-               if ( $data !== false ) {
-                       $file = tmpfile();
-                       fwrite( $file, $data );
-                       fflush( $file );
-                       fseek( $file, 0 );
-                       return Status::newGood( new ImportStreamSource( $file ) );
-               } else {
-                       return Status::newFatal( 'importcantopen' );
-               }
-       }
-
-       /**
-        * @param string $interwiki
-        * @param string $page
-        * @param bool $history
-        * @param bool $templates
-        * @param int $pageLinkDepth
-        * @return Status
-        */
-       public static function newFromInterwiki( $interwiki, $page, $history = false,
-               $templates = false, $pageLinkDepth = 0
-       ) {
-               if ( $page == '' ) {
-                       return Status::newFatal( 'import-noarticle' );
-               }
-
-               # Look up the first interwiki prefix, and let the foreign site handle
-               # subsequent interwiki prefixes
-               $firstIwPrefix = strtok( $interwiki, ':' );
-               $firstIw = Interwiki::fetch( $firstIwPrefix );
-               if ( !$firstIw ) {
-                       return Status::newFatal( 'importbadinterwiki' );
-               }
-
-               $additionalIwPrefixes = strtok( '' );
-               if ( $additionalIwPrefixes ) {
-                       $additionalIwPrefixes .= ':';
-               }
-               # Have to do a DB-key replacement ourselves; otherwise spaces get
-               # URL-encoded to +, which is wrong in this case. Similar to logic in
-               # Title::getLocalURL
-               $link = $firstIw->getURL( strtr( "${additionalIwPrefixes}Special:Export/$page",
-                       ' ', '_' ) );
-
-               $params = array();
-               if ( $history ) {
-                       $params['history'] = 1;
-               }
-               if ( $templates ) {
-                       $params['templates'] = 1;
-               }
-               if ( $pageLinkDepth ) {
-                       $params['pagelink-depth'] = $pageLinkDepth;
-               }
-
-               $url = wfAppendQuery( $link, $params );
-               # For interwikis, use POST to avoid redirects.
-               return ImportStreamSource::newFromURL( $url, "POST" );
-       }
-}
index 78eb458..3e0564b 100644 (file)
@@ -816,7 +816,7 @@ class OutputPage extends ContextSource {
 
                $clientHeader = $this->getRequest()->getHeader( 'If-Modified-Since' );
                if ( $clientHeader === false ) {
-                       wfDebug( __METHOD__ . ": client did not send If-Modified-Since header\n", 'log' );
+                       wfDebug( __METHOD__ . ": client did not send If-Modified-Since header", 'private' );
                        return false;
                }
 
@@ -845,17 +845,17 @@ class OutputPage extends ContextSource {
                }
 
                wfDebug( __METHOD__ . ": client sent If-Modified-Since: " .
-                       wfTimestamp( TS_ISO_8601, $clientHeaderTime ) . "\n", 'log' );
+                       wfTimestamp( TS_ISO_8601, $clientHeaderTime ), 'private' );
                wfDebug( __METHOD__ . ": effective Last-Modified: " .
-                       wfTimestamp( TS_ISO_8601, $maxModified ) . "\n", 'log' );
+                       wfTimestamp( TS_ISO_8601, $maxModified ), 'private' );
                if ( $clientHeaderTime < $maxModified ) {
-                       wfDebug( __METHOD__ . ": STALE, $info\n", 'log' );
+                       wfDebug( __METHOD__ . ": STALE, $info", 'private' );
                        return false;
                }
 
                # Not modified
                # Give a 304 Not Modified response code and disable body output
-               wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", 'log' );
+               wfDebug( __METHOD__ . ": NOT MODIFIED, $info", 'private' );
                ini_set( 'zlib.output_compression', 0 );
                $this->getRequest()->response()->statusHeader( 304 );
                $this->sendCacheControl();
@@ -2184,7 +2184,7 @@ class OutputPage extends ContextSource {
                                        # We'll purge the proxy cache explicitly, but require end user agents
                                        # to revalidate against the proxy on each visit.
                                        # Surrogate-Control controls our CDN, Cache-Control downstream caches
-                                       wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **\n", 'log' );
+                                       wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **", 'private' );
                                        # start with a shorter timeout for initial testing
                                        # header( 'Surrogate-Control: max-age=2678400+2678400, content="ESI/1.0"');
                                        $response->header( 'Surrogate-Control: max-age=' . $config->get( 'SquidMaxage' )
@@ -2195,7 +2195,7 @@ class OutputPage extends ContextSource {
                                        # to revalidate against the proxy on each visit.
                                        # IMPORTANT! The CDN needs to replace the Cache-Control header with
                                        # Cache-Control: s-maxage=0, must-revalidate, max-age=0
-                                       wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **\n", 'log' );
+                                       wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **", 'private' );
                                        # start with a shorter timeout for initial testing
                                        # header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" );
                                        $response->header( 'Cache-Control: s-maxage=' . $this->mCdnMaxage
@@ -2204,7 +2204,7 @@ class OutputPage extends ContextSource {
                        } else {
                                # We do want clients to cache if they can, but they *must* check for updates
                                # on revisiting the page.
-                               wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **\n", 'log' );
+                               wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **", 'private' );
                                $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
                                $response->header( "Cache-Control: private, must-revalidate, max-age=0" );
                        }
@@ -2212,7 +2212,7 @@ class OutputPage extends ContextSource {
                                $response->header( "Last-Modified: {$this->mLastModified}" );
                        }
                } else {
-                       wfDebug( __METHOD__ . ": no caching **\n", 'log' );
+                       wfDebug( __METHOD__ . ": no caching **", 'private' );
 
                        # In general, the absence of a last modified header should be enough to prevent
                        # the client from using its cache. We send a few other things just to make sure.
index 5b05755..eaad4de 100644 (file)
@@ -8,6 +8,21 @@
        "apihelp-main-param-format": "Гойту формат.",
        "apihelp-main-param-curtimestamp": "Хилламийн юкъатоха ханна йолу билгало",
        "apihelp-createaccount-param-name": "Декъашхочун цӀе.",
+       "apihelp-delete-description": "ДӀаяккха агӀо.",
+       "apihelp-edit-example-edit": "АгӀо таян",
+       "apihelp-emailuser-description": "Декъашхочунга кехат",
+       "apihelp-emailuser-param-target": "Электронан кехатан адрес.",
+       "apihelp-emailuser-param-subject": "Хьедаран корта.",
+       "apihelp-emailuser-param-text": "Кехатан чулацам",
+       "apihelp-expandtemplates-param-title": "АгӀонан корта.",
+       "apihelp-feedrecentchanges-param-tagfilter": "Тегийн литтар.",
+       "apihelp-login-example-login": "ЧугӀо",
+       "apihelp-logout-description": "ЧугӀой сессийн хаамаш дӀацӀанбе.",
+       "apihelp-move-description": "АгӀон цӀе хийца.",
+       "apihelp-opensearch-param-search": "Лахаран могӀа.",
+       "apihelp-parse-example-page": "АгӀо зер",
+       "apihelp-parse-example-text": "Wikitext зер.",
+       "apihelp-protect-example-protect": "Ларъе агӀо.",
        "apihelp-userrights-param-userid": "Декъашхочун ID.",
        "api-help-datatypes-header": "Хаамийн тайпанаш"
 }
index ded11b4..d745baa 100644 (file)
        "apihelp-query+blocks-paramvalue-prop-expiry": "Fügt den Zeitstempel wann die Sperre abläuft hinzu.",
        "apihelp-query+blocks-paramvalue-prop-reason": "Fügt den angegebenen Grund für die Sperrung hinzu.",
        "apihelp-query+blocks-paramvalue-prop-range": "Fügt den von der Sperrung betroffenen Bereich von IP-Adressen hinzu.",
+       "apihelp-query+blocks-paramvalue-prop-flags": "Markiert die Sperre mit (autoblock, anononly, etc.).",
        "apihelp-query+blocks-param-show": "Zeige nur Elemente, die diese Kriterien erfüllen. Um zum Beispiel unbestimmte Sperren von IP-Adressen zu sehen, setzte <kbd>$1show=ip|!temp</kbd>.",
        "apihelp-query+blocks-example-simple": "Sperren auflisten",
        "apihelp-query+blocks-example-users": "Listet Sperren der Benutzer <kbd>Alice</kbd> und <kbd>Bob</kbd> auf.",
        "apihelp-query+categories-example-generator": "Rufe Informationen über alle Kategorien ab, die in der Seite <kbd>Albert Einstein</kbd> eingetragen sind.",
        "apihelp-query+categoryinfo-description": "Gibt Informationen zu den angegebenen Kategorien zurück.",
        "apihelp-query+categorymembers-description": "Liste alle Seiten in der angegebenen Kategorie auf.",
+       "apihelp-query+categorymembers-param-prop": "Welche Informationsteile einbinden:",
        "apihelp-query+categorymembers-paramvalue-prop-ids": "Fügt die Seitenkennung hinzu.",
        "apihelp-query+categorymembers-paramvalue-prop-title": "Fügt die Titel- und Namensraum-ID der Seite hinzu.",
        "apihelp-query+categorymembers-paramvalue-prop-sortkey": "Fügt den Sortierungsschlüssel (hexadezimale Zeichenkette) hinzu, der verwendet wird, um innerhalb dieser Kategorie zu sortieren.",
        "apihelp-query+categorymembers-param-limit": "Die maximale Anzahl der zurückzugebenden Seiten.",
        "apihelp-query+categorymembers-param-sort": "Eigenschaft, nach der sortiert werden soll.",
        "apihelp-query+categorymembers-param-dir": "Sortierungsrichtung.",
+       "apihelp-query+categorymembers-param-start": "Zeitstempel bei dem die Auflistung beginnen soll. Darf nur zusammen mit <kbd>$1sort=timestamp</kbd> benutzt werden.",
+       "apihelp-query+categorymembers-param-end": "Zeitstempel bei dem die Auflistung enden soll. Darf nur zusammen mit <kbd>$1sort=timestamp</kbd> benutzt werden.",
+       "apihelp-query+categorymembers-param-starthexsortkey": "Sortierungsschlüssel bei dem die Auflistung beginnen soll, wie von <kbd>$1prop=sortkey</kbd> zurückgegeben. Darf nur zusammen mit <kbd>$1sort=sortkey</kbd> verwendet werden.",
+       "apihelp-query+categorymembers-param-endhexsortkey": "Suchschlüssel bei dem die Auflistung enden soll, wie von <kbd>$1prop=sortkey</kbd> zurückgegeben. Darf nur zusammen mit <kbd>$1sort=sortkey</kbd> verwendet werden.",
        "apihelp-query+categorymembers-param-startsortkey": "Stattdessen $1starthexsortkey verwenden.",
        "apihelp-query+categorymembers-param-endsortkey": "Stattdessen $1endhexsortkey verwenden.",
+       "apihelp-query+categorymembers-example-simple": "Rufe die ersten 10 Seiten von <kbd>Category:Physics</kbd> ab.",
+       "apihelp-query+categorymembers-example-generator": "Rufe die Seiteninformationen zu den ersten 10 Seiten von<kbd>Category:Physics</kbd> ab.",
        "apihelp-query+contributors-param-limit": "Wie viele Spender zurückgegeben werden sollen.",
+       "apihelp-query+contributors-example-simple": "Zeige Mitwirkende der Seite <kbd>Main Page</kbd>.",
+       "apihelp-query+deletedrevisions-param-tag": "Listet nur Bearbeitungen auf, die die angegebene Markierung haben.",
        "apihelp-query+deletedrevisions-param-user": "Nur Versionen von diesem Benutzer auflisten.",
+       "apihelp-query+deletedrevisions-param-excludeuser": "Schließe Bearbeitungen dieses Benutzers bei der Auflistung aus.",
+       "apihelp-query+deletedrevs-param-start": "Der Zeitstempel bei dem die Auflistung beginnen soll.",
+       "apihelp-query+deletedrevs-param-end": "Der Zeitstempel bei dem die Auflistung enden soll.",
        "apihelp-query+deletedrevs-param-from": "Auflistung bei diesem Titel beginnen.",
        "apihelp-query+deletedrevs-param-to": "Auflistung bei diesem Titel beenden.",
+       "apihelp-query+deletedrevs-param-prefix": "Suche nach allen Seitentiteln, die mit dem angegebenen Wert beginnen.",
+       "apihelp-query+deletedrevs-param-unique": "Listet nur eine Bearbeitung für jede Seite auf.",
+       "apihelp-query+deletedrevs-param-tag": "Listet nur Bearbeitungen auf, die die angegebene Markierung haben.",
+       "apihelp-query+deletedrevs-param-user": "Liste nur Bearbeitungen von diesem Benutzer auf.",
+       "apihelp-query+deletedrevs-param-excludeuser": "Schließe Bearbeitungen dieses Benutzers bei der Auflistung aus.",
+       "apihelp-query+deletedrevs-param-namespace": "Nur Seiten dieses Namensraums auflisten.",
+       "apihelp-query+deletedrevs-param-limit": "Die maximale Anzahl aufzulistendender Bearbeitungen.",
+       "apihelp-query+disabled-description": "Dieses Abfrage-Modul wurde deaktiviert.",
+       "apihelp-query+duplicatefiles-description": "Liste alle Dateien auf die, basierend auf der Prüfsumme, Duplikate der angegebenen Dateien sind.",
+       "apihelp-query+duplicatefiles-param-limit": "Wie viele doppelte Dateien zurückgeben.",
+       "apihelp-query+duplicatefiles-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+duplicatefiles-param-localonly": "Sucht nur nach Dateien im lokalen Repositorium.",
        "apihelp-query+duplicatefiles-example-simple": "Sucht nach Duplikaten von [[:File:Albert Einstein Head.jpg]].",
        "apihelp-query+duplicatefiles-example-generated": "Sucht nach Duplikaten aller Dateien.",
+       "apihelp-query+embeddedin-description": "Finde alle Seiten, die den angegebenen Titel einbetten (transkludieren).",
+       "apihelp-query+embeddedin-param-title": "Titel nach dem gesucht werden soll. Darf nicht zusammen mit $1pageid verwendet werden.",
+       "apihelp-query+embeddedin-param-pageid": "Seitenkennung nach der gesucht werden soll. Darf nicht zusammen mit $1title verwendet werden.",
        "apihelp-query+embeddedin-param-namespace": "Der aufzulistende Namensraum.",
+       "apihelp-query+embeddedin-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+embeddedin-param-filterredir": "Wie Weiterleitungen behandelt werden sollen.",
        "apihelp-query+embeddedin-param-limit": "Wie viele Seiten insgesamt zurückgegeben werden sollen.",
+       "apihelp-query+embeddedin-example-simple": "Zeige Seiten, die <kbd>Template:Stub</kbd> transkludieren.",
+       "apihelp-query+embeddedin-example-generator": "Rufe Informationen über Seiten ab, die <kbd>Template:Stub</kbd> transkludieren.",
+       "apihelp-query+extlinks-description": "Gebe alle externen URLs (nicht Interwiki) der angegebenen Seiten zurück.",
        "apihelp-query+extlinks-param-limit": "Wie viele Links zurückgegeben werden sollen.",
+       "apihelp-query+extlinks-example-simple": "Rufe eine Liste erxterner Verweise auf <kbd>Main Page</kbd> ab.",
+       "apihelp-query+exturlusage-description": "Listet Seiten auf, die die angegebene URL beinhalten.",
+       "apihelp-query+exturlusage-param-prop": "Welche Informationsteile einbinden:",
+       "apihelp-query+exturlusage-paramvalue-prop-ids": "Fügt die ID der Seite hinzu.",
+       "apihelp-query+exturlusage-paramvalue-prop-title": "Fügt die Titel- und Namensraum-ID der Seite hinzu.",
+       "apihelp-query+exturlusage-paramvalue-prop-url": "Fügt die URL, die in der Seite verwendet wird, hinzu.",
+       "apihelp-query+exturlusage-param-namespace": "Die aufzulistenden Seiten-Namensräume.",
        "apihelp-query+exturlusage-param-limit": "Wie viele Seiten zurückgegeben werden sollen.",
        "apihelp-query+filearchive-param-from": "Der Bildertitel, bei dem die Auflistung beginnen soll.",
        "apihelp-query+filearchive-param-to": "Der Bildertitel, bei dem die Auflistung enden soll.",
        "apihelp-query+filearchive-param-limit": "Wie viele Bilder insgesamt zurückgegeben werden sollen.",
+       "apihelp-query+filearchive-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+filearchive-paramvalue-prop-sha1": "Ergänzt die SHA-1-Prüfsumme für das Bild.",
        "apihelp-query+filearchive-paramvalue-prop-dimensions": "Alias für die Größe.",
        "apihelp-query+filearchive-paramvalue-prop-mediatype": "Ergänzt den Medientyp des Bildes.",
        "apihelp-query+imageinfo-param-start": "Zeitstempel, von dem die Liste beginnen soll.",
        "apihelp-query+imageinfo-param-end": "Zeitstempel, an dem die Liste enden soll.",
        "apihelp-query+imageinfo-param-urlheight": "Ähnlich wie $1urlwidth.",
+       "apihelp-query+images-param-dir": "Die Auflistungsrichtung.",
+       "apihelp-query+imageusage-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+info-description": "Ruft Basisinformationen über die Seite ab.",
        "apihelp-query+info-paramvalue-prop-watchers": "Die Anzahl der Beobachter, falls erlaubt.",
        "apihelp-query+info-param-testactions": "Überprüft, ob der aktuelle Benutzer gewisse Aktionen auf der Seite ausführen kann.",
        "apihelp-query+iwbacklinks-param-prefix": "Präfix für das Interwiki.",
        "apihelp-query+iwbacklinks-paramvalue-prop-iwprefix": "Ergänzt das Präfix des Interwikis.",
        "apihelp-query+iwbacklinks-paramvalue-prop-iwtitle": "Ergänzt den Titel des Interwikis.",
+       "apihelp-query+iwbacklinks-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+iwlinks-paramvalue-prop-url": "Ergänzt die vollständige URL.",
+       "apihelp-query+iwlinks-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+langbacklinks-param-limit": "Wie viele Gesamtseiten zurückgegeben werden sollen.",
+       "apihelp-query+langbacklinks-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+langbacklinks-example-simple": "Ruft Seiten ab, die auf [[:fr:Test]] verlinken.",
        "apihelp-query+langlinks-param-limit": "Wie viele Sprachlinks zurückgegeben werden sollen.",
        "apihelp-query+langlinks-paramvalue-prop-url": "Ergänzt die vollständige URL.",
+       "apihelp-query+langlinks-param-dir": "Die Auflistungsrichtung.",
+       "apihelp-query+links-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+links-example-simple": "Links von der <kbd>Hauptseite</kbd> abrufen",
        "apihelp-query+linkshere-description": "Alle Seiten finden, die auf die angegebenen Seiten verlinken.",
        "apihelp-query+logevents-description": "Ereignisse von den Logbüchern abrufen.",
+       "apihelp-query+logevents-example-simple": "Listet die letzten Logbuch-Ereignisse auf.",
        "apihelp-query+pageswithprop-paramvalue-prop-ids": "Fügt die Seitenkennung hinzu.",
+       "apihelp-query+pageswithprop-param-limit": "Die maximale Anzahl zurückzugebender Seiten.",
        "apihelp-query+prefixsearch-param-search": "Such-Zeichenfolge.",
+       "apihelp-query+prefixsearch-param-offset": "Anzahl der zu überspringenden Ergebnisse.",
        "apihelp-query+recentchanges-paramvalue-prop-timestamp": "Ergänzt den Zeitstempel für die Bearbeitung.",
        "apihelp-query+recentchanges-paramvalue-prop-tags": "Listet Markierungen für den Eintrag auf.",
        "apihelp-query+recentchanges-example-simple": "Listet die letzten Änderungen auf.",
        "apihelp-query+siteinfo-example-simple": "Websiteinformationen abrufen",
        "apihelp-query+tags-description": "Änderungs-Tags auflisten.",
        "apihelp-query+tags-example-simple": "Verfügbare Tags auflisten",
+       "apihelp-query+templates-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+usercontribs-description": "Alle Bearbeitungen von einem Benutzer abrufen.",
        "apihelp-query+usercontribs-param-limit": "Die maximale Anzahl der zurückzugebenden Beiträge.",
        "apihelp-query+usercontribs-param-start": "Der zurückzugebende Start-Zeitstempel.",
index 8b93819..5eaa449 100644 (file)
        "apihelp-delete-example-simple": "<kbd>Main Page</kbd> törlése.",
        "apihelp-edit-example-edit": "Lap szerkesztése",
        "apihelp-expandtemplates-param-title": "Lap címe.",
-       "apihelp-userrights-param-userid": "Felhasználói azonosító."
+       "apihelp-userrights-param-userid": "Felhasználói azonosító.",
+       "api-help-title": "MediaWiki API súgó",
+       "api-help-lead": "Ez egy automatikusan generált MediaWiki API-dokumentációs lap.\n\nDokumentáció és példák: https://www.mediawiki.org/wiki/API",
+       "api-help-main-header": "Fő modul",
+       "api-help-flag-deprecated": "Ez a modul elavult.",
+       "api-help-flag-internal": "<strong>Ez a modul belső használatú vagy nem stabil.</strong> A működése értesítés nélkül változhat.",
+       "api-help-flag-readrights": "Ez a modul olvasási jogot igényel.",
+       "api-help-flag-writerights": "Ez a modul írási jogot igényel.",
+       "api-help-flag-mustbeposted": "Ez a modul csak POST kéréseket fogad el.",
+       "api-help-flag-generator": "Ez a modul használható generátorként.",
+       "api-help-source": "Forrás: $1",
+       "api-help-source-unknown": "Forrás: <span class=\"apihelp-unknown\">ismeretlen</span>",
+       "api-help-license": "Licenc: [[$1|$2]]",
+       "api-help-license-noname": "Licenc: [[$1|Lásd a linken]]",
+       "api-help-license-unknown": "Licenc: <span class=\"apihelp-unknown\">ismeretlen</span>",
+       "api-help-parameters": "{{PLURAL:$1|Paraméter|Paraméterek}}:",
+       "api-help-param-deprecated": "Elavult.",
+       "api-help-param-required": "Ez a paraméter kötelező.",
+       "api-help-datatypes-header": "Adattípusok",
+       "api-help-param-type-limit": "Típus: egész vagy <kbd>max</kbd>",
+       "api-help-param-type-integer": "Típus: {{PLURAL:$1|1=egész|2=egészek listája}}",
+       "api-help-param-type-boolean": "Típus: logikai ([[Special:ApiHelp/main#main/datatypes|részletek]])",
+       "api-help-param-type-timestamp": "Típus: {{PLURAL:$1|1=időbélyeg|2=időbélyegek listája}} ([[Special:ApiHelp/main#main/datatypes|engedélyezett formátumok]])",
+       "api-help-param-type-user": "Típus: {{PLURAL:$1|1=felhasználónév|2=felhasználónevek listája}}",
+       "api-help-param-list": "{{PLURAL:$1|1=A következő értékek egyike|2=Értékek (elválasztó: <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Üresnek kell lennie|Lehet üres vagy $2}}",
+       "api-help-param-limit": "Nem engedélyezett több mint $1.",
+       "api-help-param-limit2": "Nem engedélyezett több mint $1 (botoknak $2).",
+       "api-help-param-integer-min": "Az {{PLURAL:$1|1=érték nem lehet kisebb|2=értékek nem lehetnek kisebbek}} mint $2.",
+       "api-help-param-integer-max": "Az {{PLURAL:$1|1=érték nem lehet nagyobb|2=értékek nem lehetnek nagyobbak}} mint $3.",
+       "api-help-param-integer-minmax": "{{PLURAL:$1|1=Az értéknek $2 és $3 között kell lennie.|2=Az értékeknek $2 és $3 között kell lenniük.}}"
 }
index 010ff04..31541a5 100644 (file)
@@ -11,7 +11,8 @@
                        "Nemo bis",
                        "Amire80",
                        "Siebrand",
-                       "Purodha"
+                       "Purodha",
+                       "Tacsipacsi"
                ]
        },
        "apihelp-main-description": "{{doc-apihelp-description|main}}",
        "api-help-param-required": "Displayed in the API help for any required parameter",
        "api-help-datatypes-header": "Header for the data type section in the API help output",
        "api-help-datatypes": "{{technical}} {{doc-important|Do not translate or reformat dates inside &lt;kbd%gt; tags}} Documentation of certain API data types\nSee also:\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
-       "api-help-param-type-limit": "{{technical}} {{doc-important|Do not translate text inside &lt;kbd%gt; tags}} Used to indicate that a parameter is a \"limit\" type. Parameters:\n* $1 - Always 1.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
+       "api-help-param-type-limit": "{{technical}} {{doc-important|Do not translate text inside &lt;kbd&gt; tags}} Used to indicate that a parameter is a \"limit\" type. Parameters:\n* $1 - Always 1.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
        "api-help-param-type-integer": "{{technical}} Used to indicate that a parameter is an integer or list of integers. Parameters:\n* $1 - 1 if the parameter takes one value, 2 if the parameter takes a list of values.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
        "api-help-param-type-boolean": "{{technical}} {{doc-important|Do not translate <code>Special:ApiHelp</code> in this message.}} Used to indicate that a parameter is a boolean. Parameters:\n* $1 - Always 1.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
        "api-help-param-type-password": "{{ignored}}{{technical}} Used to indicate that a parameter is a password or list of passwords. Parameters:\n* $1 - 1 if the parameter takes one value, 2 if the parameter takes a list of values.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
diff --git a/includes/api/i18n/tt-cyrl.json b/includes/api/i18n/tt-cyrl.json
new file mode 100644 (file)
index 0000000..54c534c
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Ильнар"
+               ]
+       },
+       "apihelp-feedcontributions-param-newonly": "Битләр ясау үзгәртмәләрен генә күрсәтү."
+}
index 298f6e2..29db9c6 100644 (file)
@@ -178,7 +178,7 @@ class HTMLFileCache extends FileCacheBase {
                        return $text;
                }
 
-               wfDebug( __METHOD__ . "()\n", 'log' );
+               wfDebug( __METHOD__ . "()\n", 'private' );
 
                $now = wfTimestampNow();
                if ( $this->useGzip() ) {
index abd4e3a..3fec522 100644 (file)
@@ -1286,13 +1286,14 @@ abstract class DatabaseBase implements IDatabase {
         * @param string|array $cond The condition array. See DatabaseBase::select() for details.
         * @param string $fname The function name of the caller.
         * @param string|array $options The query options. See DatabaseBase::select() for details.
+        * @param string|array $join_conds The join conditions. See DatabaseBase::select() for details.
         *
         * @return bool|array The values from the field, or false on failure
         * @throws DBUnexpectedError
         * @since 1.25
         */
        public function selectFieldValues(
-               $table, $var, $cond = '', $fname = __METHOD__, $options = array()
+               $table, $var, $cond = '', $fname = __METHOD__, $options = array(), $join_conds = array()
        ) {
                if ( $var === '*' ) { // sanity
                        throw new DBUnexpectedError( $this, "Cannot use a * field: got '$var'" );
@@ -1302,7 +1303,7 @@ abstract class DatabaseBase implements IDatabase {
                        $options = array( $options );
                }
 
-               $res = $this->select( $table, $var, $cond, $fname, $options );
+               $res = $this->select( $table, $var, $cond, $fname, $options, $join_conds );
                if ( $res === false ) {
                        return false;
                }
index 841636c..8f943bf 100644 (file)
@@ -300,7 +300,7 @@ class MWDebug {
                        trigger_error( $msg, $level );
                }
 
-               wfDebugLog( $group, $msg, 'log' );
+               wfDebugLog( $group, $msg, 'private' );
        }
 
        /**
index ef7d819..34ea641 100644 (file)
@@ -90,8 +90,10 @@ class LegacyLogger extends AbstractLogger {
                        $destination = self::destination( $this->channel, $message, $context );
                        self::emit( $text, $destination );
                }
-               // Add to debug toolbar
-               MWDebug::debugMsg( $message, array( 'channel' => $this->channel ) + $context );
+               if ( !isset( $context['private'] ) || !$context['private'] ) {
+                       // Add to debug toolbar if not marked as "private"
+                       MWDebug::debugMsg( $message, array( 'channel' => $this->channel ) + $context );
+               }
        }
 
        /**
@@ -116,6 +118,13 @@ class LegacyLogger extends AbstractLogger {
                        // All messages on the wfErrorLog channel should be emitted.
                        $shouldEmit = true;
 
+               } elseif ( $channel === 'wfDebug' ) {
+                       // wfDebug messages are emitted if a catch all logging file has
+                       // been specified. Checked explicitly so that 'private' flagged
+                       // messages are not discarded by unset $wgDebugLogGroups channel
+                       // handling below.
+                       $shouldEmit = $wgDebugLogFile != '';
+
                } elseif ( isset( $wgDebugLogGroups[$channel] ) ) {
                        $logConfig = $wgDebugLogGroups[$channel];
 
index ec7a6b2..c299166 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Sends dump output via the p7zip compressor.
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index 8767b92..bbc1c11 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Sends dump output via the bgzip2 compressor.
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index de1c0a5..4bec7d4 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Stream outputter to send data to a file.
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index 224262d..5c27658 100644 (file)
@@ -4,7 +4,7 @@
  * This just does output filtering and streaming; XML formatting is done
  * higher up, so be careful in what you do.
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index 3f0333e..d9e74a7 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Sends dump output via the gzip compressor.
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index d21dfa9..d3742b7 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Dump output filter to include only the last revision in each page sequence.
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index 2f5a782..6fe11a3 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Base class for output stream; prints to stdout or buffer or wherever.
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index c7d1b2e..e8d4428 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Dump output filter to include or exclude pages in a given set of namespaces.
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index 9974d67..d99b1b1 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Simple dump output filter to exclude all talk pages.
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index bdcaf35..edd73fc 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Base class for output stream; prints to stdout or buffer or wherever.
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index b4ad672..61177ab 100644 (file)
@@ -4,7 +4,7 @@
  * Even if compression is available in a library, using a separate
  * program can allow us to make use of a multi-processor system.
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index a24418c..ab2632d 100644 (file)
@@ -2,7 +2,7 @@
 /**
  * Base class for exporting
  *
- * Copyright © 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
+ * Copyright Â© 2003, 2005, 2006 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  *
  * This program is free software; you can redistribute it and/or modify
index faa1314..95a7897 100644 (file)
@@ -49,7 +49,7 @@ class FileOpBatch {
         *   - a) unexpected operation errors occurred (network partitions, disk full...)
         *   - b) significant operation errors occurred and 'force' was not set
         *
-        * @param array $performOps List of FileOp operations
+        * @param FileOp[] $performOps List of FileOp operations
         * @param array $opts Batch operation options
         * @param FileJournal $journal Journal to log operations to
         * @return Status
@@ -147,6 +147,7 @@ class FileOpBatch {
        protected static function runParallelBatches( array $pPerformOps, Status $status ) {
                $aborted = false; // set to true on unexpected errors
                foreach ( $pPerformOps as $performOpsBatch ) {
+                       /** @var FileOp[] $performOpsBatch */
                        if ( $aborted ) { // check batch op abort flag...
                                // We can't continue (even with $ignoreErrors) as $predicates is wrong.
                                // Log the remaining ops as failed for recovery...
@@ -157,6 +158,7 @@ class FileOpBatch {
                                }
                                continue;
                        }
+                       /** @var Status[] $statuses */
                        $statuses = array();
                        $opHandles = array();
                        // Get the backend; all sub-batch ops belong to a single backend
diff --git a/includes/import/ImportSource.php b/includes/import/ImportSource.php
new file mode 100644 (file)
index 0000000..75d20b4
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Source interface for XML import.
+ *
+ * Copyright © 2003,2005 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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 SpecialPage
+ */
+
+/**
+ * Source interface for XML import.
+ *
+ * @ingroup SpecialPage
+ */
+interface ImportSource {
+
+       /**
+        * Indicates whether the end of the input has been reached.
+        * Will return true after a finite number of calls to readChunk.
+        *
+        * @return bool true if there is no more input, false otherwise.
+        */
+       function atEnd();
+
+       /**
+        * Return a chunk of the input, as a (possibly empty) string.
+        * When the end of input is reached, readChunk() returns false.
+        * If atEnd() returns false, readChunk() will return a string.
+        * If atEnd() returns true, readChunk() will return false.
+        *
+        * @return bool|string
+        */
+       function readChunk();
+}
diff --git a/includes/import/ImportStreamSource.php b/includes/import/ImportStreamSource.php
new file mode 100644 (file)
index 0000000..0e03d9f
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+/**
+ * MediaWiki page data importer.
+ *
+ * Copyright © 2003,2005 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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 SpecialPage
+ */
+
+/**
+ * Imports a XML dump from a file (either from file upload, files on disk, or HTTP)
+ * @ingroup SpecialPage
+ */
+class ImportStreamSource implements ImportSource {
+       function __construct( $handle ) {
+               $this->mHandle = $handle;
+       }
+
+       /**
+        * @return bool
+        */
+       function atEnd() {
+               return feof( $this->mHandle );
+       }
+
+       /**
+        * @return string
+        */
+       function readChunk() {
+               return fread( $this->mHandle, 32768 );
+       }
+
+       /**
+        * @param string $filename
+        * @return Status
+        */
+       static function newFromFile( $filename ) {
+               MediaWiki\suppressWarnings();
+               $file = fopen( $filename, 'rt' );
+               MediaWiki\restoreWarnings();
+               if ( !$file ) {
+                       return Status::newFatal( "importcantopen" );
+               }
+               return Status::newGood( new ImportStreamSource( $file ) );
+       }
+
+       /**
+        * @param string $fieldname
+        * @return Status
+        */
+       static function newFromUpload( $fieldname = "xmlimport" ) {
+               $upload =& $_FILES[$fieldname];
+
+               if ( $upload === null || !$upload['name'] ) {
+                       return Status::newFatal( 'importnofile' );
+               }
+               if ( !empty( $upload['error'] ) ) {
+                       switch ( $upload['error'] ) {
+                               case 1:
+                                       # The uploaded file exceeds the upload_max_filesize directive in php.ini.
+                                       return Status::newFatal( 'importuploaderrorsize' );
+                               case 2:
+                                       # The uploaded file exceeds the MAX_FILE_SIZE directive that
+                                       # was specified in the HTML form.
+                                       return Status::newFatal( 'importuploaderrorsize' );
+                               case 3:
+                                       # The uploaded file was only partially uploaded
+                                       return Status::newFatal( 'importuploaderrorpartial' );
+                               case 6:
+                                       # Missing a temporary folder.
+                                       return Status::newFatal( 'importuploaderrortemp' );
+                               # case else: # Currently impossible
+                       }
+
+               }
+               $fname = $upload['tmp_name'];
+               if ( is_uploaded_file( $fname ) ) {
+                       return ImportStreamSource::newFromFile( $fname );
+               } else {
+                       return Status::newFatal( 'importnofile' );
+               }
+       }
+
+       /**
+        * @param string $url
+        * @param string $method
+        * @return Status
+        */
+       static function newFromURL( $url, $method = 'GET' ) {
+               wfDebug( __METHOD__ . ": opening $url\n" );
+               # Use the standard HTTP fetch function; it times out
+               # quicker and sorts out user-agent problems which might
+               # otherwise prevent importing from large sites, such
+               # as the Wikimedia cluster, etc.
+               $data = Http::request( $method, $url, array( 'followRedirects' => true ), __METHOD__ );
+               if ( $data !== false ) {
+                       $file = tmpfile();
+                       fwrite( $file, $data );
+                       fflush( $file );
+                       fseek( $file, 0 );
+                       return Status::newGood( new ImportStreamSource( $file ) );
+               } else {
+                       return Status::newFatal( 'importcantopen' );
+               }
+       }
+
+       /**
+        * @param string $interwiki
+        * @param string $page
+        * @param bool $history
+        * @param bool $templates
+        * @param int $pageLinkDepth
+        * @return Status
+        */
+       public static function newFromInterwiki( $interwiki, $page, $history = false,
+               $templates = false, $pageLinkDepth = 0
+       ) {
+               if ( $page == '' ) {
+                       return Status::newFatal( 'import-noarticle' );
+               }
+
+               # Look up the first interwiki prefix, and let the foreign site handle
+               # subsequent interwiki prefixes
+               $firstIwPrefix = strtok( $interwiki, ':' );
+               $firstIw = Interwiki::fetch( $firstIwPrefix );
+               if ( !$firstIw ) {
+                       return Status::newFatal( 'importbadinterwiki' );
+               }
+
+               $additionalIwPrefixes = strtok( '' );
+               if ( $additionalIwPrefixes ) {
+                       $additionalIwPrefixes .= ':';
+               }
+               # Have to do a DB-key replacement ourselves; otherwise spaces get
+               # URL-encoded to +, which is wrong in this case. Similar to logic in
+               # Title::getLocalURL
+               $link = $firstIw->getURL( strtr( "${additionalIwPrefixes}Special:Export/$page",
+                       ' ', '_' ) );
+
+               $params = array();
+               if ( $history ) {
+                       $params['history'] = 1;
+               }
+               if ( $templates ) {
+                       $params['templates'] = 1;
+               }
+               if ( $pageLinkDepth ) {
+                       $params['pagelink-depth'] = $pageLinkDepth;
+               }
+
+               $url = wfAppendQuery( $link, $params );
+               # For interwikis, use POST to avoid redirects.
+               return ImportStreamSource::newFromURL( $url, "POST" );
+       }
+}
diff --git a/includes/import/ImportStringSource.php b/includes/import/ImportStringSource.php
new file mode 100644 (file)
index 0000000..85983b1
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * MediaWiki page data importer.
+ *
+ * Copyright © 2003,2005 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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 SpecialPage
+ */
+
+/**
+ * Used for importing XML dumps where the content of the dump is in a string.
+ * This class is ineffecient, and should only be used for small dumps.
+ * For larger dumps, ImportStreamSource should be used instead.
+ *
+ * @ingroup SpecialPage
+ */
+class ImportStringSource implements ImportSource {
+       function __construct( $string ) {
+               $this->mString = $string;
+               $this->mRead = false;
+       }
+
+       /**
+        * @return bool
+        */
+       function atEnd() {
+               return $this->mRead;
+       }
+
+       /**
+        * @return bool|string
+        */
+       function readChunk() {
+               if ( $this->atEnd() ) {
+                       return false;
+               }
+               $this->mRead = true;
+               return $this->mString;
+       }
+}
diff --git a/includes/import/UploadSourceAdapter.php b/includes/import/UploadSourceAdapter.php
new file mode 100644 (file)
index 0000000..17fbdfb
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+/**
+ * MediaWiki page data importer.
+ *
+ * Copyright © 2003,2005 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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 SpecialPage
+ */
+
+/**
+ * This is a horrible hack used to keep source compatibility.
+ * @ingroup SpecialPage
+ */
+class UploadSourceAdapter {
+       /** @var array */
+       public static $sourceRegistrations = array();
+
+       /** @var string */
+       private $mSource;
+
+       /** @var string */
+       private $mBuffer;
+
+       /** @var int */
+       private $mPosition;
+
+       /**
+        * @param ImportSource $source
+        * @return string
+        */
+       static function registerSource( ImportSource $source ) {
+               $id = wfRandomString();
+
+               self::$sourceRegistrations[$id] = $source;
+
+               return $id;
+       }
+
+       /**
+        * @param string $path
+        * @param string $mode
+        * @param array $options
+        * @param string $opened_path
+        * @return bool
+        */
+       function stream_open( $path, $mode, $options, &$opened_path ) {
+               $url = parse_url( $path );
+               $id = $url['host'];
+
+               if ( !isset( self::$sourceRegistrations[$id] ) ) {
+                       return false;
+               }
+
+               $this->mSource = self::$sourceRegistrations[$id];
+
+               return true;
+       }
+
+       /**
+        * @param int $count
+        * @return string
+        */
+       function stream_read( $count ) {
+               $return = '';
+               $leave = false;
+
+               while ( !$leave && !$this->mSource->atEnd() &&
+                               strlen( $this->mBuffer ) < $count ) {
+                       $read = $this->mSource->readChunk();
+
+                       if ( !strlen( $read ) ) {
+                               $leave = true;
+                       }
+
+                       $this->mBuffer .= $read;
+               }
+
+               if ( strlen( $this->mBuffer ) ) {
+                       $return = substr( $this->mBuffer, 0, $count );
+                       $this->mBuffer = substr( $this->mBuffer, $count );
+               }
+
+               $this->mPosition += strlen( $return );
+
+               return $return;
+       }
+
+       /**
+        * @param string $data
+        * @return bool
+        */
+       function stream_write( $data ) {
+               return false;
+       }
+
+       /**
+        * @return mixed
+        */
+       function stream_tell() {
+               return $this->mPosition;
+       }
+
+       /**
+        * @return bool
+        */
+       function stream_eof() {
+               return $this->mSource->atEnd();
+       }
+
+       /**
+        * @return array
+        */
+       function url_stat() {
+               $result = array();
+
+               $result['dev'] = $result[0] = 0;
+               $result['ino'] = $result[1] = 0;
+               $result['mode'] = $result[2] = 0;
+               $result['nlink'] = $result[3] = 0;
+               $result['uid'] = $result[4] = 0;
+               $result['gid'] = $result[5] = 0;
+               $result['rdev'] = $result[6] = 0;
+               $result['size'] = $result[7] = 0;
+               $result['atime'] = $result[8] = 0;
+               $result['mtime'] = $result[9] = 0;
+               $result['ctime'] = $result[10] = 0;
+               $result['blksize'] = $result[11] = 0;
+               $result['blocks'] = $result[12] = 0;
+
+               return $result;
+       }
+}
diff --git a/includes/import/WikiImporter.php b/includes/import/WikiImporter.php
new file mode 100644 (file)
index 0000000..9bf9282
--- /dev/null
@@ -0,0 +1,1070 @@
+<?php
+/**
+ * MediaWiki page data importer.
+ *
+ * Copyright © 2003,2005 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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 SpecialPage
+ */
+
+/**
+ * XML file reader for the page data importer.
+ *
+ * implements Special:Import
+ * @ingroup SpecialPage
+ */
+class WikiImporter {
+       private $reader = null;
+       private $foreignNamespaces = null;
+       private $mLogItemCallback, $mUploadCallback, $mRevisionCallback, $mPageCallback;
+       private $mSiteInfoCallback, $mPageOutCallback;
+       private $mNoticeCallback, $mDebug;
+       private $mImportUploads, $mImageBasePath;
+       private $mNoUpdates = false;
+       /** @var Config */
+       private $config;
+       /** @var ImportTitleFactory */
+       private $importTitleFactory;
+       /** @var array */
+       private $countableCache = array();
+
+       /**
+        * Creates an ImportXMLReader drawing from the source provided
+        * @param ImportSource $source
+        * @param Config $config
+        * @throws Exception
+        */
+       function __construct( ImportSource $source, Config $config = null ) {
+               if ( !class_exists( 'XMLReader' ) ) {
+                       throw new Exception( 'Import requires PHP to have been compiled with libxml support' );
+               }
+
+               $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' );
+               }
+               $id = UploadSourceAdapter::registerSource( $source );
+
+               // Enable the entity loader, as it is needed for loading external URLs via
+               // XMLReader::open (T86036)
+               $oldDisable = libxml_disable_entity_loader( false );
+               if ( defined( 'LIBXML_PARSEHUGE' ) ) {
+                       $status = $this->reader->open( "uploadsource://$id", null, LIBXML_PARSEHUGE );
+               } else {
+                       $status = $this->reader->open( "uploadsource://$id" );
+               }
+               if ( !$status ) {
+                       $error = libxml_get_last_error();
+                       libxml_disable_entity_loader( $oldDisable );
+                       throw new MWException( 'Encountered an internal error while initializing WikiImporter object: ' .
+                               $error->message );
+               }
+               libxml_disable_entity_loader( $oldDisable );
+
+               // Default callbacks
+               $this->setPageCallback( array( $this, 'beforeImportPage' ) );
+               $this->setRevisionCallback( array( $this, "importRevision" ) );
+               $this->setUploadCallback( array( $this, 'importUpload' ) );
+               $this->setLogItemCallback( array( $this, 'importLogItem' ) );
+               $this->setPageOutCallback( array( $this, 'finishImportPage' ) );
+
+               $this->importTitleFactory = new NaiveImportTitleFactory();
+       }
+
+       /**
+        * @return null|XMLReader
+        */
+       public function getReader() {
+               return $this->reader;
+       }
+
+       public function throwXmlError( $err ) {
+               $this->debug( "FAILURE: $err" );
+               wfDebug( "WikiImporter XML error: $err\n" );
+       }
+
+       public function debug( $data ) {
+               if ( $this->mDebug ) {
+                       wfDebug( "IMPORT: $data\n" );
+               }
+       }
+
+       public function warn( $data ) {
+               wfDebug( "IMPORT: $data\n" );
+       }
+
+       public function notice( $msg /*, $param, ...*/ ) {
+               $params = func_get_args();
+               array_shift( $params );
+
+               if ( is_callable( $this->mNoticeCallback ) ) {
+                       call_user_func( $this->mNoticeCallback, $msg, $params );
+               } else { # No ImportReporter -> CLI
+                       echo wfMessage( $msg, $params )->text() . "\n";
+               }
+       }
+
+       /**
+        * Set debug mode...
+        * @param bool $debug
+        */
+       function setDebug( $debug ) {
+               $this->mDebug = $debug;
+       }
+
+       /**
+        * Set 'no updates' mode. In this mode, the link tables will not be updated by the importer
+        * @param bool $noupdates
+        */
+       function setNoUpdates( $noupdates ) {
+               $this->mNoUpdates = $noupdates;
+       }
+
+       /**
+        * Set a callback that displays notice messages
+        *
+        * @param callable $callback
+        * @return callable
+        */
+       public function setNoticeCallback( $callback ) {
+               return wfSetVar( $this->mNoticeCallback, $callback );
+       }
+
+       /**
+        * Sets the action to perform as each new page in the stream is reached.
+        * @param callable $callback
+        * @return callable
+        */
+       public function setPageCallback( $callback ) {
+               $previous = $this->mPageCallback;
+               $this->mPageCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform as each page in the stream is completed.
+        * Callback accepts the page title (as a Title object), a second object
+        * with the original title form (in case it's been overridden into a
+        * local namespace), and a count of revisions.
+        *
+        * @param callable $callback
+        * @return callable
+        */
+       public function setPageOutCallback( $callback ) {
+               $previous = $this->mPageOutCallback;
+               $this->mPageOutCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform as each page revision is reached.
+        * @param callable $callback
+        * @return callable
+        */
+       public function setRevisionCallback( $callback ) {
+               $previous = $this->mRevisionCallback;
+               $this->mRevisionCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform as each file upload version is reached.
+        * @param callable $callback
+        * @return callable
+        */
+       public function setUploadCallback( $callback ) {
+               $previous = $this->mUploadCallback;
+               $this->mUploadCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform as each log item reached.
+        * @param callable $callback
+        * @return callable
+        */
+       public function setLogItemCallback( $callback ) {
+               $previous = $this->mLogItemCallback;
+               $this->mLogItemCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform when site info is encountered
+        * @param callable $callback
+        * @return callable
+        */
+       public function setSiteInfoCallback( $callback ) {
+               $previous = $this->mSiteInfoCallback;
+               $this->mSiteInfoCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the factory object to use to convert ForeignTitle objects into local
+        * Title objects
+        * @param ImportTitleFactory $factory
+        */
+       public function setImportTitleFactory( $factory ) {
+               $this->importTitleFactory = $factory;
+       }
+
+       /**
+        * Set a target namespace to override the defaults
+        * @param null|int $namespace
+        * @return bool
+        */
+       public function setTargetNamespace( $namespace ) {
+               if ( is_null( $namespace ) ) {
+                       // Don't override namespaces
+                       $this->setImportTitleFactory( new NaiveImportTitleFactory() );
+                       return true;
+               } elseif (
+                       $namespace >= 0 &&
+                       MWNamespace::exists( intval( $namespace ) )
+               ) {
+                       $namespace = intval( $namespace );
+                       $this->setImportTitleFactory( new NamespaceImportTitleFactory( $namespace ) );
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Set a target root page under which all pages are imported
+        * @param null|string $rootpage
+        * @return Status
+        */
+       public function setTargetRootPage( $rootpage ) {
+               $status = Status::newGood();
+               if ( is_null( $rootpage ) ) {
+                       // No rootpage
+                       $this->setImportTitleFactory( new NaiveImportTitleFactory() );
+               } elseif ( $rootpage !== '' ) {
+                       $rootpage = rtrim( $rootpage, '/' ); // avoid double slashes
+                       $title = Title::newFromText( $rootpage );
+
+                       if ( !$title || $title->isExternal() ) {
+                               $status->fatal( 'import-rootpage-invalid' );
+                       } else {
+                               if ( !MWNamespace::hasSubpages( $title->getNamespace() ) ) {
+                                       global $wgContLang;
+
+                                       $displayNSText = $title->getNamespace() == NS_MAIN
+                                               ? wfMessage( 'blanknamespace' )->text()
+                                               : $wgContLang->getNsText( $title->getNamespace() );
+                                       $status->fatal( 'import-rootpage-nosubpage', $displayNSText );
+                               } else {
+                                       // set namespace to 'all', so the namespace check in processTitle() can pass
+                                       $this->setTargetNamespace( null );
+                                       $this->setImportTitleFactory( new SubpageImportTitleFactory( $title ) );
+                               }
+                       }
+               }
+               return $status;
+       }
+
+       /**
+        * @param string $dir
+        */
+       public function setImageBasePath( $dir ) {
+               $this->mImageBasePath = $dir;
+       }
+
+       /**
+        * @param bool $import
+        */
+       public function setImportUploads( $import ) {
+               $this->mImportUploads = $import;
+       }
+
+       /**
+        * Default per-page callback. Sets up some things related to site statistics
+        * @param array $titleAndForeignTitle Two-element array, with Title object at
+        * index 0 and ForeignTitle object at index 1
+        * @return bool
+        */
+       public function beforeImportPage( $titleAndForeignTitle ) {
+               $title = $titleAndForeignTitle[0];
+               $page = WikiPage::factory( $title );
+               $this->countableCache['title_' . $title->getPrefixedText()] = $page->isCountable();
+               return true;
+       }
+
+       /**
+        * Default per-revision callback, performs the import.
+        * @param WikiRevision $revision
+        * @return bool
+        */
+       public function importRevision( $revision ) {
+               if ( !$revision->getContentHandler()->canBeUsedOn( $revision->getTitle() ) ) {
+                       $this->notice( 'import-error-bad-location',
+                               $revision->getTitle()->getPrefixedText(),
+                               $revision->getID(),
+                               $revision->getModel(),
+                               $revision->getFormat() );
+
+                       return false;
+               }
+
+               try {
+                       $dbw = wfGetDB( DB_MASTER );
+                       return $dbw->deadlockLoop( array( $revision, 'importOldRevision' ) );
+               } catch ( MWContentSerializationException $ex ) {
+                       $this->notice( 'import-error-unserialize',
+                               $revision->getTitle()->getPrefixedText(),
+                               $revision->getID(),
+                               $revision->getModel(),
+                               $revision->getFormat() );
+               }
+
+               return false;
+       }
+
+       /**
+        * Default per-revision callback, performs the import.
+        * @param WikiRevision $revision
+        * @return bool
+        */
+       public function importLogItem( $revision ) {
+               $dbw = wfGetDB( DB_MASTER );
+               return $dbw->deadlockLoop( array( $revision, 'importLogItem' ) );
+       }
+
+       /**
+        * Dummy for now...
+        * @param WikiRevision $revision
+        * @return bool
+        */
+       public function importUpload( $revision ) {
+               $dbw = wfGetDB( DB_MASTER );
+               return $dbw->deadlockLoop( array( $revision, 'importUpload' ) );
+       }
+
+       /**
+        * Mostly for hook use
+        * @param Title $title
+        * @param ForeignTitle $foreignTitle
+        * @param int $revCount
+        * @param int $sRevCount
+        * @param array $pageInfo
+        * @return bool
+        */
+       public function finishImportPage( $title, $foreignTitle, $revCount,
+                       $sRevCount, $pageInfo ) {
+
+               // Update article count statistics (T42009)
+               // The normal counting logic in WikiPage->doEditUpdates() is designed for
+               // one-revision-at-a-time editing, not bulk imports. In this situation it
+               // suffers from issues of slave lag. We let WikiPage handle the total page
+               // and revision count, and we implement our own custom logic for the
+               // article (content page) count.
+               $page = WikiPage::factory( $title );
+               $page->loadPageData( 'fromdbmaster' );
+               $content = $page->getContent();
+               if ( $content === null ) {
+                       wfDebug( __METHOD__ . ': Skipping article count adjustment for ' . $title .
+                               ' because WikiPage::getContent() returned null' );
+               } else {
+                       $editInfo = $page->prepareContentForEdit( $content );
+                       $countKey = 'title_' . $title->getPrefixedText();
+                       $countable = $page->isCountable( $editInfo );
+                       if ( array_key_exists( $countKey, $this->countableCache ) &&
+                               $countable != $this->countableCache[$countKey] ) {
+                               DeferredUpdates::addUpdate( SiteStatsUpdate::factory( array(
+                                       'articles' => ( (int)$countable - (int)$this->countableCache[$countKey] )
+                               ) ) );
+                       }
+               }
+
+               $args = func_get_args();
+               return Hooks::run( 'AfterImportPage', $args );
+       }
+
+       /**
+        * Alternate per-revision callback, for debugging.
+        * @param WikiRevision $revision
+        */
+       public function debugRevisionHandler( &$revision ) {
+               $this->debug( "Got revision:" );
+               if ( is_object( $revision->title ) ) {
+                       $this->debug( "-- Title: " . $revision->title->getPrefixedText() );
+               } else {
+                       $this->debug( "-- Title: <invalid>" );
+               }
+               $this->debug( "-- User: " . $revision->user_text );
+               $this->debug( "-- Timestamp: " . $revision->timestamp );
+               $this->debug( "-- Comment: " . $revision->comment );
+               $this->debug( "-- Text: " . $revision->text );
+       }
+
+       /**
+        * Notify the callback function of site info
+        * @param array $siteInfo
+        * @return bool|mixed
+        */
+       private function siteInfoCallback( $siteInfo ) {
+               if ( isset( $this->mSiteInfoCallback ) ) {
+                       return call_user_func_array( $this->mSiteInfoCallback,
+                                       array( $siteInfo, $this ) );
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Notify the callback function when a new "<page>" is reached.
+        * @param Title $title
+        */
+       function pageCallback( $title ) {
+               if ( isset( $this->mPageCallback ) ) {
+                       call_user_func( $this->mPageCallback, $title );
+               }
+       }
+
+       /**
+        * Notify the callback function when a "</page>" is closed.
+        * @param Title $title
+        * @param ForeignTitle $foreignTitle
+        * @param int $revCount
+        * @param int $sucCount Number of revisions for which callback returned true
+        * @param array $pageInfo Associative array of page information
+        */
+       private function pageOutCallback( $title, $foreignTitle, $revCount,
+                       $sucCount, $pageInfo ) {
+               if ( isset( $this->mPageOutCallback ) ) {
+                       $args = func_get_args();
+                       call_user_func_array( $this->mPageOutCallback, $args );
+               }
+       }
+
+       /**
+        * Notify the callback function of a revision
+        * @param WikiRevision $revision
+        * @return bool|mixed
+        */
+       private function revisionCallback( $revision ) {
+               if ( isset( $this->mRevisionCallback ) ) {
+                       return call_user_func_array( $this->mRevisionCallback,
+                                       array( $revision, $this ) );
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Notify the callback function of a new log item
+        * @param WikiRevision $revision
+        * @return bool|mixed
+        */
+       private function logItemCallback( $revision ) {
+               if ( isset( $this->mLogItemCallback ) ) {
+                       return call_user_func_array( $this->mLogItemCallback,
+                                       array( $revision, $this ) );
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Retrieves the contents of the named attribute of the current element.
+        * @param string $attr The name of the attribute
+        * @return string The value of the attribute or an empty string if it is not set in the current
+        * element.
+        */
+       public function nodeAttribute( $attr ) {
+               return $this->reader->getAttribute( $attr );
+       }
+
+       /**
+        * Shouldn't something like this be built-in to XMLReader?
+        * Fetches text contents of the current element, assuming
+        * no sub-elements or such scary things.
+        * @return string
+        * @access private
+        */
+       public function nodeContents() {
+               if ( $this->reader->isEmptyElement ) {
+                       return "";
+               }
+               $buffer = "";
+               while ( $this->reader->read() ) {
+                       switch ( $this->reader->nodeType ) {
+                       case XMLReader::TEXT:
+                       case XMLReader::CDATA:
+                       case XMLReader::SIGNIFICANT_WHITESPACE:
+                               $buffer .= $this->reader->value;
+                               break;
+                       case XMLReader::END_ELEMENT:
+                               return $buffer;
+                       }
+               }
+
+               $this->reader->close();
+               return '';
+       }
+
+       /**
+        * Primary entry point
+        * @throws MWException
+        * @return bool
+        */
+       public function doImport() {
+               // Calls to reader->read need to be wrapped in calls to
+               // libxml_disable_entity_loader() to avoid local file
+               // inclusion attacks (bug 46932).
+               $oldDisable = libxml_disable_entity_loader( true );
+               $this->reader->read();
+
+               if ( $this->reader->localName != 'mediawiki' ) {
+                       libxml_disable_entity_loader( $oldDisable );
+                       throw new MWException( "Expected <mediawiki> tag, got " .
+                               $this->reader->localName );
+               }
+               $this->debug( "<mediawiki> tag is correct." );
+
+               $this->debug( "Starting primary dump processing loop." );
+
+               $keepReading = $this->reader->read();
+               $skip = false;
+               $rethrow = null;
+               try {
+                       while ( $keepReading ) {
+                               $tag = $this->reader->localName;
+                               $type = $this->reader->nodeType;
+
+                               if ( !Hooks::run( 'ImportHandleToplevelXMLTag', array( $this ) ) ) {
+                                       // Do nothing
+                               } elseif ( $tag == 'mediawiki' && $type == XMLReader::END_ELEMENT ) {
+                                       break;
+                               } elseif ( $tag == 'siteinfo' ) {
+                                       $this->handleSiteInfo();
+                               } elseif ( $tag == 'page' ) {
+                                       $this->handlePage();
+                               } elseif ( $tag == 'logitem' ) {
+                                       $this->handleLogItem();
+                               } elseif ( $tag != '#text' ) {
+                                       $this->warn( "Unhandled top-level XML tag $tag" );
+
+                                       $skip = true;
+                               }
+
+                               if ( $skip ) {
+                                       $keepReading = $this->reader->next();
+                                       $skip = false;
+                                       $this->debug( "Skip" );
+                               } else {
+                                       $keepReading = $this->reader->read();
+                               }
+                       }
+               } catch ( Exception $ex ) {
+                       $rethrow = $ex;
+               }
+
+               // finally
+               libxml_disable_entity_loader( $oldDisable );
+               $this->reader->close();
+
+               if ( $rethrow ) {
+                       throw $rethrow;
+               }
+
+               return true;
+       }
+
+       private function handleSiteInfo() {
+               $this->debug( "Enter site info handler." );
+               $siteInfo = array();
+
+               // Fields that can just be stuffed in the siteInfo object
+               $normalFields = array( 'sitename', 'base', 'generator', 'case' );
+
+               while ( $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XmlReader::END_ELEMENT &&
+                                       $this->reader->localName == 'siteinfo' ) {
+                               break;
+                       }
+
+                       $tag = $this->reader->localName;
+
+                       if ( $tag == 'namespace' ) {
+                               $this->foreignNamespaces[$this->nodeAttribute( 'key' )] =
+                                       $this->nodeContents();
+                       } elseif ( in_array( $tag, $normalFields ) ) {
+                               $siteInfo[$tag] = $this->nodeContents();
+                       }
+               }
+
+               $siteInfo['_namespaces'] = $this->foreignNamespaces;
+               $this->siteInfoCallback( $siteInfo );
+       }
+
+       private function handleLogItem() {
+               $this->debug( "Enter log item handler." );
+               $logInfo = array();
+
+               // Fields that can just be stuffed in the pageInfo object
+               $normalFields = array( 'id', 'comment', 'type', 'action', 'timestamp',
+                                       'logtitle', 'params' );
+
+               while ( $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
+                                       $this->reader->localName == 'logitem' ) {
+                               break;
+                       }
+
+                       $tag = $this->reader->localName;
+
+                       if ( !Hooks::run( 'ImportHandleLogItemXMLTag', array(
+                               $this, $logInfo
+                       ) ) ) {
+                               // Do nothing
+                       } elseif ( in_array( $tag, $normalFields ) ) {
+                               $logInfo[$tag] = $this->nodeContents();
+                       } elseif ( $tag == 'contributor' ) {
+                               $logInfo['contributor'] = $this->handleContributor();
+                       } elseif ( $tag != '#text' ) {
+                               $this->warn( "Unhandled log-item XML tag $tag" );
+                       }
+               }
+
+               $this->processLogItem( $logInfo );
+       }
+
+       /**
+        * @param array $logInfo
+        * @return bool|mixed
+        */
+       private function processLogItem( $logInfo ) {
+
+               $revision = new WikiRevision( $this->config );
+
+               if ( isset( $logInfo['id'] ) ) {
+                       $revision->setID( $logInfo['id'] );
+               }
+               $revision->setType( $logInfo['type'] );
+               $revision->setAction( $logInfo['action'] );
+               if ( isset( $logInfo['timestamp'] ) ) {
+                       $revision->setTimestamp( $logInfo['timestamp'] );
+               }
+               if ( isset( $logInfo['params'] ) ) {
+                       $revision->setParams( $logInfo['params'] );
+               }
+               if ( isset( $logInfo['logtitle'] ) ) {
+                       // @todo Using Title for non-local titles is a recipe for disaster.
+                       // We should use ForeignTitle here instead.
+                       $revision->setTitle( Title::newFromText( $logInfo['logtitle'] ) );
+               }
+
+               $revision->setNoUpdates( $this->mNoUpdates );
+
+               if ( isset( $logInfo['comment'] ) ) {
+                       $revision->setComment( $logInfo['comment'] );
+               }
+
+               if ( isset( $logInfo['contributor']['ip'] ) ) {
+                       $revision->setUserIP( $logInfo['contributor']['ip'] );
+               }
+
+               if ( !isset( $logInfo['contributor']['username'] ) ) {
+                       $revision->setUsername( 'Unknown user' );
+               } else {
+                       $revision->setUserName( $logInfo['contributor']['username'] );
+               }
+
+               return $this->logItemCallback( $revision );
+       }
+
+       private function handlePage() {
+               // Handle page data.
+               $this->debug( "Enter page handler." );
+               $pageInfo = array( 'revisionCount' => 0, 'successfulRevisionCount' => 0 );
+
+               // Fields that can just be stuffed in the pageInfo object
+               $normalFields = array( 'title', 'ns', 'id', 'redirect', 'restrictions' );
+
+               $skip = false;
+               $badTitle = false;
+
+               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
+                                       $this->reader->localName == 'page' ) {
+                               break;
+                       }
+
+                       $skip = false;
+
+                       $tag = $this->reader->localName;
+
+                       if ( $badTitle ) {
+                               // The title is invalid, bail out of this page
+                               $skip = true;
+                       } elseif ( !Hooks::run( 'ImportHandlePageXMLTag', array( $this,
+                                               &$pageInfo ) ) ) {
+                               // Do nothing
+                       } elseif ( in_array( $tag, $normalFields ) ) {
+                               // An XML snippet:
+                               // <page>
+                               //     <id>123</id>
+                               //     <title>Page</title>
+                               //     <redirect title="NewTitle"/>
+                               //     ...
+                               // Because the redirect tag is built differently, we need special handling for that case.
+                               if ( $tag == 'redirect' ) {
+                                       $pageInfo[$tag] = $this->nodeAttribute( 'title' );
+                               } else {
+                                       $pageInfo[$tag] = $this->nodeContents();
+                               }
+                       } elseif ( $tag == 'revision' || $tag == 'upload' ) {
+                               if ( !isset( $title ) ) {
+                                       $title = $this->processTitle( $pageInfo['title'],
+                                               isset( $pageInfo['ns'] ) ? $pageInfo['ns'] : null );
+
+                                       // $title is either an array of two titles or false.
+                                       if ( is_array( $title ) ) {
+                                               $this->pageCallback( $title );
+                                               list( $pageInfo['_title'], $foreignTitle ) = $title;
+                                       } else {
+                                               $badTitle = true;
+                                               $skip = true;
+                                       }
+                               }
+
+                               if ( $title ) {
+                                       if ( $tag == 'revision' ) {
+                                               $this->handleRevision( $pageInfo );
+                                       } else {
+                                               $this->handleUpload( $pageInfo );
+                                       }
+                               }
+                       } elseif ( $tag != '#text' ) {
+                               $this->warn( "Unhandled page XML tag $tag" );
+                               $skip = true;
+                       }
+               }
+
+               // @note $pageInfo is only set if a valid $title is processed above with
+               //       no error. If we have a valid $title, then pageCallback is called
+               //       above, $pageInfo['title'] is set and we do pageOutCallback here.
+               //       If $pageInfo['_title'] is not set, then $foreignTitle is also not
+               //       set since they both come from $title above.
+               if ( array_key_exists( '_title', $pageInfo ) ) {
+                       $this->pageOutCallback( $pageInfo['_title'], $foreignTitle,
+                                       $pageInfo['revisionCount'],
+                                       $pageInfo['successfulRevisionCount'],
+                                       $pageInfo );
+               }
+       }
+
+       /**
+        * @param array $pageInfo
+        */
+       private function handleRevision( &$pageInfo ) {
+               $this->debug( "Enter revision handler" );
+               $revisionInfo = array();
+
+               $normalFields = array( 'id', 'timestamp', 'comment', 'minor', 'model', 'format', 'text' );
+
+               $skip = false;
+
+               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
+                                       $this->reader->localName == 'revision' ) {
+                               break;
+                       }
+
+                       $tag = $this->reader->localName;
+
+                       if ( !Hooks::run( 'ImportHandleRevisionXMLTag', array(
+                               $this, $pageInfo, $revisionInfo
+                       ) ) ) {
+                               // Do nothing
+                       } elseif ( in_array( $tag, $normalFields ) ) {
+                               $revisionInfo[$tag] = $this->nodeContents();
+                       } elseif ( $tag == 'contributor' ) {
+                               $revisionInfo['contributor'] = $this->handleContributor();
+                       } elseif ( $tag != '#text' ) {
+                               $this->warn( "Unhandled revision XML tag $tag" );
+                               $skip = true;
+                       }
+               }
+
+               $pageInfo['revisionCount']++;
+               if ( $this->processRevision( $pageInfo, $revisionInfo ) ) {
+                       $pageInfo['successfulRevisionCount']++;
+               }
+       }
+
+       /**
+        * @param array $pageInfo
+        * @param array $revisionInfo
+        * @return bool|mixed
+        */
+       private function processRevision( $pageInfo, $revisionInfo ) {
+               global $wgMaxArticleSize;
+
+               // Make sure revisions won't violate $wgMaxArticleSize, which could lead to
+               // database errors and instability. Testing for revisions with only listed
+               // content models, as other content models might use serialization formats
+               // which aren't checked against $wgMaxArticleSize.
+               if ( ( !isset( $revisionInfo['model'] ) ||
+                       in_array( $revisionInfo['model'], array(
+                               'wikitext',
+                               'css',
+                               'json',
+                               'javascript',
+                               'text',
+                               ''
+                       ) ) ) &&
+                       (int)( strlen( $revisionInfo['text'] ) / 1024 ) > $wgMaxArticleSize
+               ) {
+                       throw new MWException( 'The text of ' .
+                               ( isset( $revisionInfo['id'] ) ?
+                                       "the revision with ID $revisionInfo[id]" :
+                                       'a revision'
+                               ) . " exceeds the maximum allowable size ($wgMaxArticleSize KB)" );
+               }
+
+               $revision = new WikiRevision( $this->config );
+
+               if ( isset( $revisionInfo['id'] ) ) {
+                       $revision->setID( $revisionInfo['id'] );
+               }
+               if ( isset( $revisionInfo['model'] ) ) {
+                       $revision->setModel( $revisionInfo['model'] );
+               }
+               if ( isset( $revisionInfo['format'] ) ) {
+                       $revision->setFormat( $revisionInfo['format'] );
+               }
+               $revision->setTitle( $pageInfo['_title'] );
+
+               if ( isset( $revisionInfo['text'] ) ) {
+                       $handler = $revision->getContentHandler();
+                       $text = $handler->importTransform(
+                               $revisionInfo['text'],
+                               $revision->getFormat() );
+
+                       $revision->setText( $text );
+               }
+               if ( isset( $revisionInfo['timestamp'] ) ) {
+                       $revision->setTimestamp( $revisionInfo['timestamp'] );
+               } else {
+                       $revision->setTimestamp( wfTimestampNow() );
+               }
+
+               if ( isset( $revisionInfo['comment'] ) ) {
+                       $revision->setComment( $revisionInfo['comment'] );
+               }
+
+               if ( isset( $revisionInfo['minor'] ) ) {
+                       $revision->setMinor( true );
+               }
+               if ( isset( $revisionInfo['contributor']['ip'] ) ) {
+                       $revision->setUserIP( $revisionInfo['contributor']['ip'] );
+               } elseif ( isset( $revisionInfo['contributor']['username'] ) ) {
+                       $revision->setUserName( $revisionInfo['contributor']['username'] );
+               } else {
+                       $revision->setUserName( 'Unknown user' );
+               }
+               $revision->setNoUpdates( $this->mNoUpdates );
+
+               return $this->revisionCallback( $revision );
+       }
+
+       /**
+        * @param array $pageInfo
+        * @return mixed
+        */
+       private function handleUpload( &$pageInfo ) {
+               $this->debug( "Enter upload handler" );
+               $uploadInfo = array();
+
+               $normalFields = array( 'timestamp', 'comment', 'filename', 'text',
+                                       'src', 'size', 'sha1base36', 'archivename', 'rel' );
+
+               $skip = false;
+
+               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
+                                       $this->reader->localName == 'upload' ) {
+                               break;
+                       }
+
+                       $tag = $this->reader->localName;
+
+                       if ( !Hooks::run( 'ImportHandleUploadXMLTag', array(
+                               $this, $pageInfo
+                       ) ) ) {
+                               // Do nothing
+                       } elseif ( in_array( $tag, $normalFields ) ) {
+                               $uploadInfo[$tag] = $this->nodeContents();
+                       } elseif ( $tag == 'contributor' ) {
+                               $uploadInfo['contributor'] = $this->handleContributor();
+                       } elseif ( $tag == 'contents' ) {
+                               $contents = $this->nodeContents();
+                               $encoding = $this->reader->getAttribute( 'encoding' );
+                               if ( $encoding === 'base64' ) {
+                                       $uploadInfo['fileSrc'] = $this->dumpTemp( base64_decode( $contents ) );
+                                       $uploadInfo['isTempSrc'] = true;
+                               }
+                       } elseif ( $tag != '#text' ) {
+                               $this->warn( "Unhandled upload XML tag $tag" );
+                               $skip = true;
+                       }
+               }
+
+               if ( $this->mImageBasePath && isset( $uploadInfo['rel'] ) ) {
+                       $path = "{$this->mImageBasePath}/{$uploadInfo['rel']}";
+                       if ( file_exists( $path ) ) {
+                               $uploadInfo['fileSrc'] = $path;
+                               $uploadInfo['isTempSrc'] = false;
+                       }
+               }
+
+               if ( $this->mImportUploads ) {
+                       return $this->processUpload( $pageInfo, $uploadInfo );
+               }
+       }
+
+       /**
+        * @param string $contents
+        * @return string
+        */
+       private function dumpTemp( $contents ) {
+               $filename = tempnam( wfTempDir(), 'importupload' );
+               file_put_contents( $filename, $contents );
+               return $filename;
+       }
+
+       /**
+        * @param array $pageInfo
+        * @param array $uploadInfo
+        * @return mixed
+        */
+       private function processUpload( $pageInfo, $uploadInfo ) {
+               $revision = new WikiRevision( $this->config );
+               $text = isset( $uploadInfo['text'] ) ? $uploadInfo['text'] : '';
+
+               $revision->setTitle( $pageInfo['_title'] );
+               $revision->setID( $pageInfo['id'] );
+               $revision->setTimestamp( $uploadInfo['timestamp'] );
+               $revision->setText( $text );
+               $revision->setFilename( $uploadInfo['filename'] );
+               if ( isset( $uploadInfo['archivename'] ) ) {
+                       $revision->setArchiveName( $uploadInfo['archivename'] );
+               }
+               $revision->setSrc( $uploadInfo['src'] );
+               if ( isset( $uploadInfo['fileSrc'] ) ) {
+                       $revision->setFileSrc( $uploadInfo['fileSrc'],
+                               !empty( $uploadInfo['isTempSrc'] ) );
+               }
+               if ( isset( $uploadInfo['sha1base36'] ) ) {
+                       $revision->setSha1Base36( $uploadInfo['sha1base36'] );
+               }
+               $revision->setSize( intval( $uploadInfo['size'] ) );
+               $revision->setComment( $uploadInfo['comment'] );
+
+               if ( isset( $uploadInfo['contributor']['ip'] ) ) {
+                       $revision->setUserIP( $uploadInfo['contributor']['ip'] );
+               }
+               if ( isset( $uploadInfo['contributor']['username'] ) ) {
+                       $revision->setUserName( $uploadInfo['contributor']['username'] );
+               }
+               $revision->setNoUpdates( $this->mNoUpdates );
+
+               return call_user_func( $this->mUploadCallback, $revision );
+       }
+
+       /**
+        * @return array
+        */
+       private function handleContributor() {
+               $fields = array( 'id', 'ip', 'username' );
+               $info = array();
+
+               if ( $this->reader->isEmptyElement ) {
+                       return $info;
+               }
+               while ( $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
+                                       $this->reader->localName == 'contributor' ) {
+                               break;
+                       }
+
+                       $tag = $this->reader->localName;
+
+                       if ( in_array( $tag, $fields ) ) {
+                               $info[$tag] = $this->nodeContents();
+                       }
+               }
+
+               return $info;
+       }
+
+       /**
+        * @param string $text
+        * @param string|null $ns
+        * @return array|bool
+        */
+       private function processTitle( $text, $ns = null ) {
+               if ( is_null( $this->foreignNamespaces ) ) {
+                       $foreignTitleFactory = new NaiveForeignTitleFactory();
+               } else {
+                       $foreignTitleFactory = new NamespaceAwareForeignTitleFactory(
+                               $this->foreignNamespaces );
+               }
+
+               $foreignTitle = $foreignTitleFactory->createForeignTitle( $text,
+                       intval( $ns ) );
+
+               $title = $this->importTitleFactory->createTitleFromForeignTitle(
+                       $foreignTitle );
+
+               $commandLineMode = $this->config->get( 'CommandLineMode' );
+               if ( is_null( $title ) ) {
+                       # Invalid page title? Ignore the page
+                       $this->notice( 'import-error-invalid', $foreignTitle->getFullText() );
+                       return false;
+               } elseif ( $title->isExternal() ) {
+                       $this->notice( 'import-error-interwiki', $title->getPrefixedText() );
+                       return false;
+               } elseif ( !$title->canExist() ) {
+                       $this->notice( 'import-error-special', $title->getPrefixedText() );
+                       return false;
+               } 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' ) && !$commandLineMode ) {
+                       # Do not import if the importing wiki user cannot create this page
+                       $this->notice( 'import-error-create', $title->getPrefixedText() );
+                       return false;
+               }
+
+               return array( $title, $foreignTitle );
+       }
+}
diff --git a/includes/import/WikiRevision.php b/includes/import/WikiRevision.php
new file mode 100644 (file)
index 0000000..9b8c74c
--- /dev/null
@@ -0,0 +1,677 @@
+<?php
+/**
+ * MediaWiki page data importer.
+ *
+ * Copyright © 2003,2005 Brion Vibber <brion@pobox.com>
+ * https://www.mediawiki.org/
+ *
+ * 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 SpecialPage
+ */
+
+/**
+ * Represents a revision, log entry or upload during the import process.
+ * This class sticks closely to the structure of the XML dump.
+ *
+ * @ingroup SpecialPage
+ */
+class WikiRevision {
+       /** @todo Unused? */
+       public $importer = null;
+
+       /** @var Title */
+       public $title = null;
+
+       /** @var int */
+       public $id = 0;
+
+       /** @var string */
+       public $timestamp = "20010115000000";
+
+       /**
+        * @var int
+        * @todo Can't find any uses. Public, because that's suspicious. Get clarity. */
+       public $user = 0;
+
+       /** @var string */
+       public $user_text = "";
+
+       /** @var string */
+       public $model = null;
+
+       /** @var string */
+       public $format = null;
+
+       /** @var string */
+       public $text = "";
+
+       /** @var int */
+       protected $size;
+
+       /** @var Content */
+       public $content = null;
+
+       /** @var ContentHandler */
+       protected $contentHandler = null;
+
+       /** @var string */
+       public $comment = "";
+
+       /** @var bool */
+       public $minor = false;
+
+       /** @var string */
+       public $type = "";
+
+       /** @var string */
+       public $action = "";
+
+       /** @var string */
+       public $params = "";
+
+       /** @var string */
+       public $fileSrc = '';
+
+       /** @var bool|string */
+       public $sha1base36 = false;
+
+       /**
+        * @var bool
+        * @todo Unused?
+        */
+       public $isTemp = false;
+
+       /** @var string */
+       public $archiveName = '';
+
+       protected $filename;
+
+       /** @var mixed */
+       protected $src;
+
+       /** @todo Unused? */
+       public $fileIsTemp;
+
+       /** @var bool */
+       private $mNoUpdates = false;
+
+       /** @var Config $config */
+       private $config;
+
+       public function __construct( Config $config ) {
+               $this->config = $config;
+       }
+
+       /**
+        * @param Title $title
+        * @throws MWException
+        */
+       function setTitle( $title ) {
+               if ( is_object( $title ) ) {
+                       $this->title = $title;
+               } elseif ( is_null( $title ) ) {
+                       throw new MWException( "WikiRevision given a null title in import. "
+                               . "You may need to adjust \$wgLegalTitleChars." );
+               } else {
+                       throw new MWException( "WikiRevision given non-object title in import." );
+               }
+       }
+
+       /**
+        * @param int $id
+        */
+       function setID( $id ) {
+               $this->id = $id;
+       }
+
+       /**
+        * @param string $ts
+        */
+       function setTimestamp( $ts ) {
+               # 2003-08-05T18:30:02Z
+               $this->timestamp = wfTimestamp( TS_MW, $ts );
+       }
+
+       /**
+        * @param string $user
+        */
+       function setUsername( $user ) {
+               $this->user_text = $user;
+       }
+
+       /**
+        * @param string $ip
+        */
+       function setUserIP( $ip ) {
+               $this->user_text = $ip;
+       }
+
+       /**
+        * @param string $model
+        */
+       function setModel( $model ) {
+               $this->model = $model;
+       }
+
+       /**
+        * @param string $format
+        */
+       function setFormat( $format ) {
+               $this->format = $format;
+       }
+
+       /**
+        * @param string $text
+        */
+       function setText( $text ) {
+               $this->text = $text;
+       }
+
+       /**
+        * @param string $text
+        */
+       function setComment( $text ) {
+               $this->comment = $text;
+       }
+
+       /**
+        * @param bool $minor
+        */
+       function setMinor( $minor ) {
+               $this->minor = (bool)$minor;
+       }
+
+       /**
+        * @param mixed $src
+        */
+       function setSrc( $src ) {
+               $this->src = $src;
+       }
+
+       /**
+        * @param string $src
+        * @param bool $isTemp
+        */
+       function setFileSrc( $src, $isTemp ) {
+               $this->fileSrc = $src;
+               $this->fileIsTemp = $isTemp;
+       }
+
+       /**
+        * @param string $sha1base36
+        */
+       function setSha1Base36( $sha1base36 ) {
+               $this->sha1base36 = $sha1base36;
+       }
+
+       /**
+        * @param string $filename
+        */
+       function setFilename( $filename ) {
+               $this->filename = $filename;
+       }
+
+       /**
+        * @param string $archiveName
+        */
+       function setArchiveName( $archiveName ) {
+               $this->archiveName = $archiveName;
+       }
+
+       /**
+        * @param int $size
+        */
+       function setSize( $size ) {
+               $this->size = intval( $size );
+       }
+
+       /**
+        * @param string $type
+        */
+       function setType( $type ) {
+               $this->type = $type;
+       }
+
+       /**
+        * @param string $action
+        */
+       function setAction( $action ) {
+               $this->action = $action;
+       }
+
+       /**
+        * @param array $params
+        */
+       function setParams( $params ) {
+               $this->params = $params;
+       }
+
+       /**
+        * @param bool $noupdates
+        */
+       public function setNoUpdates( $noupdates ) {
+               $this->mNoUpdates = $noupdates;
+       }
+
+       /**
+        * @return Title
+        */
+       function getTitle() {
+               return $this->title;
+       }
+
+       /**
+        * @return int
+        */
+       function getID() {
+               return $this->id;
+       }
+
+       /**
+        * @return string
+        */
+       function getTimestamp() {
+               return $this->timestamp;
+       }
+
+       /**
+        * @return string
+        */
+       function getUser() {
+               return $this->user_text;
+       }
+
+       /**
+        * @return string
+        *
+        * @deprecated Since 1.21, use getContent() instead.
+        */
+       function getText() {
+               ContentHandler::deprecated( __METHOD__, '1.21' );
+
+               return $this->text;
+       }
+
+       /**
+        * @return ContentHandler
+        */
+       function getContentHandler() {
+               if ( is_null( $this->contentHandler ) ) {
+                       $this->contentHandler = ContentHandler::getForModelID( $this->getModel() );
+               }
+
+               return $this->contentHandler;
+       }
+
+       /**
+        * @return Content
+        */
+       function getContent() {
+               if ( is_null( $this->content ) ) {
+                       $handler = $this->getContentHandler();
+                       $this->content = $handler->unserializeContent( $this->text, $this->getFormat() );
+               }
+
+               return $this->content;
+       }
+
+       /**
+        * @return string
+        */
+       function getModel() {
+               if ( is_null( $this->model ) ) {
+                       $this->model = $this->getTitle()->getContentModel();
+               }
+
+               return $this->model;
+       }
+
+       /**
+        * @return string
+        */
+       function getFormat() {
+               if ( is_null( $this->format ) ) {
+                       $this->format = $this->getContentHandler()->getDefaultFormat();
+               }
+
+               return $this->format;
+       }
+
+       /**
+        * @return string
+        */
+       function getComment() {
+               return $this->comment;
+       }
+
+       /**
+        * @return bool
+        */
+       function getMinor() {
+               return $this->minor;
+       }
+
+       /**
+        * @return mixed
+        */
+       function getSrc() {
+               return $this->src;
+       }
+
+       /**
+        * @return bool|string
+        */
+       function getSha1() {
+               if ( $this->sha1base36 ) {
+                       return Wikimedia\base_convert( $this->sha1base36, 36, 16 );
+               }
+               return false;
+       }
+
+       /**
+        * @return string
+        */
+       function getFileSrc() {
+               return $this->fileSrc;
+       }
+
+       /**
+        * @return bool
+        */
+       function isTempSrc() {
+               return $this->isTemp;
+       }
+
+       /**
+        * @return mixed
+        */
+       function getFilename() {
+               return $this->filename;
+       }
+
+       /**
+        * @return string
+        */
+       function getArchiveName() {
+               return $this->archiveName;
+       }
+
+       /**
+        * @return mixed
+        */
+       function getSize() {
+               return $this->size;
+       }
+
+       /**
+        * @return string
+        */
+       function getType() {
+               return $this->type;
+       }
+
+       /**
+        * @return string
+        */
+       function getAction() {
+               return $this->action;
+       }
+
+       /**
+        * @return string
+        */
+       function getParams() {
+               return $this->params;
+       }
+
+       /**
+        * @return bool
+        */
+       function importOldRevision() {
+               $dbw = wfGetDB( DB_MASTER );
+
+               # Sneak a single revision into place
+               $user = User::newFromName( $this->getUser() );
+               if ( $user ) {
+                       $userId = intval( $user->getId() );
+                       $userText = $user->getName();
+                       $userObj = $user;
+               } else {
+                       $userId = 0;
+                       $userText = $this->getUser();
+                       $userObj = new User;
+               }
+
+               // avoid memory leak...?
+               Title::clearCaches();
+
+               $page = WikiPage::factory( $this->title );
+               $page->loadPageData( 'fromdbmaster' );
+               if ( !$page->exists() ) {
+                       # must create the page...
+                       $pageId = $page->insertOn( $dbw );
+                       $created = true;
+                       $oldcountable = null;
+               } else {
+                       $pageId = $page->getId();
+                       $created = false;
+
+                       $prior = $dbw->selectField( 'revision', '1',
+                               array( 'rev_page' => $pageId,
+                                       'rev_timestamp' => $dbw->timestamp( $this->timestamp ),
+                                       'rev_user_text' => $userText,
+                                       'rev_comment' => $this->getComment() ),
+                               __METHOD__
+                       );
+                       if ( $prior ) {
+                               // @todo FIXME: This could fail slightly for multiple matches :P
+                               wfDebug( __METHOD__ . ": skipping existing revision for [[" .
+                                       $this->title->getPrefixedText() . "]], timestamp " . $this->timestamp . "\n" );
+                               return false;
+                       }
+               }
+
+               // Select previous version to make size diffs correct
+               $prevId = $dbw->selectField( 'revision', 'rev_id',
+                       array(
+                               'rev_page' => $pageId,
+                               'rev_timestamp <= ' . $dbw->timestamp( $this->timestamp ),
+                       ),
+                       __METHOD__,
+                       array( 'ORDER BY' => array(
+                                       'rev_timestamp DESC',
+                                       'rev_id DESC', // timestamp is not unique per page
+                               )
+                       )
+               );
+
+               # @todo FIXME: Use original rev_id optionally (better for backups)
+               # Insert the row
+               $revision = new Revision( array(
+                       'title' => $this->title,
+                       'page' => $pageId,
+                       'content_model' => $this->getModel(),
+                       'content_format' => $this->getFormat(),
+                       // XXX: just set 'content' => $this->getContent()?
+                       'text' => $this->getContent()->serialize( $this->getFormat() ),
+                       'comment' => $this->getComment(),
+                       'user' => $userId,
+                       'user_text' => $userText,
+                       'timestamp' => $this->timestamp,
+                       'minor_edit' => $this->minor,
+                       'parent_id' => $prevId,
+                       ) );
+               $revision->insertOn( $dbw );
+               $changed = $page->updateIfNewerOn( $dbw, $revision );
+
+               if ( $changed !== false && !$this->mNoUpdates ) {
+                       wfDebug( __METHOD__ . ": running updates\n" );
+                       // countable/oldcountable stuff is handled in WikiImporter::finishImportPage
+                       $page->doEditUpdates(
+                               $revision,
+                               $userObj,
+                               array( 'created' => $created, 'oldcountable' => 'no-change' )
+                       );
+               }
+
+               return true;
+       }
+
+       function importLogItem() {
+               $dbw = wfGetDB( DB_MASTER );
+               # @todo FIXME: This will not record autoblocks
+               if ( !$this->getTitle() ) {
+                       wfDebug( __METHOD__ . ": skipping invalid {$this->type}/{$this->action} log time, timestamp " .
+                               $this->timestamp . "\n" );
+                       return;
+               }
+               # Check if it exists already
+               // @todo FIXME: Use original log ID (better for backups)
+               $prior = $dbw->selectField( 'logging', '1',
+                       array( 'log_type' => $this->getType(),
+                               'log_action' => $this->getAction(),
+                               'log_timestamp' => $dbw->timestamp( $this->timestamp ),
+                               'log_namespace' => $this->getTitle()->getNamespace(),
+                               'log_title' => $this->getTitle()->getDBkey(),
+                               'log_comment' => $this->getComment(),
+                               # 'log_user_text' => $this->user_text,
+                               'log_params' => $this->params ),
+                       __METHOD__
+               );
+               // @todo FIXME: This could fail slightly for multiple matches :P
+               if ( $prior ) {
+                       wfDebug( __METHOD__
+                               . ": skipping existing item for Log:{$this->type}/{$this->action}, timestamp "
+                               . $this->timestamp . "\n" );
+                       return;
+               }
+               $log_id = $dbw->nextSequenceValue( 'logging_log_id_seq' );
+               $data = array(
+                       'log_id' => $log_id,
+                       'log_type' => $this->type,
+                       'log_action' => $this->action,
+                       'log_timestamp' => $dbw->timestamp( $this->timestamp ),
+                       'log_user' => User::idFromName( $this->user_text ),
+                       # 'log_user_text' => $this->user_text,
+                       'log_namespace' => $this->getTitle()->getNamespace(),
+                       'log_title' => $this->getTitle()->getDBkey(),
+                       'log_comment' => $this->getComment(),
+                       'log_params' => $this->params
+               );
+               $dbw->insert( 'logging', $data, __METHOD__ );
+       }
+
+       /**
+        * @return bool
+        */
+       function importUpload() {
+               # Construct a file
+               $archiveName = $this->getArchiveName();
+               if ( $archiveName ) {
+                       wfDebug( __METHOD__ . "Importing archived file as $archiveName\n" );
+                       $file = OldLocalFile::newFromArchiveName( $this->getTitle(),
+                               RepoGroup::singleton()->getLocalRepo(), $archiveName );
+               } else {
+                       $file = wfLocalFile( $this->getTitle() );
+                       $file->load( File::READ_LATEST );
+                       wfDebug( __METHOD__ . 'Importing new file as ' . $file->getName() . "\n" );
+                       if ( $file->exists() && $file->getTimestamp() > $this->getTimestamp() ) {
+                               $archiveName = $file->getTimestamp() . '!' . $file->getName();
+                               $file = OldLocalFile::newFromArchiveName( $this->getTitle(),
+                                       RepoGroup::singleton()->getLocalRepo(), $archiveName );
+                               wfDebug( __METHOD__ . "File already exists; importing as $archiveName\n" );
+                       }
+               }
+               if ( !$file ) {
+                       wfDebug( __METHOD__ . ': Bad file for ' . $this->getTitle() . "\n" );
+                       return false;
+               }
+
+               # Get the file source or download if necessary
+               $source = $this->getFileSrc();
+               $flags = $this->isTempSrc() ? File::DELETE_SOURCE : 0;
+               if ( !$source ) {
+                       $source = $this->downloadSource();
+                       $flags |= File::DELETE_SOURCE;
+               }
+               if ( !$source ) {
+                       wfDebug( __METHOD__ . ": Could not fetch remote file.\n" );
+                       return false;
+               }
+               $sha1 = $this->getSha1();
+               if ( $sha1 && ( $sha1 !== sha1_file( $source ) ) ) {
+                       if ( $flags & File::DELETE_SOURCE ) {
+                               # Broken file; delete it if it is a temporary file
+                               unlink( $source );
+                       }
+                       wfDebug( __METHOD__ . ": Corrupt file $source.\n" );
+                       return false;
+               }
+
+               $user = User::newFromName( $this->user_text );
+
+               # Do the actual upload
+               if ( $archiveName ) {
+                       $status = $file->uploadOld( $source, $archiveName,
+                               $this->getTimestamp(), $this->getComment(), $user, $flags );
+               } else {
+                       $status = $file->upload( $source, $this->getComment(), $this->getComment(),
+                               $flags, false, $this->getTimestamp(), $user );
+               }
+
+               if ( $status->isGood() ) {
+                       wfDebug( __METHOD__ . ": Successful\n" );
+                       return true;
+               } else {
+                       wfDebug( __METHOD__ . ': failed: ' . $status->getHTML() . "\n" );
+                       return false;
+               }
+       }
+
+       /**
+        * @return bool|string
+        */
+       function downloadSource() {
+               if ( !$this->config->get( 'EnableUploads' ) ) {
+                       return false;
+               }
+
+               $tempo = tempnam( wfTempDir(), 'download' );
+               $f = fopen( $tempo, 'wb' );
+               if ( !$f ) {
+                       wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
+                       return false;
+               }
+
+               // @todo FIXME!
+               $src = $this->getSrc();
+               $data = Http::get( $src, array(), __METHOD__ );
+               if ( !$data ) {
+                       wfDebug( "IMPORT: couldn't fetch source $src\n" );
+                       fclose( $f );
+                       unlink( $tempo );
+                       return false;
+               }
+
+               fwrite( $f, $data );
+               fclose( $f );
+
+               return $tempo;
+       }
+
+}
index 0709665..757e2e2 100644 (file)
@@ -48,6 +48,8 @@
        "config-site-name-blank": "Язъе сайтан цӀе.",
        "config-project-namespace": "ЦӀерийн ана проектан:",
        "config-ns-generic": "Проект",
+       "config-ns-other-default": "MyWiki",
+       "config-admin-password": "Пароль:",
        "config-admin-password-confirm": "Кхин цӀа пароль:",
        "config-profile-wiki": "Елин вики",
        "config-profile-no-anon": "ДӀаяздар кхолла деза",
        "config-email-settings": "Электронан пошт нисяр",
        "config-enable-email": "Латае дӀайохьуьйту e-mail",
        "config-upload-deleted": "ДӀаяхна файлийн директори:",
+       "config-logo": "Логотипан URL:",
        "config-cc-again": "Хьаржа кхин цӀа…",
        "config-skins": "Кечяран тема",
        "config-skins-use-as-default": "ХӀара тема Ӏад йитарца лелае",
        "config-skins-must-enable-some": "Ахьа цхьаъ мукъа тема латина йита езаш ю.",
        "config-skins-must-enable-default": "Ӏад йитарца йолу тема латина хила еза.",
+       "config-install-step-done": "кхочушдина",
+       "config-install-step-failed": "тар цаделира",
        "config-install-user": "Декъашхочун хаамийн база кхоллар",
        "config-install-user-alreadyexists": "Декъашхо «$1» хӀинцале волуш ву",
        "config-install-user-create-failed": "Декъашхо «$1» кхолла цаделира: $2",
index 283ad3c..1443452 100644 (file)
@@ -2,10 +2,12 @@
        "@metadata": {
                "authors": [
                        "Wu-chinese.com",
-                       "Poiuyt"
+                       "Poiuyt",
+                       "飞舞回堂前"
                ]
        },
        "config-information": "信息",
+       "config-page-language": "闲话",
        "mainpagetext": "'''MediaWiki安装成功哉!'''",
        "mainpagedocfooter": "请访问[//meta.wikimedia.org/wiki/Help:Contents 用户手册]以获得使用此维基软件个信息!\n\n== 入门 ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings MediaWiki 配置设置列表]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki 常见问题解答]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki 发布邮件列表]"
 }
index 84e873d..cab889f 100644 (file)
@@ -102,6 +102,7 @@ class ExtensionProcessor implements Processor {
                'ParserTestFiles',
                'AutoloadClasses',
                'manifest_version',
+               'load_composer_autoloader',
        );
 
        /**
@@ -353,4 +354,15 @@ class ExtensionProcessor implements Processor {
                        $array[$name] = $value;
                }
        }
+
+       public function getExtraAutoloaderPaths( $dir, array $info ) {
+               $paths = array();
+               if ( isset( $info['load_composer_autoloader'] ) && $info['load_composer_autoloader'] === true ) {
+                       $path = "$dir/vendor/autoload.php";
+                       if ( file_exists( $path ) ) {
+                               $paths[] = $path;
+                       }
+               }
+               return $paths;
+       }
 }
index 732b4a0..e37e7f5 100644 (file)
@@ -29,7 +29,7 @@ class ExtensionRegistry {
        /**
         * Bump whenever the registration cache needs resetting
         */
-       const CACHE_VERSION = 1;
+       const CACHE_VERSION = 2;
 
        /**
         * Special key that defines the merge strategy
@@ -173,6 +173,7 @@ class ExtensionRegistry {
        public function readFromQueue( array $queue ) {
                global $wgVersion;
                $autoloadClasses = array();
+               $autoloaderPaths = array();
                $processor = new ExtensionProcessor();
                $incompatible = array();
                $coreVersionParser = new CoreVersionChecker( $wgVersion );
@@ -208,6 +209,9 @@ class ExtensionRegistry {
                                        . '.';
                                continue;
                        }
+                       // Get extra paths for later inclusion
+                       $autoloaderPaths = array_merge( $autoloaderPaths,
+                               $processor->getExtraAutoloaderPaths( dirname( $path ), $info ) );
                        // Compatible, read and extract info
                        $processor->extractInfo( $path, $info, $version );
                }
@@ -226,6 +230,7 @@ class ExtensionRegistry {
                }
                $data['globals']['wgExtensionCredits'][self::MERGE_STRATEGY] = 'array_merge_recursive';
                $data['autoload'] = $autoloadClasses;
+               $data['autoloaderPaths'] = $autoloaderPaths;
                return $data;
        }
 
@@ -279,8 +284,11 @@ class ExtensionRegistry {
                        call_user_func( $cb );
                }
 
-               $this->loaded += $info['credits'];
+               foreach ( $info['autoloaderPaths'] as $path ) {
+                       require_once $path;
+               }
 
+               $this->loaded += $info['credits'];
                if ( $info['attributes'] ) {
                        if ( !$this->attributes ) {
                                $this->attributes = $info['attributes'];
index e5669d2..a4100bb 100644 (file)
@@ -40,4 +40,14 @@ interface Processor {
         *              like 'MediaWiki'. Values are a constraint string like "1.26.1".
         */
        public function getRequirements( array $info );
+
+       /**
+        * Get the path for additional autoloaders, e.g. the one of Composer.
+        *
+        * @param string $dir
+        * @param array $info
+        * @return array Containing the paths for autoloader file(s).
+        * @since 1.27
+        */
+       public function getExtraAutoloaderPaths( $dir, array $info );
 }
index e125d94..db50bd8 100644 (file)
@@ -971,6 +971,24 @@ class SpecialBlock extends FormSpecialPage {
                $out->addWikiMsg( 'blockipsuccesstext', wfEscapeWikiText( $this->target ) );
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index bf3ab90..ab6614b 100644 (file)
@@ -645,6 +645,24 @@ class SpecialContributions extends IncludableSpecialPage {
                return $form;
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index 6f8e786..f6d560f 100644 (file)
@@ -658,6 +658,24 @@ class DeletedContributionsPage extends SpecialPage {
                return $f;
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index 3b31530..618e700 100644 (file)
@@ -392,6 +392,24 @@ class SpecialEmailUser extends UnlistedSpecialPage {
                }
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index 8de4e2f..9a73a25 100644 (file)
@@ -57,6 +57,24 @@ class SpecialListFiles extends IncludableSpecialPage {
                }
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'media';
        }
index f81f1c3..f776832 100644 (file)
@@ -237,6 +237,24 @@ class SpecialUnblock extends SpecialPage {
                return true;
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index ea22274..cf94e50 100644 (file)
@@ -776,6 +776,24 @@ class UserrightsPage extends SpecialPage {
                LogEventsList::showLogExtract( $output, 'rights', $user->getUserPage() );
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
diff --git a/includes/user/UserNamePrefixSearch.php b/includes/user/UserNamePrefixSearch.php
new file mode 100644 (file)
index 0000000..f565266
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Prefix search of user names.
+ *
+ * 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
+ */
+
+/**
+ * Handles searching prefixes of user names
+ *
+ * @since 1.27
+ */
+class UserNamePrefixSearch {
+
+       /**
+        * Do a prefix search of user names and return a list of matching user names.
+        *
+        * @param string|User $audience The string 'public' or a user object to show the search for
+        * @param string $search
+        * @param int $limit
+        * @param int $offset How many results to offset from the beginning
+        * @return array Array of strings
+        */
+       public static function search( $audience, $search, $limit, $offset = 0 ) {
+               $user = User::newFromName( $search );
+
+               $dbr = wfGetDB( DB_SLAVE );
+               $prefix = $user ? $user->getName() : '';
+               $tables = array( 'user' );
+               $cond = array( 'user_name ' . $dbr->buildLike( $prefix, $dbr->anyString() ) );
+               $joinConds = array();
+
+               // Filter out hidden user names
+               if ( $audience === 'public' || !$audience->isAllowed( 'hideuser' ) ) {
+                       $tables[] = 'ipblocks';
+                       $cond['ipb_deleted'] = array( 0, null );
+                       $joinConds['ipblocks'] = array( 'LEFT JOIN', 'user_id=ipb_user' );
+               }
+
+               $res = $dbr->selectFieldValues(
+                       $tables,
+                       'user_name',
+                       $cond,
+                       __METHOD__,
+                       array(
+                               'LIMIT' => $limit,
+                               'ORDER BY' => 'user_name',
+                               'OFFSET' => $offset
+                       ),
+                       $joinConds
+               );
+
+               return $res === false ? array() : $res;
+       }
+}
index 0b8ce7d..36c3957 100644 (file)
        "protectthispage": "بۇ صحیفه‌‌نی قوْرو",
        "unprotect": "قوْروماغی دَییشدیر",
        "unprotectthispage": "بۇ صحیفه‌نین قوْروماسینی دَییشدیر",
-       "newpage": "يئنی صحیفه‌‌",
+       "newpage": "يئنی صفحه‌‌",
        "talkpage": "بۇ صحیفه‌نی دانیش",
        "talkpagelinktext": "دانیشیق",
        "specialpage": "اؤزل صفحه",
        "templatesusedsection": "{{PLURAL:$1|شابلون}} بو بؤلمه‌ده ایشلنیب‌دیر:",
        "template-protected": "(قورونوب)",
        "template-semiprotected": "(یاریم‌قورونموش)",
-       "hiddencategories": "بو صحیفه {{PLURAL:$1|بیر گیزلی دسته‌یه|$1 گیزلی دسته‌لره}} عایددیر:",
+       "hiddencategories": "بۇ صفحه {{PLURAL:$1|بیر گیزلی بؤلمه‌یه|$1 گیزلی بؤلمه‌لره}} مربوط‌دور:",
        "nocreatetext": "{{SITENAME}} یئنی صحیفه یارادماق ایمکانی‌نی محدودلاشدیریب‌دیر.\nسیز دالی دؤنوب و اؤنجه‌دن اولان بیر صحیفه‌نی دَییشدیره بیلرسینیز، یا دا [[Special:UserLogin|گیریب یوخسا یئنی حساب آچین]].",
        "nocreate-loggedin": "سیزین یئنی صحیفه‌لر یاراتماغا ایجازه‌نیز یوخدور.",
        "sectioneditnotsupported-title": "بؤلوم دییشدیرمه‌سی دستک‌لنمیر",
        "randomredirect": "راست‌گله یول‌لاندیرما",
        "randomredirect-nopages": "«$1» آدفضاسیندا هئچ بیر یول‌لاندیرما یوخدور.",
        "statistics": "آمارلار",
-       "statistics-header-pages": "صحیفه آمارلاری",
+       "statistics-header-pages": "صفحه آمارلاری",
        "statistics-header-edits": "دَییشمه آمارلاری",
        "statistics-header-users": "ایشلدن‌لر آمارلاری",
        "statistics-header-hooks": "باشقا آمارلار",
        "statistics-articles": "مقاله‌لر",
-       "statistics-pages": "صحیفه‌لر:",
+       "statistics-pages": "صفحه‌لر:",
        "statistics-pages-desc": "بو ویکی‌ده بوتون صحیفه‌لر، او جومله‌دن دانیشیق صحیفه‌لری، یول‌لاندیرمالار و سونرا.",
        "statistics-files": "یوکلنمیش فایل‌لار",
        "statistics-edits": "{{SITENAME}} یولا دوشندن بَری صحیفه دَییشیکلیکلری",
index 664b941..3e33f8c 100644 (file)
        "protectedtitles": "Забароненыя старонкі",
        "protectedtitles-summary": "На гэтай старонцы знаходзіцца сьпіс назваў, якія абароненыя ад стварэньня. Дзеля сьпісу старонак, якія ў цяперашні час абароненыя, глядзіце [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Цяпер няма абароненых назваў з пазначанымі парамэтрамі.",
+       "protectedtitles-submit": "Паказаць загалоўкі",
        "listusers": "Сьпіс удзельнікаў і ўдзельніц",
        "listusers-editsonly": "Паказаць толькі ўдзельнікаў, якія маюць рэдагаваньні",
        "listusers-creationsort": "Адсартаваць па даце стварэньня",
        "usereditcount": "$1 {{PLURAL:$1|рэдагаваньне|рэдагаваньні|рэдагаваньняў}}",
        "usercreated": "{{GENDER:$3|Створаны|Створаная}} $1 у $2",
        "newpages": "Новыя старонкі",
+       "newpages-submit": "Паказаць",
        "newpages-username": "Імя ўдзельніка:",
        "ancientpages": "Найстарэйшыя старонкі",
        "move": "Перанесьці",
        "specialloguserlabel": "Выканаўца:",
        "speciallogtitlelabel": "Мэта (назва ці {{ns:user}}:імя_ўдзельніка для ўдзельніка):",
        "log": "Журналы падзеяў",
+       "logeventslist-submit": "Паказаць",
        "all-logs-page": "Усе публічныя журналы падзеяў",
        "alllogstext": "Сумесны паказ усіх журналаў падзеяў {{GRAMMAR:родны|{{SITENAME}}}}.\nВы можаце адфільтраваць вынікі па тыпе журналу, удзельніку ці старонцы.",
        "logempty": "Падобных запісаў у журнале няма.",
        "cachedspecial-viewing-cached-ts": "Вы праглядаеце закэшаваную вэрсію старонкі, якая можа быць неактуальнай.",
        "cachedspecial-refresh-now": "Пабачыць апошнюю вэрсію.",
        "categories": "Катэгорыі",
+       "categories-submit": "Паказаць",
        "categoriespagetext": "{{PLURAL:$1|1=Наступная катэгорыя зьмяшчае|Наступныя катэгорыі зьмяшчаюць}} старонкі альбо мэдыяфайлы.\nТут не паказаныя [[Special:UnusedCategories|катэгорыі, якія не выкарыстоўваюцца]].\nГлядзіце таксама [[Special:WantedCategories|сьпіс запатрабаваных катэгорыяў]].",
        "categoriesfrom": "Паказаць катэгорыі, пачынаючы з:",
        "special-categories-sort-count": "сартаваць паводле колькасьці",
index c4f7245..acafcdd 100644 (file)
        "createacct-error": "Error de creació de compte",
        "createaccounterror": "No s'ha pogut crear el compte: $1",
        "nocookiesnew": "S'ha creat el compte d'usuari, però no s'ha iniciat la sessió.\nEl projecte {{SITENAME}} usa galetes per a iniciar la sessió d'usuari. \nTeniu les galetes desactivades. \nActiveu-les per a poder iniciar la sessió amb el nou nom d'usuari i la nova clau.",
-       "nocookieslogin": "El programari {{SITENAME}} utilitza galetes per enregistrar usuaris. Teniu les galetes desactivades. Activeu-les i torneu a provar.",
+       "nocookieslogin": "{{SITENAME}} utilitza galetes per a enregistrar usuaris. Teniu les galetes desactivades. Activeu-les i torneu a provar.",
        "nocookiesfornew": "No s'ha creat el compte d'usuari, ja que no es podia confirmar el seu origen.\nVerifiqueu que teniu habilitades les galetes al vostre navegador, torneu a carregar aquesta pàgina i intenteu-lo de nou.",
        "nocookiesforlogin": "{{int:nocookieslogin}}",
        "noname": "No heu especificat un nom vàlid d'usuari.",
index 2b08bee..6a35831 100644 (file)
@@ -16,6 +16,7 @@
        "tog-hideminor": "Къайладаха кигийра нисдарш оц могӀама керла хийцамехь",
        "tog-hidepatrolled": "Къайладаха гӀаролладина нисдарш оц могӀама керла нисдаршкахь",
        "tog-newpageshidepatrolled": "Къайлаяха гӀароллайина агӀонаш оьцу могӀама керла агӀонашкахь",
+       "tog-hidecategorization": "Къайлаяха агӀонийн категореш",
        "tog-extendwatchlist": "Шорбина тӀехьажарна могӀам, ша беригге а, хийцамаш чубогӀуш, тӀехьаббина боцурш а",
        "tog-usenewrc": "Лелабе дика могӀам керла чу хийцамашна (оьшу JavaScript)",
        "tog-numberheadings": "Ша шех хlитто терахь корташна",
@@ -37,7 +38,7 @@
        "tog-shownumberswatching": "Гайта декъашхойн терахь, агӀо латийна болу шай тергаме могӀанан юкъа",
        "tog-oldsig": "Карара куьгтаӀорна:",
        "tog-fancysig": "Шен вики-къастаман куьгтаӀдар (ша шех хьажорг йоцуш)",
-       "tog-uselivepreview": "Ð\9bелайа Ñ\87еÑ\85ка Ñ\85Ñ\8cалÑ\85а Ñ\85Ñ\8cажа (JavaScript, Ð¼Ñ\83Ñ\85а Ñ\8e Ñ\85Ñ\8cажаÑ\80на)",
+       "tog-uselivepreview": "Ð\9bелае Ñ\87еÑ\85ка Ñ\85Ñ\8cалÑ\85а Ñ\85Ñ\8cажаÑ\80",
        "tog-forceeditsummary": "Дага даийта, нагахь нисйарх лаьцна чохь язйина яцахь",
        "tog-watchlisthideown": "Къайлаяха ас нисйинарш тергаме могӀам чура",
        "tog-watchlisthidebots": "Къайладаха тергаме могӀам чура ботан нисдинарш",
        "recentchanges-label-plusminus": "байташкахь барам хийцар",
        "recentchanges-legend-heading": "'''Легенда:&nbsp;'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (хьажа кхин [[Special:NewPages|керла агӀонийн могӀа]])",
+       "recentchanges-submit": "Гайта",
        "rcnotefrom": "Лахахь гайтина тӀера <strong>$2</strong> (хийцамаш <strong>$1</strong> кӀезиг).",
        "rclistfrom": "Гайта хийцам {{CURRENTYEAR}} шаран {{CURRENTDAY}} {{CURRENTMONTHNAMEGEN}} {{CURRENTTIME}} бина болу",
        "rcshowhideminor": "$1 кегийра нисдарш",
        "rcshowhidemine": "$1 айхьа нисдинарш",
        "rcshowhidemine-show": "Гайта",
        "rcshowhidemine-hide": "Къайладаха",
+       "rcshowhidecategorization": "$1 агӀонийн категореш",
+       "rcshowhidecategorization-show": "Гайта",
+       "rcshowhidecategorization-hide": "Къайлаяккха",
        "rclinks": "Гайта тӀаьххьарлерачу $2 дийнахь бина болу $1 хийцамаш\n<br />$3",
        "diff": "башхалла",
        "hist": "истори",
        "boteditletter": "б",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|тӀехьожу декъашхо|тӀехьожу декъашхой}}]",
        "rc_categories": "Категори чура бен (къасторг «|»)",
-       "rc_categories_any": "Муьлхаа",
+       "rc_categories_any": "Муьлха а хаьржиначух",
        "rc-change-size-new": "Хийцам бин чул тӀехьа болу барам: $1 {{PLURAL:$1|байт}}",
        "newsectionsummary": "/* $1 */ Керла хьедар",
        "rc-enhanced-expand": "Гайта мадарра",
        "recentchangeslinked-summary": "ХӀара хийцам биначу агӀонийн могӀам бу, тӀетовжар долуш хьагучу агӀон (я хьагойтуш йолучу категорена).\nАгӀонаш юькъа йогӀуш йолу хьан [[Special:Watchlist|тергаме могӀам чохь]] '''къастийна ю'''.",
        "recentchangeslinked-page": "АгӀон цӀе:",
        "recentchangeslinked-to": "Кхечу агӀор, гайта хийцамаш агӀонашца, хӀоттийначу агӀонтӀе хьажорг йолуш",
+       "recentchanges-page-added-to-category": "[[:$1]] категори чу тоьхна",
        "upload": "Файл чуяккхар",
        "uploadbtn": "Файл чуяккхар",
        "reuploaddesc": "Юху гӀо файл чуйоккху агӀоне",
        "upload-file-error": "Чоьхьара гӀалат",
        "upload-misc-error": "Чуяккхаран цадевза гӀалат",
        "upload-http-error": "Даьлла гӀалат HTTP: $1",
+       "upload-dialog-button-cancel": "Цаоьшу",
+       "upload-dialog-button-done": "Кийчча ю",
+       "upload-dialog-button-save": "Ӏалашъян",
+       "upload-dialog-button-upload": "Чуяккха",
+       "upload-form-label-select-file": "Харжа файл",
+       "upload-form-label-infoform-title": "Мадарра",
+       "upload-form-label-infoform-name": "ЦӀе",
+       "upload-form-label-infoform-description": "Цуьнах лаьцна",
+       "upload-form-label-usage-title": "Лелор",
+       "upload-form-label-usage-filename": "файлан цӀе",
+       "foreign-structured-upload-form-label-own-work": "ХӀара сан долара болх бу",
        "foreign-structured-upload-form-label-infoform-categories": "Категореш",
        "foreign-structured-upload-form-label-infoform-date": "Терахь",
+       "foreign-structured-upload-form-3-label-yes": "ХӀаъ",
+       "foreign-structured-upload-form-3-label-no": "ХӀахӀа",
        "backend-fail-stream": "ДӀаяккха цатарло файл «$1».",
        "backend-fail-backup": "Таро яц файлан $1 тӀаьхьалонан копиян.",
        "backend-fail-notexists": "Файл $1 яц.",
        "mostrevisions": "Сих сиха нисйина йолу агӀонаш",
        "prefixindex": "Хьалха агӀонийн цӀерш хӀотто еза",
        "prefixindex-namespace": "Хьалха агӀонийн цӀерш хӀотто еза («{{ns:$1}}»)",
+       "prefixindex-submit": "Гайта",
        "prefixindex-strip": "Хиламийн могӀам чура префикс къайлаяккха",
        "shortpages": "Боца яззамаш",
        "longpages": "Беха яззамаш",
        "usereditcount": "$1 {{PLURAL:$1|нисдар|нисдарш}}",
        "usercreated": "{{GENDER:$3|дӀавазвелла|дӀаязелла}} $1 $2",
        "newpages": "Керла агӀонаш",
+       "newpages-submit": "Гайта",
        "newpages-username": "Декъашхо:",
        "ancientpages": "Шира агӀонаш",
        "move": "ЦӀе хийца",
        "specialloguserlabel": "Декъашхо:",
        "speciallogtitlelabel": "Ӏалашо (цӀе я декъашхо):",
        "log": "Тéптарш",
+       "logeventslist-submit": "Гайта",
        "all-logs-page": "Дерриге тӀекхочучехь долу тептарш",
        "alllogstext": "Массо юкъара журлийн могӀам. {{SITENAME}}.\nШуьга харжалур бу хилам оцу тептаре хьаьжжина, декъашхочун цӀе (дӀаяздар диц а цадеш) я цо хьейина агӀонаш (ишта дӀаяздар а диц цадеш).",
        "logempty": "Тептарш чохь хӀокху агӀона дӀаяздарш дац.",
        "cachedspecial-viewing-cached-ttl": "Хьо хьоьжу агӀона верси кэш чура ю, иза карлаяьккхина хила мега $1 хьалха.",
        "cachedspecial-refresh-now": "Хьажа тӀехьарчу версега.",
        "categories": "Категореш",
+       "categories-submit": "Гайта",
        "categoriespagetext": "{{PLURAL:$1|1=Лахара категореш чохь ю|Лахара категореш чохь ю}} агӀонаш я медиа-файлаш.\nКхузахь гойтуш яц [[Special:UnusedCategories|лелош йоцу категореш]].\nКхин дӀа [[Special:WantedCategories| хийла еза категореш]].",
        "categoriesfrom": "Гучé яха категореш, тӀера:",
        "special-categories-sort-count": "нисъе дукхаллица",
        "wlnote": "Гойту <strong>$2</strong> {{plural:$2|сахьтчохь}} бина {{PLURAL:$1|тӀеххьара '''$1''' хийцам}}, хан $3 $4",
        "wlshowlast": "Гайта тӀаьххьара $1 сахьт $2 де",
        "watchlistall2": "массо",
+       "watchlist-hide": "Къайлаяккха",
+       "watchlist-submit": "Гайта",
+       "wlshowhideminor": "жима нисдарш",
+       "wlshowhidebots": "Боташ",
+       "wlshowhideliu": "ДӀабазбелла декъашхой",
+       "wlshowhideanons": "ЦӀе хьулйина декъашхой",
+       "wlshowhidepatr": "хьажжина нисдарш",
+       "wlshowhidemine": "Сан нисдарш",
        "watchlist-options": "Тергаме могlаман гlирс нисбар",
        "watching": "Тергаме мlогаман юкъаяккха…",
        "unwatching": "Тергаме могӀанан чура дӀаяккхар…",
        "delete-confirm": "$1 — дӀаяккхар",
        "delete-legend": "ДӀаяккхар",
        "historywarning": "<strong>Тергам бе:</strong> Хьо дӀаяккха гӀертачу агӀона, нисдарийн истори ю, $1 {{PLURAL:$1|верси}} йолуш:",
+       "historyaction-submit": "Гайта",
        "confirmdeletetext": "Хьо гӀерта агӀо я файл дӀаяккха '''дехар до''', дӀаяккхале хьалха хьажа [[{{MediaWiki:Policy-url}}|кхуза]].",
        "actioncomplete": "Дешдерг кхочушдина",
        "actionfailed": "Кхочушъ дина дац",
        "whatlinkshere-hidelinks": "$1 хьажорг",
        "whatlinkshere-hideimages": "$1 файлийн хьажоргаш",
        "whatlinkshere-filters": "Литтарш",
+       "whatlinkshere-submit": "Кхочушдé",
        "autoblockid": "Ша блоккхетар #$1",
        "block": "Декъашхочун блоктохар",
        "unblock": "ДекъашхонтӀера блокдӀаякхар",
        "patrol-log-page": "ТӀехьажаран тептар",
        "patrol-log-header": "Хьажжина версеш йолу тептар.",
        "log-show-hide-patrol": "$1 тӀехьажаран тептар",
+       "log-show-hide-tag": "$1 билгалонийн тептар",
        "deletedrevision": "ДӀаяьккхина шира верси $1",
        "filedeleteerror-short": "Файл дӀаяккхаран гӀалат: $1",
        "filedeleteerror-long": "Файл дӀайоккхуш гӀалат даьлла:\n\n$1",
        "file-nohires": "Кхи йоккха гlоле башхо яц.",
        "svg-long-desc": "SVG-файл, лартӀахь ю $1 × $2 пиксель, файлан барам: $3",
        "svg-long-desc-animated": "Анимироват йина SVG-файл, номиналан $1 × $2 пиксель, файлан барам: $3",
+       "svg-long-error": "нийса йоцу SVG-файл: $1",
        "show-big-image": "Оригиналан файл",
        "show-big-image-preview": "Барам хьажале: $1.",
        "show-big-image-other": "{{PLURAL:$2|1=Кхин шоралла|Кхин шоралла}}: $1.",
        "version-entrypoints-header-url": "URL",
        "version-entrypoints-articlepath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgArticlePath АгӀона тӀе некъ]",
        "version-entrypoints-scriptpath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgScriptPath Скриптан тӀе некъ]",
+       "version-libraries": "ДӀахӀоттийна библиотекаш",
+       "version-libraries-library": "Библиотека",
+       "version-libraries-version": "Верси",
+       "version-libraries-license": "Лицензи",
        "version-libraries-description": "Цуьнах лаьцна",
        "version-libraries-authors": "Автораш",
        "redirect": "Декъашхочун файлан тӀера дӀасхьажор",
        "mediastatistics-header-text": "Йозанан",
        "mediastatistics-header-executable": "Кхочушдийриш",
        "mediastatistics-header-archive": "Архиваш",
+       "mediastatistics-header-total": "Массо файлаш",
        "json-error-unknown": "JSON бала бу. ГӀалат: $1",
        "json-error-syntax": "Синтаксин гӀалат",
        "headline-anchor-title": "ХӀокху дакъан тӀе хьажорг",
index aaf599d..a4d7e26 100644 (file)
        "autoredircomment": "Přesměrování na [[$1]]",
        "autosumm-new": "Založena nová stránka s textem „$1“",
        "autosumm-newblank": "Založena prázdná stránka",
+       "size-bytes": "$1 {{PLURAL:$1|bajt|bajty|bajtů}}",
        "size-kilobytes": "$1 KB",
        "lag-warn-normal": "Změny za {{PLURAL:$1|poslední sekundu|poslední $1 sekundy|posledních $1 sekund}} nemusí být v tomto seznamu zobrazeny.",
        "lag-warn-high": "Protože je databázový server právě mimořádně vytížen, nemusí být změny za {{PLURAL:$1|poslední sekundu|poslední $1 sekundy|posledních $1 sekund}} v tomto seznamu zobrazeny.",
index ae89893..6eb859b 100644 (file)
        "mediastatistics-summary": "Statistiken über hochgeladene Dateitypen. Dies beinhaltet nur die aktuellste Version einer Datei. Alte oder gelöschte Dateiversionen sind ausgeschlossen.",
        "mediastatistics-nfiles": "$1 ($2 %)",
        "mediastatistics-nbytes": "{{PLURAL:$1|Ein Byte|$1 Bytes}} ($2; $3 %)",
-       "mediastatistics-bytespertype": "Gesamte Dateigröße für diesen Abschnitt: $1 Bytes.",
-       "mediastatistics-allbytes": "Gesamte Dateigröße für alle Dateien: $1 Bytes.",
+       "mediastatistics-bytespertype": "Gesamte Dateigröße für diesen Abschnitt: {{PLURAL:$1|Ein Byte|$1 Bytes}} ($2; $3%).",
+       "mediastatistics-allbytes": "Gesamte Dateigröße für alle Dateien: {{PLURAL:$1|Ein Byte|$1 Bytes}} ($2).",
        "mediastatistics-table-mimetype": "MIME-Typ",
        "mediastatistics-table-extensions": "Mögliche Erweiterungen",
        "mediastatistics-table-count": "Anzahl der Dateien",
index da82abe..cbb8b76 100644 (file)
                        "Nelson6e65",
                        "Matiia",
                        "SinNovedades",
-                       "Rodm23"
+                       "Rodm23",
+                       "Yllelder"
                ]
        },
        "tog-underline": "Subrayar los enlaces:",
        "passwordreset-emailtext-user": "El usuario $1 de {{SITENAME}} solicitó el restablecimiento de tu contraseña en {{SITENAME}}\n($4). {{PLURAL:$3|La siguiente cuenta está asociada|Las siguientes cuentas están asociadas}} a esta dirección de correo electrónico:\n\n$2\n\n{{PLURAL:$3|Esta contraseña temporal|Estas contraseñas temporales}} caducarán en {{PLURAL:$5|un día|$5 días}}.\nAhora puedes iniciar sesión y establecer una nueva contraseña. Si fue otra persona la que realizó esta solicitud, o si ya recuerdas tu contraseña original y, por tanto, no deseas cambiarla, puedes ignorar este mensaje y continuar usando tu contraseña anterior.",
        "passwordreset-emailelement": "Nombre de {{GENDER:$1|usuario|usuaria}}: \n$1\n\nContraseña temporal: \n$2",
        "passwordreset-emailsentemail": "Si esta es una dirección de correo electrónico registrada para tu cuenta, entonces se enviará un correo electrónico para el restablecimiento de tu contraseña.",
-       "passwordreset-emailsentusername": "Si hay una dirección de correo electrónico conectada a esta cuenta, entonces se enviará un correo electrónico para el restablecimiento de tu contraseña.",
+       "passwordreset-emailsentusername": "Si existe una dirección de correo electrónico asociada a este nombre de usuario, entonces el correo para restablecer la contraseña será enviado.",
        "passwordreset-emailsent-capture": "Se ha enviado un correo para el restablecimiento de la contraseña, el cual se muestra a continuación.",
        "passwordreset-emailerror-capture": "Se ha generado un correo electrónico de restablecimiento de contraseña, que se muestra a continuación, pero ha fallado el envío {{GENDER:$2|al usuario|a la usuaria}}: $1",
        "changeemail": "Cambiar o eliminar la dirección de correo electrónico",
        "pagelang-language": "Idioma",
        "pagelang-use-default": "Utilizar el idioma predeterminado",
        "pagelang-select-lang": "Seleccionar idioma",
+       "pagelang-submit": "Enviar",
        "right-pagelang": "Cambiar el idioma de la página",
        "action-pagelang": "cambiar el idioma de la página",
        "log-name-pagelang": "Registro de cambios en idiomas",
        "mediastatistics-header-text": "Textual",
        "mediastatistics-header-executable": "Ejecutables",
        "mediastatistics-header-archive": "Formatos comprimidos",
+       "mediastatistics-header-total": "Todos los archivos",
        "json-warn-trailing-comma": "Se {{PLURAL:$1|eliminó una coma|eliminaron $1 comas}} al final en el archivo JSON",
        "json-error-unknown": "Ocurrió un problema con el código JSON. Error: $1",
        "json-error-depth": "Se ha superado la profundidad máxima de la pila",
index b014450..3a5c6d8 100644 (file)
        "nstab-template": "الگو",
        "nstab-help": "صفحهٔ راهنما",
        "nstab-category": "رده",
-       "mainpage-nstab": "صفحه اصلی",
+       "mainpage-nstab": "صفحهٔ اصلی",
        "nosuchaction": "چنین عملی وجود ندارد",
        "nosuchactiontext": "عمل مشخص‌شده در نشانی اینترنتی نامجاز است.\nممکن است نشانی اینترنتی را اشتباه وارد کرده باشید یا پیوند مشکل‌داری را دنبال کرده باشید.\nهمچنین ممکن است ایرادی در نرم‌افزار استفاده‌شده در {{SITENAME}} وجود داشته باشد.",
        "nosuchspecialpage": "چنین صفحهٔ ویژه‌ای وجود ندارد",
index 83dfa37..e57a866 100644 (file)
        "mediastatistics": "סטטיסטיקות קבצים",
        "mediastatistics-summary": "סטטיסטיקה על סוגי קבצים שהועלו. הסטטיסטיקה כוללת רק את הגרסה החדשה ביותר של הקובץ: גרסאות ישנות או מחוקות של קבצים אינן כלולות.",
        "mediastatistics-nbytes": "{{PLURAL:$1|בית אחד|$1 בתים}} ($2; $3%)",
-       "mediastatistics-bytespertype": "ס×\9a ×\92×\93×\9c×\99 ×\94ק×\91צ×\99×\9d ×\91פרק ×\96×\94: $1 ×\91ת×\99×\9d.",
-       "mediastatistics-allbytes": "ס×\9a ×\92×\93×\9c×\99 ×\9b×\9c ×\94ק×\91צ×\99×\9d: $1 ×\91ת×\99×\9d.",
+       "mediastatistics-bytespertype": "×\94×\92×\95×\93×\9c ×\94×\9b×\95×\9c×\9c ×©×\9c ×\94ק×\91צ×\99×\9d ×\91פרק ×\96×\94: {{PLURAL:$1|×\91×\99ת ×\90×\97×\93|$1 ×\91ת×\99×\9d}} ($2; $3%).",
+       "mediastatistics-allbytes": "×\94×\92×\95×\93×\9c ×\94×\9b×\95×\9c×\9c ×©×\9c ×\9b×\9c ×\94ק×\91צ×\99×\9d: {{PLURAL:$1|×\91×\99ת ×\90×\97×\93|$1 ×\91ת×\99×\9d}} ($2).",
        "mediastatistics-table-mimetype": "סוג MIME",
        "mediastatistics-table-extensions": "סיומות אפשריות",
        "mediastatistics-table-count": "מספר הקבצים",
index d1e45f7..422c028 100644 (file)
        "sig_tip": "Aláírás időponttal",
        "hr_tip": "Vízszintes vonal (ritkán használd)",
        "summary": "Összefoglaló:",
-       "subject": "Téma/fÅ\91cím:",
+       "subject": "Tárgy:",
        "minoredit": "Apró változtatás",
        "watchthis": "A lap figyelése",
        "savearticle": "Lap mentése",
index e26678c..9b3e238 100644 (file)
        "morenotlisted": "Þessi listi er ekki tæmandi.",
        "mypage": "Síða",
        "mytalk": "Spjall",
-       "anontalk": "Spjallsíða þessa vistfangs.",
+       "anontalk": "Spjall",
        "navigation": "Flakk",
        "and": "&#32;og",
        "qbfind": "Finna",
        "view-foreign": "Skoða á $1",
        "edit": "Breyta",
        "create": "Skapa",
+       "create-local": "Bæta við staðbundinni lýsingu",
        "editthispage": "Breyta þessari síðu",
        "create-this-page": "Skapa þessari síðu",
        "delete": "Eyða",
        "viewsource": "Skoða efni",
        "viewsource-title": "Skoða efni $1",
        "actionthrottled": "Aðgerðin kafnaði",
-       "actionthrottledtext": "Til þess að verjast ruslpósti, er ekki hægt að framkvæma þessa aðgerð of oft, og þú hefur farið fram yfir þau takmörk. Gjörðu svo vel og reyndu aftur eftir nokkrar mínútur.",
+       "actionthrottledtext": "Til þess að verjast misnotkun, er ekki hægt að framkvæma þessa aðgerð of oft, og þú hefur farið fram yfir þau takmörk. Vinsamlegast reyndu aftur eftir nokkrar mínútur.",
        "protectedpagetext": "Þessari síðu hefur verið læst til að koma í veg fyrir breytingar eða aðrar aðgerðir.",
-       "viewsourcetext": "Þú getur skoðað og afritað kóða þessarar síðu:",
+       "viewsourcetext": "Þú getur skoðað og afritað kóða þessarar síðu.",
        "viewyourtext": "Þú getur skoðað og afritað kóða <strong>breytinganna þinna</strong> yfir á þessa síðu.",
        "protectedinterface": "Þessi síða útvegar textann sem birtist í viðmóti hugbúnaðarins sem keyrir þessa síðu, og er læst til að koma í veg fyrir misnotkun.\nTil þess að bæta við eða breyta þýðingum fyrir öll wiki verkefni, vinsamlegast notaðu [//translatewiki.net/ translatewiki.net], staðfæringaverkefni MediaWiki",
        "editinginterface": "<strong>Aðvörun:</strong> Þú ert að breyta síðu sem hefur að geyma texta fyrir notendaumhverfi hugbúnaðarins.\nBreytingar á þessari síðu munu hafa áhrif á notendaumhverfi annarra notenda á þessu vefsvæði.",
        "mycustomjsprotected": "Þú hefur ekki leyfi til þess að breyta þessari JavaScript-síðu.",
        "ns-specialprotected": "Kerfissíðum er ekki hægt að breyta.",
        "titleprotected": "Þessi titill hefur verið verndaður fyrir sköpun af [[User:$1|$1]].\nÁstæðan sem gefin var ''$2''.",
-       "filereadonlyerror": "Ekki var hægt að breyta skránni \"$1\" því skráin í skráarsafninu \"$2\" er engöngu hægt að lesa.\n\nMöppudýrið sem læsti skránni gaf þessa ástæðu: \"''$3''\".",
+       "filereadonlyerror": "Ekki var hægt að breyta skránni \"$1\" því skráin í skráarsafninu \"$2\" er engöngu hægt að lesa.\n\nKerfisstjórinn sem læsti skránni gaf þessa ástæðu: \"$3\".",
        "invalidtitle-knownnamespace": "Ógildur titill í nafnrými \"$2\" og með textann \"$3\"",
        "invalidtitle-unknownnamespace": "Ógildur titill með óþekkt nafnrými númer $1 og texta \"$2\"",
        "exception-nologin": "Óinnskráð(ur)",
        "createacct-reason": "Ástæða",
        "createacct-reason-ph": "Afhverju ertu að búa til annan aðgang",
        "createacct-submit": "Búa til aðganginn",
-       "createacct-another-submit": "Stofna annan aðgang",
+       "createacct-another-submit": "Stofna aðgang",
        "createacct-benefit-heading": "{{SITENAME}} er skrifuð af fólki eins og þér.",
        "createacct-benefit-body1": "{{PLURAL:$1|breyting|breytingar}}",
        "createacct-benefit-body2": "{{PLURAL:$1|síða|síður}}",
        "passwordreset-emailtext-ip": "Einhver (líklegast þú, á vistfanginu $1) hefur beðið um \nendursetningu lykilorðsins þíns fyrir {{SITENAME}} ($4). Aðgangur eftirfarandi {{PLURAL:$3|notanda er|notendum eru}} tengd þessu netfangi:\n\n$2\n\nEf þetta er það sem þú vildir, þarftu að skrá þig inn og velja nýtt lykilorð. {{PLURAL:$3|Tímabundna lykilorðið rennur|Tímabundnu lykilorðin renna}} út eftir $5 {{PLURAL:$5|dag|daga}}.\n\nEf það varst ekki þú sem fórst fram á þetta, eða ef þú manst lykilorðið þitt, og villt ekki lengur breyta því, skaltu hunsa þessi skilaboð og halda áfram að nota gamla lykilorðið.",
        "passwordreset-emailtext-user": "Notandinn $1 á {{SITENAME}} hefur beðið um endursetningu lykilorðsins þíns fyrir {{SITENAME}} ($4). Aðgangur eftirfarandi {{PLURAL:$3|notanda er|notendum eru}} tengd þessu netfangi:\n\n$2\n\nEf þetta er það sem þú vildir, þarftu að skrá þig inn og velja nýtt lykilorð. {{PLURAL:$3|Tímabundna lykilorðið rennur|Tímabundnu lykilorðin renna}} út eftir $5 {{PLURAL:$5|dag|daga}}.\n\nEf það varst ekki þú sem fórst fram á þetta, eða ef þú manst aftur lykilorðið þitt, og vilt ekki lengur breyta því, skaltu hunsa þessi skilaboð og halda áfram að nota gamla lykilorðið.",
        "passwordreset-emailelement": "Notendanafn: \n$1\n\nTímabundið lykilorð: \n$2",
-       "passwordreset-emailsentemail": "Töluvpóstur til að endursetja lykilorðið hefur verið sendur.",
+       "passwordreset-emailsentemail": "Ef þetta netfang er skráð fyrir aðganginum þínum þá hefur töluvpóstur verið sendur til að endursetja lykilorðið.",
        "passwordreset-emailsent-capture": "Tölvupóstur til að endursetja lykilorðið hefur verið sendur í tölvupósti, sem er sýndur hér fyrir neðan.",
        "passwordreset-emailerror-capture": "Tölvupóstur til að endursetja lykilorðið var búinn til, sem er sýndur hér fyrir neðan, en ekki tókst að senda hana til {{GENDER:$2|notandans}}: $1",
-       "changeemail": "Breyting netfangs",
-       "changeemail-header": "Breyta skráðu netfangi",
+       "changeemail": "Breyta eða fjarlægja netfang",
+       "changeemail-header": "Fylltu út þetta eyðublað til að breyta netfanginu þínu. Ef þú vilt fjarlægja tengingu allra netfanga frá aðganginum þínum skildu þá netfangs reitinn eftir tóman.",
        "changeemail-no-info": "Þú verður að vera skráð(ur) inn til að hafa aðgang að þessari síðu.",
        "changeemail-oldemail": "Núverandi netfang:",
        "changeemail-newemail": "Nýtt netfang:",
        "sig_tip": "Undirskrift þín auk tímasetningar",
        "hr_tip": "Lárétt lína (notist sparlega)",
        "summary": "Breytingarágrip:",
-       "subject": "Fyrirsögn:",
+       "subject": "Umræðuefni:",
        "minoredit": "Þetta er minniháttar breyting",
        "watchthis": "Vakta þessa síðu",
        "savearticle": "Vista síðu",
        "anonpreviewwarning": "Þú ert ekki innskráð(ur). Vistfang þitt skráist í breytingaskrá síðunnar.",
        "missingsummary": "'''Áminning:''' Þú hefur ekki skrifað breytingarágrip.\nEf þú smellir á Vista aftur, verður breyting þín vistuð án þess.",
        "missingcommenttext": "Gerðu svo vel og skrifaðu athugasemd fyrir neðan.",
-       "missingcommentheader": "'''Áminning:''' Þú hefur ekki gefið upp umræðuefni/fyrirsögn.\nEf þú smellir á \"{{int:savearticle}}\" aftur, verður breyting þín vistuð án þess.",
+       "missingcommentheader": "<strong>Áminning:</strong> Þú hefur ekki gefið upp umræðuefni.\nEf þú smellir á \"{{int:savearticle}}\" aftur, verður breyting þín vistuð án þess.",
        "summary-preview": "Forskoða breytingarágrip:",
-       "subject-preview": "Forskoðun umræðuefnis/fyrirsagnar:",
+       "subject-preview": "Forskoðun umræðuefnis:",
        "blockedtitle": "Notandi er bannaður",
        "blockedtext": "'''Notandanafn þitt eða vistfang hefur verið bannað.'''\n\nBannið var sett af $1.\nÁstæðan er eftirfarandi: ''$2''.\n\n* Bannið hófst: $8\n* Banninu lýkur: $6\n* Sá sem banna átti: $7\n\nÞú getur haft samband við $1 eða annan [[{{MediaWiki:Grouppage-sysop}}|stjórnanda]] til að ræða bannið.\nÞú getur ekki notað „Senda þessum notanda tölvupóst“ aðgerðina nema gilt netfang sé skráð í [[Special:Preferences|notandastillingum þínum]] og að þér hafi ekki verið óheimilað það.\nNúverandi vistfang þitt er $3, og bönnunarnúmerið er #$5.\nVinsamlegast tilgreindu allt að ofanverðu í fyrirspurnum þínum.",
        "autoblockedtext": "Vistfang þitt hefur verið sjálfvirkt bannað því það var notað af öðrum notanda, sem var bannaður af $1.\nÁstæðan er eftirfarandi:\n\n:''$2''\n\n* Bannið hófst: $8\n* Banninu lýkur: $6\n* Sá sem banna átti: $7\n\nÞú getur haft samband við $1 eða annan [[{{MediaWiki:Grouppage-sysop}}|stjórnanda]] til að ræða bannið.\n\nAthugaðu að þú getur ekki notað „Senda þessum notanda tölvupóst“ aðgerðina nema gilt netfang sé skráð í [[Special:Preferences|notandastillingum þínum]] og að þér hafi ekki verið óheimilað það.\n\nNúverandi vistfang þitt er $3, og bönnunarnúmerið er #$5.\nVinsamlegast tilgreindu allt að ofanverðu í fyrirspurnum þínum.",
        "copyrightwarning": "Vinsamlegast athugaðu að öll framlög á {{SITENAME}} eru álitin leyfisbundin samkvæmt $2 (sjá $1 fyrir frekari upplýsingar).  Ef þú vilt ekki að skrif þín falli undir þetta leyfi og öllum verði frjálst að breyta og endurútgefa efnið samkvæmt því skaltu ekki leggja þau fram hér.<br />\nÞú berð ábyrgð á framlögum þínum, þau verða að vera þín skrif eða afrit texta í almannaeigu eða sambærilegs frjáls texta.\n'''AFRITIРEKKI HÖFUNDARRÉTTARVARIN VERK Á ÞESSA SÍÐU ÁN LEYFIS'''",
        "copyrightwarning2": "Vinsamlegast athugið að aðrir notendur geta breytt eða fjarlægt öll framlög til {{SITENAME}}.\nEf þú vilt ekki að textanum verði breytt skaltu ekki senda hann inn hér.<br />\nÞú lofar okkur einnig að þú hafir skrifað þetta sjálfur, að efnið sé í almannaeigu eða að það heyri undir frjálst leyfi. (sjá $1).\n'''EKKI SENDA INN HÖFUNDARRÉTTARVARIРEFNI ÁN LEYFIS RÉTTHAFA!'''",
        "longpageerror": "'''VILLA: Textinn sem þú sendir inn er $1 {{PLURAL:$1|kílóbæti}} að lengd, en hámarkið er $2 {{PLURAL:$2|kílóbæti}}. Ekki er hægt að vista textann.'''",
-       "readonlywarning": "'''AÐVÖRUN: Gagnagrunninum hefur verið læst til að unnt sé að framkvæma viðhaldsaðgerðir, svo þú getur ekki vistað breytingar þínar núna.'''\nÞú ættir að klippa og líma textann yfir í textaskjal til þess að geyma hann til seinni tíma.\n\nStjórnandinn sem læsti honum gaf þessa skýringu: $1",
+       "readonlywarning": "<strong>AÐVÖRUN: Gagnagrunninum hefur verið læst til að unnt sé að framkvæma viðhaldsaðgerðir, svo þú getur ekki vistað breytingar þínar núna.</strong>\nÞú ættir að klippa og líma textann yfir í textaskjal til þess að geyma hann til seinni tíma.\n\nKerfisstjórinn sem læsti honum gaf þessa skýringu: $1",
        "protectedpagewarning": "'''Viðvörun: Þessari síðu hefur verið læst svo aðeins notendur með möppudýraréttindi geti breytt henni.'''\nSíðasta færsla síðunnar úr verndunarskrá er sýnd til skýringar:",
        "semiprotectedpagewarning": "'''Athugið''': Þessari síðu hefur verið læst þannig að aðeins innskráðir notendur geti breytt henni.\nSíðasta færsla síðunnar úr verndunarskrá er sýnd til skýringar:",
        "cascadeprotectedwarning": "<strong>Viðvörun:</strong> Þessari síðu hefur verið læst svo aðeins möppudýr geta breytt henni, því hún er ítengd keðjuvörn eftirfarandi {{PLURAL:$1|síðu|síðna}}:",
        "template-protected": "(vernduð)",
        "template-semiprotected": "(hálfvernduð)",
        "hiddencategories": "Þessi síða er meðlimur í $1 {{PLURAL:$1|földum flokki|földum flokkum}}:",
+       "edittools": "<!-- Þessi texti verður sýndur undir breytingar og upphölunar eyðublöðum. -->",
        "nocreatetext": "{{SITENAME}} hefur takmarkað eiginleikann að gera nýjar síður.\nÞú getur farið til baka og breytt núverandi síðum, eða [[Special:UserLogin|skráð þið inn eða búið til aðgang]].",
        "nocreate-loggedin": "Þú hefur ekki leyfi til að búa til nýjar síður.",
        "sectioneditnotsupported-title": "Hlutabreyting er ekki virk",
        "mergehistory-go": "Sýna breytingar sem hægt er að sameina",
        "mergehistory-submit": "Sameina útgáfur",
        "mergehistory-empty": "Engar útgáfur sem hægt er að sameina.",
-       "mergehistory-done": "$3 {{PLURAL:$3|útgáfa|útgáfur}} af $1 sameinaðar í [[:$2]].",
+       "mergehistory-done": "$3 {{PLURAL:$3|útgáfa|útgáfur}} af $1 {{PLURAL:$3|var|voru}} sameinaðar í [[:$2]].",
        "mergehistory-fail": "Gat ekki sameinað breytingasögur. Vinsamlegast athugaðu síðuna og tímabreyturnar.",
        "mergehistory-no-source": "Upprunasíðan $1 er ekki til.",
        "mergehistory-no-destination": "Marksíðan $1 er ekki til.",
        "prefs-watchlist-token": "Tóki vaktlistans:",
        "prefs-misc": "Aðrar stillingar",
        "prefs-resetpass": "Breyta lykilorði",
-       "prefs-changeemail": "Breyta netfangi",
+       "prefs-changeemail": "Breyta eða fjarlægja netfang",
        "prefs-setemail": "Skrá netfang",
        "prefs-email": "Tölvupóststillingar",
        "prefs-rendering": "Útlit",
        "rows": "Raðir",
        "columns": "Dálkar",
        "searchresultshead": "Leit",
-       "stub-threshold": "Þröskuldur fyrir <a href=\"#\" class=\"stub\">stubbatengla</a> (bæt):",
+       "stub-threshold": "Þröskuldur fyrir stílsnið stubbatengla ($1):",
        "stub-threshold-disabled": "Óvirkt",
        "recentchangesdays": "Fjöldi daga sem nýlegar breytingar ná yfir:",
        "recentchangesdays-max": "(hámark $1 {{PLURAL:$1|dag|daga}})",
        "enhancedrc-history": "breytingaskrá",
        "recentchanges": "Nýlegar breytingar",
        "recentchanges-legend": "Stillingar nýlegra breytinga",
-       "recentchanges-summary": "Hér geturðu fylgst með nýjustu breytingunum. {{SITENAME}} inniheldur '''[[Special:NewPages|{{NUMBEROFARTICLES}}]]''' {{PLURAL:{{NUMBEROFARTICLES}}|grein|greinar}} og '''[[Special:Statistics|{{NUMBEROFEDITS}}]]''' {{PLURAL:{{NUMBEROFEDITS}}|breytingu|breytingar}}. '''[[Special:ActiveUsers|{{NUMBEROFACTIVEUSERS}}]]''' {{PLURAL:{{NUMBEROFACTIVEUSERS}}|notandi|notendur}} hafa hjálpað til í þessum mánuði.",
+       "recentchanges-summary": "Hér geturðu fylgst með nýjustu breytingunum.",
        "recentchanges-noresult": "Engar breytingar í uppgefna tímabilinu sem passa við þessa mælikvarða.",
        "recentchanges-feed-description": "Hér er hægt að fylgjast með nýlegum breytingum á {{SITENAME}}.",
        "recentchanges-label-newpage": "Þessi breyting skapaði nýja síðu",
        "booksources-text": "Fyrir neðan er listi af tenglum í aðrar síður sem selja nýjar og notaðar bækur og gætu einnig haft nánari upplýsingar í sambandi við bókina sem þú varst að leita að:",
        "booksources-invalid-isbn": "ISBN gildið virðist ekki vera gilt; leitaðu eftir villum við innslátt eða afritun gildisins frá upsprettu þess.",
        "specialloguserlabel": "Gerandi:",
-       "speciallogtitlelabel": "Beinist að (titill eða notandi):",
+       "speciallogtitlelabel": "Beinist að (titill eða {{ns:user}}:notendanafn fyrir notanda):",
        "log": "Aðgerðaskrár",
        "all-logs-page": "Allar aðgerðir",
        "alllogstext": "Safn allra aðgerðaskráa {{SITENAME}}.\nÞú getur takmarkað listann með því að velja tegund aðgerðaskráar, notandanafn, eða síðu.",
        "activeusers-noresult": "Enginn notandi fannst.",
        "listgrouprights": "Notandahópréttindi",
        "listgrouprights-summary": "Hér er listi yfir notendahópa á þessum wiki, með þeirra réttindum. \nÞað gæti verið til síða með [[{{MediaWiki:Listgrouprights-helppage}}|frekari upplýsingar]] um einstök réttindi.",
-       "listgrouprights-key": "* <span class=\"listgrouprights-granted\">Veitt réttindi</span>\n* <span class=\"listgrouprights-revoked\">Afturkölluð réttindi</span>",
+       "listgrouprights-key": "Skýringar:\n* <span class=\"listgrouprights-granted\">Veitt réttindi</span>\n* <span class=\"listgrouprights-revoked\">Afturkölluð réttindi</span>",
        "listgrouprights-group": "Hópur",
        "listgrouprights-rights": "Réttindi",
        "listgrouprights-helppage": "Help:Hópréttindi",
        "emailccsubject": "Afrit af skilaboðinu þínu til $1: $2",
        "emailsent": "Sending tókst",
        "emailsenttext": "Skilaboðin þín hafa verið send.",
-       "emailuserfooter": "Þessi tölvupóstur var sendur af $1 til $2 með möguleikanum \"{{int:emailuser}}\" á {{SITENAME}}.",
+       "emailuserfooter": "Þessi tölvupóstur var {{GENDER:$1|sendur}} af $1 til {{GENDER:$2|$2}} með möguleikanum \"{{int:emailuser}}\" á {{SITENAME}}.",
        "usermessage-summary": "Skil eftir meldingu.",
        "usermessage-editor": "Meldinga sendiboði",
        "watchlist": "Vaktlistinn",
        "deletepage": "Eyða",
        "confirm": "Staðfesta",
        "excontent": "innihaldið var: „$1“",
-       "excontentauthor": "innihaldið var: '$1' (og öll framlög voru frá '[[Special:Contributions/$2|$2]]')",
+       "excontentauthor": "innihaldið var: „$1“ og öll framlög voru frá „[[Special:Contributions/$2|$2]]“ ([[User talk:$2|talk]])",
        "exbeforeblank": "innihald fyrir tæmingu var: '$1'",
        "delete-confirm": "Eyða „$1“",
        "delete-legend": "Eyða",
        "undeletepagetext": "Eftirfarandi $1 {{PLURAL:$1|síðu hefur verið eytt en hún er þó enn í gagnagrunninum og getur verið endurvakin|síðum hefur verið eytt en eru þó enn í gagnagrunninum og geta verið endurvaknar}}.\nGagnagrunnurinn kann að vera tæmdur reglulega.",
        "undelete-fieldset-title": "Endurvekja breytingar",
        "undeleteextrahelp": "Til þess að endurvekja alla breytingarskrá síðunnar, skildu öll box eftir óhökuð og ýttu á '''''{{int:undeletebtn}}'''''.\nTil þess að framkvæma ákveðna endurvakningu, ýttu á þau box sem standa hliðiná þeim útgáfum sem á að endurvekja og ýttu á '''''{{int:undeletebtn}}'''''.",
-       "undeleterevisions": "$1 {{PLURAL:$1|breyting|breytingar}}",
+       "undeleterevisions": "$1 {{PLURAL:$1|breytingu|breytingum}} eytt",
        "undeletehistory": "Ef þú endurvekur síðuna verða allar útgáfur færðar í breytingarsögu.\nEf ný síða með sama nafni hefur verið stofnuð síðan henni var eytt, verða breytingar síðunnar færðar síðast í breytingarskránna.",
        "undeleterevdel": "Endurvakning síðu verður ekki framkvæmd ef það leiðir til þess að haus síðunnar eða breytingarsaga hennar verði að hluta til eydd.\nÍ slíkum málum, þarft þú að afhaka við eða affela nýjustu eyddu breytinguna.",
        "undeletehistorynoadmin": "Þessari síðu hefur verið eytt. Ástæðan sést í ágripinu fyrir neðan, ásamt upplýsingum um hvaða notendur breyttu síðunni fyrir eyðingu.\nInnihald greinarinnar er einungis aðgengilegt möppudýrum.",
        "contributions": "Framlög {{GENDER:$1|notanda}}",
        "contributions-title": "Framlög notanda $1",
        "mycontris": "Framlög",
+       "anoncontribs": "Framlög",
        "contribsub2": "Eftir {{GENDER:$3|$1}} ($2)",
        "nocontribs": "Engar breytingar fundnar sem passa við þessa viðmiðun.",
        "uctop": "(núverandi)",
        "move-page-legend": "Færa síðu",
        "movepagetext": "Hér er hægt að endurnefna síðu. Hún færist, ásamt breytingaskránni, yfir á nýtt heiti og eldra heitið myndar tilvísun á það. Þú getur sjálfkrafa uppfært tilvísanir á nýja heitið. Ef þú vilt það síður, athugaðu þá hvort nokkuð myndist [[Special:DoubleRedirects|tvöfaldar]] eða [[Special:BrokenRedirects|brotnar tilvísanir]].\nÞú berð ábyrgð á því að tenglar vísi á rétta staði.\n\nAthugaðu að síðan mun '''ekki''' færast ef þegar er síða á nafninu sem þú hyggst færa hana á, nema sú síða sé tóm eða tilvísun sem vísar á síðuna sem þú ætlar að færa. Þú getur þar með fært síðuna aftur til baka án þess að missa breytingarsöguna, en ekki fært hana yfir venjulega síðu.\n\n'''Varúð:'''\nAthugaðu að þessi aðgerð getur kallað fram viðbrögð annarra notenda og getur þýtt mjög rótækar breytingar á vinsælum síðum.",
        "movepagetext-noredirectfixer": "Með þessu eyðublaði er hægt að endurnefna síðu og færa alla breytingarskrá hennar á nýja nafnið. Gamli titillinn verður að tilvísun á nýja titilinn. \nAthugaðu hvort síðan tengist [[Special:DoubleRedirects|tvöfaldri]]- eða [[Special:BrokenRedirects|brotinni]] tilvísun.\nÞú berð ábyrgð á því að tenglarnir haldi áfram að tengjast á réttan stað.\n\nAthugaðu að síðan verður '''ekki''' færð ef síða er þegar til á nýja titlinum, nema hann sé annaðhvort tómur, tilvísun eða hafi enga breytingarskrá.\nÞetta merkir að þú getur fært síðu aftur til baka á þann stað sem hún var færð frá ef þú gerir mistök og þú getur ekki skrifað yfir síðu sem er þegar til.\n\n'''Varúð:'''\nEf síðan er vinsæl þá getur þessi aðgerð kallað fram viðbrögð annara notenda og getur þýtt mjög rótækar breytingar á öðrum síðum. Vertu viss um að þú skiljir hættuna áður en þú heldur áfram.",
-       "movepagetalktext": "Spallsíða síðunnar verður sjálfkrafa færð með ef hún er til nema:\n* Þú sért að færa síðuna á milli nafnrýma\n* Spallsíða sé þegar til undir nýja nafninu\n* Þú veljir að færa hana ekki\nÍ þeim tilfellum verður að færa hana handvirkt.",
+       "movepagetalktext": "Ef þú hakar við þennan reit mun viðeigandi spjallsíða vera færð sjálfkrafa á nýja titilinn, nema að spjallsíða sem er ekki tóm sé þegar til staðar.\n\nÍ því tilfelli þarft þú að færa eða sameina síðuna handvirkt ef þess er óskað.",
        "moveuserpage-warning": "'''Viðvörun:''' Þú ert í þann mund að færa notendasíðu. Athugaðu aðeins síðan verður færð og notendanafni hans verður '''ekki''' breytt.",
        "movenologintext": "Þú verður að vera [[Special:UserLogin|innskráð(ur)]] til að geta fært síður.",
        "movenotallowed": "Þú hefur ekki leyfi til að færa síður.",
        "movenotallowedfile": "Þú hefur ekki leyfi til að færa skrár.",
        "cant-move-user-page": "Þú hefur ekki leyfi til að færa notandasíðu (fyrir utan undirsíður).",
        "cant-move-to-user-page": "Þú hefur ekki leyfi til að færa síðu á notandasíðu (að frátöldum undirsíðum notanda).",
-       "newtitle": "Á nýja titilinn:",
+       "newtitle": "Nýr titill:",
        "move-watch": "Vakta þessa síðu",
        "movepagebtn": "Færa síðu",
        "pagemovedsub": "Færsla tókst",
index 9d213b0..55049ec 100644 (file)
        "mediastatistics": "Statistiche relative ai file multimediali",
        "mediastatistics-summary": "Statistiche sui tipi di file caricati. Sono incluse solo la versione più recente di un file. Versioni vecchie o cancellate dei file sono escluse.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte}} ($2; $3%)",
-       "mediastatistics-bytespertype": "Dimensione totale dei file per questa sezione: $1 byte.",
-       "mediastatistics-allbytes": "Dimensione totale di tutti i file: $1 byte.",
+       "mediastatistics-bytespertype": "Dimensione totale dei file per questa sezione: {{PLURAL:$1|$1 byte}} ($2; $3%).",
+       "mediastatistics-allbytes": "Dimensione totale di tutti i file: {{PLURAL:$1|$1 byte}} ($2).",
        "mediastatistics-table-mimetype": "Tipo MIME",
        "mediastatistics-table-extensions": "Possibili estensioni",
        "mediastatistics-table-count": "Numero di file",
index 81bfe5f..845e7e7 100644 (file)
        "foreign-structured-upload-form-label-not-own-work-local-default": "このファイルはその方針の下でそこにアップロードすることができれば、また、 [[Special:Upload|the upload page on {{SITENAME}}]]を使用してみてください",
        "foreign-structured-upload-form-label-own-work-message-shared": "私は、このファイルの著作権を所有していることを宣誓し、取消し不能な形で  [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] ライセンスのもとでウィキメディア・コモンズに、このファイルを解放することに同意します。そして私は、  [https://wikimediafoundation.org/wiki/Terms_of_Use Terms of Use] に同意します。",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "このファイルの著作権を所有していない場合、または別のライセンスの下でそれをリリースしたい場合には、 [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard] を使用することを検討してください。",
-       "foreign-structured-upload-form-label-not-own-work-local-shared": "ã\82\82ã\81\97ã\82µã\82¤ã\83\88ã\81\8cã\80\81ã\81\9dã\82\8cã\82\89ã\81®æ\96¹é\87\9dã\81®ä¸\8bã\81§ã\80\81ã\81\93ã\81®ã\83\95ã\82¡ã\82¤ã\83«ã\81®ã\82¢ã\83\83ã\83\97ã\83­ã\83¼ã\83\89ã\82\92許å\8f¯ã\81\99ã\82\8bå ´å\90\88ã\81¯ã\80\81You may also want to try using [[Special:Upload|{{SITENAME}}ä¸\8aã\81§ã\81®ã\82¢ã\83\83ã\83\97ã\83­ã\83¼ã\83\89ã\83\9aã\83¼ã\82¸]]ã\82\92使ç\94¨ã\81\99ã\82\8bã\81\93ã\81¨ã\82\82試ã\81\97ã\81¦ã\81\8fã\81 ã\81\95ã\81\84。",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "ã\82\82ã\81\97ã\82µã\82¤ã\83\88ã\81\8cã\80\81ã\81\9dã\82\8cã\82\89ã\81®æ\96¹é\87\9dã\81®ä¸\8bã\81«ã\81¦ã\80\81ã\81\93ã\81®ã\83\95ã\82¡ã\82¤ã\83«ã\81®ã\82¢ã\83\83ã\83\97ã\83­ã\83¼ã\83\89ã\82\92許å\8f¯ã\81\97ã\81¦ã\81\84ã\82\8bå ´å\90\88ã\81¯ã\80\81[[Special:Upload|{{SITENAME}}ä¸\8aã\81§ã\81®ã\82¢ã\83\83ã\83\97ã\83­ã\83¼ã\83\89ã\83\9aã\83¼ã\82¸]]ã\81®å\88©ç\94¨ã\82\82æ¤\9cè¨\8eã\81§ã\81\8dã\81¾ã\81\99。",
        "foreign-structured-upload-form-3-label-yes": "はい",
        "foreign-structured-upload-form-3-label-no": "いいえ",
        "backend-fail-stream": "ファイル $1 をストリームできませんでした。",
index 0eaae57..70e0d64 100644 (file)
        "mediastatistics": "Statistike vun de Medien",
        "mediastatistics-summary": "Statistike vun den Type vun den eropgeluedene Fichieren. Dobäi gëtt nëmmen déi lescht Versioun vun engem Fichier gezielt, al oder geläscht Versioune vu Fichiere sinn ausgeschloss.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 Byte|$1 Byten}} ($2; $3%)",
-       "mediastatistics-bytespertype": "Gesamtgréisst vun de Fichiere vun dësem Abschnitt: $1",
+       "mediastatistics-bytespertype": "Gesamtgréisst vun de Fichiere vun dësem Abschnitt:  {{PLURAL:$1|$1 Byte|$1 Bytes}} ($2; $3%).",
        "mediastatistics-table-mimetype": "MIME-Typ",
        "mediastatistics-table-extensions": "Méiglech Erweiderungen",
        "mediastatistics-table-count": "Zuel vun de Fichieren",
index 59f5979..e6af41d 100644 (file)
@@ -9,7 +9,8 @@
                        "아라",
                        "Leeheonjin",
                        "TTO",
-                       "Macofe"
+                       "Macofe",
+                       "Amiel Guanlao"
                ]
        },
        "tog-underline": "Gulisan lang panglalam deng suglung:",
        "unprotectthispage": "Lako ya pangaprotekta ing bulung a ini",
        "newpage": "Bayung bulung",
        "talkpage": "Pisabian ya ining bulung",
-       "talkpagelinktext": "Pisasabian",
+       "talkpagelinktext": "talamitam",
        "specialpage": "Bulung a Makabukud",
        "personaltools": "Sariling kasangkapan",
        "articlepage": "Lawen me ing kalamnan ning bulung",
index dfa5375..0dc4695 100644 (file)
        "mediastatistics": "Statystyki mediów",
        "mediastatistics-summary": "Statystyki dotyczące przesłanych typów plików. Dotyczą one tylko najnowszej wersji pliku. Starsze lub usunięte wersje plików nie są uwzględniane.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtów}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Całkowity rozmiar pliku dla tej sekcji: {{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtów}} ($2; $3%).",
+       "mediastatistics-allbytes": "Całkowity rozmiar pliku dla wszystkich plików: {{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtów}} ($2).",
        "mediastatistics-table-mimetype": "Typ MIME",
        "mediastatistics-table-extensions": "Możliwe rozszerzenia",
        "mediastatistics-table-count": "Liczba plików",
index 61415d6..67fcdff 100644 (file)
        "ipb-change-block": "एतैः विन्यासैः सदस्यं पुनः अवरुणद्धु ।",
        "ipb-confirm": "अवरोधं दृढयतु ।",
        "badipaddress": "अमान्यः ऐपिसङ्केतः ।",
-       "blockipsuccesssub": "अवरोधः सफलः ।",
+       "blockipsuccesssub": "अवरोधः सफलः",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]]इत्येतत् अवरुद्धम् । <br />\nअवरोधानां समीक्षां करोतु । [[Special:BlockList|IP अवरोधसूचिका]]",
        "ipb-blockingself": "भवान् स्वयम् अवरोधने निरतः । निश्चयेन स्वावरोधनम् इच्छति वा ?",
        "ipb-confirmhideuser": "सदस्यगोपनस्य पिञ्जं निपीडयन् भवान् सदस्यावरुद्धिं यतते । एतत् सर्वावलीषु सर्वप्रवेशसूचिकासु च सदस्यनाम निग्रहति । भवान् निश्चयेन एतत् कर्तुमिच्छति वा ?",
index 0484663..11dbca2 100644 (file)
        "tog-hideminor": "تازين تبديلين منجھہ معمولي تبديليون لڪايو",
        "tog-hidepatrolled": "تازيون نگرانيل تبديليون لڪايو",
        "tog-newpageshidepatrolled": "نَوَن صفحن واري فهرست مان نگرانيل صفحا لڪايو",
-       "tog-hidecategorization": "صÙ\81Ø­Ù\86 Ø¬Ø§ Ø°مرا لڪايو",
-       "tog-extendwatchlist": "تازه ترين بدران سموريون تبديليون ڏيکارڻ لاءِ ٽيٽ لسٽ کي وسيع ڪريو.",
+       "tog-hidecategorization": "صÙ\81Ø­Ù\86 Ø¬Ø§ Ø²مرا لڪايو",
+       "tog-extendwatchlist": "تازه ترين بدران سموريون تبديليون ڏيکارڻ لاءِ زير نظر فهرست کي وسيع ڪريو.",
        "tog-numberheadings": "سُرخين کي خودڪاراً نمبر ڏيو",
        "tog-showtoolbar": "سنوار اوزار ڏيکاريو",
        "tog-editondblclick": "ٻٽي ڪلڪ تي صفحا سنواريو",
        "tog-watchcreations": "منهنجا سرجيل صفحا ۽ منهنجا چاڙهيل فائيل منهنجي زيرِ نظر فهرست تي رکو",
-       "tog-watchdefault": "منهنجا ترميميل صفحا منهنجي ٽيٽ فهرست تي رکو",
-       "tog-watchmoves": "جيڪي صفحا ۽ فائيلس آئون چوريان، سي منهنجي ٽيٽ لسٽ ۾ شامل ڪريو.",
-       "tog-watchdeletion": "آئÙ\88Ù\86 Ø¬Ù\8aÚªÙ\8a ØµÙ\81حا Ú\8aاÙ\87Ù\8aاÙ\86Ø\8c Ø³Ù\8a Ù\85Ù\86Ù\87Ù\86جÙ\8a Ù½Ù\8aÙ½ فهرست تي رکو",
-       "tog-watchrollback": "انهن صفحن کي منهنجي ٽيٽ فهرست تي رکو، جن ۾ تبديلين کي مون واپس ورايو آهي.",
+       "tog-watchdefault": "منهنجا ترميميل صفحا ۽ فائيل  منهنجي زير نظر فهرست تي رکو",
+       "tog-watchmoves": "جيڪي صفحا ۽ فائيل آءُٗ چوريان، سي منهنجي زير نظر فهرست ۾ شامل ڪريو.",
+       "tog-watchdeletion": "آءÙ\8fÙ\97 Ø¬Ù\8aÚªÙ\8a ØµÙ\81حا Û½ Ù\81ائÙ\8aÙ\84  Ú\8aاÙ\87Ù\8aاÙ\86Ø\8c Ø³Ù\8a Ù\85Ù\86Ù\87Ù\86جÙ\8a Ø²Ù\8aر Ù\86ظر فهرست تي رکو",
+       "tog-watchrollback": "انهن صفحن کي منهنجي زير نظر فهرست تي رکو، جن ۾ تبديلين کي مون واپس ورايو آهي.",
        "tog-minordefault": "سمورين تبديلين کي بنان چئي معمولي ترميم تصور ڪريو",
        "tog-previewontop": "ترميمي باڪس مٿان پيش نگاهہ ڏيکاريو",
        "tog-previewonfirst": "پهرين ترميم تي پيش نگاهہ ڏيکاريو",
+       "tog-enotifwatchlistpages": "مونکي ايميل ڪريو جڏهن منهنجي زير نظر فهرست ڪا صفحو يا فائيل تبديل ڪيو وڃي",
        "tog-enotifusertalkpages": "منهنجي مباحثي صفحي ۾ تبديليءَ جي صورت ۾ مون کي برق ٽپال اماڻيو",
        "tog-enotifminoredits": "صفحن ۾ معمولي ترميمن جي صورت ۾ بہ مون کي برق ٽپال ڪريو",
        "tog-enotifrevealaddr": "پڌراين ۾ منهنجو برق ٽپال پتو ظاهر ڪريو.",
-       "tog-shownumberswatching": "Ù½Ù\8aÙ½Ù\8aÙ\86دÚ\99 Ù\8aÙ\88زرس Ø¬Ù\88 ØªØ¹Ø¯Ø§Ø¯ Ú\8fÙ\8aکارÙ\8aÙ\88",
+       "tog-shownumberswatching": "Ú\8fسÙ\86دÚ\99 Ù\8aÙ\88زرس Ø¬Ù\88 Ø§Ù\86Ú¯ Ú\8fÙ\8aکارÙ\8aÙ\88",
        "tog-oldsig": "موجوده دستخط",
        "tog-uselivepreview": "سڌي سنئين پيش نگاھہ استعمال ڪريو",
-       "tog-watchlisthideown": "ٽيٽ فهرست مان منهنجون ڪيل ترميمون لڪايو",
+       "tog-watchlisthideown": "زير نظر فهرست مان منهنجون ڪيل ترميمون لڪايو",
        "tog-watchlisthidebots": "ٽيٽ فهرست تان بوٽ جون ترميمون لڪايو",
        "tog-watchlisthideminor": "ٽيٽ فهرست تان معمولي ترميمون لڪايو",
-       "tog-watchlisthideliu": "لاگ اِن ٿيل يوزرس جون ڪيل ترميمون ٽيٽ فهرست ۾ نہ ڏيکاريو",
+       "tog-watchlisthideliu": "لاگ اِن ٿيل يوزرس جون ڪيل ترميمون زيرنظر فهرست ۾ نہ ڏيکاريو",
        "tog-watchlisthideanons": "ٽيٽ فهرست تان اڻڄاتل يوزر جون ترميمون لڪايو",
        "tog-watchlisthidecategorization": "صفحن جا زمرا لڪايو",
        "tog-ccmeonemails": "ٻين يوزرس ڏانهن منهنجي موڪليل برق ٽپال جو پرت مون کي اماڻيو",
@@ -44,6 +45,7 @@
        "tog-prefershttps": "هميشه محفوظ ڪنيڪشن استعمال ڪريو جڏهن لاگ اِن ٿيل هجو",
        "underline-always": "هميشہ",
        "underline-never": "ڪڏهن بہ نہ",
+       "editfont-style": "ايراضي جو فونٽ اسٽائيل سنواريو:",
        "sunday": "آچر",
        "monday": "سومر",
        "tuesday": "اڱارو",
@@ -62,7 +64,7 @@
        "february": "فيبروري",
        "march": "مارچ",
        "april": "اپريل",
-       "may_long": "مَي",
+       "may_long": "مَئي",
        "june": "جُونِ",
        "july": "جُولاءِ",
        "august": "آگسٽ",
@@ -74,7 +76,7 @@
        "february-gen": "فيبروري",
        "march-gen": "مارچ",
        "april-gen": "اپريل",
-       "may-gen": "مَي",
+       "may-gen": "مَئي",
        "june-gen": "جُونِ",
        "july-gen": "جُولاءِ",
        "august-gen": "آگسٽ",
@@ -86,7 +88,7 @@
        "feb": "فيبروري",
        "mar": "مارچ",
        "apr": "اپريل",
-       "may": "مَي",
+       "may": "مَئي",
        "jun": "جُونِ",
        "jul": "جُولاءِ",
        "aug": "آگسٽ",
        "february-date": "فيبروري $1",
        "march-date": "مارچ $1",
        "april-date": "اپريل $1",
-       "may-date": "مَي $1",
+       "may-date": "مَئي $1",
        "june-date": "جُون $1",
        "july-date": "جُولاءِ $1",
        "august-date": "آگسٽ $1",
        "qbedit": "سنواريو",
        "qbpageoptions": "هيءُ صفحو",
        "qbmyoptions": "منهنجا صفحا",
-       "faq": "ڪپوس",
+       "faq": "ڪپس",
        "faqpage": "Project:ڪپوس",
        "actions": "فعل",
        "namespaces": "نانءُ پولار:",
        "notloggedin": "لاگ اِن ٿيل ناهيو",
        "userlogin-noaccount": "کاتو نہ ٿا رکو؟",
        "userlogin-joinproject": "{{SITENAME}} ۾ شامل ٿيو",
-       "nologin": "پنهنجو کاتو نہ ٿا رکو؟ '''$1'''.",
+       "nologin": " کاتو نہ ٿا رکو؟ '''$1'''.",
        "nologinlink": "نئون کاتو کوليو",
        "createaccount": "کاتو کوليو",
        "gotaccount": "ڇا اڳي ئي کاتو رکو ٿا؟ '''$1'''.",
        "changeemail-submit": "برق ٽپال پتو بدلايو",
        "changeemail-throttled": "توهان تازو ئي لاگ اِن ٿيڻ جون هيڪانديون گھڻيون ڪوششون ڪيون آهن. مهرباني ڪري $1 لاءِ ترسي پوءِ وري ڪوشش ڪريو.",
        "changeemail-nochange": "مهرباني ڪري مختلف نئون برق ٽپال پتو ڄاڻايو.",
+       "resettokens": "ٻيهر ترتيب ڪرڻ جا ٽوڪن",
+       "resettokens-no-tokens": "ٻيهر ترتيب ڪرڻ لاءِ ڪي بہ ٽوڪن نہ آهن.",
        "resettokens-tokens": "ٽوڪنس:",
        "resettokens-token-label": "$1 (حاليہ قدر: $2)",
+       "resettokens-resetbutton": "چونڊيل ٽوڪن ٻيهر ترتيب ڪريو",
        "bold_sample": "گهري تحرير",
        "bold_tip": "گهري لکت",
        "italic_sample": "ترڇي لکت",
        "mergehistory-from": "ذريعہ صفحو:",
        "mergehistory-into": "مقصود صفحو:",
        "mergehistory-list": "ضمائتي ترميم سوانح",
+       "mergehistory-go": "ضم ڪرڻ لائق ترميمون ڏيکاريو",
        "mergehistory-submit": "ڀيرن کي ضم ڪريو",
        "mergehistory-empty": "ڪي بہ ڀيرا ضم ڪري نہ ٿا سگھجن.",
        "mergehistory-no-source": "مصدر صفحو $1 وجود نٿو رکي.",
index a7a2bcf..82e7e41 100644 (file)
        "mediastatistics": "Statistika predstavnosti",
        "mediastatistics-summary": "Statistika o naloženih vrstah datotek. To vključuje samo najnovejše različice datotek. Stare in izbrisane različice niso vključene.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bajt|$1 bajta|$1 bajti|$1 bajtov}} ($2; $3 %)",
-       "mediastatistics-bytespertype": "Skupna velikost datoteke za ta razdelek: $1 bajtov.",
-       "mediastatistics-allbytes": "Skupna velikost datoteke za vse datoteke: $1 bajtov.",
+       "mediastatistics-bytespertype": "Skupna velikost datoteke za ta razdelek: $1 {{PLURAL:$1|bajt|bajta|bajte|bajtov}} ($2; $3 %).",
+       "mediastatistics-allbytes": "Skupna velikost datoteke za vse datoteke: $1 {{PLURAL:$1|bajt|bajta|bajte|bajtov}} ($2).",
        "mediastatistics-table-mimetype": "Vrsta MIME",
        "mediastatistics-table-extensions": "Možne razširitve",
        "mediastatistics-table-count": "Število datotek",
index 43c067f..198f93e 100644 (file)
        "laggedslavemode": "<strong>Varning:</strong> Sidan kan sakna de senaste uppdateringarna.",
        "readonly": "Databasen är låst",
        "enterlockreason": "Ange varför databasen låsts och inkludera en uppskattning om när låsningen kommer att hävas",
-       "readonlytext": "Databasen är tillfälligt låst för nya inlägg och andra modifieringar, förmodligen på grund av rutinmässigt underhåll, efter vilket den kommer den att återgå till normalläge.\n\nDen systemadministratör som låste den har angivit följande förklaring: $1",
+       "readonlytext": "Databasen är tillfälligt låst för nya inlägg och andra modifieringar, förmodligen på grund av rutinmässigt underhåll, efter vilket den kommer att återgå till normalläge.\n\nDen systemadministratör som låste den har angivit följande förklaring: $1",
        "missing-article": "Databasen hittade inte texten för en sida som den borde ha funnit, med namnet \"$1\" $2.\n\nDetta orsakas oftast av att man följer en inaktuell länk till en jämförelse mellan versioner (diff) eller en historiklänk för en sida som raderats.\n\nOm inte så är fallet, kan du ha hittat en bugg i mjukvaran.\nRapportera gärna problemet till någon [[Special:ListUsers/sysop|administratör]], ange då URL:en (webbadressen).",
        "missingarticle-rev": "(versionsnummer: $1)",
        "missingarticle-diff": "(Skillnad: $1, $2)",
        "copyrightwarning2": "Observera att alla bidrag till {{SITENAME}} kan komma att redigeras, ändras, eller tas bort av andra deltagare. Om du inte vill se din text förändrad efter andras gottfinnade skall du inte skriva in någon text här.<br />\nDu lovar oss också att du skrev texten själv, eller kopierade från kulturellt allmängods som inte skyddas av upphovsrätt, eller liknande källor - se $1 för detaljer.\n'''LÄGG INTE UT UPPHOVSRÄTTSSKYDDAT MATERIAL HÄR UTAN TILLÅTELSE!'''",
        "editpage-cannot-use-custom-model": "Innehållsmodellen för denna sida kan inte ändras.",
        "longpageerror": "'''FEL: Texten som du försöker spara är {{PLURAL:$1|en kilobyte|$1 kilobyte}}, vilket är mer än det maximalt tillåtna {{PLURAL:$2|en kilobyte|$2 kilobyte}}.'''\nDen kan inte sparas.",
-       "readonlywarning": "<strong>VARNING: Databasen är tillfälligt låst för underhåll. Du kommer inte att kunna spara dina ändringar just nu.\nDet kan vara klokt att kopiera texten till ett textdokument som sparas på din dator tills vidare.</strong>\n\nSystemadministratören som låste databasen gav följande förklaring: $1",
+       "readonlywarning": "<strong>VARNING: Databasen är tillfälligt låst för underhåll. Du kommer inte att kunna spara dina ändringar just nu.</strong>\nDet kan vara klokt att kopiera texten till ett textdokument som sparas på din dator tills vidare.\n\nSystemadministratören som låste databasen gav följande förklaring: $1",
        "protectedpagewarning": "'''Varning: Den här sidan har låsts så att bara användare med administratörsrättigheter kan redigera den.'''\nDen senaste loggposten tillhandahålls nedan som referens:",
        "semiprotectedpagewarning": "'''Observera:''' Denna sida har låsts så att endast registrerade användare kan redigera den.\nDen senaste loggposten tillhandahålls nedan som referens:",
        "cascadeprotectedwarning": "'''Varning:''' Den här sidan har låsts så att bara användare med administratörsrättigheter kan redigera den, eftersom den är inkluderad på följande {{PLURAL:$1|sida|sidor}} som skyddats med kaskaderande skrivskydd:",
        "permissionserrors": "Behörighetsfel",
        "permissionserrorstext": "Du har inte behörighet att göra det du försöker göra, av följande {{PLURAL:$1|anledning|anledningar}}:",
        "permissionserrorstext-withaction": "Du har inte behörighet att $2, av följande {{PLURAL:$1|anledning|anledningar}}:",
-       "contentmodelediterror": "Du kan inte redigera denna sidversion eftersom dess innehållsmodell är <code>$1</code> som skiljer sig från sidans aktuella innehållsmodell <code>$2</code>.",
+       "contentmodelediterror": "Du kan inte redigera den här sidversionen eftersom dess innehållsmodell är <code>$1</code> som skiljer sig från sidans aktuella innehållsmodell <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Varning: Du återskapar en sida som tidigare raderats.'''\n\nDu bör överväga om det är lämpligt att fortsätta redigera den här sidan.\nRaderings- och sidflyttningsloggen för den här sidan visas här som hjälp:",
        "moveddeleted-notice": "Den här sidan har raderats.\nRaderings- och sidflyttningsloggen för sidan visas nedan som referens.",
        "moveddeleted-notice-recent": "Tyvärr, denna sida raderades nyligen (inom de senaste 24 timmarna).\nLoggen för radering och flyttning av sidan visas nedan som referens.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Jag intygar att jag äger upphovsrätten för denna fil och samtycker till att oåterkalleligen släppa filen på Wikimedia Commons under licensen \n[https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] och jag accepterar [https://wikimediafoundation.org/wiki/Terms_of_Use villkoren för användning].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Om du inte äger upphovsrätten för denna fil eller om du önskar att släppa den under en annan licens bör du överväga att använda [https://commons.wikimedia.org/wiki/Special:UploadWizard uppladdningsguiden på Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Du kanske skulle vilja prova att använda [[Special:Upload|uppladdningssidan på {{SITENAME}}]] om webbplatsens policys tillåter att denna fil laddas upp.",
-       "foreign-structured-upload-form-2-label-intro": "Tack för att du donera en bild för att användas på {{SITENAME}}. Du bör endast fortsätta om det uppfyller flera villkor:",
-       "foreign-structured-upload-form-2-label-ownwork": "Det måste vara helt och hållet <strong>din egen skapelse</strong>, inte bara taget från internet",
-       "foreign-structured-upload-form-2-label-noderiv": "Det får inte innehålla <strong>något verk av någon annan</strong>, eller inspirerats av dem",
-       "foreign-structured-upload-form-2-label-useful": "Det bör vara <strong>pedagogisk och användbar</strong> för att undervisa andra",
-       "foreign-structured-upload-form-2-label-ccbysa": "Det måste vara <strong>OK att publicera för evigt</strong> på internet under [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Erkännande-DelaLika 4.0]-licensen",
+       "foreign-structured-upload-form-2-label-intro": "Tack för att du donera en bild för att användas på {{SITENAME}}. Du bör endast fortsätta om den uppfyller flera villkor:",
+       "foreign-structured-upload-form-2-label-ownwork": "Den måste vara helt och hållet <strong>din egen skapelse</strong>, inte bara tagen från Internet",
+       "foreign-structured-upload-form-2-label-noderiv": "Den får inte innehålla <strong>något verk av någon annan</strong>, eller inspirerats av dem",
+       "foreign-structured-upload-form-2-label-useful": "Den bör vara <strong>pedagogisk och användbar</strong> för att undervisa andra",
+       "foreign-structured-upload-form-2-label-ccbysa": "Den måste vara <strong>OK att publicera för evigt</strong> på Internet under [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Erkännande-DelaLika 4.0]-licensen",
        "foreign-structured-upload-form-2-label-alternative": "Om inte alla av ovanstående är stämmer in, kan du fortfarande ha möjlighet att ladda upp denna fil med hjälp av [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard], så länge den är tillgänglig under en fri licens.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Genom att ladda upp filen, att du äger upphovsrätten till denna fil, samt att du samtycker till att oåterkalleligt släppa denna fil till Wikimedia Commons under Creative Commons Erkännande-DelaLika 4.0-licensen, samt att du samtycker till WIkimeidas  [https://wikimediafoundation.org/wiki/Terms_of_Use villkor för användning].",
+       "foreign-structured-upload-form-2-label-termsofuse": "Genom att ladda upp filen bekräftar du att du äger upphovsrätten till denna fil, samt att du samtycker till att oåterkalleligt släppa denna fil till Wikimedia Commons under Creative Commons Erkännande-DelaLika 4.0-licensen, samt att du samtycker till Wikimedias  [https://wikimediafoundation.org/wiki/Terms_of_Use användarvilkor].",
        "foreign-structured-upload-form-3-label-question-website": "Laddade du ner den här bilden från en webbplats eller hittade du den genom en bildsökning?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Har du skapa denna bild (tagit bilden, skissat, ritat, etc.) själv?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Innehåller det, eller är det inspirerade av arbete som ägs av någon annan, som t.ex. en logotyp?",
+       "foreign-structured-upload-form-3-label-question-ownwork": "Har du skapat denna bild (tagit bilden, skissat, ritat, etc.) själv?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "Innehåller den, eller är den inspirerade av arbete som ägs av någon annan, som t.ex. en logotyp?",
        "foreign-structured-upload-form-3-label-yes": "Ja",
        "foreign-structured-upload-form-3-label-no": "Nej",
-       "foreign-structured-upload-form-3-label-alternative": "Tyvärr har detta verktyg i detta fall inte stöd för att ladda upp denna fil. Du kan fortfarande ladda upp den med hjälp av [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons uppladdningsguide] så länge den är tillgänglig under en fri licens.",
+       "foreign-structured-upload-form-3-label-alternative": "Tyvärr har detta verktyg i detta fall inte stöd för att ladda upp den här filen. Du kan fortfarande ladda upp den med hjälp av [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons uppladdningsguide] så länge den är tillgänglig under en fri licens.",
        "foreign-structured-upload-form-4-label-good": "Med detta verktyg kan du ladda upp pedagogiska bilder som du har skapat och fotografier som du har tagit, som inte innehåller verk som någon annan äger.",
        "foreign-structured-upload-form-4-label-bad": "Du kan inte ladda upp bilder som hittats på en sökmotor eller har laddats ned från andra webbplatser.",
        "backend-fail-stream": "Kunde inte strömma filen $1.",
index fb26815..ef9ab19 100644 (file)
        "laggedslavemode": "Игътибар: биттә соңгы яңартулар күрсәтелмәгән булырга мөмкин.",
        "readonly": "Мәгълүматлар базасына язу ябылган",
        "enterlockreason": "Ябылу сәбәбен һәм вакытын күрсәтегез.",
-       "readonlytext": "Мәгълүмат базасы хәзерге вакытта яңа битләр ясаудан һәм башка үзгәртүләрдән ябылган. Бу планлаштырылган хезмәт күрсәтү сәбәпле булырга мөмкин.\nЯбучы оператор түбәндәге аңлатманы калдырган:\n$1",
+       "readonlytext": "Мәгълүмат базасы хәзерге вакытта яңа битләр ясаудан һәм башка үзгәртүләрдән ябылган: бу планлаштырылган хезмәт күрсәтү сәбәпле булырга мөмкин.\nЯбучы идарәче түбәндәге аңлатманы калдырган: $1",
        "missing-article": "Мәгълүматлар базасында «$1» $2 битенең соралган тексты табылмады.\n\nБу, гадәттә, искергән сылтама буенча бетерелгән битнең үзгәртү тарихына күчкәндә килеп чыга.\n\nӘгәр хата монда түгел икән, сез программада хата тапкан булырга мөмкинсез.\nЗинһар өчен, URLны күрсәтеп, бу турыда [[Special:ListUsers/sysop|идарәчегә]] хәбәр итегез.",
        "missingarticle-rev": "(юрама № $1)",
        "missingarticle-diff": "(аерма: $1, $2)",
        "viewsource": "Карау",
        "viewsource-title": "$1 битенең яхма текстын карау",
        "actionthrottled": "Тизлек киметелгән",
-       "actionthrottledtext": "Спамга каршы көрәш өчен аз вакыт эчендә бу гамәлне еш куллану тыелган. Зинһар, соңарак кабатлагыз.",
+       "actionthrottledtext": "Спамга каршы көрәш өчен, аз вакыт эчендә бу гамәлне еш куллану тыелган һәм СЕз бирелгән вакытны бетергәнсез инде. Зинһар, соңарак кабатлагыз.",
        "protectedpagetext": "Бу бит үзгәртүләрдән һәм башка төрле гамәлләрдән якланган.",
-       "viewsourcetext": "Сез бу битнең башлангыч текстын карый һәм күчерә аласыз:",
-       "viewyourtext": "Сез '''үз төзәтмәләрегезне''' бу сәхифәдә карый һәм чыгарылма текстны күчермәли аласыз:",
+       "viewsourcetext": "Сез бу битнең башлангыч текстын карый һәм күчерә аласыз.",
+       "viewyourtext": "Сез <strong>үз төзәтмәләрегезне</strong> бу сәхифәдә карый һәм чыгарылма текстны күчермәли аласыз.",
        "protectedinterface": "Бу биттә программа тәэминатының интерфейс хәбәрләре бар. Вандализмга каршы көрәш сәбәпле, бу битне үзгәртү тыела. Әлеге хәбәрнең тәрҗемәсен өстәү яки үзгәртү өчен, зинһар өчен, MediaWiki [//translatewiki.net/ translatewiki.net] тәрҗемәләү сайтын кулланыгыз.",
        "editinginterface": "<strong>Игътибар:</strong> Сез программа тәэминатының интерфейс тексты булган битне үзгәртәсез. Бу башка кулланучыларга да тәэсир итәчәк.",
        "translateinterface": "Бу хәбәрнең текстын үзгәртү өчен яки өстәмәләр кертү өчен MediaWiki җирләштерү сайтын кулланыгыз [//translatewiki.net/ translatewiki.net].",
        "virus-badscanner": "Көйләү хатасы. Билгесез вируслар сканеры: ''$1''",
        "virus-scanfailed": "сканерлау хатасы ($1 коды)",
        "virus-unknownscanner": "билгесез антивирус:",
-       "logouttext": "'''Сез хисап язмагыздан чыктыгыз.'''\n\nСез {{SITENAME}} проектында аноним рәвештә кала яисә шул ук яки башка исем белән яңадан <span class='plainlinks'>[$1 керә]</span> аласыз.\nКайбер битләр Сез кергән кебек күрсәтелергә мөмкин. Моны бетерү өчен браузер кэшын чистартыгыз.",
+       "logouttext": "<strong>Сез хисап язмагыздан чыктыгыз.</strong>\n\nКайбер битләр Сез кергән кебек күрсәтелергә мөмкин. Моны бетерү өчен браузер кэшын чистартыгыз.",
        "welcomeuser": "Хуш килдегез, $1!",
        "yourname": "Кулланучы исеме:",
        "userlogin-yourname": "Кулланучы исеме",
        "yourpasswordagain": "Серсүзне кабат кертү:",
        "createacct-yourpasswordagain": "Серсүзне раслагыз",
        "createacct-yourpasswordagain-ph": "Серсүзне кабаттан кертегез",
-       "remembermypassword": "Хисап язмамны бу браузерда саклансын (иң күп $1 {{PLURAL:$1|көн|көн|көн}}гә кадәр)",
+       "remembermypassword": "Хисап язмам бу браузерда саклансын (иң күбе $1 {{PLURAL:$1|көн}})",
        "userlogin-remembermypassword": "Системада калырга",
        "userlogin-signwithsecure": "Якланган кушылу",
        "yourdomainname": "Сезнең доменыгыз:",
        "createacct-emailoptional": "Электрон почта юлламагыз (мәҗбүри түгел)",
        "createacct-email-ph": "Электрон почта юлламагызны языгыз",
        "createacct-another-email-ph": "Электрон почта юлламагызны кертегез",
-       "createaccountmail": "электрон почта аша",
+       "createaccountmail": "Очраклы рәвештә ясалган ваҡытлыча серсузне файдаланырга һәм аны минем электрон почтама җибәрергә",
        "createacct-realname": "Чын исем (мәҗбүри түгел)",
        "createaccountreason": "Сәбәп:",
        "createacct-reason": "Сәбәп",
        "login-userblocked": "Бу кулланучы тыелды. Керү тыелган.",
        "wrongpassword": "Язылган серсүз дөрес түгел. Тагын бер тапкыр сынагыз.",
        "wrongpasswordempty": "Серсүз юлы буш булырга тиеш түгел.",
-       "passwordtooshort": "Сезсүз $1 {{PLURAL:$1|символдан}} торырга тиеш.",
+       "passwordtooshort": "Сезсүз кимендә $1 {{PLURAL:$1|символдан}} торырга тиеш.",
        "password-name-match": "Кертелгән серсүз кулланучы исеменнән аерылырга тиеш.",
        "password-login-forbidden": "Бу кулланучы исемен һәм серсүзне куллану тыелган",
        "mailmypassword": "Серсүзне бетерү",
        "passwordremindertitle": "{{SITENAME}} кулланучысына вакытлы серсүз тапшыру",
-       "passwordremindertext": "Кемдер (бәлки, сездер, IP адресы: $1) {{SITENAME}} ($4) өчен яңа серсүз соратты. $2 өчен яңа серсүз: $3. Әгәр бу сез булсагыз, системага керегез һәм серсүзне алмаштырыгыз. Яңа серсүз $5 {{PLURAL:$5|көн}} гамәлдә булачак.\n\nӘгәр сез серсүзне алмаштыруны сорамаган булсагыз яки, оныткан очракта, исегезгә төшергән булсагыз, бу хәбәргә игътибар бирмичә, иске серсүзегезне куллануны дәвам итегез.",
+       "passwordremindertext": "Кемдер (бәлки, сездер, IP адресы: $1)  {{grammar:genitive|{{SITENAME}}}} ($4) өчен яңа серсүз соратты. $2 кулланучысы өчен яңа вакытлыча серсүз: $3. Әгәр бу сез булган булсагыз, системага керегез һәм яңа серсүз сайлагыз. Сезнең вакытлыча серсүз гамәлдә $5 {{PLURAL:$5|көн}} булачак.\n\nӘгәр сез серсүзне алмаштыруны сорамаган булсагыз яки, оныткан очракта, исегезгә төшергән булсагыз, бу хәбәргә игътибар бирмичә, иске серсүзегезне куллануны дәвам итегез.",
        "noemail": "$1 исемле кулланучы өчен электрон почта адресы язылмаган.",
        "noemailcreate": "Сез дөрес e-mail адресы күрсәтергә тиеш",
        "passwordsent": "Яңа серсүз $1 исемле кулланучының электрон почта адресына җибәрелде.\n\nЗинһар, серсүзне алгач, системага яңадан керегез.",
        "blocked-mailpassword": "Сезнең IP адресыгыз белән битләр үзгәртеп һәм серсүзне яңартып булмый.",
-       "eauthentsent": "Ð\90дÑ\80еÑ\81 Ò¯Ð·Ð³Ó\99Ñ\80Ñ\82үне Ð´Ó\99лиллÓ\99Ò¯ Ó©Ñ\87ен Ð°Ò£Ð° Ð¼Ð°Ñ\85Ñ\81Ñ\83Ñ\81 Ñ\85аÑ\82 Ò\97ибÓ\99Ñ\80елде. Ð¥Ð°Ñ\82Ñ\82а Ñ\8fзÑ\8bлганнаÑ\80нÑ\8b Ò¯Ñ\82Ó\99вегез Ñ\81оÑ\80ала.",
+       "eauthentsent": "Ð\9aÒ¯Ñ\80Ñ\81Ó\99Ñ\82елгÓ\99н Ñ\8dлекÑ\82Ñ\80он Ð¿Ð¾Ñ\87Ñ\82а Ð°Ð´Ñ\80еÑ\81Ñ\8bна Ò¯Ð·Ð³Ó\99Ñ\80Ñ\82үлÓ\99Ñ\80не Ñ\80аÑ\81лаÑ\83 Ó©Ñ\87ен Ñ\85аÑ\82 Ò\97ибÓ\99Ñ\80елде. Ð\9aилÓ\99Ñ\87Ó\99кÑ\82Ó\99дÓ\99 Ñ\85аÑ\82лаÑ\80 ÐºÐ°Ð±Ñ\83л Ð¸Ñ\82Ò¯ Ó©Ñ\87ен, Ñ\80аÑ\81лаÑ\83нÑ\8b Ò¯Ñ\82егез.",
        "throttled-mailpassword": "Серсүзне электрон почтага җибәрү гамәлен сез {{PLURAL:$1|1=соңгы $1 сәгать}} эчендә кулландыгыз инде. Бу гамәлне явызларча куллануны кисәтү максатыннан аны $1 {{PLURAL:$1|сәгать}} аралыгында бер генә тапкыр башкарып була.",
        "mailerror": "Хат җибәрү хатасы: $1",
        "acct_creation_throttle_hit": "Сезнең IP адресыннан бу тәүлек эчендә {{PLURAL:$1|$1 хисап язмасы}} төзелде инде. Шунлыктан бу гамәл сезнең өчен вакытлыча ябык.",
        "passwordreset-emailerror-capture": "Түбәндә язылган хат-искәртү китерелгән, аны җибәрмәүнең сәбәбе:$1",
        "changeemail": "Электрон әрҗә адресын үзгәртү яисә бетерү",
        "changeemail-header": "Электрон әрҗә адресын үзгәртү",
+       "changeemail-passwordrequired": "Әлеге үзгәрешләрне раслау өчен, Сезгә кулланылучы серсүзне язарга кирәк.",
        "changeemail-no-info": "Бу сәхифәгә турыдан-туры мөрәҗәгать итәр өчен, сез системага керергә тиешсез",
        "changeemail-oldemail": "Хәзерге электрон әрҗә адресы:",
        "changeemail-newemail": "Яңа электрон почта адресы:",
        "changeemail-none": "(юк)",
        "changeemail-password": "«{{SITENAME}}» проекты өчен серсүзегез:",
        "changeemail-submit": "E-mail адресын үзгәртү",
+       "resettokens-tokens": "Токеннар:",
+       "resettokens-token-label": "$1 (агымдагы мәгънә: $2)",
        "bold_sample": "Калын язылыш",
        "bold_tip": "Калын язылыш",
        "italic_sample": "Курсив язылыш",
        "prefs-watchlist-token": "Күзәтү исемлеге токены:",
        "prefs-misc": "Башка көйләнмәләр",
        "prefs-resetpass": "Серсүзне үзгәртү",
+       "prefs-changeemail": "Электрон әрҗә адресын үзгәртү яисә бетерү",
        "prefs-email": "E-mail көйләнмәләре",
        "prefs-rendering": "Күренеш",
        "saveprefs": "Саклау",
        "linkstoimage": "{{PLURAL:$1|Киләсе $1 бит|Киләсе $1 битләр|}} әлеге файлга сылтама ясый:",
        "nolinkstoimage": "Бу файлга сылтаган битләр юк.",
        "duplicatesoffile": "{{PLURAL:$1|Әлеге $1 файл }} астагы файлның күчерелмәсе булып тора ([[Special:FileDuplicateSearch/$2|тулырак]]):",
-       "sharedupload": "Бу файл $1'дан һәм башка проектларда кулланырга мөмкин.",
-       "sharedupload-desc-here": "Бу файл $1'дан һәм башка проектларда кулланырга мөмкин. Файл турында [$2 мәгълүмат ] аста бирелгән.",
+       "sharedupload": "Бу файл $1 проектыннан һәм башка проектларда кулланырга мөмкин",
+       "sharedupload-desc-here": "Бу файл $1 проектыннан һәм башка проектларда кулланырга мөмкин. \nФайл турында [$2 тулырак мәгълүмат] түбәндәрәк күрсәтелгән.",
        "filepage-nofile": "Мондый исемле файл юк.",
        "filepage-nofile-link": "Мондый исемле файл  юк. Сез аны [$1 йөкли аласыз].",
        "uploadnewversion-linktext": "Бу файлның яңа юрамасын йөкләү",
        "sp-contributions-search": "Кертемне эзләү",
        "sp-contributions-username": "Кулланучының IP адресы яки исеме:",
        "sp-contributions-toponly": "Соңгы версия булган үзгәртүләрне генә күрсәтелсен",
+       "sp-contributions-newonly": "Битләр ясау үзгәртмәләрен генә күрсәтү",
        "sp-contributions-submit": "Эзләү",
        "whatlinkshere": "Бирегә нәрсә сылтый",
        "whatlinkshere-title": "$1 битенә сылтый торган битләр",
index 1892cb6..c446107 100644 (file)
        "protect-othertime": "دیگر وقت:",
        "protect-othertime-op": "دیگر وقت",
        "protect-otherreason-op": "دیگر وجہ",
-       "protect-expiry-options": "1 گھنٹہ:1 گھنٹہ،1 دن:1 دن،1 ہفتہ:1 ہفتہ،2 ہفتے:2 ہفتے،1 مہینا:1 مہینا،3 مہینے:3 مہینے،6 مہینے:6 مہینے،1 سال:1 سال،لامحدود:لامحدود",
+       "protect-expiry-options": "1 hour:1 hour,1 day:1 day,1 week:1 week,2 weeks:2 weeks,1 month:1 month,3 months:3 months,6 months:6 months,1 year:1 year,infinite:infinite",
        "restriction-type": "اجازت:",
        "pagesize": "(بائیٹ)",
        "restriction-edit": "تحریر و ترمیم",
index 28eb8cc..151a632 100644 (file)
        "unprotectthispage": "更改此页个保护",
        "newpage": "新页",
        "talkpage": "探討箇頁",
-       "talkpagelinktext": "讨论",
+       "talkpagelinktext": "讲张",
        "specialpage": "特別頁",
        "personaltools": "私人家伙",
        "articlepage": "望内容页",
        "pool-timeout": "等锁过时",
        "pool-queuefull": "池队列满哉",
        "pool-errorunknown": "弗识个错误",
-       "pool-servererror": "池计数器服务现在弗好用($1)",
+       "pool-servererror": "池计数器服务弗能用($1)。",
        "poolcounter-usage-error": "用法出错:$1",
        "aboutsite": "有关{{SITENAME}}",
        "aboutpage": "Project:关于",
        "laggedslavemode": "警告: 页面可能弗包含最近个更新。",
        "readonly": "數據庫鎖牢",
        "enterlockreason": "请输入锁定个原因,包括预计解锁个辰光",
-       "readonlytext": "数据库目前禁止输入新内容及更改,\n箇蛮有可能是因为数据库拉许维修,完成仔即可恢复。\n\n管理员有如下解释:$1",
+       "readonlytext": "数据库目前锁牢勒上,禁止输入新内容及更改,箇蛮有可能是因为数据库拉许维修,完成仔即可恢复。\n\n系统管理员有如下解释:$1",
        "missing-article": "数据库寻弗着想寻个页面文本:名字“$1”$2。\n\n箇一般是由于点击了链向旧有差异或历史个链接,而原有修订已拨删除导致个。\n\n如果弗是箇种情况,你侬作兴寻着软件里一个错误。畀URL地址记落来,搭[[Special:ListUsers/sysop|管理员]]报告。",
        "missingarticle-rev": "(版本#:$1)",
        "missingarticle-diff": "(两样:$1、$2)",
        "title-invalid-empty": "请求个页面标题是空个,或着只包括名字空间个名称。",
        "title-invalid-utf8": "请求个页面标题包括一只无效个UTF-8序列。",
        "title-invalid-interwiki": "请求个页面标题包含跨wiki个链接,伊弗好用于标题。",
-       "title-invalid-talk-namespace": "请求个页面标题引用子一只弗好存在个讨论页面。",
+       "title-invalid-talk-namespace": "请求个页面标题引用著一只弗能存在个讨论页。",
        "title-invalid-characters": "请求个页面标题包括无效字符:“$1”。",
        "title-invalid-relative": "标题有相对个路径。相关个页面标题(./, ../)呒没效果,因为用户浏览器经常呒没办法到达这些页面。",
        "title-invalid-magic-tilde": "请求个页面标题包含呒没效果个连续波浪线(<nowiki>~~~</nowiki>)。",
        "mypreferencesprotected": "你个私人偏好你呒处编。",
        "ns-specialprotected": "特殊页编辑是弗来三个。",
        "titleprotected": "箇只标题已经拨[[User:$1|$1]]保护以防止创建。理由是''$2''。",
-       "filereadonlyerror": "\"$1\"文件呒处改,文件存勒 \"$2\" 是只读模式。管理员考虑畀渠锁牢个理由是:\"$3\"。",
+       "filereadonlyerror": "“$1”文件呒处改,因为文件库“$2”是只读模式。\n\n锁牢数据库个系统管理员解释如下:“$3”。",
        "invalidtitle-knownnamespace": "非法个题目头,有名字空间$2搭文字$3",
        "invalidtitle-unknownnamespace": "非法个题目头,有弗识个数字$1搭文字$2",
        "exception-nologin": "朆登录",
        "userlogin-yourname": "用户名",
        "userlogin-yourname-ph": "打进侬个用户名",
        "createacct-another-username-ph": "打进用户名",
-       "yourpassword": "密码:",
+       "yourpassword": "密码",
        "userlogin-yourpassword": "密码",
        "userlogin-yourpassword-ph": "密码打进去",
        "createacct-yourpassword-ph": "打进密码",
        "createacct-reason-ph": "为何物建别样账号",
        "createacct-submit": "建立侬个账号",
        "createacct-another-submit": "建立账号",
-       "createacct-benefit-heading": "{{SITENAME}}是靠著搭侬一样个人建立出来个。",
+       "createacct-benefit-heading": "{{SITENAME}}靠像侬一样个人建立。",
        "createacct-benefit-body1": "{{PLURAL:$1|编写}}",
        "createacct-benefit-body2": "{{PLURAL:$1|页}}",
        "createacct-benefit-body3": "此垡{{PLURAL:$1|出力个人}}",
        "pt-login-button": "登录",
        "pt-createaccount": "建账号",
        "pt-userlogout": "登出",
-       "user-mail-no-addy": "尝试发送邮件而弗附带电子邮件个地址。",
-       "user-mail-no-body": "试图发送空个或者主体短的一点也弗合理个电子邮件",
+       "user-mail-no-addy": "尝试发送电子邮件而弗带地址。",
+       "user-mail-no-body": "尝试发送空个或者短得弗合理个电子邮件",
        "changepassword": "改密码",
        "resetpass_announce": "要完成登录,侬必须设定一只新密码。",
        "resetpass_header": "更改密码",
        "oldpassword": "旧密码:",
-       "newpassword": "新密码:",
-       "retypenew": "再打一遍新密码:",
+       "newpassword": "新密码",
+       "retypenew": "再打一遍新密码",
        "resetpass_submit": "设置密码再登录",
        "changepassword-success": "密碼改好哉!\n能界登錄當中...",
        "changepassword-throttled": "侬试登录忒多次哉。等$1再试试看。",
        "resetpass-submit-loggedin": "更改密码",
        "resetpass-submit-cancel": "取消",
        "resetpass-wrong-oldpass": "无效个临时或者现有密码。\n侬作兴已经成功拿密码改脱,或者已经请求一个新个临时密码。",
-       "resetpass-recycled": "请é\87\8d置侬个å¯\86ç \81æ\98¯å¿\92侬å½\93å\89\8då¯\86ç \81ä¸\8då\90\8c个密码。",
+       "resetpass-recycled": "请é\87\8dç½®ä¸\80å\8fªæ\90­ä¾¬å½\93å\89\8då¯\86ç \81å¼\97ä¸\80æ ·个密码。",
        "resetpass-temp-password": "临时密码:",
        "resetpass-abort-generic": "密码更改已经畀扩展程序中止。",
        "resetpass-expired": "侬个密码到期哉。请设置新个登录密码。",
        "userpage-userdoesnotexist": "用户账户“<nowiki>$1</nowiki>”弗曾创建。请垃拉创建/编辑迭个页面前头先检查一记。",
        "userpage-userdoesnotexist-view": "用户账户“$1”弗曾创建。",
        "blocked-notice-logextract": "箇位用户箇歇畀封锁垃许。\n下头有最近个封锁纪录以供参考:",
-       "clearyourcache": "'''注意:垃拉保存之后,侬必须清除浏览器个缓存再好看见所作出个改变。'''\n'''Mozilla / Firefox / Safari''':揿牢''Shift''再点击''刷新'',或揿''Ctrl-F5''或''Ctrl-R''(垃拉Mac上揿 ''Command-R'');\n'''Konqueror''':只需点击''刷新''或揿''F5'';\n'''Opera''':垃拉 ''工具→首选项''里向完整清除渠拉个缓存,或揿''Alt-F5'';\n'''Internet Explorer''':揿牢''Ctrl''再点击''刷新'',或揿''Ctrl-F5''。",
+       "clearyourcache": "<strong>注意:</strong>垃拉保存之后,侬作兴要清除浏览器个缓存再好看见改变。\n* <strong>Firefox或Safari:</strong>揿牢“Shift”个同时点击“刷新”,或揿“Ctrl-F5”或“Ctrl-R”(Mac上是“⌘-R”)\n* <strong>Google Chrome:</strong>揿“Ctrl-Shift-R”(Mac上是“⌘-Shift-R”)\n* <strong>Internet Explorer:</strong>揿牢“Ctrl”个同时点击“刷新”,或揿“Ctrl-F5”\n* <strong>Opera:</strong>垃拉“工具→首选项”里向清除缓存",
        "usercssyoucanpreview": "'''提示:''' 垃拉保存之前请用“{{int:showpreview}}”揿钮来测试新 CSS 。",
        "userjsyoucanpreview": "'''提示:''' 垃拉保存之前请用“{{int:showpreview}}”揿钮来测试新 JavaScript 。",
        "usercsspreview": "'''注意侬只是垃许预览侬个 CSS。'''\n'''还弗曾保存!'''",
        "yourdiff": "两样",
        "copyrightwarning": "请注意侬对{{SITENAME}}个所有贡献侪必须垃拉$2下头发布(请查看垃拉$1个细节)。假使侬弗希望侬个文字畀任意修改搭再发布,请弗要提交。<br />\n侬同时也要向阿拉保证侬所提交个内容是侬自家所作,或得自一个弗受版权保护或相似自由个来源。<strong>弗要垃拉弗曾获得授权个情况下头发表!</strong>",
        "copyrightwarning2": "请注意侬对{{SITENAME}}个所有贡献\n侪可能畀别个贡献者编辑,修改或删除。\n假使侬弗希望侬个文字畀任意修改搭仔再发布,请弗要提交。<br />\n侬同时也要向我伲保证侬提交个内容是侬自家所作,或得自一个弗受版权保护或相似自由个来源(参阅$1个细节)。\n''' 弗要垃拉弗曾获得授权个情况下头发表!'''",
-       "longpageerror": "'''错误:侬提交个文本长度有$1KB,大于$2KB个顶大值。'''该文本弗能保存。",
-       "readonlywarning": "<strong>警告:数据库锁定垃许维护,侬箇歇弗好保存侬个修改。</strong>侬作兴希望先拿侬个文字复制并保存到文本文件,等歇再修改。\n\n锁牢数据库个管理员有如下解释:$1",
+       "longpageerror": "<strong>错误:侬提交个文本长度有$1KB,大于$2KB个顶大值。</strong>该文本弗能保存。",
+       "readonlywarning": "<strong>è­¦å\91\8aï¼\9aæ\95°æ\8d®åº\93é\94\81å®\9aå\9e\83许维æ\8a¤ï¼\8c侬ç®\87æ­\87å¼\97好ä¿\9då­\98侬个修æ\94¹ã\80\82</strong>侬ä½\9cå\85´å¸\8cæ\9c\9bå\85\88æ\8b¿ä¾¬ä¸ªæ\96\87å­\97å¤\8då\88¶å¹¶ä¿\9då­\98å\88°æ\96\87æ\9c¬æ\96\87件ï¼\8cç­\89æ­\87å\86\8dä¿®æ\94¹ã\80\82\n\né\94\81ç\89¢æ\95°æ\8d®åº\93个系ç»\9f管ç\90\86å\91\98æ\9c\89å¦\82ä¸\8b解é\87\8aï¼\9a$1",
        "protectedpagewarning": "<strong>警告:此页已经畀保护,只有拥有管理员权限个用户再好修改。</strong>最近个日志垃拉下底提供以便参考:",
        "semiprotectedpagewarning": "'''注意:''' 本页面畀锁定,仅限注册用户编辑。\n最近个日志垃拉下底提供以便参考:",
        "cascadeprotectedwarning": "<strong>警告:</strong>本页已经畀保护,只有拥有管理员权限个用户再好修改,因为本页已畀下底眼级联保护个{{PLURAL:$1|一只|多只}}页面所嵌入:",
        "next": "后头",
        "last": "上个",
        "page_first": "最前",
-       "page_last": "末",
+       "page_last": "末",
        "histlegend": "选择比较版本:标记要比较个两只版本,回车或者揿页面底里个揿钮。<br /> 图例:(当前) = 搭当前版本有啥两样, (上个) = 搭上个版本有啥两样,小 = 小改动。",
        "history-fieldset-title": "浏览页史",
        "history-show-deleted": "只准删脱个",
        "stub-threshold": "短链接格式阈值($1):",
        "stub-threshold-disabled": "停用",
        "recentchangesdays": "“近段辰光个改动”当中显示几日天:",
-       "recentchangesdays-max": "最长 $1 日",
+       "recentchangesdays-max": "顶多$1天",
        "recentchangescount": "默认显示个编辑数:",
        "prefs-help-recentchangescount": "迭个包括近段辰光个改动、页面历史搭著日志。",
        "savedprefs": "倷个偏好已经保存哉。",
        "timezonelegend": "时区:",
        "localtime": "当地辰光:",
        "timezoneuseserverdefault": "使用wiki默认值($1)",
-       "timezoneuseoffset": "其(指定时差)",
+       "timezoneuseoffset": "其(指定时差)",
        "servertime": "服务器辰光:",
        "guesstimezone": "从浏览器填写",
        "timezoneregion-africa": "非洲",
        "prefs-custom-js": "自定义JavaScript",
        "prefs-common-css-js": "所有皮肤一道用个CSS/JavaScript:",
        "prefs-emailconfirm-label": "电子邮件确认:",
-       "youremail": "电子信箱:",
+       "youremail": "电子信箱",
        "username": "{{GENDER:$1|用户名}}:",
        "prefs-registration": "注册辰光:",
-       "yourrealname": "真名字:",
-       "yourlanguage": "语言:",
-       "yournick": "绰号:",
+       "yourrealname": "真名字",
+       "yourlanguage": "界面语言:",
+       "yournick": "新签名:",
        "badsig": "无效原始签名;检查 HTML 标签。",
        "yourgender": "侬希望畀哪亨称呼?",
        "gender-unknown": "提到侬个辰光,软件会尽量用性别中立个词汇",
        "unwatch": "弗关注",
        "unwatchthispage": "停止监控",
        "notanarticle": "弗是內容頁",
-       "watchlist-details": "弗包括讨论页,有 $1 页徕你侬关注表里向。",
+       "watchlist-details": "有$1页垃拉侬关注表高头,弗包括讨论页。",
        "wlheader-showupdated": "勒侬上趟查看之后畀修改个页面<strong>加粗</strong>显示。",
        "wlnote": "下底是{{PLURAL:$2|过去<strong>$2</strong>个钟头}}个{{PLURAL:$1|最后<strong>$1</strong>届更改}},截至$3 $4。",
        "wlshowlast": "显示上$1个钟头$2日天",
        "changed": "改变哉",
        "deletepage": "删脱页面",
        "confirm": "确认",
+       "excontentauthor": "内容是:“$1”,唯一贡献者是“[[Special:Contributions/$2|$2]]”([[User talk:$2|讲张]])",
        "historywarning": "<strong>警告:</strong>侬要删脱个页面有$1次{{PLURAL:$1|修订}}历史:",
        "confirmdeletetext": "侬即将删除一只页面或图像以及其历史。\n请确定侬要进行此项操作,并且了解其后果,同时侬个行为符合[[{{MediaWiki:Policy-url}}|the policy]]。",
        "actioncomplete": "操作完成哉",
        "year": "从箇年往前:",
        "sp-contributions-newbies": "只显示新用户个贡献",
        "sp-contributions-blocklog": "查封记录",
-       "sp-contributions-talk": "è¨\8eè«\96",
+       "sp-contributions-talk": "讲张",
        "sp-contributions-search": "搜寻贡献记录",
        "sp-contributions-username": "IP地址要勿用户名:",
        "sp-contributions-submit": "搜寻",
        "allmessagesname": "名字",
        "allmessagesdefault": "默认文本",
        "allmessagescurrent": "当前文本",
-       "allmessagestext": "该个是MediaWiki名字空间里可用个系统消息列表。\n如果想为MediaWiki个本地化贡献翻译,请访问[https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki本地化]搭[//translatewiki.net translatewiki.net]。",
+       "allmessagestext": "该个是MediaWiki名字空间里可用个系统消息列表。如果想为MediaWiki个本地化贡献翻译,请访问[https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki本地化]搭[//translatewiki.net translatewiki.net]。",
        "allmessagesnotsupportedDB": "'''{{ns:special}}:Allmessages''' 呒处显示,因为 '''$wgUseDatabaseMessages''' 关勒浪。",
        "thumbnail-more": "放大",
        "filemissing": "文件寻弗着哉",
        "logentry-delete-delete": "$1{{GENDER:$2|删除}}页面$3",
        "revdelete-restricted": "已将限制应用到管理员",
        "revdelete-unrestricted": "已移除对管理员个限制",
+       "logentry-block-block": "$1{{GENDER:$2|查封}}{{GENDER:$4|$3}},终止辰光为$5$6",
        "logentry-move-move": "$1{{GENDER:$2|捅荡}}页面$3到$4",
        "logentry-newusers-create": "用户账号$1畀{{GENDER:$2|创建}}",
+       "logentry-newusers-create2": "用户账号$3畀$1{{GENDER:$2|创建}}",
+       "logentry-newusers-autocreate": "用户账号$1畀自动{{GENDER:$2|创建}}",
        "logentry-upload-upload": "$1{{GENDER:$2|上传}}$3",
        "rightsnone": "(呒)",
        "revdelete-summary": "编辑摘要",
-       "searchsuggest-search": "搜寻"
+       "searchsuggest-search": "搜寻",
+       "pagelang-language": "闲话"
 }
index 7825ce9..3dd7ee8 100644 (file)
@@ -105,7 +105,7 @@ abstract class Maintenance {
 
        /**
         * Used by getDB() / setDB()
-        * @var DatabaseBase
+        * @var IDatabase
         */
        private $mDb = null;
 
@@ -122,6 +122,18 @@ abstract class Maintenance {
         */
        private $config;
 
+       /**
+        * Used to read the options in the order
+        * they were passed. Useful for option
+        * chaining. (Ex. dumpBackup.php)
+        *
+        * This is an array of arrays where
+        * 0 => the option and 1 => parameter value
+        *
+        * @var array
+        */
+       public $orderedOptions = array();
+
        /**
         * Default constructor. Children should call this *first* if implementing
         * their own constructors
@@ -184,15 +196,17 @@ abstract class Maintenance {
         * @param bool $required Is the param required?
         * @param bool $withArg Is an argument required with this option?
         * @param string $shortName Character to use as short name
+        * @param bool $multiOccurrence Can this option be passed multiple times?
         */
        protected function addOption( $name, $description, $required = false,
-               $withArg = false, $shortName = false
+               $withArg = false, $shortName = false, $multiOccurrence = false
        ) {
                $this->mParams[$name] = array(
                        'desc' => $description,
                        'require' => $required,
                        'withArg' => $withArg,
-                       'shortName' => $shortName
+                       'shortName' => $shortName,
+                       'multiOccurrence' => $multiOccurrence
                );
 
                if ( $shortName !== false ) {
@@ -210,7 +224,11 @@ abstract class Maintenance {
        }
 
        /**
-        * Get an option, or return the default
+        * Get an option, or return the default.
+        *
+        * If the option was added to support multiple occurrences,
+        * this will return an array.
+        *
         * @param string $name The name of the param
         * @param mixed $default Anything you want, default null
         * @return mixed
@@ -673,6 +691,7 @@ abstract class Maintenance {
 
                $options = array();
                $args = array();
+               $this->orderedOptions = array();
 
                # Parse arguments
                for ( $arg = reset( $argv ); $arg !== false; $arg = next( $argv ) ) {
@@ -687,17 +706,14 @@ abstract class Maintenance {
                        } elseif ( substr( $arg, 0, 2 ) == '--' ) {
                                # Long options
                                $option = substr( $arg, 2 );
-                               if ( array_key_exists( $option, $options ) ) {
-                                       $this->error( "\nERROR: $option parameter given twice\n" );
-                                       $this->maybeHelp( true );
-                               }
                                if ( isset( $this->mParams[$option] ) && $this->mParams[$option]['withArg'] ) {
                                        $param = next( $argv );
                                        if ( $param === false ) {
                                                $this->error( "\nERROR: $option parameter needs a value after it\n" );
                                                $this->maybeHelp( true );
                                        }
-                                       $options[$option] = $param;
+
+                                       $this->setParam( $options, $option, $param );
                                } else {
                                        $bits = explode( '=', $option, 2 );
                                        if ( count( $bits ) > 1 ) {
@@ -706,7 +722,8 @@ abstract class Maintenance {
                                        } else {
                                                $param = 1;
                                        }
-                                       $options[$option] = $param;
+
+                                       $this->setParam( $options, $option, $param );
                                }
                        } elseif ( $arg == '-' ) {
                                # Lonely "-", often used to indicate stdin or stdout.
@@ -719,19 +736,16 @@ abstract class Maintenance {
                                        if ( !isset( $this->mParams[$option] ) && isset( $this->mShortParamsMap[$option] ) ) {
                                                $option = $this->mShortParamsMap[$option];
                                        }
-                                       if ( array_key_exists( $option, $options ) ) {
-                                               $this->error( "\nERROR: $option parameter given twice\n" );
-                                               $this->maybeHelp( true );
-                                       }
+
                                        if ( isset( $this->mParams[$option]['withArg'] ) && $this->mParams[$option]['withArg'] ) {
                                                $param = next( $argv );
                                                if ( $param === false ) {
                                                        $this->error( "\nERROR: $option parameter needs a value after it\n" );
                                                        $this->maybeHelp( true );
                                                }
-                                               $options[$option] = $param;
+                                               $this->setParam( $options, $option, $param );
                                        } else {
-                                               $options[$option] = 1;
+                                               $this->setParam( $options, $option, 1 );
                                        }
                                }
                        } else {
@@ -745,6 +759,37 @@ abstract class Maintenance {
                $this->mInputLoaded = true;
        }
 
+       /**
+        * Helper function used solely by loadParamsAndArgs
+        * to prevent code duplication
+        *
+        * This sets the param in the options array based on
+        * whether or not it can be specified multiple times.
+        *
+        * @since 1.27
+        * @param array $options
+        * @param string $option
+        * @param mixed $value
+        */
+       private function setParam( &$options, $option, $value ) {
+               $this->orderedOptions[] = array( $option, $value );
+
+               if ( isset( $this->mParams[$option] ) ) {
+                       $multi = $this->mParams[$option]['multiOccurrence'];
+                       $exists = array_key_exists( $option, $options );
+                       if ( $multi && $exists ) {
+                               $options[$option][] = $value;
+                       } elseif ( $multi ) {
+                               $options[$option] = array( $value );
+                       } elseif ( !$exists ) {
+                               $options[$option] = $value;
+                       } else {
+                               $this->error( "\nERROR: $option parameter given twice\n" );
+                               $this->maybeHelp( true );
+                       }
+               }
+       }
+
        /**
         * Run some validation checks on the params, etc
         */
@@ -1026,7 +1071,7 @@ abstract class Maintenance {
        public function purgeRedundantText( $delete = true ) {
                # Data should come off the master, wrapped in a transaction
                $dbw = $this->getDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                # Get "active" text records from the revisions table
                $this->output( 'Searching for active text records in revisions table...' );
@@ -1069,7 +1114,7 @@ abstract class Maintenance {
                }
 
                # Done
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
        }
 
        /**
@@ -1085,7 +1130,10 @@ abstract class Maintenance {
         * If not set, wfGetDB() will be used.
         * This function has the same parameters as wfGetDB()
         *
-        * @return DatabaseBase
+        * @param integer $db DB index (DB_SLAVE/DB_MASTER)
+        * @param array $groups; default: empty array
+        * @param string|bool $wiki; default: current wiki
+        * @return IDatabase
         */
        protected function getDB( $db, $groups = array(), $wiki = false ) {
                if ( is_null( $this->mDb ) ) {
@@ -1098,12 +1146,54 @@ abstract class Maintenance {
        /**
         * Sets database object to be returned by getDB().
         *
-        * @param DatabaseBase $db Database object to be used
+        * @param IDatabase $db Database object to be used
         */
-       public function setDB( $db ) {
+       public function setDB( IDatabase $db ) {
                $this->mDb = $db;
        }
 
+       /**
+        * Begin a transcation on a DB
+        *
+        * This method makes it clear that begin() is called from a maintenance script,
+        * which has outermost scope. This is safe, unlike $dbw->begin() called in other places.
+        *
+        * @param IDatabase $dbw
+        * @param string $fname Caller name
+        * @since 1.27
+        */
+       protected function beginTransaction( IDatabase $dbw, $fname ) {
+               $dbw->begin( $fname );
+       }
+
+       /**
+        * Commit a transcation on a DB
+        *
+        * This method makes it clear that commit() is called from a maintenance script,
+        * which has outermost scope. This is safe, unlike $dbw->commit() called in other places.
+        *
+        * @param IDatabase $dbw
+        * @param string $fname Caller name
+        * @since 1.27
+        */
+       protected function commitTransaction( IDatabase $dbw, $fname ) {
+               $dbw->commit( $fname );
+       }
+
+       /**
+        * Rollback a transcation on a DB
+        *
+        * This method makes it clear that rollback() is called from a maintenance script,
+        * which has outermost scope. This is safe, unlike $dbw->rollback() called in other places.
+        *
+        * @param IDatabase $dbw
+        * @param string $fname Caller name
+        * @since 1.27
+        */
+       protected function rollbackTransaction( IDatabase $dbw, $fname ) {
+               $dbw->rollback( $fname );
+       }
+
        /**
         * Lock the search index
         * @param DatabaseBase &$db
index 1bd0217..5baa8d3 100644 (file)
@@ -167,7 +167,7 @@ class ImageCleanup extends TableCleanup {
                } else {
                        $this->output( "renaming $path to $finalPath\n" );
                        // @todo FIXME: Should this use File::move()?
-                       $db->begin( __METHOD__ );
+                       $this->beginTransaction( $db, __METHOD__ );
                        $db->update( 'image',
                                array( 'img_name' => $final ),
                                array( 'img_name' => $orig ),
@@ -184,16 +184,16 @@ class ImageCleanup extends TableCleanup {
                        if ( !file_exists( $dir ) ) {
                                if ( !wfMkdirParents( $dir, null, __METHOD__ ) ) {
                                        $this->output( "RENAME FAILED, COULD NOT CREATE $dir" );
-                                       $db->rollback( __METHOD__ );
+                                       $this->rollbackTransaction( $db, __METHOD__ );
 
                                        return;
                                }
                        }
                        if ( rename( $path, $finalPath ) ) {
-                               $db->commit( __METHOD__ );
+                               $this->commitTransaction( $db, __METHOD__ );
                        } else {
                                $this->error( "RENAME FAILED" );
-                               $db->rollback( __METHOD__ );
+                               $this->rollbackTransaction( $db, __METHOD__ );
                        }
                }
        }
index 4e19b79..5253ab3 100644 (file)
@@ -34,8 +34,8 @@ class CleanupPreferences extends Maintenance {
        public function execute() {
                global $wgHiddenPrefs;
 
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $dbw = $this->getDB( DB_MASTER );
+               $this->beginTransaction( $dbw, __METHOD__ );
                foreach ( $wgHiddenPrefs as $item ) {
                        $dbw->delete(
                                'user_properties',
@@ -43,7 +43,7 @@ class CleanupPreferences extends Maintenance {
                                __METHOD__
                        );
                };
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
                $this->output( "Finished!\n" );
        }
 }
index 44d5810..e6471dd 100644 (file)
@@ -121,7 +121,7 @@ class CleanupSpam extends Maintenance {
                        $this->output( "False match\n" );
                } else {
                        $dbw = wfGetDB( DB_MASTER );
-                       $dbw->begin( __METHOD__ );
+                       $this->beginTransaction( $dbw, __METHOD__ );
                        $page = WikiPage::factory( $title );
                        if ( $rev ) {
                                // Revert to this revision
@@ -151,7 +151,7 @@ class CleanupSpam extends Maintenance {
                                        wfMessage( 'spam_blanking', $domain )->inContentLanguage()->text()
                                );
                        }
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
                }
        }
 }
index bd8ca10..94ebf87 100644 (file)
@@ -47,7 +47,7 @@ class DeleteArchivedFiles extends Maintenance {
 
                # Data should come off the master, wrapped in a transaction
                $dbw = $this->getDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $this->beginTransaction( $dbw, __METHOD__ );
                $repo = RepoGroup::singleton()->getLocalRepo();
 
                # Get "active" revisions from the filearchive table
@@ -113,7 +113,7 @@ class DeleteArchivedFiles extends Maintenance {
                        $dbw->delete( 'filearchive', array( 'fa_id' => $id ), __METHOD__ );
                }
 
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
                $this->output( "Done! [$count file(s)]\n" );
        }
 }
index 847d863..0c06ec5 100644 (file)
@@ -46,7 +46,7 @@ class DeleteOldRevisions extends Maintenance {
 
                # Data should come off the master, wrapped in a transaction
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                $pageConds = array();
                $revConds = array();
@@ -92,7 +92,7 @@ class DeleteOldRevisions extends Maintenance {
 
                # This bit's done
                # Purge redundant text records
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
                if ( $delete ) {
                        $this->purgeRedundantText( true );
                }
index 7f1ffe4..776d345 100644 (file)
@@ -44,7 +44,7 @@ class DeleteOrphanedRevisions extends Maintenance {
                $report = $this->hasOption( 'report' );
 
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $this->beginTransaction( $dbw, __METHOD__ );
                list( $page, $revision ) = $dbw->tableNamesN( 'page', 'revision' );
 
                # Find all the orphaned revisions
@@ -63,7 +63,7 @@ class DeleteOrphanedRevisions extends Maintenance {
 
                # Nothing to do?
                if ( $report || $count == 0 ) {
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
                        exit( 0 );
                }
 
@@ -73,7 +73,7 @@ class DeleteOrphanedRevisions extends Maintenance {
                $this->output( "done.\n" );
 
                # Close the transaction and call the script to purge unused text records
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
                $this->purgeRedundantText( true );
        }
 
index a097622..a397663 100644 (file)
@@ -42,7 +42,7 @@ class DeleteSelfExternals extends Maintenance {
                $db = wfGetDB( DB_MASTER );
                while ( 1 ) {
                        wfWaitForSlaves();
-                       $db->commit( __METHOD__ );
+                       $this->commitTransaction( $db, __METHOD__ );
                        $q = $db->limitResult( "DELETE /* deleteSelfExternals */ FROM externallinks WHERE el_to"
                                . $db->buildLike( $wgServer . '/', $db->anyString() ), $this->mBatchSize );
                        $this->output( "Deleting a batch\n" );
index 6903365..a0c41fd 100644 (file)
@@ -58,7 +58,7 @@ class MigrateUserGroup extends Maintenance {
                        $affected = 0;
                        $this->output( "Doing users $blockStart to $blockEnd\n" );
 
-                       $dbw->begin( __METHOD__ );
+                       $this->beginTransaction( $dbw, __METHOD__ );
                        $dbw->update( 'user_groups',
                                array( 'ug_group' => $newGroup ),
                                array( 'ug_group' => $oldGroup,
@@ -77,7 +77,7 @@ class MigrateUserGroup extends Maintenance {
                                __METHOD__
                        );
                        $affected += $dbw->affectedRows();
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
 
                        // Clear cache for the affected users (bug 40340)
                        if ( $affected > 0 ) {
index 5849908..8a81fdd 100644 (file)
@@ -106,13 +106,13 @@ class MoveBatch extends Maintenance {
                        }
 
                        $this->output( $source->getPrefixedText() . ' --> ' . $dest->getPrefixedText() );
-                       $dbw->begin( __METHOD__ );
+                       $this->beginTransaction( $dbw, __METHOD__ );
                        $mp = new MovePage( $source, $dest );
                        $status = $mp->move( $wgUser, $reason, !$noredirects );
                        if ( !$status->isOK() ) {
                                $this->output( "\nFAILED: " . $status->getWikiText() );
                        }
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
                        $this->output( "\n" );
 
                        if ( $interval ) {
index 64bf1b6..04e2673 100644 (file)
@@ -55,7 +55,7 @@ class NukeNS extends Maintenance {
                $delete = $this->getOption( 'delete', false );
                $all = $this->getOption( 'all', false );
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                $tbl_pag = $dbw->tableName( 'page' );
                $tbl_rev = $dbw->tableName( 'revision' );
@@ -86,7 +86,7 @@ class NukeNS extends Maintenance {
                                // I already have the id & revs
                                if ( $delete ) {
                                        $dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
-                                       $dbw->commit( __METHOD__ );
+                                       $this->commitTransaction( $dbw, __METHOD__ );
                                        // Delete revisions as appropriate
                                        $child = $this->runChild( 'NukePage', 'nukePage.php' );
                                        $child->deleteRevisions( $revs );
@@ -97,7 +97,7 @@ class NukeNS extends Maintenance {
                                $this->output( "skip: " . $title->getPrefixedText() . "\n" );
                        }
                }
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
 
                if ( $n_deleted > 0 ) {
                        # update statistics - better to decrement existing count, or just count
index 1870273..94e3409 100644 (file)
@@ -44,7 +44,7 @@ class NukePage extends Maintenance {
                $delete = $this->getOption( 'delete', false );
 
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                $tbl_pag = $dbw->tableName( 'page' );
                $tbl_rec = $dbw->tableName( 'recentchanges' );
@@ -79,7 +79,7 @@ class NukePage extends Maintenance {
                                $this->output( "done.\n" );
                        }
 
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
 
                        # Delete revisions as appropriate
                        if ( $delete && $count ) {
@@ -99,20 +99,20 @@ class NukePage extends Maintenance {
                        }
                } else {
                        $this->output( "not found in database.\n" );
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
                }
        }
 
        public function deleteRevisions( $ids ) {
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                $tbl_rev = $dbw->tableName( 'revision' );
 
                $set = implode( ', ', $ids );
                $dbw->query( "DELETE FROM $tbl_rev WHERE rev_id IN ( $set )" );
 
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
        }
 }
 
index 96cb1ec..60329c0 100644 (file)
@@ -67,12 +67,12 @@ class PopulateLogUsertext extends LoggedUpdateMaintenance {
                        $res = $db->select( array( 'logging', 'user' ),
                                array( 'log_id', 'user_name' ), $cond, __METHOD__ );
 
-                       $db->begin( __METHOD__ );
+                       $this->beginTransaction( $db, __METHOD__ );
                        foreach ( $res as $row ) {
                                $db->update( 'logging', array( 'log_user_text' => $row->user_name ),
                                        array( 'log_id' => $row->log_id ), __METHOD__ );
                        }
-                       $db->commit( __METHOD__ );
+                       $this->commitTransaction( $db, __METHOD__ );
                        $blockStart += $this->mBatchSize;
                        $blockEnd += $this->mBatchSize;
                        wfWaitForSlaves();
index b73ac7f..a9fb394 100644 (file)
@@ -100,14 +100,14 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
                                __METHOD__
                        );
 
-                       $db->begin( __METHOD__ );
+                       $this->beginTransaction( $db, __METHOD__ );
                        # Go through and update rev_len from these rows.
                        foreach ( $res as $row ) {
                                if ( $this->upgradeRow( $row, $table, $idCol, $prefix ) ) {
                                        $count++;
                                }
                        }
-                       $db->commit( __METHOD__ );
+                       $this->commitTransaction( $db, __METHOD__ );
 
                        $blockStart += $this->mBatchSize;
                        $blockEnd += $this->mBatchSize;
index b401db0..43504b1 100644 (file)
@@ -95,13 +95,13 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
                                AND $idCol IS NOT NULL AND {$prefix}_sha1 = ''";
                        $res = $db->select( $table, '*', $cond, __METHOD__ );
 
-                       $db->begin( __METHOD__ );
+                       $this->beginTransaction( $db, __METHOD__ );
                        foreach ( $res as $row ) {
                                if ( $this->upgradeRow( $row, $table, $idCol, $prefix ) ) {
                                        $count++;
                                }
                        }
-                       $db->commit( __METHOD__ );
+                       $this->commitTransaction( $db, __METHOD__ );
 
                        $blockStart += $this->mBatchSize;
                        $blockEnd += $this->mBatchSize;
@@ -121,20 +121,20 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
                        array( 'ar_rev_id IS NULL', 'ar_sha1' => '' ), __METHOD__ );
 
                $updateSize = 0;
-               $db->begin( __METHOD__ );
+               $this->beginTransaction( $db, __METHOD__ );
                foreach ( $res as $row ) {
                        if ( $this->upgradeLegacyArchiveRow( $row ) ) {
                                ++$count;
                        }
                        if ( ++$updateSize >= 100 ) {
                                $updateSize = 0;
-                               $db->commit( __METHOD__ );
+                               $this->commitTransaction( $db, __METHOD__ );
                                $this->output( "Commited row with ar_timestamp={$row->ar_timestamp}\n" );
                                wfWaitForSlaves();
-                               $db->begin( __METHOD__ );
+                               $this->beginTransaction( $db, __METHOD__ );
                        }
                }
-               $db->commit( __METHOD__ );
+               $this->commitTransaction( $db, __METHOD__ );
 
                return $count;
        }
index 679cadb..4d1c537 100644 (file)
@@ -75,7 +75,7 @@ class ReassignEdits extends Maintenance {
         */
        private function doReassignEdits( &$from, &$to, $rc = false, $report = false ) {
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                # Count things
                $this->output( "Checking current edits..." );
@@ -139,7 +139,7 @@ class ReassignEdits extends Maintenance {
                        }
                }
 
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
 
                return (int)$total;
        }
index 924457a..4147491 100644 (file)
@@ -99,7 +99,7 @@ class RebuildFileCache extends Maintenance {
                                array( 'ORDER BY' => 'page_id ASC', 'USE INDEX' => 'PRIMARY' )
                        );
 
-                       $dbw->begin( __METHOD__ ); // for any changes
+                       $this->beginTransaction( $dbw, __METHOD__ ); // for any changes
                        foreach ( $res as $row ) {
                                $rebuilt = false;
                                $wgRequestTime = microtime( true ); # bug 22852
@@ -145,7 +145,7 @@ class RebuildFileCache extends Maintenance {
                                        $this->output( "Page {$row->page_id} not cacheable\n" );
                                }
                        }
-                       $dbw->commit( __METHOD__ ); // commit any changes (just for sanity)
+                       $this->commitTransaction( $dbw, __METHOD__ ); // commit any changes (just for sanity)
 
                        $blockStart += $this->mBatchSize;
                        $blockEnd += $this->mBatchSize;
index 16c676d..f115bf8 100644 (file)
@@ -359,7 +359,7 @@ class CompressOld extends Maintenance {
 
                                $chunk = new ConcatenatedGzipHistoryBlob();
                                $stubs = array();
-                               $dbw->begin( __METHOD__ );
+                               $this->beginTransaction( $dbw, __METHOD__ );
                                $usedChunk = false;
                                $primaryOldid = $revs[$i]->rev_text_id;
 
@@ -463,7 +463,7 @@ class CompressOld extends Maintenance {
                                }
                                # Done, next
                                $this->output( "/" );
-                               $dbw->commit( __METHOD__ );
+                               $this->commitTransaction( $dbw, __METHOD__ );
                                $i += $thisChunkSize;
                                wfWaitForSlaves();
                        }
index dd4cd54..c6692b5 100644 (file)
@@ -213,7 +213,7 @@ class FixBug20757 extends Maintenance {
 
                                if ( !$dryRun ) {
                                        // Reset the text row to point to the original copy
-                                       $dbw->begin( __METHOD__ );
+                                       $this->beginTransaction( $dbw, __METHOD__ );
                                        $dbw->update(
                                                'text',
                                                // SET
@@ -241,7 +241,7 @@ class FixBug20757 extends Maintenance {
                                                ),
                                                __METHOD__
                                        );
-                                       $dbw->commit( __METHOD__ );
+                                       $this->commitTransaction( $dbw, __METHOD__ );
                                        $this->waitForSlaves();
                                }
 
index f7907ad..7386df8 100644 (file)
@@ -58,6 +58,7 @@ class RecompressTracked {
        public $orphanBatchSize = 1000;
        public $reportingInterval = 10;
        public $numProcs = 1;
+       public $numBatches = 0;
        public $useDiff, $pageBlobClass, $orphanBlobClass;
        public $slavePipes, $slaveProcs, $prevSlaveId;
        public $copyOnly = false;
@@ -195,7 +196,7 @@ class RecompressTracked {
 
                        return false;
                }
-               $row = $dbr->selectRow( 'blob_tracking', '*', false, __METHOD__ );
+               $row = $dbr->selectRow( 'blob_tracking', '*', '', __METHOD__ );
                if ( !$row ) {
                        $this->info( "Warning: blob_tracking table contains no rows, skipping this wiki." );
 
@@ -228,7 +229,7 @@ class RecompressTracked {
 
                $this->slavePipes = $this->slaveProcs = array();
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
-                       $pipes = false;
+                       $pipes = array();
                        $spec = array(
                                array( 'pipe', 'r' ),
                                array( 'file', 'php://stdout', 'w' ),
@@ -340,10 +341,10 @@ class RecompressTracked {
                                break;
                        }
                        foreach ( $res as $row ) {
+                               $startId = $row->bt_page;
                                $this->dispatch( 'doPage', $row->bt_page );
                                $i++;
                        }
-                       $startId = $row->bt_page;
                        $this->report( 'pages', $i, $numPages );
                }
                $this->report( 'pages', $i, $numPages );
@@ -413,6 +414,7 @@ class RecompressTracked {
                        }
                        $ids = array();
                        foreach ( $res as $row ) {
+                               $startId = $row->bt_text_id;
                                $ids[] = $row->bt_text_id;
                                $i++;
                        }
@@ -431,7 +433,6 @@ class RecompressTracked {
                                call_user_func_array( array( $this, 'dispatch' ), $args );
                        }
 
-                       $startId = $row->bt_text_id;
                        $this->report( 'orphans', $i, $numOrphans );
                }
                $this->report( 'orphans', $i, $numOrphans );
@@ -513,6 +514,7 @@ class RecompressTracked {
 
                        $lastTextId = 0;
                        foreach ( $res as $row ) {
+                               $startId = $row->bt_text_id;
                                if ( $lastTextId == $row->bt_text_id ) {
                                        // Duplicate (null edit)
                                        continue;
@@ -533,7 +535,6 @@ class RecompressTracked {
                                        wfWaitForSlaves();
                                }
                        }
-                       $startId = $row->bt_text_id;
                }
 
                $this->debug( "$titleText: committing blob with " . $trx->getSize() . " items" );
@@ -611,12 +612,12 @@ class RecompressTracked {
                        }
                        $this->debug( 'Incomplete: ' . $res->numRows() . ' rows' );
                        foreach ( $res as $row ) {
+                               $startId = $row->bt_text_id;
                                $this->moveTextRow( $row->bt_text_id, $row->bt_new_url );
                                if ( $row->bt_text_id % 10 == 0 ) {
                                        wfWaitForSlaves();
                                }
                        }
-                       $startId = $row->bt_text_id;
                }
        }
 
@@ -693,8 +694,10 @@ class RecompressTracked {
  * Class to represent a recompression operation for a single CGZ blob
  */
 class CgzCopyTransaction {
+       /** @var RecompressTracked */
        public $parent;
        public $blobClass;
+       /** @var ConcatenatedGzipHistoryBlob */
        public $cgz;
        public $referrers;
 
@@ -787,7 +790,8 @@ class CgzCopyTransaction {
                                // All have been moved already
                                if ( $originalCount > 1 ) {
                                        // This is suspcious, make noise
-                                       $this->critical( "Warning: concurrent operation detected, are there two conflicting " .
+                                       $this->parent->critical(
+                                               "Warning: concurrent operation detected, are there two conflicting " .
                                                "processes running, doing the same job?" );
                                }
 
index 5cf8afa..bb75314 100644 (file)
@@ -141,7 +141,7 @@ TEXT;
                        $this->output( " processing..." );
 
                        if ( !$dryRun ) {
-                               $dbw->begin( __METHOD__ );
+                               $this->beginTransaction( $dbw, __METHOD__ );
                        }
                        foreach ( $res as $row ) {
                                $title = Title::newFromRow( $row );
@@ -193,7 +193,7 @@ TEXT;
                                }
                        }
                        if ( !$dryRun ) {
-                               $dbw->commit( __METHOD__ );
+                               $this->commitTransaction( $dbw, __METHOD__ );
                        }
 
                        $count += $res->numRows();
index 37272a0..e0c10f8 100644 (file)
@@ -72,7 +72,7 @@ class WrapOldPasswords extends Maintenance {
 
                $minUserId = 0;
                do {
-                       $dbw->begin();
+                       $this->beginTransaction( $dbw, __METHOD__ );
 
                        $res = $dbw->select( 'user',
                                array( 'user_id', 'user_name', 'user_password' ),
@@ -112,7 +112,7 @@ class WrapOldPasswords extends Maintenance {
                                $minUserId = $row->user_id;
                        }
 
-                       $dbw->commit();
+                       $this->commitTransaction( $dbw, __METHOD__ );
 
                        // Clear memcached so old passwords are wiped out
                        foreach ( $updateUsers as $user ) {
index 3b4a403..507109a 100644 (file)
@@ -72,3 +72,7 @@
 // Icon related variables
 @iconSize: 1.5em;
 @iconGutterWidth: 1em;
+
+// Form input sizes
+@checkboxSize: 2em;
+@radioSize: 2em;
index f0fb7b9..5bb69b8 100644 (file)
@@ -11,7 +11,8 @@
                color: lighten( @mainColor, @colorLightenPercentage );
        }
        // Focus and active states
-       &:focus, &:active {
+       &:focus,
+       &:active {
                color: darken( @mainColor, @colorDarkenPercentage );
                outline: none; // outline fix
        }
@@ -74,7 +75,8 @@ Styleguide 6.2.1.
        &:hover {
                color: @mainColor;
        }
-       &:focus, &:active {
+       &:focus,
+       &:active {
                color: darken( @mainColor, @colorDarkenPercentage );
        }
 }
index 600b771..4ffaeee 100644 (file)
        display: inline-block;
        padding: .5em 1em;
        margin: 0;
-       .box-sizing(border-box);
+       .box-sizing( border-box );
 
        // Disable weird iOS styling
        -webkit-appearance: none;
 
-       // IE6/IE7 hack
-       // http://stackoverflow.com/a/5838575/365238
+       // IE 6 & 7 hack
+       // https://stackoverflow.com/a/5838575/365238
        *display: inline;
        zoom: 1;
 
        // Container styling
-       .button-colors(#FFF, #CCC, #777);
+       .button-colors( #fff, #ccc, #777 );
        border-radius: @borderRadius;
        min-width: 4em;
 
        // Styleguide 2.1.1.
        &.mw-ui-progressive,
        &.mw-ui-primary {
-               .button-colors(@colorProgressive, @colorProgressiveHighlight, @colorProgressiveActive);
+               .button-colors( @colorProgressive, @colorProgressiveHighlight, @colorProgressiveActive );
 
                &.mw-ui-quiet {
-                       .button-colors-quiet(@colorProgressive, @colorProgressiveHighlight, @colorProgressiveActive);
+                       .button-colors-quiet( @colorProgressive, @colorProgressiveHighlight, @colorProgressiveActive );
                }
        }
 
        //
        // Styleguide 2.1.2.
        &.mw-ui-constructive {
-               .button-colors(@colorConstructive, @colorConstructiveHighlight, @colorConstructiveActive);
+               .button-colors( @colorConstructive, @colorConstructiveHighlight, @colorConstructiveActive );
 
                &.mw-ui-quiet {
-                       .button-colors-quiet(@colorConstructive, @colorConstructiveHighlight, @colorConstructiveActive);
+                       .button-colors-quiet( @colorConstructive, @colorConstructiveHighlight, @colorConstructiveActive );
                }
        }
 
        //
        // Styleguide 2.1.3.
        &.mw-ui-destructive {
-               .button-colors(@colorDestructive, @colorDestructiveHighlight, @colorDestructiveActive);
+               .button-colors( @colorDestructive, @colorDestructiveHighlight, @colorDestructiveActive );
 
                &.mw-ui-quiet {
-                       .button-colors-quiet(@colorDestructive, @colorDestructiveHighlight, @colorDestructiveActive);
+                       .button-colors-quiet( @colorDestructive, @colorDestructiveHighlight, @colorDestructiveActive );
                }
        }
 
        // Styleguide 2.1.4.
        &.mw-ui-quiet {
                background: transparent;
-               border: none;
+               border: 0;
                text-shadow: none;
-               .button-colors-quiet(@colorButtonText, @colorButtonTextHighlight, @colorButtonTextActive);
+               .button-colors-quiet( @colorButtonText, @colorButtonTextHighlight, @colorButtonTextActive );
 
                &:hover,
                &:focus {
@@ -269,8 +269,8 @@ a.mw-ui-button {
                border-bottom-left-radius: @borderRadius;
        }
 
-       &:not(:first-child) {
-               border-left: none;
+       &:not( :first-child ) {
+               border-left: 0;
        }
 
        &:last-child{
index bd5dd4a..d44e5d7 100644 (file)
@@ -5,8 +5,8 @@
 //
 // Styling checkboxes in a way that works cross browser is a tricky problem to solve.
 // In MediaWiki UI put a checkbox and label inside a mw-ui-checkbox div.
-// This renders in all browsers except IE6-8 which do not support the :checked selector;
-// these are kept backwards-compatible using the :not(#noop) selector.
+// This renders in all browsers except IE 6-8 which do not support the :checked selector;
+// these are kept backwards-compatible using the `:not( #noop )` selector.
 // You should give the checkbox and label matching "id" and "for" attributes, respectively.
 //
 // Markup:
        vertical-align: middle;
 }
 
-@checkboxSize: 2em;
-
 // We use the not selector to cancel out styling on IE 8 and below
-// We also disable this styling on javascript disabled devices. This fixes the issue with
+// We also disable this styling on JavaScript disabled devices. This fixes the issue with
 // Opera Mini where checking/unchecking doesn't apply styling but potentially leaves other
 // more capable browsers with unstyled checkboxes.
-.client-js .mw-ui-checkbox:not(#noop) {
+.client-js .mw-ui-checkbox:not( #noop ) {
        // Position relatively so we can make use of absolute pseudo elements
        position: relative;
        display: table;
@@ -62,8 +60,7 @@
                height: @checkboxSize;
                // This is needed for Firefox mobile (See bug 71750 to workaround default Firefox stylesheet)
                max-width: none;
-               margin: 0;
-               margin-right: 0.4em;
+               margin: 0 0.4em 0 0;
                display: table-cell;
 
                & + label {
                // the pseudo before element of the label after the checkbox now looks like a checkbox
                & + label::before {
                        content: '';
-                       cursor: pointer;
-                       .box-sizing(border-box);
+                       background-color: #fff;
+                       .background-image-svg( 'images/checked.svg', 'images/checked.png' );
+                       background-position: center center;
+                       background-origin: border-box;
+                       background-repeat: no-repeat;
+                       .background-size( @checkboxSize - 0.2em, @checkboxSize - 0.2em );
+                       background-size: 0 0;
+                       .box-sizing( border-box );
                        position: absolute;
+                       // align the checkbox to middle of the text
+                       top: 50%;
                        left: 0;
-                       border-radius: @borderRadius;
                        width: @checkboxSize;
                        height: @checkboxSize;
-                       line-height: @checkboxSize;
-                       background-color: #fff;
-                       border: 1px solid @colorGray7;
-                       // align the checkbox to middle of the text
-                       top: 50%;
                        margin-top: -1em;
-                       .background-image-svg('images/checked.svg', 'images/checked.png');
-                       .background-size( @checkboxSize - 0.2em, @checkboxSize - 0.2em );
-                       background-repeat: no-repeat;
-                       background-position: center center;
-                       background-origin: border-box;
-                       background-size: 0 0;
+                       border: 1px solid @colorGray7;
+                       border-radius: @borderRadius;
+                       line-height: @checkboxSize;
+                       cursor: pointer;
                }
 
                // when the input is checked, style the label pseudo before element that followed as a checked checkbox
 
                // disabled and checked checkboxes have a white circle
                &:disabled:checked + label::before {
-                       .background-image-svg('images/checked_disabled.svg', 'images/checked_disabled.png');
+                       .background-image-svg( 'images/checked_disabled.svg', 'images/checked_disabled.png' );
                }
        }
 }
index 6a5fa96..cc96a5c 100644 (file)
@@ -36,7 +36,7 @@
 //
 // Styleguide 5.1.
 .mw-ui-vform {
-       .box-sizing(border-box);
+       .box-sizing( border-box );
 
        width: @defaultFormWidth;
 
@@ -44,7 +44,7 @@
        select,
        .mw-ui-button {
                display: block;
-               .box-sizing(border-box);
+               .box-sizing( border-box );
                margin: 0;
                width: 100%;
        }
        // Give dropdown lists the same spacing as input fields for consistency.
        // Values taken from .agora-field-styling() in mixins/form.less
        select {
-               padding: 0.35em 0.5em 0.35em 0.5em;
+               padding: 0.35em 0.5em;
                vertical-align: middle;
        }
 
        > label {
                display: block;
-               .box-sizing(border-box);
+               .box-sizing( border-box );
                .agora-label-styling();
                width: auto;
                margin: 0 0 0.2em;
@@ -68,7 +68,7 @@
        // Override input styling just for checkboxes and radio inputs.
        input[type="radio"] {
                display: inline;
-               .box-sizing(content-box);
+               .box-sizing( content-box );
                width: auto;
        }
 
        .errorbox,
        .warningbox,
        .successbox {
-               .box-sizing(border-box);
+               .box-sizing( border-box );
                font-size: 0.9em;
                margin: 0 0 1em 0;
                padding: 0.5em;
 
        // Colours taken from those for .errorbox in shared.css
        .error {
-               color: #cc0000;
+               color: @colorErrorText;
                border: 1px solid #fac5c5;
                background-color: #fae3e3;
                text-shadow: 0 1px #fae3e3;
index c90a6b9..9b9d324 100644 (file)
@@ -2,10 +2,10 @@
 @import "mediawiki.ui/variables";
 
 // Mixins
-.mixin-mw-ui-icon-bgimage(@iconSvg, @iconPng) {
+.mixin-mw-ui-icon-bgimage( @iconSvg, @iconPng ) {
        &.mw-ui-icon {
                &:before {
-                       .background-image-svg(@iconSvg, @iconPng);
+                       .background-image-svg( @iconSvg, @iconPng );
                }
        }
 }
@@ -13,7 +13,7 @@
 // Icons
 //
 // To use icons you must be using a browser that supports pseudo elements.
-// This includes support for IE8.
+// This includes support for IE 8.
 // http://caniuse.com/#feat=css-gencontent
 //
 // For elements that are intended to have both an icon and text, browsers that
@@ -45,6 +45,7 @@
                width: @width;
                min-width: @width;
                max-width: @width;
+
                &:before {
                        left: 0;
                        right: 0;
        &.mw-ui-icon-before:before,
        &.mw-ui-icon-element:before {
                background-position: 50% 50%;
-               float: left;
-               display: block;
                background-repeat: no-repeat;
                background-size: 100% auto;
+               float: left;
+               display: block;
                min-height: @iconSize;
                content: '';
        }
index 62f0e83..d0633ae 100644 (file)
 .mw-ui-input {
        // turn off default input styling for input[type="search"] fields
        -webkit-appearance: none;
-       border: 1px solid @colorFieldBorder;
-       .box-sizing(border-box);
-       width: 100%;
-       padding: .3em .3em .3em .6em;
+       .box-sizing( border-box );
        display: block;
-       vertical-align: middle;
+       width: 100%;
+       border: 1px solid @colorFieldBorder;
        border-radius: @borderRadius;
+       padding: 0.3em 0.3em 0.3em 0.6em;
        font-family: inherit;
        font-size: inherit;
        line-height: inherit;
+       vertical-align: middle;
 
        // Placeholder text styling must be set individually for each browser @winter
        &::-webkit-input-placeholder { // webkit
index 52effd6..448390a 100644 (file)
@@ -5,8 +5,8 @@
 //
 // Styling radios in a way that works cross browser is a tricky problem to solve.
 // In MediaWiki UI put a radio and label inside a mw-ui-radio div.
-// This renders in all browsers except IE6-8 which do not support the :checked selector;
-// these are kept backwards-compatible using the :not(#noop) selector.
+// This renders in all browsers except IE 6-8 which do not support the :checked selector;
+// these are kept backwards-compatible using the `:not( #noop )` selector.
 // You should give the radio and label matching "id" and "for" attributes, respectively.
 //
 // Markup:
        vertical-align: middle;
 }
 
-@radioSize: 2em;
-
 // We use the not selector to cancel out styling on IE 8 and below.
-// We also disable this styling on javascript disabled devices. This fixes the issue with
+// We also disable this styling on JavaScript disabled devices. This fixes the issue with
 // Opera Mini where checking/unchecking doesn't apply styling but potentially leaves other
 // more capable browsers with unstyled radio buttons.
-.client-js .mw-ui-radio:not(#noop) {
+.client-js .mw-ui-radio:not( #noop ) {
        // Position relatively so we can make use of absolute pseudo elements
        position: relative;
        line-height: @radioSize;
                // the pseudo before element of the label after the radio now looks like a radio
                & + label::before {
                        content: '';
-                       cursor: pointer;
-                       .box-sizing(border-box);
+                       background-color: #fff;
+                       .background-image-svg( 'images/radio_checked.svg', 'images/radio_checked.png' );
+                       background-origin: border-box;
+                       background-position: center center;
+                       background-repeat: no-repeat;
+                       .background-size( @radioSize, @radioSize );
+                       background-size: 0 0;
+                       .box-sizing( border-box );
                        position: absolute;
                        left: 0;
-                       border-radius: 100%;
                        width: @radioSize;
                        height: @radioSize;
-                       background-color: #fff;
                        border: 1px solid @colorGray7;
-                       .background-image-svg('images/radio_checked.svg', 'images/radio_checked.png');
-                       .background-size( @radioSize, @radioSize );
-                       background-repeat: no-repeat;
-                       background-position: center center;
-                       background-origin: border-box;
-                       background-size: 0 0;
+                       border-radius: 100%;
+                       cursor: pointer;
                }
 
                // when the input is checked, style the label pseudo before element that followed as a checked radio
 
                // disabled radios have a gray background
                &:disabled + label::before {
-                       cursor: default;
                        background-color: @colorGray14;
                        border-color: @colorGray14;
+                       cursor: default;
                }
 
                // disabled and checked radios have a white circle
                &:disabled:checked + label::before {
-                       .background-image-svg('images/radio_disabled.svg', 'images/radio_disabled.png');
+                       .background-image-svg( 'images/radio_disabled.svg', 'images/radio_disabled.png' );
                }
        }
 }
index 500d42c..cc27e9e 100644 (file)
@@ -26,7 +26,7 @@ Styleguide 6.1.
 */
 
 .mw-ui-text {
-       // The selector order is like this on purpose; IE6 ignores the second selector,
+       // The selector order is like this on purpose; IE 6 ignores the second selector,
        // so we don't want to accidentally apply this color on all mw-ui-CONTEXT classes
        .mw-ui-progressive& {
                color: @colorProgressive;
index 0ed8270..d0a3d08 100644 (file)
@@ -14,6 +14,7 @@
 # Plus any combination of these:
 #
 # cat           add category links
+#               (ignored by Parsoid, since it emits <link>s)
 # ill           add inter-language links
 #               (ignored by Parsoid, since it emits <link>s)
 # subpage       enable subpages (disabled by default)
@@ -1335,6 +1336,8 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tåg" data-mw='{"name":"tåg","attrs":{},"body":{"extsrc":"tåg"}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
 !! test
@@ -3329,14 +3332,18 @@ parsoid=wt2html,wt2wt
 <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":" [[Category:foo]]"}},"i":0}}]}'> </span><link rel="mw:PageProp/Category" href="./Category:Foo" about="#mwt1"> <!-- No pre&#x2D;wrapping -->
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
 !! test
 7b. Indent-pre and category links
 !! options
-parsoid=wt2html,wt2wt
+parsoid=wt2html
 !! wikitext
  [[Category:foo]] a
  [[Category:foo]] {{echo|b}}
-!! html
+!! html/parsoid
 <pre><link rel="mw:PageProp/Category" href="./Category:Foo"> a
  <link rel="mw:PageProp/Category" href="./Category:Foo"> <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"b"}},"i":0}}]}'>b</span></pre>
 !! end
@@ -7119,6 +7126,17 @@ Piped link with multiple pipe characters in link text
 <p><a rel="mw:WikiLink" href="Main_Page" title="Main Page">|The|Main|Page|</a></p>
 !! end
 
+!! test
+Piped link with no link text
+!! wikitext
+[[Thomas Bek (bishop of St David's)|]]
+!! html/php
+<p>[[Thomas Bek (bishop of St David's)|]]
+</p>
+!! html/parsoid
+<p>[[Thomas Bek (bishop of St David's)|]]</p>
+!! end
+
 !! test
 Broken link
 !! wikitext
@@ -10860,8 +10878,14 @@ Un-closed <includeonly>
 !! html
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize the include directives to serialize on their own line.
+## Selser will take care of preserving formatting in scenarios where they
+## intermingled with other wikitext.
 !! test
 Includes and comments at SOL
+!! options
+parsoid=wt2html,html2html
 !! wikitext
 <!-- comment --><noinclude><!-- comment --></noinclude><!-- comment -->== hu ==
 
@@ -11042,10 +11066,14 @@ parsoid=wt2html,wt2wt
 </tbody></table>
 !!end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize the include directives to serialize on their own line.
+## Selser will take care of preserving formatting in scenarios where they
+## intermingled with other wikitext.
 !!test
 2. Table tag in SOL posn. should get reparsed correctly with valid TSR
 !!options
-parsoid=wt2html,wt2wt
+parsoid=wt2html
 !!wikitext
 <includeonly>a</includeonly>{| {{{b}}}
 |c
@@ -14245,7 +14273,7 @@ cat
 pst
 !! wikitext
 [[Category:MediaWiki User's Guide|]]
-!! html
+!! html/php
 [[Category:MediaWiki User's Guide|MediaWiki User's Guide]]
 !! end
 
@@ -14256,19 +14284,26 @@ cat
 pst
 !! wikitext
 [[Category:Foo (bar)|]]
-!! html
+!! html/php
 [[Category:Foo (bar)|Foo]]
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
 !! test
 Category with link tail
 !! options
 cat
 pst
+parsoid=wt2html
 !! wikitext
 123[[Category:Foo]]456
-!! html
+!! html/php
 123[[Category:Foo]]456
+!! html/parsoid
+<p>123<link rel="mw:PageProp/Category" href="Category:Foo"/>456</p>
 !! end
 
 !! test
@@ -14278,7 +14313,7 @@ cat
 pst
 !! wikitext
 [[Category:{{echo|Foo}}]]
-!! html
+!! html/php
 [[Category:{{echo|Foo}}]]
 !! end
 
@@ -14289,7 +14324,7 @@ cat
 pst
 !! wikitext
 [[Category:Foo|{{echo|Bar}}]]
-!! html
+!! html/php
 [[Category:Foo|{{echo|Bar}}]]
 !! end
 
@@ -14300,12 +14335,18 @@ cat
 pst
 !! wikitext
 [[Category:{{echo|Foo}}|{{echo|Bar}}]]
-!! html
+!! html/php
 [[Category:{{echo|Foo}}|{{echo|Bar}}]]
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
 !! test
 Category / paragraph interactions
+!! options
+parsoid=wt2html
 !! wikitext
 Foo [[Category:Baz]] Bar
 
@@ -14332,7 +14373,7 @@ Bar
 [[Category:Baz]]
  {{echo|[[Category:Baz]]}}
 [[Category:Baz]]
-!! html
+!! html/php
 <p>Foo Bar
 </p><p>Foo
 Bar
@@ -14342,20 +14383,32 @@ Bar
 </p><p>Foo
 Bar
 </p>
+!! html/parsoid
+<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
+<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
+<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
+<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
+<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"[[Category:Baz]]"}},"i":0}}]}'/></p>
+<link rel="mw:PageProp/Category" href="Category:Baz"/>
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
+##
 ## The whitespace on the empty line is part of the test. Please do not delete
 !! test
 1. Categories and newlines: All preceding newlines should be suppressed (courtesy bug 87)
 !! options
-parsoid=wt2html,wt2wt
+parsoid=wt2html
 !! wikitext
 This
    
 [[Category:Foo]] and this should be part of same paragraph (not an indent-pre)
    
 {{echo|[[Category:Foo]] and so should this!}}
-!! html
+!! html/php
 <p>This and this should be part of same paragraph (not an indent-pre) and so should this!
 </p>
 !! html/parsoid
@@ -14453,8 +14506,14 @@ parsoid=wt2html
 <link rel="mw:PageProp/Category" href="./Category:Foo" data-parsoid='{"stx":"simple","a":{"href":"./Category:Foo"},"sa":{"href":"Category:Foo"}}'/>
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
 !! test
 6. Categories and newlines: migrateTrailingCategories dom pass should not migrate categories not preceded by newlines
+!! options
+parsoid=wt2html
 !! wikitext
 * a [[Category:Foo]]
 !! html/parsoid
@@ -14505,13 +14564,20 @@ parsoid
 </p>
 !! end
 
-# html2wt localizes the "Category" namespace.
-# XXX the <link> element needs an empty data-parsoid attribute, or
-# else the html2html test fails because spaces are inserted.
+# We used to, but no longer wt2wt this test since the default serializer
+# will normalize all categories to serialize on their own line.
+# This wikitext usage is going to be fairly uncommon in production and
+# selser will take care of preventing whitespace insertion if this
+# occurs in an article.
+#
+# html2html disabled for the same reason (whitespace insertion between
+# x and y).
+#
+# html2wt disabled because it localizes the "Category" namespace.
 !! test
 Link prefix/suffixes aren't applied to category links
 !! options
-parsoid=wt2html,wt2wt,html2html
+parsoid=wt2html
 language=is
 !! wikitext
 x[[Category:Foo]]y
@@ -16152,10 +16218,15 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":{"extsrc":""}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
+## Don't expect parsoid to rt this form.
 !! test
 Parser hook: empty input using terminated empty elements
+!! options
+parsoid=wt2html,html2html
 !! wikitext
 <tag/>
 !! html/php
@@ -16165,6 +16236,8 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":null}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
 !! test
@@ -16178,6 +16251,8 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":null}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
 !! test
@@ -16191,11 +16266,15 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":{"extsrc":"input"}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
-
+## Don't expect parsoid to rt this form.
 !! test
 Parser hook: case insensitive
+!! options
+parsoid=wt2html,html2html
 !! wikitext
 <TAG>input</TAG>
 !! html/php
@@ -16205,11 +16284,15 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":{"extsrc":"input"}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
-
+## Don't expect parsoid to rt this form.
 !! test
 Parser hook: case insensitive, redux
+!! options
+parsoid=wt2html,html2html
 !! wikitext
 <TaG>input</TAg>
 !! html/php
@@ -16219,6 +16302,8 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":{"extsrc":"input"}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
 !! test
@@ -16234,11 +16319,35 @@ array (
 )
 </pre>&lt;/tag&gt;
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":{"extsrc":"&lt;tag>"}}' data-parsoid='{}' about="#mwt2"></pre>&lt;/tag>
 !! end
 
 !! test
 Parser hook: basic arguments
 !! wikitext
+<tag width="200" height="100" depth="50" square=""></tag>
+!! html/php
+<pre>
+''
+array (
+  'width' => '200',
+  'height' => '100',
+  'depth' => '50',
+  'square' => '',
+)
+</pre>
+
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{"width":"200","height":"100","depth":"50","square":""},"body":{"extsrc":""}}' data-parsoid='{}' about="#mwt2"></pre>
+!! end
+
+## Don't expect parsoid to rt this form.
+!! test
+Parser hook: basic arguments, variations
+!! options
+parsoid=wt2html,html2html
+!! wikitext
 <tag width=200 height = "100" depth = '50' square></tag>
 !! html/php
 <pre>
@@ -16251,12 +16360,14 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{"width":"200","height":"100","depth":"50","square":""},"body":{"extsrc":""}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
 !! test
 Parser hook: argument containing a forward slash (bug 5344)
 !! wikitext
-<tag filename='/tmp/bla'></tag>
+<tag filename="/tmp/bla"></tag>
 !! html/php
 <pre>
 ''
@@ -16265,10 +16376,15 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{"filename":"/tmp/bla"},"body":{"extsrc":""}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
+## Don't expect parsoid to rt this form.
 !! test
 Parser hook: empty input using terminated empty elements (bug 2374)
+!! options
+parsoid=wt2html,html2html
 !! wikitext
 <tag foo=bar/>text
 !! html/php
@@ -16279,6 +16395,8 @@ array (
 )
 </pre>text
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{"foo":"bar"},"body":null}' data-parsoid='{}' about="#mwt2"></pre>text
 !! end
 
 # </tag> should be output literally since there is no matching tag that begins it
@@ -16311,21 +16429,28 @@ array (
 Parser hook: static parser hook not inside a comment
 !! wikitext
 <statictag>hello, world</statictag>
-<statictag action=flush/>
+
+<statictag action="flush" />
 !! html/php
-<p>hello, world
+<p><br />
+hello, world
 </p>
+!! html/parsoid
+<p><span typeof="mw:Extension/statictag" data-mw='{"name":"statictag","attrs":{},"body":{"extsrc":"hello, world"}}' data-parsoid='{}' about="#mwt2"></span></p>
+<p typeof="mw:Extension/statictag" data-mw='{"name":"statictag","attrs":{"action":"flush"},"body":null}' data-parsoid='{}' about="#mwt4">hello, world</p>
 !! end
 
-
 !! test
 Parser hook: static parser hook inside a comment
 !! wikitext
 <!-- <statictag>hello, world</statictag> -->
-<statictag action=flush/>
+<statictag action="flush" />
 !! html/php
 <p><br />
 </p>
+!! html/parsoid
+<!-- <statictag&#x3E;hello, world</statictag&#x3E; -->
+<p typeof="mw:Extension/statictag" data-mw='{"name":"statictag","attrs":{"action":"flush"},"body":null}' data-parsoid='{}' about="#mwt2"></p>
 !! end
 
 # Nested template calls; this case was broken by Parser.php rev 1.506,
@@ -19211,17 +19336,25 @@ Category:分類
 blah
 !! endarticle
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
 !! test
 Don't convert blue categorylinks to another variant (bug 33210)
 !! options
-language=zh cat
+cat
+language=zh
+parsoid=wt2html
 !! wikitext
 [[A]][[Category:分类]]
-!! html
+!! html/php
 <a href="/wiki/Category:%E5%88%86%E7%B1%BB" title="Category:分类">分类</a>
+!! html/parsoid
+<p><a rel="mw:WikiLink" href="A" title="A">A</a></p>
+<link rel="mw:PageProp/Category" href="Category:分类"/>
 !! end
 
-
 !! test
 Stripping -{}- tags (language variants)
 !! options
@@ -19756,7 +19889,7 @@ Tildes in comments
 pst
 !! wikitext
 <!-- ~~~~ -->
-!! html
+!! html/php
 <!-- ~~~~ -->
 !! end
 
@@ -20078,7 +20211,7 @@ Edit comment with link
 comment
 !! wikitext
 I like the [[Main Page]] a lot
-!! html
+!! html/php
 I like the <a href="/wiki/Main_Page" title="Main Page">Main Page</a> a lot
 !!end
 
@@ -20088,7 +20221,7 @@ Edit comment with link and link text
 comment
 !! wikitext
 I like the [[Main Page|best pages]] a lot
-!! html
+!! html/php
 I like the <a href="/wiki/Main_Page" title="Main Page">best pages</a> a lot
 !!end
 
@@ -20098,7 +20231,7 @@ Edit comment with link and link text with suffix
 comment
 !! wikitext
 I like the [[Main Page|best page]]s a lot
-!! html
+!! html/php
 I like the <a href="/wiki/Main_Page" title="Main Page">best pages</a> a lot
 !!end
 
@@ -20108,7 +20241,7 @@ Edit comment with section link (non-local, eg in history list)
 comment title=[[Main Page]]
 !! wikitext
 /* External links */ removed bogus entries
-!! html
+!! html/php
 <a href="/wiki/Main_Page#External_links" title="Main Page">→</a>‎<span dir="auto"><span class="autocomment">External links: </span> removed bogus entries</span>
 !!end
 
@@ -20118,7 +20251,7 @@ Edit comment with section link and text before it (non-local, eg in history list
 comment title=[[Main Page]]
 !! wikitext
 pre-comment text /* External links */ removed bogus entries
-!! html
+!! html/php
 pre-comment text <a href="/wiki/Main_Page#External_links" title="Main Page">→</a>‎<span dir="auto"><span class="autocomment">External links: </span> removed bogus entries</span>
 !!end
 
@@ -20128,7 +20261,7 @@ Edit comment with section link (local, eg in diff view)
 comment local title=[[Main Page]]
 !! wikitext
 /* External links */ removed bogus entries
-!! html
+!! html/php
 <a href="#External_links">→</a>‎<span dir="auto"><span class="autocomment">External links: </span> removed bogus entries</span>
 !!end
 
@@ -20140,7 +20273,7 @@ subpage
 title=[[Subpage test]]
 !! wikitext
 Poked at a [[/subpage]] here...
-!! html
+!! html/php
 Poked at a <a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">/subpage</a> here...
 !!end
 
@@ -20152,7 +20285,7 @@ subpage
 title=[[Subpage test]]
 !! wikitext
 Poked at a [[/subpage|neat little page]] here...
-!! html
+!! html/php
 Poked at a <a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">neat little page</a> here...
 !!end
 
@@ -20163,7 +20296,7 @@ comment
 title=[[Subpage test]]
 !! wikitext
 Poked at a [[/subpage]] here...
-!! html
+!! html/php
 Poked at a <a href="/index.php?title=/subpage&amp;action=edit&amp;redlink=1" class="new" title="/subpage (page does not exist)">/subpage</a> here...
 !!end
 
@@ -20175,7 +20308,7 @@ local
 title=[[Main Page]]
 !! wikitext
 [[#section]]
-!! html
+!! html/php
 <a href="#section">#section</a>
 !! end
 
@@ -20186,24 +20319,28 @@ comment
 title=[[Main Page]]
 !! wikitext
 [[#section]]
-!! html
+!! html/php
 <a href="/wiki/Main_Page#section" title="Main Page">#section</a>
 !! end
 
 !! test
 Anchor starting with underscore
+!! options
+title=[[Foo]]
 !! wikitext
 [[#_ref|One]]
-!! html
+!! html/php
 <p><a href="#_ref">One</a>
 </p>
+!! html/parsoid
+<p><a rel="mw:WikiLink" href="./Foo#_ref" data-parsoid='{"stx":"piped","a":{"href":"./Foo#_ref"},"sa":{"href":"#_ref"}}'>One</a></p>
 !! end
 
 !! test
 Id starting with underscore
 !! wikitext
 <div id="_ref"></div>
-!! html
+!! html/*
 <div id="_ref"></div>
 
 !! end
@@ -20215,7 +20352,7 @@ comment
 title=[[Main Page]]
 !! wikitext
 /* __hello__world__ */
-!! html
+!! html/php
 <a href="/wiki/Main_Page#hello_world" title="Main Page">→</a>‎<span dir="auto"><span class="autocomment">__hello__world__</span></span>
 !! end
 
@@ -20534,11 +20671,14 @@ HTML5 data attributes
 !! wikitext
 <span data-foo="bar">Baz</span>
 <p data-abc-def_hij="">Quuz</p>
-!! html
+!! html/php
 <p><span data-foo="bar">Baz</span>
 </p>
 <p data-abc-def_hij="">Quuz</p>
 
+!! html/parsoid
+<p><span data-foo="bar" data-parsoid='{"stx":"html"}'>Baz</span></p>
+<p data-abc-def_hij="" data-parsoid='{"stx":"html"}'>Quuz</p>
 !! end
 
 !! test
@@ -21386,14 +21526,12 @@ parsoid=wt2html,wt2wt
 
 !!test
 Ref: 1. ref-location should be replaced with an index span
-!!options
-parsoid
 !! wikitext
 A <ref>foo</ref>
 B <ref name="x">foo</ref>
 C <ref name="y" />
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span>
 B <span about="#mwt4" class="mw-ref" id="cite_ref-x_2-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-x-2"},"attrs":{"name":"x"}}'><a href="#cite_note-x-2"><span class="mw-reflink-text">[2]</span></a></span>
 C <span about="#mwt6" class="mw-ref" id="cite_ref-y_3-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"y"}}'><a href="#cite_note-y-3"><span class="mw-reflink-text">[3]</span></a></span></p>
@@ -21406,13 +21544,11 @@ C <span about="#mwt6" class="mw-ref" id="cite_ref-y_3-0" rel="dc:references" typ
 
 !!test
 Ref: 2. ref-tags with identical names should all get the same index
-!!options
-parsoid
 !! wikitext
 A <ref name="x">foo</ref>
 B <ref name="x" />
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-x_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-x-1"},"attrs":{"name":"x"}}'><a href="#cite_note-x-1"><span class="mw-reflink-text">[1]</span></a></span>
 B <span about="#mwt4" class="mw-ref" id="cite_ref-x_1-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"x"}}'><a href="#cite_note-x-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt6" data-mw='{"name":"references","attrs":{}}'>
@@ -21422,14 +21558,12 @@ B <span about="#mwt4" class="mw-ref" id="cite_ref-x_1-1" rel="dc:references" typ
 
 !!test
 Ref: 3. spaces in ref-names should be ignored
-!!options
-parsoid
 !! wikitext
 A <ref name="x">foo</ref>
 B <ref name=" x " />
 C <ref name= x  />
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-x_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-x-1"},"attrs":{"name":"x"}}'><a href="#cite_note-x-1"><span class="mw-reflink-text">[1]</span></a></span>
 B <span about="#mwt4" class="mw-ref" id="cite_ref-x_1-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"x"}}'><a href="#cite_note-x-1"><span class="mw-reflink-text">[1]</span></a></span>
 C <span about="#mwt6" class="mw-ref" id="cite_ref-x_1-2" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"x"}}'><a href="#cite_note-x-1"><span class="mw-reflink-text">[1]</span></a></span></p>
@@ -21441,12 +21575,10 @@ C <span about="#mwt6" class="mw-ref" id="cite_ref-x_1-2" rel="dc:references" typ
 # NOTE: constructor is a predefined property in JS and constructor as a ref-name can clash with it if not handled properly)
 !!test
 Ref: 4. 'constructor' should be accepted as a valid ref-name
-!!options
-parsoid
 !! wikitext
 A <ref name="constructor">foo</ref>
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-constructor_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-constructor-1"},"attrs":{"name":"constructor"}}'><a href="#cite_note-constructor-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
 <li about="#cite_note-constructor-1" id="cite_note-constructor-1"><a href="#cite_ref-constructor_1-0" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-constructor-1" class="mw-reference-text">foo</span></li>
@@ -21455,15 +21587,13 @@ A <ref name="constructor">foo</ref>
 
 !!test
 Ref: 5. body should accept generic wikitext
-!!options
-parsoid
 !! wikitext
 A <ref>
  This is a '''[[bolded link]]''' and this is a {{echo|transclusion}}
 </ref>
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt5" data-mw='{"name":"references","attrs":{}}'>
@@ -21474,8 +21604,6 @@ A <ref>
 
 !!test
 Ref: 6. indent-pres should not be output in ref-body
-!!options
-parsoid
 !! wikitext
 A <ref>
  foo
@@ -21484,7 +21612,7 @@ A <ref>
 </ref>
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
@@ -21497,8 +21625,6 @@ A <ref>
 
 !!test
 Ref: 7. No p-wrapping in ref-body
-!!options
-parsoid
 !! wikitext
 A <ref>
 foo
@@ -21514,7 +21640,7 @@ booz
 </ref>
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
@@ -21534,27 +21660,23 @@ booz
 
 !!test
 Ref: 8. transclusion wikitext has lower precedence
-!!options
-parsoid
 !! wikitext
 A <ref> foo {{echo|</ref> B C}}
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span> B C<span typeof="mw:Nowiki">}}</span></p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
-<li about="#cite_note-1" id="cite_note-1"><a href="#cite_ref-1" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">foo <span typeof="mw:Nowiki" data-parsoid='{"src":"{{","dsr":[12,14,0,0]}'>{{</span>echo|</span></li>
+<li about="#cite_note-1" id="cite_note-1"><a href="#cite_ref-1" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">foo {{echo|</span></li>
 </ol>
 !!end
 
 !!test
 Ref: 9. unclosed comments should not leak out of ref-body
-!!options
-parsoid
 !! wikitext
 A <ref> foo <!--</ref> B C
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span> B C</p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
 <li about="#cite_note-1" id="cite_note-1"><a href="#cite_ref-1" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">foo <!----></span></li>
@@ -21563,13 +21685,11 @@ A <ref> foo <!--</ref> B C
 
 !!test
 Ref: 10. Unclosed HTML tags should not leak out of ref-body
-!!options
-parsoid
 !! wikitext
 A <ref> <b> foo </ref> B C
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span> B C</p>
 
 
@@ -21580,13 +21700,11 @@ A <ref> <b> foo </ref> B C
 
 !!test
 Ref: 11. ref-tags acts like an inline element wrt P-wrapping
-!!options
-parsoid
 !! wikitext
 A <ref>foo</ref> B
 C <ref>bar</ref> D
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span> B
 C <span about="#mwt4" class="mw-ref" id="cite_ref-2" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-2"},"attrs":{}}'><a href="#cite_note-2"><span class="mw-reflink-text">[2]</span></a></span> D</p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt6" data-mw='{"name":"references","attrs":{}}'>
 
 !!test
 Ref: 13. ref-tags are not SOL-transparent and block indent-pres
-!!options
-parsoid
 !! wikitext
 <ref>foo</ref> A
 <ref>bar
 </ref> B
 <references />
-!! html
+!! html/parsoid
 <p><span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span> A
 <span about="#mwt4" class="mw-ref" id="cite_ref-2" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-2"},"attrs":{}}'><a href="#cite_note-2"><span class="mw-reflink-text">[2]</span></a></span> B</p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt6" data-mw='{"name":"references","attrs":{}}'>
@@ -21639,13 +21755,11 @@ parsoid
 
 !!test
 Ref: 14. A nested ref-tag should be emitted as plain text
-!!options
-parsoid
 !! wikitext
 <ref>foo <ref>bar</ref> baz</ref>
 
 <references />
-!! html
+!! html/parsoid
 <p><span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span>
 </p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt5" data-mw='{"name":"references","attrs":{}}'>
@@ -21655,14 +21769,12 @@ parsoid
 
 !!test
 Ref: 15. ref-tags with identical names should get identical indexes
-!!options
-parsoid
 !! wikitext
 A1 <ref name="a">foo</ref> A2 <ref name="a" />
 B1 <ref name="b" /> B2 <ref name="b">bar</ref>
 
 <references />
-!! html
+!! html/parsoid
 <p>A1 <span about="#mwt3" class="mw-ref" id="cite_ref-a_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-a-1"},"attrs":{"name":"a"}}'><a href="#cite_note-a-1"><span class="mw-reflink-text">[1]</span></a></span> A2 <span about="#mwt4" class="mw-ref" id="cite_ref-a_1-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"a"}}'><a href="#cite_note-a-1"><span class="mw-reflink-text">[1]</span></a></span>
 B1 <span about="#mwt7" class="mw-ref" id="cite_ref-b_2-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"b"}}'><a href="#cite_note-b-2"><span class="mw-reflink-text">[2]</span></a></span> B2 <span about="#mwt8" class="mw-ref" id="cite_ref-b_2-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-b-2"},"attrs":{"name":"b"}}'><a href="#cite_note-b-2"><span class="mw-reflink-text">[2]</span></a></span></p>
 
@@ -21679,7 +21791,7 @@ parsoid=wt2html
 A <ref >foo</ref >
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
 <li about="#cite_note-1" id="cite_note-1"><a href="#cite_ref-1" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">foo</span></li></ol>
@@ -21687,13 +21799,11 @@ A <ref >foo</ref >
 
 !!test
 Ref: 17. Generate valid HTML5 id/about attributes
-!!options
-parsoid
 !!wikitext
 <ref name="a b">foo</ref>
 
 <references />
-!!html
+!!html/parsoid
 <p><span class="mw-ref" id="cite_ref-a_b_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-a_b-1"},"attrs":{"name":"a b"}}'><a href="#cite_note-a_b-1"><span class="mw-reflink-text">[1]</span></a></span>
 </p>
 
@@ -21704,13 +21814,11 @@ parsoid
 
 !!test
 Ref: 18. T58916: Extension attributes should be parsed as plain text
-!!options
-parsoid
 !!wikitext
 <ref name="{{echo|a}}">foo</ref>
 
 <references />
-!!html
+!!html/parsoid
 <p><span class="mw-ref" id="cite_ref-.7B.7Becho.7Ca.7D.7D_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-.7B.7Becho.7Ca.7D.7D-1"},"attrs":{"name":"{{echo|a}}"}}'><a href="#cite_note-.7B.7Becho.7Ca.7D.7D-1"><span class="mw-reflink-text">[1]</span></a></span>
 </p>
 
@@ -21721,13 +21829,11 @@ parsoid
 
 !!test
 Ref: 19. ref-tags with identical name encodings should get identical indexes
-!!options
-parsoid
 !! wikitext
 1 <ref name="a & b">foo</ref> 2 <ref name="a &amp; b" />
 
 <references />
-!! html
+!! html/parsoid
 <p>1 <span about="#mwt3" class="mw-ref" id="cite_ref-a_.26_b_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-a_.26_b-1"},"attrs":{"name":"a &amp; b"}}'><a href="#cite_note-a_.26_b-1"><span class="mw-reflink-text">[1]</span></a></span> 2 <span about="#mwt4" class="mw-ref" id="cite_ref-a_.26_b_1-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"a &amp;amp; b"}}'><a href="#cite_note-a_.26_b-1"><span class="mw-reflink-text">[1]</span></a></span>
 </p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt6" data-mw='{"name":"references","attrs":{}}'>
@@ -21737,15 +21843,13 @@ parsoid
 
 !!test
 Ref: 20. ref-tags with identical names but different content should keep it
-!!options
-parsoid
 !! wikitext
 A <ref name="foo">Foo one</ref>
 B <ref name="foo">Foo two</ref>
 C <ref name="foo" />
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-foo_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-foo-1"},"attrs":{"name":"foo"}}'><a href="#cite_note-foo-1"><span class="mw-reflink-text">[1]</span></a></span>
 B <span about="#mwt4" class="mw-ref" id="cite_ref-foo_1-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"html":"Foo two"},"attrs":{"name":"foo"}}'><a href="#cite_note-foo-1"><span class="mw-reflink-text">[1]</span></a></span>
 C <span about="#mwt6" class="mw-ref" id="cite_ref-foo_1-2" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"foo"}}'><a href="#cite_note-foo-1"><span class="mw-reflink-text">[1]</span></a></span></p>
@@ -21979,8 +22083,10 @@ foo<ol class="mw-references" typeof="mw:Extension/references" about="#mwt2" data
 #### https://www.mediawiki.org/wiki/Parsoid/HTML_based_LST
 #### ----------------------------------------------------------------
 
-!!test
+!! test
 LST Sections: 1. Simple section start and end
+!! options
+parsoid={ "suppressErrors": true }
 !! wikitext
 <section begin="2011-05-16" />
 <section end="2014-04-10 (MW 1.23wmf22)" />
@@ -23574,7 +23680,8 @@ parsoid=html2wt
 
  __TOC__ foo
 
-__TOC__ bar
+__TOC__
+ bar
 !! end
 
 #### --------------- HTML tags ---------------
@@ -23723,6 +23830,19 @@ HTML tag with broken attribute value quoting
 </p>
 !! end
 
+!! test
+Self-closed tag with broken attribute value quoting
+!! options
+parsoid=wt2html,html2html
+!! wikitext
+<div title="Hello world />Foo
+!! html/php+tidy
+<div title="Hello world"></div>
+<p>Foo</p>
+!! html/parsoid
+<div title="Hello world " data-parsoid='{"stx":"html","selfClose":true}'></div><p>Foo</p>
+!! end
+
 !! test
 Table with broken attribute value quoting
 !! wikitext
@@ -24351,6 +24471,19 @@ Properly encapsulate empty-content transclusions in fosterable positions
 </table>
 !! end
 
+!! test
+Always encapsulate foster box when template range is expanded to table
+!! options
+parsoid=wt2wt
+!! wikitext
+{|
+hello
+{{OpenTable}}
+|}
+!! html/parsoid
+
+!! end
+
 !!test
 Support <object> element with .data attribute
 !!options
@@ -24785,7 +24918,8 @@ parsoid=html2wt
 </div>
 !! wikitext
 foo
-<nowiki> </nowiki><span>bar</span>
+<span>bar</span>
 
 <span>foo2
 <nowiki> </nowiki></span>bar2
@@ -24827,15 +24961,15 @@ parsoid={
 
 <h2><meta property="mw:PageProp/toc" /> ok</h2>
 !! wikitext
-== hello there [[Category:A1]]  ==
+== hello there [[Category:A1]] ==
 
-==  [[Category:A2]] hi pal ==
+== [[Category:A2]] hi pal ==
 
-==  <!--foo-->  [[Category:A3]]    how goes it ==
+== <!--foo-->  [[Category:A3]]    how goes it ==
 
-== it goes well    [[Category:A4]]  <!--bar-->  ==
+== it goes well    [[Category:A4]]  <!--bar--> ==
 
-==howdy [[Category:A5]] ==
+==howdy [[Category:A5]]==
 
 ==  __TOC__  ok ==
 !! end
@@ -25620,7 +25754,7 @@ parsoid={ "modes": ["html2wt"], "suppressErrors": true }
 # shown to sneak through on occasion. See T101768.
 # The original wikitext here is: [http://test.com [[one]] two three]
 !! test
-Strip span tags added to mark as misnested
+Strip span tags added to mark misnested links
 !! options
 parsoid=html2wt
 !! html/parsoid
@@ -25629,10 +25763,112 @@ parsoid=html2wt
 [http://test.com][[one]] two three
 !! end
 
+!! test
+Use data-parsoid.firstWikitextNode to compute newline constraints for template content
+!! options
+parsoid=html2wt
+!! html/parsoid
+<span about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"pi":[[{"k":"1","spc":["","","",""]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"a"}},"i":0}}]}'>a</span><table about="#mwt2" typeof="mw:Transclusion mw:ExpandedAttrs" data-parsoid='{"a":{"{{echo|c\n{{!}}d\n}}":null},"sa":{"{{echo|c\n{{!}}d\n}}":""},"firstWikitextNode":"table","pi":[[{"k":"1","spc":["","","",""]}]]}' data-mw='{"parts":["{|",{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"c\n{{!}}d\n"}},"i":0}},"\n|}"]}'>
+<tbody><tr><td>d
+</td></tr>
+</tbody></table>
+!! wikitext
+{{echo|a}}
+{|{{echo|c
+{{!}}d
+}}
+|}
+!! end
+
+## This test verifies the presence and computation of this attribute indirectly
+## by making an edit and ensuring that the serialization is correct (which it would be
+## only if firstWikitextNode is properly set).
+!! test
+data-parsoid.firstWikitextNode should be computed properly in the presence of fostered content
+!! options
+parsoid= {
+  "modes": ["wt2wt"],
+  "changes": [
+    [ "div#x", "remove" ],
+    [ "div", "before", "<div>new</div>" ]
+  ]
+}
+!! wikitext
+<div id="x">foo</div>
+{|
+{{echo|<div>boo</div>
+{{!}}b}}
+|c
+|}
+!! wikitext/edited
+
+<div>new</div>
+{|
+{{echo|<div>boo</div>
+{{!}}b}}
+|c
+|}
+!! end
+
 # --------------------------------------------
 # Tests spec'ing wikitext serialization norms |
 # --------------------------------------------
 
+!! test
+1. Categories should always be serialized on their own line
+!! options
+parsoid=html2wt
+!! html/parsoid
+foo<link rel="mw:PageProp/Category" href="./Category:Foo">bar
+!! wikitext
+foo
+[[Category:Foo]]
+bar
+!! end
+
+!! test
+2. Categories that are part of templates should not introduce a line break
+!! wikitext
+foo {{echo|<span>bar</span> [[Category:baz]]}} bar
+!! html/parsoid
+<p>foo <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"&lt;span>bar&lt;/span> [[Category:baz]]"}},"i":0}}]}'>bar</span><span about="#mwt1"> </span><link rel="mw:PageProp/Category" href="./Category:Baz" about="#mwt1" data-parsoid='{"stx":"simple","a":{"href":"./Category:Baz"},"sa":{"href":"Category:baz"}}'/> bar</p>
+!! end
+
+# Careful while editing these next 2 tests. There are \u200f characters
+# before and after the <link> tags in the HTML and following some
+# of the categories in wikitext
+# Do not remove these characters in edits.
+#
+# As part of the serialization, these bidi characters will get stripped.
+!! test
+RTL (\u200f) and LTR (\u200e) markers around category tags should be stripped
+!! options
+parsoid={
+  "modes": ["html2wt"],
+  "scrubWikitext": true
+}
+!! html/parsoid
+<p>‏<link rel="mw:PageProp/Category" href="./קטגוריה:טקסים" />‏
+‏<link rel="mw:PageProp/Category" href="./קטגוריה:_שיטות_משפט" />‏</p>
+!! wikitext
+[[קטגוריה:טקסים]]
+[[קטגוריה: שיטות משפט]]
+!! end
+
+!! test
+RTL (\u200f) and LTR (\u200e) markers should not be stripped if followed by a text node
+!! options
+parsoid={
+  "modes": ["html2wt"],
+  "scrubWikitext": true
+}
+!! html/parsoid
+<p>‏<link rel="mw:PageProp/Category" href="./קטגוריה:טקסים" />‏y</p>
+!! wikitext
+[[קטגוריה:טקסים]]
+‏y
+!! end
+
 !! test
 Lists: Add space after bullets
 !! options
@@ -25720,6 +25956,35 @@ parsoid={
 !! wikitext/edited
 !! end
 
+!! test
+Headings: Replace <br/> with a single whitespace char (when scrubWikitext = true)
+!! options
+parsoid={
+  "modes": ["html2wt"],
+  "scrubWikitext": true
+}
+!! html/parsoid
+<h2>foo<br/>bar</h2>
+<h2>foo <span><br/>bar</span> baz</h2>
+!! wikitext
+== foo bar ==
+
+== foo <span> bar</span> baz ==
+!! end
+
+!! test
+Headings: Replace <br/> with a single whitespace char (when scrubWikitext = false)
+!! options
+parsoid={
+  "modes": ["html2wt"],
+  "scrubWikitext": false
+}
+!! html/parsoid
+<h2>foo<br/>bar</h2>
+!! wikitext
+== foo<br> bar ==
+!! end
+
 !! test
 1. WT Quote Tags: suppress newly created empty style tags
 !! options
@@ -26225,10 +26490,61 @@ parsoid=html2wt
 &lt;nowiki&gt;''foo''&lt;/nowiki&gt;
 !! end
 
+# This is meant to be an interim fix while we go about figuring out
+# how to not introduce these trailing <nowiki/>s in the first place.
+!! test
+T115717: Strip trailing <nowiki/>s (without affecting valid uses)
+!! options
+parsoid=html2wt
+!! html/parsoid
+<p>x<meta typeof="mw:Placeholder" data-parsoid='{"src":"&lt;nowiki/>"}'/><meta typeof="mw:Placeholder" data-parsoid='{"src":"&lt;nowiki/>"}'/>
+y</p>
+<p><span about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"dsr":[0,23,null,null],"pi":[[{"k":"1","named":true,"spc":["\n"," "," ",""]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"&lt;nowiki/>"}},"i":0}}]}'></span></p>
+<p><span about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"dsr":[0,24,null,null],"pi":[[{"k":"1","named":true,"spc":["\n"," "," ","\n"]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"&lt;nowiki/>"}},"i":0}}]}'></span></p>
+!! wikitext
+x
+y
+
+{{echo|
+1 = <nowiki/>}}
+
+{{echo|
+1 = <nowiki/>
+}}
+!! end
+
 # ---------------------------------------------------
 # End of tests spec'ing wikitext serialization norms |
 # ---------------------------------------------------
 
+# T104032
+!! test
+Bare inline nodes not wrapped inside p-tags should be treated as p-wrapped
+!! options
+parsoid=html2wt
+!! html/parsoid
+a<p>b</p>
+<b>c</b><p>d</p>
+<table><tr>
+<td>a<p>b</p></td>
+<td><b>c</b><p>d</p></td>
+</tr></table>
+!! wikitext
+a
+
+b
+
+'''c'''
+
+d
+{|
+|a
+b
+|'''c'''
+d
+|}
+!! end
+
 # -----------------------------------------------------------------
 # End of section for Parsoid-only html2wt tests for serialization
 # of new content
index 201cbfc..543eb5c 100644 (file)
@@ -25,6 +25,7 @@ class ExtensionRegistryTest extends MediaWikiTestCase {
                        'defines' => array(),
                        'credits' => array(),
                        'attributes' => array(),
+                       'autoloaderPaths' => array()
                );
                $registry = new ExtensionRegistry();
                $class = new ReflectionClass( 'ExtensionRegistry' );
index 5c6a6cd..7b84dfa 100644 (file)
@@ -120,6 +120,16 @@ class MaintenanceFixup extends Maintenance {
                return call_user_func_array( array( "parent", __FUNCTION__ ), func_get_args() );
        }
 
+       public function addOption( $name, $description, $required = false,
+               $withArg = false, $shortName = false, $multiOccurance = false
+       ) {
+               return call_user_func_array( array( "parent", __FUNCTION__ ), func_get_args() );
+       }
+
+       public function getOption( $name, $default = null ) {
+               return call_user_func_array( array( "parent", __FUNCTION__ ), func_get_args() );
+       }
+
        // --- Requirements for getting instance of abstract class
 
        public function execute() {
@@ -829,4 +839,45 @@ class MaintenanceTest extends MediaWikiTestCase {
                $this->m->setConfig( $conf );
                $this->assertSame( $conf, $this->m->getConfig() );
        }
+
+       function testParseArgs() {
+               global $argv;
+               $oldArgv = $argv;
+
+               $argv = array( '', '--multi', 'this1', '--multi', 'this2' );
+               $m2 = new MaintenanceFixup( $this );
+               // Create an option with an argument allowed to be specified multiple times
+               $m2->addOption( 'multi', 'This option does stuff', false, true, false, true );
+               $m2->loadParamsAndArgs();
+
+               $this->assertEquals( array( 'this1', 'this2' ), $m2->getOption( 'multi' ) );
+               $this->assertEquals( array( array( 'multi', 'this1' ), array( 'multi', 'this2' ) ),
+                       $m2->orderedOptions );
+
+               $m2->simulateShutdown();
+
+               $argv = array( '', '--multi', '--multi' );
+               $m2 = new MaintenanceFixup( $this );
+
+               $m2->addOption( 'multi', 'This option does stuff', false, false, false, true );
+               $m2->loadParamsAndArgs();
+
+               $this->assertEquals( array( 1, 1 ), $m2->getOption( 'multi' ) );
+               $this->assertEquals( array( array( 'multi', 1 ), array( 'multi', 1 ) ), $m2->orderedOptions );
+
+               $m2->simulateShutdown();
+
+               $argv = array( '', '--multi=yo' );
+               $m2 = new MaintenanceFixup( $this );
+               // Create an option with an argument allowed to be specified multiple times
+               $m2->addOption( 'multi', 'This option doesn\'t actually support multiple occurrences' );
+               $m2->loadParamsAndArgs();
+
+               $this->assertEquals( 'yo', $m2->getOption( 'multi' ) );
+               $this->assertEquals( array( array( 'multi', 'yo' ) ), $m2->orderedOptions );
+
+               $m2->simulateShutdown();
+
+               $argv = $oldArgv;
+       }
 }