Merge "Fix SpecialFileDuplicateSearch::prefixSearchSubpages"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 28 Dec 2015 23:44:43 +0000 (23:44 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 28 Dec 2015 23:44:43 +0000 (23:44 +0000)
58 files changed:
autoload.php
includes/DefaultSettings.php
includes/Export.php [deleted file]
includes/api/ApiMain.php
includes/api/i18n/ko.json
includes/api/i18n/lb.json
includes/changes/ChangesList.php
includes/changes/EnhancedChangesList.php
includes/changes/OldChangesList.php
includes/db/DBConnRef.php
includes/db/Database.php
includes/db/IDatabase.php
includes/export/Dump7ZipOutput.php [new file with mode: 0644]
includes/export/DumpBZip2Output.php [new file with mode: 0644]
includes/export/DumpFileOutput.php [new file with mode: 0644]
includes/export/DumpFilter.php [new file with mode: 0644]
includes/export/DumpGZipOutput.php [new file with mode: 0644]
includes/export/DumpLatestFilter.php [new file with mode: 0644]
includes/export/DumpMultiWriter.php [new file with mode: 0644]
includes/export/DumpNamespaceFilter.php [new file with mode: 0644]
includes/export/DumpNotalkFilter.php [new file with mode: 0644]
includes/export/DumpOutput.php [new file with mode: 0644]
includes/export/DumpPipeOutput.php [new file with mode: 0644]
includes/export/WikiExporter.php [new file with mode: 0644]
includes/export/XmlDumpWriter.php [new file with mode: 0644]
includes/specialpage/QueryPage.php
includes/specials/SpecialMediaStatistics.php
includes/specials/SpecialPreferences.php
includes/templates/EnhancedChangesListGroup.mustache [new file with mode: 0644]
languages/i18n/azb.json
languages/i18n/be-tarask.json
languages/i18n/bg.json
languages/i18n/br.json
languages/i18n/cs.json
languages/i18n/cv.json
languages/i18n/en.json
languages/i18n/eu.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/kn.json
languages/i18n/ko.json
languages/i18n/lb.json
languages/i18n/ml.json
languages/i18n/nah.json
languages/i18n/ne.json
languages/i18n/qqq.json
languages/i18n/sgs.json
languages/i18n/tt-cyrl.json
languages/messages/MessagesGlk.php
maintenance/benchmarks/Benchmarker.php
resources/Resources.php
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js
resources/src/mediawiki/mediawiki.Title.js
tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js

index 8c5ec81..4d7ed5b 100644 (file)
@@ -352,21 +352,21 @@ $wgAutoloadLocalClasses = array(
        'DoubleReplacer' => __DIR__ . '/includes/libs/replacers/DoubleReplacer.php',
        'DummyLinker' => __DIR__ . '/includes/Linker.php',
        'DummyTermColorer' => __DIR__ . '/maintenance/term/MWTerm.php',
-       'Dump7ZipOutput' => __DIR__ . '/includes/Export.php',
-       'DumpBZip2Output' => __DIR__ . '/includes/Export.php',
+       'Dump7ZipOutput' => __DIR__ . '/includes/export/Dump7ZipOutput.php',
+       'DumpBZip2Output' => __DIR__ . '/includes/export/DumpBZip2Output.php',
        'DumpDBZip2Output' => __DIR__ . '/maintenance/backup.inc',
-       'DumpFileOutput' => __DIR__ . '/includes/Export.php',
-       'DumpFilter' => __DIR__ . '/includes/Export.php',
-       'DumpGZipOutput' => __DIR__ . '/includes/Export.php',
+       'DumpFileOutput' => __DIR__ . '/includes/export/DumpFileOutput.php',
+       'DumpFilter' => __DIR__ . '/includes/export/DumpFilter.php',
+       'DumpGZipOutput' => __DIR__ . '/includes/export/DumpGZipOutput.php',
        'DumpIterator' => __DIR__ . '/maintenance/dumpIterator.php',
-       'DumpLatestFilter' => __DIR__ . '/includes/Export.php',
+       'DumpLatestFilter' => __DIR__ . '/includes/export/DumpLatestFilter.php',
        'DumpLinks' => __DIR__ . '/maintenance/dumpLinks.php',
        'DumpMessages' => __DIR__ . '/maintenance/language/dumpMessages.php',
-       'DumpMultiWriter' => __DIR__ . '/includes/Export.php',
-       'DumpNamespaceFilter' => __DIR__ . '/includes/Export.php',
-       'DumpNotalkFilter' => __DIR__ . '/includes/Export.php',
-       'DumpOutput' => __DIR__ . '/includes/Export.php',
-       'DumpPipeOutput' => __DIR__ . '/includes/Export.php',
+       'DumpMultiWriter' => __DIR__ . '/includes/export/DumpMultiWriter.php',
+       'DumpNamespaceFilter' => __DIR__ . '/includes/export/DumpNamespaceFilter.php',
+       'DumpNotalkFilter' => __DIR__ . '/includes/export/DumpNotalkFilter.php',
+       'DumpOutput' => __DIR__ . '/includes/export/DumpOutput.php',
+       'DumpPipeOutput' => __DIR__ . '/includes/export/DumpPipeOutput.php',
        'DumpRenderer' => __DIR__ . '/maintenance/renderDump.php',
        'DumpRev' => __DIR__ . '/maintenance/storage/dumpRev.php',
        'DuplicateJob' => __DIR__ . '/includes/jobqueue/jobs/DuplicateJob.php',
@@ -1386,7 +1386,7 @@ $wgAutoloadLocalClasses = array(
        'WebResponse' => __DIR__ . '/includes/WebResponse.php',
        'WikiCategoryPage' => __DIR__ . '/includes/page/WikiCategoryPage.php',
        'WikiDiff3' => __DIR__ . '/includes/diff/WikiDiff3.php',
-       'WikiExporter' => __DIR__ . '/includes/Export.php',
+       'WikiExporter' => __DIR__ . '/includes/export/WikiExporter.php',
        'WikiFilePage' => __DIR__ . '/includes/page/WikiFilePage.php',
        'WikiImporter' => __DIR__ . '/includes/Import.php',
        'WikiMap' => __DIR__ . '/includes/WikiMap.php',
@@ -1408,7 +1408,7 @@ $wgAutoloadLocalClasses = array(
        'XMPValidate' => __DIR__ . '/includes/media/XMPValidate.php',
        'Xhprof' => __DIR__ . '/includes/libs/Xhprof.php',
        'Xml' => __DIR__ . '/includes/Xml.php',
-       'XmlDumpWriter' => __DIR__ . '/includes/Export.php',
+       'XmlDumpWriter' => __DIR__ . '/includes/export/XmlDumpWriter.php',
        'XmlJsCode' => __DIR__ . '/includes/Xml.php',
        'XmlSelect' => __DIR__ . '/includes/XmlSelect.php',
        'XmlTypeCheck' => __DIR__ . '/includes/libs/XmlTypeCheck.php',
index 0d4ecac..a6d34b0 100644 (file)
@@ -5556,6 +5556,11 @@ $wgTrxProfilerLimits = array(
                'writeQueryTime' => 1,
                'maxAffected' => 500
        ),
+       'POST-nonwrite' => array(
+               'masterConns' => 0,
+               'writes' => 0,
+               'readQueryTime' => 5
+       ),
        // Background job runner
        'JobRunner' => array(
                'readQueryTime' => 30,
diff --git a/includes/Export.php b/includes/Export.php
deleted file mode 100644 (file)
index b4d7737..0000000
+++ /dev/null
@@ -1,1549 +0,0 @@
-<?php
-/**
- * Base classes for dumps and export
- *
- * 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
- * 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
- */
-
-/**
- * @defgroup Dump Dump
- */
-
-/**
- * @ingroup SpecialPage Dump
- */
-class WikiExporter {
-       /** @var bool Return distinct author list (when not returning full history) */
-       public $list_authors = false;
-
-       /** @var bool */
-       public $dumpUploads = false;
-
-       /** @var bool */
-       public $dumpUploadFileContents = false;
-
-       /** @var string */
-       public $author_list = "";
-
-       const FULL = 1;
-       const CURRENT = 2;
-       const STABLE = 4; // extension defined
-       const LOGS = 8;
-       const RANGE = 16;
-
-       const BUFFER = 0;
-       const STREAM = 1;
-
-       const TEXT = 0;
-       const STUB = 1;
-
-       /** @var int */
-       public $buffer;
-
-       /** @var int */
-       public $text;
-
-       /** @var DumpOutput */
-       public $sink;
-
-       /**
-        * Returns the export schema version.
-        * @return string
-        */
-       public static function schemaVersion() {
-               return "0.10";
-       }
-
-       /**
-        * If using WikiExporter::STREAM to stream a large amount of data,
-        * provide a database connection which is not managed by
-        * LoadBalancer to read from: some history blob types will
-        * make additional queries to pull source data while the
-        * main query is still running.
-        *
-        * @param IDatabase $db
-        * @param int|array $history One of WikiExporter::FULL, WikiExporter::CURRENT,
-        *   WikiExporter::RANGE or WikiExporter::STABLE, or an associative array:
-        *   - offset: non-inclusive offset at which to start the query
-        *   - limit: maximum number of rows to return
-        *   - dir: "asc" or "desc" timestamp order
-        * @param int $buffer One of WikiExporter::BUFFER or WikiExporter::STREAM
-        * @param int $text One of WikiExporter::TEXT or WikiExporter::STUB
-        */
-       function __construct( $db, $history = WikiExporter::CURRENT,
-                       $buffer = WikiExporter::BUFFER, $text = WikiExporter::TEXT ) {
-               $this->db = $db;
-               $this->history = $history;
-               $this->buffer = $buffer;
-               $this->writer = new XmlDumpWriter();
-               $this->sink = new DumpOutput();
-               $this->text = $text;
-       }
-
-       /**
-        * Set the DumpOutput or DumpFilter object which will receive
-        * various row objects and XML output for filtering. Filters
-        * can be chained or used as callbacks.
-        *
-        * @param DumpOutput $sink
-        */
-       public function setOutputSink( &$sink ) {
-               $this->sink =& $sink;
-       }
-
-       public function openStream() {
-               $output = $this->writer->openStream();
-               $this->sink->writeOpenStream( $output );
-       }
-
-       public function closeStream() {
-               $output = $this->writer->closeStream();
-               $this->sink->writeCloseStream( $output );
-       }
-
-       /**
-        * Dumps a series of page and revision records for all pages
-        * in the database, either including complete history or only
-        * the most recent version.
-        */
-       public function allPages() {
-               $this->dumpFrom( '' );
-       }
-
-       /**
-        * Dumps a series of page and revision records for those pages
-        * in the database falling within the page_id range given.
-        * @param int $start Inclusive lower limit (this id is included)
-        * @param int $end Exclusive upper limit (this id is not included)
-        *   If 0, no upper limit.
-        */
-       public function pagesByRange( $start, $end ) {
-               $condition = 'page_id >= ' . intval( $start );
-               if ( $end ) {
-                       $condition .= ' AND page_id < ' . intval( $end );
-               }
-               $this->dumpFrom( $condition );
-       }
-
-       /**
-        * Dumps a series of page and revision records for those pages
-        * in the database with revisions falling within the rev_id range given.
-        * @param int $start Inclusive lower limit (this id is included)
-        * @param int $end Exclusive upper limit (this id is not included)
-        *   If 0, no upper limit.
-        */
-       public function revsByRange( $start, $end ) {
-               $condition = 'rev_id >= ' . intval( $start );
-               if ( $end ) {
-                       $condition .= ' AND rev_id < ' . intval( $end );
-               }
-               $this->dumpFrom( $condition );
-       }
-
-       /**
-        * @param Title $title
-        */
-       public function pageByTitle( $title ) {
-               $this->dumpFrom(
-                       'page_namespace=' . $title->getNamespace() .
-                       ' AND page_title=' . $this->db->addQuotes( $title->getDBkey() ) );
-       }
-
-       /**
-        * @param string $name
-        * @throws MWException
-        */
-       public function pageByName( $name ) {
-               $title = Title::newFromText( $name );
-               if ( is_null( $title ) ) {
-                       throw new MWException( "Can't export invalid title" );
-               } else {
-                       $this->pageByTitle( $title );
-               }
-       }
-
-       /**
-        * @param array $names
-        */
-       public function pagesByName( $names ) {
-               foreach ( $names as $name ) {
-                       $this->pageByName( $name );
-               }
-       }
-
-       public function allLogs() {
-               $this->dumpFrom( '' );
-       }
-
-       /**
-        * @param int $start
-        * @param int $end
-        */
-       public function logsByRange( $start, $end ) {
-               $condition = 'log_id >= ' . intval( $start );
-               if ( $end ) {
-                       $condition .= ' AND log_id < ' . intval( $end );
-               }
-               $this->dumpFrom( $condition );
-       }
-
-       /**
-        * Generates the distinct list of authors of an article
-        * Not called by default (depends on $this->list_authors)
-        * Can be set by Special:Export when not exporting whole history
-        *
-        * @param array $cond
-        */
-       protected function do_list_authors( $cond ) {
-               $this->author_list = "<contributors>";
-               // rev_deleted
-
-               $res = $this->db->select(
-                       array( 'page', 'revision' ),
-                       array( 'DISTINCT rev_user_text', 'rev_user' ),
-                       array(
-                               $this->db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0',
-                               $cond,
-                               'page_id = rev_id',
-                       ),
-                       __METHOD__
-               );
-
-               foreach ( $res as $row ) {
-                       $this->author_list .= "<contributor>" .
-                               "<username>" .
-                               htmlentities( $row->rev_user_text ) .
-                               "</username>" .
-                               "<id>" .
-                               $row->rev_user .
-                               "</id>" .
-                               "</contributor>";
-               }
-               $this->author_list .= "</contributors>";
-       }
-
-       /**
-        * @param string $cond
-        * @throws MWException
-        * @throws Exception
-        */
-       protected function dumpFrom( $cond = '' ) {
-               # For logging dumps...
-               if ( $this->history & self::LOGS ) {
-                       $where = array( 'user_id = log_user' );
-                       # Hide private logs
-                       $hideLogs = LogEventsList::getExcludeClause( $this->db );
-                       if ( $hideLogs ) {
-                               $where[] = $hideLogs;
-                       }
-                       # Add on any caller specified conditions
-                       if ( $cond ) {
-                               $where[] = $cond;
-                       }
-                       # Get logging table name for logging.* clause
-                       $logging = $this->db->tableName( 'logging' );
-
-                       if ( $this->buffer == WikiExporter::STREAM ) {
-                               $prev = $this->db->bufferResults( false );
-                       }
-                       $result = null; // Assuring $result is not undefined, if exception occurs early
-                       try {
-                               $result = $this->db->select( array( 'logging', 'user' ),
-                                       array( "{$logging}.*", 'user_name' ), // grab the user name
-                                       $where,
-                                       __METHOD__,
-                                       array( 'ORDER BY' => 'log_id', 'USE INDEX' => array( 'logging' => 'PRIMARY' ) )
-                               );
-                               $this->outputLogStream( $result );
-                               if ( $this->buffer == WikiExporter::STREAM ) {
-                                       $this->db->bufferResults( $prev );
-                               }
-                       } catch ( Exception $e ) {
-                               // Throwing the exception does not reliably free the resultset, and
-                               // would also leave the connection in unbuffered mode.
-
-                               // Freeing result
-                               try {
-                                       if ( $result ) {
-                                               $result->free();
-                                       }
-                               } catch ( Exception $e2 ) {
-                                       // Already in panic mode -> ignoring $e2 as $e has
-                                       // higher priority
-                               }
-
-                               // Putting database back in previous buffer mode
-                               try {
-                                       if ( $this->buffer == WikiExporter::STREAM ) {
-                                               $this->db->bufferResults( $prev );
-                                       }
-                               } catch ( Exception $e2 ) {
-                                       // Already in panic mode -> ignoring $e2 as $e has
-                                       // higher priority
-                               }
-
-                               // Inform caller about problem
-                               throw $e;
-                       }
-               # For page dumps...
-               } else {
-                       $tables = array( 'page', 'revision' );
-                       $opts = array( 'ORDER BY' => 'page_id ASC' );
-                       $opts['USE INDEX'] = array();
-                       $join = array();
-                       if ( is_array( $this->history ) ) {
-                               # Time offset/limit for all pages/history...
-                               $revJoin = 'page_id=rev_page';
-                               # Set time order
-                               if ( $this->history['dir'] == 'asc' ) {
-                                       $op = '>';
-                                       $opts['ORDER BY'] = 'rev_timestamp ASC';
-                               } else {
-                                       $op = '<';
-                                       $opts['ORDER BY'] = 'rev_timestamp DESC';
-                               }
-                               # Set offset
-                               if ( !empty( $this->history['offset'] ) ) {
-                                       $revJoin .= " AND rev_timestamp $op " .
-                                               $this->db->addQuotes( $this->db->timestamp( $this->history['offset'] ) );
-                               }
-                               $join['revision'] = array( 'INNER JOIN', $revJoin );
-                               # Set query limit
-                               if ( !empty( $this->history['limit'] ) ) {
-                                       $opts['LIMIT'] = intval( $this->history['limit'] );
-                               }
-                       } elseif ( $this->history & WikiExporter::FULL ) {
-                               # Full history dumps...
-                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page' );
-                       } elseif ( $this->history & WikiExporter::CURRENT ) {
-                               # Latest revision dumps...
-                               if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
-                                       $this->do_list_authors( $cond );
-                               }
-                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' );
-                       } elseif ( $this->history & WikiExporter::STABLE ) {
-                               # "Stable" revision dumps...
-                               # Default JOIN, to be overridden...
-                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' );
-                               # One, and only one hook should set this, and return false
-                               if ( Hooks::run( 'WikiExporter::dumpStableQuery', array( &$tables, &$opts, &$join ) ) ) {
-                                       throw new MWException( __METHOD__ . " given invalid history dump type." );
-                               }
-                       } elseif ( $this->history & WikiExporter::RANGE ) {
-                               # Dump of revisions within a specified range
-                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page' );
-                               $opts['ORDER BY'] = array( 'rev_page ASC', 'rev_id ASC' );
-                       } else {
-                               # Unknown history specification parameter?
-                               throw new MWException( __METHOD__ . " given invalid history dump type." );
-                       }
-                       # Query optimization hacks
-                       if ( $cond == '' ) {
-                               $opts[] = 'STRAIGHT_JOIN';
-                               $opts['USE INDEX']['page'] = 'PRIMARY';
-                       }
-                       # Build text join options
-                       if ( $this->text != WikiExporter::STUB ) { // 1-pass
-                               $tables[] = 'text';
-                               $join['text'] = array( 'INNER JOIN', 'rev_text_id=old_id' );
-                       }
-
-                       if ( $this->buffer == WikiExporter::STREAM ) {
-                               $prev = $this->db->bufferResults( false );
-                       }
-
-                       $result = null; // Assuring $result is not undefined, if exception occurs early
-                       try {
-                               Hooks::run( 'ModifyExportQuery',
-                                               array( $this->db, &$tables, &$cond, &$opts, &$join ) );
-
-                               # Do the query!
-                               $result = $this->db->select( $tables, '*', $cond, __METHOD__, $opts, $join );
-                               # Output dump results
-                               $this->outputPageStream( $result );
-
-                               if ( $this->buffer == WikiExporter::STREAM ) {
-                                       $this->db->bufferResults( $prev );
-                               }
-                       } catch ( Exception $e ) {
-                               // Throwing the exception does not reliably free the resultset, and
-                               // would also leave the connection in unbuffered mode.
-
-                               // Freeing result
-                               try {
-                                       if ( $result ) {
-                                               $result->free();
-                                       }
-                               } catch ( Exception $e2 ) {
-                                       // Already in panic mode -> ignoring $e2 as $e has
-                                       // higher priority
-                               }
-
-                               // Putting database back in previous buffer mode
-                               try {
-                                       if ( $this->buffer == WikiExporter::STREAM ) {
-                                               $this->db->bufferResults( $prev );
-                                       }
-                               } catch ( Exception $e2 ) {
-                                       // Already in panic mode -> ignoring $e2 as $e has
-                                       // higher priority
-                               }
-
-                               // Inform caller about problem
-                               throw $e;
-                       }
-               }
-       }
-
-       /**
-        * Runs through a query result set dumping page and revision records.
-        * The result set should be sorted/grouped by page to avoid duplicate
-        * page records in the output.
-        *
-        * Should be safe for
-        * streaming (non-buffered) queries, as long as it was made on a
-        * separate database connection not managed by LoadBalancer; some
-        * blob storage types will make queries to pull source data.
-        *
-        * @param ResultWrapper $resultset
-        */
-       protected function outputPageStream( $resultset ) {
-               $last = null;
-               foreach ( $resultset as $row ) {
-                       if ( $last === null ||
-                               $last->page_namespace != $row->page_namespace ||
-                               $last->page_title != $row->page_title ) {
-                               if ( $last !== null ) {
-                                       $output = '';
-                                       if ( $this->dumpUploads ) {
-                                               $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents );
-                                       }
-                                       $output .= $this->writer->closePage();
-                                       $this->sink->writeClosePage( $output );
-                               }
-                               $output = $this->writer->openPage( $row );
-                               $this->sink->writeOpenPage( $row, $output );
-                               $last = $row;
-                       }
-                       $output = $this->writer->writeRevision( $row );
-                       $this->sink->writeRevision( $row, $output );
-               }
-               if ( $last !== null ) {
-                       $output = '';
-                       if ( $this->dumpUploads ) {
-                               $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents );
-                       }
-                       $output .= $this->author_list;
-                       $output .= $this->writer->closePage();
-                       $this->sink->writeClosePage( $output );
-               }
-       }
-
-       /**
-        * @param ResultWrapper $resultset
-        */
-       protected function outputLogStream( $resultset ) {
-               foreach ( $resultset as $row ) {
-                       $output = $this->writer->writeLogItem( $row );
-                       $this->sink->writeLogItem( $row, $output );
-               }
-       }
-}
-
-/**
- * @ingroup Dump
- */
-class XmlDumpWriter {
-       /**
-        * Opens the XML output stream's root "<mediawiki>" element.
-        * This does not include an xml directive, so is safe to include
-        * as a subelement in a larger XML stream. Namespace and XML Schema
-        * references are included.
-        *
-        * Output will be encoded in UTF-8.
-        *
-        * @return string
-        */
-       function openStream() {
-               global $wgLanguageCode;
-               $ver = WikiExporter::schemaVersion();
-               return Xml::element( 'mediawiki', array(
-                       'xmlns'              => "http://www.mediawiki.org/xml/export-$ver/",
-                       'xmlns:xsi'          => "http://www.w3.org/2001/XMLSchema-instance",
-                       /*
-                        * When a new version of the schema is created, it needs staging on mediawiki.org.
-                        * This requires a change in the operations/mediawiki-config git repo.
-                        *
-                        * Create a changeset like https://gerrit.wikimedia.org/r/#/c/149643/ in which
-                        * you copy in the new xsd file.
-                        *
-                        * After it is reviewed, merged and deployed (sync-docroot), the index.html needs purging.
-                        * echo "http://www.mediawiki.org/xml/index.html" | mwscript purgeList.php --wiki=aawiki
-                        */
-                       'xsi:schemaLocation' => "http://www.mediawiki.org/xml/export-$ver/ " .
-                               "http://www.mediawiki.org/xml/export-$ver.xsd",
-                       'version'            => $ver,
-                       'xml:lang'           => $wgLanguageCode ),
-                       null ) .
-                       "\n" .
-                       $this->siteInfo();
-       }
-
-       /**
-        * @return string
-        */
-       function siteInfo() {
-               $info = array(
-                       $this->sitename(),
-                       $this->dbname(),
-                       $this->homelink(),
-                       $this->generator(),
-                       $this->caseSetting(),
-                       $this->namespaces() );
-               return "  <siteinfo>\n    " .
-                       implode( "\n    ", $info ) .
-                       "\n  </siteinfo>\n";
-       }
-
-       /**
-        * @return string
-        */
-       function sitename() {
-               global $wgSitename;
-               return Xml::element( 'sitename', array(), $wgSitename );
-       }
-
-       /**
-        * @return string
-        */
-       function dbname() {
-               global $wgDBname;
-               return Xml::element( 'dbname', array(), $wgDBname );
-       }
-
-       /**
-        * @return string
-        */
-       function generator() {
-               global $wgVersion;
-               return Xml::element( 'generator', array(), "MediaWiki $wgVersion" );
-       }
-
-       /**
-        * @return string
-        */
-       function homelink() {
-               return Xml::element( 'base', array(), Title::newMainPage()->getCanonicalURL() );
-       }
-
-       /**
-        * @return string
-        */
-       function caseSetting() {
-               global $wgCapitalLinks;
-               // "case-insensitive" option is reserved for future
-               $sensitivity = $wgCapitalLinks ? 'first-letter' : 'case-sensitive';
-               return Xml::element( 'case', array(), $sensitivity );
-       }
-
-       /**
-        * @return string
-        */
-       function namespaces() {
-               global $wgContLang;
-               $spaces = "<namespaces>\n";
-               foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
-                       $spaces .= '      ' .
-                               Xml::element( 'namespace',
-                                       array(
-                                               'key' => $ns,
-                                               'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
-                                       ), $title ) . "\n";
-               }
-               $spaces .= "    </namespaces>";
-               return $spaces;
-       }
-
-       /**
-        * Closes the output stream with the closing root element.
-        * Call when finished dumping things.
-        *
-        * @return string
-        */
-       function closeStream() {
-               return "</mediawiki>\n";
-       }
-
-       /**
-        * Opens a "<page>" section on the output stream, with data
-        * from the given database row.
-        *
-        * @param object $row
-        * @return string
-        */
-       public function openPage( $row ) {
-               $out = "  <page>\n";
-               $title = Title::makeTitle( $row->page_namespace, $row->page_title );
-               $out .= '    ' . Xml::elementClean( 'title', array(), self::canonicalTitle( $title ) ) . "\n";
-               $out .= '    ' . Xml::element( 'ns', array(), strval( $row->page_namespace ) ) . "\n";
-               $out .= '    ' . Xml::element( 'id', array(), strval( $row->page_id ) ) . "\n";
-               if ( $row->page_is_redirect ) {
-                       $page = WikiPage::factory( $title );
-                       $redirect = $page->getRedirectTarget();
-                       if ( $redirect instanceof Title && $redirect->isValidRedirectTarget() ) {
-                               $out .= '    ';
-                               $out .= Xml::element( 'redirect', array( 'title' => self::canonicalTitle( $redirect ) ) );
-                               $out .= "\n";
-                       }
-               }
-
-               if ( $row->page_restrictions != '' ) {
-                       $out .= '    ' . Xml::element( 'restrictions', array(),
-                               strval( $row->page_restrictions ) ) . "\n";
-               }
-
-               Hooks::run( 'XmlDumpWriterOpenPage', array( $this, &$out, $row, $title ) );
-
-               return $out;
-       }
-
-       /**
-        * Closes a "<page>" section on the output stream.
-        *
-        * @access private
-        * @return string
-        */
-       function closePage() {
-               return "  </page>\n";
-       }
-
-       /**
-        * Dumps a "<revision>" section on the output stream, with
-        * data filled in from the given database row.
-        *
-        * @param object $row
-        * @return string
-        * @access private
-        */
-       function writeRevision( $row ) {
-
-               $out = "    <revision>\n";
-               $out .= "      " . Xml::element( 'id', null, strval( $row->rev_id ) ) . "\n";
-               if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
-                       $out .= "      " . Xml::element( 'parentid', null, strval( $row->rev_parent_id ) ) . "\n";
-               }
-
-               $out .= $this->writeTimestamp( $row->rev_timestamp );
-
-               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_USER ) ) {
-                       $out .= "      " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
-               } else {
-                       $out .= $this->writeContributor( $row->rev_user, $row->rev_user_text );
-               }
-
-               if ( isset( $row->rev_minor_edit ) && $row->rev_minor_edit ) {
-                       $out .= "      <minor/>\n";
-               }
-               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_COMMENT ) ) {
-                       $out .= "      " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
-               } elseif ( $row->rev_comment != '' ) {
-                       $out .= "      " . Xml::elementClean( 'comment', array(), strval( $row->rev_comment ) ) . "\n";
-               }
-
-               if ( isset( $row->rev_content_model ) && !is_null( $row->rev_content_model ) ) {
-                       $content_model = strval( $row->rev_content_model );
-               } else {
-                       // probably using $wgContentHandlerUseDB = false;
-                       $title = Title::makeTitle( $row->page_namespace, $row->page_title );
-                       $content_model = ContentHandler::getDefaultModelFor( $title );
-               }
-
-               $content_handler = ContentHandler::getForModelID( $content_model );
-
-               if ( isset( $row->rev_content_format ) && !is_null( $row->rev_content_format ) ) {
-                       $content_format = strval( $row->rev_content_format );
-               } else {
-                       // probably using $wgContentHandlerUseDB = false;
-                       $content_format = $content_handler->getDefaultFormat();
-               }
-
-               $out .= "      " . Xml::element( 'model', null, strval( $content_model ) ) . "\n";
-               $out .= "      " . Xml::element( 'format', null, strval( $content_format ) ) . "\n";
-
-               $text = '';
-               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_TEXT ) ) {
-                       $out .= "      " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
-               } elseif ( isset( $row->old_text ) ) {
-                       // Raw text from the database may have invalid chars
-                       $text = strval( Revision::getRevisionText( $row ) );
-                       $text = $content_handler->exportTransform( $text, $content_format );
-                       $out .= "      " . Xml::elementClean( 'text',
-                               array( 'xml:space' => 'preserve', 'bytes' => intval( $row->rev_len ) ),
-                               strval( $text ) ) . "\n";
-               } else {
-                       // Stub output
-                       $out .= "      " . Xml::element( 'text',
-                               array( 'id' => $row->rev_text_id, 'bytes' => intval( $row->rev_len ) ),
-                               "" ) . "\n";
-               }
-
-               if ( isset( $row->rev_sha1 )
-                       && $row->rev_sha1
-                       && !( $row->rev_deleted & Revision::DELETED_TEXT )
-               ) {
-                       $out .= "      " . Xml::element( 'sha1', null, strval( $row->rev_sha1 ) ) . "\n";
-               } else {
-                       $out .= "      <sha1/>\n";
-               }
-
-               Hooks::run( 'XmlDumpWriterWriteRevision', array( &$this, &$out, $row, $text ) );
-
-               $out .= "    </revision>\n";
-
-               return $out;
-       }
-
-       /**
-        * Dumps a "<logitem>" section on the output stream, with
-        * data filled in from the given database row.
-        *
-        * @param object $row
-        * @return string
-        * @access private
-        */
-       function writeLogItem( $row ) {
-
-               $out = "  <logitem>\n";
-               $out .= "    " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n";
-
-               $out .= $this->writeTimestamp( $row->log_timestamp, "    " );
-
-               if ( $row->log_deleted & LogPage::DELETED_USER ) {
-                       $out .= "    " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
-               } else {
-                       $out .= $this->writeContributor( $row->log_user, $row->user_name, "    " );
-               }
-
-               if ( $row->log_deleted & LogPage::DELETED_COMMENT ) {
-                       $out .= "    " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
-               } elseif ( $row->log_comment != '' ) {
-                       $out .= "    " . Xml::elementClean( 'comment', null, strval( $row->log_comment ) ) . "\n";
-               }
-
-               $out .= "    " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n";
-               $out .= "    " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n";
-
-               if ( $row->log_deleted & LogPage::DELETED_ACTION ) {
-                       $out .= "    " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
-               } else {
-                       $title = Title::makeTitle( $row->log_namespace, $row->log_title );
-                       $out .= "    " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n";
-                       $out .= "    " . Xml::elementClean( 'params',
-                               array( 'xml:space' => 'preserve' ),
-                               strval( $row->log_params ) ) . "\n";
-               }
-
-               $out .= "  </logitem>\n";
-
-               return $out;
-       }
-
-       /**
-        * @param string $timestamp
-        * @param string $indent Default to six spaces
-        * @return string
-        */
-       function writeTimestamp( $timestamp, $indent = "      " ) {
-               $ts = wfTimestamp( TS_ISO_8601, $timestamp );
-               return $indent . Xml::element( 'timestamp', null, $ts ) . "\n";
-       }
-
-       /**
-        * @param int $id
-        * @param string $text
-        * @param string $indent Default to six spaces
-        * @return string
-        */
-       function writeContributor( $id, $text, $indent = "      " ) {
-               $out = $indent . "<contributor>\n";
-               if ( $id || !IP::isValid( $text ) ) {
-                       $out .= $indent . "  " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n";
-                       $out .= $indent . "  " . Xml::element( 'id', null, strval( $id ) ) . "\n";
-               } else {
-                       $out .= $indent . "  " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n";
-               }
-               $out .= $indent . "</contributor>\n";
-               return $out;
-       }
-
-       /**
-        * Warning! This data is potentially inconsistent. :(
-        * @param object $row
-        * @param bool $dumpContents
-        * @return string
-        */
-       function writeUploads( $row, $dumpContents = false ) {
-               if ( $row->page_namespace == NS_FILE ) {
-                       $img = wfLocalFile( $row->page_title );
-                       if ( $img && $img->exists() ) {
-                               $out = '';
-                               foreach ( array_reverse( $img->getHistory() ) as $ver ) {
-                                       $out .= $this->writeUpload( $ver, $dumpContents );
-                               }
-                               $out .= $this->writeUpload( $img, $dumpContents );
-                               return $out;
-                       }
-               }
-               return '';
-       }
-
-       /**
-        * @param File $file
-        * @param bool $dumpContents
-        * @return string
-        */
-       function writeUpload( $file, $dumpContents = false ) {
-               if ( $file->isOld() ) {
-                       $archiveName = "      " .
-                               Xml::element( 'archivename', null, $file->getArchiveName() ) . "\n";
-               } else {
-                       $archiveName = '';
-               }
-               if ( $dumpContents ) {
-                       $be = $file->getRepo()->getBackend();
-                       # Dump file as base64
-                       # Uses only XML-safe characters, so does not need escaping
-                       # @todo Too bad this loads the contents into memory (script might swap)
-                       $contents = '      <contents encoding="base64">' .
-                               chunk_split( base64_encode(
-                                       $be->getFileContents( array( 'src' => $file->getPath() ) ) ) ) .
-                               "      </contents>\n";
-               } else {
-                       $contents = '';
-               }
-               if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
-                       $comment = Xml::element( 'comment', array( 'deleted' => 'deleted' ) );
-               } else {
-                       $comment = Xml::elementClean( 'comment', null, $file->getDescription() );
-               }
-               return "    <upload>\n" .
-                       $this->writeTimestamp( $file->getTimestamp() ) .
-                       $this->writeContributor( $file->getUser( 'id' ), $file->getUser( 'text' ) ) .
-                       "      " . $comment . "\n" .
-                       "      " . Xml::element( 'filename', null, $file->getName() ) . "\n" .
-                       $archiveName .
-                       "      " . Xml::element( 'src', null, $file->getCanonicalURL() ) . "\n" .
-                       "      " . Xml::element( 'size', null, $file->getSize() ) . "\n" .
-                       "      " . Xml::element( 'sha1base36', null, $file->getSha1() ) . "\n" .
-                       "      " . Xml::element( 'rel', null, $file->getRel() ) . "\n" .
-                       $contents .
-                       "    </upload>\n";
-       }
-
-       /**
-        * Return prefixed text form of title, but using the content language's
-        * canonical namespace. This skips any special-casing such as gendered
-        * user namespaces -- which while useful, are not yet listed in the
-        * XML "<siteinfo>" data so are unsafe in export.
-        *
-        * @param Title $title
-        * @return string
-        * @since 1.18
-        */
-       public static function canonicalTitle( Title $title ) {
-               if ( $title->isExternal() ) {
-                       return $title->getPrefixedText();
-               }
-
-               global $wgContLang;
-               $prefix = $wgContLang->getFormattedNsText( $title->getNamespace() );
-
-               if ( $prefix !== '' ) {
-                       $prefix .= ':';
-               }
-
-               return $prefix . $title->getText();
-       }
-}
-
-/**
- * Base class for output stream; prints to stdout or buffer or wherever.
- * @ingroup Dump
- */
-class DumpOutput {
-
-       /**
-        * @param string $string
-        */
-       function writeOpenStream( $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeCloseStream( $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * @param object $page
-        * @param string $string
-        */
-       function writeOpenPage( $page, $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeClosePage( $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeRevision( $rev, $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeLogItem( $rev, $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * Override to write to a different stream type.
-        * @param string $string
-        * @return bool
-        */
-       function write( $string ) {
-               print $string;
-       }
-
-       /**
-        * Close the old file, move it to a specified name,
-        * and reopen new file with the old name. Use this
-        * for writing out a file in multiple pieces
-        * at specified checkpoints (e.g. every n hours).
-        * @param string|array $newname File name. May be a string or an array with one element
-        */
-       function closeRenameAndReopen( $newname ) {
-       }
-
-       /**
-        * Close the old file, and move it to a specified name.
-        * Use this for the last piece of a file written out
-        * at specified checkpoints (e.g. every n hours).
-        * @param string|array $newname File name. May be a string or an array with one element
-        * @param bool $open If true, a new file with the old filename will be opened
-        *   again for writing (default: false)
-        */
-       function closeAndRename( $newname, $open = false ) {
-       }
-
-       /**
-        * Returns the name of the file or files which are
-        * being written to, if there are any.
-        * @return null
-        */
-       function getFilenames() {
-               return null;
-       }
-}
-
-/**
- * Stream outputter to send data to a file.
- * @ingroup Dump
- */
-class DumpFileOutput extends DumpOutput {
-       protected $handle = false, $filename;
-
-       /**
-        * @param string $file
-        */
-       function __construct( $file ) {
-               $this->handle = fopen( $file, "wt" );
-               $this->filename = $file;
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeCloseStream( $string ) {
-               parent::writeCloseStream( $string );
-               if ( $this->handle ) {
-                       fclose( $this->handle );
-                       $this->handle = false;
-               }
-       }
-
-       /**
-        * @param string $string
-        */
-       function write( $string ) {
-               fputs( $this->handle, $string );
-       }
-
-       /**
-        * @param string $newname
-        */
-       function closeRenameAndReopen( $newname ) {
-               $this->closeAndRename( $newname, true );
-       }
-
-       /**
-        * @param string $newname
-        * @throws MWException
-        */
-       function renameOrException( $newname ) {
-                       if ( !rename( $this->filename, $newname ) ) {
-                               throw new MWException( __METHOD__ . ": rename of file {$this->filename} to $newname failed\n" );
-                       }
-       }
-
-       /**
-        * @param array $newname
-        * @return string
-        * @throws MWException
-        */
-       function checkRenameArgCount( $newname ) {
-               if ( is_array( $newname ) ) {
-                       if ( count( $newname ) > 1 ) {
-                               throw new MWException( __METHOD__ . ": passed multiple arguments for rename of single file\n" );
-                       } else {
-                               $newname = $newname[0];
-                       }
-               }
-               return $newname;
-       }
-
-       /**
-        * @param string $newname
-        * @param bool $open
-        */
-       function closeAndRename( $newname, $open = false ) {
-               $newname = $this->checkRenameArgCount( $newname );
-               if ( $newname ) {
-                       if ( $this->handle ) {
-                               fclose( $this->handle );
-                               $this->handle = false;
-                       }
-                       $this->renameOrException( $newname );
-                       if ( $open ) {
-                               $this->handle = fopen( $this->filename, "wt" );
-                       }
-               }
-       }
-
-       /**
-        * @return string|null
-        */
-       function getFilenames() {
-               return $this->filename;
-       }
-}
-
-/**
- * Stream outputter to send data to a file via some filter program.
- * Even if compression is available in a library, using a separate
- * program can allow us to make use of a multi-processor system.
- * @ingroup Dump
- */
-class DumpPipeOutput extends DumpFileOutput {
-       protected $command, $filename;
-       protected $procOpenResource = false;
-
-       /**
-        * @param string $command
-        * @param string $file
-        */
-       function __construct( $command, $file = null ) {
-               if ( !is_null( $file ) ) {
-                       $command .= " > " . wfEscapeShellArg( $file );
-               }
-
-               $this->startCommand( $command );
-               $this->command = $command;
-               $this->filename = $file;
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeCloseStream( $string ) {
-               parent::writeCloseStream( $string );
-               if ( $this->procOpenResource ) {
-                       proc_close( $this->procOpenResource );
-                       $this->procOpenResource = false;
-               }
-       }
-
-       /**
-        * @param string $command
-        */
-       function startCommand( $command ) {
-               $spec = array(
-                       0 => array( "pipe", "r" ),
-               );
-               $pipes = array();
-               $this->procOpenResource = proc_open( $command, $spec, $pipes );
-               $this->handle = $pipes[0];
-       }
-
-       /**
-        * @param string $newname
-        */
-       function closeRenameAndReopen( $newname ) {
-               $this->closeAndRename( $newname, true );
-       }
-
-       /**
-        * @param string $newname
-        * @param bool $open
-        */
-       function closeAndRename( $newname, $open = false ) {
-               $newname = $this->checkRenameArgCount( $newname );
-               if ( $newname ) {
-                       if ( $this->handle ) {
-                               fclose( $this->handle );
-                               $this->handle = false;
-                       }
-                       if ( $this->procOpenResource ) {
-                               proc_close( $this->procOpenResource );
-                               $this->procOpenResource = false;
-                       }
-                       $this->renameOrException( $newname );
-                       if ( $open ) {
-                               $command = $this->command;
-                               $command .= " > " . wfEscapeShellArg( $this->filename );
-                               $this->startCommand( $command );
-                       }
-               }
-       }
-}
-
-/**
- * Sends dump output via the gzip compressor.
- * @ingroup Dump
- */
-class DumpGZipOutput extends DumpPipeOutput {
-       /**
-        * @param string $file
-        */
-       function __construct( $file ) {
-               parent::__construct( "gzip", $file );
-       }
-}
-
-/**
- * Sends dump output via the bgzip2 compressor.
- * @ingroup Dump
- */
-class DumpBZip2Output extends DumpPipeOutput {
-       /**
-        * @param string $file
-        */
-       function __construct( $file ) {
-               parent::__construct( "bzip2", $file );
-       }
-}
-
-/**
- * Sends dump output via the p7zip compressor.
- * @ingroup Dump
- */
-class Dump7ZipOutput extends DumpPipeOutput {
-       /**
-        * @param string $file
-        */
-       function __construct( $file ) {
-               $command = $this->setup7zCommand( $file );
-               parent::__construct( $command );
-               $this->filename = $file;
-       }
-
-       /**
-        * @param string $file
-        * @return string
-        */
-       function setup7zCommand( $file ) {
-               $command = "7za a -bd -si -mx=4 " . wfEscapeShellArg( $file );
-               // Suppress annoying useless crap from p7zip
-               // Unfortunately this could suppress real error messages too
-               $command .= ' >' . wfGetNull() . ' 2>&1';
-               return $command;
-       }
-
-       /**
-        * @param string $newname
-        * @param bool $open
-        */
-       function closeAndRename( $newname, $open = false ) {
-               $newname = $this->checkRenameArgCount( $newname );
-               if ( $newname ) {
-                       fclose( $this->handle );
-                       proc_close( $this->procOpenResource );
-                       $this->renameOrException( $newname );
-                       if ( $open ) {
-                               $command = $this->setup7zCommand( $this->filename );
-                               $this->startCommand( $command );
-                       }
-               }
-       }
-}
-
-/**
- * Dump output filter class.
- * This just does output filtering and streaming; XML formatting is done
- * higher up, so be careful in what you do.
- * @ingroup Dump
- */
-class DumpFilter {
-       /**
-        * @var DumpOutput
-        * FIXME will need to be made protected whenever legacy code
-        * is updated.
-        */
-       public $sink;
-
-       /**
-        * @var bool
-        */
-       protected $sendingThisPage;
-
-       /**
-        * @param DumpOutput $sink
-        */
-       function __construct( &$sink ) {
-               $this->sink =& $sink;
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeOpenStream( $string ) {
-               $this->sink->writeOpenStream( $string );
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeCloseStream( $string ) {
-               $this->sink->writeCloseStream( $string );
-       }
-
-       /**
-        * @param object $page
-        * @param string $string
-        */
-       function writeOpenPage( $page, $string ) {
-               $this->sendingThisPage = $this->pass( $page, $string );
-               if ( $this->sendingThisPage ) {
-                       $this->sink->writeOpenPage( $page, $string );
-               }
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeClosePage( $string ) {
-               if ( $this->sendingThisPage ) {
-                       $this->sink->writeClosePage( $string );
-                       $this->sendingThisPage = false;
-               }
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeRevision( $rev, $string ) {
-               if ( $this->sendingThisPage ) {
-                       $this->sink->writeRevision( $rev, $string );
-               }
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeLogItem( $rev, $string ) {
-               $this->sink->writeRevision( $rev, $string );
-       }
-
-       /**
-        * @param string $newname
-        */
-       function closeRenameAndReopen( $newname ) {
-               $this->sink->closeRenameAndReopen( $newname );
-       }
-
-       /**
-        * @param string $newname
-        * @param bool $open
-        */
-       function closeAndRename( $newname, $open = false ) {
-               $this->sink->closeAndRename( $newname, $open );
-       }
-
-       /**
-        * @return array
-        */
-       function getFilenames() {
-               return $this->sink->getFilenames();
-       }
-
-       /**
-        * Override for page-based filter types.
-        * @param object $page
-        * @return bool
-        */
-       function pass( $page ) {
-               return true;
-       }
-}
-
-/**
- * Simple dump output filter to exclude all talk pages.
- * @ingroup Dump
- */
-class DumpNotalkFilter extends DumpFilter {
-       /**
-        * @param object $page
-        * @return bool
-        */
-       function pass( $page ) {
-               return !MWNamespace::isTalk( $page->page_namespace );
-       }
-}
-
-/**
- * Dump output filter to include or exclude pages in a given set of namespaces.
- * @ingroup Dump
- */
-class DumpNamespaceFilter extends DumpFilter {
-       /** @var bool */
-       public $invert = false;
-
-       /** @var array */
-       public $namespaces = array();
-
-       /**
-        * @param DumpOutput $sink
-        * @param array $param
-        * @throws MWException
-        */
-       function __construct( &$sink, $param ) {
-               parent::__construct( $sink );
-
-               $constants = array(
-                       "NS_MAIN"           => NS_MAIN,
-                       "NS_TALK"           => NS_TALK,
-                       "NS_USER"           => NS_USER,
-                       "NS_USER_TALK"      => NS_USER_TALK,
-                       "NS_PROJECT"        => NS_PROJECT,
-                       "NS_PROJECT_TALK"   => NS_PROJECT_TALK,
-                       "NS_FILE"           => NS_FILE,
-                       "NS_FILE_TALK"      => NS_FILE_TALK,
-                       "NS_IMAGE"          => NS_IMAGE, // NS_IMAGE is an alias for NS_FILE
-                       "NS_IMAGE_TALK"     => NS_IMAGE_TALK,
-                       "NS_MEDIAWIKI"      => NS_MEDIAWIKI,
-                       "NS_MEDIAWIKI_TALK" => NS_MEDIAWIKI_TALK,
-                       "NS_TEMPLATE"       => NS_TEMPLATE,
-                       "NS_TEMPLATE_TALK"  => NS_TEMPLATE_TALK,
-                       "NS_HELP"           => NS_HELP,
-                       "NS_HELP_TALK"      => NS_HELP_TALK,
-                       "NS_CATEGORY"       => NS_CATEGORY,
-                       "NS_CATEGORY_TALK"  => NS_CATEGORY_TALK );
-
-               if ( $param { 0 } == '!' ) {
-                       $this->invert = true;
-                       $param = substr( $param, 1 );
-               }
-
-               foreach ( explode( ',', $param ) as $key ) {
-                       $key = trim( $key );
-                       if ( isset( $constants[$key] ) ) {
-                               $ns = $constants[$key];
-                               $this->namespaces[$ns] = true;
-                       } elseif ( is_numeric( $key ) ) {
-                               $ns = intval( $key );
-                               $this->namespaces[$ns] = true;
-                       } else {
-                               throw new MWException( "Unrecognized namespace key '$key'\n" );
-                       }
-               }
-       }
-
-       /**
-        * @param object $page
-        * @return bool
-        */
-       function pass( $page ) {
-               $match = isset( $this->namespaces[$page->page_namespace] );
-               return $this->invert xor $match;
-       }
-}
-
-/**
- * Dump output filter to include only the last revision in each page sequence.
- * @ingroup Dump
- */
-class DumpLatestFilter extends DumpFilter {
-       public $page;
-
-       public $pageString;
-
-       public $rev;
-
-       public $revString;
-
-       /**
-        * @param object $page
-        * @param string $string
-        */
-       function writeOpenPage( $page, $string ) {
-               $this->page = $page;
-               $this->pageString = $string;
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeClosePage( $string ) {
-               if ( $this->rev ) {
-                       $this->sink->writeOpenPage( $this->page, $this->pageString );
-                       $this->sink->writeRevision( $this->rev, $this->revString );
-                       $this->sink->writeClosePage( $string );
-               }
-               $this->rev = null;
-               $this->revString = null;
-               $this->page = null;
-               $this->pageString = null;
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeRevision( $rev, $string ) {
-               if ( $rev->rev_id == $this->page->page_latest ) {
-                       $this->rev = $rev;
-                       $this->revString = $string;
-               }
-       }
-}
-
-/**
- * Base class for output stream; prints to stdout or buffer or wherever.
- * @ingroup Dump
- */
-class DumpMultiWriter {
-
-       /**
-        * @param array $sinks
-        */
-       function __construct( $sinks ) {
-               $this->sinks = $sinks;
-               $this->count = count( $sinks );
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeOpenStream( $string ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->writeOpenStream( $string );
-               }
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeCloseStream( $string ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->writeCloseStream( $string );
-               }
-       }
-
-       /**
-        * @param object $page
-        * @param string $string
-        */
-       function writeOpenPage( $page, $string ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->writeOpenPage( $page, $string );
-               }
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeClosePage( $string ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->writeClosePage( $string );
-               }
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeRevision( $rev, $string ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->writeRevision( $rev, $string );
-               }
-       }
-
-       /**
-        * @param array $newnames
-        */
-       function closeRenameAndReopen( $newnames ) {
-               $this->closeAndRename( $newnames, true );
-       }
-
-       /**
-        * @param array $newnames
-        * @param bool $open
-        */
-       function closeAndRename( $newnames, $open = false ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->closeAndRename( $newnames[$i], $open );
-               }
-       }
-
-       /**
-        * @return array
-        */
-       function getFilenames() {
-               $filenames = array();
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $filenames[] = $this->sinks[$i]->getFilenames();
-               }
-               return $filenames;
-       }
-}
index 49b9786..89ff19a 100644 (file)
@@ -1253,6 +1253,8 @@ class ApiMain extends ApiBase {
                $module = $this->setupModule();
                $this->mModule = $module;
 
+               $this->setRequestExpectations( $module );
+
                $this->checkExecutePermissions( $module );
 
                if ( !$this->checkMaxLag( $module, $params ) ) {
@@ -1284,6 +1286,24 @@ class ApiMain extends ApiBase {
                }
        }
 
+       /**
+        * Set database connection, query, and write expectations given this module request
+        * @param ApiBase $module
+        */
+       protected function setRequestExpectations( ApiBase $module ) {
+               $limits = $this->getConfig()->get( 'TrxProfilerLimits' );
+               $trxProfiler = Profiler::instance()->getTransactionProfiler();
+               if ( $this->getRequest()->wasPosted() ) {
+                       if ( $module->isWriteMode() ) {
+                               $trxProfiler->setExpectations( $limits['POST'], __METHOD__ );
+                       } else {
+                               $trxProfiler->setExpectations( $limits['POST-nonwrite'], __METHOD__ );
+                       }
+               } else {
+                       $trxProfiler->setExpectations( $limits['GET'], __METHOD__ );
+               }
+       }
+
        /**
         * Log the preceding request
         * @param float $time Time in seconds
index cf45cae..1497e34 100644 (file)
@@ -10,7 +10,8 @@
                        "Hwangjy9",
                        "Kurousagi",
                        "Revi",
-                       "Yearning"
+                       "Yearning",
+                       "Priviet"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|설명문서]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 메일링 리스트]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API 공지 사항] * [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 버그 및 요청] </div>\n<strong>상태:</strong> 이 페이지에 표시된 모든 기능은 정상적으로 작동하지만, API는 여전히 활발하게 개발되고 있으며, 언제든지 변경될 수 있습니다. 업데이트 정보를 받아보려면 [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce 메일링 리스트]를 구독하십시오.\n\n<strong>잘못된 요청:</strong> API에 잘못된 요청이 전송되면 HTTP 헤더에서 \"MediaWiki-API-Error\" 키를 보내고, 헤더 값과 오류 코드가 같게 설정됩니다. 자세한 정보에 대해서는 [[mw:API:Errors_and_warnings|API:오류와 경고]]를 참조하십시오.",
        "apihelp-query+pageswithprop-param-limit": "나타낼 문서의 최대 수입니다.",
        "apihelp-query+pageswithprop-param-dir": "정렬 순서",
        "apihelp-query+prefixsearch-param-search": "문자열 검색",
+       "apihelp-rollback-param-tags": "되돌리기를 적용하기 위해 태그합니다.",
        "apihelp-unblock-description": "사용자를 차단 해제합니다.",
        "apihelp-rawfm-description": "출력 데이터, 디버깅 요소를 포함, (HTML에 포함된)JSON형식.",
        "api-help-title": "미디어위키 API 도움말",
index c334d38..e7bfbe1 100644 (file)
@@ -5,6 +5,7 @@
                        "Macofe"
                ]
        },
+       "apihelp-main-param-curtimestamp": "Den aktuellen Zäitstempel an d'Resultat integréieren.",
        "apihelp-block-description": "E Benotzer spären.",
        "apihelp-block-param-user": "Benotzernumm, IP-Adress oder IP-Beräich deen Dir späre wëllt.",
        "apihelp-block-param-reason": "Grond fir ze spären.",
@@ -13,6 +14,8 @@
        "apihelp-block-param-reblock": "Wann de Benotzer scho gespaart ass, déi aktuell Spär iwwerschreiwen.",
        "apihelp-block-param-watchuser": "Dem Benotzer oder der IP-Adress hier Benotzer- an Diskussiouns-Säiten iwwerwaachen.",
        "apihelp-compare-param-fromtitle": "Éischten Titel fir ze vergläichen.",
+       "apihelp-compare-param-fromrev": "Éischt Versioun fir ze vergläichen.",
+       "apihelp-compare-param-totitle": "Zweeten Titel fir ze vergläichen.",
        "apihelp-compare-param-torev": "Zweet Versioun fir ze vergläichen.",
        "apihelp-createaccount-description": "En neie Benotzerkont uleeën.",
        "apihelp-createaccount-param-name": "Benotzernumm.",
        "apihelp-createaccount-param-realname": "Richtegen Numm vum Benotzer (fakultativ).",
        "apihelp-delete-description": "Eng Säit läschen.",
        "apihelp-delete-param-watch": "D'Säit op dem aktuelle Benotzer seng Iwwerwaachungslëscht dobäisetzen.",
+       "apihelp-delete-param-unwatch": "D'Säit vun der Iwwerwaachungslëscht vum aktuelle Benotzer erofhuelen.",
        "apihelp-delete-example-simple": "D'<kbd>Main Page</kbd> läschen.",
        "apihelp-disabled-description": "Dëse Modul gouf ausgeschalt.",
        "apihelp-edit-param-sectiontitle": "Den Titel fir en neien Abschnitt.",
        "apihelp-edit-param-text": "Säiteninhalt.",
        "apihelp-edit-param-minor": "Kleng Ännerung.",
+       "apihelp-edit-param-notminor": "Keng kleng Ännerung",
        "apihelp-edit-param-bot": "Dës Ännerung als Bot-Ännerung markéieren.",
+       "apihelp-edit-param-createonly": "D'Säit net ännere wann et se scho gëtt.",
        "apihelp-edit-param-watch": "D'Säit op dem aktuelle Benotzer seng Iwwerwaachungslëscht dobäisetzen.",
        "apihelp-edit-example-edit": "Eng Säit änneren",
        "apihelp-emailuser-example-email": "Dem Benotzer <kbd>WikiSysop</kbd> eng E-Mail mam Text <kbd>Content</kbd> schécken.",
@@ -33,6 +39,9 @@
        "apihelp-expandtemplates-paramvalue-prop-ttl": "D'Maximalzäit no där den Tëschespäicher vum Resultat net méi valabel si soll.",
        "apihelp-feedcontributions-param-year": "Vum Joer (a virdrun).",
        "apihelp-feedcontributions-param-month": "Vum Mount (a virdrun).",
+       "apihelp-feedcontributions-param-deletedonly": "Nëmme geläscht Kontributioune weisen.",
+       "apihelp-feedcontributions-param-toponly": "Nëmmen Ännerunge weisen déi déi lescht Versioun sinn.",
+       "apihelp-feedrecentchanges-param-days": "Deeg, op déi d'Resultater limitéiert gi sollen",
        "apihelp-feedrecentchanges-param-hideminor": "Kleng Ännerunge verstoppen.",
        "apihelp-feedrecentchanges-param-hidebots": "Ännerunge vu Botte verstoppen.",
        "apihelp-feedrecentchanges-param-hideanons": "Ännerunge vun anonyme Benotzer verstoppen.",
@@ -42,6 +51,7 @@
        "apihelp-feedrecentchanges-param-categories": "Nëmmen Ännerunge vu Säiten aus all dëse Kategorië weisen.",
        "apihelp-feedrecentchanges-param-categories_any": "Nëmmen Ännerunge vu Säiten aus enger vun dëse Kategorië weisen.",
        "apihelp-feedrecentchanges-example-simple": "Rezent Ännerunge weisen",
+       "apihelp-filerevert-param-comment": "Bemierkung eroplueden.",
        "apihelp-help-example-main": "Hëllef fir den Haaptmodul.",
        "apihelp-help-example-recursive": "All Hëllef op enger Säit",
        "apihelp-imagerotate-description": "Eent oder méi Biller dréinen.",
        "apihelp-login-param-name": "Benotzernumm.",
        "apihelp-login-param-password": "Passwuert.",
        "apihelp-login-example-login": "Aloggen.",
+       "apihelp-logout-example-logout": "Den aktuelle Benotzer ausloggen.",
        "apihelp-move-description": "Eng Säit réckelen.",
+       "apihelp-move-param-reason": "Grond fir d'Ëmbenennen.",
        "apihelp-move-param-movetalk": "D'Diskussiounssäit ëmbenennen, wann et se gëtt.",
+       "apihelp-move-param-noredirect": "Keng Viruleedung uleeën.",
        "apihelp-move-param-ignorewarnings": "All Warnungen ignoréieren.",
        "apihelp-options-description": "Astellunge fir den aktuelle Benotzer änneren.\n\nNëmmen Optiounen aus dem Haaptdeel (core) oder aus enger vun den installéierten Erweiderunge, oder Optioune mat Schlësselen déi viragestallt si mat <code>userjs-</code> (geduecht fir mat Benotzer-Scripte benotzt ze ginn), kënnen agestallt ginn.",
        "apihelp-options-param-optionname": "Den Numm vun der Optioun deen op de Wäert vun <var>$1optionvalue</var> gesat gi muss",
index 2494ef1..9567700 100644 (file)
@@ -364,12 +364,24 @@ class ChangesList extends ContextSource {
        }
 
        /**
-        * @param string $s HTML to update
+        * @param string $s Article link will be appended to this string, in place.
         * @param RecentChange $rc
         * @param bool $unpatrolled
         * @param bool $watched
+        * @deprecated since 1.27, use getArticleLink instead.
         */
-       public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
+       public function insertArticleLink( &$s, RecentChange $rc, $unpatrolled, $watched ) {
+               $s .= $this->getArticleLink( $rc, $unpatrolled, $watched );
+       }
+
+       /**
+        * @param RecentChange $rc
+        * @param bool $unpatrolled
+        * @param bool $watched
+        * @return string HTML
+        * @since 1.26
+        */
+       public function getArticleLink( &$rc, $unpatrolled, $watched ) {
                $params = array();
                if ( $rc->getTitle()->isRedirect() ) {
                        $params = array( 'redirect' => 'no' );
@@ -389,23 +401,12 @@ class ChangesList extends ContextSource {
                # RTL/LTR marker
                $articlelink .= $this->getLanguage()->getDirMark();
 
+               # TODO: Deprecate the $s argument, it seems happily unused.
+               $s = '';
                Hooks::run( 'ChangesListInsertArticleLink',
                        array( &$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched ) );
 
-               $s .= " $articlelink";
-       }
-
-       /**
-        * @param RecentChange $rc
-        * @param bool $unpatrolled
-        * @param bool $watched
-        * @return string
-        * @since 1.26
-        */
-       public function getArticleLink( RecentChange $rc, $unpatrolled, $watched ) {
-               $s = '';
-               $this->insertArticleLink( $s, $rc, $unpatrolled, $watched );
-               return $s;
+               return "{$s} {$articlelink}";
        }
 
        /**
index ed374b0..1c49545 100644 (file)
@@ -161,19 +161,22 @@ class EnhancedChangesList extends ChangesList {
        protected function recentChangesBlockGroup( $block ) {
 
                # Add the namespace and title of the block as part of the class
-               $classes = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' );
+               $tableClasses = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' );
                if ( $block[0]->mAttribs['rc_log_type'] ) {
                        # Log entry
-                       $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
+                       $tableClasses[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
                                . $block[0]->mAttribs['rc_log_type'] );
                } else {
-                       $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns'
+                       $tableClasses[] = Sanitizer::escapeClass( 'mw-changeslist-ns'
                                . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] );
                }
-               $classes[] = $block[0]->watched && $block[0]->mAttribs['rc_timestamp'] >= $block[0]->watched
-                       ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
-               $r = Html::openElement( 'table', array( 'class' => $classes ) ) .
-                       Html::openElement( 'tr' );
+               if ( $block[0]->watched
+                       && $block[0]->mAttribs['rc_timestamp'] >= $block[0]->watched
+               ) {
+                       $tableClasses[] = 'mw-changeslist-line-watched';
+               } else {
+                       $tableClasses[] = 'mw-changeslist-line-not-watched';
+               }
 
                # Collate list of users
                $userlinks = array();
@@ -243,61 +246,44 @@ class EnhancedChangesList extends ChangesList {
                        array_push( $users, $text );
                }
 
-               $users = ' <span class="changedby">'
-                       . $this->msg( 'brackets' )->rawParams(
-                               implode( $this->message['semicolon-separator'], $users )
-                       )->escaped() . '</span>';
-
-               $tl = '<span class="mw-collapsible-toggle mw-collapsible-arrow ' .
-                       'mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>';
-               $r .= "<td>$tl</td>";
-
-               # Main line
-               $r .= '<td class="mw-enhanced-rc">' . $this->recentChangesFlags(
-                       $collectedRcFlags
-               );
-
-               # Timestamp
-               $r .= '&#160;' . $block[0]->timestamp . '&#160;</td><td>';
-
                # Article link
+               $articleLink = '';
+               $revDeletedMsg = false;
                if ( $namehidden ) {
-                       $r .= ' <span class="history-deleted">' .
-                               $this->msg( 'rev-deleted-event' )->escaped() . '</span>';
+                       $revDeletedMsg = $this->msg( 'rev-deleted-event' )->escaped();
                } elseif ( $allLogs ) {
-                       $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
+                       $articleLink = $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
                } else {
-                       $this->insertArticleLink( $r, $block[0], $block[0]->unpatrolled, $block[0]->watched );
+                       $articleLink = $this->getArticleLink( $block[0], $block[0]->unpatrolled, $block[0]->watched );
                }
 
-               $r .= $this->getLanguage()->getDirMark();
-
                $queryParams['curid'] = $curId;
 
                # Sub-entries
-               $lines = '';
+               $lines = array();
                foreach ( $block as $i => $rcObj ) {
                        $line = $this->getLineData( $block, $rcObj, $queryParams );
-                       $lines .= $line;
                        if ( !$line ) {
                                // completely ignore this RC entry if we don't want to render it
                                unset( $block[$i] );
                        }
+                       $lines[] = $line;
                }
                // Further down are some assumptions that $block is a 0-indexed array
                // with (count-1) as last key. Let's make sure it is.
                $block = array_values( $block );
-               if ( empty( $block ) ) {
+
+               if ( empty( $block ) || !$lines ) {
                        // if we can't show anything, don't display this block altogether
                        return '';
                }
 
-               $r .= $this->getLogText( $block, $queryParams, $allLogs,
-                       $collectedRcFlags['newpage'], $namehidden );
-
-               $r .= ' <span class="mw-changeslist-separator">. .</span> ';
+               $logText = $this->getLogText( $block, $queryParams, $allLogs,
+                       $collectedRcFlags['newpage'], $namehidden
+               );
 
                # Character difference (does not apply if only log items)
+               $charDifference = false;
                if ( $RCShowChangedSize && !$allLogs ) {
                        $last = 0;
                        $first = count( $block ) - 1;
@@ -309,37 +295,42 @@ class EnhancedChangesList extends ChangesList {
                                $first--;
                        }
                        # Get net change
-                       $chardiff = $this->formatCharacterDifference( $block[$first], $block[$last] );
-
-                       if ( $chardiff == '' ) {
-                               $r .= ' ';
-                       } else {
-                               $r .= ' ' . $chardiff . ' <span class="mw-changeslist-separator">. .</span> ';
-                       }
-               }
-
-               $r .= $users;
-               $r .= $this->numberofWatchingusers( $block[0]->numberofWatchingusers );
-               $r .= '</td></tr>';
-
-               if ( !$lines ) {
-                       // if there are no lines to be rendered (all aborted by hook), don't render the block
-                       return '';
-               }
-
-               $r .= $lines;
-               $r .= "</table>\n";
+                       $charDifference = $this->formatCharacterDifference( $block[$first], $block[$last] );
+               }
+
+               $numberofWatchingusers = $this->numberofWatchingusers( $block[0]->numberofWatchingusers );
+               $usersList = $this->msg( 'brackets' )->rawParams(
+                       implode( $this->message['semicolon-separator'], $users )
+               )->escaped();
+
+               $templateParams = array(
+                       'articleLink' => $articleLink,
+                       'charDifference' => $charDifference,
+                       'collectedRcFlags' => $this->recentChangesFlags( $collectedRcFlags ),
+                       'languageDirMark' => $this->getLanguage()->getDirMark(),
+                       'lines' => $lines,
+                       'logText' => $logText,
+                       'numberofWatchingusers' => $numberofWatchingusers,
+                       'rev-deleted-event' => $revDeletedMsg,
+                       'tableClasses' => $tableClasses,
+                       'timestamp' => $block[0]->timestamp,
+                       'users' => $usersList,
+               );
 
                $this->rcCacheIndex++;
 
-               return $r;
+               $templateParser = new TemplateParser();
+               return $templateParser->processTemplate(
+                       'EnhancedChangesListGroup',
+                       $templateParams
+               );
        }
 
        /**
         * @param RCCacheEntry[] $block
         * @param RCCacheEntry $rcObj
         * @param array $queryParams
-        * @return string
+        * @return array
         * @throws Exception
         * @throws FatalError
         * @throws MWException
@@ -351,9 +342,13 @@ class EnhancedChangesList extends ChangesList {
                $classes = array();
                $type = $rcObj->mAttribs['rc_type'];
                $data = array();
+               $lineParams = array();
 
-               $trClass = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
-                       ? ' class="mw-enhanced-watched"' : '';
+               if ( $rcObj->watched
+                       && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
+               ) {
+                       $lineParams['classes'] = array( 'mw-enhanced-watched' );
+               }
                $separator = ' <span class="mw-changeslist-separator">. .</span> ';
 
                $data['recentChangesFlags'] = array(
@@ -430,27 +425,23 @@ class EnhancedChangesList extends ChangesList {
                        array( $this, &$data, $block, $rcObj ) );
                if ( !$success ) {
                        // skip entry if hook aborted it
-                       return '';
+                       return array();
                }
 
-               $line = '<tr' . $trClass . '><td></td><td class="mw-enhanced-rc">';
                if ( isset( $data['recentChangesFlags'] ) ) {
-                       $line .= $this->recentChangesFlags( $data['recentChangesFlags'] );
+                       $lineParams['recentChangesFlags'] = $this->recentChangesFlags( $data['recentChangesFlags'] );
                        unset( $data['recentChangesFlags'] );
                }
-               $line .= '&#160;</td><td class="mw-enhanced-rc-nested">';
 
                if ( isset( $data['timestampLink'] ) ) {
-                       $line .= '<span class="mw-enhanced-rc-time">' . $data['timestampLink'] . '</span>';
+                       $lineParams['timestampLink'] = $data['timestampLink'];
                        unset( $data['timestampLink'] );
                }
 
                // everything else: makes it easier for extensions to add or remove data
-               $line .= implode( '', $data );
+               $lineParams['data'] = array_values( $data );
 
-               $line .= "</td></tr>\n";
-
-               return $line;
+               return $lineParams;
        }
 
        /**
index 31b355d..58a57a4 100644 (file)
@@ -98,7 +98,7 @@ class OldChangesList extends ChangesList {
                                ),
                                ''
                        );
-                       $this->insertArticleLink( $html, $rc, $unpatrolled, $watched );
+                       $html .= $this->getArticleLink( $rc, $unpatrolled, $watched );
                }
                # Edit/log timestamp
                $this->insertTimestamp( $html, $rc );
index 5443eeb..3cac22a 100644 (file)
@@ -429,6 +429,10 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
+       public function doAtomicSection( $fname, $callback ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
        public function begin( $fname = __METHOD__ ) {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
index 31e2653..abd4e3a 100644 (file)
@@ -3446,6 +3446,21 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
+       final public function doAtomicSection( $fname, $callback ) {
+               if ( !is_callable( $callback ) ) {
+                       throw new UnexpectedValueException( "Invalid callback." );
+               };
+
+               $this->startAtomic( $fname );
+               try {
+                       call_user_func_array( $callback, array( $this, $fname ) );
+               } catch ( Exception $e ) {
+                       $this->rollback( $fname );
+                       throw $e;
+               }
+               $this->endAtomic( $fname );
+       }
+
        /**
         * Begin a transaction. If a transaction is already in progress,
         * that transaction will be committed before the new transaction is started.
index 4674c17..31b2758 100644 (file)
@@ -1268,6 +1268,34 @@ interface IDatabase {
         */
        public function endAtomic( $fname = __METHOD__ );
 
+       /**
+        * Run a callback to do an atomic set of updates for this database
+        *
+        * The $callback takes the following arguments:
+        *   - This database object
+        *   - The value of $fname
+        *
+        * If any exception occurs in the callback, then rollback() will be called and the error will
+        * be re-thrown. It may also be that the rollback itself fails with an exception before then.
+        * In any case, such errors are expected to terminate the request, without any outside caller
+        * attempting to catch errors and commit anyway. Note that any rollback undoes all prior
+        * atomic section and uncommitted updates, which trashes the current request, requiring an
+        * error to be displayed.
+        *
+        * This can be an alternative to explicit startAtomic()/endAtomic() calls.
+        *
+        * @see DatabaseBase::startAtomic
+        * @see DatabaseBase::endAtomic
+        *
+        * @param string $fname Caller name (usually __METHOD__)
+        * @param callable $callback Callback that issues DB updates
+        * @throws DBError
+        * @throws RuntimeException
+        * @throws UnexpectedValueException
+        * @since 1.27
+        */
+       public function doAtomicSection( $fname, $callback );
+
        /**
         * Begin a transaction. If a transaction is already in progress,
         * that transaction will be committed before the new transaction is started.
diff --git a/includes/export/Dump7ZipOutput.php b/includes/export/Dump7ZipOutput.php
new file mode 100644 (file)
index 0000000..ec7a6b2
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Sends dump output via the p7zip compressor.
+ *
+ * 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
+ * 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 Dump
+ */
+class Dump7ZipOutput extends DumpPipeOutput {
+       /**
+        * @param string $file
+        */
+       function __construct( $file ) {
+               $command = $this->setup7zCommand( $file );
+               parent::__construct( $command );
+               $this->filename = $file;
+       }
+
+       /**
+        * @param string $file
+        * @return string
+        */
+       function setup7zCommand( $file ) {
+               $command = "7za a -bd -si -mx=4 " . wfEscapeShellArg( $file );
+               // Suppress annoying useless crap from p7zip
+               // Unfortunately this could suppress real error messages too
+               $command .= ' >' . wfGetNull() . ' 2>&1';
+               return $command;
+       }
+
+       /**
+        * @param string $newname
+        * @param bool $open
+        */
+       function closeAndRename( $newname, $open = false ) {
+               $newname = $this->checkRenameArgCount( $newname );
+               if ( $newname ) {
+                       fclose( $this->handle );
+                       proc_close( $this->procOpenResource );
+                       $this->renameOrException( $newname );
+                       if ( $open ) {
+                               $command = $this->setup7zCommand( $this->filename );
+                               $this->startCommand( $command );
+                       }
+               }
+       }
+}
diff --git a/includes/export/DumpBZip2Output.php b/includes/export/DumpBZip2Output.php
new file mode 100644 (file)
index 0000000..8767b92
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Sends dump output via the bgzip2 compressor.
+ *
+ * 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
+ * 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 Dump
+ */
+class DumpBZip2Output extends DumpPipeOutput {
+       /**
+        * @param string $file
+        */
+       function __construct( $file ) {
+               parent::__construct( "bzip2", $file );
+       }
+}
diff --git a/includes/export/DumpFileOutput.php b/includes/export/DumpFileOutput.php
new file mode 100644 (file)
index 0000000..de1c0a5
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Stream outputter to send data to a file.
+ *
+ * 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
+ * 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 Dump
+ */
+class DumpFileOutput extends DumpOutput {
+       protected $handle = false, $filename;
+
+       /**
+        * @param string $file
+        */
+       function __construct( $file ) {
+               $this->handle = fopen( $file, "wt" );
+               $this->filename = $file;
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeCloseStream( $string ) {
+               parent::writeCloseStream( $string );
+               if ( $this->handle ) {
+                       fclose( $this->handle );
+                       $this->handle = false;
+               }
+       }
+
+       /**
+        * @param string $string
+        */
+       function write( $string ) {
+               fputs( $this->handle, $string );
+       }
+
+       /**
+        * @param string $newname
+        */
+       function closeRenameAndReopen( $newname ) {
+               $this->closeAndRename( $newname, true );
+       }
+
+       /**
+        * @param string $newname
+        * @throws MWException
+        */
+       function renameOrException( $newname ) {
+                       if ( !rename( $this->filename, $newname ) ) {
+                               throw new MWException( __METHOD__ . ": rename of file {$this->filename} to $newname failed\n" );
+                       }
+       }
+
+       /**
+        * @param array $newname
+        * @return string
+        * @throws MWException
+        */
+       function checkRenameArgCount( $newname ) {
+               if ( is_array( $newname ) ) {
+                       if ( count( $newname ) > 1 ) {
+                               throw new MWException( __METHOD__ . ": passed multiple arguments for rename of single file\n" );
+                       } else {
+                               $newname = $newname[0];
+                       }
+               }
+               return $newname;
+       }
+
+       /**
+        * @param string $newname
+        * @param bool $open
+        */
+       function closeAndRename( $newname, $open = false ) {
+               $newname = $this->checkRenameArgCount( $newname );
+               if ( $newname ) {
+                       if ( $this->handle ) {
+                               fclose( $this->handle );
+                               $this->handle = false;
+                       }
+                       $this->renameOrException( $newname );
+                       if ( $open ) {
+                               $this->handle = fopen( $this->filename, "wt" );
+                       }
+               }
+       }
+
+       /**
+        * @return string|null
+        */
+       function getFilenames() {
+               return $this->filename;
+       }
+}
diff --git a/includes/export/DumpFilter.php b/includes/export/DumpFilter.php
new file mode 100644 (file)
index 0000000..224262d
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Dump output filter class.
+ * 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>
+ * 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 Dump
+ */
+class DumpFilter {
+       /**
+        * @var DumpOutput
+        * FIXME will need to be made protected whenever legacy code
+        * is updated.
+        */
+       public $sink;
+
+       /**
+        * @var bool
+        */
+       protected $sendingThisPage;
+
+       /**
+        * @param DumpOutput $sink
+        */
+       function __construct( &$sink ) {
+               $this->sink =& $sink;
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeOpenStream( $string ) {
+               $this->sink->writeOpenStream( $string );
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeCloseStream( $string ) {
+               $this->sink->writeCloseStream( $string );
+       }
+
+       /**
+        * @param object $page
+        * @param string $string
+        */
+       function writeOpenPage( $page, $string ) {
+               $this->sendingThisPage = $this->pass( $page, $string );
+               if ( $this->sendingThisPage ) {
+                       $this->sink->writeOpenPage( $page, $string );
+               }
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeClosePage( $string ) {
+               if ( $this->sendingThisPage ) {
+                       $this->sink->writeClosePage( $string );
+                       $this->sendingThisPage = false;
+               }
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeRevision( $rev, $string ) {
+               if ( $this->sendingThisPage ) {
+                       $this->sink->writeRevision( $rev, $string );
+               }
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeLogItem( $rev, $string ) {
+               $this->sink->writeRevision( $rev, $string );
+       }
+
+       /**
+        * @param string $newname
+        */
+       function closeRenameAndReopen( $newname ) {
+               $this->sink->closeRenameAndReopen( $newname );
+       }
+
+       /**
+        * @param string $newname
+        * @param bool $open
+        */
+       function closeAndRename( $newname, $open = false ) {
+               $this->sink->closeAndRename( $newname, $open );
+       }
+
+       /**
+        * @return array
+        */
+       function getFilenames() {
+               return $this->sink->getFilenames();
+       }
+
+       /**
+        * Override for page-based filter types.
+        * @param object $page
+        * @return bool
+        */
+       function pass( $page ) {
+               return true;
+       }
+}
diff --git a/includes/export/DumpGZipOutput.php b/includes/export/DumpGZipOutput.php
new file mode 100644 (file)
index 0000000..3f0333e
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Sends dump output via the gzip compressor.
+ *
+ * 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
+ * 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 Dump
+ */
+class DumpGZipOutput extends DumpPipeOutput {
+       /**
+        * @param string $file
+        */
+       function __construct( $file ) {
+               parent::__construct( "gzip", $file );
+       }
+}
diff --git a/includes/export/DumpLatestFilter.php b/includes/export/DumpLatestFilter.php
new file mode 100644 (file)
index 0000000..d21dfa9
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Dump output filter to include only the last revision in each page sequence.
+ *
+ * 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
+ * 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 Dump
+ */
+class DumpLatestFilter extends DumpFilter {
+       public $page;
+
+       public $pageString;
+
+       public $rev;
+
+       public $revString;
+
+       /**
+        * @param object $page
+        * @param string $string
+        */
+       function writeOpenPage( $page, $string ) {
+               $this->page = $page;
+               $this->pageString = $string;
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeClosePage( $string ) {
+               if ( $this->rev ) {
+                       $this->sink->writeOpenPage( $this->page, $this->pageString );
+                       $this->sink->writeRevision( $this->rev, $this->revString );
+                       $this->sink->writeClosePage( $string );
+               }
+               $this->rev = null;
+               $this->revString = null;
+               $this->page = null;
+               $this->pageString = null;
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeRevision( $rev, $string ) {
+               if ( $rev->rev_id == $this->page->page_latest ) {
+                       $this->rev = $rev;
+                       $this->revString = $string;
+               }
+       }
+}
diff --git a/includes/export/DumpMultiWriter.php b/includes/export/DumpMultiWriter.php
new file mode 100644 (file)
index 0000000..2f5a782
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+/**
+ * Base class for output stream; prints to stdout or buffer or wherever.
+ *
+ * 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
+ * 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 Dump
+ */
+class DumpMultiWriter {
+
+       /**
+        * @param array $sinks
+        */
+       function __construct( $sinks ) {
+               $this->sinks = $sinks;
+               $this->count = count( $sinks );
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeOpenStream( $string ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->writeOpenStream( $string );
+               }
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeCloseStream( $string ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->writeCloseStream( $string );
+               }
+       }
+
+       /**
+        * @param object $page
+        * @param string $string
+        */
+       function writeOpenPage( $page, $string ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->writeOpenPage( $page, $string );
+               }
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeClosePage( $string ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->writeClosePage( $string );
+               }
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeRevision( $rev, $string ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->writeRevision( $rev, $string );
+               }
+       }
+
+       /**
+        * @param array $newnames
+        */
+       function closeRenameAndReopen( $newnames ) {
+               $this->closeAndRename( $newnames, true );
+       }
+
+       /**
+        * @param array $newnames
+        * @param bool $open
+        */
+       function closeAndRename( $newnames, $open = false ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->closeAndRename( $newnames[$i], $open );
+               }
+       }
+
+       /**
+        * @return array
+        */
+       function getFilenames() {
+               $filenames = array();
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $filenames[] = $this->sinks[$i]->getFilenames();
+               }
+               return $filenames;
+       }
+}
diff --git a/includes/export/DumpNamespaceFilter.php b/includes/export/DumpNamespaceFilter.php
new file mode 100644 (file)
index 0000000..c7d1b2e
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Dump output filter to include or exclude pages in a given set of namespaces.
+ *
+ * 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
+ * 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 Dump
+ */
+class DumpNamespaceFilter extends DumpFilter {
+       /** @var bool */
+       public $invert = false;
+
+       /** @var array */
+       public $namespaces = array();
+
+       /**
+        * @param DumpOutput $sink
+        * @param array $param
+        * @throws MWException
+        */
+       function __construct( &$sink, $param ) {
+               parent::__construct( $sink );
+
+               $constants = array(
+                       "NS_MAIN"           => NS_MAIN,
+                       "NS_TALK"           => NS_TALK,
+                       "NS_USER"           => NS_USER,
+                       "NS_USER_TALK"      => NS_USER_TALK,
+                       "NS_PROJECT"        => NS_PROJECT,
+                       "NS_PROJECT_TALK"   => NS_PROJECT_TALK,
+                       "NS_FILE"           => NS_FILE,
+                       "NS_FILE_TALK"      => NS_FILE_TALK,
+                       "NS_IMAGE"          => NS_IMAGE, // NS_IMAGE is an alias for NS_FILE
+                       "NS_IMAGE_TALK"     => NS_IMAGE_TALK,
+                       "NS_MEDIAWIKI"      => NS_MEDIAWIKI,
+                       "NS_MEDIAWIKI_TALK" => NS_MEDIAWIKI_TALK,
+                       "NS_TEMPLATE"       => NS_TEMPLATE,
+                       "NS_TEMPLATE_TALK"  => NS_TEMPLATE_TALK,
+                       "NS_HELP"           => NS_HELP,
+                       "NS_HELP_TALK"      => NS_HELP_TALK,
+                       "NS_CATEGORY"       => NS_CATEGORY,
+                       "NS_CATEGORY_TALK"  => NS_CATEGORY_TALK );
+
+               if ( $param { 0 } == '!' ) {
+                       $this->invert = true;
+                       $param = substr( $param, 1 );
+               }
+
+               foreach ( explode( ',', $param ) as $key ) {
+                       $key = trim( $key );
+                       if ( isset( $constants[$key] ) ) {
+                               $ns = $constants[$key];
+                               $this->namespaces[$ns] = true;
+                       } elseif ( is_numeric( $key ) ) {
+                               $ns = intval( $key );
+                               $this->namespaces[$ns] = true;
+                       } else {
+                               throw new MWException( "Unrecognized namespace key '$key'\n" );
+                       }
+               }
+       }
+
+       /**
+        * @param object $page
+        * @return bool
+        */
+       function pass( $page ) {
+               $match = isset( $this->namespaces[$page->page_namespace] );
+               return $this->invert xor $match;
+       }
+}
diff --git a/includes/export/DumpNotalkFilter.php b/includes/export/DumpNotalkFilter.php
new file mode 100644 (file)
index 0000000..9974d67
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Simple dump output filter to exclude all talk pages.
+ *
+ * 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
+ * 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 Dump
+ */
+class DumpNotalkFilter extends DumpFilter {
+       /**
+        * @param object $page
+        * @return bool
+        */
+       function pass( $page ) {
+               return !MWNamespace::isTalk( $page->page_namespace );
+       }
+}
diff --git a/includes/export/DumpOutput.php b/includes/export/DumpOutput.php
new file mode 100644 (file)
index 0000000..bdcaf35
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Base class for output stream; prints to stdout or buffer or wherever.
+ *
+ * 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
+ * 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 Dump
+ */
+class DumpOutput {
+
+       /**
+        * @param string $string
+        */
+       function writeOpenStream( $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeCloseStream( $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * @param object $page
+        * @param string $string
+        */
+       function writeOpenPage( $page, $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeClosePage( $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeRevision( $rev, $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeLogItem( $rev, $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * Override to write to a different stream type.
+        * @param string $string
+        * @return bool
+        */
+       function write( $string ) {
+               print $string;
+       }
+
+       /**
+        * Close the old file, move it to a specified name,
+        * and reopen new file with the old name. Use this
+        * for writing out a file in multiple pieces
+        * at specified checkpoints (e.g. every n hours).
+        * @param string|array $newname File name. May be a string or an array with one element
+        */
+       function closeRenameAndReopen( $newname ) {
+       }
+
+       /**
+        * Close the old file, and move it to a specified name.
+        * Use this for the last piece of a file written out
+        * at specified checkpoints (e.g. every n hours).
+        * @param string|array $newname File name. May be a string or an array with one element
+        * @param bool $open If true, a new file with the old filename will be opened
+        *   again for writing (default: false)
+        */
+       function closeAndRename( $newname, $open = false ) {
+       }
+
+       /**
+        * Returns the name of the file or files which are
+        * being written to, if there are any.
+        * @return null
+        */
+       function getFilenames() {
+               return null;
+       }
+}
diff --git a/includes/export/DumpPipeOutput.php b/includes/export/DumpPipeOutput.php
new file mode 100644 (file)
index 0000000..b4ad672
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Stream outputter to send data to a file via some filter program.
+ * 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>
+ * 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 Dump
+ */
+class DumpPipeOutput extends DumpFileOutput {
+       protected $command, $filename;
+       protected $procOpenResource = false;
+
+       /**
+        * @param string $command
+        * @param string $file
+        */
+       function __construct( $command, $file = null ) {
+               if ( !is_null( $file ) ) {
+                       $command .= " > " . wfEscapeShellArg( $file );
+               }
+
+               $this->startCommand( $command );
+               $this->command = $command;
+               $this->filename = $file;
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeCloseStream( $string ) {
+               parent::writeCloseStream( $string );
+               if ( $this->procOpenResource ) {
+                       proc_close( $this->procOpenResource );
+                       $this->procOpenResource = false;
+               }
+       }
+
+       /**
+        * @param string $command
+        */
+       function startCommand( $command ) {
+               $spec = array(
+                       0 => array( "pipe", "r" ),
+               );
+               $pipes = array();
+               $this->procOpenResource = proc_open( $command, $spec, $pipes );
+               $this->handle = $pipes[0];
+       }
+
+       /**
+        * @param string $newname
+        */
+       function closeRenameAndReopen( $newname ) {
+               $this->closeAndRename( $newname, true );
+       }
+
+       /**
+        * @param string $newname
+        * @param bool $open
+        */
+       function closeAndRename( $newname, $open = false ) {
+               $newname = $this->checkRenameArgCount( $newname );
+               if ( $newname ) {
+                       if ( $this->handle ) {
+                               fclose( $this->handle );
+                               $this->handle = false;
+                       }
+                       if ( $this->procOpenResource ) {
+                               proc_close( $this->procOpenResource );
+                               $this->procOpenResource = false;
+                       }
+                       $this->renameOrException( $newname );
+                       if ( $open ) {
+                               $command = $this->command;
+                               $command .= " > " . wfEscapeShellArg( $this->filename );
+                               $this->startCommand( $command );
+                       }
+               }
+       }
+}
diff --git a/includes/export/WikiExporter.php b/includes/export/WikiExporter.php
new file mode 100644 (file)
index 0000000..a24418c
--- /dev/null
@@ -0,0 +1,469 @@
+<?php
+/**
+ * Base class for exporting
+ *
+ * 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
+ * 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
+ */
+
+/**
+ * @defgroup Dump Dump
+ */
+
+/**
+ * @ingroup SpecialPage Dump
+ */
+class WikiExporter {
+       /** @var bool Return distinct author list (when not returning full history) */
+       public $list_authors = false;
+
+       /** @var bool */
+       public $dumpUploads = false;
+
+       /** @var bool */
+       public $dumpUploadFileContents = false;
+
+       /** @var string */
+       public $author_list = "";
+
+       const FULL = 1;
+       const CURRENT = 2;
+       const STABLE = 4; // extension defined
+       const LOGS = 8;
+       const RANGE = 16;
+
+       const BUFFER = 0;
+       const STREAM = 1;
+
+       const TEXT = 0;
+       const STUB = 1;
+
+       /** @var int */
+       public $buffer;
+
+       /** @var int */
+       public $text;
+
+       /** @var DumpOutput */
+       public $sink;
+
+       /**
+        * Returns the export schema version.
+        * @return string
+        */
+       public static function schemaVersion() {
+               return "0.10";
+       }
+
+       /**
+        * If using WikiExporter::STREAM to stream a large amount of data,
+        * provide a database connection which is not managed by
+        * LoadBalancer to read from: some history blob types will
+        * make additional queries to pull source data while the
+        * main query is still running.
+        *
+        * @param IDatabase $db
+        * @param int|array $history One of WikiExporter::FULL, WikiExporter::CURRENT,
+        *   WikiExporter::RANGE or WikiExporter::STABLE, or an associative array:
+        *   - offset: non-inclusive offset at which to start the query
+        *   - limit: maximum number of rows to return
+        *   - dir: "asc" or "desc" timestamp order
+        * @param int $buffer One of WikiExporter::BUFFER or WikiExporter::STREAM
+        * @param int $text One of WikiExporter::TEXT or WikiExporter::STUB
+        */
+       function __construct( $db, $history = WikiExporter::CURRENT,
+                       $buffer = WikiExporter::BUFFER, $text = WikiExporter::TEXT ) {
+               $this->db = $db;
+               $this->history = $history;
+               $this->buffer = $buffer;
+               $this->writer = new XmlDumpWriter();
+               $this->sink = new DumpOutput();
+               $this->text = $text;
+       }
+
+       /**
+        * Set the DumpOutput or DumpFilter object which will receive
+        * various row objects and XML output for filtering. Filters
+        * can be chained or used as callbacks.
+        *
+        * @param DumpOutput $sink
+        */
+       public function setOutputSink( &$sink ) {
+               $this->sink =& $sink;
+       }
+
+       public function openStream() {
+               $output = $this->writer->openStream();
+               $this->sink->writeOpenStream( $output );
+       }
+
+       public function closeStream() {
+               $output = $this->writer->closeStream();
+               $this->sink->writeCloseStream( $output );
+       }
+
+       /**
+        * Dumps a series of page and revision records for all pages
+        * in the database, either including complete history or only
+        * the most recent version.
+        */
+       public function allPages() {
+               $this->dumpFrom( '' );
+       }
+
+       /**
+        * Dumps a series of page and revision records for those pages
+        * in the database falling within the page_id range given.
+        * @param int $start Inclusive lower limit (this id is included)
+        * @param int $end Exclusive upper limit (this id is not included)
+        *   If 0, no upper limit.
+        */
+       public function pagesByRange( $start, $end ) {
+               $condition = 'page_id >= ' . intval( $start );
+               if ( $end ) {
+                       $condition .= ' AND page_id < ' . intval( $end );
+               }
+               $this->dumpFrom( $condition );
+       }
+
+       /**
+        * Dumps a series of page and revision records for those pages
+        * in the database with revisions falling within the rev_id range given.
+        * @param int $start Inclusive lower limit (this id is included)
+        * @param int $end Exclusive upper limit (this id is not included)
+        *   If 0, no upper limit.
+        */
+       public function revsByRange( $start, $end ) {
+               $condition = 'rev_id >= ' . intval( $start );
+               if ( $end ) {
+                       $condition .= ' AND rev_id < ' . intval( $end );
+               }
+               $this->dumpFrom( $condition );
+       }
+
+       /**
+        * @param Title $title
+        */
+       public function pageByTitle( $title ) {
+               $this->dumpFrom(
+                       'page_namespace=' . $title->getNamespace() .
+                       ' AND page_title=' . $this->db->addQuotes( $title->getDBkey() ) );
+       }
+
+       /**
+        * @param string $name
+        * @throws MWException
+        */
+       public function pageByName( $name ) {
+               $title = Title::newFromText( $name );
+               if ( is_null( $title ) ) {
+                       throw new MWException( "Can't export invalid title" );
+               } else {
+                       $this->pageByTitle( $title );
+               }
+       }
+
+       /**
+        * @param array $names
+        */
+       public function pagesByName( $names ) {
+               foreach ( $names as $name ) {
+                       $this->pageByName( $name );
+               }
+       }
+
+       public function allLogs() {
+               $this->dumpFrom( '' );
+       }
+
+       /**
+        * @param int $start
+        * @param int $end
+        */
+       public function logsByRange( $start, $end ) {
+               $condition = 'log_id >= ' . intval( $start );
+               if ( $end ) {
+                       $condition .= ' AND log_id < ' . intval( $end );
+               }
+               $this->dumpFrom( $condition );
+       }
+
+       /**
+        * Generates the distinct list of authors of an article
+        * Not called by default (depends on $this->list_authors)
+        * Can be set by Special:Export when not exporting whole history
+        *
+        * @param array $cond
+        */
+       protected function do_list_authors( $cond ) {
+               $this->author_list = "<contributors>";
+               // rev_deleted
+
+               $res = $this->db->select(
+                       array( 'page', 'revision' ),
+                       array( 'DISTINCT rev_user_text', 'rev_user' ),
+                       array(
+                               $this->db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0',
+                               $cond,
+                               'page_id = rev_id',
+                       ),
+                       __METHOD__
+               );
+
+               foreach ( $res as $row ) {
+                       $this->author_list .= "<contributor>" .
+                               "<username>" .
+                               htmlentities( $row->rev_user_text ) .
+                               "</username>" .
+                               "<id>" .
+                               $row->rev_user .
+                               "</id>" .
+                               "</contributor>";
+               }
+               $this->author_list .= "</contributors>";
+       }
+
+       /**
+        * @param string $cond
+        * @throws MWException
+        * @throws Exception
+        */
+       protected function dumpFrom( $cond = '' ) {
+               # For logging dumps...
+               if ( $this->history & self::LOGS ) {
+                       $where = array( 'user_id = log_user' );
+                       # Hide private logs
+                       $hideLogs = LogEventsList::getExcludeClause( $this->db );
+                       if ( $hideLogs ) {
+                               $where[] = $hideLogs;
+                       }
+                       # Add on any caller specified conditions
+                       if ( $cond ) {
+                               $where[] = $cond;
+                       }
+                       # Get logging table name for logging.* clause
+                       $logging = $this->db->tableName( 'logging' );
+
+                       if ( $this->buffer == WikiExporter::STREAM ) {
+                               $prev = $this->db->bufferResults( false );
+                       }
+                       $result = null; // Assuring $result is not undefined, if exception occurs early
+                       try {
+                               $result = $this->db->select( array( 'logging', 'user' ),
+                                       array( "{$logging}.*", 'user_name' ), // grab the user name
+                                       $where,
+                                       __METHOD__,
+                                       array( 'ORDER BY' => 'log_id', 'USE INDEX' => array( 'logging' => 'PRIMARY' ) )
+                               );
+                               $this->outputLogStream( $result );
+                               if ( $this->buffer == WikiExporter::STREAM ) {
+                                       $this->db->bufferResults( $prev );
+                               }
+                       } catch ( Exception $e ) {
+                               // Throwing the exception does not reliably free the resultset, and
+                               // would also leave the connection in unbuffered mode.
+
+                               // Freeing result
+                               try {
+                                       if ( $result ) {
+                                               $result->free();
+                                       }
+                               } catch ( Exception $e2 ) {
+                                       // Already in panic mode -> ignoring $e2 as $e has
+                                       // higher priority
+                               }
+
+                               // Putting database back in previous buffer mode
+                               try {
+                                       if ( $this->buffer == WikiExporter::STREAM ) {
+                                               $this->db->bufferResults( $prev );
+                                       }
+                               } catch ( Exception $e2 ) {
+                                       // Already in panic mode -> ignoring $e2 as $e has
+                                       // higher priority
+                               }
+
+                               // Inform caller about problem
+                               throw $e;
+                       }
+               # For page dumps...
+               } else {
+                       $tables = array( 'page', 'revision' );
+                       $opts = array( 'ORDER BY' => 'page_id ASC' );
+                       $opts['USE INDEX'] = array();
+                       $join = array();
+                       if ( is_array( $this->history ) ) {
+                               # Time offset/limit for all pages/history...
+                               $revJoin = 'page_id=rev_page';
+                               # Set time order
+                               if ( $this->history['dir'] == 'asc' ) {
+                                       $op = '>';
+                                       $opts['ORDER BY'] = 'rev_timestamp ASC';
+                               } else {
+                                       $op = '<';
+                                       $opts['ORDER BY'] = 'rev_timestamp DESC';
+                               }
+                               # Set offset
+                               if ( !empty( $this->history['offset'] ) ) {
+                                       $revJoin .= " AND rev_timestamp $op " .
+                                               $this->db->addQuotes( $this->db->timestamp( $this->history['offset'] ) );
+                               }
+                               $join['revision'] = array( 'INNER JOIN', $revJoin );
+                               # Set query limit
+                               if ( !empty( $this->history['limit'] ) ) {
+                                       $opts['LIMIT'] = intval( $this->history['limit'] );
+                               }
+                       } elseif ( $this->history & WikiExporter::FULL ) {
+                               # Full history dumps...
+                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page' );
+                       } elseif ( $this->history & WikiExporter::CURRENT ) {
+                               # Latest revision dumps...
+                               if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
+                                       $this->do_list_authors( $cond );
+                               }
+                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' );
+                       } elseif ( $this->history & WikiExporter::STABLE ) {
+                               # "Stable" revision dumps...
+                               # Default JOIN, to be overridden...
+                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' );
+                               # One, and only one hook should set this, and return false
+                               if ( Hooks::run( 'WikiExporter::dumpStableQuery', array( &$tables, &$opts, &$join ) ) ) {
+                                       throw new MWException( __METHOD__ . " given invalid history dump type." );
+                               }
+                       } elseif ( $this->history & WikiExporter::RANGE ) {
+                               # Dump of revisions within a specified range
+                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page' );
+                               $opts['ORDER BY'] = array( 'rev_page ASC', 'rev_id ASC' );
+                       } else {
+                               # Unknown history specification parameter?
+                               throw new MWException( __METHOD__ . " given invalid history dump type." );
+                       }
+                       # Query optimization hacks
+                       if ( $cond == '' ) {
+                               $opts[] = 'STRAIGHT_JOIN';
+                               $opts['USE INDEX']['page'] = 'PRIMARY';
+                       }
+                       # Build text join options
+                       if ( $this->text != WikiExporter::STUB ) { // 1-pass
+                               $tables[] = 'text';
+                               $join['text'] = array( 'INNER JOIN', 'rev_text_id=old_id' );
+                       }
+
+                       if ( $this->buffer == WikiExporter::STREAM ) {
+                               $prev = $this->db->bufferResults( false );
+                       }
+
+                       $result = null; // Assuring $result is not undefined, if exception occurs early
+                       try {
+                               Hooks::run( 'ModifyExportQuery',
+                                               array( $this->db, &$tables, &$cond, &$opts, &$join ) );
+
+                               # Do the query!
+                               $result = $this->db->select( $tables, '*', $cond, __METHOD__, $opts, $join );
+                               # Output dump results
+                               $this->outputPageStream( $result );
+
+                               if ( $this->buffer == WikiExporter::STREAM ) {
+                                       $this->db->bufferResults( $prev );
+                               }
+                       } catch ( Exception $e ) {
+                               // Throwing the exception does not reliably free the resultset, and
+                               // would also leave the connection in unbuffered mode.
+
+                               // Freeing result
+                               try {
+                                       if ( $result ) {
+                                               $result->free();
+                                       }
+                               } catch ( Exception $e2 ) {
+                                       // Already in panic mode -> ignoring $e2 as $e has
+                                       // higher priority
+                               }
+
+                               // Putting database back in previous buffer mode
+                               try {
+                                       if ( $this->buffer == WikiExporter::STREAM ) {
+                                               $this->db->bufferResults( $prev );
+                                       }
+                               } catch ( Exception $e2 ) {
+                                       // Already in panic mode -> ignoring $e2 as $e has
+                                       // higher priority
+                               }
+
+                               // Inform caller about problem
+                               throw $e;
+                       }
+               }
+       }
+
+       /**
+        * Runs through a query result set dumping page and revision records.
+        * The result set should be sorted/grouped by page to avoid duplicate
+        * page records in the output.
+        *
+        * Should be safe for
+        * streaming (non-buffered) queries, as long as it was made on a
+        * separate database connection not managed by LoadBalancer; some
+        * blob storage types will make queries to pull source data.
+        *
+        * @param ResultWrapper $resultset
+        */
+       protected function outputPageStream( $resultset ) {
+               $last = null;
+               foreach ( $resultset as $row ) {
+                       if ( $last === null ||
+                               $last->page_namespace != $row->page_namespace ||
+                               $last->page_title != $row->page_title ) {
+                               if ( $last !== null ) {
+                                       $output = '';
+                                       if ( $this->dumpUploads ) {
+                                               $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents );
+                                       }
+                                       $output .= $this->writer->closePage();
+                                       $this->sink->writeClosePage( $output );
+                               }
+                               $output = $this->writer->openPage( $row );
+                               $this->sink->writeOpenPage( $row, $output );
+                               $last = $row;
+                       }
+                       $output = $this->writer->writeRevision( $row );
+                       $this->sink->writeRevision( $row, $output );
+               }
+               if ( $last !== null ) {
+                       $output = '';
+                       if ( $this->dumpUploads ) {
+                               $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents );
+                       }
+                       $output .= $this->author_list;
+                       $output .= $this->writer->closePage();
+                       $this->sink->writeClosePage( $output );
+               }
+       }
+
+       /**
+        * @param ResultWrapper $resultset
+        */
+       protected function outputLogStream( $resultset ) {
+               foreach ( $resultset as $row ) {
+                       $output = $this->writer->writeLogItem( $row );
+                       $this->sink->writeLogItem( $row, $output );
+               }
+       }
+}
diff --git a/includes/export/XmlDumpWriter.php b/includes/export/XmlDumpWriter.php
new file mode 100644 (file)
index 0000000..3bd4c96
--- /dev/null
@@ -0,0 +1,440 @@
+<?php
+/**
+ * XmlDumpWriter
+ *
+ * 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
+ * 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 Dump
+ */
+class XmlDumpWriter {
+       /**
+        * Opens the XML output stream's root "<mediawiki>" element.
+        * This does not include an xml directive, so is safe to include
+        * as a subelement in a larger XML stream. Namespace and XML Schema
+        * references are included.
+        *
+        * Output will be encoded in UTF-8.
+        *
+        * @return string
+        */
+       function openStream() {
+               global $wgLanguageCode;
+               $ver = WikiExporter::schemaVersion();
+               return Xml::element( 'mediawiki', array(
+                       'xmlns'              => "http://www.mediawiki.org/xml/export-$ver/",
+                       'xmlns:xsi'          => "http://www.w3.org/2001/XMLSchema-instance",
+                       /*
+                        * When a new version of the schema is created, it needs staging on mediawiki.org.
+                        * This requires a change in the operations/mediawiki-config git repo.
+                        *
+                        * Create a changeset like https://gerrit.wikimedia.org/r/#/c/149643/ in which
+                        * you copy in the new xsd file.
+                        *
+                        * After it is reviewed, merged and deployed (sync-docroot), the index.html needs purging.
+                        * echo "http://www.mediawiki.org/xml/index.html" | mwscript purgeList.php --wiki=aawiki
+                        */
+                       'xsi:schemaLocation' => "http://www.mediawiki.org/xml/export-$ver/ " .
+                               "http://www.mediawiki.org/xml/export-$ver.xsd",
+                       'version'            => $ver,
+                       'xml:lang'           => $wgLanguageCode ),
+                       null ) .
+                       "\n" .
+                       $this->siteInfo();
+       }
+
+       /**
+        * @return string
+        */
+       function siteInfo() {
+               $info = array(
+                       $this->sitename(),
+                       $this->dbname(),
+                       $this->homelink(),
+                       $this->generator(),
+                       $this->caseSetting(),
+                       $this->namespaces() );
+               return "  <siteinfo>\n    " .
+                       implode( "\n    ", $info ) .
+                       "\n  </siteinfo>\n";
+       }
+
+       /**
+        * @return string
+        */
+       function sitename() {
+               global $wgSitename;
+               return Xml::element( 'sitename', array(), $wgSitename );
+       }
+
+       /**
+        * @return string
+        */
+       function dbname() {
+               global $wgDBname;
+               return Xml::element( 'dbname', array(), $wgDBname );
+       }
+
+       /**
+        * @return string
+        */
+       function generator() {
+               global $wgVersion;
+               return Xml::element( 'generator', array(), "MediaWiki $wgVersion" );
+       }
+
+       /**
+        * @return string
+        */
+       function homelink() {
+               return Xml::element( 'base', array(), Title::newMainPage()->getCanonicalURL() );
+       }
+
+       /**
+        * @return string
+        */
+       function caseSetting() {
+               global $wgCapitalLinks;
+               // "case-insensitive" option is reserved for future
+               $sensitivity = $wgCapitalLinks ? 'first-letter' : 'case-sensitive';
+               return Xml::element( 'case', array(), $sensitivity );
+       }
+
+       /**
+        * @return string
+        */
+       function namespaces() {
+               global $wgContLang;
+               $spaces = "<namespaces>\n";
+               foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
+                       $spaces .= '      ' .
+                               Xml::element( 'namespace',
+                                       array(
+                                               'key' => $ns,
+                                               'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
+                                       ), $title ) . "\n";
+               }
+               $spaces .= "    </namespaces>";
+               return $spaces;
+       }
+
+       /**
+        * Closes the output stream with the closing root element.
+        * Call when finished dumping things.
+        *
+        * @return string
+        */
+       function closeStream() {
+               return "</mediawiki>\n";
+       }
+
+       /**
+        * Opens a "<page>" section on the output stream, with data
+        * from the given database row.
+        *
+        * @param object $row
+        * @return string
+        */
+       public function openPage( $row ) {
+               $out = "  <page>\n";
+               $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+               $out .= '    ' . Xml::elementClean( 'title', array(), self::canonicalTitle( $title ) ) . "\n";
+               $out .= '    ' . Xml::element( 'ns', array(), strval( $row->page_namespace ) ) . "\n";
+               $out .= '    ' . Xml::element( 'id', array(), strval( $row->page_id ) ) . "\n";
+               if ( $row->page_is_redirect ) {
+                       $page = WikiPage::factory( $title );
+                       $redirect = $page->getRedirectTarget();
+                       if ( $redirect instanceof Title && $redirect->isValidRedirectTarget() ) {
+                               $out .= '    ';
+                               $out .= Xml::element( 'redirect', array( 'title' => self::canonicalTitle( $redirect ) ) );
+                               $out .= "\n";
+                       }
+               }
+
+               if ( $row->page_restrictions != '' ) {
+                       $out .= '    ' . Xml::element( 'restrictions', array(),
+                               strval( $row->page_restrictions ) ) . "\n";
+               }
+
+               Hooks::run( 'XmlDumpWriterOpenPage', array( $this, &$out, $row, $title ) );
+
+               return $out;
+       }
+
+       /**
+        * Closes a "<page>" section on the output stream.
+        *
+        * @access private
+        * @return string
+        */
+       function closePage() {
+               return "  </page>\n";
+       }
+
+       /**
+        * Dumps a "<revision>" section on the output stream, with
+        * data filled in from the given database row.
+        *
+        * @param object $row
+        * @return string
+        * @access private
+        */
+       function writeRevision( $row ) {
+
+               $out = "    <revision>\n";
+               $out .= "      " . Xml::element( 'id', null, strval( $row->rev_id ) ) . "\n";
+               if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
+                       $out .= "      " . Xml::element( 'parentid', null, strval( $row->rev_parent_id ) ) . "\n";
+               }
+
+               $out .= $this->writeTimestamp( $row->rev_timestamp );
+
+               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_USER ) ) {
+                       $out .= "      " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
+               } else {
+                       $out .= $this->writeContributor( $row->rev_user, $row->rev_user_text );
+               }
+
+               if ( isset( $row->rev_minor_edit ) && $row->rev_minor_edit ) {
+                       $out .= "      <minor/>\n";
+               }
+               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_COMMENT ) ) {
+                       $out .= "      " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
+               } elseif ( $row->rev_comment != '' ) {
+                       $out .= "      " . Xml::elementClean( 'comment', array(), strval( $row->rev_comment ) ) . "\n";
+               }
+
+               if ( isset( $row->rev_content_model ) && !is_null( $row->rev_content_model ) ) {
+                       $content_model = strval( $row->rev_content_model );
+               } else {
+                       // probably using $wgContentHandlerUseDB = false;
+                       $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+                       $content_model = ContentHandler::getDefaultModelFor( $title );
+               }
+
+               $content_handler = ContentHandler::getForModelID( $content_model );
+
+               if ( isset( $row->rev_content_format ) && !is_null( $row->rev_content_format ) ) {
+                       $content_format = strval( $row->rev_content_format );
+               } else {
+                       // probably using $wgContentHandlerUseDB = false;
+                       $content_format = $content_handler->getDefaultFormat();
+               }
+
+               $out .= "      " . Xml::element( 'model', null, strval( $content_model ) ) . "\n";
+               $out .= "      " . Xml::element( 'format', null, strval( $content_format ) ) . "\n";
+
+               $text = '';
+               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_TEXT ) ) {
+                       $out .= "      " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
+               } elseif ( isset( $row->old_text ) ) {
+                       // Raw text from the database may have invalid chars
+                       $text = strval( Revision::getRevisionText( $row ) );
+                       $text = $content_handler->exportTransform( $text, $content_format );
+                       $out .= "      " . Xml::elementClean( 'text',
+                               array( 'xml:space' => 'preserve', 'bytes' => intval( $row->rev_len ) ),
+                               strval( $text ) ) . "\n";
+               } else {
+                       // Stub output
+                       $out .= "      " . Xml::element( 'text',
+                               array( 'id' => $row->rev_text_id, 'bytes' => intval( $row->rev_len ) ),
+                               "" ) . "\n";
+               }
+
+               if ( isset( $row->rev_sha1 )
+                       && $row->rev_sha1
+                       && !( $row->rev_deleted & Revision::DELETED_TEXT )
+               ) {
+                       $out .= "      " . Xml::element( 'sha1', null, strval( $row->rev_sha1 ) ) . "\n";
+               } else {
+                       $out .= "      <sha1/>\n";
+               }
+
+               Hooks::run( 'XmlDumpWriterWriteRevision', array( &$this, &$out, $row, $text ) );
+
+               $out .= "    </revision>\n";
+
+               return $out;
+       }
+
+       /**
+        * Dumps a "<logitem>" section on the output stream, with
+        * data filled in from the given database row.
+        *
+        * @param object $row
+        * @return string
+        * @access private
+        */
+       function writeLogItem( $row ) {
+
+               $out = "  <logitem>\n";
+               $out .= "    " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n";
+
+               $out .= $this->writeTimestamp( $row->log_timestamp, "    " );
+
+               if ( $row->log_deleted & LogPage::DELETED_USER ) {
+                       $out .= "    " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
+               } else {
+                       $out .= $this->writeContributor( $row->log_user, $row->user_name, "    " );
+               }
+
+               if ( $row->log_deleted & LogPage::DELETED_COMMENT ) {
+                       $out .= "    " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
+               } elseif ( $row->log_comment != '' ) {
+                       $out .= "    " . Xml::elementClean( 'comment', null, strval( $row->log_comment ) ) . "\n";
+               }
+
+               $out .= "    " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n";
+               $out .= "    " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n";
+
+               if ( $row->log_deleted & LogPage::DELETED_ACTION ) {
+                       $out .= "    " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
+               } else {
+                       $title = Title::makeTitle( $row->log_namespace, $row->log_title );
+                       $out .= "    " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n";
+                       $out .= "    " . Xml::elementClean( 'params',
+                               array( 'xml:space' => 'preserve' ),
+                               strval( $row->log_params ) ) . "\n";
+               }
+
+               $out .= "  </logitem>\n";
+
+               return $out;
+       }
+
+       /**
+        * @param string $timestamp
+        * @param string $indent Default to six spaces
+        * @return string
+        */
+       function writeTimestamp( $timestamp, $indent = "      " ) {
+               $ts = wfTimestamp( TS_ISO_8601, $timestamp );
+               return $indent . Xml::element( 'timestamp', null, $ts ) . "\n";
+       }
+
+       /**
+        * @param int $id
+        * @param string $text
+        * @param string $indent Default to six spaces
+        * @return string
+        */
+       function writeContributor( $id, $text, $indent = "      " ) {
+               $out = $indent . "<contributor>\n";
+               if ( $id || !IP::isValid( $text ) ) {
+                       $out .= $indent . "  " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n";
+                       $out .= $indent . "  " . Xml::element( 'id', null, strval( $id ) ) . "\n";
+               } else {
+                       $out .= $indent . "  " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n";
+               }
+               $out .= $indent . "</contributor>\n";
+               return $out;
+       }
+
+       /**
+        * Warning! This data is potentially inconsistent. :(
+        * @param object $row
+        * @param bool $dumpContents
+        * @return string
+        */
+       function writeUploads( $row, $dumpContents = false ) {
+               if ( $row->page_namespace == NS_FILE ) {
+                       $img = wfLocalFile( $row->page_title );
+                       if ( $img && $img->exists() ) {
+                               $out = '';
+                               foreach ( array_reverse( $img->getHistory() ) as $ver ) {
+                                       $out .= $this->writeUpload( $ver, $dumpContents );
+                               }
+                               $out .= $this->writeUpload( $img, $dumpContents );
+                               return $out;
+                       }
+               }
+               return '';
+       }
+
+       /**
+        * @param File $file
+        * @param bool $dumpContents
+        * @return string
+        */
+       function writeUpload( $file, $dumpContents = false ) {
+               if ( $file->isOld() ) {
+                       $archiveName = "      " .
+                               Xml::element( 'archivename', null, $file->getArchiveName() ) . "\n";
+               } else {
+                       $archiveName = '';
+               }
+               if ( $dumpContents ) {
+                       $be = $file->getRepo()->getBackend();
+                       # Dump file as base64
+                       # Uses only XML-safe characters, so does not need escaping
+                       # @todo Too bad this loads the contents into memory (script might swap)
+                       $contents = '      <contents encoding="base64">' .
+                               chunk_split( base64_encode(
+                                       $be->getFileContents( array( 'src' => $file->getPath() ) ) ) ) .
+                               "      </contents>\n";
+               } else {
+                       $contents = '';
+               }
+               if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
+                       $comment = Xml::element( 'comment', array( 'deleted' => 'deleted' ) );
+               } else {
+                       $comment = Xml::elementClean( 'comment', null, $file->getDescription() );
+               }
+               return "    <upload>\n" .
+                       $this->writeTimestamp( $file->getTimestamp() ) .
+                       $this->writeContributor( $file->getUser( 'id' ), $file->getUser( 'text' ) ) .
+                       "      " . $comment . "\n" .
+                       "      " . Xml::element( 'filename', null, $file->getName() ) . "\n" .
+                       $archiveName .
+                       "      " . Xml::element( 'src', null, $file->getCanonicalURL() ) . "\n" .
+                       "      " . Xml::element( 'size', null, $file->getSize() ) . "\n" .
+                       "      " . Xml::element( 'sha1base36', null, $file->getSha1() ) . "\n" .
+                       "      " . Xml::element( 'rel', null, $file->getRel() ) . "\n" .
+                       $contents .
+                       "    </upload>\n";
+       }
+
+       /**
+        * Return prefixed text form of title, but using the content language's
+        * canonical namespace. This skips any special-casing such as gendered
+        * user namespaces -- which while useful, are not yet listed in the
+        * XML "<siteinfo>" data so are unsafe in export.
+        *
+        * @param Title $title
+        * @return string
+        * @since 1.18
+        */
+       public static function canonicalTitle( Title $title ) {
+               if ( $title->isExternal() ) {
+                       return $title->getPrefixedText();
+               }
+
+               global $wgContLang;
+               $prefix = $wgContLang->getFormattedNsText( $title->getNamespace() );
+
+               if ( $prefix !== '' ) {
+                       $prefix .= ':';
+               }
+
+               return $prefix . $title->getText();
+       }
+}
index 9755e8e..2d25710 100644 (file)
@@ -325,25 +325,39 @@ abstract class QueryPage extends SpecialPage {
                                                $value = 0;
                                        }
 
-                                       $vals[] = array( 'qc_type' => $this->getName(),
-                                                       'qc_namespace' => $row->namespace,
-                                                       'qc_title' => $row->title,
-                                                       'qc_value' => $value );
+                                       $vals[] = array(
+                                               'qc_type' => $this->getName(),
+                                               'qc_namespace' => $row->namespace,
+                                               'qc_title' => $row->title,
+                                               'qc_value' => $value
+                                       );
                                }
 
-                               $dbw->startAtomic( __METHOD__ );
-                               # Clear out any old cached data
-                               $dbw->delete( 'querycache', array( 'qc_type' => $this->getName() ), $fname );
-                               # Save results into the querycache table on the master
-                               if ( count( $vals ) ) {
-                                       $dbw->insert( 'querycache', $vals, __METHOD__ );
-                               }
-                               # Update the querycache_info record for the page
-                               $dbw->delete( 'querycache_info', array( 'qci_type' => $this->getName() ), $fname );
-                               $dbw->insert( 'querycache_info',
-                                       array( 'qci_type' => $this->getName(), 'qci_timestamp' => $dbw->timestamp() ),
-                                       $fname );
-                               $dbw->endAtomic( __METHOD__ );
+                               $that = $this;
+                               $dbw->doAtomicSection(
+                                       __METHOD__,
+                                       function ( IDatabase $dbw, $fname ) use ( $that, $vals ) {
+                                               # Clear out any old cached data
+                                               $dbw->delete( 'querycache',
+                                                       array( 'qc_type' => $that->getName() ),
+                                                       $fname
+                                               );
+                                               # Save results into the querycache table on the master
+                                               if ( count( $vals ) ) {
+                                                       $dbw->insert( 'querycache', $vals, $fname );
+                                               }
+                                               # Update the querycache_info record for the page
+                                               $dbw->delete( 'querycache_info',
+                                                       array( 'qci_type' => $that->getName() ),
+                                                       $fname
+                                               );
+                                               $dbw->insert( 'querycache_info',
+                                                       array( 'qci_type' => $that->getName(),
+                                                               'qci_timestamp' => $dbw->timestamp() ),
+                                                       $fname
+                                               );
+                                       }
+                               );
                        }
                } catch ( DBError $e ) {
                        if ( !$ignoreErrors ) {
index 6e67df0..fb78a40 100644 (file)
@@ -144,6 +144,7 @@ class MediaStatisticsPage extends QueryPage {
                        $this->getOutput()->addWikiText(
                                $this->msg( 'mediastatistics-allbytes' )
                                        ->numParams( $this->totalSize )
+                                       ->sizeParams( $this->totalSize )
                                        ->text()
                        );
                }
@@ -157,6 +158,8 @@ class MediaStatisticsPage extends QueryPage {
                $this->getOutput()->addWikiText(
                                $this->msg( 'mediastatistics-bytespertype' )
                                        ->numParams( $this->totalPerType )
+                                       ->sizeParams( $this->totalPerType )
+                                       ->numParams( $this->makePercentPretty( $this->totalPerType / $this->totalBytes ) )
                                        ->text()
                );
                $this->totalSize += $this->totalPerType;
index 49ab6d5..b45946f 100644 (file)
@@ -128,7 +128,7 @@ class SpecialPreferences extends SpecialPage {
                        throw new PermissionsError( 'editmyoptions' );
                }
 
-               $user = $this->getUser();
+               $user = $this->getUser()->getInstanceForUpdate();
                $user->resetOptions( 'all', $this->getContext() );
                $user->saveSettings();
 
diff --git a/includes/templates/EnhancedChangesListGroup.mustache b/includes/templates/EnhancedChangesListGroup.mustache
new file mode 100644 (file)
index 0000000..352eb17
--- /dev/null
@@ -0,0 +1,28 @@
+<table class="{{# tableClasses }}{{ . }} {{/ tableClasses }}">
+       <tr>
+               <td>
+                       <span class="mw-collapsible-toggle mw-collapsible-arrow mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>
+               </td>
+               <td class="mw-enhanced-rc">{{{ collectedRcFlags }}}&#160;{{ timestamp }}&#160;</td>
+               <td>
+                       {{# rev-deleted-event }}<span class="history-deleted">{{{ . }}}</span>{{/ rev-deleted-event }}
+                       {{{ articleLink }}}{{{ languageDirMark }}}{{{ logText }}}
+                       <span class="mw-changeslist-separator">. .</span>
+                       {{# charDifference }}{{{ . }}} <span class="mw-changeslist-separator">. .</span>{{/ charDifference }}
+                       <span class="changedby">{{{ users }}}</span>
+                       {{ numberofWatchingusers }}
+               </td>
+       </tr>
+       {{# lines }}
+       <tr class="{{# classes }}{{ . }} {{/ classes }}">
+               <td></td>
+               <td class="mw-enhanced-rc">{{{ recentChangesFlags }}}&#160;</td>
+               <td class="mw-enhanced-rc-nested">
+                       {{# timestampLink }}
+                       <span class="mw-enhanced-rc-time">{{{ . }}}</span>
+                       {{/ timestampLink }}
+                       {{# data }}{{{ . }}}{{/ data }}
+               </td>
+       </tr>
+       {{/ lines }}
+</table>
index afd939d..0b8ce7d 100644 (file)
        "password-change-forbidden": "بو ویکی‌ده رمزلری دَییشه بیلنمه‌سینیز.",
        "externaldberror": "بیر دیتابیس دوغرولاما خطاسی اولدو، یوخسا سیزین ائشیک حسابینیزی گونجل‌لدمگه ایجازه‌نیز یوخدور.",
        "login": "گیریش",
-       "nav-login-createaccount": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cاراد",
-       "userlogin": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cاراد",
+       "nav-login-createaccount": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cارات",
+       "userlogin": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cارات",
        "userloginnocreate": "گیریش",
        "logout": "چیخیش",
        "userlogout": "چیخیش",
        "fileduplicatesearch-noresults": "\"$1\" آدیندا فایل تاپیلمادی.",
        "specialpages": "اؤزل صفحه‌لر",
        "specialpages-note": "* نورمال اؤزل صفحه‌لر.\n* <span class=\"mw-specialpagerestricted\">محدودلاشدیریلمیش اؤزل صفحه‌لر.</span>",
-       "specialpages-group-maintenance": "جارÛ\8c Ù\85رÙ\88زÙ\87â\80\8cÙ\84ر",
+       "specialpages-group-maintenance": "ساخÙ\84اÙ\86Û\8cØ´ Ø±Ø§Ù¾Ù\88رتÙ\84ارÛ\8c",
        "specialpages-group-other": "آیری اؤزل صفحه‌لر",
-       "specialpages-group-login": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cاراد",
+       "specialpages-group-login": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cارات",
        "specialpages-group-changes": "سون دییشیک‌لیک‌لر و قئیدلر",
        "specialpages-group-media": "مئدیا مروزه‌لری و یوکلمه‌لر",
-       "specialpages-group-users": "اÛ\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8c‌لر و حاقلار",
+       "specialpages-group-users": "اÛ\8cØ´Ù\84دÙ\86‌لر و حاقلار",
        "specialpages-group-highuse": "ان چوْخ ایشله‌نن صفحه‌لر",
-       "specialpages-group-pages": "صحیفه‌لرین سیاهی‌لاری",
+       "specialpages-group-pages": "صفحه‌لرین لیست‌لری",
        "specialpages-group-pagetools": "صفحه آلتلری",
        "specialpages-group-wiki": "بیلگیلر و آلتلر",
        "specialpages-group-redirects": "خصوصی ایستیقامتلندیرمه صحیفه‌لری",
index 2d07ce8..664b941 100644 (file)
        "foreign-structured-upload-form-3-label-question-website": "Вы спампавалі гэтую выяву зь нейкага сайту або знайшлі яе праз пошук выяваў?",
        "foreign-structured-upload-form-3-label-question-ownwork": "Вы стварылі гэтую выяву (зрабілі фота, накід малюнку і г. д.) самі?",
        "foreign-structured-upload-form-3-label-question-noderiv": "Ці ўтрымлівае яна або яна натхнёная працай, якой валодае нехта іншы, як прыклад, лягатып?",
+       "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 не існуе.",
index 32101b5..2633e0b 100644 (file)
@@ -39,6 +39,7 @@
        "tog-hideminor": "Скриване на малки редакции в последните промени",
        "tog-hidepatrolled": "Скриване на патрулираните редакции от списъка с последните промени",
        "tog-newpageshidepatrolled": "Скриване на патрулираните редакции от списъка на новите страници",
+       "tog-hidecategorization": "Скриване на категоризацията на статии",
        "tog-extendwatchlist": "Разширяване на списъка за наблюдение, така че да показва всички промени, не само последните",
        "tog-usenewrc": "Групиране по страници на промените на Последни промени и в списъка за наблюдение",
        "tog-numberheadings": "Автоматично номериране на заглавията",
        "tog-watchlisthidebots": "Скриване на редакциите на ботове в списъка ми за наблюдение",
        "tog-watchlisthideminor": "Скриване на малките промени в списъка ми за наблюдение",
        "tog-watchlisthideliu": "Скриване на редакциите от влезли потребители от списъка за наблюдение",
+       "tog-watchlistreloadautomatically": "Обновяване на списъка за наблюдение всеки път, когато е сменен филтър (изисква се JavaScript)",
        "tog-watchlisthideanons": "Скриване на редакциите от анонимни потребители в списъка за наблюдение",
        "tog-watchlisthidepatrolled": "Скриване на патрулираните редакции от списъка за наблюдение",
+       "tog-watchlisthidecategorization": "Скриване на категоризацията на статии",
        "tog-ccmeonemails": "Получаване на копия на писмата, които пращам на другите потребители",
        "tog-diffonly": "Без показване на съдържанието на страницата при преглед на разлики",
        "tog-showhiddencats": "Показване на скритите категории",
        "morenotlisted": "Този списък не е пълен.",
        "mypage": "Страница",
        "mytalk": "Беседа",
-       "anontalk": "Беседа за адреса",
+       "anontalk": "Беседа",
        "navigation": "Навигация",
        "and": "&#32;и",
        "qbfind": "Търсене",
        "no-null-revision": "Не може да бъде създадена празна версия на страницата „$1“",
        "badtitle": "Невалидно заглавие",
        "badtitletext": "Желаното заглавие на страница е невалидно, празно или неправилна препратка към друго уики. Възможно е да съдържа знаци, които не са позволени в заглавия.",
+       "title-invalid-empty": "Желаното заглавие на статия е празно или съдържа единствено името на именното пространство.",
        "title-invalid-utf8": "Желаната страница съдържа невалиден низ с кодиране UTF-8",
        "title-invalid-interwiki": "Желаното заглавие на страница съдържа препратка към друго уики, което не може да бъде ползвано в заглавия.",
        "title-invalid-talk-namespace": "Желаното заглавие на страница се отнася към беседа, която не съществува",
        "title-invalid-relative": "Заглавието съдържа относителен път. Относителни заглавия на статии (./,../) са невалидни, защото често ще са недостижимо, когато биват извиквани от браузъра на потребителя.",
        "title-invalid-magic-tilde": "Желаното заглавие на статия съдържа невалидна поредица от вълчнички (<nowiki>~~~</nowiki>).",
        "title-invalid-too-long": "Желаното заглавие на статия е твърде дълго. Трябва да е не по-дълго от $1 {{PLURAL:$1|байт|байта}} в кодиране UTF-8.",
+       "title-invalid-leading-colon": "Желаното заглавие на статия съдържа невалидно двоеточие в началото.",
        "perfcached": "Следните данни са извлечени от склада и затова може да не отговарят на текущото състояние. В складираното копие {{PLURAL:$1|е допустим най-много един резултат|са допустими най-много $1 резултата}}.",
        "perfcachedts": "Данните са складирани и обновени за последно на $1. Най-много {{PLURAL:$4|един резултат е допустим и наличен|$4 резултата са допустими и налични}} в складираното копие.",
        "querypage-no-updates": "Обновяването на тази страница в момента е изключено. Засега данните тук няма да бъдат обновявани.",
        "actionthrottled": "Ограничение в скоростта",
        "actionthrottledtext": "Като част от защитата против спам, многократното повтаряне на това действие за кратък период от време е ограничено и вие вече сте надвишили лимита си. Опитайте отново след няколко минути.",
        "protectedpagetext": "Тази страница е защитена, за да се предотвратят редактиране или други действия.",
-       "viewsourcetext": "Можете да разгледате и да копирате кодa на страницата:",
-       "viewyourtext": "Можете да прегледате и копирате изходния код на '''вашите редакции''' на тази страница:",
+       "viewsourcetext": "Можете да разгледате и да копирате кодa на страницата.",
+       "viewyourtext": "Можете да прегледате и копирате изходния код на <strong>вашите редакции</strong> на тази страница.",
        "protectedinterface": "Тази страница съдържа текст, нужен за работата на системата. Тя е защитена против редактиране, за да се предотвратят възможни злоупотреби.\nЗа извършване на промяна за всички уикита, посетете [//translatewiki.net/ translatewiki.net], проектът за локализация на MediaWiki.",
        "editinginterface": "<strong>Внимание:</strong> Редактирате страница, която се използва за текстовия интерфейс на софтуера. Промяната й ще повлияе на външния вид на уикито.",
        "translateinterface": "За да добавите или промените преводи за всички уикита, моля, използвайте [//translatewiki.net/ translatewiki.net], локализиращия проект на МедияУики.",
        "createacct-reason": "Причина",
        "createacct-reason-ph": "Защо създавате друга сметка",
        "createacct-submit": "Създаване на сметката",
-       "createacct-another-submit": "Създаване на друга сметка",
+       "createacct-another-submit": "Създаване на сметка",
        "createacct-benefit-heading": "{{SITENAME}} се създава от хора като вас.",
        "createacct-benefit-body1": "{{PLURAL:$1|редакция|редакции}}",
        "createacct-benefit-body2": "{{PLURAL:$1|страница|страници}}",
        "wrongpassword": "Въведената парола е невалидна. Опитайте отново.",
        "wrongpasswordempty": "Не е въведена парола. Опитайте отново.",
        "passwordtooshort": "Необходимо е паролата да съдържа поне {{PLURAL:$1|1 знак|$1 знака}}.",
+       "passwordtoopopular": "Често използвани пароли не могат да бъдат ползвани. Моля, изберете по-уникална парола.",
        "password-name-match": "Паролата ви трябва да се различава от потребителското ви име.",
        "password-login-forbidden": "Използването на това потребителско име и парола е забранено.",
        "mailmypassword": "Възстановяване на парола",
        "passwordreset-emailtext-ip": "Някой (вероятно вие, от IP адрес $1) поиска възстановяване на паролата за сметката в {{SITENAME}} ($4). За {{PLURAL:$3|следната сметка|следните сметки}}\nе посочен този адрес за електронна поща:\n\n$2\n\n{{PLURAL:$3|Тази временна парола ще бъде активна|Тези временни пароли ще бъдат активни}} {{PLURAL:$5|един ден|$5 дни}}.\nСега би трябвало да влезете в системата и да си изберете нова парола. Ако заявката е направена от друг или пък сте си спомнили паролата и не искате да я променяте, можете да пренебрегнете това съобщение и да продължите да използвате старата си парола.",
        "passwordreset-emailtext-user": "Потребител $1 от {{SITENAME}} поиска възстановяване на паролата за сметката в {{SITENAME}}\n($4). За {{PLURAL:$3|следната сметка|следните сметки}} е посочен този адрес за електронна поща:\n\n$2\n\n{{PLURAL:$3|Тази временна парола ще бъде активна|Тези временни пароли ще бъдат активни}} {{PLURAL:$5|един ден|$5 дни}}.\nСега би трябвало да влезете в системата и да изберете нова парола. Ако заявката е направена \nот друг или пък сте си спомнили паролата и не искате да я променяте, можете да пренебрегнете \nтова съобщение и да продължите да използвате старата си парола.",
        "passwordreset-emailelement": "Потребителско име: \n$1\n\nВременна парола: \n$2",
-       "passwordreset-emailsentemail": "На електронната поща беше изпратено писмо за възстановяване на паролата.",
+       "passwordreset-emailsentemail": "Ако електронната Ви поща е свързана със сметката Ви, на нея е изпратено писмо за възстановяване на паролата.",
+       "passwordreset-emailsentusername": "Ако това потребителско име е свързано с електронна поща, е изпратено писмо за възстановяване на паролата.",
        "passwordreset-emailsent-capture": "По-долу е показано електронното писмо за възстановяване на паролата, което беше изпратено.",
        "passwordreset-emailerror-capture": "По-долу е показано създадено електронно писмо за възстановяване на паролата, което не беше изпратено на {{GENDER:$2|потребителя}}: $1",
-       "changeemail": "Промяна на адреса за е-поща",
+       "changeemail": "Ð\9fÑ\80омÑ\8fна Ð¸Ð»Ð¸ Ð¿Ñ\80емаÑ\85ване Ð½Ð° Ð°Ð´Ñ\80еÑ\81а Ð·Ð° Ðµ-поÑ\89а",
        "changeemail-header": "Промяна на адреса за е-поща на сметката",
+       "changeemail-passwordrequired": "Трябва да въведете паролата си, за да потвърдите тази промяна.",
        "changeemail-no-info": "За да достъпвате тази страница директно, необходимо е да влезете в системата.",
        "changeemail-oldemail": "Текущ адрес за е-поща:",
        "changeemail-newemail": "Нов адрес за е-поща:",
        "changeemail-password": "Парола за {{SITENAME}}:",
        "changeemail-submit": "Промяна на е-пощата",
        "changeemail-throttled": "Направили сте твърде много опити за влизане в системата. \nМоля, изчакайте $1 преди следващия опит.",
+       "changeemail-nochange": "Моля, въведете различен нов адрес на електронна поща.",
        "resettokens": "Изчистване на маркерите",
        "resettokens-no-tokens": "Няма маркери за изчистване.",
        "resettokens-tokens": "Маркери:",
        "preview": "Предварителен преглед",
        "showpreview": "Предварителен преглед",
        "showdiff": "Показване на промените",
+       "blankarticle": "<strong>Предупреждение:</strong> Статията, която създавате е празна.\nАко щракнете на „\"{{int:savearticle}}“ отново, статията ще бъде създадена без никакво съдържание.",
        "anoneditwarning": "<strong>Внимание:</strong> Не сте влезли в системата. Ако направите редакция IP-адресът Ви ще бъде публично видим. Ако <strong>[$1 влезете]</strong> или си <strong>[$2 създадете акаунт]</strong>, редакциите Ви ще бъдат свързани с потребителското Ви име, заедно с други преимущества.",
        "anonpreviewwarning": "Внимание: Не сте влезли в системата. Ако съхраните редакцията си, тя ще бъде записана в историята на страницата с вашият IP-адрес.",
        "missingsummary": "'''Напомняне:''' Не е въведено кратко описание на промените. При повторно натискане на бутона „Съхраняване“, редакцията ще бъде съхранена без резюме.",
index f45e43c..b00dab0 100644 (file)
        "passwordreset-emailtext-ip": "Unan bennak (c'hwi moarvat gant ar chomlec'h IP $1) en deus goulennet ma vefe degaset soñj dezhañ eus titouroù e gont evit {{SITENAME}} ($4). Emañ liammet {{PLURAL:$3|ar gont implijer|ar c'hontoù implijer}} da-heul gant ar chomlec'h postel-mañ :\n\n$2\n\nMont a raio da get {{PLURAL:$3|ar ger-tremen da c'hortoz|ar gerioù-tremen da c'hortoz}} a-benn {{PLURAL:$5|un devezh|$5 deiz}}.\nMat e vefe deoc'h kevreañ ha dibab ur ger-tremen nevez bremañ. Mard eo bet goulennet kement-se gant unan bennak all pe m'hoc'h eus soñj eus ho ker-tremen orin ha mar ne fell ket deoc'h e cheñch ken, na daolit ket evezh ouzh ar gemennadenn-mañ ha dalc'hit d'ober gant ho ker-tremen kozh.",
        "passwordreset-emailtext-user": "Goulennet en deus an implijer $1 war  {{SITENAME}} e vefe degaset soñj dezhañ eus titouroù e gont evit {{SITENAME}} ($4). Emañ liammet {{PLURAL:$3|ar gont implijer|ar c'hontoù implijer}} da-heul gant ar chomlec'h postel-mañ :\n\n$2\n\nMont a raio da get {{PLURAL:$3|ar ger-tremen da c'hortoz|ar gerioù-tremen da c'hortoz}} a-benn {{PLURAL:$5|un devezh|$5 deiz}}.\nMat e vefe deoc'h kevreañ ha dibab ur ger-tremen nevez bremañ. Mard eo bet goulennet kement-se gant unan bennak all pe m'hoc'h eus soñj eus ho ker-tremen orin ha mar ne fell ket deoc'h e cheñch ken, na daolit ket evezh ouzh ar gemennadenn-mañ ha dalc'hit d'ober gant ho ker-tremen kozh.",
        "passwordreset-emailelement": "Anv implijer :           \n$1\n\nGer-tremen da c'hortoz : \n$2",
-       "passwordreset-emailsent": "Kaset ez eus bet ur postel deoc'h da adderaouekaat ho ker-tremen.",
+       "passwordreset-emailsentemail": "Kaset ez eus bet ur postel deoc'h da adderaouekaat ho ker-tremen.",
        "passwordreset-emailsent-capture": "Ur postel evit aderaouekaat ho ker-tremen, evel diskouezet amañ dindan, zo bet kaset.",
        "passwordreset-emailerror-capture": "Kaset ez eus bet ur postel degas da soñj evel m'emañ diskouezet amañ dindan met c'hwitet eo bet ar gasadenn d'an {{GENDER:$2|implijer|implijerez}} : $1",
        "changeemail": "Kemmañ ar chomlec'h postel",
        "prefs-diffs": "Diforc'hioù",
        "prefs-help-prefershttps": "Efediñ a ray an dibarzh-mañ kentañ gwech ma kevreoc'h.",
        "prefs-tabs-navigation-hint": "Titourig : Gallout a rit implijout an touchennoù bir kleiz ha bir dehoù evit merdeiñ etre an ivinelloù e roll an ivinelloù.",
-       "email-address-validity-valid": "Reizh eo ar chomlec'h postel war a seblant",
-       "email-address-validity-invalid": "Ebarzhit ur chomlec'h postel reizh",
        "userrights": "Merañ statud an implijerien",
        "userrights-lookup-user": "Merañ strolladoù an implijer",
        "userrights-user-editname": "Lakait un anv implijer :",
        "contributions": "Degasadennoù an {{GENDER:$1|implijer|implijerez}}",
        "contributions-title": "Degasadennoù an implijer evit $1",
        "mycontris": "Ma degasadennoù",
+       "anoncontribs": "Ma degasadennoù",
        "contribsub2": "Evit {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "N'eo ket enrollet ar gont implijer \"$1\".",
        "nocontribs": "N'eus bet kavet kemm ebet o klotañ gant an dezverkoù-se.",
        "movenosubpage": "Ispajenn ebet d'ar bajenn-mañ.",
        "movereason": "Abeg :",
        "revertmove": "nullañ",
-       "delete_and_move": "Diverkañ ha sevel adkas",
        "delete_and_move_text": "==Ezhomm diverkañ==\n\nSavet eo ar pennad tal \"[[:$1]]\" c'hoazh.\nDiverkañ anezhañ a fell deoc'h ober evit reiñ lec'h d'an adkas ?",
        "delete_and_move_confirm": "Ya, diverkañ ar bajenn",
        "delete_and_move_reason": "Diverket evit ober lec'h d'an adkas \"[[$1]]\"",
index 431f680..aaf599d 100644 (file)
        "passwordreset-emailtext-ip": "Někdo (patrně vy, z IP adresy $1) zažádal na {{grammar:6sg|{{SITENAME}}}} ($4) o nastavení nového hesla k vašemu účtu. K této adrese {{PLURAL:$3|je přiřazen následující účet|jsou přiřazeny následující účty}}:\n\n$2\n\n{{PLURAL:$3|Toto dočasné heslo|Tato dočasná hesla}} vyprší za {{PLURAL:$5|jeden den|$5 dny|$5 dnů}}.\nNyní byste se měli přihlásit a zvolit si nové heslo. Pokud tento požadavek poslal někdo jiný nebo jste si na své staré heslo vzpomněli, a nechcete ho tedy změnit, můžete tuto zprávu ignorovat a nadále používat původní heslo.",
        "passwordreset-emailtext-user": "{{gender:$1|Uživatel|Uživatelka}} $1 na {{grammar:6sg|{{SITENAME}}}} {{gender:$1|zažádal|zažádala}} na {{grammar:6sg|{{SITENAME}}}} ($4) o nastavení nového hesla k vašemu\núčtu. K této adrese {{PLURAL:$3|je přiřazen následující účet|jsou přiřazeny následující účty}}:\n\n$2\n\n{{PLURAL:$3|Toto dočasné heslo|Tato dočasná hesla}} vyprší {{PLURAL:$5|za jeden den|za $5 dny|za $5 dnů}}.\nNyní byste se měl(a) přihlásit a zvolit si nové heslo. Pokud tento požadavek\nposlal někdo jiný nebo jste si na své staré heslo vzpomněl(a), a nechcete ho\ntedy změnit, můžete tuto zprávu ignorovat a nadále používat původní heslo.",
        "passwordreset-emailelement": "Uživatelské jméno: \n$1\n\nDočasné heslo: \n$2",
-       "passwordreset-emailsentemail": "Pokud je to registrovaná e-mailová adresa k vašemu účtu, bude vám zaslán e-mail pro získání nového hesla.",
-       "passwordreset-emailsentusername": "Pokud existuje odpovídající registrovaná e-mailová adresa, bude vám zaslán e-mail pro získání nového hesla.",
+       "passwordreset-emailsentemail": "Pokud je u vašeho účtu nastavena tato e-mailová adresa, bude vám zaslán e-mail pro získání nového hesla.",
+       "passwordreset-emailsentusername": "Pokud je u tohoto účtu nastavena e-mailová adresa, bude vám zaslán e-mail pro získání nového hesla.",
        "passwordreset-emailsent-capture": "Byl odeslán e-mail pro získání nového hesla, který je zobrazen níže.",
        "passwordreset-emailerror-capture": "Byl vygenerován e-mail pro získání nového hesla, který je zobrazen níže, ale {{GENDER:$2|uživateli|uživatelce}} se ho nepodařilo odeslat: $1",
        "changeemail": "Změna nebo odstranění e-mailové adresy",
        "wlshowhideanons": "anonymní uživatele",
        "wlshowhidepatr": "prověřené editace",
        "wlshowhidemine": "moje editace",
+       "wlshowhidecategorization": "kategorizaci stránek",
        "watchlist-options": "Možnosti sledovaných stránek",
        "watching": "Přidávám na seznam sledovaných stránek…",
        "unwatching": "Odebírám ze seznamu sledovaných stránek…",
        "pagelang-language": "Jazyk",
        "pagelang-use-default": "Použít implicitní jazyk",
        "pagelang-select-lang": "Vybrat jazyk",
+       "pagelang-submit": "Odeslat",
        "right-pagelang": "Změnit jazyk stránky",
        "action-pagelang": "měnit jazyk stránky",
        "log-name-pagelang": "Kniha změn jazyků",
        "mediastatistics-summary": "Statistika o typech načtených souborů. Zahrnuje vždy jen nejnovější verzi souboru. Staré nebo smazané verze se nezapočítávají.",
        "mediastatistics-nfiles": "$1 ($2 %)",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtů}} ($2; $3 %)",
+       "mediastatistics-bytespertype": "Celková velikost souborů v této sekci: $1 bajtů.",
+       "mediastatistics-allbytes": "Celková velikost všech souborů: $1 bajtů.",
        "mediastatistics-table-mimetype": "MIME typ",
        "mediastatistics-table-extensions": "Možné přípony",
        "mediastatistics-table-count": "Počet souborů",
        "mediastatistics-header-text": "Text",
        "mediastatistics-header-executable": "Spustitelné soubory",
        "mediastatistics-header-archive": "Komprimované formáty",
+       "mediastatistics-header-total": "Všechny soubory",
        "json-warn-trailing-comma": "Z JSONu {{PLURAL:$1|byla odstraněna 1 koncová čárka|byly odstraněny $1 koncové čárky|bylo odstraněno $1 koncových čárek}}",
        "json-error-unknown": "Došlo k potížím s JSONem. Chyba: $1",
        "json-error-depth": "Byla překročena maximální hloubka zásobníku",
index 7cc7bed..f47d451 100644 (file)
        "unusedtemplateswlh": "ытти каçăсем",
        "randompage": "Ăнсăртран лекнĕ страница",
        "randomincategory-category": "Категори:",
+       "randomincategory-submit": "Куç",
        "randomredirect": "Ăнсăртран илнĕ куçару",
        "statistics": "Статистика",
        "statistics-header-users": "Хутшăнакансен статистики",
        "whatlinkshere-hideredirs": "куçарнисене $1",
        "whatlinkshere-hidelinks": "Каçаканнисене $1",
        "whatlinkshere-filters": "Аласем",
+       "whatlinkshere-submit": "Ту",
        "blockip": "{{GENDER:$1|хутшăнакана}} чар",
        "ipaddressorusername": "IP адрес е усă куракан ят:",
        "ipbreason": "Сăлтавĕ",
        "allmessages": "Система пĕлтерĕвĕсем",
        "allmessagesname": "Пĕлтерӳ",
        "allmessagescurrent": "Хальхи текст",
+       "allmessages-filter-submit": "Куç",
        "allmessages-filter-translate": "Куçар",
        "thumbnail-more": "Пысăклатмалли",
        "filemissing": "Файл тупăнмарĕ",
        "watchlisttools-view": "Ку тӳрлетӳпе çыхăннăскерсем",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|сӳтсе явни]])",
        "version": "Верси",
+       "redirect-submit": "Куç",
        "fileduplicatesearch": "Пĕр пек файлсен шыравĕ",
        "fileduplicatesearch-legend": "Дубликатсен шыравĕ",
        "fileduplicatesearch-filename": "Файл ячĕ:",
index 349cac6..688e9b8 100644 (file)
        "mediastatistics-summary": "Statistics about uploaded file types. This only includes the most recent version of a file. Old or deleted versions of files are excluded.",
        "mediastatistics-nfiles": "$1 ($2%)",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%)",
-       "mediastatistics-bytespertype": "Total file size for this section: $1 bytes.",
-       "mediastatistics-allbytes": "Total file size for all files: $1 bytes.",
+       "mediastatistics-bytespertype": "Total file size for this section: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%).",
+       "mediastatistics-allbytes": "Total file size for all files: {{PLURAL:$1|$1 byte|$1 bytes}} ($2).",
        "mediastatistics-table-mimetype": "MIME type",
        "mediastatistics-table-extensions": "Possible extensions",
        "mediastatistics-table-count": "Number of files",
index baaa4a7..91da6b7 100644 (file)
        "mediastatistics-header-text": "Testuala",
        "mediastatistics-header-executable": "Exekutagarriak",
        "mediastatistics-header-archive": "Formatu konprimatuak",
+       "mediastatistics-header-total": "Fitxategi guztiak",
        "json-error-syntax": "Sintaxi-errorea",
        "headline-anchor-title": "Lotura sekzio honetara",
        "special-characters-group-latin": "Latina",
index c103b04..1962b3f 100644 (file)
        "passwordreset-emailtext-ip": "Joku (todennäköisesti sinä, IP-osoitteesta $1) pyysi salasanasi\nvaihtamista sivustolla {{SITENAME}} ($4). {{PLURAL:$3|Seuraava käyttäjätunnus on|Seuraavat käyttäjätunnukset ovat}}\nyhdistettynä tähän sähköpostiosoitteeseen:\n\n$2\n\n{{PLURAL:$3|Tämä väliaikainen salasana vanhentuu|Nämä väliaikaiset salasanat vanhentuvat}} {{PLURAL:$5|yhden päivän|$5 päivän}} kuluttua.\nKirjaudu sisään nyt ja valitse uusi salasana heti. Jos joku toinen teki tämän pyynnön \ntai jos muistitkin vanhan salasanasi etkä halua enää muuttaa sitä,\nvoit jättää tämän viestin huomiotta ja jatkaa vanhan salasanasi käyttämistä.",
        "passwordreset-emailtext-user": "Käyttäjä $1 pyysi muistutusta tunnuksesi tiedoista sivustolla {{SITENAME}} ($4).\n{{PLURAL:$3|Seuraava käyttäjätunnus on|Seuraavat käyttäjätunnukset ovat}} liitetty tähän sähköpostiosoitteeseen:\n\n$2\n\n{{PLURAL:$3|Tämä väliaikainen salasana vanhentuu|Nämä väliaikaiset salasanat vanhentuvat}} {{PLURAL:$5|yhden päivän|$5 päivän}} kuluttua.\nSinun kannattaa kirjautua sisään ja valita uusi salasana. Jos joku toinen teki tämän\npyynnön, tai muistat sittenkin vanhan salasanasi, etkä halua muuttaa sitä,\nvoit jättää tämän viestin huomiotta ja jatkaa vanhan salasanan käyttöä.",
        "passwordreset-emailelement": "Käyttäjätunnus: \n$1\n\nVäliaikainen salasana: \n$2",
-       "passwordreset-emailsentemail": "Jos tämä on sinun tunnuksellesi rekisteröity sähköpostiosoite, salasanan uudistamisesta kertova viesti lähetetään.",
-       "passwordreset-emailsentusername": "Jos on olemassa vastaava rekisteröity sähköpostiosoite, salasanan uudistamisesta kertova viesti lähetetään.",
+       "passwordreset-emailsentemail": "Mikäli tämä sähköpostiosoite on liitetty tiliisi, salasanan nollaamisviesti lähetetään.",
+       "passwordreset-emailsentusername": "Mikäli tähän käyttäjänimeen liitetty sähköpostiosoite löytyy, salasanan nollaamisviesti lähetetään.",
        "passwordreset-emailsent-capture": "Salasanan uudistamisesta kertova sähköpostiviesti on lähetetty, ja se näkyy myös alla.",
        "passwordreset-emailerror-capture": "Allaoleva sähköpostiviesti luotiin, mutta sen lähettäminen {{GENDER:$2|käyttäjälle}} epäonnistui: $1",
        "changeemail": "Muuta tai poista sähköpostiosoite",
        "mediastatistics-header-text": "Tekstitiedostot",
        "mediastatistics-header-executable": "Ohjelmatiedostot",
        "mediastatistics-header-archive": "Pakatussa muodossa",
+       "mediastatistics-header-total": "Kaikki tiedostot",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|jäljelle jäänyt pilkku|jäljelle jäänyttä pilkkua}} poistettiin JSON-tekstistä.",
        "json-error-unknown": "Syntyi ongelma JSONin kanssa. Virhe: $1",
        "json-error-depth": "Suurin mahdollinen pinosyvyys (stack depth) on ylitetty",
index 609c173..9232f26 100644 (file)
        "passwordreset-emailtext-ip": "Quelqu'un (probablement vous, depuis l'adresse IP $1) a demandé un réinitialisation de votre mot de passe pour {{SITENAME}} ($4). {{PLURAL:$3|Le compte utilisateur suivant est associé|Les comptes utilisateurs suivants sont associés}} à cette adresse de courriel :\n\n$2\n\n{{PLURAL:$3|Ce mot de passe temporaire expirera|Ces mots de passe temporaires expireront}} dans {{PLURAL:$5|un jour|$5 jours}}. Vous devez maintenant vous connecter et choisir un nouveau mot de passe. Si cette demande ne provient pas de vous, ou que vous avez retrouvé votre mot de passe initial, et ne souhaitez plus le modifier, vous pouvez ignorer ce message et continuer à utiliser votre ancien mot de passe.",
        "passwordreset-emailtext-user": "L'utilisateur $1 sur {{SITENAME}} a demandé un réinitialisation de votre mot de passe pour {{SITENAME}} ($4). {{PLURAL:$3|Le compte utilisateur suivant est associé|Les comptes utilisateurs suivants sont associés}} à cette adresse de courriel :\n\n$2\n\n{{PLURAL:$3|Ce mot de passe temporaire expirera|Ces mots de passe temporaires expireront}} dans {{PLURAL:$5|un jour|$5 jours}}. Vous devez maintenant vous connecter et choisir un nouveau mot de passe. Si cette demande ne provient pas de vous, ou que vous avez retrouvé votre mot de passe initial, et ne souhaitez plus le modifier, vous pouvez ignorer ce message et continuer à utiliser votre ancien mot de passe.",
        "passwordreset-emailelement": "Nom d'utilisateur : \n$1\n\nMot de passe temporaire : \n$2",
-       "passwordreset-emailsentemail": "Si c’est une adresse de courriel enregistrée pour votre compte, alors un courriel de réinitialisation de mot de passe sera envoyé.",
-       "passwordreset-emailsentusername": "S’il y a une adresse de courriel enregistrée pour ce compte, alors un courriel de réinitialisation de mot de passe sera envoyé.",
+       "passwordreset-emailsentemail": "Si cette adresse de courriel est associée à votre compte, alors un courriel de réinitialisation de mot de passe sera envoyé.",
+       "passwordreset-emailsentusername": "S’il y a une adresse de courriel associée à ce nom d’utilisateur, alors un courriel de réinitialisation de mot de passe sera envoyé.",
        "passwordreset-emailsent-capture": "Un courriel de réinitialisation de mot de passe a été envoyé, qui est affiché ci-dessous.",
        "passwordreset-emailerror-capture": "Un courriel de réinitialisation de mot de passe a été généré, qui est affiché ci-dessous, mais l'envoi à l'{{GENDER:$2|utilisateur|utilisatrice}} a échoué : $1",
        "changeemail": "Changer ou supprimer l’adresse de courriel",
        "pagelang-language": "Langue",
        "pagelang-use-default": "Utiliser la langue par défaut",
        "pagelang-select-lang": "Sélectionner la langue",
+       "pagelang-submit": "Envoyer",
        "right-pagelang": "Changer la langue de la page",
        "action-pagelang": "changer la langue de la page",
        "log-name-pagelang": "Tracer les changements de langue",
index 62b9644..1a46b82 100644 (file)
        "rcshowhidemine": "$1 moje promjene",
        "rcshowhidemine-show": "prikaži",
        "rcshowhidemine-hide": "sakrij",
+       "rcshowhidecategorization-show": "Prikaži",
        "rclinks": "Prikaži posljednjih $1 promjena {{PLURAL:$2|prethodni dan|u posljednja $2 dana|u posljednjih $2 dana}}<br />$3",
        "diff": "razl",
        "hist": "pov",
        "mostrevisions": "Popis članaka po broju uređivanja",
        "prefixindex": "Sve stranice prema početku naslova",
        "prefixindex-namespace": "Sve stranice s predmetkom (imenski prostor $1)",
+       "prefixindex-submit": "Prikaži",
        "prefixindex-strip": "Ne prikazuj predmetak u popisu",
        "shortpages": "Kratke stranice",
        "longpages": "Duge stranice",
index 09f4767..d1e45f7 100644 (file)
        "recentchanges-label-plusminus": "Az oldal mérete ennyi bájttal módosult",
        "recentchanges-legend-heading": "Jelmagyarázat:",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (lásd még: [[Special:NewPages|új lapok listája]])",
+       "recentchanges-submit": "Megjelenítés",
        "rcnotefrom": "Alább a <strong>$3 $4</strong> óta történt változtatások láthatóak (legfeljebb <b>$1</b> db).",
        "rclistfrom": "$3 $2 után történt változtatások megtekintése",
        "rcshowhideminor": "apró szerkesztések $1",
        "wlshowlast": "Az elmúlt $1 órában | $2 napon történt változtatások legyenek láthatóak",
        "watchlistall2": "bármikor",
        "watchlist-hide": "Elrejtés",
+       "watchlist-submit": "Megjelenítés",
+       "wlshowtime": "Időszak:",
+       "wlshowhideminor": "apró",
+       "wlshowhidebots": "botok",
+       "wlshowhideliu": "bejelentkezett",
+       "wlshowhideanons": "névtelen",
+       "wlshowhidemine": "saját",
        "watchlist-options": "A figyelőlista beállításai",
        "watching": "Figyelés...",
        "unwatching": "Figyelés befejezése...",
index a47f3d7..9d213b0 100644 (file)
        "passwordreset-emailtext-ip": "Qualcuno (probabilmente tu, con indirizzo IP $1) ha richiesto l'invio di una nuova password per l'accesso a {{SITENAME}} ($4). {{PLURAL:$3|L'utente associato|Gli utenti associati}} a questo indirizzo email sono:\n\n$2\n\n{{PLURAL:$3|Questa password temporanea scadrà|Queste password temporanee scadranno}} dopo {{PLURAL:$5|un giorno|$5 giorni}}.\nDovresti accedere e scegliere una nuova password ora. \n\nSe non sei stato tu a fare la richiesta, o se ti sei ricordato la password originale e non vuoi più cambiarla, puoi ignorare questo messaggio e continuare ad utilizzare la tua vecchia password.",
        "passwordreset-emailtext-user": "L'utente $1 di {{SITENAME}} ha richiesto l'invio di una nuova password per l'accesso a {{SITENAME}} ($4). {{PLURAL:$3|L'utente associato|Gli utenti associati}} a questo indirizzo email sono:\n\n$2\n\n{{PLURAL:$3|Questa password temporanea scadrà|Queste password temporanee scadranno}} dopo {{PLURAL:$5|un giorno|$5 giorni}}.\nDovresti accedere e scegliere una nuova password ora. \n\nSe non sei stato tu a fare la richiesta, o se ti sei ricordato la password originale e non vuoi più cambiarla, puoi ignorare questo messaggio e continuare al utilizzare la tua vecchia password.",
        "passwordreset-emailelement": "Nome utente: \n$1\n\nPassword temporanea: \n$2",
-       "passwordreset-emailsentemail": "Se questo è un indirizzo di posta elettronica registrato per la tua utenza, allora verrà inviata una email per reimpostare la password.",
-       "passwordreset-emailsentusername": "Se c'è un corrispondente indirizzo di posta elettronica registrato, allora verrà inviata una email per reimpostare la password.",
+       "passwordreset-emailsentemail": "Se questo indirizzo di posta elettronica è associato con la tua utenza, allora verrà inviata una email per reimpostare la password.",
+       "passwordreset-emailsentusername": "Se c'è un indirizzo di posta elettronica associato con questo nome utente, allora verrà inviata una email per reimpostare la password.",
        "passwordreset-emailsent-capture": "È stata inviata una email di reimpostazione della password, il contenuto è riportato di seguito.",
        "passwordreset-emailerror-capture": "È stata generata una email di reimpostazione della password, riportata di seguito. L'invio {{GENDER:$2|all'utente}} non è riuscito: $1",
        "changeemail": "Modifica o rimuovi indirizzo email",
        "tags-deactivate": "disattiva",
        "tags-hitcount": "$1 {{PLURAL:$1|modifica|modifiche}}",
        "tags-manage-no-permission": "Non hai il permesso di gestire il cambiamento tag.",
+       "tags-manage-blocked": "Non puoi gestire le etichette alle modifiche mentre sei bloccato.",
        "tags-create-heading": "Crea un nuovo tag",
        "tags-create-explanation": "Per impostazione predefinita, i tag appena creati saranno disponibili per l'utilizzo di utenti e bot.",
        "tags-create-tag-name": "Nome del tag:",
        "tags-deactivate-not-allowed": "Non è possibile disattivare il tag \"$1\".",
        "tags-deactivate-submit": "Disattiva",
        "tags-apply-no-permission": "Non disponi dell'autorizzazione per applicare la modifica di tag insieme con le tue modifiche.",
+       "tags-apply-blocked": "Non puoi applicare le etichette alle modifiche mentre sei bloccato.",
        "tags-apply-not-allowed-one": "L'etichetta \"$1\" non può essere applicata manualmente.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|La seguente etichetta non può essere applicata|Le seguenti etichette non possono essere applicate}}  manualmente: $1",
        "tags-update-no-permission": "Non hai il permesso di aggiungere o rimuovere modifiche di tag dalle singole revisioni o voci di registro.",
+       "tags-update-blocked": "Non puoi aggiungere o rimuovere le etichette alle modifiche mentre sei bloccato.",
        "tags-update-add-not-allowed-one": "Il tag \"$1\" non può essere aggiunto manualmente.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|Il seguente tag non può essere aggiunto|I seguenti tag non possono essere aggiunti}} manualmente: $1",
        "tags-update-remove-not-allowed-one": "Il tag \"$1\" non può essere rimosso.",
        "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-table-mimetype": "Tipo MIME",
        "mediastatistics-table-extensions": "Possibili estensioni",
        "mediastatistics-table-count": "Numero di file",
index 10a4b06..81bfe5f 100644 (file)
        "passwordreset-emailtext-ip": "誰か (おそらくあなた、IP アドレス $1) が {{SITENAME}} ($4)\nでのパスワードを再設定するよう申請しました。\n以下の利用者{{PLURAL:$3|アカウント|アカウント群}}がこのメールアドレスと紐付けられています。\n\n$2\n\n{{PLURAL:$3|この仮パスワード|これらの仮パスワード}}は {{PLURAL:$5|$5 日|$5 日間}}で有効期限が切れます。\nあなたはすぐにログインして新しいパスワードを設定する必要があります。\nこれが他の誰かによる申請である場合、あるいはあなたが自分の元のパスワードを\n覚えていてそれを変更したくない場合には、このメッセージを無視して以前のパスワードを\n使用し続けることができます。",
        "passwordreset-emailtext-user": "{{SITENAME}} の利用者 $1 があなたの {{SITENAME}} ($4)\nでのパスワードを再設定するよう申請しました。\n以下の利用者{{PLURAL:$3|アカウント|アカウント群}}がこのメールアドレスと紐付けられています。\n\n$2\n\n{{PLURAL:$3|この仮パスワード|これらの仮パスワード}}は{{PLURAL:$5|$5日}}で有効期限が切れます。\nあなたは、すぐにログインして新しいパスワードを設定する必要があります。\nこの申請が他の誰かによるものの場合、あるいはあなたが自分の元のパスワードを\n覚えていて、変更したくない場合は、このメッセージを無視して\n以前のパスワードを使い続けることができます。",
        "passwordreset-emailelement": "利用者名: \n$1\n\n仮パスワード: \n$2",
-       "passwordreset-emailsentemail": "ã\82\82ã\81\97ã\80\81ã\81\93ã\82\8cã\81\8cã\81\82ã\81ªã\81\9fã\81®ã\82¢ã\82«ã\82¦ã\83³ã\83\88ã\81®ã\81\9fã\82\81ã\81«ç\99»é\8c²ã\81\95ã\82\8cã\81\9fã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\81§ã\81\82ã\82\8bå ´å\90\88ã\80\81ã\81\93ã\82\8cã\81\8bã\82\89パスワードリセットのメールが送信されます。",
-       "passwordreset-emailsentusername": "ã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\81\8cç\99»é\8c²ã\81\95ã\82\8cã\81¦ã\81\84る場合は、パスワードリセットのメールが送信されます。",
+       "passwordreset-emailsentemail": "ã\81\93ã\81®ã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\81\8cã\81\82ã\81ªã\81\9fã\81®ã\82¢ã\82«ã\82¦ã\83³ã\83\88ã\81«é\96¢é\80£ä»\98ã\81\91ã\82\89ã\82\8cã\81¦ã\81\84ã\82\8bå ´å\90\88ã\81¯ã\80\81パスワードリセットのメールが送信されます。",
+       "passwordreset-emailsentusername": "ã\81\93ã\81®å\88©ç\94¨è\80\85å\90\8dã\81«é\96¢é\80£ä»\98ã\81\91ã\82\89ã\82\8cã\81\9fã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\81\8cã\81\82る場合は、パスワードリセットのメールが送信されます。",
        "passwordreset-emailsent-capture": "下記の内容の、パスワード再設定メールをお送りしました。",
        "passwordreset-emailerror-capture": "以下の内容のパスワード再設定メールを生成しましたが、{{GENDER:$2|利用者}}への送信に失敗しました: $1",
        "changeemail": "メールアドレスを変更または除去",
        "wlshowhideanons": "IP利用者",
        "wlshowhidepatr": "巡回された編集",
        "wlshowhidemine": "自分の編集",
+       "wlshowhidecategorization": "ページのカテゴリ化",
        "watchlist-options": "ウォッチリストのオプション",
        "watching": "ウォッチリストに追加中...",
        "unwatching": "ウォッチリストから除去中...",
index be67aa4..98c3c68 100644 (file)
@@ -29,7 +29,7 @@
                ]
        },
        "tog-underline": "ಕೊಂಡಿಗಳ ಕೆಳಗೆ ಗೆರೆ ತೋರಿಸಿ",
-       "tog-hideminor": "à²\9aಿà²\95à³\8dà²\95ಪà³\81à²\9fà³\8dà²\9f à²¬à²¦à²²à²¾à²µà²£ೆಗಳನ್ನು ಅಡಗಿಸಿ",
+       "tog-hideminor": "à²\87ತà³\8dತà³\80à²\9aಿನ à²¬à²¦à²²à²¾à²µà²£à³\86à²\97ಳಲà³\8dಲಿ à²\9aಿà²\95à³\8dà²\95ಪà³\81à²\9fà³\8dà²\9f à²¸à²\82ಪಾದನೆಗಳನ್ನು ಅಡಗಿಸಿ",
        "tog-hidepatrolled": "ಪಹರೆಯಲ್ಲಿ ಆದ ಸಂಪಾದನೆಗಳನ್ನು ಇತ್ತೀಚೆಗಿನ ಬದಲಾವಣೆಗಳಲ್ಲಿ ಅಡಗಿಸು",
        "tog-newpageshidepatrolled": "ಪಹರೆಯಲ್ಲಿ ಆದ ಪುಟಗಳನ್ನು ಹೊಸ ಪುಟಗಳ ಪಟ್ಟಿಯಲ್ಲಿ ಅಡಗಿಸು",
        "tog-extendwatchlist": "ಕೇವಲ ಇತ್ತೀಚೆಗಿನ ಬದಲಾವಣೆಗಳಲ್ಲದೆ, ಸಂಬಂಧಿತ ಎಲ್ಲಾ ಬದಲಾವಣೆಗಳನ್ನು ತೋರುವಂತೆ ಪಟ್ಟಿಯನ್ನು ವಿಸ್ತರಿಸಿ",
        "morenotlisted": "ಈ ಪಟ್ಟಿ ಪೂರ ಇಲ್ಲ.",
        "mypage": "ಪುಟ",
        "mytalk": "ಚರ್ಚೆ",
-       "anontalk": "à²\88 à²\90.ಪಿ à²\97à³\86 à²®à²¾à²¤à²¨à²¾à²¡à²¿",
+       "anontalk": "à²\9aರà³\8dà²\9aà³\86",
        "navigation": "ಸಂಚರಣೆ",
        "and": "&#32;ಮತ್ತು",
        "qbfind": "ಹುಡುಕು",
        "nstab-image": "ಚಿತ್ರ",
        "nstab-mediawiki": "ಸಂದೇಶ",
        "nstab-template": "ಟೆಂಪ್ಲೇಟು",
-       "nstab-help": "ಸಹಾಯ",
+       "nstab-help": "ಸಹಾಯ ಪುಟ",
        "nstab-category": "ವರ್ಗ",
        "mainpage-nstab": "ಮುಖ್ಯ ಪುಟ",
        "nosuchaction": "ಆ ರೀತಿಯ ಕೃತ್ಯ ಯಾವುದೂ ಇಲ್ಲ",
        "missingcommenttext": "ಕೆಳಗೆ ಒಂದು ಟಿಪ್ಪಣಿ ನಮೂದಿಸಿ",
        "missingcommentheader": "'''ಗಮನಿಸಿ:''' ಈ ವ್ಯಾಖ್ಯಾನಕ್ಕೆ ವಿಷಯ ಅಥವ ತಲೆಬರಹ ನೀವು ಸೂಚಿಸಿಲ್ಲ. ನೀವು \"{{int:savearticle}}\"\nಮತ್ತೊಮೆ ಒತ್ತಿದರೆ ನಿಮ್ಮ ಸಂಪಾದನೆಯನ್ನು ಹಾಗೆಯೇ ಉಳಿಸಲಾಗುವುದು.",
        "summary-preview": "ತಾತ್ಪರ್ಯ ಮುನ್ನೋಟ:",
-       "subject-preview": "ವಿಷಯದ/ತಲೆಬರಹದ ಮುನ್ನೋಟ:",
+       "subject-preview": "ವಿಷಯದ ಮುನ್ನೋಟ:",
        "blockedtitle": "ಈ ಸದಸ್ಯರನ್ನು ತಡೆ ಹಿಡಿಯಲಾಗಿದೆ.",
        "blockedtext": "'''ನಿಮ್ಮ ಸದಸ್ಯತ್ವವನ್ನು ಅಥವ IP ವಿಳಾಸವನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ.'''\n\n$1 ಅವರು ಈ ನಿರ್ಬಂಧನೆಯನ್ನು ಒಡ್ಡಿರುವರು.\nಇದಕ್ಕೆ ಅವರು ನೀಡಿರುವ ಕಾರಣ: ''$2''.\n\n* ನಿರ್ಬಂಧನೆಯ ಪ್ರಾರಂಭ: $8\n* ನಿರ್ಬಂಧನೆ ಮುಗಿಯುವುದು: $6\n* ನಿರ್ಬಂಧನೆ ಹೇರಲ್ಪಟ್ಟವರು: $7\n\nನೀವು $1 ಅಥವ ಇತರ [[{{MediaWiki:Grouppage-sysop}}|ನಿರ್ವಾಹಕರನ್ನು]] ಈ ನಿರ್ಬಂಧನೆಯನ್ನು ಚರ್ಚಿಸಲು ಸಂಪರ್ಕಿಸಬಹುದು.\nನೀವು 'ಸದಸ್ಯರಿಗೆ ಇ-ಅಂಚೆ ಕಳುಹಿಸಿ' ಸೌಲಭ್ಯವನ್ನು ಉಪಯೋಗಿಸಲು ನಿಮ್ಮ \"[[Special:Preferences|ನನ್ನ ಪ್ರಾಶಸ್ತ್ಯಗಳು]]\" ಪುಟದಲ್ಲಿ ನಿಮ್ಮ ಇ-ಅಂಚೆ ವಿಳಾಸವನ್ನು ನೀಡಿರಬೇಕು. ಈಗ ಆ ಪುಟವನ್ನು ನೀವು ಉಪಯೋಗಿಸದಂತೆ ನಿರ್ಬಂಧಿಸಲಾಗಿಲ್ಲ.\nನಿಮ್ಮ ಪ್ರಸಕ್ತ IP ವಿಳಾಸವು $3, ಮತ್ತು ಈ ನಿರ್ಭಂಧನೆಯ ಕ್ರಮಸಂಖ್ಯೆ (ID) #$5.\nದಯವಿಟ್ಟು ನಿಮ್ಮ ಸಂಪರ್ಕಗಳಲ್ಲಿ ಈ ಸಂಖ್ಯೆಗಳನ್ನು ಸೇರಿಸಿ.",
        "autoblockedtext": "$1 ಅವರಿಂದ ತಡೆಹಿಡಿಯಲ್ಪಟ್ಟ ಇನ್ನೊಬ್ಬ ಬಳಕೆದಾರ ನಿಮ್ಮ ಐಪಿ ವಿಳಾಸವನ್ನು ಉಪಯೋಗಿಸುತ್ತಿದ್ದರಿಂದ ಅ ವಿಳಾಸವನ್ನು ಯಾಂತ್ರಿಕವಾಗಿ ತಡೆಹಿಡಿಯಲ್ಪಟ್ಟಿದೆ.\nತಡೆಗೆ ನೀಡಿರುವ ಕಾರಣ:\n\n:''$2''\n\n* ತಡೆಯ ಪ್ರಾರಂಭ: $8\n* ತಡೆಯ ಅಂತ್ಯ: $6\n* ಉದ್ದೇಶಿತ ತಡೆ: $7\n\nನೀವು $1 ಅವರನ್ನು ಅಥವ ಇತರ [[{{MediaWiki:Grouppage-sysop}}|ನಿರ್ವಾಹಕರನ್ನು]] ಈ ತಡೆಯನ್ನು ಚರ್ಚಿಸಲು ಸಂಪರ್ಕಿಸಬಹುದು.\nಗಮನಿಸಿ: ನಿಮ್ಮ [[Special:Preferences|ಬಳಕೆದಾರ ಪ್ರಾಶಸ್ತ್ಯಗಳಲ್ಲಿ]] ಧೃಡೀಕೃತ ಇ-ಅಂಚೆ ವಿಳಾಸ ನೀಡಿದ್ದಲ್ಲಿ ಮತ್ತು ಅದನ್ನು ಉಪಯೋಗಿಸದಂತೆ ತಡೆಹಿಡಿಯಲ್ಪಟ್ಟಿಲ್ಲದಿದ್ದಲ್ಲಿ ಮಾತ್ರ \"ಸದಸ್ಯರಿಗೆ ಇ-ಅಂಚೆ ಕಳಿಸಿ\" ಸೌಲಭ್ಯವನ್ನು ಉಪಯೋಗಿಸಬಹುದು.\n\nನಿಮ್ಮ ಪ್ರಸ್ತಕ ಐಪಿ ವಿಳಾಸ $3, ಮತ್ತು ಈ ತಡೆಯ ಸಂಖ್ಯೆ $5.\nನೀವು ಸಂಪರ್ಕಿಸಿದಾಗ ದಯವಿಟ್ಟು ಈ ವಿವರಣೆಗಳನ್ನು ಸೇರಿಸಿ.",
        "group-user-member": "ಬಳಕೆದಾರ",
        "group-autoconfirmed-member": "ಸ್ವಧೃಡೀಕೃತ ಬಳಕೆದಾರ",
        "group-bot-member": "{{ಲಿಂಗ:$1|ಬೋಟ್}}",
-       "group-sysop-member": "{{ಲಿಂಗ:$1|ಮಾಜಿ ಆಡಳಿತಗಾರ}}",
-       "group-bureaucrat-member": "ಮೇಲ್ವಿಚಾರಕ",
+       "group-sysop-member": "{{GENDER:$1|ನಿರ್ವಾಹಕ}}",
+       "group-bureaucrat-member": "{{GENDER:$1|ಮೇಲ್ವಿಚಾರಕ}}",
        "group-suppress-member": "ನಿಗ ಇಡುವವ",
        "grouppage-user": "{{ns:project}}:ಬಳಕೆದಾರರು",
        "grouppage-autoconfirmed": "{{ns:project}}:ಸ್ವಧೃಡೀಕೃತ ಬಳಕೆದಾರರು",
        "movepagetalktext": "ಜೊತೆಗಿನ ಚರ್ಚೆ ಪುಟವೂ ಸ್ಥಳಾಂತರಿಸಲಾಗುವುದು. ಈ ಸ್ಥಳಾಂತರ '''ಆಗದಿರುವ''' ಪ್ರಸಂಗಗಳು:\n*ಸ್ಥಳಾಂತರಿಕೆಯ ಹೆಸರಿನಲ್ಲಿ ಆಗಲೇ ಖಾಲಿಯಲ್ಲದ ಒಂದು ಪುಟವು ಇದ್ದಲ್ಲಿ, ಅಥವ\n*ಕೆಳಗಿನ ಚೌಕದಲ್ಲಿರುವ tick mark ಅನ್ನು ನೀವು ತಗೆದಲ್ಲಿ.\n\nಈ ಪ್ರಸಂಗಗಳಲ್ಲಿ ನೀವು ಸ್ವತಃ ಚರ್ಚೆ ಪುಟವನ್ನು ಸ್ಥಳಾಂತರಿಸಬೇಕು ಅಥವ ಒಂದುಗೂಡಿಸಬೇಕು.",
        "movenologintext": "ಪುಟವನ್ನು ಸ್ಥಳಾಂತರಿಸಲು ನೀವು ನೋಂದಾಯಿತ ಸದಸ್ಯರಾಗಿದ್ದು [[Special:UserLogin|ಲಾಗಿನ್]] ಆಗಿರಬೇಕು.",
        "movenotallowed": "ನಿಮಗೆ {{SITENAME}} ಅಲ್ಲಿ ಪುಟಗಳನ್ನು ಸ್ಥಳಾಂತರಿಸುವ ಅನುಮತಿ ಇಲ್ಲ.",
-       "newtitle": "à²\88 à²¹à³\8aಸ à²¶à³\80ರà³\8dಷಿà²\95à³\86à²\97ೆ:",
+       "newtitle": "ಹà³\8aಸ à²¶à³\80ರà³\8dಷಿà²\95ೆ:",
        "move-watch": "ಈ ಪುಟವನ್ನು ವೀಕ್ಷಿಸು",
        "movepagebtn": "ಪುಟವನ್ನು ಸ್ಥಳಾಂತರಿಸಿ",
        "pagemovedsub": "ಸ್ಥಳಾಂತರಿಸುವಿಕೆ ಯಶಸ್ವಿಯಾಯಿತು",
index 110cc23..0d5f011 100644 (file)
        "mostrevisions": "가장 많이 편집된 문서 목록",
        "prefixindex": "접두어에 따른 문서 목록",
        "prefixindex-namespace": "접두어가 있는 모든 문서 ($1 이름공간)",
+       "prefixindex-submit": "보이기",
        "prefixindex-strip": "목록에서 접두어 생략",
        "shortpages": "짧은 문서 목록",
        "longpages": "긴 문서 목록",
        "protectedpages-performer": "보호한 사용자",
        "protectedpages-params": "보호 변수",
        "protectedpages-reason": "이유",
+       "protectedpages-submit": "문서 보이기",
        "protectedpages-unknown-timestamp": "알 수 없음",
        "protectedpages-unknown-performer": "알 수 없는 사용자",
        "protectedtitles": "만들기 보호된 표제어 목록",
        "protectedtitles-summary": "이 페이지는 현재 만들기 보호가 설정되어 있는 문서 제목을 나열합니다. 보호된 기존 문서들의 목록을 보려면 [[{{#special:ProtectedPages}}|{{int:protectedpages}}]]을 보세요.",
        "protectedtitlesempty": "해당 조건에 맞는 만들기 금지 표제어가 없습니다.",
+       "protectedtitles-submit": "제목 보이기",
        "listusers": "사용자 목록",
        "listusers-editsonly": "기여가 있는 사용자만 보기",
        "listusers-creationsort": "계정을 만든 날짜순으로 정렬",
        "usereditcount": "{{PLURAL:$1|편집}} $1회",
        "usercreated": "$1 $2에 계정이 {{GENDER:$3|만들어짐}}",
        "newpages": "새 문서 목록",
+       "newpages-submit": "보이기",
        "newpages-username": "사용자 이름:",
        "ancientpages": "오래된 문서 목록",
        "move": "이동",
        "cachedspecial-viewing-cached-ts": "현재 이 문서는 캐시 처리된 버전으로 현재 문서 상태를 반영하지 않을 수도 있습니다.",
        "cachedspecial-refresh-now": "최신 버전 보기.",
        "categories": "분류 목록",
+       "categories-submit": "보이기",
        "categoriespagetext": "문서나 자료를 {{PLURAL:$1|포함하고 있는 분류}} 목록입니다.\n[[Special:UnusedCategories|사용되지 않는 분류]]는 여기에 보이지 않습니다.\n[[Special:WantedCategories|필요한 분류]]도 참조하세요.",
        "categoriesfrom": "다음으로 시작하는 분류를 보여주기:",
        "special-categories-sort-count": "갯수 순으로 정렬",
        "activeusers-hidebots": "봇을 숨기기",
        "activeusers-hidesysops": "관리자를 숨기기",
        "activeusers-noresult": "사용자가 없습니다.",
+       "activeusers-submit": "활동하고 있는 사용자 보이기",
        "listgrouprights": "사용자 권한 목록",
        "listgrouprights-summary": "다음은 이 위키에 있는 사용자 권한 그룹의 목록입니다.\n각각의 권한에 대해서는 [[{{MediaWiki:Listgrouprights-helppage}}|추가 정보]]를 참조하세요.",
        "listgrouprights-key": "범례:\n* <span class=\"listgrouprights-granted\">부여된 권한</span>\n* <span class=\"listgrouprights-revoked\">해제된 권한</span>",
        "delete-confirm": "\"$1\" 삭제",
        "delete-legend": "삭제",
        "historywarning": "<strong>경고:</strong> 삭제하려고 하는 문서에 {{PLURAL:$1|판}} $1개의 역사가 있습니다:",
+       "historyaction-submit": "보이기",
        "confirmdeletetext": "문서와 문서 역사를 삭제하려고 합니다.\n삭제하려는 문서가 맞는지, 이 문서를 삭제하는 것이 [[{{MediaWiki:Policy-url}}|정책]]에 맞는 행동인지를 확인해 주세요.",
        "actioncomplete": "동작 완료",
        "actionfailed": "명령 실패",
        "whatlinkshere-hidelinks": "링크를 $1",
        "whatlinkshere-hideimages": "파일 링크를 $1",
        "whatlinkshere-filters": "필터",
+       "whatlinkshere-submit": "계속",
        "autoblockid": "자동 차단 #$1",
        "block": "사용자 차단",
        "unblock": "사용자 차단 해제",
index d378025..0eaae57 100644 (file)
        "laggedslavemode": "'''Opgepasst:''' Dës Säit ass net onbedéngt um neiste Stand.",
        "readonly": "D'Datebank ass gespaart",
        "enterlockreason": "Gitt w.e.g. e Grond u firwat d'Datebank gespaart ass, a wéi laang dës Spär ongeféier bestoe soll.",
-       "readonlytext": "D'Datebank ass elo fir all Ännerunge gespaart, wahrscheinlech wéinst Maintenance vun der Datebank, duerno ass erëm alles beim alen.\n\nDen Administrateur huet dës Erklärung uginn: $1",
+       "readonlytext": "D'Datebank ass elo fir all Ännerunge gespaart, wahrscheinlech wéinst Maintenance vun der Datebank, duerno ass erëm alles beim Alen.\n\nDe System-Administrateur, deen se gespaart huet, huet dës Erklärung uginn: $1",
        "missing-article": "Den Text „$1“ $2 gouf net an der Datebank fonnt.\n\nDat geschitt normalerweis duerch e Link op eng Säit déi geläscht oder geréckelt gouf.\n\nWann dat net de Fall ass, hutt Dir eventuell e Feeler an der Software fonnt.\nMellt dëst w.e.g. bei engem [[Special:ListUsers/sysop|Administrateur]] a vergiesst net d'URL unzeginn.",
        "missingarticle-rev": "(Versiounsnummer: $1)",
        "missingarticle-diff": "(Ënnerscheed tëscht Versiounen: $1, $2)",
        "wrongpasswordempty": "D'Passwuert dat Dir aginn hutt war eidel.\nProbéiert w.e.g. nach eng Kéier.",
        "passwordtooshort": "Passwierder musse mindestens {{PLURAL:$1|1 Zeeche|$1 Zeeche}} laang sinn.",
        "passwordtoolong": "Passwierder kënnen net méi laang wéi {{PLURAL:$1|1 Zeeche|$1 Zeeche}} sinn.",
+       "passwordtoopopular": "Dacks gewielt Passwierder kënnen net benotzt ginn. Sicht Iech w.e.g. e méi e spezifescht Passwuert.",
        "password-name-match": "Äert Passwuert muss verschidde vun Ärem Benotzernumm sinn.",
        "password-login-forbidden": "D'Benotze vun dësem Benotzernumm a Passwuert gouf verbueden.",
        "mailmypassword": "Passwuert zrécksetzen",
        "passwordreset-emailtext-ip": "Iergendee mat der IP-Adress $1, wahrscheinlech Dir selwer, huet d'Zrécksetze vun Ärem Passwuert op {{SITENAME}} gefrot ($4). {{PLURAL:$3|De Benotzerkont ass|D'Benutzerkonte si}} mat dëser E-Mail-Adress verbonn:\n\n$2\n\n{{PLURAL:$3|Dëst temporärt Passwuert leeft|Dës temporär Passwierder lafe}} bannent {{PLURAL:$5|engem Dag|$5 Deeg}} of.\nDir sollt Iech aloggen an een neit Passwuert festleeën. Wann een Aneren déi Ufro gemaach huet oder Dir Iech erëm un Äert Passwuert erënnere kënnt an et net ännere wëllt, kënnt Dir dës Noriicht ignoréieren an Äert aalt Passwuert weider benotzen.",
        "passwordreset-emailtext-user": "De Benotzer $1 vu(n) {{SITENAME}} huet d'Zrécksetze vun Ärem Passwuert op {{SITENAME}} gefrot ($4). {{PLURAL:$3|De Benotzerkont|D'Benutzerkonte}} \n\n$2\n\n{{PLURAL:$3|ass|si}} mat dëser E-Mail-Adress verbonn.\n\n{{PLURAL:$3|Dëst temporärt Passwuert leeft|Dës temporär Passwierder lafe}} bannent {{PLURAL:$5|engem Dag|$5 Deeg}} of.\nDir sollt Iech aloggen an een neit Passwuert festleeën. Wann een Aneren déi Ufro gemaach huet oder Dir Iech erëm un Äert Passwuert erënnere kënnt an et net ännere wëllt, kënnt Dir dës Noriicht ignoréieren an Äert aalt Passwuert weider benotzen.",
        "passwordreset-emailelement": "Benotzernumm: \n$1\n\nTemporärt Passwuert: \n$2",
-       "passwordreset-emailsentemail": "Wann dëst eng registréiert E-Mailadress vun Ärem benotzerkont ass da gëtt Eng E-Mail fir d'Passwuert zréckzesetze geschéckt.",
+       "passwordreset-emailsentemail": "Wann dës E-Mailadress mat Ärem Benotzerkont assoziéiert ass, da gëtt Eng E-Mail fir d'Passwuert zréckzesetze geschéckt.",
+       "passwordreset-emailsentusername": "Wann eng E-Mailadress mat dësem Benotzernumm associéiert ass, da gëtt Eng E-Mail fir d'Passwuert zeréckzesetze geschéckt.",
        "passwordreset-emailsent-capture": "Eng Mail fir d'Passwuert zréckzesetze gouf geschéckt, Dir gesitt se hei drënner.",
        "passwordreset-emailerror-capture": "Eng Mail fir d'Passwuert zréckzesetze gouf geschéckt, Dir gesitt se hei drënner, awer de {{GENDER:$2|Benotzer}} konnt se net kréien: $1",
        "changeemail": "E-Mail-Adress änneren oder ewechhuelen",
index 4fcf27a..b5e5065 100644 (file)
        "passwordreset-emailtext-user": "{{SITENAME}} സംരംഭത്തിലെ ഉപയോക്താവായ $1 {{SITENAME}} സംരംഭത്തിലെ ($4) രഹസ്യവാക്ക് പുനർസജ്ജീകരിക്കാൻ അഭ്യർത്ഥിച്ചിരിക്കുന്നു. ഈ ഇമെയിൽ വിലാസവുമായി ബന്ധപ്പെട്ടിരിക്കുന്ന {{PLURAL:$3|അംഗത്വം|അംഗത്വങ്ങൾ}} താഴെക്കൊടുത്തിരിക്കുന്നു:\n\n$2\n\n\nഈ {{PLURAL:$3|താത്കാലിക രഹസ്യവാക്ക്|താത്കാലിക രഹസ്യവാക്കുകൾ}} {{PLURAL:$5|ഒരു ദിവസം|$5 ദിവസങ്ങൾ}} കൊണ്ട് കാലഹരണപ്പെട്ട് പോകുന്നവയാണ്.\nതാങ്കൾ ഇപ്പോൾ തന്നെ പ്രവേശിച്ച് രഹസ്യവാക്ക് മാറ്റുന്നതാണ് ഉചിതം. ഈ അഭ്യർത്ഥന മറ്റാരോ ആണ് നടത്തിയത് അല്ലെങ്കിൽ, യഥാർത്ഥ രഹസ്യവാക്ക് താങ്കൾ ഓർമ്മിക്കുകയും അത് മാറ്റാൻ ആഗ്രഹിക്കാതിരിക്കുകയും ആണെങ്കിൽ, ഈ സന്ദേശം അവഗണിച്ച് താങ്കളുടെ പഴയ രഹസ്യവാക്ക് തുടർന്നും ഉപയോഗിക്കാവുന്നതാണ്.",
        "passwordreset-emailelement": "ഉപയോക്തൃനാമം: \n$1\n\nതാത്കാലിക രഹസ്യവാക്ക്: \n$2",
        "passwordreset-emailsentemail": "താങ്കളുടെ അംഗത്വത്തിന് നൽകിയിട്ടുള്ള ഇമെയിൽ വിലാസം ഇതാണെങ്കിൽ,  രഹസ്യവാക്ക് പുനർസജ്ജീകരണ ഇമെയിൽ അയക്കുന്നതാണ്.",
-       "passwordreset-emailsentusername": "ഒരു ഇമെയിൽ വിലാസം ചേർത്തിട്ടുണ്ടെങ്കിൽ,  രഹസ്യവാക്ക് പുനർസജ്ജീകരണ ഇമെയിൽ അയക്കുന്നതാണ്.",
+       "passwordreset-emailsentusername": "à´\88 à´\89പയàµ\8bà´\95àµ\8dà´¤àµ\83നാമതàµ\8dതിനàµ\8d à´\92à´°àµ\81 à´\87à´®àµ\86യിൽ à´µà´¿à´²à´¾à´¸à´\82 à´\9aàµ\87ർതàµ\8dതിà´\9fàµ\8dà´\9fàµ\81à´£àµ\8dà´\9fàµ\86à´\99àµ\8dà´\95ിൽ,  à´°à´¹à´¸àµ\8dയവാà´\95àµ\8dà´\95àµ\8d à´ªàµ\81നർസà´\9càµ\8dà´\9càµ\80à´\95à´°à´£ à´\87à´®àµ\86യിൽ à´\85à´¯à´\95àµ\8dà´\95àµ\81à´¨àµ\8dനതാണàµ\8d.",
        "passwordreset-emailsent-capture": "രഹസ്യവാക്ക് പുനർസജ്ജീകരണ ഇമെയിൽ അയച്ചിട്ടുണ്ട്, അത് താഴെക്കൊടുക്കുന്നു.",
        "passwordreset-emailerror-capture": "താഴെക്കൊടുത്തിരിക്കുന്ന, രഹസ്യവാക്ക് പുനർസജ്ജീകരണ ഇമെയിൽ സൃഷ്ടിക്കാനായെങ്കിലും, അത് {{GENDER:$2|ഉപയോക്താവിന്}} അയയ്ക്കുന്നത് പരാജയപ്പെട്ടു: $1",
        "changeemail": "ഇമെയിൽ വിലാസം മാറ്റുക അല്ലെങ്കിൽ നീക്കംചെയ്യുക",
        "foreign-structured-upload-form-label-own-work-message-shared": "ഈ പ്രമാണത്തിന്റെ പകർപ്പവകാശം എനിക്ക് സ്വന്തമാണെന്നും, ഈ പ്രമാണം വിക്കിമീഡിയ കോമൺസിൽ പിന്നീട് മാറ്റാനാവത്തവിധം [https://creativecommons.org/licenses/by-sa/4.0/ ക്രിയേറ്റീവ് കോമൺസ് ആട്രിബ്യൂഷൻ-ഷെയർഎലൈക് 4.0] ഉപയോഗാനുമതിയിൽ പ്രസിദ്ധീകരിക്കാമെന്നും [https://wikimediafoundation.org/wiki/Terms_of_Use/ml ഉപയോഗനിബന്ധനകൾ] അംഗീകരിക്കുന്നുവെന്നും സാക്ഷ്യപ്പെടുത്തുന്നു.",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "ഈ പ്രമാണത്തിന്റെ പകർപ്പവകാശം താങ്കളുടെ സ്വന്തമല്ലെങ്കിൽ അഥവാ മറ്റൊരു ഉപയോഗാനുമതിയിലാണ് പ്രമാണം പ്രസിദ്ധീകരിക്കാൻ ഉദ്ദേശിക്കുന്നതെങ്കിൽ [https://commons.wikimedia.org/wiki/Special:UploadWizard?uselang=ml കോമൺസിലെ അപ്‌ലോഡ് സഹായി] ഉപയോഗിക്കുന്നത് പരിഗണിക്കുക.",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "ഈ പ്രമാണം അവരുടെ നയങ്ങൾക്കനുസൃതമായി അപ്‌ലോഡ് ചെയ്യാൻ സൈറ്റ് അനുവദിക്കുമെങ്കിൽ [[Special:Upload|{{SITENAME}} സംരംഭത്തിലെ അപ്‌ലോഡ് താൾ]] പരീക്ഷിച്ചു നോക്കാവുന്നതാണ്.",
+       "foreign-structured-upload-form-2-label-intro": "{{SITENAME}} സംരംഭത്തിലേക്ക് ഒരു ചിത്രം സംഭാവന ചെയ്യുന്നതിന് നന്ദി. നിബന്ധനകൾ പാലിക്കുന്നുണ്ടോയെന്ന് പരിശോധിക്കുക:",
+       "foreign-structured-upload-form-2-label-ownwork": "ഇത് <strong>താങ്കളുടെ സ്വന്തം സൃഷ്ടി</strong> ആയിരിക്കണം, ഇന്റർനെറ്റിൽ നിന്ന് സംഘടിപ്പിച്ചത് ആയിരിക്കരുത്",
+       "foreign-structured-upload-form-2-label-noderiv": "ഇതിൽ <strong>മറ്റുള്ളവരുടെ സൃഷ്ടികൾ ഉൾപ്പെടരുത്</strong>, അവയിൽ നിന്ന് പ്രചോദിതമായി സൃഷ്ടിച്ചതും പാടില്ല",
+       "foreign-structured-upload-form-2-label-useful": "ഇത് <strong>വൈജ്ഞാനിക  മൂല്യമുള്ളതും</strong> മറ്റുള്ളവർക്ക് വിദ്യാഭ്യാസാവശ്യങ്ങൾക്ക് ഉപയോഗിക്കാനാകുന്നതുമാവണം",
+       "foreign-structured-upload-form-2-label-ccbysa": "ഇത് ഇന്റർനെറ്റിൽ  [https://creativecommons.org/licenses/by-sa/4.0/ ക്രിയേറ്റീവ് കോമൺസ് ആട്രിബ്യൂഷൻ-ഷെയർഎലൈക് 4.0] ഉപയോഗാനുമതിയിൽ <strong>എന്നെന്നേയ്ക്കുമായി പ്രസിദ്ധീകരിക്കുന്നതുമാവണം</strong>",
+       "foreign-structured-upload-form-2-label-alternative": "മുകളിൽ കൊടുത്തിരിക്കുന്നതത്രയും പാലിക്കുന്നില്ലെങ്കിലും, അതൊരു സ്വതന്ത്ര ഉപയോഗാനുമതിയിൽ ഉള്ളതാണെങ്കിൽ [https://commons.wikimedia.org/wiki/Special:UploadWizard കോമൺസിലെ അപ്‌ലോഡ് സഹായി] ഉപയോഗിച്ച് താങ്കൾക്ക് ഈ പ്രമാണം അപ്‌ലോഡ് ചെയ്യാൻ സാധിച്ചേക്കാം.",
+       "foreign-structured-upload-form-2-label-termsofuse": "ഈ പ്രമാണം അപ്‌ലോഡ് ചെയ്യുന്നത് വഴി, ഈ പ്രമാണത്തിന്റെ പകർപ്പവകാശം താങ്കൾക്ക് സ്വന്തമാണെന്ന് താങ്കൾ സാക്ഷ്യപ്പെടുത്തുന്നുണ്ട്, അതോടൊപ്പം ഈ പ്രമാണം ഇനി മാറ്റാനാവാത്ത വിധം വിക്കിമീഡിയ കോമൺസിൽ ക്രിയേറ്റീവ് കോമൺസ് ആട്രിബ്യൂഷൻ-ഷെയർഎലൈക് 4.0 ഉപയോഗാനുമതി പ്രകാരമാണ് താങ്കൾ പ്രസിദ്ധീകരിക്കുന്നതെന്നും, [https://wikimediafoundation.org/wiki/Terms_of_Use ഉപയോഗനിബന്ധനകൾക്കും] സമ്മതിക്കുകയും ചെയ്യുന്നുണ്ട്.",
+       "foreign-structured-upload-form-3-label-question-website": "ഈ ചിത്രം ഏതെങ്കിലും വെബ്‌സൈറ്റിൽ നിന്ന് ഡൗൺലോഡ് ചെയ്തതോ, ചിത്രങ്ങൾ തിരഞ്ഞ് ലഭ്യമാക്കിയതോ ആണോ?",
+       "foreign-structured-upload-form-3-label-question-ownwork": "താങ്കൾ ഈ ചിത്രം സ്വന്തമായി (ഫോട്ടോ എടുത്ത്, ചിത്രം വരച്ച് തുടങ്ങിയ രീതികളിൽ) സൃഷ്ടിച്ചതാണോ?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "ഇത് ലോഗോ പോലുള്ള മറ്റാരുടെയെങ്കിലും സൃഷ്ടി ഉൾപ്പെടുന്ന ചിത്രം ആണോ, അല്ലെങ്കിൽ അതിൽ നിന്ന് പ്രചോദനം ഉൾക്കൊണ്ട് സൃഷ്ടിച്ചതാണോ?",
+       "foreign-structured-upload-form-3-label-yes": "അതെ",
+       "foreign-structured-upload-form-3-label-no": "അല്ല",
+       "foreign-structured-upload-form-3-label-alternative": "അങ്ങനെയെങ്കിൽ നിർഭാഗ്യവശാൽ, ഈ ഉപകരണം ഉപയോഗിച്ച് ഈ പ്രമാണം അപ്‌ലോഡ് ചെയ്യുന്നത് പിന്തുണയ്ക്കാനാവില്ല. അതൊരു സ്വതന്ത്ര ഉപയോഗാനുമതിയിൽ ഉള്ളതാണെങ്കിൽ [https://commons.wikimedia.org/wiki/Special:UploadWizard കോമൺസിലെ അപ്‌ലോഡ് സഹായി] ഉപയോഗിച്ച് താങ്കൾക്ക് ഈ പ്രമാണം അപ്‌ലോഡ് ചെയ്യാൻ സാധിച്ചേക്കാം.",
+       "foreign-structured-upload-form-4-label-good": "ഈ ഉപകരണം ഉപയോഗിച്ച്, മറ്റുള്ളവരുടെ സൃഷ്ടികൾ ഉൾപ്പെടാത്ത, വിദ്യാഭ്യാസ ആവശ്യങ്ങൾക്ക് താങ്കൾ സൃഷ്ടിച്ച പടങ്ങളോ, താങ്കൾ എടുത്ത ഫോട്ടോഗ്രാഫുകളോ താങ്കൾക്ക് അപ്‌ലോഡ് ചെയ്യാവുന്നതാണ്.",
+       "foreign-structured-upload-form-4-label-bad": "സേർച്ച് എഞ്ചിനിൽ നിന്ന ലഭിച്ച അല്ലെങ്കിൽ മറ്റ് വെബ്‌സൈറ്റുകളിൽ നിന്ന് അപ്‌ലോഡ് ചെയ്ത ചിത്രങ്ങൾ അപ്‌ലോഡ് ചെയ്യാൻ കഴിയില്ല.",
        "backend-fail-stream": "$1 എന്ന പ്രമാണം സ്ട്രീം ചെയ്യാൻ കഴിഞ്ഞില്ല.",
        "backend-fail-backup": "$1 എന്ന പ്രമാണത്തിന്റെ ബാക്ക്അപ് എടുക്കാൻ കഴിഞ്ഞില്ല.",
        "backend-fail-notexists": "$1 എന്ന പ്രമാണം നിലവിലില്ല.",
index 8d21fde..419cd72 100644 (file)
        "createaccount-title": "Cuentah ītlachīhualiz ic {{SITENAME}}",
        "loginlanguagelabel": "Tlahtōlli: $1",
        "pt-login": "Xicalaqui",
+       "pt-login-button": "Xicalaqui",
        "pt-createaccount": "Xicchīhua motlapōhual",
        "changepassword": "Xicpatla motlahtōlichtacāyo",
        "resetpass_header": "Xicpatla motlahtōlichtacāyo",
        "rcshowhideminor": "$1 tlapatlalitzintli",
        "rcshowhideminor-show": "Ticnēxtīz",
        "rcshowhidebots": "$1 tepoztlācah",
+       "rcshowhidebots-show": "Xicnēxti",
        "rcshowhidebots-hide": "Tiquihyānaz",
        "rcshowhideliu": "$1 tēmachiyōmacalli tlatequitiltilīltin",
        "rcshowhideanons": "$1 ahtōcā tlatequitiltilīlli",
        "linksearch-ok": "Tictēmōz",
        "linksearch-line": "$1 tzonhuīlo īxquichca $2",
        "listusers-submit": "Tiquittāz",
+       "activeusers-submit": "Xiquitta",
        "listgrouprights-group": "Olōlli",
        "listgrouprights-rights": "Huelītiliztli",
        "emailuser": "Tiquēhualtlīz maltzinteyōtl netitlaniztli inīn tlatequitiltilīlli",
        "rollback-success": "Ōmotlacuep $1 ītlahcuilōl; āxcān achto $2 ītlahcuilōl.",
        "changecontentmodel-title-label": "Tlaīxtōcāitl",
        "changecontentmodel-reason-label": "Tleīpampa:",
+       "protectlogpage": "Tlapiyaliztlīlmachiyōtīlli",
        "protectedarticle": "ōmoquīxti \"[[$1]]\"",
        "unprotectedarticle": "ōahmoquīxtih «[[$1]]»",
        "prot_1movedto2": "[[$1]] ōmozacac īhuīc [[$2]]",
        "movelogpage": "Tlazacaliztli tlahcuilōlloh",
        "movereason": "Īxtlamatiliztli:",
        "revertmove": "tlacuepāz",
-       "delete_and_move": "Ticpolōz auh ticzacāz",
        "delete_and_move_confirm": "Quēmah, ticpolōz in zāzanilli",
        "immobile-source-namespace": "Ahmo huelīti mozaca zāzanilli tōcātzimpan \"$1\"",
        "immobile-target-namespace": "Ahmo huelīti mozaca zāzanilli tōcātzinhuīc \"$1\"",
        "exif-imagedescription": "Īxiptli ītōcā",
        "exif-software": "Software ōmotēquitilti",
        "exif-artist": "Chīhualōni",
+       "exif-exifversion": "Exif-cuepaliztli",
        "exif-usercomment": "Quihtoa tlatequitiltilīlli",
        "exif-exposuretime": "Cāuhcāyōtl",
        "exif-fnumber": "F Tlapōhualli",
index 4607026..6548aa4 100644 (file)
        "morenotlisted": "यो सूची पूर्ण हैन।",
        "mypage": "पृष्ठ",
        "mytalk": "वार्ता",
-       "anontalk": "यस IP à¤\95à¥\8b à¤µà¤¾à¤°à¥\87मा à¤µà¤¾à¤°à¥\8dतालाप à¤\97रà¥\8dनà¥\81हà¥\8bसà¥\8d",
+       "anontalk": "वारà¥\8dता",
        "navigation": "अन्वेषण",
        "and": "&#32;र",
        "qbfind": "पत्ता लगाउनु",
        "laggedslavemode": "<strong>चेतावनी:</strong> पृष्ठमा हालका अद्यतनहरू नहुनसक्छन् ।",
        "readonly": "डेटाबेस बन्द गरिएको छ",
        "enterlockreason": "ताल्चा मार्नुको कारण दिनुहोस्, साथै ताल्चा हटाउने समयको अवधि अनुमान लगाउनुहोस्।",
-       "readonlytext": "समà¥\8dभवतà¤\83 à¤¨à¤¿à¤¯à¤®à¤¿à¤¤ à¤¡à¥\87à¤\9fाबà¥\87स à¤°à¤\96-रà¤\96ाà¤\89à¤\95à¥\8b à¤\95ारण à¤\85हिलà¥\87लाà¤\88 à¤¨à¤¯à¤¾à¤\81 à¤¡à¥\87à¤\9fाबà¥\87स à¤ªà¥\8dरविषà¥\8dà¤\9fà¥\80 à¤° à¤\85नà¥\8dय à¤¸à¤\82शà¥\8bधनहरà¥\82  à¤¬à¤¨à¥\8dद à¤°à¤¾à¤\96िà¤\8fà¤\95à¥\8bà¤\9b,, à¤\9cसलाà¤\88 à¤ªà¤\9bिबाà¤\9f à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤\97रिनà¥\87à¤\9b। \nपà¥\8dरबनà¥\8dधà¤\95 à¤\9cसलà¥\87 à¤¯à¥\8b à¤¬à¤¨à¥\8dद à¤\97रà¥\87à¤\95ाà¤\9bनà¥\8d, à¤¯à¥\8b à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤¦à¤¿à¤\8fà¤\95ाछन्: $1",
+       "readonlytext": "समà¥\8dभवतà¤\83 à¤¨à¤¿à¤¯à¤®à¤¿à¤¤ à¤¡à¥\87à¤\9fाबà¥\87स à¤°à¥\87à¤\96दà¥\87à¤\96à¤\95à¥\8b à¤\95ारण à¤\85हिलà¥\87लाà¤\88 à¤¨à¤¯à¤¾à¤\81 à¤¡à¥\87à¤\9fाबà¥\87स à¤ªà¥\8dरविषà¥\8dà¤\9fà¥\80 à¤° à¤\85नà¥\8dय à¤¸à¤\82शà¥\8bधनहरà¥\82 à¤¬à¤¨à¥\8dद à¤°à¤¾à¤\96िà¤\8fà¤\95à¥\8b à¤\9b, à¤\9cसलाà¤\88 à¤ªà¤\9bिबाà¤\9f à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤\97रिनà¥\87à¤\9b। \nपà¥\8dरबनà¥\8dधà¤\95 à¤\9cसलà¥\87 à¤¯à¥\8b à¤¬à¤¨à¥\8dद à¤\97रà¥\87à¤\95ा à¤\9bनà¥\8d, à¤¯à¥\8b à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤¦à¤¿à¤\8fà¤\95ा छन्: $1",
        "missing-article": "नाम \"$1\" $2 भएको भेटिनु पर्ने पृष्ठको पाठ डेटाबेसले  भेटाइएन, \n\nयस्तो प्राय: मिति नाघिसकेको भिन्न वा इतिहास वा कुनै मेटिसकेको पानाको लिंक पहिल्याउनाले हुन्छ ।\n\nयदि यस्तो भएको होइन भने सफ्टवेयरको त्रुटि पनि हुनसक्छ ।\nकृपया यसको url खुलाइ [[Special:ListUsers/sysop|प्रबन्धक]]लाई उजुरी गर्नुहोस्",
        "missingarticle-rev": "(संशोधन #: $1)",
        "missingarticle-diff": "(परि: $1, $2)",
        "watchlist-submit": "देखाउनुहोस्",
        "wlshowhideminor": "सामान्य सम्पादनहरू",
        "wlshowhidebots": "बोटहरू",
+       "wlshowhideliu": "दर्ता गरिएका प्रयोगकर्ताहरू",
+       "wlshowhideanons": "अज्ञात प्रयोगकर्ताहरू",
+       "wlshowhidepatr": "गस्ती गरिएका सम्पादनहरू",
+       "wlshowhidemine": "मेरा सम्पादनहरू",
+       "wlshowhidecategorization": "पृष्ठ श्रेणीकरण",
        "watchlist-options": "निगरानि सूची विकल्प",
        "watching": "निगरानी गर्दै...",
        "unwatching": "निगरानीबाट हटाउँदै...",
index 87470bf..0d02549 100644 (file)
        "mediastatistics-summary": "Used to explain that this page only does statistics over current versions of files. \"Old\" versions of files and deleted files are not counted.",
        "mediastatistics-nfiles": "{{optional}}\nEntry in table on [[Special:MediaStatistics]] that gives total number of files. $1 - number of files. $2 - percentage of total files that is this type (percent will be formatted to have about 3 interesting digits. e.g. 0.121 or 10.2)",
        "mediastatistics-nbytes": "Combined space of this type of file. Bytes and \"human units\" are shown so that users can better get a sense of magnitude when making comparisons.\n*$1 - total space in bytes.\n*$2 - total space in \"human units\" (i.e. KB, MB, GB, etc)\n*$3 - What percentage of the space all uploads take up does this file take up.",
-       "mediastatistics-bytespertype": "Combined space of one section of [[Special:MediaStatistics]]. \n*$1 - total space in bytes",
-       "mediastatistics-allbytes": "Combined space of all uploaded files. \n*$1 - total space in bytes",
+       "mediastatistics-bytespertype": "Combined space of one section of [[Special:MediaStatistics]]. \n*$1 - total space in bytes\n*$2 - total space in \"human units\" (i.e. KB, MB, GB, etc)\n*$3 - What percentage of the space all uploads take up does this file take up.",
+       "mediastatistics-allbytes": "Combined space of all uploaded files. \n*$1 - total space in bytes\n*$2 - total space in \"human units\" (i.e. KB, MB, GB, etc)",
        "mediastatistics-table-mimetype": "Header for table on Special:MediaStatistics. Column that lists MIME types (The values in this column will look like 'image/jpeg', and be linked to Special:MIMESearch).",
        "mediastatistics-table-extensions": "Header for column in tables on [[Special:MediaStatistics]] that lists possible extensions for a given file type. (The values in this column will be a comma separated list of file extensions, such as '.webm' or '.png, .apng').",
        "mediastatistics-table-count": "Column header on Special:MediaStatistics for the number of files column. The headers in this column use {{msg-mw|mediastatistics-nfiles}}.",
index 2730515..abbea4e 100644 (file)
        "search-interwiki-caption": "Dokterėnē pruojektā",
        "search-interwiki-default": "Soėiškuota nug $1ː",
        "search-interwiki-more": "(daugiau)",
-       "search-relatedarticle": "Sosėjėn",
-       "searchrelated": "sosėjėn",
+       "search-relatedarticle": "Sosėjė̄",
+       "searchrelated": "sosėjė̄",
        "searchall": "vėsė",
        "showingresults": "Žemiau ruodoma lėgė '''$1''' {{PLURAL:$1|rezoltata|rezoltatu|rezoltatu}} pradedont #'''$2'''.",
        "showingresultsinrange": "Apatiuo ruod lėgė {{PLURAL:$1|<strong>1</strong> gavėnė|<strong>$1</strong> gavėniū}} nug #<strong>$2</strong> lėgė #<strong>$3</strong>.",
        "sp-contributions-toponly": "Ruodītė tėktās paskiausius keitėmus",
        "sp-contributions-newonly": "Ruodītė tėktās tūs keitėmus, katrās padėrbtė straipsnē",
        "sp-contributions-submit": "Ėiškuotė",
-       "whatlinkshere": "Sosėjėn straipsnē",
+       "whatlinkshere": "Sosėjė̄ straipsnē",
        "whatlinkshere-title": "Poslapē, katrėi ruod i \"$1\"",
        "whatlinkshere-page": "Poslapis:",
        "linkshere": "Anėi poslapē ruod i '''[[:$1]]''':",
        "logentry-delete-delete": "$1 ėštrīnė poslapi $3",
        "logentry-delete-restore": "$1 atkūrė poslapi $3",
        "revdelete-content-hid": "torėnīs pakavuots",
-       "logentry-block-block": "ožgīnė „[[$1]]“ nug dėrbėma, tas vēk ton čiesa - $2 $3",
+       "logentry-block-block": "$1 {{GENDER:$2|ožgīnė}} {{GENDER:$4|$3}} nug dėrbėma, tas vēk ton čiesa - $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|parvadėna}} poslapi $3 i $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|parvadėna}} poslapi nug $3 i $4 nepalinkdoms nusokėma",
        "logentry-move-move_redir": "$1 {{GENDER:$2|parvadėna}} poslapi nog $3 i $4 ont bovosė nusokėma",
index c0eaef3..fb26815 100644 (file)
        "hidden-categories": "{{PLURAL:$1|1=Яшерен төркем|Яшерен төркемнәр}}",
        "hidden-category-category": "Яшерен төркемнәр",
        "category-subcat-count": "{{PLURAL:$2|1=Әлеге төркем бары тик бу астөркемне генә үз өченә ала.|Әлеге төркемдә $2 астөркемдән бары тик $1 {{PLURAL:$1|астөркем}} генә күрсәтелгән.}}",
-       "category-subcat-count-limited": "Бу төркемдә {{PLURAL:$1|$1 төркемчә}} бар.",
+       "category-subcat-count-limited": "Бу төркемдә {{PLURAL:$1|$1 төркемчә}}.",
        "category-article-count": "{{PLURAL:$2|1=Әлеге төркемдә бер генә бит бар.|Әлеге төркемнең $2 {{PLURAL:$2|битеннән}} {{PLURAL:$1|$1 бит}} кенә курсәтелгән.}}",
-       "category-article-count-limited": "Бу төркемдә {{PLURAL:$1|$1 бит}} бар.",
+       "category-article-count-limited": "Бу төркемдә {{PLURAL:$1|$1 бит|1=бары тик бер бит}}.",
        "category-file-count": "{{PLURAL:$2|1=Әлеге төркемдә бер генә файл бар.|Әлеге төркемдә $2 {{PLURAL:$2|файлдан}} {{PLURAL:$1|$1 файл}} гына курсәтелгән.}}",
-       "category-file-count-limited": "Бу төркемдә {{PLURAL:$1|$1 файл}} бар.",
+       "category-file-count-limited": "Бу төркемдә {{PLURAL:$1|$1 файл|1=бары тик бер файл}}.",
        "listingcontinuesabbrev": "дәвамы",
        "index-category": "Индексланган битләр",
        "noindex-category": "Индексланмаган битләр",
index c2e5433..48af44d 100644 (file)
@@ -12,3 +12,39 @@ $fallback = 'fa';
 
 $rtl = true;
 
+$namespaceNames = array(
+       NS_MEDIA            => 'مديا',
+       NS_SPECIAL          => 'خاص',
+       NS_TALK             => 'گب',
+       NS_USER             => 'کارگير',
+       NS_USER_TALK        => 'کارگيرˇ_گب',
+       NS_PROJECT_TALK     => 'مدي_$1',
+       NS_FILE             => 'فاىل',
+       NS_FILE_TALK        => 'فاىلˇ_گب',
+       NS_MEDIAWIKI        => 'مدياويکي',
+       NS_MEDIAWIKI_TALK   => 'مدياويکي_گب',
+       NS_TEMPLATE         => 'قالب',
+       NS_TEMPLATE_TALK    => 'قالبˇ_گب',
+       NS_HELP             => 'رانما',
+       NS_HELP_TALK        => 'رانما_گب',
+       NS_CATEGORY         => 'جرگه',
+       NS_CATEGORY_TALK    => 'جرگه_گب',
+);
+
+$namespaceAliases = array(
+       // Aliases from old Persian (fa) namespace names
+       'ویژه' => NS_SPECIAL,
+       'بحث' => NS_TALK,
+       'کاربر' => NS_USER,
+       'بحث_کاربر' => NS_USER_TALK,
+       'بحث_$1' => NS_PROJECT_TALK,
+       'پرونده' => NS_FILE,
+       'بحث_پرونده' => NS_FILE_TALK,
+       'بحث_مدیاویکی' => NS_MEDIAWIKI_TALK,
+       'الگو' => NS_TEMPLATE,
+       'بحث_الگو' => NS_TEMPLATE_TALK,
+       'راهنما' => NS_HELP,
+       'بحث_راهنما' => NS_HELP_TALK,
+       'رده' => NS_CATEGORY,
+       'بحث_رده' => NS_CATEGORY_TALK,
+);
index 3f8a899..f3e2db0 100644 (file)
@@ -22,7 +22,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
- * @todo Report PHP version, OS ..
  * @file
  * @ingroup Benchmark
  */
@@ -39,7 +38,7 @@ abstract class Benchmarker extends Maintenance {
 
        public function __construct() {
                parent::__construct();
-               $this->addOption( 'count', "How many time to run a benchmark", false, true );
+               $this->addOption( 'count', "How many times to run a benchmark", false, true );
        }
 
        public function bench( array $benchs ) {
@@ -76,7 +75,13 @@ abstract class Benchmarker extends Maintenance {
        }
 
        public function getFormattedResults() {
-               $ret = '';
+               $ret = sprintf( "Running PHP version %s (%s) on %s %s %s\n\n",
+                       phpversion(),
+                       php_uname( 'm' ),
+                       php_uname( 's' ),
+                       php_uname( 'r' ),
+                       php_uname( 'v' )
+               );
                foreach ( $this->results as $res ) {
                        // show function with args
                        $ret .= sprintf( "%s times: function %s(%s) :\n",
index 1037528..ad2f12c 100644 (file)
@@ -1261,6 +1261,7 @@ return array(
                        'mediawiki.widgets.DateInputWidget',
                        'mediawiki.jqueryMsg',
                        'moment',
+                       'mediawiki.libs.jpegmeta',
                ),
                'messages' => array(
                        'foreign-structured-upload-form-label-own-work',
index aa2998b..d896756 100644 (file)
                this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
                this.ownWorkCheckbox.on( 'change', onUploadFormChange.bind( this ) );
 
+               this.selectFileWidget.on( 'change', function () {
+                       var file = layout.getFile();
+
+                       // Set the date to lastModified once we have the file
+                       if ( layout.getDateFromLastModified( file ) !== undefined ) {
+                               layout.dateWidget.setValue( layout.getDateFromLastModified( file ) );
+                       }
+
+                       // Check if we have EXIF data and set to that where available
+                       layout.getDateFromExif( file ).done( function ( date ) {
+                               layout.dateWidget.setValue( date );
+                       } );
+               } );
+
                return this.uploadForm;
        };
 
                        multiline: true,
                        autosize: true
                } );
-               this.dateWidget = new mw.widgets.DateInputWidget( {
-                       $overlay: this.$overlay,
-                       required: true,
-                       mustBeBefore: moment().add( 1, 'day' ).locale( 'en' ).format( 'YYYY-MM-DD' ) // Tomorrow
-               } );
                this.categoriesWidget = new mw.widgets.CategorySelector( {
                        // Can't be done here because we don't know the target wiki yet... done in #initialize.
                        // api: new mw.ForeignApi( ... ),
                        $overlay: this.$overlay
                } );
+               this.dateWidget = new mw.widgets.DateInputWidget( {
+                       $overlay: this.$overlay,
+                       required: true,
+                       mustBeBefore: moment().add( 1, 'day' ).locale( 'en' ).format( 'YYYY-MM-DD' ) // Tomorrow
+               } );
 
                fieldset = new OO.ui.FieldsetLayout( {
                        label: mw.msg( 'upload-form-label-infoform-title' )
                return this.upload.getText();
        };
 
+       /**
+        * Get original date from EXIF data
+        *
+        * @param {Object} file
+        * @return {jQuery.Promise} Promise resolved with the EXIF date
+        */
+       mw.ForeignStructuredUpload.BookletLayout.prototype.getDateFromExif = function ( file ) {
+               var fileReader,
+                       deferred = $.Deferred();
+
+               if ( file && file.type === 'image/jpeg' ) {
+                       fileReader = new FileReader();
+                       fileReader.onload = function () {
+                               var fileStr, arr, i, metadata;
+
+                               if ( typeof fileReader.result === 'string' ) {
+                                       fileStr = fileReader.result;
+                               } else {
+                                       // Array buffer; convert to binary string for the library.
+                                       arr = new Uint8Array( fileReader.result );
+                                       fileStr = '';
+                                       for ( i = 0; i < arr.byteLength; i++ ) {
+                                               fileStr += String.fromCharCode( arr[ i ] );
+                                       }
+                               }
+
+                               try {
+                                       metadata = mw.libs.jpegmeta( this.result, file.name );
+                               } catch ( e ) {
+                                       metadata = null;
+                               }
+
+                               if ( metadata !== null && metadata.exif !== undefined && metadata.exif.DateTimeOriginal ) {
+                                       deferred.resolve( moment( metadata.exif.DateTimeOriginal, 'YYYY:MM:DD' ).format( 'YYYY-MM-DD' ) );
+                               } else {
+                                       deferred.reject();
+                               }
+                       };
+
+                       if ( 'readAsBinaryString' in fileReader ) {
+                               fileReader.readAsBinaryString( file );
+                       } else if ( 'readAsArrayBuffer' in fileReader ) {
+                               fileReader.readAsArrayBuffer( file );
+                       } else {
+                               // We should never get here
+                               deferred.reject();
+                               throw new Error( 'Cannot read thumbnail as binary string or array buffer.' );
+                       }
+               }
+
+               return deferred.promise();
+       };
+
+       /**
+        * Get last modified date from file
+        *
+        * @param {Object} file
+        * @return {Object} Last modified date from file
+        */
+       mw.ForeignStructuredUpload.BookletLayout.prototype.getDateFromLastModified = function ( file ) {
+               if ( file && file.lastModified ) {
+                       return moment( file.lastModified ).format( 'YYYY-MM-DD' );
+               }
+       };
+
        /* Setters */
 
        /**
index 47250ee..033636c 100644 (file)
                 * @return {string}
                 */
                getUrl: function ( params ) {
-                       return mw.util.getUrl( this.toString(), params );
+                       var fragment = this.getFragment();
+                       if ( fragment ) {
+                               return mw.util.getUrl( this.toString() + '#' + this.getFragment(), params );
+                       } else {
+                               return mw.util.getUrl( this.toString(), params );
+                       }
                },
 
                /**
index 4bcb12e..2e63b7a 100644 (file)
 
        } );
 
-       QUnit.test( 'getUrl', 3, function ( assert ) {
+       QUnit.test( 'getUrl', 4, function ( assert ) {
                var title;
 
                // Config
 
                title = new mw.Title( 'John Doe', 3 );
                assert.equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' );
+
+               title = new mw.Title( 'John Cena#And_His_Name_Is', 3 );
+               assert.equal( title.getUrl( { meme: true } ), '/wiki/User_talk:John_Cena?meme=true#And_His_Name_Is', 'title with fragment and query parameter' );
        } );
 
        QUnit.test( 'newFromImg', 44, function ( assert ) {