Merge "Make it possible for EditPage::getCopyrightWarning to return HTML"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Mon, 21 Oct 2013 01:19:27 +0000 (01:19 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 21 Oct 2013 01:19:27 +0000 (01:19 +0000)
120 files changed:
RELEASE-NOTES-1.22
docs/hooks.txt
includes/AutoLoader.php
includes/ChangesList.php [deleted file]
includes/DefaultSettings.php
includes/EditPage.php
includes/FormOptions.php
includes/GlobalFunctions.php
includes/HashRing.php
includes/Message.php
includes/ProxyTools.php
includes/RecentChange.php [deleted file]
includes/SkinTemplate.php
includes/SpecialPageFactory.php
includes/changes/ChangesList.php [new file with mode: 0644]
includes/changes/EnhancedChangesList.php [new file with mode: 0644]
includes/changes/OldChangesList.php [new file with mode: 0644]
includes/changes/RCCacheEntry.php [new file with mode: 0644]
includes/changes/RecentChange.php [new file with mode: 0644]
includes/content/ContentHandler.php
includes/db/Database.php
includes/db/DatabaseMysqlBase.php
includes/db/DatabaseMysqli.php [new file with mode: 0644]
includes/db/DatabaseSqlite.php
includes/filebackend/FileBackend.php
includes/filebackend/FileBackendMultiWrite.php
includes/filebackend/FileBackendStore.php
includes/filerepo/file/LocalFile.php
includes/installer/MysqlInstaller.php
includes/installer/MysqlUpdater.php
includes/installer/OracleUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/job/JobQueue.php
includes/job/JobQueueFederated.php
includes/parser/Parser.php
includes/specials/SpecialBlockme.php [deleted file]
includes/specials/SpecialContributions.php
languages/LanguageConverter.php
languages/messages/MessagesAce.php
languages/messages/MessagesAng.php
languages/messages/MessagesAr.php
languages/messages/MessagesBa.php
languages/messages/MessagesBe_tarask.php
languages/messages/MessagesBg.php
languages/messages/MessagesBn.php
languages/messages/MessagesBr.php
languages/messages/MessagesBs.php
languages/messages/MessagesCe.php
languages/messages/MessagesCy.php
languages/messages/MessagesDe.php
languages/messages/MessagesEn.php
languages/messages/MessagesEt.php
languages/messages/MessagesEu.php
languages/messages/MessagesFa.php
languages/messages/MessagesFi.php
languages/messages/MessagesFr.php
languages/messages/MessagesHi.php
languages/messages/MessagesHr.php
languages/messages/MessagesInh.php
languages/messages/MessagesKa.php
languages/messages/MessagesKo.php
languages/messages/MessagesMg.php
languages/messages/MessagesNl.php
languages/messages/MessagesPms.php
languages/messages/MessagesPs.php
languages/messages/MessagesPt.php
languages/messages/MessagesPt_br.php
languages/messages/MessagesQqq.php
languages/messages/MessagesRu.php
languages/messages/MessagesSr_ec.php
languages/messages/MessagesSr_el.php
languages/messages/MessagesSv.php
languages/messages/MessagesTh.php
languages/messages/MessagesTk.php
languages/messages/MessagesUk.php
languages/messages/MessagesVi.php
languages/messages/MessagesVmf.php
languages/messages/MessagesYi.php
languages/messages/MessagesZh_hans.php
languages/messages/MessagesZh_hant.php
maintenance/archives/patch-archive-ar_id.sql [new file with mode: 0644]
maintenance/archives/patch-change_tag.sql
maintenance/archives/patch-externallinks-el_id.sql [new file with mode: 0644]
maintenance/archives/patch-rc_source.sql [deleted file]
maintenance/archives/patch-tag_summary.sql [new file with mode: 0644]
maintenance/archives/patch-valid_tag.sql [new file with mode: 0644]
maintenance/deleteEqualMessages.php
maintenance/fuzz-tester.php
maintenance/importImages.php
maintenance/language/messages.inc
maintenance/mssql/tables.sql
maintenance/oracle/archives/patch-archive-ar_id.sql [new file with mode: 0644]
maintenance/oracle/archives/patch-externallinks-el_id.sql [new file with mode: 0644]
maintenance/oracle/tables.sql
maintenance/populateRecentChangesSource.php [deleted file]
maintenance/postgres/tables.sql
maintenance/proxyCheck.php [deleted file]
maintenance/rebuildrecentchanges.php
maintenance/sqlite/archives/initial-indexes.sql
maintenance/sqlite/archives/patch-archive-ar_id.sql [new file with mode: 0644]
maintenance/sqlite/archives/patch-externallinks-el_id.sql [new file with mode: 0644]
maintenance/tables.sql
resources/mediawiki.api/mediawiki.api.edit.js
resources/mediawiki.api/mediawiki.api.js
resources/mediawiki/mediawiki.notification.js
resources/mediawiki/mediawiki.notify.js
tests/phpunit/includes/MessageTest.php
tests/phpunit/includes/cache/GenderCacheTest.php
tests/phpunit/includes/cache/MessageCacheTest.php
tests/phpunit/includes/content/ContentHandlerTest.php
tests/phpunit/includes/content/CssContentTest.php
tests/phpunit/includes/content/JavaScriptContentTest.php
tests/phpunit/includes/content/TextContentTest.php
tests/phpunit/includes/content/WikitextContentHandlerTest.php
tests/phpunit/includes/content/WikitextContentTest.php
tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
tests/phpunit/includes/db/DatabaseSQLTest.php
tests/phpunit/includes/db/DatabaseSqliteTest.php
tests/phpunit/includes/db/DatabaseTest.php

index 0015d29..70b5f80 100644 (file)
@@ -61,6 +61,9 @@ production.
 * The precise format of metric datagrams produced by the UDP profiler and stats counter
   may now be specified as $wgUDPProfilerFormatString and $wgStatsFormatString,
   respectively.
+* (bug 54597) $wgBlockOpenProxies, $wgProxyPorts, $wgProxyScriptPath, and
+  $wgProxyMemcExpiry have been removed, along with the open proxy scanner
+  script they were added for.
 
 === New features in 1.22 ===
 * (bug 44525) mediawiki.jqueryMsg can now parse (whitelisted) HTML elements and attributes.
@@ -242,6 +245,12 @@ production.
 * Added $wgExtensionEntryPointListFiles for use in mergeMessageFileList.php.
 * Added a hook, APIQuerySiteInfoStatisticsInfo, to allow extensions to modify
   the output of the API query meta=siteinfo&siprop=statistics
+* Primary keys have been added to both the archive table and the externallinks
+  tables.
+* Added $wgEnableParserLimitReporting to control whether the NewPP limit report is
+  output in a HTML comment.
+* The 'UnwatchArticle' and 'WatchArticle' hooks now support a Status object
+  instead of just a boolean return value to abort the hook.
 
 === Bug fixes in 1.22 ===
 * Disable Special:PasswordReset when $wgEnableEmail is false. Previously one
@@ -326,6 +335,9 @@ production.
   database is created.
 * (bug 47191) Fixed "Column 'si_title' cannot be part of FULLTEXT index"
   MySQL error when installing using the binary character set option.
+* (bug 45288) Support mysqli PHP extension
+* (bug 55818) BREAKING CHANGE: Removed undocumented 'Debug' hook in wfDebug.
+  This resolves an infinite loop when using $wgDebugFunctionEntry = true.
 
 === API changes in 1.22 ===
 * (bug 25553) The JSON output formatter now leaves forward slashes unescaped
@@ -364,7 +376,7 @@ production.
   user blocks.
 * (bug 48201) action=parse&text=foo now assumes wikitext if no title is given,
   rather than using the content model of the page "API".
-* action=watch may now return errors.
+* action=watch no longer silently ignores hook abort.
 * (bug 50785) action=purge with forcelinkupdate=1 no longer queues refreshLinks
   jobs in the job queue for link table updates of pages that use the given page
   as a template. Instead, forcerecursivelinkupdate=1 is introduced and should
@@ -398,15 +410,6 @@ changes to languages because of Bugzilla reports.
 * (bug 46751) Made Buryat (Russia) (буряад) (bxr) fallback to Russian.
 
 === Other changes in 1.22 ===
-* The rc_type field in the recentchanges table has been superseded by a new
-  rc_source field.  The rc_source field is a string representation of the
-  change type where rc_type was a numeric constant.  This field is not yet
-  queried but will be in a future point release of 1.22.  
-** Utilize update.php to create and populate this new field.  On larger wiki's
-   which do not wish to update recentchanges table in one large update please
-   review the sql and comments in maintenance/archives/patch-rc_source.sql.
-** The rc_type field of recentchanges will be deprecated in a future point
-   release.
 * BREAKING CHANGE: Implementation of MediaWiki's JS and JSON value encoding
   has changed:
 ** MediaWiki no longer supports PHP installations in which the native JSON
@@ -502,6 +505,8 @@ changes to languages because of Bugzilla reports.
   to the Vector skin in core.
 * SpecialRecentChanges::addRecentChangesJS() function has been renamed
   to addModules() and made protected.
+* Methods WatchAction::doWatch and WatchAction::doUnwatch now return a Status
+  object instead of a boolean.
 
 == Compatibility ==
 
index 53993de..26032cb 100644 (file)
@@ -866,10 +866,6 @@ etc.
 'DatabaseOraclePostInit': Called after initialising an Oracle database
 &$db: the DatabaseOracle object
 
-'Debug': Called when outputting a debug log line via wfDebug() or wfDebugLog()
-$text: plaintext string to be output
-$group: null or a string naming a logging group (as defined in $wgDebugLogGroups)
-
 'NewDifferenceEngine': Called when a new DifferenceEngine object is made
 $title: the diff page title (nullable)
 &$oldId: the actual old Id to use in the diff
index 8d571ad..2e82441 100644 (file)
@@ -55,7 +55,6 @@ $wgAutoloadLocalClasses = array(
        'CdbWriter_DBA' => 'includes/Cdb.php',
        'CdbWriter_PHP' => 'includes/Cdb_PHP.php',
        'ChangesFeed' => 'includes/ChangesFeed.php',
-       'ChangesList' => 'includes/ChangesList.php',
        'ChangeTags' => 'includes/ChangeTags.php',
        'ChannelFeed' => 'includes/Feed.php',
        'Collation' => 'includes/Collation.php',
@@ -87,7 +86,6 @@ $wgAutoloadLocalClasses = array(
        'DumpPipeOutput' => 'includes/Export.php',
        'EditPage' => 'includes/EditPage.php',
        'EmailNotification' => 'includes/UserMailer.php',
-       'EnhancedChangesList' => 'includes/ChangesList.php',
        'ErrorPageError' => 'includes/Exception.php',
        'ExplodeIterator' => 'includes/StringUtils.php',
        'FakeTitle' => 'includes/FakeTitle.php',
@@ -178,7 +176,6 @@ $wgAutoloadLocalClasses = array(
        'MWHttpRequest' => 'includes/HttpFunctions.php',
        'MWInit' => 'includes/Init.php',
        'MWNamespace' => 'includes/Namespace.php',
-       'OldChangesList' => 'includes/ChangesList.php',
        'OutputPage' => 'includes/OutputPage.php',
        'Page' => 'includes/WikiPage.php',
        'PageQueryPage' => 'includes/PageQueryPage.php',
@@ -200,10 +197,8 @@ $wgAutoloadLocalClasses = array(
        'QueryPage' => 'includes/QueryPage.php',
        'QuickTemplate' => 'includes/SkinTemplate.php',
        'RawMessage' => 'includes/Message.php',
-       'RCCacheEntry' => 'includes/ChangesList.php',
        'RdfMetaData' => 'includes/Metadata.php',
        'ReadOnlyError' => 'includes/Exception.php',
-       'RecentChange' => 'includes/RecentChange.php',
        'RedirectSpecialArticle' => 'includes/SpecialPage.php',
        'RedirectSpecialPage' => 'includes/SpecialPage.php',
        'RegexlikeReplacer' => 'includes/StringUtils.php',
@@ -457,6 +452,13 @@ $wgAutoloadLocalClasses = array(
        'TitleDependency' => 'includes/cache/CacheDependency.php',
        'TitleListDependency' => 'includes/cache/CacheDependency.php',
 
+       # includes/changes
+       'ChangesList' => 'includes/changes/ChangesList.php',
+       'EnhancedChangesList' => 'includes/changes/EnhancedChangesList.php',
+       'OldChangesList' => 'includes/changes/OldChangesList.php',
+       'RCCacheEntry' => 'includes/changes/RCCacheEntry.php',
+       'RecentChange' => 'includes/changes/RecentChange.php',
+
        # includes/clientpool
        'RedisConnectionPool' => 'includes/clientpool/RedisConnectionPool.php',
        'RedisConnRef' => 'includes/clientpool/RedisConnectionPool.php',
@@ -479,6 +481,7 @@ $wgAutoloadLocalClasses = array(
        'DatabaseMssql' => 'includes/db/DatabaseMssql.php',
        'DatabaseMysql' => 'includes/db/DatabaseMysql.php',
        'DatabaseMysqlBase' => 'includes/db/DatabaseMysqlBase.php',
+       'DatabaseMysqli' => 'includes/db/DatabaseMysqli.php',
        'DatabaseOracle' => 'includes/db/DatabaseOracle.php',
        'DatabasePostgres' => 'includes/db/DatabasePostgres.php',
        'DatabaseSqlite' => 'includes/db/DatabaseSqlite.php',
@@ -970,7 +973,6 @@ $wgAutoloadLocalClasses = array(
        'SpecialBlankpage' => 'includes/specials/SpecialBlankpage.php',
        'SpecialBlock' => 'includes/specials/SpecialBlock.php',
        'SpecialBlockList' => 'includes/specials/SpecialBlockList.php',
-       'SpecialBlockme' => 'includes/specials/SpecialBlockme.php',
        'SpecialBookSources' => 'includes/specials/SpecialBooksources.php',
        'SpecialCachedPage' => 'includes/specials/SpecialCachedPage.php',
        'SpecialCategories' => 'includes/specials/SpecialCategories.php',
diff --git a/includes/ChangesList.php b/includes/ChangesList.php
deleted file mode 100644 (file)
index 9c441af..0000000
+++ /dev/null
@@ -1,1334 +0,0 @@
-<?php
-/**
- * Classes to show lists of changes.
- *
- * These can be:
- * - watchlist
- * - related changes
- * - recent changes
- *
- * 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
- */
-
-/**
- * @todo document
- */
-class RCCacheEntry extends RecentChange {
-       var $secureName, $link;
-       var $curlink, $difflink, $lastlink, $usertalklink, $versionlink;
-       var $userlink, $timestamp, $watched;
-
-       /**
-        * @param $rc RecentChange
-        * @return RCCacheEntry
-        */
-       static function newFromParent( $rc ) {
-               $rc2 = new RCCacheEntry;
-               $rc2->mAttribs = $rc->mAttribs;
-               $rc2->mExtra = $rc->mExtra;
-               return $rc2;
-       }
-}
-
-/**
- * Base class for all changes lists
- */
-class ChangesList extends ContextSource {
-
-       /**
-        * @var Skin
-        */
-       public $skin;
-
-       protected $watchlist = false;
-
-       protected $message;
-
-       /**
-        * Changeslist constructor
-        *
-        * @param $obj Skin or IContextSource
-        */
-       public function __construct( $obj ) {
-               if ( $obj instanceof IContextSource ) {
-                       $this->setContext( $obj );
-                       $this->skin = $obj->getSkin();
-               } else {
-                       $this->setContext( $obj->getContext() );
-                       $this->skin = $obj;
-               }
-               $this->preCacheMessages();
-       }
-
-       /**
-        * Fetch an appropriate changes list class for the main context
-        * This first argument used to be an User object.
-        *
-        * @deprecated in 1.18; use newFromContext() instead
-        * @param string|User $unused Unused
-        * @return ChangesList|EnhancedChangesList|OldChangesList derivative
-        */
-       public static function newFromUser( $unused ) {
-               wfDeprecated( __METHOD__, '1.18' );
-               return self::newFromContext( RequestContext::getMain() );
-       }
-
-       /**
-        * Fetch an appropriate changes list class for the specified context
-        * Some users might want to use an enhanced list format, for instance
-        *
-        * @param $context IContextSource to use
-        * @return ChangesList|EnhancedChangesList|OldChangesList derivative
-        */
-       public static function newFromContext( IContextSource $context ) {
-               $user = $context->getUser();
-               $sk = $context->getSkin();
-               $list = null;
-               if ( wfRunHooks( 'FetchChangesList', array( $user, &$sk, &$list ) ) ) {
-                       $new = $context->getRequest()->getBool( 'enhanced', $user->getOption( 'usenewrc' ) );
-                       return $new ? new EnhancedChangesList( $context ) : new OldChangesList( $context );
-               } else {
-                       return $list;
-               }
-       }
-
-       /**
-        * Sets the list to use a "<li class='watchlist-(namespace)-(page)'>" tag
-        * @param $value Boolean
-        */
-       public function setWatchlistDivs( $value = true ) {
-               $this->watchlist = $value;
-       }
-
-       /**
-        * As we use the same small set of messages in various methods and that
-        * they are called often, we call them once and save them in $this->message
-        */
-       private function preCacheMessages() {
-               if ( !isset( $this->message ) ) {
-                       foreach ( array(
-                               'cur', 'diff', 'hist', 'enhancedrc-history', 'last', 'blocklink', 'history',
-                               'semicolon-separator', 'pipe-separator' ) as $msg
-                       ) {
-                               $this->message[$msg] = $this->msg( $msg )->escaped();
-                       }
-               }
-       }
-
-       /**
-        * Returns the appropriate flags for new page, minor change and patrolling
-        * @param array $flags Associative array of 'flag' => Bool
-        * @param string $nothing to use for empty space
-        * @return String
-        */
-       public function recentChangesFlags( $flags, $nothing = '&#160;' ) {
-               global $wgRecentChangesFlags;
-               $f = '';
-               foreach ( array_keys( $wgRecentChangesFlags ) as $flag ) {
-                       $f .= isset( $flags[$flag] ) && $flags[$flag]
-                               ? self::flag( $flag )
-                               : $nothing;
-               }
-               return $f;
-       }
-
-       /**
-        * Provide the "<abbr>" element appropriate to a given abbreviated flag,
-        * namely the flag indicating a new page, a minor edit, a bot edit, or an
-        * unpatrolled edit.  By default in English it will contain "N", "m", "b",
-        * "!" respectively, plus it will have an appropriate title and class.
-        *
-        * @param string $flag One key of $wgRecentChangesFlags
-        * @return String: Raw HTML
-        */
-       public static function flag( $flag ) {
-               static $flagInfos = null;
-               if ( is_null( $flagInfos ) ) {
-                       global $wgRecentChangesFlags;
-                       $flagInfos = array();
-                       foreach ( $wgRecentChangesFlags as $key => $value ) {
-                               $flagInfos[$key]['letter'] = wfMessage( $value['letter'] )->escaped();
-                               $flagInfos[$key]['title'] = wfMessage( $value['title'] )->escaped();
-                               // Allow customized class name, fall back to flag name
-                               $flagInfos[$key]['class'] = Sanitizer::escapeClass(
-                                       isset( $value['class'] ) ? $value['class'] : $key );
-                       }
-               }
-
-               // Inconsistent naming, bleh, kepted for b/c
-               $map = array(
-                       'minoredit' => 'minor',
-                       'botedit' => 'bot',
-               );
-               if ( isset( $map[$flag] ) ) {
-                       $flag = $map[$flag];
-               }
-
-               return "<abbr class='" . $flagInfos[$flag]['class'] . "' title='" . $flagInfos[$flag]['title'] . "'>" .
-                       $flagInfos[$flag]['letter'] .
-                       '</abbr>';
-       }
-
-       /**
-        * Returns text for the start of the tabular part of RC
-        * @return String
-        */
-       public function beginRecentChangesList() {
-               $this->rc_cache = array();
-               $this->rcMoveIndex = 0;
-               $this->rcCacheIndex = 0;
-               $this->lastdate = '';
-               $this->rclistOpen = false;
-               $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
-               return '';
-       }
-
-       /**
-        * Show formatted char difference
-        * @param $old Integer: bytes
-        * @param $new Integer: bytes
-        * @param $context IContextSource context to use
-        * @return String
-        */
-       public static function showCharacterDifference( $old, $new, IContextSource $context = null ) {
-               global $wgRCChangedSizeThreshold, $wgMiserMode;
-
-               if ( !$context ) {
-                       $context = RequestContext::getMain();
-               }
-
-               $new = (int)$new;
-               $old = (int)$old;
-               $szdiff = $new - $old;
-
-               $lang = $context->getLanguage();
-               $code = $lang->getCode();
-               static $fastCharDiff = array();
-               if ( !isset( $fastCharDiff[$code] ) ) {
-                       $fastCharDiff[$code] = $wgMiserMode || $context->msg( 'rc-change-size' )->plain() === '$1';
-               }
-
-               $formattedSize = $lang->formatNum( $szdiff );
-
-               if ( !$fastCharDiff[$code] ) {
-                       $formattedSize = $context->msg( 'rc-change-size', $formattedSize )->text();
-               }
-
-               if ( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) {
-                       $tag = 'strong';
-               } else {
-                       $tag = 'span';
-               }
-
-               if ( $szdiff === 0 ) {
-                       $formattedSizeClass = 'mw-plusminus-null';
-               }
-               if ( $szdiff > 0 ) {
-                       $formattedSize = '+' . $formattedSize;
-                       $formattedSizeClass = 'mw-plusminus-pos';
-               }
-               if ( $szdiff < 0 ) {
-                       $formattedSizeClass = 'mw-plusminus-neg';
-               }
-
-               $formattedTotalSize = $context->msg( 'rc-change-size-new' )->numParams( $new )->text();
-
-               return Html::element( $tag,
-                       array( 'dir' => 'ltr', 'class' => $formattedSizeClass, 'title' => $formattedTotalSize ),
-                       $context->msg( 'parentheses', $formattedSize )->plain() ) . $lang->getDirMark();
-       }
-
-       /**
-        * Format the character difference of one or several changes.
-        *
-        * @param $old RecentChange
-        * @param $new RecentChange last change to use, if not provided, $old will be used
-        * @return string HTML fragment
-        */
-       public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) {
-               $oldlen = $old->mAttribs['rc_old_len'];
-
-               if ( $new ) {
-                       $newlen = $new->mAttribs['rc_new_len'];
-               } else {
-                       $newlen = $old->mAttribs['rc_new_len'];
-               }
-
-               if ( $oldlen === null || $newlen === null ) {
-                       return '';
-               }
-
-               return self::showCharacterDifference( $oldlen, $newlen, $this->getContext() );
-       }
-
-       /**
-        * Returns text for the end of RC
-        * @return String
-        */
-       public function endRecentChangesList() {
-               if ( $this->rclistOpen ) {
-                       return "</ul>\n";
-               } else {
-                       return '';
-               }
-       }
-
-       /**
-        * @param string $s HTML to update
-        * @param $rc_timestamp mixed
-        */
-       public function insertDateHeader( &$s, $rc_timestamp ) {
-               # Make date header if necessary
-               $date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() );
-               if ( $date != $this->lastdate ) {
-                       if ( $this->lastdate != '' ) {
-                               $s .= "</ul>\n";
-                       }
-                       $s .= Xml::element( 'h4', null, $date ) . "\n<ul class=\"special\">";
-                       $this->lastdate = $date;
-                       $this->rclistOpen = true;
-               }
-       }
-
-       /**
-        * @param string $s HTML to update
-        * @param $title Title
-        * @param $logtype string
-        */
-       public function insertLog( &$s, $title, $logtype ) {
-               $page = new LogPage( $logtype );
-               $logname = $page->getName()->escaped();
-               $s .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $title, $logname ) )->escaped();
-       }
-
-       /**
-        * @param string $s HTML to update
-        * @param $rc RecentChange
-        * @param $unpatrolled
-        */
-       public function insertDiffHist( &$s, &$rc, $unpatrolled ) {
-               # Diff link
-               if ( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
-                       $diffLink = $this->message['diff'];
-               } elseif ( !self::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
-                       $diffLink = $this->message['diff'];
-               } else {
-                       $query = array(
-                               'curid' => $rc->mAttribs['rc_cur_id'],
-                               'diff' => $rc->mAttribs['rc_this_oldid'],
-                               'oldid' => $rc->mAttribs['rc_last_oldid']
-                       );
-
-                       $diffLink = Linker::linkKnown(
-                               $rc->getTitle(),
-                               $this->message['diff'],
-                               array( 'tabindex' => $rc->counter ),
-                               $query
-                       );
-               }
-               $diffhist = $diffLink . $this->message['pipe-separator'];
-               # History link
-               $diffhist .= Linker::linkKnown(
-                       $rc->getTitle(),
-                       $this->message['hist'],
-                       array(),
-                       array(
-                               'curid' => $rc->mAttribs['rc_cur_id'],
-                               'action' => 'history'
-                       )
-               );
-               $s .= $this->msg( 'parentheses' )->rawParams( $diffhist )->escaped() . ' <span class="mw-changeslist-separator">. .</span> ';
-       }
-
-       /**
-        * @param string $s HTML to update
-        * @param $rc RecentChange
-        * @param $unpatrolled
-        * @param $watched
-        */
-       public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
-               $params = array();
-
-               $articlelink = Linker::linkKnown(
-                       $rc->getTitle(),
-                       null,
-                       array( 'class' => 'mw-changeslist-title' ),
-                       $params
-               );
-               if ( $this->isDeleted( $rc, Revision::DELETED_TEXT ) ) {
-                       $articlelink = '<span class="history-deleted">' . $articlelink . '</span>';
-               }
-               # To allow for boldening pages watched by this user
-               $articlelink = "<span class=\"mw-title\">{$articlelink}</span>";
-               # RTL/LTR marker
-               $articlelink .= $this->getLanguage()->getDirMark();
-
-               wfRunHooks( 'ChangesListInsertArticleLink',
-                       array( &$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched ) );
-
-               $s .= " $articlelink";
-       }
-
-       /**
-        * Get the timestamp from $rc formatted with current user's settings
-        * and a separator
-        *
-        * @param $rc RecentChange
-        * @return string HTML fragment
-        */
-       public function getTimestamp( $rc ) {
-               return $this->message['semicolon-separator'] . '<span class="mw-changeslist-date">' .
-                       $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() ) . '</span> <span class="mw-changeslist-separator">. .</span> ';
-       }
-
-       /**
-        * Insert time timestamp string from $rc into $s
-        *
-        * @param string $s HTML to update
-        * @param $rc RecentChange
-        */
-       public function insertTimestamp( &$s, $rc ) {
-               $s .= $this->getTimestamp( $rc );
-       }
-
-       /**
-        * Insert links to user page, user talk page and eventually a blocking link
-        *
-        * @param &$s String HTML to update
-        * @param &$rc RecentChange
-        */
-       public function insertUserRelatedLinks( &$s, &$rc ) {
-               if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
-                       $s .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
-               } else {
-                       $s .= $this->getLanguage()->getDirMark() . Linker::userLink( $rc->mAttribs['rc_user'],
-                               $rc->mAttribs['rc_user_text'] );
-                       $s .= Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
-               }
-       }
-
-       /**
-        * Insert a formatted action
-        *
-        * @param $rc RecentChange
-        * @return string
-        */
-       public function insertLogEntry( $rc ) {
-               $formatter = LogFormatter::newFromRow( $rc->mAttribs );
-               $formatter->setContext( $this->getContext() );
-               $formatter->setShowUserToolLinks( true );
-               $mark = $this->getLanguage()->getDirMark();
-               return $formatter->getActionText() . " $mark" . $formatter->getComment();
-       }
-
-       /**
-        * Insert a formatted comment
-        * @param $rc RecentChange
-        * @return string
-        */
-       public function insertComment( $rc ) {
-               if ( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
-                       if ( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) {
-                               return ' <span class="history-deleted">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
-                       } else {
-                               return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
-                       }
-               }
-               return '';
-       }
-
-       /**
-        * Check whether to enable recent changes patrol features
-        *
-        * @deprecated since 1.22
-        * @return Boolean
-        */
-       public static function usePatrol() {
-               global $wgUser;
-
-               wfDeprecated( __METHOD__, '1.22' );
-
-               return $wgUser->useRCPatrol();
-       }
-
-       /**
-        * Returns the string which indicates the number of watching users
-        * @return string
-        */
-       protected function numberofWatchingusers( $count ) {
-               static $cache = array();
-               if ( $count > 0 ) {
-                       if ( !isset( $cache[$count] ) ) {
-                               $cache[$count] = $this->msg( 'number_of_watching_users_RCview' )->numParams( $count )->escaped();
-                       }
-                       return $cache[$count];
-               } else {
-                       return '';
-               }
-       }
-
-       /**
-        * Determine if said field of a revision is hidden
-        * @param $rc RCCacheEntry
-        * @param $field Integer: one of DELETED_* bitfield constants
-        * @return Boolean
-        */
-       public static function isDeleted( $rc, $field ) {
-               return ( $rc->mAttribs['rc_deleted'] & $field ) == $field;
-       }
-
-       /**
-        * Determine if the current user is allowed to view a particular
-        * field of this revision, if it's marked as deleted.
-        * @param $rc RCCacheEntry
-        * @param $field Integer
-        * @param $user User object to check, or null to use $wgUser
-        * @return Boolean
-        */
-       public static function userCan( $rc, $field, User $user = null ) {
-               if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
-                       return LogEventsList::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
-               } else {
-                       return Revision::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
-               }
-       }
-
-       /**
-        * @param $link string
-        * @param $watched bool
-        * @return string
-        */
-       protected function maybeWatchedLink( $link, $watched = false ) {
-               if ( $watched ) {
-                       return '<strong class="mw-watched">' . $link . '</strong>';
-               } else {
-                       return '<span class="mw-rc-unwatched">' . $link . '</span>';
-               }
-       }
-
-       /** Inserts a rollback link
-        *
-        * @param $s string
-        * @param $rc RecentChange
-        */
-       public function insertRollback( &$s, &$rc ) {
-               if ( $rc->mAttribs['rc_type'] == RC_EDIT && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
-                       $page = $rc->getTitle();
-                       /** Check for rollback and edit permissions, disallow special pages, and only
-                         * show a link on the top-most revision */
-                       if ( $this->getUser()->isAllowed( 'rollback' ) && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] )
-                       {
-                               $rev = new Revision( array(
-                                       'title' => $page,
-                                       'id' => $rc->mAttribs['rc_this_oldid'],
-                                       'user' => $rc->mAttribs['rc_user'],
-                                       'user_text' => $rc->mAttribs['rc_user_text'],
-                                       'deleted' => $rc->mAttribs['rc_deleted']
-                               ) );
-                               $s .= ' ' . Linker::generateRollback( $rev, $this->getContext() );
-                       }
-               }
-       }
-
-       /**
-        * @param $s string
-        * @param $rc RecentChange
-        * @param $classes
-        */
-       public function insertTags( &$s, &$rc, &$classes ) {
-               if ( empty( $rc->mAttribs['ts_tags'] ) ) {
-                       return;
-               }
-
-               list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( $rc->mAttribs['ts_tags'], 'changeslist' );
-               $classes = array_merge( $classes, $newClasses );
-               $s .= ' ' . $tagSummary;
-       }
-
-       public function insertExtra( &$s, &$rc, &$classes ) {
-               // Empty, used for subclasses to add anything special.
-       }
-
-       protected function showAsUnpatrolled( RecentChange $rc ) {
-               $unpatrolled = false;
-               if ( !$rc->mAttribs['rc_patrolled'] ) {
-                       if ( $this->getUser()->useRCPatrol() ) {
-                               $unpatrolled = true;
-                       } elseif ( $this->getUser()->useNPPatrol() && $rc->mAttribs['rc_type'] == RC_NEW ) {
-                               $unpatrolled = true;
-                       }
-               }
-               return $unpatrolled;
-       }
-}
-
-/**
- * Generate a list of changes using the good old system (no javascript)
- */
-class OldChangesList extends ChangesList {
-       /**
-        * Format a line using the old system (aka without any javascript).
-        *
-        * @param $rc RecentChange, passed by reference
-        * @param bool $watched (default false)
-        * @param int $linenumber (default null)
-        *
-        * @return string|bool
-        */
-       public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
-               global $wgRCShowChangedSize;
-               wfProfileIn( __METHOD__ );
-
-               # Should patrol-related stuff be shown?
-               $unpatrolled = $this->showAsUnpatrolled( $rc );
-
-               $dateheader = ''; // $s now contains only <li>...</li>, for hooks' convenience.
-               $this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
-
-               $s = '';
-               $classes = array();
-               // use mw-line-even/mw-line-odd class only if linenumber is given (feature from bug 14468)
-               if ( $linenumber ) {
-                       if ( $linenumber & 1 ) {
-                               $classes[] = 'mw-line-odd';
-                       } else {
-                               $classes[] = 'mw-line-even';
-                       }
-               }
-
-               // Indicate watched status on the line to allow for more
-               // comprehensive styling.
-               $classes[] = $watched && $rc->mAttribs['rc_timestamp'] >= $watched
-                       ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
-
-               // Moved pages (very very old, not supported anymore)
-               if ( $rc->mAttribs['rc_type'] == RC_MOVE || $rc->mAttribs['rc_type'] == RC_MOVE_OVER_REDIRECT ) {
-               // Log entries
-               } elseif ( $rc->mAttribs['rc_log_type'] ) {
-                       $logtitle = SpecialPage::getTitleFor( 'Log', $rc->mAttribs['rc_log_type'] );
-                       $this->insertLog( $s, $logtitle, $rc->mAttribs['rc_log_type'] );
-               // Log entries (old format) or log targets, and special pages
-               } elseif ( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
-                       list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $rc->mAttribs['rc_title'] );
-                       if ( $name == 'Log' ) {
-                               $this->insertLog( $s, $rc->getTitle(), $subpage );
-                       }
-               // Regular entries
-               } else {
-                       $this->insertDiffHist( $s, $rc, $unpatrolled );
-                       # M, N, b and ! (minor, new, bot and unpatrolled)
-                       $s .= $this->recentChangesFlags(
-                               array(
-                                       'newpage' => $rc->mAttribs['rc_type'] == RC_NEW,
-                                       'minor' => $rc->mAttribs['rc_minor'],
-                                       'unpatrolled' => $unpatrolled,
-                                       'bot' => $rc->mAttribs['rc_bot']
-                               ),
-                               ''
-                       );
-                       $this->insertArticleLink( $s, $rc, $unpatrolled, $watched );
-               }
-               # Edit/log timestamp
-               $this->insertTimestamp( $s, $rc );
-               # Bytes added or removed
-               if ( $wgRCShowChangedSize ) {
-                       $cd = $this->formatCharacterDifference( $rc );
-                       if ( $cd !== '' ) {
-                               $s .= $cd . '  <span class="mw-changeslist-separator">. .</span> ';
-                       }
-               }
-
-               if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
-                       $s .= $this->insertLogEntry( $rc );
-               } else {
-                       # User tool links
-                       $this->insertUserRelatedLinks( $s, $rc );
-                       # LTR/RTL direction mark
-                       $s .= $this->getLanguage()->getDirMark();
-                       $s .= $this->insertComment( $rc );
-               }
-
-               # Tags
-               $this->insertTags( $s, $rc, $classes );
-               # Rollback
-               $this->insertRollback( $s, $rc );
-               # For subclasses
-               $this->insertExtra( $s, $rc, $classes );
-
-               # How many users watch this page
-               if ( $rc->numberofWatchingusers > 0 ) {
-                       $s .= ' ' . $this->numberofWatchingusers( $rc->numberofWatchingusers );
-               }
-
-               if ( $this->watchlist ) {
-                       $classes[] = Sanitizer::escapeClass( 'watchlist-' . $rc->mAttribs['rc_namespace'] . '-' . $rc->mAttribs['rc_title'] );
-               }
-
-               if ( !wfRunHooks( 'OldChangesListRecentChangesLine', array( &$this, &$s, $rc, &$classes ) ) ) {
-                       wfProfileOut( __METHOD__ );
-                       return false;
-               }
-
-               wfProfileOut( __METHOD__ );
-               return "$dateheader<li class=\"" . implode( ' ', $classes ) . "\">" . $s . "</li>\n";
-       }
-}
-
-/**
- * Generate a list of changes using an Enhanced system (uses javascript).
- */
-class EnhancedChangesList extends ChangesList {
-
-       protected $rc_cache;
-
-       /**
-        * Add the JavaScript file for enhanced changeslist
-        * @return String
-        */
-       public function beginRecentChangesList() {
-               $this->rc_cache = array();
-               $this->rcMoveIndex = 0;
-               $this->rcCacheIndex = 0;
-               $this->lastdate = '';
-               $this->rclistOpen = false;
-               $this->getOutput()->addModuleStyles( array(
-                       'mediawiki.special.changeslist',
-                       'mediawiki.special.changeslist.enhanced',
-               ) );
-               $this->getOutput()->addModules( array(
-                       'jquery.makeCollapsible',
-                       'mediawiki.icon',
-               ) );
-               return '';
-       }
-       /**
-        * Format a line for enhanced recentchange (aka with javascript and block of lines).
-        *
-        * @param $baseRC RecentChange
-        * @param $watched bool
-        *
-        * @return string
-        */
-       public function recentChangesLine( &$baseRC, $watched = false ) {
-               wfProfileIn( __METHOD__ );
-
-               # Create a specialised object
-               $rc = RCCacheEntry::newFromParent( $baseRC );
-
-               $curIdEq = array( 'curid' => $rc->mAttribs['rc_cur_id'] );
-
-               # If it's a new day, add the headline and flush the cache
-               $date = $this->getLanguage()->userDate( $rc->mAttribs['rc_timestamp'], $this->getUser() );
-               $ret = '';
-               if ( $date != $this->lastdate ) {
-                       # Process current cache
-                       $ret = $this->recentChangesBlock();
-                       $this->rc_cache = array();
-                       $ret .= Xml::element( 'h4', null, $date ) . "\n";
-                       $this->lastdate = $date;
-               }
-
-               # Should patrol-related stuff be shown?
-               $rc->unpatrolled = $this->showAsUnpatrolled( $rc );
-
-               $showdifflinks = true;
-               # Make article link
-               $type = $rc->mAttribs['rc_type'];
-               $logType = $rc->mAttribs['rc_log_type'];
-               // Page moves, very old style, not supported anymore
-               if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
-               // New unpatrolled pages
-               } elseif ( $rc->unpatrolled && $type == RC_NEW ) {
-                       $clink = Linker::linkKnown( $rc->getTitle() );
-               // Log entries
-               } elseif ( $type == RC_LOG ) {
-                       if ( $logType ) {
-                               $logtitle = SpecialPage::getTitleFor( 'Log', $logType );
-                               $logpage = new LogPage( $logType );
-                               $logname = $logpage->getName()->escaped();
-                               $clink = $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logtitle, $logname ) )->escaped();
-                       } else {
-                               $clink = Linker::link( $rc->getTitle() );
-                       }
-                       $watched = false;
-               // Log entries (old format) and special pages
-               } elseif ( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
-                       wfDebug( "Unexpected special page in recentchanges\n" );
-                       $clink = '';
-               // Edits
-               } else {
-                       $clink = Linker::linkKnown( $rc->getTitle() );
-               }
-
-               # Don't show unusable diff links
-               if ( !ChangesList::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
-                       $showdifflinks = false;
-               }
-
-               $time = $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() );
-               $rc->watched = $watched;
-               $rc->link = $clink;
-               $rc->timestamp = $time;
-               $rc->numberofWatchingusers = $baseRC->numberofWatchingusers;
-
-               # Make "cur" and "diff" links.  Do not use link(), it is too slow if
-               # called too many times (50% of CPU time on RecentChanges!).
-               $thisOldid = $rc->mAttribs['rc_this_oldid'];
-               $lastOldid = $rc->mAttribs['rc_last_oldid'];
-
-               $querycur = $curIdEq + array( 'diff' => '0', 'oldid' => $thisOldid );
-               $querydiff = $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid );
-
-               if ( !$showdifflinks ) {
-                       $curLink = $this->message['cur'];
-                       $diffLink = $this->message['diff'];
-               } elseif ( in_array( $type, array( RC_NEW, RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
-                       if ( $type != RC_NEW ) {
-                               $curLink = $this->message['cur'];
-                       } else {
-                               $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
-                               $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
-                       }
-                       $diffLink = $this->message['diff'];
-               } else {
-                       $diffUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querydiff ) );
-                       $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
-                       $diffLink = "<a href=\"$diffUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['diff']}</a>";
-                       $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
-               }
-
-               # Make "last" link
-               if ( !$showdifflinks || !$lastOldid ) {
-                       $lastLink = $this->message['last'];
-               } elseif ( in_array( $type, array( RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
-                       $lastLink = $this->message['last'];
-               } else {
-                       $lastLink = Linker::linkKnown( $rc->getTitle(), $this->message['last'],
-                               array(), $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid ) );
-               }
-
-               # Make user links
-               if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
-                       $rc->userlink = ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
-               } else {
-                       $rc->userlink = Linker::userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
-                       $rc->usertalklink = Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
-               }
-
-               $rc->lastlink = $lastLink;
-               $rc->curlink = $curLink;
-               $rc->difflink = $diffLink;
-
-               # Put accumulated information into the cache, for later display
-               # Page moves go on their own line
-               $title = $rc->getTitle();
-               $secureName = $title->getPrefixedDBkey();
-               if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
-                       # Use an @ character to prevent collision with page names
-                       $this->rc_cache['@@' . ( $this->rcMoveIndex++ )] = array( $rc );
-               } else {
-                       # Logs are grouped by type
-                       if ( $type == RC_LOG ) {
-                               $secureName = SpecialPage::getTitleFor( 'Log', $logType )->getPrefixedDBkey();
-                       }
-                       if ( !isset( $this->rc_cache[$secureName] ) ) {
-                               $this->rc_cache[$secureName] = array();
-                       }
-
-                       array_push( $this->rc_cache[$secureName], $rc );
-               }
-
-               wfProfileOut( __METHOD__ );
-
-               return $ret;
-       }
-
-       /**
-        * Enhanced RC group
-        * @return string
-        */
-       protected function recentChangesBlockGroup( $block ) {
-               global $wgRCShowChangedSize;
-
-               wfProfileIn( __METHOD__ );
-
-               # Add the namespace and title of the block as part of the class
-               $classes = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' );
-               if ( $block[0]->mAttribs['rc_log_type'] ) {
-                       # Log entry
-                       $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
-                                       . $block[0]->mAttribs['rc_log_type'] . '-' . $block[0]->mAttribs['rc_title'] );
-               } else {
-                       $classes[] = 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' );
-
-               # Collate list of users
-               $userlinks = array();
-               # Other properties
-               $unpatrolled = false;
-               $isnew = false;
-               $allBots = true;
-               $allMinors = true;
-               $curId = $currentRevision = 0;
-               # Some catalyst variables...
-               $namehidden = true;
-               $allLogs = true;
-               foreach ( $block as $rcObj ) {
-                       $oldid = $rcObj->mAttribs['rc_last_oldid'];
-                       if ( $rcObj->mAttribs['rc_type'] == RC_NEW ) {
-                               $isnew = true;
-                       }
-                       // If all log actions to this page were hidden, then don't
-                       // give the name of the affected page for this block!
-                       if ( !$this->isDeleted( $rcObj, LogPage::DELETED_ACTION ) ) {
-                               $namehidden = false;
-                       }
-                       $u = $rcObj->userlink;
-                       if ( !isset( $userlinks[$u] ) ) {
-                               $userlinks[$u] = 0;
-                       }
-                       if ( $rcObj->unpatrolled ) {
-                               $unpatrolled = true;
-                       }
-                       if ( $rcObj->mAttribs['rc_type'] != RC_LOG ) {
-                               $allLogs = false;
-                       }
-                       # Get the latest entry with a page_id and oldid
-                       # since logs may not have these.
-                       if ( !$curId && $rcObj->mAttribs['rc_cur_id'] ) {
-                               $curId = $rcObj->mAttribs['rc_cur_id'];
-                       }
-                       if ( !$currentRevision && $rcObj->mAttribs['rc_this_oldid'] ) {
-                               $currentRevision = $rcObj->mAttribs['rc_this_oldid'];
-                       }
-
-                       if ( !$rcObj->mAttribs['rc_bot'] ) {
-                               $allBots = false;
-                       }
-                       if ( !$rcObj->mAttribs['rc_minor'] ) {
-                               $allMinors = false;
-                       }
-
-                       $userlinks[$u]++;
-               }
-
-               # Sort the list and convert to text
-               krsort( $userlinks );
-               asort( $userlinks );
-               $users = array();
-               foreach ( $userlinks as $userlink => $count ) {
-                       $text = $userlink;
-                       $text .= $this->getLanguage()->getDirMark();
-                       if ( $count > 1 ) {
-                               $text .= ' ' . $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->formatNum( $count ) . '×' )->escaped();
-                       }
-                       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( array(
-                       'newpage' => $isnew, # show, when one have this flag
-                       'minor' => $allMinors, # show only, when all have this flag
-                       'unpatrolled' => $unpatrolled, # show, when one have this flag
-                       'bot' => $allBots, # show only, when all have this flag
-               ) );
-
-               # Timestamp
-               $r .= '&#160;' . $block[0]->timestamp . '&#160;</td><td>';
-
-               # Article link
-               if ( $namehidden ) {
-                       $r .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-event' )->escaped() . '</span>';
-               } elseif ( $allLogs ) {
-                       $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
-               } else {
-                       $this->insertArticleLink( $r, $block[0], $block[0]->unpatrolled, $block[0]->watched );
-               }
-
-               $r .= $this->getLanguage()->getDirMark();
-
-               $queryParams['curid'] = $curId;
-
-               # Changes message
-               static $nchanges = array();
-               static $sinceLastVisitMsg = array();
-
-               $n = count( $block );
-               if ( !isset( $nchanges[$n] ) ) {
-                       $nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped();
-               }
-
-               $sinceLast = 0;
-               $unvisitedOldid = null;
-               foreach ( $block as $rcObj ) {
-                       // Same logic as below inside main foreach
-                       if ( $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched ) {
-                               $sinceLast++;
-                               $unvisitedOldid = $rcObj->mAttribs['rc_last_oldid'];
-                       }
-               }
-               if ( !isset( $sinceLastVisitMsg[$sinceLast] ) ) {
-                       $sinceLastVisitMsg[$sinceLast] =
-                               $this->msg( 'enhancedrc-since-last-visit' )->numParams( $sinceLast )->escaped();
-               }
-
-               # Total change link
-               $r .= ' ';
-               $logtext = '';
-               if ( !$allLogs ) {
-                       if ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
-                               $logtext .= $nchanges[$n];
-                       } elseif ( $isnew ) {
-                               $logtext .= $nchanges[$n];
-                       } else {
-                               $logtext .= Linker::link(
-                                       $block[0]->getTitle(),
-                                       $nchanges[$n],
-                                       array(),
-                                       $queryParams + array(
-                                               'diff' => $currentRevision,
-                                               'oldid' => $oldid,
-                                       ),
-                                       array( 'known', 'noclasses' )
-                               );
-                               if ( $sinceLast > 0 && $sinceLast < $n ) {
-                                       $logtext .= $this->message['pipe-separator'] . Linker::link(
-                                               $block[0]->getTitle(),
-                                               $sinceLastVisitMsg[$sinceLast],
-                                               array(),
-                                               $queryParams + array(
-                                                       'diff' => $currentRevision,
-                                                       'oldid' => $unvisitedOldid,
-                                               ),
-                                               array( 'known', 'noclasses' )
-                                       );
-                               }
-                       }
-               }
-
-               # History
-               if ( $allLogs ) {
-                       // don't show history link for logs
-               } elseif ( $namehidden || !$block[0]->getTitle()->exists() ) {
-                       $logtext .= $this->message['pipe-separator'] . $this->message['enhancedrc-history'];
-               } else {
-                       $params = $queryParams;
-                       $params['action'] = 'history';
-
-                       $logtext .= $this->message['pipe-separator'] .
-                               Linker::linkKnown(
-                                       $block[0]->getTitle(),
-                                       $this->message['enhancedrc-history'],
-                                       array(),
-                                       $params
-                               );
-               }
-
-               if ( $logtext !== '' ) {
-                       $r .= $this->msg( 'parentheses' )->rawParams( $logtext )->escaped();
-               }
-
-               $r .= ' <span class="mw-changeslist-separator">. .</span> ';
-
-               # Character difference (does not apply if only log items)
-               if ( $wgRCShowChangedSize && !$allLogs ) {
-                       $last = 0;
-                       $first = count( $block ) - 1;
-                       # Some events (like logs) have an "empty" size, so we need to skip those...
-                       while ( $last < $first && $block[$last]->mAttribs['rc_new_len'] === null ) {
-                               $last++;
-                       }
-                       while ( $first > $last && $block[$first]->mAttribs['rc_old_len'] === null ) {
-                               $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 );
-
-               # Sub-entries
-               foreach ( $block as $rcObj ) {
-                       # Classes to apply -- TODO implement
-                       $classes = array();
-                       $type = $rcObj->mAttribs['rc_type'];
-
-                       $trClass = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
-                               ? ' class="mw-enhanced-watched"' : '';
-
-                       $r .= '<tr' . $trClass . '><td></td><td class="mw-enhanced-rc">';
-                       $r .= $this->recentChangesFlags( array(
-                               'newpage' => $type == RC_NEW,
-                               'minor' => $rcObj->mAttribs['rc_minor'],
-                               'unpatrolled' => $rcObj->unpatrolled,
-                               'bot' => $rcObj->mAttribs['rc_bot'],
-                       ) );
-                       $r .= '&#160;</td><td class="mw-enhanced-rc-nested"><span class="mw-enhanced-rc-time">';
-
-                       $params = $queryParams;
-
-                       if ( $rcObj->mAttribs['rc_this_oldid'] != 0 ) {
-                               $params['oldid'] = $rcObj->mAttribs['rc_this_oldid'];
-                       }
-
-                       # Log timestamp
-                       if ( $type == RC_LOG ) {
-                               $link = $rcObj->timestamp;
-                       # Revision link
-                       } elseif ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
-                               $link = '<span class="history-deleted">' . $rcObj->timestamp . '</span> ';
-                       } else {
-
-                               $link = Linker::linkKnown(
-                                               $rcObj->getTitle(),
-                                               $rcObj->timestamp,
-                                               array(),
-                                               $params
-                                       );
-                               if ( $this->isDeleted( $rcObj, Revision::DELETED_TEXT ) ) {
-                                       $link = '<span class="history-deleted">' . $link . '</span> ';
-                               }
-                       }
-                       $r .= $link . '</span>';
-
-                       if ( !$type == RC_LOG || $type == RC_NEW ) {
-                               $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->curlink . $this->message['pipe-separator'] . $rcObj->lastlink )->escaped();
-                       }
-                       $r .= ' <span class="mw-changeslist-separator">. .</span> ';
-
-                       # Character diff
-                       if ( $wgRCShowChangedSize ) {
-                               $cd = $this->formatCharacterDifference( $rcObj );
-                               if ( $cd !== '' ) {
-                                       $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
-                               }
-                       }
-
-                       if ( $rcObj->mAttribs['rc_type'] == RC_LOG ) {
-                               $r .= $this->insertLogEntry( $rcObj );
-                       } else {
-                               # User links
-                               $r .= $rcObj->userlink;
-                               $r .= $rcObj->usertalklink;
-                               $r .= $this->insertComment( $rcObj );
-                       }
-
-                       # Rollback
-                       $this->insertRollback( $r, $rcObj );
-                       # Tags
-                       $this->insertTags( $r, $rcObj, $classes );
-
-                       $r .= "</td></tr>\n";
-               }
-               $r .= "</table>\n";
-
-               $this->rcCacheIndex++;
-
-               wfProfileOut( __METHOD__ );
-
-               return $r;
-       }
-
-       /**
-        * Generate HTML for an arrow or placeholder graphic
-        * @param string $dir one of '', 'd', 'l', 'r'
-        * @param string $alt text
-        * @param string $title text
-        * @return String: HTML "<img>" tag
-        */
-       protected function arrow( $dir, $alt = '', $title = '' ) {
-               global $wgStylePath;
-               $encUrl = htmlspecialchars( $wgStylePath . '/common/images/Arr_' . $dir . '.png' );
-               $encAlt = htmlspecialchars( $alt );
-               $encTitle = htmlspecialchars( $title );
-               return "<img src=\"$encUrl\" width=\"12\" height=\"12\" alt=\"$encAlt\" title=\"$encTitle\" />";
-       }
-
-       /**
-        * Generate HTML for a right- or left-facing arrow,
-        * depending on language direction.
-        * @return String: HTML "<img>" tag
-        */
-       protected function sideArrow() {
-               $dir = $this->getLanguage()->isRTL() ? 'l' : 'r';
-               return $this->arrow( $dir, '+', $this->msg( 'rc-enhanced-expand' )->text() );
-       }
-
-       /**
-        * Generate HTML for a down-facing arrow
-        * depending on language direction.
-        * @return String: HTML "<img>" tag
-        */
-       protected function downArrow() {
-               return $this->arrow( 'd', '-', $this->msg( 'rc-enhanced-hide' )->text() );
-       }
-
-       /**
-        * Generate HTML for a spacer image
-        * @return String: HTML "<img>" tag
-        */
-       protected function spacerArrow() {
-               return $this->arrow( '', codepointToUtf8( 0xa0 ) ); // non-breaking space
-       }
-
-       /**
-        * Enhanced RC ungrouped line.
-        *
-        * @param $rcObj RecentChange
-        * @return String: a HTML formatted line (generated using $r)
-        */
-       protected function recentChangesBlockLine( $rcObj ) {
-               global $wgRCShowChangedSize;
-
-               wfProfileIn( __METHOD__ );
-               $query['curid'] = $rcObj->mAttribs['rc_cur_id'];
-
-               $type = $rcObj->mAttribs['rc_type'];
-               $logType = $rcObj->mAttribs['rc_log_type'];
-               $classes = array( 'mw-enhanced-rc' );
-               if ( $logType ) {
-                       # Log entry
-                       $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
-                                       . $logType . '-' . $rcObj->mAttribs['rc_title'] );
-               } else {
-                       $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' .
-                                       $rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] );
-               }
-               $classes[] = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
-                       ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
-               $r = Html::openElement( 'table', array( 'class' => $classes ) ) .
-                       Html::openElement( 'tr' );
-
-               $r .= '<td class="mw-enhanced-rc"><span class="mw-enhancedchanges-arrow-space"></span>';
-               # Flag and Timestamp
-               if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
-                       $r .= $this->recentChangesFlags( array() ); // no flags, but need the placeholders
-               } else {
-                       $r .= $this->recentChangesFlags( array(
-                               'newpage' => $type == RC_NEW,
-                               'minor' => $rcObj->mAttribs['rc_minor'],
-                               'unpatrolled' => $rcObj->unpatrolled,
-                               'bot' => $rcObj->mAttribs['rc_bot'],
-                       ) );
-               }
-               $r .= '&#160;' . $rcObj->timestamp . '&#160;</td><td>';
-               # Article or log link
-               if ( $logType ) {
-                       $logPage = new LogPage( $logType );
-                       $logTitle = SpecialPage::getTitleFor( 'Log', $logType );
-                       $logName = $logPage->getName()->escaped();
-                       $r .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logTitle, $logName ) )->escaped();
-               } else {
-                       $this->insertArticleLink( $r, $rcObj, $rcObj->unpatrolled, $rcObj->watched );
-               }
-               # Diff and hist links
-               if ( $type != RC_LOG ) {
-                       $query['action'] = 'history';
-                       $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->difflink . $this->message['pipe-separator'] . Linker::linkKnown(
-                               $rcObj->getTitle(),
-                               $this->message['hist'],
-                               array(),
-                               $query
-                       ) )->escaped();
-               }
-               $r .= ' <span class="mw-changeslist-separator">. .</span> ';
-               # Character diff
-               if ( $wgRCShowChangedSize ) {
-                       $cd = $this->formatCharacterDifference( $rcObj );
-                       if ( $cd !== '' ) {
-                               $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
-                       }
-               }
-
-               if ( $type == RC_LOG ) {
-                       $r .= $this->insertLogEntry( $rcObj );
-               } else {
-                       $r .= ' ' . $rcObj->userlink . $rcObj->usertalklink;
-                       $r .= $this->insertComment( $rcObj );
-                       $this->insertRollback( $r, $rcObj );
-               }
-
-               # Tags
-               $this->insertTags( $r, $rcObj, $classes );
-               # Show how many people are watching this if enabled
-               $r .= $this->numberofWatchingusers( $rcObj->numberofWatchingusers );
-
-               $r .= "</td></tr></table>\n";
-
-               wfProfileOut( __METHOD__ );
-
-               return $r;
-       }
-
-       /**
-        * If enhanced RC is in use, this function takes the previously cached
-        * RC lines, arranges them, and outputs the HTML
-        *
-        * @return string
-        */
-       protected function recentChangesBlock() {
-               if ( count ( $this->rc_cache ) == 0 ) {
-                       return '';
-               }
-
-               wfProfileIn( __METHOD__ );
-
-               $blockOut = '';
-               foreach ( $this->rc_cache as $block ) {
-                       if ( count( $block ) < 2 ) {
-                               $blockOut .= $this->recentChangesBlockLine( array_shift( $block ) );
-                       } else {
-                               $blockOut .= $this->recentChangesBlockGroup( $block );
-                       }
-               }
-
-               wfProfileOut( __METHOD__ );
-
-               return '<div>' . $blockOut . '</div>';
-       }
-
-       /**
-        * Returns text for the end of RC
-        * If enhanced RC is in use, returns pretty much all the text
-        * @return string
-        */
-       public function endRecentChangesList() {
-               return $this->recentChangesBlock() . parent::endRecentChangesList();
-       }
-
-}
index ab03be3..880f05a 100644 (file)
@@ -3936,7 +3936,7 @@ $wgReservedUsernames = array(
        'ScriptImporter', // Default user name used by maintenance/importSiteScripts.php
        'msg:double-redirect-fixer', // Automatic double redirect fix
        'msg:usermessage-editor', // Default user for leaving user messages
-       'msg:proxyblocker', // For Special:Blockme
+       'msg:proxyblocker', // For $wgProxyList and Special:Blockme (removed in 1.22)
 );
 
 /**
@@ -4695,31 +4695,6 @@ $wgPasswordAttemptThrottle = array( 'count' => 5, 'seconds' => 300 );
  * @{
  */
 
-/**
- * If you enable this, every editor's IP address will be scanned for open HTTP
- * proxies.
- *
- * @warning Don't enable this. Many sysops will report "hostile TCP port scans"
- * to your ISP and ask for your server to be shut down.
- * You have been warned.
- */
-$wgBlockOpenProxies = false;
-
-/**
- * Port we want to scan for a proxy
- */
-$wgProxyPorts = array( 80, 81, 1080, 3128, 6588, 8000, 8080, 8888, 65506 );
-
-/**
- * Script used to scan
- */
-$wgProxyScriptPath = "$IP/maintenance/proxyCheck.php";
-
-/**
- * Expiration time for cached proxy IPs
- */
-$wgProxyMemcExpiry = 86400;
-
 /**
  * This should always be customised in LocalSettings.php
  */
@@ -5936,6 +5911,11 @@ $wgExtensionEntryPointListFiles = array();
  */
 $wgParserOutputHooks = array();
 
+/**
+ * Whether to include the NewPP limit report as a HTML comment
+ */
+$wgEnableParserLimitReporting = true;
+
 /**
  * List of valid skin names.
  * The key should be the name in all lower case, the value should be a properly
index 7471589..d7b2b5e 100644 (file)
@@ -840,7 +840,6 @@ class EditPage {
                if ( $this->textbox1 === false ) {
                        return false;
                }
-               wfProxyCheck();
                return true;
        }
 
index eff1756..54822e3 100644 (file)
@@ -144,10 +144,10 @@ class FormOptions implements ArrayAccess {
        /**
         * Use to set the value of an option.
         *
-        * @param string $name option name
-        * @param mixed $value value for the option
-        * @param bool $force Whether to set the value when it is equivalent to
-        * the default value for this option (default false).
+        * @param string $name Option name
+        * @param mixed $value Value for the option
+        * @param bool $force Whether to set the value when it is equivalent to the default value for this
+        *     option (default false).
         */
        public function setValue( $name, $value, $force = false ) {
                $this->validateName( $name, true );
@@ -161,10 +161,9 @@ class FormOptions implements ArrayAccess {
        }
 
        /**
-        * Get the value for the given option name.
-        * Internally use getValueReal()
+        * Get the value for the given option name. Uses getValueReal() internally.
         *
-        * @param string $name option name
+        * @param string $name Option name
         * @return mixed
         */
        public function getValue( $name ) {
@@ -174,9 +173,10 @@ class FormOptions implements ArrayAccess {
        }
 
        /**
-        * @todo Document
-        * @param array $option array structure describing the option
-        * @return mixed Value or the default value if it is null
+        * Return current option value, based on a structure taken from $options.
+        *
+        * @param array $option Array structure describing the option
+        * @return mixed Value, or the default value if it is null
         */
        protected function getValueReal( $option ) {
                if ( $option['value'] !== null ) {
index 8241d81..f2a8dab 100644 (file)
@@ -931,14 +931,12 @@ function wfDebug( $text, $logonly = false ) {
                MWDebug::debugMsg( $text );
        }
 
-       if ( wfRunHooks( 'Debug', array( $text, null /* no log group */ ) ) ) {
-               if ( $wgDebugLogFile != '' && !$wgProfileOnly ) {
-                       # Strip unprintables; they can switch terminal modes when binary data
-                       # gets dumped, which is pretty annoying.
-                       $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
-                       $text = $wgDebugLogPrefix . $text;
-                       wfErrorLog( $text, $wgDebugLogFile );
-               }
+       if ( $wgDebugLogFile != '' && !$wgProfileOnly ) {
+               # Strip unprintables; they can switch terminal modes when binary data
+               # gets dumped, which is pretty annoying.
+               $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
+               $text = $wgDebugLogPrefix . $text;
+               wfErrorLog( $text, $wgDebugLogFile );
        }
 }
 
@@ -1013,9 +1011,7 @@ function wfDebugLog( $logGroup, $text, $public = true ) {
                $time = wfTimestamp( TS_DB );
                $wiki = wfWikiID();
                $host = wfHostname();
-               if ( wfRunHooks( 'Debug', array( $text, $logGroup ) ) ) {
-                       wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] );
-               }
+               wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] );
        } elseif ( $public === true ) {
                wfDebug( "[$logGroup] $text", false );
        }
index cd39ad8..930f8c0 100644 (file)
@@ -27,6 +27,8 @@
  * @since 1.22
  */
 class HashRing {
+       /** @var Array (location => weight) */
+       protected $sourceMap = array();
        /** @var Array (location => (start, end)) */
        protected $ring = array();
 
@@ -40,6 +42,7 @@ class HashRing {
                if ( !count( $map ) ) {
                        throw new MWException( "Ring is empty or all weights are zero." );
                }
+               $this->sourceMap = $map;
                // Sort the locations based on the hash of their names
                $hashes = array();
                foreach ( $map as $location => $weight ) {
@@ -112,4 +115,28 @@ class HashRing {
                }
                return $locations;
        }
+
+       /**
+        * Get the map of locations to weight (ignores 0-weight items)
+        *
+        * @return array
+        */
+       public function getLocationWeights() {
+               return $this->sourceMap;
+       }
+
+       /**
+        * Get a new hash ring with a location removed from the ring
+        *
+        * @param string $location
+        * @return HashRing|bool Returns false if no non-zero weighted spots are left
+        */
+       public function newWithoutLocation( $location ) {
+               $map = $this->sourceMap;
+               unset( $map[$location] );
+               if ( count( $map ) ) {
+                       return new self( $map );
+               }
+               return false;
+       }
 }
index 73e0af2..208f96e 100644 (file)
@@ -356,6 +356,96 @@ class Message {
                return $this;
        }
 
+       /**
+        * Add parameters that are durations of time and will be passed through
+        * Language::formatDuration before substitution
+        * @since 1.22
+        * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+        * @return Message: $this
+        */
+       public function durationParams( /*...*/ ) {
+               $params = func_get_args();
+               if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+                       $params = $params[0];
+               }
+               foreach ( $params as $param ) {
+                       $this->parameters[] = self::durationParam( $param );
+               }
+               return $this;
+       }
+
+       /**
+        * Add parameters that are expiration times and will be passed through
+        * Language::formatExpiry before substitution
+        * @since 1.22
+        * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+        * @return Message: $this
+        */
+       public function expiryParams( /*...*/ ) {
+               $params = func_get_args();
+               if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+                       $params = $params[0];
+               }
+               foreach ( $params as $param ) {
+                       $this->parameters[] = self::expiryParam( $param );
+               }
+               return $this;
+       }
+
+       /**
+        * Add parameters that are time periods and will be passed through
+        * Language::formatTimePeriod before substitution
+        * @since 1.22
+        * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+        * @return Message: $this
+        */
+       public function timeperiodParams( /*...*/ ) {
+               $params = func_get_args();
+               if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+                       $params = $params[0];
+               }
+               foreach ( $params as $param ) {
+                       $this->parameters[] = self::timeperiodParam( $param );
+               }
+               return $this;
+       }
+
+       /**
+        * Add parameters that are file sizes and will be passed through
+        * Language::formatSize before substitution
+        * @since 1.22
+        * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+        * @return Message: $this
+        */
+       public function sizeParams( /*...*/ ) {
+               $params = func_get_args();
+               if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+                       $params = $params[0];
+               }
+               foreach ( $params as $param ) {
+                       $this->parameters[] = self::sizeParam( $param );
+               }
+               return $this;
+       }
+
+       /**
+        * Add parameters that are bitrates and will be passed through
+        * Language::formatBitrate before substitution
+        * @since 1.22
+        * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
+        * @return Message: $this
+        */
+       public function bitrateParams( /*...*/ ) {
+               $params = func_get_args();
+               if ( isset( $params[0] ) && is_array( $params[0] ) ) {
+                       $params = $params[0];
+               }
+               foreach ( $params as $param ) {
+                       $this->parameters[] = self::bitrateParam( $param );
+               }
+               return $this;
+       }
+
        /**
         * Set the language and the title from a context object
         * @since 1.19
@@ -638,6 +728,51 @@ class Message {
                return array( 'num' => $value );
        }
 
+       /**
+        * @since 1.22
+        * @param $value
+        * @return array
+        */
+       public static function durationParam( $value ) {
+               return array( 'duration' => $value );
+       }
+
+       /**
+        * @since 1.22
+        * @param $value
+        * @return array
+        */
+       public static function expiryParam( $value ) {
+               return array( 'expiry' => $value );
+       }
+
+       /**
+        * @since 1.22
+        * @param $value
+        * @return array
+        */
+       public static function timeperiodParam( $value ) {
+               return array( 'period' => $value );
+       }
+
+       /**
+        * @since 1.22
+        * @param $value
+        * @return array
+        */
+       public static function sizeParam( $value ) {
+               return array( 'size' => $value );
+       }
+
+       /**
+        * @since 1.22
+        * @param $value
+        * @return array
+        */
+       public static function bitrateParam( $value ) {
+               return array( 'bitrate' => $value );
+       }
+
        /**
         * Substitutes any parameters into the message text.
         * @since 1.17
@@ -664,20 +799,32 @@ class Message {
         * @return Tuple(type, value)
         */
        protected function extractParam( $param ) {
-               if ( is_array( $param ) && isset( $param['raw'] ) ) {
-                       return array( 'after', $param['raw'] );
-               } elseif ( is_array( $param ) && isset( $param['num'] ) ) {
-                       // Replace number params always in before step for now.
-                       // No support for combined raw and num params
-                       return array( 'before', $this->language->formatNum( $param['num'] ) );
-               } elseif ( !is_array( $param ) ) {
-                       return array( 'before', $param );
+               if ( is_array( $param ) ){
+                       if ( isset( $param['raw'] ) ) {
+                               return array( 'after', $param['raw'] );
+                       } elseif ( isset( $param['num'] ) ) {
+                               // Replace number params always in before step for now.
+                               // No support for combined raw and num params
+                               return array( 'before', $this->language->formatNum( $param['num'] ) );
+                       } elseif ( isset( $param['duration'] ) ) {
+                               return array( 'before', $this->language->formatDuration( $param['duration'] ) );
+                       } elseif ( isset( $param['expiry'] ) ) {
+                               return array( 'before', $this->language->formatExpiry( $param['expiry'] ) );
+                       } elseif ( isset( $param['period'] ) ) {
+                               return array( 'before', $this->language->formatTimePeriod( $param['period'] ) );
+                       } elseif ( isset( $param['size'] ) ) {
+                               return array( 'before', $this->language->formatSize( $param['size'] ) );
+                       } elseif ( isset( $param['bitrate'] ) ) {
+                               return array( 'before', $this->language->formatBitrate( $param['bitrate'] ) );
+                       } else {
+                               trigger_error(
+                                       "Invalid message parameter: " . htmlspecialchars( serialize( $param ) ),
+                                       E_USER_WARNING
+                               );
+                               return array( 'before', '[INVALID]' );
+                       }
                } else {
-                       trigger_error(
-                               "Invalid message parameter: " . htmlspecialchars( serialize( $param ) ),
-                               E_USER_WARNING
-                       );
-                       return array( 'before', '[INVALID]' );
+                       return array( 'before', $param );
                }
        }
 
index b54a9a3..bf1c405 100644 (file)
@@ -84,43 +84,3 @@ function wfIsConfiguredProxy( $ip ) {
                in_array( $ip, $wgSquidServersNoPurge );
        return $trusted;
 }
-
-/**
- * Forks processes to scan the originating IP for an open proxy server
- * MemCached can be used to skip IPs that have already been scanned
- */
-function wfProxyCheck() {
-       global $wgBlockOpenProxies, $wgProxyPorts, $wgProxyScriptPath;
-       global $wgMemc, $wgProxyMemcExpiry, $wgRequest;
-       global $wgProxyKey;
-
-       if ( !$wgBlockOpenProxies ) {
-               return;
-       }
-
-       $ip = $wgRequest->getIP();
-
-       # Get MemCached key
-       $mcKey = wfMemcKey( 'proxy', 'ip', $ip );
-       $mcValue = $wgMemc->get( $mcKey );
-       $skip = (bool)$mcValue;
-
-       # Fork the processes
-       if ( !$skip ) {
-               $title = SpecialPage::getTitleFor( 'Blockme' );
-               $iphash = md5( $ip . $wgProxyKey );
-               $url = wfExpandUrl( $title->getFullURL( 'ip=' . $iphash ), PROTO_HTTP );
-
-               foreach ( $wgProxyPorts as $port ) {
-                       $params = implode( ' ', array(
-                                               escapeshellarg( $wgProxyScriptPath ),
-                                               escapeshellarg( $ip ),
-                                               escapeshellarg( $port ),
-                                               escapeshellarg( $url )
-                                               ));
-                       exec( "php $params >" . wfGetNull() . " 2>&1 &" );
-               }
-               # Set MemCached key
-               $wgMemc->set( $mcKey, 1, $wgProxyMemcExpiry );
-       }
-}
diff --git a/includes/RecentChange.php b/includes/RecentChange.php
deleted file mode 100644 (file)
index 282890f..0000000
+++ /dev/null
@@ -1,860 +0,0 @@
-<?php
-/**
- * Utility class for creating and accessing recent change entries.
- *
- * 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
- */
-
-/**
- * Utility class for creating new RC entries
- *
- * mAttribs:
- *  rc_id           id of the row in the recentchanges table
- *  rc_timestamp    time the entry was made
- *  rc_cur_time     timestamp on the cur row
- *  rc_namespace    namespace #
- *  rc_title        non-prefixed db key
- *  rc_type         is new entry, used to determine whether updating is necessary
- *  rc_source       string representation of change source
- *  rc_minor        is minor
- *  rc_cur_id       page_id of associated page entry
- *  rc_user         user id who made the entry
- *  rc_user_text    user name who made the entry
- *  rc_comment      edit summary
- *  rc_this_oldid   rev_id associated with this entry (or zero)
- *  rc_last_oldid   rev_id associated with the entry before this one (or zero)
- *  rc_bot          is bot, hidden
- *  rc_ip           IP address of the user in dotted quad notation
- *  rc_new          obsolete, use rc_type==RC_NEW
- *  rc_patrolled    boolean whether or not someone has marked this edit as patrolled
- *  rc_old_len      integer byte length of the text before the edit
- *  rc_new_len      the same after the edit
- *  rc_deleted      partial deletion
- *  rc_logid        the log_id value for this log entry (or zero)
- *  rc_log_type     the log type (or null)
- *  rc_log_action   the log action (or null)
- *  rc_params       log params
- *
- * mExtra:
- *  prefixedDBkey   prefixed db key, used by external app via msg queue
- *  lastTimestamp   timestamp of previous entry, used in WHERE clause during update
- *  lang            the interwiki prefix, automatically set in save()
- *  oldSize         text size before the change
- *  newSize         text size after the change
- *  pageStatus      status of the page: created, deleted, moved, restored, changed
- *
- * temporary:       not stored in the database
- *      notificationtimestamp
- *      numberofWatchingusers
- *
- * @todo document functions and variables
- */
-class RecentChange {
-
-       // Constants for the rc_source field.  Extensions may also have
-       // their own source constants.
-       const SRC_EDIT = 'mw.edit';
-       const SRC_NEW = 'mw.new';
-       const SRC_LOG = 'mw.log';
-       const SRC_EXTERNAL = 'mw.external'; // obsolete
-
-       var $mAttribs = array(), $mExtra = array();
-
-       /**
-        * @var Title
-        */
-       var $mTitle = false;
-
-       /**
-        * @var User
-        */
-       private $mPerformer = false;
-
-       /**
-        * @var Title
-        */
-       var $mMovedToTitle = false;
-       var $numberofWatchingusers = 0; # Dummy to prevent error message in SpecialRecentchangeslinked
-       var $notificationtimestamp;
-
-       # Factory methods
-
-       /**
-        * @param $row
-        * @return RecentChange
-        */
-       public static function newFromRow( $row ) {
-               $rc = new RecentChange;
-               $rc->loadFromRow( $row );
-               return $rc;
-       }
-
-       /**
-        * @deprecated in 1.22
-        * @param $row
-        * @return RecentChange
-        */
-       public static function newFromCurRow( $row ) {
-               wfDeprecated( __METHOD__, '1.22' );
-               $rc = new RecentChange;
-               $rc->loadFromCurRow( $row );
-               $rc->notificationtimestamp = false;
-               $rc->numberofWatchingusers = false;
-               return $rc;
-       }
-
-       /**
-        * Obtain the recent change with a given rc_id value
-        *
-        * @param int $rcid rc_id value to retrieve
-        * @return RecentChange
-        */
-       public static function newFromId( $rcid ) {
-               return self::newFromConds( array( 'rc_id' => $rcid ), __METHOD__ );
-       }
-
-       /**
-        * Find the first recent change matching some specific conditions
-        *
-        * @param array $conds of conditions
-        * @param $fname Mixed: override the method name in profiling/logs
-        * @param $options Array Query options
-        * @return RecentChange
-        */
-       public static function newFromConds( $conds, $fname = __METHOD__, $options = array() ) {
-               $dbr = wfGetDB( DB_SLAVE );
-               $row = $dbr->selectRow( 'recentchanges', self::selectFields(), $conds, $fname, $options );
-               if ( $row !== false ) {
-                       return self::newFromRow( $row );
-               } else {
-                       return null;
-               }
-       }
-
-       /**
-        * Return the list of recentchanges fields that should be selected to create
-        * a new recentchanges object.
-        * @return array
-        */
-       public static function selectFields() {
-               return array(
-                       'rc_id',
-                       'rc_timestamp',
-                       'rc_cur_time',
-                       'rc_user',
-                       'rc_user_text',
-                       'rc_namespace',
-                       'rc_title',
-                       'rc_comment',
-                       'rc_minor',
-                       'rc_bot',
-                       'rc_new',
-                       'rc_cur_id',
-                       'rc_this_oldid',
-                       'rc_last_oldid',
-                       'rc_type',
-                       'rc_source',
-                       'rc_patrolled',
-                       'rc_ip',
-                       'rc_old_len',
-                       'rc_new_len',
-                       'rc_deleted',
-                       'rc_logid',
-                       'rc_log_type',
-                       'rc_log_action',
-                       'rc_params',
-               );
-       }
-
-       # Accessors
-
-       /**
-        * @param $attribs array
-        */
-       public function setAttribs( $attribs ) {
-               $this->mAttribs = $attribs;
-       }
-
-       /**
-        * @param $extra array
-        */
-       public function setExtra( $extra ) {
-               $this->mExtra = $extra;
-       }
-
-       /**
-        *
-        * @return Title
-        */
-       public function &getTitle() {
-               if ( $this->mTitle === false ) {
-                       $this->mTitle = Title::makeTitle( $this->mAttribs['rc_namespace'], $this->mAttribs['rc_title'] );
-               }
-               return $this->mTitle;
-       }
-
-       /**
-        * Get the User object of the person who performed this change.
-        *
-        * @return User
-        */
-       public function getPerformer() {
-               if ( $this->mPerformer === false ) {
-                       if ( $this->mAttribs['rc_user'] ) {
-                               $this->mPerformer = User::newFromID( $this->mAttribs['rc_user'] );
-                       } else {
-                               $this->mPerformer = User::newFromName( $this->mAttribs['rc_user_text'], false );
-                       }
-               }
-               return $this->mPerformer;
-       }
-
-       /**
-        * Writes the data in this object to the database
-        * @param $noudp bool
-        */
-       public function save( $noudp = false ) {
-               global $wgLocalInterwiki, $wgPutIPinRC, $wgUseEnotif, $wgShowUpdatedMarker, $wgContLang;
-
-               $dbw = wfGetDB( DB_MASTER );
-               if ( !is_array( $this->mExtra ) ) {
-                       $this->mExtra = array();
-               }
-               $this->mExtra['lang'] = $wgLocalInterwiki;
-
-               if ( !$wgPutIPinRC ) {
-                       $this->mAttribs['rc_ip'] = '';
-               }
-
-               # If our database is strict about IP addresses, use NULL instead of an empty string
-               if ( $dbw->strictIPs() and $this->mAttribs['rc_ip'] == '' ) {
-                       unset( $this->mAttribs['rc_ip'] );
-               }
-
-               # Trim spaces on user supplied text
-               $this->mAttribs['rc_comment'] = trim( $this->mAttribs['rc_comment'] );
-
-               # Make sure summary is truncated (whole multibyte characters)
-               $this->mAttribs['rc_comment'] = $wgContLang->truncate( $this->mAttribs['rc_comment'], 255 );
-
-               # Fixup database timestamps
-               $this->mAttribs['rc_timestamp'] = $dbw->timestamp( $this->mAttribs['rc_timestamp'] );
-               $this->mAttribs['rc_cur_time'] = $dbw->timestamp( $this->mAttribs['rc_cur_time'] );
-               $this->mAttribs['rc_id'] = $dbw->nextSequenceValue( 'recentchanges_rc_id_seq' );
-
-               ## If we are using foreign keys, an entry of 0 for the page_id will fail, so use NULL
-               if ( $dbw->cascadingDeletes() and $this->mAttribs['rc_cur_id'] == 0 ) {
-                       unset( $this->mAttribs['rc_cur_id'] );
-               }
-
-               # Insert new row
-               $dbw->insert( 'recentchanges', $this->mAttribs, __METHOD__ );
-
-               # Set the ID
-               $this->mAttribs['rc_id'] = $dbw->insertId();
-
-               # Notify extensions
-               wfRunHooks( 'RecentChange_save', array( &$this ) );
-
-               # Notify external application via UDP
-               if ( !$noudp ) {
-                       $this->notifyRCFeeds();
-               }
-
-               # E-mail notifications
-               if ( $wgUseEnotif || $wgShowUpdatedMarker ) {
-                       $editor = $this->getPerformer();
-                       $title = $this->getTitle();
-
-                       if ( wfRunHooks( 'AbortEmailNotification', array( $editor, $title ) ) ) {
-                               # @todo FIXME: This would be better as an extension hook
-                               $enotif = new EmailNotification();
-                               $enotif->notifyOnPageChange( $editor, $title,
-                                       $this->mAttribs['rc_timestamp'],
-                                       $this->mAttribs['rc_comment'],
-                                       $this->mAttribs['rc_minor'],
-                                       $this->mAttribs['rc_last_oldid'],
-                                       $this->mExtra['pageStatus'] );
-                       }
-               }
-       }
-
-       /**
-        * @deprecated since 1.22, use notifyRCFeeds instead.
-        */
-       public function notifyRC2UDP() {
-               wfDeprecated( __METHOD__, '1.22' );
-               $this->notifyRCFeeds();
-       }
-
-       /**
-        * Send some text to UDP.
-        * @deprecated since 1.22
-        */
-       public static function sendToUDP( $line, $address = '', $prefix = '', $port = '' ) {
-               global $wgRC2UDPAddress, $wgRC2UDPInterwikiPrefix, $wgRC2UDPPort, $wgRC2UDPPrefix;
-
-               wfDeprecated( __METHOD__, '1.22' );
-
-               # Assume default for standard RC case
-               $address = $address ? $address : $wgRC2UDPAddress;
-               $prefix = $prefix ? $prefix : $wgRC2UDPPrefix;
-               $port = $port ? $port : $wgRC2UDPPort;
-
-               $engine = new UDPRCFeedEngine();
-               $feed = array(
-                       'uri' => "udp://$address:$port/$prefix",
-                       'formatter' => 'IRCColourfulRCFeedFormatter',
-                       'add_interwiki_prefix' => $wgRC2UDPInterwikiPrefix,
-               );
-
-               return $engine->send( $feed, $line );
-       }
-
-       /**
-        * Notify all the feeds about the change.
-        */
-       public function notifyRCFeeds() {
-               global $wgRCFeeds;
-
-               foreach ( $wgRCFeeds as $feed ) {
-                       $engine = self::getEngine( $feed['uri'] );
-
-                       if ( isset( $this->mExtra['actionCommentIRC'] ) ) {
-                               $actionComment = $this->mExtra['actionCommentIRC'];
-                       } else {
-                               $actionComment = null;
-                       }
-
-                       $omitBots = isset( $feed['omit_bots'] ) ? $feed['omit_bots'] : false;
-
-                       if (
-                               ( $omitBots && $this->mAttribs['rc_bot'] ) ||
-                               $this->mAttribs['rc_type'] == RC_EXTERNAL
-                       ) {
-                               continue;
-                       }
-
-                       $formatter = new $feed['formatter']();
-                       $line = $formatter->getLine( $feed, $this, $actionComment );
-
-                       $engine->send( $feed, $line );
-               }
-       }
-
-       /**
-        * Gets the stream engine object for a given URI from $wgRCEngines
-        *
-        * @param $uri string URI to get the engine object for
-        * @return object The engine object
-        */
-       private static function getEngine( $uri ) {
-               global $wgRCEngines;
-
-               $scheme = parse_url( $uri, PHP_URL_SCHEME );
-               if ( !$scheme ) {
-                       throw new MWException( __FUNCTION__ . ": Invalid stream logger URI: '$uri'" );
-               }
-
-               if ( !isset( $wgRCEngines[$scheme] ) ) {
-                       throw new MWException( __FUNCTION__ . ": Unknown stream logger URI scheme: $scheme" );
-               }
-
-               return new $wgRCEngines[$scheme];
-       }
-
-       /**
-        * @deprecated since 1.22, moved to IRCColourfulRCFeedFormatter
-        */
-       public static function cleanupForIRC( $text ) {
-               wfDeprecated( __METHOD__, '1.22' );
-               return IRCColourfulRCFeedFormatter::cleanupForIRC( $text );
-       }
-
-       /**
-        * Mark a given change as patrolled
-        *
-        * @param $change Mixed: RecentChange or corresponding rc_id
-        * @param $auto Boolean: for automatic patrol
-        * @return Array See doMarkPatrolled(), or null if $change is not an existing rc_id
-        */
-       public static function markPatrolled( $change, $auto = false ) {
-               global $wgUser;
-
-               $change = $change instanceof RecentChange
-                       ? $change
-                       : RecentChange::newFromId( $change );
-
-               if ( !$change instanceof RecentChange ) {
-                       return null;
-               }
-               return $change->doMarkPatrolled( $wgUser, $auto );
-       }
-
-       /**
-        * Mark this RecentChange as patrolled
-        *
-        * NOTE: Can also return 'rcpatroldisabled', 'hookaborted' and 'markedaspatrollederror-noautopatrol' as errors
-        * @param $user User object doing the action
-        * @param $auto Boolean: for automatic patrol
-        * @return array of permissions errors, see Title::getUserPermissionsErrors()
-        */
-       public function doMarkPatrolled( User $user, $auto = false ) {
-               global $wgUseRCPatrol, $wgUseNPPatrol;
-               $errors = array();
-               // If recentchanges patrol is disabled, only new pages
-               // can be patrolled
-               if ( !$wgUseRCPatrol && ( !$wgUseNPPatrol || $this->getAttribute( 'rc_type' ) != RC_NEW ) ) {
-                       $errors[] = array( 'rcpatroldisabled' );
-               }
-               // Automatic patrol needs "autopatrol", ordinary patrol needs "patrol"
-               $right = $auto ? 'autopatrol' : 'patrol';
-               $errors = array_merge( $errors, $this->getTitle()->getUserPermissionsErrors( $right, $user ) );
-               if ( !wfRunHooks( 'MarkPatrolled', array( $this->getAttribute( 'rc_id' ), &$user, false ) ) ) {
-                       $errors[] = array( 'hookaborted' );
-               }
-               // Users without the 'autopatrol' right can't patrol their
-               // own revisions
-               if ( $user->getName() == $this->getAttribute( 'rc_user_text' ) && !$user->isAllowed( 'autopatrol' ) ) {
-                       $errors[] = array( 'markedaspatrollederror-noautopatrol' );
-               }
-               if ( $errors ) {
-                       return $errors;
-               }
-               // If the change was patrolled already, do nothing
-               if ( $this->getAttribute( 'rc_patrolled' ) ) {
-                       return array();
-               }
-               // Actually set the 'patrolled' flag in RC
-               $this->reallyMarkPatrolled();
-               // Log this patrol event
-               PatrolLog::record( $this, $auto, $user );
-               wfRunHooks( 'MarkPatrolledComplete', array( $this->getAttribute( 'rc_id' ), &$user, false ) );
-               return array();
-       }
-
-       /**
-        * Mark this RecentChange patrolled, without error checking
-        * @return Integer: number of affected rows
-        */
-       public function reallyMarkPatrolled() {
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->update(
-                       'recentchanges',
-                       array(
-                               'rc_patrolled' => 1
-                       ),
-                       array(
-                               'rc_id' => $this->getAttribute( 'rc_id' )
-                       ),
-                       __METHOD__
-               );
-               // Invalidate the page cache after the page has been patrolled
-               // to make sure that the Patrol link isn't visible any longer!
-               $this->getTitle()->invalidateCache();
-               return $dbw->affectedRows();
-       }
-
-       /**
-        * Makes an entry in the database corresponding to an edit
-        *
-        * @param $timestamp
-        * @param $title Title
-        * @param $minor
-        * @param $user User
-        * @param $comment
-        * @param $oldId
-        * @param $lastTimestamp
-        * @param $bot
-        * @param $ip string
-        * @param $oldSize int
-        * @param $newSize int
-        * @param $newId int
-        * @param $patrol int
-        * @return RecentChange
-        */
-       public static function notifyEdit( $timestamp, &$title, $minor, &$user, $comment, $oldId,
-               $lastTimestamp, $bot, $ip = '', $oldSize = 0, $newSize = 0, $newId = 0, $patrol = 0 ) {
-               $rc = new RecentChange;
-               $rc->mTitle = $title;
-               $rc->mPerformer = $user;
-               $rc->mAttribs = array(
-                       'rc_timestamp'  => $timestamp,
-                       'rc_cur_time'   => $timestamp,
-                       'rc_namespace'  => $title->getNamespace(),
-                       'rc_title'      => $title->getDBkey(),
-                       'rc_type'       => RC_EDIT,
-                       'rc_source'     => self::SRC_EDIT,
-                       'rc_minor'      => $minor ? 1 : 0,
-                       'rc_cur_id'     => $title->getArticleID(),
-                       'rc_user'       => $user->getId(),
-                       'rc_user_text'  => $user->getName(),
-                       'rc_comment'    => $comment,
-                       'rc_this_oldid' => $newId,
-                       'rc_last_oldid' => $oldId,
-                       'rc_bot'        => $bot ? 1 : 0,
-                       'rc_ip'         => self::checkIPAddress( $ip ),
-                       'rc_patrolled'  => intval( $patrol ),
-                       'rc_new'        => 0,  # obsolete
-                       'rc_old_len'    => $oldSize,
-                       'rc_new_len'    => $newSize,
-                       'rc_deleted'    => 0,
-                       'rc_logid'      => 0,
-                       'rc_log_type'   => null,
-                       'rc_log_action' => '',
-                       'rc_params'     => ''
-               );
-
-               $rc->mExtra = array(
-                       'prefixedDBkey' => $title->getPrefixedDBkey(),
-                       'lastTimestamp' => $lastTimestamp,
-                       'oldSize'       => $oldSize,
-                       'newSize'       => $newSize,
-                       'pageStatus'   => 'changed'
-               );
-               $rc->save();
-               return $rc;
-       }
-
-       /**
-        * Makes an entry in the database corresponding to page creation
-        * Note: the title object must be loaded with the new id using resetArticleID()
-        * @todo Document parameters and return
-        *
-        * @param $timestamp
-        * @param $title Title
-        * @param $minor
-        * @param $user User
-        * @param $comment
-        * @param $bot
-        * @param $ip string
-        * @param $size int
-        * @param $newId int
-        * @param $patrol int
-        * @return RecentChange
-        */
-       public static function notifyNew( $timestamp, &$title, $minor, &$user, $comment, $bot,
-               $ip = '', $size = 0, $newId = 0, $patrol = 0 ) {
-               $rc = new RecentChange;
-               $rc->mTitle = $title;
-               $rc->mPerformer = $user;
-               $rc->mAttribs = array(
-                       'rc_timestamp'      => $timestamp,
-                       'rc_cur_time'       => $timestamp,
-                       'rc_namespace'      => $title->getNamespace(),
-                       'rc_title'          => $title->getDBkey(),
-                       'rc_type'           => RC_NEW,
-                       'rc_source'         => self::SRC_NEW,
-                       'rc_minor'          => $minor ? 1 : 0,
-                       'rc_cur_id'         => $title->getArticleID(),
-                       'rc_user'           => $user->getId(),
-                       'rc_user_text'      => $user->getName(),
-                       'rc_comment'        => $comment,
-                       'rc_this_oldid'     => $newId,
-                       'rc_last_oldid'     => 0,
-                       'rc_bot'            => $bot ? 1 : 0,
-                       'rc_ip'             => self::checkIPAddress( $ip ),
-                       'rc_patrolled'      => intval( $patrol ),
-                       'rc_new'            => 1, # obsolete
-                       'rc_old_len'        => 0,
-                       'rc_new_len'        => $size,
-                       'rc_deleted'        => 0,
-                       'rc_logid'          => 0,
-                       'rc_log_type'       => null,
-                       'rc_log_action'     => '',
-                       'rc_params'         => ''
-               );
-
-               $rc->mExtra = array(
-                       'prefixedDBkey' => $title->getPrefixedDBkey(),
-                       'lastTimestamp' => 0,
-                       'oldSize' => 0,
-                       'newSize' => $size,
-                       'pageStatus' => 'created'
-               );
-               $rc->save();
-               return $rc;
-       }
-
-       /**
-        * @param $timestamp
-        * @param $title
-        * @param $user
-        * @param $actionComment
-        * @param $ip string
-        * @param $type
-        * @param $action
-        * @param $target
-        * @param $logComment
-        * @param $params
-        * @param $newId int
-        * @param $actionCommentIRC string
-        * @return bool
-        */
-       public static function notifyLog( $timestamp, &$title, &$user, $actionComment, $ip, $type,
-               $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = '' )
-       {
-               global $wgLogRestrictions;
-               # Don't add private logs to RC!
-               if ( isset( $wgLogRestrictions[$type] ) && $wgLogRestrictions[$type] != '*' ) {
-                       return false;
-               }
-               $rc = self::newLogEntry( $timestamp, $title, $user, $actionComment, $ip, $type, $action,
-                       $target, $logComment, $params, $newId, $actionCommentIRC );
-               $rc->save();
-               return true;
-       }
-
-       /**
-        * @param $timestamp
-        * @param $title Title
-        * @param $user User
-        * @param $actionComment
-        * @param $ip string
-        * @param $type
-        * @param $action
-        * @param $target Title
-        * @param $logComment
-        * @param $params
-        * @param $newId int
-        * @param $actionCommentIRC string
-        * @return RecentChange
-        */
-       public static function newLogEntry( $timestamp, &$title, &$user, $actionComment, $ip,
-               $type, $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = '' ) {
-               global $wgRequest;
-
-               ## Get pageStatus for email notification
-               switch ( $type . '-' . $action ) {
-                       case 'delete-delete':
-                               $pageStatus = 'deleted';
-                               break;
-                       case 'move-move':
-                       case 'move-move_redir':
-                               $pageStatus = 'moved';
-                               break;
-                       case 'delete-restore':
-                               $pageStatus = 'restored';
-                               break;
-                       case 'upload-upload':
-                               $pageStatus = 'created';
-                               break;
-                       case 'upload-overwrite':
-                       default:
-                               $pageStatus = 'changed';
-                               break;
-               }
-
-               $rc = new RecentChange;
-               $rc->mTitle = $target;
-               $rc->mPerformer = $user;
-               $rc->mAttribs = array(
-                       'rc_timestamp'  => $timestamp,
-                       'rc_cur_time'   => $timestamp,
-                       'rc_namespace'  => $target->getNamespace(),
-                       'rc_title'      => $target->getDBkey(),
-                       'rc_type'       => RC_LOG,
-                       'rc_source'     => self::SRC_LOG,
-                       'rc_minor'      => 0,
-                       'rc_cur_id'     => $target->getArticleID(),
-                       'rc_user'       => $user->getId(),
-                       'rc_user_text'  => $user->getName(),
-                       'rc_comment'    => $logComment,
-                       'rc_this_oldid' => 0,
-                       'rc_last_oldid' => 0,
-                       'rc_bot'        => $user->isAllowed( 'bot' ) ? $wgRequest->getBool( 'bot', true ) : 0,
-                       'rc_ip'         => self::checkIPAddress( $ip ),
-                       'rc_patrolled'  => 1,
-                       'rc_new'        => 0, # obsolete
-                       'rc_old_len'    => null,
-                       'rc_new_len'    => null,
-                       'rc_deleted'    => 0,
-                       'rc_logid'      => $newId,
-                       'rc_log_type'   => $type,
-                       'rc_log_action' => $action,
-                       'rc_params'     => $params
-               );
-
-               $rc->mExtra = array(
-                       'prefixedDBkey' => $title->getPrefixedDBkey(),
-                       'lastTimestamp' => 0,
-                       'actionComment' => $actionComment, // the comment appended to the action, passed from LogPage
-                       'pageStatus'    => $pageStatus,
-                       'actionCommentIRC' => $actionCommentIRC
-               );
-               return $rc;
-       }
-
-       /**
-        * Initialises the members of this object from a mysql row object
-        *
-        * @param $row
-        */
-       public function loadFromRow( $row ) {
-               $this->mAttribs = get_object_vars( $row );
-               $this->mAttribs['rc_timestamp'] = wfTimestamp( TS_MW, $this->mAttribs['rc_timestamp'] );
-               $this->mAttribs['rc_deleted'] = $row->rc_deleted; // MUST be set
-       }
-
-       /**
-        * Makes a pseudo-RC entry from a cur row
-        *
-        * @deprected in 1.22
-        * @param $row
-        */
-       public function loadFromCurRow( $row ) {
-               wfDeprecated( __METHOD__, '1.22' );
-               $this->mAttribs = array(
-                       'rc_timestamp' => wfTimestamp( TS_MW, $row->rev_timestamp ),
-                       'rc_cur_time' => $row->rev_timestamp,
-                       'rc_user' => $row->rev_user,
-                       'rc_user_text' => $row->rev_user_text,
-                       'rc_namespace' => $row->page_namespace,
-                       'rc_title' => $row->page_title,
-                       'rc_comment' => $row->rev_comment,
-                       'rc_minor' => $row->rev_minor_edit ? 1 : 0,
-                       'rc_type' => $row->page_is_new ? RC_NEW : RC_EDIT,
-                       'rc_source' => $row->page_is_new ? self::SRC_NEW : self::SRC_EDIT,
-                       'rc_cur_id' => $row->page_id,
-                       'rc_this_oldid' => $row->rev_id,
-                       'rc_last_oldid' => isset( $row->rc_last_oldid ) ? $row->rc_last_oldid : 0,
-                       'rc_bot' => 0,
-                       'rc_ip' => '',
-                       'rc_id' => $row->rc_id,
-                       'rc_patrolled' => $row->rc_patrolled,
-                       'rc_new' => $row->page_is_new, # obsolete
-                       'rc_old_len' => $row->rc_old_len,
-                       'rc_new_len' => $row->rc_new_len,
-                       'rc_params' => isset( $row->rc_params ) ? $row->rc_params : '',
-                       'rc_log_type' => isset( $row->rc_log_type ) ? $row->rc_log_type : null,
-                       'rc_log_action' => isset( $row->rc_log_action ) ? $row->rc_log_action : null,
-                       'rc_logid' => isset( $row->rc_logid ) ? $row->rc_logid : 0,
-                       'rc_deleted' => $row->rc_deleted // MUST be set
-               );
-       }
-
-       /**
-        * Get an attribute value
-        *
-        * @param string $name Attribute name
-        * @return mixed
-        */
-       public function getAttribute( $name ) {
-               return isset( $this->mAttribs[$name] ) ? $this->mAttribs[$name] : null;
-       }
-
-       /**
-        * @return array
-        */
-       public function getAttributes() {
-               return $this->mAttribs;
-       }
-
-       /**
-        * Gets the end part of the diff URL associated with this object
-        * Blank if no diff link should be displayed
-        * @param $forceCur
-        * @return string
-        */
-       public function diffLinkTrail( $forceCur ) {
-               if ( $this->mAttribs['rc_type'] == RC_EDIT ) {
-                       $trail = "curid=" . (int)( $this->mAttribs['rc_cur_id'] ) .
-                               "&oldid=" . (int)( $this->mAttribs['rc_last_oldid'] );
-                       if ( $forceCur ) {
-                               $trail .= '&diff=0';
-                       } else {
-                               $trail .= '&diff=' . (int)( $this->mAttribs['rc_this_oldid'] );
-                       }
-               } else {
-                       $trail = '';
-               }
-               return $trail;
-       }
-
-       /**
-        * Returns the change size (HTML).
-        * The lengths can be given optionally.
-        * @param $old int
-        * @param $new int
-        * @return string
-        */
-       public function getCharacterDifference( $old = 0, $new = 0 ) {
-               if ( $old === 0 ) {
-                       $old = $this->mAttribs['rc_old_len'];
-               }
-               if ( $new === 0 ) {
-                       $new = $this->mAttribs['rc_new_len'];
-               }
-               if ( $old === null || $new === null ) {
-                       return '';
-               }
-               return ChangesList::showCharacterDifference( $old, $new );
-       }
-
-       /**
-        * Purge expired changes from the recentchanges table
-        * @since 1.22
-        */
-       public static function purgeExpiredChanges() {
-               if ( wfReadOnly() ) {
-                       return;
-               }
-
-               $method = __METHOD__;
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->onTransactionIdle( function() use ( $dbw, $method ) {
-                       global $wgRCMaxAge;
-
-                       $cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
-                       $dbw->delete(
-                               'recentchanges',
-                               array( 'rc_timestamp < ' . $dbw->addQuotes( $cutoff ) ),
-                               $method
-                       );
-               } );
-       }
-
-       private static function checkIPAddress( $ip ) {
-               global $wgRequest;
-               if ( $ip ) {
-                       if ( !IP::isIPAddress( $ip ) ) {
-                               throw new MWException( "Attempt to write \"" . $ip . "\" as an IP address into recent changes" );
-                       }
-               } else {
-                       $ip = $wgRequest->getIP();
-                       if ( !$ip ) {
-                               $ip = '';
-                       }
-               }
-               return $ip;
-       }
-
-       /**
-        * Check whether the given timestamp is new enough to have a RC row with a given tolerance
-        * as the recentchanges table might not be cleared out regularly (so older entries might exist)
-        * or rows which will be deleted soon shouldn't be included.
-        *
-        * @param $timestamp mixed MWTimestamp compatible timestamp
-        * @param $tolerance integer Tolerance in seconds
-        * @return bool
-        */
-       public static function isInRCLifespan( $timestamp, $tolerance = 0 ) {
-               global $wgRCMaxAge;
-               return wfTimestamp( TS_UNIX, $timestamp ) > time() - $tolerance - $wgRCMaxAge;
-       }
-}
index 44cafe9..e5b8872 100644 (file)
@@ -1717,6 +1717,10 @@ abstract class BaseTemplate extends QuickTemplate {
         * on the link) is present it will be used to generate a tooltip and
         * accesskey for the link.
         *
+        * The keys "context" and "primary" are ignored; these keys are used
+        * internally by skins and are not supposed to be included in the HTML
+        * output.
+        *
         * If you don't want an accesskey, set $item['tooltiponly'] = true;
         *
         * @param array $options can be used to affect the output of a link.
@@ -1757,7 +1761,7 @@ abstract class BaseTemplate extends QuickTemplate {
 
                if ( isset( $item['href'] ) || isset( $options['link-fallback'] ) ) {
                        $attrs = $item;
-                       foreach ( array( 'single-id', 'text', 'msg', 'tooltiponly' ) as $k ) {
+                       foreach ( array( 'single-id', 'text', 'msg', 'tooltiponly', 'context', 'primary' ) as $k ) {
                                unset( $attrs[$k] );
                        }
 
index c03f1ba..11edc8a 100644 (file)
@@ -155,7 +155,6 @@ class SpecialPageFactory {
 
                // Unlisted / redirects
                'Blankpage'                 => 'SpecialBlankpage',
-               'Blockme'                   => 'SpecialBlockme',
                'Emailuser'                 => 'SpecialEmailUser',
                'Movepage'                  => 'MovePageForm',
                'Mycontributions'           => 'SpecialMycontributions',
diff --git a/includes/changes/ChangesList.php b/includes/changes/ChangesList.php
new file mode 100644 (file)
index 0000000..bf800c4
--- /dev/null
@@ -0,0 +1,552 @@
+<?php
+/**
+ * Base class for all changes lists.
+ *
+ * The class is used for formatting recent changes, related changes and watchlist.
+ *
+ * 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
+ */
+
+class ChangesList extends ContextSource {
+
+       /**
+        * @var Skin
+        */
+       public $skin;
+
+       protected $watchlist = false;
+
+       protected $message;
+
+       /**
+        * Changeslist constructor
+        *
+        * @param $obj Skin or IContextSource
+        */
+       public function __construct( $obj ) {
+               if ( $obj instanceof IContextSource ) {
+                       $this->setContext( $obj );
+                       $this->skin = $obj->getSkin();
+               } else {
+                       $this->setContext( $obj->getContext() );
+                       $this->skin = $obj;
+               }
+               $this->preCacheMessages();
+       }
+
+       /**
+        * Fetch an appropriate changes list class for the main context
+        * This first argument used to be an User object.
+        *
+        * @deprecated in 1.18; use newFromContext() instead
+        * @param string|User $unused Unused
+        * @return ChangesList|EnhancedChangesList|OldChangesList derivative
+        */
+       public static function newFromUser( $unused ) {
+               wfDeprecated( __METHOD__, '1.18' );
+               return self::newFromContext( RequestContext::getMain() );
+       }
+
+       /**
+        * Fetch an appropriate changes list class for the specified context
+        * Some users might want to use an enhanced list format, for instance
+        *
+        * @param $context IContextSource to use
+        * @return ChangesList|EnhancedChangesList|OldChangesList derivative
+        */
+       public static function newFromContext( IContextSource $context ) {
+               $user = $context->getUser();
+               $sk = $context->getSkin();
+               $list = null;
+               if ( wfRunHooks( 'FetchChangesList', array( $user, &$sk, &$list ) ) ) {
+                       $new = $context->getRequest()->getBool( 'enhanced', $user->getOption( 'usenewrc' ) );
+                       return $new ? new EnhancedChangesList( $context ) : new OldChangesList( $context );
+               } else {
+                       return $list;
+               }
+       }
+
+       /**
+        * Sets the list to use a "<li class='watchlist-(namespace)-(page)'>" tag
+        * @param $value Boolean
+        */
+       public function setWatchlistDivs( $value = true ) {
+               $this->watchlist = $value;
+       }
+
+       /**
+        * As we use the same small set of messages in various methods and that
+        * they are called often, we call them once and save them in $this->message
+        */
+       private function preCacheMessages() {
+               if ( !isset( $this->message ) ) {
+                       foreach ( array(
+                               'cur', 'diff', 'hist', 'enhancedrc-history', 'last', 'blocklink', 'history',
+                               'semicolon-separator', 'pipe-separator' ) as $msg
+                       ) {
+                               $this->message[$msg] = $this->msg( $msg )->escaped();
+                       }
+               }
+       }
+
+       /**
+        * Returns the appropriate flags for new page, minor change and patrolling
+        * @param array $flags Associative array of 'flag' => Bool
+        * @param string $nothing to use for empty space
+        * @return String
+        */
+       public function recentChangesFlags( $flags, $nothing = '&#160;' ) {
+               global $wgRecentChangesFlags;
+               $f = '';
+               foreach ( array_keys( $wgRecentChangesFlags ) as $flag ) {
+                       $f .= isset( $flags[$flag] ) && $flags[$flag]
+                               ? self::flag( $flag )
+                               : $nothing;
+               }
+               return $f;
+       }
+
+       /**
+        * Provide the "<abbr>" element appropriate to a given abbreviated flag,
+        * namely the flag indicating a new page, a minor edit, a bot edit, or an
+        * unpatrolled edit.  By default in English it will contain "N", "m", "b",
+        * "!" respectively, plus it will have an appropriate title and class.
+        *
+        * @param string $flag One key of $wgRecentChangesFlags
+        * @return String: Raw HTML
+        */
+       public static function flag( $flag ) {
+               static $flagInfos = null;
+               if ( is_null( $flagInfos ) ) {
+                       global $wgRecentChangesFlags;
+                       $flagInfos = array();
+                       foreach ( $wgRecentChangesFlags as $key => $value ) {
+                               $flagInfos[$key]['letter'] = wfMessage( $value['letter'] )->escaped();
+                               $flagInfos[$key]['title'] = wfMessage( $value['title'] )->escaped();
+                               // Allow customized class name, fall back to flag name
+                               $flagInfos[$key]['class'] = Sanitizer::escapeClass(
+                                       isset( $value['class'] ) ? $value['class'] : $key );
+                       }
+               }
+
+               // Inconsistent naming, bleh, kepted for b/c
+               $map = array(
+                       'minoredit' => 'minor',
+                       'botedit' => 'bot',
+               );
+               if ( isset( $map[$flag] ) ) {
+                       $flag = $map[$flag];
+               }
+
+               return "<abbr class='" . $flagInfos[$flag]['class'] . "' title='" . $flagInfos[$flag]['title'] . "'>" .
+                       $flagInfos[$flag]['letter'] .
+                       '</abbr>';
+       }
+
+       /**
+        * Returns text for the start of the tabular part of RC
+        * @return String
+        */
+       public function beginRecentChangesList() {
+               $this->rc_cache = array();
+               $this->rcMoveIndex = 0;
+               $this->rcCacheIndex = 0;
+               $this->lastdate = '';
+               $this->rclistOpen = false;
+               $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
+               return '';
+       }
+
+       /**
+        * Show formatted char difference
+        * @param $old Integer: bytes
+        * @param $new Integer: bytes
+        * @param $context IContextSource context to use
+        * @return String
+        */
+       public static function showCharacterDifference( $old, $new, IContextSource $context = null ) {
+               global $wgRCChangedSizeThreshold, $wgMiserMode;
+
+               if ( !$context ) {
+                       $context = RequestContext::getMain();
+               }
+
+               $new = (int)$new;
+               $old = (int)$old;
+               $szdiff = $new - $old;
+
+               $lang = $context->getLanguage();
+               $code = $lang->getCode();
+               static $fastCharDiff = array();
+               if ( !isset( $fastCharDiff[$code] ) ) {
+                       $fastCharDiff[$code] = $wgMiserMode || $context->msg( 'rc-change-size' )->plain() === '$1';
+               }
+
+               $formattedSize = $lang->formatNum( $szdiff );
+
+               if ( !$fastCharDiff[$code] ) {
+                       $formattedSize = $context->msg( 'rc-change-size', $formattedSize )->text();
+               }
+
+               if ( abs( $szdiff ) > abs( $wgRCChangedSizeThreshold ) ) {
+                       $tag = 'strong';
+               } else {
+                       $tag = 'span';
+               }
+
+               if ( $szdiff === 0 ) {
+                       $formattedSizeClass = 'mw-plusminus-null';
+               }
+               if ( $szdiff > 0 ) {
+                       $formattedSize = '+' . $formattedSize;
+                       $formattedSizeClass = 'mw-plusminus-pos';
+               }
+               if ( $szdiff < 0 ) {
+                       $formattedSizeClass = 'mw-plusminus-neg';
+               }
+
+               $formattedTotalSize = $context->msg( 'rc-change-size-new' )->numParams( $new )->text();
+
+               return Html::element( $tag,
+                       array( 'dir' => 'ltr', 'class' => $formattedSizeClass, 'title' => $formattedTotalSize ),
+                       $context->msg( 'parentheses', $formattedSize )->plain() ) . $lang->getDirMark();
+       }
+
+       /**
+        * Format the character difference of one or several changes.
+        *
+        * @param $old RecentChange
+        * @param $new RecentChange last change to use, if not provided, $old will be used
+        * @return string HTML fragment
+        */
+       public function formatCharacterDifference( RecentChange $old, RecentChange $new = null ) {
+               $oldlen = $old->mAttribs['rc_old_len'];
+
+               if ( $new ) {
+                       $newlen = $new->mAttribs['rc_new_len'];
+               } else {
+                       $newlen = $old->mAttribs['rc_new_len'];
+               }
+
+               if ( $oldlen === null || $newlen === null ) {
+                       return '';
+               }
+
+               return self::showCharacterDifference( $oldlen, $newlen, $this->getContext() );
+       }
+
+       /**
+        * Returns text for the end of RC
+        * @return String
+        */
+       public function endRecentChangesList() {
+               if ( $this->rclistOpen ) {
+                       return "</ul>\n";
+               } else {
+                       return '';
+               }
+       }
+
+       /**
+        * @param string $s HTML to update
+        * @param $rc_timestamp mixed
+        */
+       public function insertDateHeader( &$s, $rc_timestamp ) {
+               # Make date header if necessary
+               $date = $this->getLanguage()->userDate( $rc_timestamp, $this->getUser() );
+               if ( $date != $this->lastdate ) {
+                       if ( $this->lastdate != '' ) {
+                               $s .= "</ul>\n";
+                       }
+                       $s .= Xml::element( 'h4', null, $date ) . "\n<ul class=\"special\">";
+                       $this->lastdate = $date;
+                       $this->rclistOpen = true;
+               }
+       }
+
+       /**
+        * @param string $s HTML to update
+        * @param $title Title
+        * @param $logtype string
+        */
+       public function insertLog( &$s, $title, $logtype ) {
+               $page = new LogPage( $logtype );
+               $logname = $page->getName()->escaped();
+               $s .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $title, $logname ) )->escaped();
+       }
+
+       /**
+        * @param string $s HTML to update
+        * @param $rc RecentChange
+        * @param $unpatrolled
+        */
+       public function insertDiffHist( &$s, &$rc, $unpatrolled ) {
+               # Diff link
+               if ( $rc->mAttribs['rc_type'] == RC_NEW || $rc->mAttribs['rc_type'] == RC_LOG ) {
+                       $diffLink = $this->message['diff'];
+               } elseif ( !self::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
+                       $diffLink = $this->message['diff'];
+               } else {
+                       $query = array(
+                               'curid' => $rc->mAttribs['rc_cur_id'],
+                               'diff' => $rc->mAttribs['rc_this_oldid'],
+                               'oldid' => $rc->mAttribs['rc_last_oldid']
+                       );
+
+                       $diffLink = Linker::linkKnown(
+                               $rc->getTitle(),
+                               $this->message['diff'],
+                               array( 'tabindex' => $rc->counter ),
+                               $query
+                       );
+               }
+               $diffhist = $diffLink . $this->message['pipe-separator'];
+               # History link
+               $diffhist .= Linker::linkKnown(
+                       $rc->getTitle(),
+                       $this->message['hist'],
+                       array(),
+                       array(
+                               'curid' => $rc->mAttribs['rc_cur_id'],
+                               'action' => 'history'
+                       )
+               );
+               $s .= $this->msg( 'parentheses' )->rawParams( $diffhist )->escaped() . ' <span class="mw-changeslist-separator">. .</span> ';
+       }
+
+       /**
+        * @param string $s HTML to update
+        * @param $rc RecentChange
+        * @param $unpatrolled
+        * @param $watched
+        */
+       public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
+               $params = array();
+
+               $articlelink = Linker::linkKnown(
+                       $rc->getTitle(),
+                       null,
+                       array( 'class' => 'mw-changeslist-title' ),
+                       $params
+               );
+               if ( $this->isDeleted( $rc, Revision::DELETED_TEXT ) ) {
+                       $articlelink = '<span class="history-deleted">' . $articlelink . '</span>';
+               }
+               # To allow for boldening pages watched by this user
+               $articlelink = "<span class=\"mw-title\">{$articlelink}</span>";
+               # RTL/LTR marker
+               $articlelink .= $this->getLanguage()->getDirMark();
+
+               wfRunHooks( 'ChangesListInsertArticleLink',
+                       array( &$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched ) );
+
+               $s .= " $articlelink";
+       }
+
+       /**
+        * Get the timestamp from $rc formatted with current user's settings
+        * and a separator
+        *
+        * @param $rc RecentChange
+        * @return string HTML fragment
+        */
+       public function getTimestamp( $rc ) {
+               return $this->message['semicolon-separator'] . '<span class="mw-changeslist-date">' .
+                       $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() ) . '</span> <span class="mw-changeslist-separator">. .</span> ';
+       }
+
+       /**
+        * Insert time timestamp string from $rc into $s
+        *
+        * @param string $s HTML to update
+        * @param $rc RecentChange
+        */
+       public function insertTimestamp( &$s, $rc ) {
+               $s .= $this->getTimestamp( $rc );
+       }
+
+       /**
+        * Insert links to user page, user talk page and eventually a blocking link
+        *
+        * @param &$s String HTML to update
+        * @param &$rc RecentChange
+        */
+       public function insertUserRelatedLinks( &$s, &$rc ) {
+               if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
+                       $s .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
+               } else {
+                       $s .= $this->getLanguage()->getDirMark() . Linker::userLink( $rc->mAttribs['rc_user'],
+                               $rc->mAttribs['rc_user_text'] );
+                       $s .= Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+               }
+       }
+
+       /**
+        * Insert a formatted action
+        *
+        * @param $rc RecentChange
+        * @return string
+        */
+       public function insertLogEntry( $rc ) {
+               $formatter = LogFormatter::newFromRow( $rc->mAttribs );
+               $formatter->setContext( $this->getContext() );
+               $formatter->setShowUserToolLinks( true );
+               $mark = $this->getLanguage()->getDirMark();
+               return $formatter->getActionText() . " $mark" . $formatter->getComment();
+       }
+
+       /**
+        * Insert a formatted comment
+        * @param $rc RecentChange
+        * @return string
+        */
+       public function insertComment( $rc ) {
+               if ( $rc->mAttribs['rc_type'] != RC_MOVE && $rc->mAttribs['rc_type'] != RC_MOVE_OVER_REDIRECT ) {
+                       if ( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) {
+                               return ' <span class="history-deleted">' . $this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
+                       } else {
+                               return Linker::commentBlock( $rc->mAttribs['rc_comment'], $rc->getTitle() );
+                       }
+               }
+               return '';
+       }
+
+       /**
+        * Check whether to enable recent changes patrol features
+        *
+        * @deprecated since 1.22
+        * @return Boolean
+        */
+       public static function usePatrol() {
+               global $wgUser;
+
+               wfDeprecated( __METHOD__, '1.22' );
+
+               return $wgUser->useRCPatrol();
+       }
+
+       /**
+        * Returns the string which indicates the number of watching users
+        * @return string
+        */
+       protected function numberofWatchingusers( $count ) {
+               static $cache = array();
+               if ( $count > 0 ) {
+                       if ( !isset( $cache[$count] ) ) {
+                               $cache[$count] = $this->msg( 'number_of_watching_users_RCview' )->numParams( $count )->escaped();
+                       }
+                       return $cache[$count];
+               } else {
+                       return '';
+               }
+       }
+
+       /**
+        * Determine if said field of a revision is hidden
+        * @param $rc RCCacheEntry
+        * @param $field Integer: one of DELETED_* bitfield constants
+        * @return Boolean
+        */
+       public static function isDeleted( $rc, $field ) {
+               return ( $rc->mAttribs['rc_deleted'] & $field ) == $field;
+       }
+
+       /**
+        * Determine if the current user is allowed to view a particular
+        * field of this revision, if it's marked as deleted.
+        * @param $rc RCCacheEntry
+        * @param $field Integer
+        * @param $user User object to check, or null to use $wgUser
+        * @return Boolean
+        */
+       public static function userCan( $rc, $field, User $user = null ) {
+               if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
+                       return LogEventsList::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
+               } else {
+                       return Revision::userCanBitfield( $rc->mAttribs['rc_deleted'], $field, $user );
+               }
+       }
+
+       /**
+        * @param $link string
+        * @param $watched bool
+        * @return string
+        */
+       protected function maybeWatchedLink( $link, $watched = false ) {
+               if ( $watched ) {
+                       return '<strong class="mw-watched">' . $link . '</strong>';
+               } else {
+                       return '<span class="mw-rc-unwatched">' . $link . '</span>';
+               }
+       }
+
+       /** Inserts a rollback link
+        *
+        * @param $s string
+        * @param $rc RecentChange
+        */
+       public function insertRollback( &$s, &$rc ) {
+               if ( $rc->mAttribs['rc_type'] == RC_EDIT && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
+                       $page = $rc->getTitle();
+                       /** Check for rollback and edit permissions, disallow special pages, and only
+                         * show a link on the top-most revision */
+                       if ( $this->getUser()->isAllowed( 'rollback' ) && $rc->mAttribs['page_latest'] == $rc->mAttribs['rc_this_oldid'] )
+                       {
+                               $rev = new Revision( array(
+                                       'title' => $page,
+                                       'id' => $rc->mAttribs['rc_this_oldid'],
+                                       'user' => $rc->mAttribs['rc_user'],
+                                       'user_text' => $rc->mAttribs['rc_user_text'],
+                                       'deleted' => $rc->mAttribs['rc_deleted']
+                               ) );
+                               $s .= ' ' . Linker::generateRollback( $rev, $this->getContext() );
+                       }
+               }
+       }
+
+       /**
+        * @param $s string
+        * @param $rc RecentChange
+        * @param $classes
+        */
+       public function insertTags( &$s, &$rc, &$classes ) {
+               if ( empty( $rc->mAttribs['ts_tags'] ) ) {
+                       return;
+               }
+
+               list( $tagSummary, $newClasses ) = ChangeTags::formatSummaryRow( $rc->mAttribs['ts_tags'], 'changeslist' );
+               $classes = array_merge( $classes, $newClasses );
+               $s .= ' ' . $tagSummary;
+       }
+
+       public function insertExtra( &$s, &$rc, &$classes ) {
+               // Empty, used for subclasses to add anything special.
+       }
+
+       protected function showAsUnpatrolled( RecentChange $rc ) {
+               $unpatrolled = false;
+               if ( !$rc->mAttribs['rc_patrolled'] ) {
+                       if ( $this->getUser()->useRCPatrol() ) {
+                               $unpatrolled = true;
+                       } elseif ( $this->getUser()->useNPPatrol() && $rc->mAttribs['rc_type'] == RC_NEW ) {
+                               $unpatrolled = true;
+                       }
+               }
+               return $unpatrolled;
+       }
+}
diff --git a/includes/changes/EnhancedChangesList.php b/includes/changes/EnhancedChangesList.php
new file mode 100644 (file)
index 0000000..433adb3
--- /dev/null
@@ -0,0 +1,662 @@
+<?php
+/**
+ * Generates a list of changes using an Enhanced system (uses javascript).
+ *
+ * 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
+ */
+
+class EnhancedChangesList extends ChangesList {
+
+       protected $rc_cache;
+
+       /**
+        * Add the JavaScript file for enhanced changeslist
+        * @return String
+        */
+       public function beginRecentChangesList() {
+               $this->rc_cache = array();
+               $this->rcMoveIndex = 0;
+               $this->rcCacheIndex = 0;
+               $this->lastdate = '';
+               $this->rclistOpen = false;
+               $this->getOutput()->addModuleStyles( array(
+                       'mediawiki.special.changeslist',
+                       'mediawiki.special.changeslist.enhanced',
+               ) );
+               $this->getOutput()->addModules( array(
+                       'jquery.makeCollapsible',
+                       'mediawiki.icon',
+               ) );
+               return '';
+       }
+       /**
+        * Format a line for enhanced recentchange (aka with javascript and block of lines).
+        *
+        * @param $baseRC RecentChange
+        * @param $watched bool
+        *
+        * @return string
+        */
+       public function recentChangesLine( &$baseRC, $watched = false ) {
+               wfProfileIn( __METHOD__ );
+
+               # Create a specialised object
+               $rc = RCCacheEntry::newFromParent( $baseRC );
+
+               $curIdEq = array( 'curid' => $rc->mAttribs['rc_cur_id'] );
+
+               # If it's a new day, add the headline and flush the cache
+               $date = $this->getLanguage()->userDate( $rc->mAttribs['rc_timestamp'], $this->getUser() );
+               $ret = '';
+               if ( $date != $this->lastdate ) {
+                       # Process current cache
+                       $ret = $this->recentChangesBlock();
+                       $this->rc_cache = array();
+                       $ret .= Xml::element( 'h4', null, $date ) . "\n";
+                       $this->lastdate = $date;
+               }
+
+               # Should patrol-related stuff be shown?
+               $rc->unpatrolled = $this->showAsUnpatrolled( $rc );
+
+               $showdifflinks = true;
+               # Make article link
+               $type = $rc->mAttribs['rc_type'];
+               $logType = $rc->mAttribs['rc_log_type'];
+               // Page moves, very old style, not supported anymore
+               if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
+               // New unpatrolled pages
+               } elseif ( $rc->unpatrolled && $type == RC_NEW ) {
+                       $clink = Linker::linkKnown( $rc->getTitle() );
+               // Log entries
+               } elseif ( $type == RC_LOG ) {
+                       if ( $logType ) {
+                               $logtitle = SpecialPage::getTitleFor( 'Log', $logType );
+                               $logpage = new LogPage( $logType );
+                               $logname = $logpage->getName()->escaped();
+                               $clink = $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logtitle, $logname ) )->escaped();
+                       } else {
+                               $clink = Linker::link( $rc->getTitle() );
+                       }
+                       $watched = false;
+               // Log entries (old format) and special pages
+               } elseif ( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
+                       wfDebug( "Unexpected special page in recentchanges\n" );
+                       $clink = '';
+               // Edits
+               } else {
+                       $clink = Linker::linkKnown( $rc->getTitle() );
+               }
+
+               # Don't show unusable diff links
+               if ( !ChangesList::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
+                       $showdifflinks = false;
+               }
+
+               $time = $this->getLanguage()->userTime( $rc->mAttribs['rc_timestamp'], $this->getUser() );
+               $rc->watched = $watched;
+               $rc->link = $clink;
+               $rc->timestamp = $time;
+               $rc->numberofWatchingusers = $baseRC->numberofWatchingusers;
+
+               # Make "cur" and "diff" links.  Do not use link(), it is too slow if
+               # called too many times (50% of CPU time on RecentChanges!).
+               $thisOldid = $rc->mAttribs['rc_this_oldid'];
+               $lastOldid = $rc->mAttribs['rc_last_oldid'];
+
+               $querycur = $curIdEq + array( 'diff' => '0', 'oldid' => $thisOldid );
+               $querydiff = $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid );
+
+               if ( !$showdifflinks ) {
+                       $curLink = $this->message['cur'];
+                       $diffLink = $this->message['diff'];
+               } elseif ( in_array( $type, array( RC_NEW, RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
+                       if ( $type != RC_NEW ) {
+                               $curLink = $this->message['cur'];
+                       } else {
+                               $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
+                               $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
+                       }
+                       $diffLink = $this->message['diff'];
+               } else {
+                       $diffUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querydiff ) );
+                       $curUrl = htmlspecialchars( $rc->getTitle()->getLinkURL( $querycur ) );
+                       $diffLink = "<a href=\"$diffUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['diff']}</a>";
+                       $curLink = "<a href=\"$curUrl\" tabindex=\"{$baseRC->counter}\">{$this->message['cur']}</a>";
+               }
+
+               # Make "last" link
+               if ( !$showdifflinks || !$lastOldid ) {
+                       $lastLink = $this->message['last'];
+               } elseif ( in_array( $type, array( RC_LOG, RC_MOVE, RC_MOVE_OVER_REDIRECT ) ) ) {
+                       $lastLink = $this->message['last'];
+               } else {
+                       $lastLink = Linker::linkKnown( $rc->getTitle(), $this->message['last'],
+                               array(), $curIdEq + array( 'diff' => $thisOldid, 'oldid' => $lastOldid ) );
+               }
+
+               # Make user links
+               if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
+                       $rc->userlink = ' <span class="history-deleted">' . $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
+               } else {
+                       $rc->userlink = Linker::userLink( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+                       $rc->usertalklink = Linker::userToolLinks( $rc->mAttribs['rc_user'], $rc->mAttribs['rc_user_text'] );
+               }
+
+               $rc->lastlink = $lastLink;
+               $rc->curlink = $curLink;
+               $rc->difflink = $diffLink;
+
+               # Put accumulated information into the cache, for later display
+               # Page moves go on their own line
+               $title = $rc->getTitle();
+               $secureName = $title->getPrefixedDBkey();
+               if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
+                       # Use an @ character to prevent collision with page names
+                       $this->rc_cache['@@' . ( $this->rcMoveIndex++ )] = array( $rc );
+               } else {
+                       # Logs are grouped by type
+                       if ( $type == RC_LOG ) {
+                               $secureName = SpecialPage::getTitleFor( 'Log', $logType )->getPrefixedDBkey();
+                       }
+                       if ( !isset( $this->rc_cache[$secureName] ) ) {
+                               $this->rc_cache[$secureName] = array();
+                       }
+
+                       array_push( $this->rc_cache[$secureName], $rc );
+               }
+
+               wfProfileOut( __METHOD__ );
+
+               return $ret;
+       }
+
+       /**
+        * Enhanced RC group
+        * @return string
+        */
+       protected function recentChangesBlockGroup( $block ) {
+               global $wgRCShowChangedSize;
+
+               wfProfileIn( __METHOD__ );
+
+               # Add the namespace and title of the block as part of the class
+               $classes = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' );
+               if ( $block[0]->mAttribs['rc_log_type'] ) {
+                       # Log entry
+                       $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
+                                       . $block[0]->mAttribs['rc_log_type'] . '-' . $block[0]->mAttribs['rc_title'] );
+               } else {
+                       $classes[] = 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' );
+
+               # Collate list of users
+               $userlinks = array();
+               # Other properties
+               $unpatrolled = false;
+               $isnew = false;
+               $allBots = true;
+               $allMinors = true;
+               $curId = $currentRevision = 0;
+               # Some catalyst variables...
+               $namehidden = true;
+               $allLogs = true;
+               foreach ( $block as $rcObj ) {
+                       $oldid = $rcObj->mAttribs['rc_last_oldid'];
+                       if ( $rcObj->mAttribs['rc_type'] == RC_NEW ) {
+                               $isnew = true;
+                       }
+                       // If all log actions to this page were hidden, then don't
+                       // give the name of the affected page for this block!
+                       if ( !$this->isDeleted( $rcObj, LogPage::DELETED_ACTION ) ) {
+                               $namehidden = false;
+                       }
+                       $u = $rcObj->userlink;
+                       if ( !isset( $userlinks[$u] ) ) {
+                               $userlinks[$u] = 0;
+                       }
+                       if ( $rcObj->unpatrolled ) {
+                               $unpatrolled = true;
+                       }
+                       if ( $rcObj->mAttribs['rc_type'] != RC_LOG ) {
+                               $allLogs = false;
+                       }
+                       # Get the latest entry with a page_id and oldid
+                       # since logs may not have these.
+                       if ( !$curId && $rcObj->mAttribs['rc_cur_id'] ) {
+                               $curId = $rcObj->mAttribs['rc_cur_id'];
+                       }
+                       if ( !$currentRevision && $rcObj->mAttribs['rc_this_oldid'] ) {
+                               $currentRevision = $rcObj->mAttribs['rc_this_oldid'];
+                       }
+
+                       if ( !$rcObj->mAttribs['rc_bot'] ) {
+                               $allBots = false;
+                       }
+                       if ( !$rcObj->mAttribs['rc_minor'] ) {
+                               $allMinors = false;
+                       }
+
+                       $userlinks[$u]++;
+               }
+
+               # Sort the list and convert to text
+               krsort( $userlinks );
+               asort( $userlinks );
+               $users = array();
+               foreach ( $userlinks as $userlink => $count ) {
+                       $text = $userlink;
+                       $text .= $this->getLanguage()->getDirMark();
+                       if ( $count > 1 ) {
+                               $text .= ' ' . $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->formatNum( $count ) . '×' )->escaped();
+                       }
+                       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( array(
+                       'newpage' => $isnew, # show, when one have this flag
+                       'minor' => $allMinors, # show only, when all have this flag
+                       'unpatrolled' => $unpatrolled, # show, when one have this flag
+                       'bot' => $allBots, # show only, when all have this flag
+               ) );
+
+               # Timestamp
+               $r .= '&#160;' . $block[0]->timestamp . '&#160;</td><td>';
+
+               # Article link
+               if ( $namehidden ) {
+                       $r .= ' <span class="history-deleted">' . $this->msg( 'rev-deleted-event' )->escaped() . '</span>';
+               } elseif ( $allLogs ) {
+                       $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
+               } else {
+                       $this->insertArticleLink( $r, $block[0], $block[0]->unpatrolled, $block[0]->watched );
+               }
+
+               $r .= $this->getLanguage()->getDirMark();
+
+               $queryParams['curid'] = $curId;
+
+               # Changes message
+               static $nchanges = array();
+               static $sinceLastVisitMsg = array();
+
+               $n = count( $block );
+               if ( !isset( $nchanges[$n] ) ) {
+                       $nchanges[$n] = $this->msg( 'nchanges' )->numParams( $n )->escaped();
+               }
+
+               $sinceLast = 0;
+               $unvisitedOldid = null;
+               foreach ( $block as $rcObj ) {
+                       // Same logic as below inside main foreach
+                       if ( $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched ) {
+                               $sinceLast++;
+                               $unvisitedOldid = $rcObj->mAttribs['rc_last_oldid'];
+                       }
+               }
+               if ( !isset( $sinceLastVisitMsg[$sinceLast] ) ) {
+                       $sinceLastVisitMsg[$sinceLast] =
+                               $this->msg( 'enhancedrc-since-last-visit' )->numParams( $sinceLast )->escaped();
+               }
+
+               # Total change link
+               $r .= ' ';
+               $logtext = '';
+               if ( !$allLogs ) {
+                       if ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
+                               $logtext .= $nchanges[$n];
+                       } elseif ( $isnew ) {
+                               $logtext .= $nchanges[$n];
+                       } else {
+                               $logtext .= Linker::link(
+                                       $block[0]->getTitle(),
+                                       $nchanges[$n],
+                                       array(),
+                                       $queryParams + array(
+                                               'diff' => $currentRevision,
+                                               'oldid' => $oldid,
+                                       ),
+                                       array( 'known', 'noclasses' )
+                               );
+                               if ( $sinceLast > 0 && $sinceLast < $n ) {
+                                       $logtext .= $this->message['pipe-separator'] . Linker::link(
+                                               $block[0]->getTitle(),
+                                               $sinceLastVisitMsg[$sinceLast],
+                                               array(),
+                                               $queryParams + array(
+                                                       'diff' => $currentRevision,
+                                                       'oldid' => $unvisitedOldid,
+                                               ),
+                                               array( 'known', 'noclasses' )
+                                       );
+                               }
+                       }
+               }
+
+               # History
+               if ( $allLogs ) {
+                       // don't show history link for logs
+               } elseif ( $namehidden || !$block[0]->getTitle()->exists() ) {
+                       $logtext .= $this->message['pipe-separator'] . $this->message['enhancedrc-history'];
+               } else {
+                       $params = $queryParams;
+                       $params['action'] = 'history';
+
+                       $logtext .= $this->message['pipe-separator'] .
+                               Linker::linkKnown(
+                                       $block[0]->getTitle(),
+                                       $this->message['enhancedrc-history'],
+                                       array(),
+                                       $params
+                               );
+               }
+
+               if ( $logtext !== '' ) {
+                       $r .= $this->msg( 'parentheses' )->rawParams( $logtext )->escaped();
+               }
+
+               $r .= ' <span class="mw-changeslist-separator">. .</span> ';
+
+               # Character difference (does not apply if only log items)
+               if ( $wgRCShowChangedSize && !$allLogs ) {
+                       $last = 0;
+                       $first = count( $block ) - 1;
+                       # Some events (like logs) have an "empty" size, so we need to skip those...
+                       while ( $last < $first && $block[$last]->mAttribs['rc_new_len'] === null ) {
+                               $last++;
+                       }
+                       while ( $first > $last && $block[$first]->mAttribs['rc_old_len'] === null ) {
+                               $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 );
+
+               # Sub-entries
+               foreach ( $block as $rcObj ) {
+                       # Classes to apply -- TODO implement
+                       $classes = array();
+                       $type = $rcObj->mAttribs['rc_type'];
+
+                       $trClass = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
+                               ? ' class="mw-enhanced-watched"' : '';
+
+                       $r .= '<tr' . $trClass . '><td></td><td class="mw-enhanced-rc">';
+                       $r .= $this->recentChangesFlags( array(
+                               'newpage' => $type == RC_NEW,
+                               'minor' => $rcObj->mAttribs['rc_minor'],
+                               'unpatrolled' => $rcObj->unpatrolled,
+                               'bot' => $rcObj->mAttribs['rc_bot'],
+                       ) );
+                       $r .= '&#160;</td><td class="mw-enhanced-rc-nested"><span class="mw-enhanced-rc-time">';
+
+                       $params = $queryParams;
+
+                       if ( $rcObj->mAttribs['rc_this_oldid'] != 0 ) {
+                               $params['oldid'] = $rcObj->mAttribs['rc_this_oldid'];
+                       }
+
+                       # Log timestamp
+                       if ( $type == RC_LOG ) {
+                               $link = $rcObj->timestamp;
+                       # Revision link
+                       } elseif ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
+                               $link = '<span class="history-deleted">' . $rcObj->timestamp . '</span> ';
+                       } else {
+
+                               $link = Linker::linkKnown(
+                                               $rcObj->getTitle(),
+                                               $rcObj->timestamp,
+                                               array(),
+                                               $params
+                                       );
+                               if ( $this->isDeleted( $rcObj, Revision::DELETED_TEXT ) ) {
+                                       $link = '<span class="history-deleted">' . $link . '</span> ';
+                               }
+                       }
+                       $r .= $link . '</span>';
+
+                       if ( !$type == RC_LOG || $type == RC_NEW ) {
+                               $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->curlink . $this->message['pipe-separator'] . $rcObj->lastlink )->escaped();
+                       }
+                       $r .= ' <span class="mw-changeslist-separator">. .</span> ';
+
+                       # Character diff
+                       if ( $wgRCShowChangedSize ) {
+                               $cd = $this->formatCharacterDifference( $rcObj );
+                               if ( $cd !== '' ) {
+                                       $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
+                               }
+                       }
+
+                       if ( $rcObj->mAttribs['rc_type'] == RC_LOG ) {
+                               $r .= $this->insertLogEntry( $rcObj );
+                       } else {
+                               # User links
+                               $r .= $rcObj->userlink;
+                               $r .= $rcObj->usertalklink;
+                               $r .= $this->insertComment( $rcObj );
+                       }
+
+                       # Rollback
+                       $this->insertRollback( $r, $rcObj );
+                       # Tags
+                       $this->insertTags( $r, $rcObj, $classes );
+
+                       $r .= "</td></tr>\n";
+               }
+               $r .= "</table>\n";
+
+               $this->rcCacheIndex++;
+
+               wfProfileOut( __METHOD__ );
+
+               return $r;
+       }
+
+       /**
+        * Generate HTML for an arrow or placeholder graphic
+        * @param string $dir one of '', 'd', 'l', 'r'
+        * @param string $alt text
+        * @param string $title text
+        * @return String: HTML "<img>" tag
+        */
+       protected function arrow( $dir, $alt = '', $title = '' ) {
+               global $wgStylePath;
+               $encUrl = htmlspecialchars( $wgStylePath . '/common/images/Arr_' . $dir . '.png' );
+               $encAlt = htmlspecialchars( $alt );
+               $encTitle = htmlspecialchars( $title );
+               return "<img src=\"$encUrl\" width=\"12\" height=\"12\" alt=\"$encAlt\" title=\"$encTitle\" />";
+       }
+
+       /**
+        * Generate HTML for a right- or left-facing arrow,
+        * depending on language direction.
+        * @return String: HTML "<img>" tag
+        */
+       protected function sideArrow() {
+               $dir = $this->getLanguage()->isRTL() ? 'l' : 'r';
+               return $this->arrow( $dir, '+', $this->msg( 'rc-enhanced-expand' )->text() );
+       }
+
+       /**
+        * Generate HTML for a down-facing arrow
+        * depending on language direction.
+        * @return String: HTML "<img>" tag
+        */
+       protected function downArrow() {
+               return $this->arrow( 'd', '-', $this->msg( 'rc-enhanced-hide' )->text() );
+       }
+
+       /**
+        * Generate HTML for a spacer image
+        * @return String: HTML "<img>" tag
+        */
+       protected function spacerArrow() {
+               return $this->arrow( '', codepointToUtf8( 0xa0 ) ); // non-breaking space
+       }
+
+       /**
+        * Enhanced RC ungrouped line.
+        *
+        * @param $rcObj RecentChange
+        * @return String: a HTML formatted line (generated using $r)
+        */
+       protected function recentChangesBlockLine( $rcObj ) {
+               global $wgRCShowChangedSize;
+
+               wfProfileIn( __METHOD__ );
+               $query['curid'] = $rcObj->mAttribs['rc_cur_id'];
+
+               $type = $rcObj->mAttribs['rc_type'];
+               $logType = $rcObj->mAttribs['rc_log_type'];
+               $classes = array( 'mw-enhanced-rc' );
+               if ( $logType ) {
+                       # Log entry
+                       $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
+                                       . $logType . '-' . $rcObj->mAttribs['rc_title'] );
+               } else {
+                       $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns' .
+                                       $rcObj->mAttribs['rc_namespace'] . '-' . $rcObj->mAttribs['rc_title'] );
+               }
+               $classes[] = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
+                       ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
+               $r = Html::openElement( 'table', array( 'class' => $classes ) ) .
+                       Html::openElement( 'tr' );
+
+               $r .= '<td class="mw-enhanced-rc"><span class="mw-enhancedchanges-arrow-space"></span>';
+               # Flag and Timestamp
+               if ( $type == RC_MOVE || $type == RC_MOVE_OVER_REDIRECT ) {
+                       $r .= $this->recentChangesFlags( array() ); // no flags, but need the placeholders
+               } else {
+                       $r .= $this->recentChangesFlags( array(
+                               'newpage' => $type == RC_NEW,
+                               'minor' => $rcObj->mAttribs['rc_minor'],
+                               'unpatrolled' => $rcObj->unpatrolled,
+                               'bot' => $rcObj->mAttribs['rc_bot'],
+                       ) );
+               }
+               $r .= '&#160;' . $rcObj->timestamp . '&#160;</td><td>';
+               # Article or log link
+               if ( $logType ) {
+                       $logPage = new LogPage( $logType );
+                       $logTitle = SpecialPage::getTitleFor( 'Log', $logType );
+                       $logName = $logPage->getName()->escaped();
+                       $r .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $logTitle, $logName ) )->escaped();
+               } else {
+                       $this->insertArticleLink( $r, $rcObj, $rcObj->unpatrolled, $rcObj->watched );
+               }
+               # Diff and hist links
+               if ( $type != RC_LOG ) {
+                       $query['action'] = 'history';
+                       $r .= ' ' . $this->msg( 'parentheses' )->rawParams( $rcObj->difflink . $this->message['pipe-separator'] . Linker::linkKnown(
+                               $rcObj->getTitle(),
+                               $this->message['hist'],
+                               array(),
+                               $query
+                       ) )->escaped();
+               }
+               $r .= ' <span class="mw-changeslist-separator">. .</span> ';
+               # Character diff
+               if ( $wgRCShowChangedSize ) {
+                       $cd = $this->formatCharacterDifference( $rcObj );
+                       if ( $cd !== '' ) {
+                               $r .= $cd . ' <span class="mw-changeslist-separator">. .</span> ';
+                       }
+               }
+
+               if ( $type == RC_LOG ) {
+                       $r .= $this->insertLogEntry( $rcObj );
+               } else {
+                       $r .= ' ' . $rcObj->userlink . $rcObj->usertalklink;
+                       $r .= $this->insertComment( $rcObj );
+                       $this->insertRollback( $r, $rcObj );
+               }
+
+               # Tags
+               $this->insertTags( $r, $rcObj, $classes );
+               # Show how many people are watching this if enabled
+               $r .= $this->numberofWatchingusers( $rcObj->numberofWatchingusers );
+
+               $r .= "</td></tr></table>\n";
+
+               wfProfileOut( __METHOD__ );
+
+               return $r;
+       }
+
+       /**
+        * If enhanced RC is in use, this function takes the previously cached
+        * RC lines, arranges them, and outputs the HTML
+        *
+        * @return string
+        */
+       protected function recentChangesBlock() {
+               if ( count ( $this->rc_cache ) == 0 ) {
+                       return '';
+               }
+
+               wfProfileIn( __METHOD__ );
+
+               $blockOut = '';
+               foreach ( $this->rc_cache as $block ) {
+                       if ( count( $block ) < 2 ) {
+                               $blockOut .= $this->recentChangesBlockLine( array_shift( $block ) );
+                       } else {
+                               $blockOut .= $this->recentChangesBlockGroup( $block );
+                       }
+               }
+
+               wfProfileOut( __METHOD__ );
+
+               return '<div>' . $blockOut . '</div>';
+       }
+
+       /**
+        * Returns text for the end of RC
+        * If enhanced RC is in use, returns pretty much all the text
+        * @return string
+        */
+       public function endRecentChangesList() {
+               return $this->recentChangesBlock() . parent::endRecentChangesList();
+       }
+
+}
diff --git a/includes/changes/OldChangesList.php b/includes/changes/OldChangesList.php
new file mode 100644 (file)
index 0000000..a7fe934
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+/**
+ * Generate a list of changes using the good old system (no javascript).
+ *
+ * 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
+ */
+class OldChangesList extends ChangesList {
+
+       /**
+        * Format a line using the old system (aka without any javascript).
+        *
+        * @param $rc RecentChange, passed by reference
+        * @param bool $watched (default false)
+        * @param int $linenumber (default null)
+        *
+        * @return string|bool
+        */
+       public function recentChangesLine( &$rc, $watched = false, $linenumber = null ) {
+               global $wgRCShowChangedSize;
+               wfProfileIn( __METHOD__ );
+
+               # Should patrol-related stuff be shown?
+               $unpatrolled = $this->showAsUnpatrolled( $rc );
+
+               $dateheader = ''; // $s now contains only <li>...</li>, for hooks' convenience.
+               $this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
+
+               $s = '';
+               $classes = array();
+               // use mw-line-even/mw-line-odd class only if linenumber is given (feature from bug 14468)
+               if ( $linenumber ) {
+                       if ( $linenumber & 1 ) {
+                               $classes[] = 'mw-line-odd';
+                       } else {
+                               $classes[] = 'mw-line-even';
+                       }
+               }
+
+               // Indicate watched status on the line to allow for more
+               // comprehensive styling.
+               $classes[] = $watched && $rc->mAttribs['rc_timestamp'] >= $watched
+                       ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
+
+               // Moved pages (very very old, not supported anymore)
+               if ( $rc->mAttribs['rc_type'] == RC_MOVE || $rc->mAttribs['rc_type'] == RC_MOVE_OVER_REDIRECT ) {
+               // Log entries
+               } elseif ( $rc->mAttribs['rc_log_type'] ) {
+                       $logtitle = SpecialPage::getTitleFor( 'Log', $rc->mAttribs['rc_log_type'] );
+                       $this->insertLog( $s, $logtitle, $rc->mAttribs['rc_log_type'] );
+               // Log entries (old format) or log targets, and special pages
+               } elseif ( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
+                       list( $name, $subpage ) = SpecialPageFactory::resolveAlias( $rc->mAttribs['rc_title'] );
+                       if ( $name == 'Log' ) {
+                               $this->insertLog( $s, $rc->getTitle(), $subpage );
+                       }
+               // Regular entries
+               } else {
+                       $this->insertDiffHist( $s, $rc, $unpatrolled );
+                       # M, N, b and ! (minor, new, bot and unpatrolled)
+                       $s .= $this->recentChangesFlags(
+                               array(
+                                       'newpage' => $rc->mAttribs['rc_type'] == RC_NEW,
+                                       'minor' => $rc->mAttribs['rc_minor'],
+                                       'unpatrolled' => $unpatrolled,
+                                       'bot' => $rc->mAttribs['rc_bot']
+                               ),
+                               ''
+                       );
+                       $this->insertArticleLink( $s, $rc, $unpatrolled, $watched );
+               }
+               # Edit/log timestamp
+               $this->insertTimestamp( $s, $rc );
+               # Bytes added or removed
+               if ( $wgRCShowChangedSize ) {
+                       $cd = $this->formatCharacterDifference( $rc );
+                       if ( $cd !== '' ) {
+                               $s .= $cd . '  <span class="mw-changeslist-separator">. .</span> ';
+                       }
+               }
+
+               if ( $rc->mAttribs['rc_type'] == RC_LOG ) {
+                       $s .= $this->insertLogEntry( $rc );
+               } else {
+                       # User tool links
+                       $this->insertUserRelatedLinks( $s, $rc );
+                       # LTR/RTL direction mark
+                       $s .= $this->getLanguage()->getDirMark();
+                       $s .= $this->insertComment( $rc );
+               }
+
+               # Tags
+               $this->insertTags( $s, $rc, $classes );
+               # Rollback
+               $this->insertRollback( $s, $rc );
+               # For subclasses
+               $this->insertExtra( $s, $rc, $classes );
+
+               # How many users watch this page
+               if ( $rc->numberofWatchingusers > 0 ) {
+                       $s .= ' ' . $this->numberofWatchingusers( $rc->numberofWatchingusers );
+               }
+
+               if ( $this->watchlist ) {
+                       $classes[] = Sanitizer::escapeClass( 'watchlist-' . $rc->mAttribs['rc_namespace'] . '-' . $rc->mAttribs['rc_title'] );
+               }
+
+               if ( !wfRunHooks( 'OldChangesListRecentChangesLine', array( &$this, &$s, $rc, &$classes ) ) ) {
+                       wfProfileOut( __METHOD__ );
+                       return false;
+               }
+
+               wfProfileOut( __METHOD__ );
+               return "$dateheader<li class=\"" . implode( ' ', $classes ) . "\">" . $s . "</li>\n";
+       }
+}
diff --git a/includes/changes/RCCacheEntry.php b/includes/changes/RCCacheEntry.php
new file mode 100644 (file)
index 0000000..9aef3d3
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+class RCCacheEntry extends RecentChange {
+       var $secureName, $link;
+       var $curlink, $difflink, $lastlink, $usertalklink, $versionlink;
+       var $userlink, $timestamp, $watched;
+
+       /**
+        * @param $rc RecentChange
+        * @return RCCacheEntry
+        */
+       static function newFromParent( $rc ) {
+               $rc2 = new RCCacheEntry;
+               $rc2->mAttribs = $rc->mAttribs;
+               $rc2->mExtra = $rc->mExtra;
+               return $rc2;
+       }
+}
diff --git a/includes/changes/RecentChange.php b/includes/changes/RecentChange.php
new file mode 100644 (file)
index 0000000..980bd0a
--- /dev/null
@@ -0,0 +1,846 @@
+<?php
+/**
+ * Utility class for creating and accessing recent change entries.
+ *
+ * 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
+ */
+
+/**
+ * Utility class for creating new RC entries
+ *
+ * mAttribs:
+ *  rc_id           id of the row in the recentchanges table
+ *  rc_timestamp    time the entry was made
+ *  rc_cur_time     timestamp on the cur row
+ *  rc_namespace    namespace #
+ *  rc_title        non-prefixed db key
+ *  rc_type         is new entry, used to determine whether updating is necessary
+ *  rc_minor        is minor
+ *  rc_cur_id       page_id of associated page entry
+ *  rc_user         user id who made the entry
+ *  rc_user_text    user name who made the entry
+ *  rc_comment      edit summary
+ *  rc_this_oldid   rev_id associated with this entry (or zero)
+ *  rc_last_oldid   rev_id associated with the entry before this one (or zero)
+ *  rc_bot          is bot, hidden
+ *  rc_ip           IP address of the user in dotted quad notation
+ *  rc_new          obsolete, use rc_type==RC_NEW
+ *  rc_patrolled    boolean whether or not someone has marked this edit as patrolled
+ *  rc_old_len      integer byte length of the text before the edit
+ *  rc_new_len      the same after the edit
+ *  rc_deleted      partial deletion
+ *  rc_logid        the log_id value for this log entry (or zero)
+ *  rc_log_type     the log type (or null)
+ *  rc_log_action   the log action (or null)
+ *  rc_params       log params
+ *
+ * mExtra:
+ *  prefixedDBkey   prefixed db key, used by external app via msg queue
+ *  lastTimestamp   timestamp of previous entry, used in WHERE clause during update
+ *  lang            the interwiki prefix, automatically set in save()
+ *  oldSize         text size before the change
+ *  newSize         text size after the change
+ *  pageStatus      status of the page: created, deleted, moved, restored, changed
+ *
+ * temporary:       not stored in the database
+ *      notificationtimestamp
+ *      numberofWatchingusers
+ *
+ * @todo document functions and variables
+ */
+class RecentChange {
+       var $mAttribs = array(), $mExtra = array();
+
+       /**
+        * @var Title
+        */
+       var $mTitle = false;
+
+       /**
+        * @var User
+        */
+       private $mPerformer = false;
+
+       /**
+        * @var Title
+        */
+       var $mMovedToTitle = false;
+       var $numberofWatchingusers = 0; # Dummy to prevent error message in SpecialRecentchangeslinked
+       var $notificationtimestamp;
+
+       # Factory methods
+
+       /**
+        * @param $row
+        * @return RecentChange
+        */
+       public static function newFromRow( $row ) {
+               $rc = new RecentChange;
+               $rc->loadFromRow( $row );
+               return $rc;
+       }
+
+       /**
+        * @deprecated in 1.22
+        * @param $row
+        * @return RecentChange
+        */
+       public static function newFromCurRow( $row ) {
+               wfDeprecated( __METHOD__, '1.22' );
+               $rc = new RecentChange;
+               $rc->loadFromCurRow( $row );
+               $rc->notificationtimestamp = false;
+               $rc->numberofWatchingusers = false;
+               return $rc;
+       }
+
+       /**
+        * Obtain the recent change with a given rc_id value
+        *
+        * @param int $rcid rc_id value to retrieve
+        * @return RecentChange
+        */
+       public static function newFromId( $rcid ) {
+               return self::newFromConds( array( 'rc_id' => $rcid ), __METHOD__ );
+       }
+
+       /**
+        * Find the first recent change matching some specific conditions
+        *
+        * @param array $conds of conditions
+        * @param $fname Mixed: override the method name in profiling/logs
+        * @param $options Array Query options
+        * @return RecentChange
+        */
+       public static function newFromConds( $conds, $fname = __METHOD__, $options = array() ) {
+               $dbr = wfGetDB( DB_SLAVE );
+               $row = $dbr->selectRow( 'recentchanges', self::selectFields(), $conds, $fname, $options );
+               if ( $row !== false ) {
+                       return self::newFromRow( $row );
+               } else {
+                       return null;
+               }
+       }
+
+       /**
+        * Return the list of recentchanges fields that should be selected to create
+        * a new recentchanges object.
+        * @return array
+        */
+       public static function selectFields() {
+               return array(
+                       'rc_id',
+                       'rc_timestamp',
+                       'rc_cur_time',
+                       'rc_user',
+                       'rc_user_text',
+                       'rc_namespace',
+                       'rc_title',
+                       'rc_comment',
+                       'rc_minor',
+                       'rc_bot',
+                       'rc_new',
+                       'rc_cur_id',
+                       'rc_this_oldid',
+                       'rc_last_oldid',
+                       'rc_type',
+                       'rc_patrolled',
+                       'rc_ip',
+                       'rc_old_len',
+                       'rc_new_len',
+                       'rc_deleted',
+                       'rc_logid',
+                       'rc_log_type',
+                       'rc_log_action',
+                       'rc_params',
+               );
+       }
+
+       # Accessors
+
+       /**
+        * @param $attribs array
+        */
+       public function setAttribs( $attribs ) {
+               $this->mAttribs = $attribs;
+       }
+
+       /**
+        * @param $extra array
+        */
+       public function setExtra( $extra ) {
+               $this->mExtra = $extra;
+       }
+
+       /**
+        *
+        * @return Title
+        */
+       public function &getTitle() {
+               if ( $this->mTitle === false ) {
+                       $this->mTitle = Title::makeTitle( $this->mAttribs['rc_namespace'], $this->mAttribs['rc_title'] );
+               }
+               return $this->mTitle;
+       }
+
+       /**
+        * Get the User object of the person who performed this change.
+        *
+        * @return User
+        */
+       public function getPerformer() {
+               if ( $this->mPerformer === false ) {
+                       if ( $this->mAttribs['rc_user'] ) {
+                               $this->mPerformer = User::newFromID( $this->mAttribs['rc_user'] );
+                       } else {
+                               $this->mPerformer = User::newFromName( $this->mAttribs['rc_user_text'], false );
+                       }
+               }
+               return $this->mPerformer;
+       }
+
+       /**
+        * Writes the data in this object to the database
+        * @param $noudp bool
+        */
+       public function save( $noudp = false ) {
+               global $wgLocalInterwiki, $wgPutIPinRC, $wgUseEnotif, $wgShowUpdatedMarker, $wgContLang;
+
+               $dbw = wfGetDB( DB_MASTER );
+               if ( !is_array( $this->mExtra ) ) {
+                       $this->mExtra = array();
+               }
+               $this->mExtra['lang'] = $wgLocalInterwiki;
+
+               if ( !$wgPutIPinRC ) {
+                       $this->mAttribs['rc_ip'] = '';
+               }
+
+               # If our database is strict about IP addresses, use NULL instead of an empty string
+               if ( $dbw->strictIPs() and $this->mAttribs['rc_ip'] == '' ) {
+                       unset( $this->mAttribs['rc_ip'] );
+               }
+
+               # Trim spaces on user supplied text
+               $this->mAttribs['rc_comment'] = trim( $this->mAttribs['rc_comment'] );
+
+               # Make sure summary is truncated (whole multibyte characters)
+               $this->mAttribs['rc_comment'] = $wgContLang->truncate( $this->mAttribs['rc_comment'], 255 );
+
+               # Fixup database timestamps
+               $this->mAttribs['rc_timestamp'] = $dbw->timestamp( $this->mAttribs['rc_timestamp'] );
+               $this->mAttribs['rc_cur_time'] = $dbw->timestamp( $this->mAttribs['rc_cur_time'] );
+               $this->mAttribs['rc_id'] = $dbw->nextSequenceValue( 'recentchanges_rc_id_seq' );
+
+               ## If we are using foreign keys, an entry of 0 for the page_id will fail, so use NULL
+               if ( $dbw->cascadingDeletes() and $this->mAttribs['rc_cur_id'] == 0 ) {
+                       unset( $this->mAttribs['rc_cur_id'] );
+               }
+
+               # Insert new row
+               $dbw->insert( 'recentchanges', $this->mAttribs, __METHOD__ );
+
+               # Set the ID
+               $this->mAttribs['rc_id'] = $dbw->insertId();
+
+               # Notify extensions
+               wfRunHooks( 'RecentChange_save', array( &$this ) );
+
+               # Notify external application via UDP
+               if ( !$noudp ) {
+                       $this->notifyRCFeeds();
+               }
+
+               # E-mail notifications
+               if ( $wgUseEnotif || $wgShowUpdatedMarker ) {
+                       $editor = $this->getPerformer();
+                       $title = $this->getTitle();
+
+                       if ( wfRunHooks( 'AbortEmailNotification', array( $editor, $title ) ) ) {
+                               # @todo FIXME: This would be better as an extension hook
+                               $enotif = new EmailNotification();
+                               $enotif->notifyOnPageChange( $editor, $title,
+                                       $this->mAttribs['rc_timestamp'],
+                                       $this->mAttribs['rc_comment'],
+                                       $this->mAttribs['rc_minor'],
+                                       $this->mAttribs['rc_last_oldid'],
+                                       $this->mExtra['pageStatus'] );
+                       }
+               }
+       }
+
+       /**
+        * @deprecated since 1.22, use notifyRCFeeds instead.
+        */
+       public function notifyRC2UDP() {
+               wfDeprecated( __METHOD__, '1.22' );
+               $this->notifyRCFeeds();
+       }
+
+       /**
+        * Send some text to UDP.
+        * @deprecated since 1.22
+        */
+       public static function sendToUDP( $line, $address = '', $prefix = '', $port = '' ) {
+               global $wgRC2UDPAddress, $wgRC2UDPInterwikiPrefix, $wgRC2UDPPort, $wgRC2UDPPrefix;
+
+               wfDeprecated( __METHOD__, '1.22' );
+
+               # Assume default for standard RC case
+               $address = $address ? $address : $wgRC2UDPAddress;
+               $prefix = $prefix ? $prefix : $wgRC2UDPPrefix;
+               $port = $port ? $port : $wgRC2UDPPort;
+
+               $engine = new UDPRCFeedEngine();
+               $feed = array(
+                       'uri' => "udp://$address:$port/$prefix",
+                       'formatter' => 'IRCColourfulRCFeedFormatter',
+                       'add_interwiki_prefix' => $wgRC2UDPInterwikiPrefix,
+               );
+
+               return $engine->send( $feed, $line );
+       }
+
+       /**
+        * Notify all the feeds about the change.
+        */
+       public function notifyRCFeeds() {
+               global $wgRCFeeds;
+
+               foreach ( $wgRCFeeds as $feed ) {
+                       $engine = self::getEngine( $feed['uri'] );
+
+                       if ( isset( $this->mExtra['actionCommentIRC'] ) ) {
+                               $actionComment = $this->mExtra['actionCommentIRC'];
+                       } else {
+                               $actionComment = null;
+                       }
+
+                       $omitBots = isset( $feed['omit_bots'] ) ? $feed['omit_bots'] : false;
+
+                       if (
+                               ( $omitBots && $this->mAttribs['rc_bot'] ) ||
+                               $this->mAttribs['rc_type'] == RC_EXTERNAL
+                       ) {
+                               continue;
+                       }
+
+                       $formatter = new $feed['formatter']();
+                       $line = $formatter->getLine( $feed, $this, $actionComment );
+
+                       $engine->send( $feed, $line );
+               }
+       }
+
+       /**
+        * Gets the stream engine object for a given URI from $wgRCEngines
+        *
+        * @param $uri string URI to get the engine object for
+        * @return object The engine object
+        */
+       private static function getEngine( $uri ) {
+               global $wgRCEngines;
+
+               $scheme = parse_url( $uri, PHP_URL_SCHEME );
+               if ( !$scheme ) {
+                       throw new MWException( __FUNCTION__ . ": Invalid stream logger URI: '$uri'" );
+               }
+
+               if ( !isset( $wgRCEngines[$scheme] ) ) {
+                       throw new MWException( __FUNCTION__ . ": Unknown stream logger URI scheme: $scheme" );
+               }
+
+               return new $wgRCEngines[$scheme];
+       }
+
+       /**
+        * @deprecated since 1.22, moved to IRCColourfulRCFeedFormatter
+        */
+       public static function cleanupForIRC( $text ) {
+               wfDeprecated( __METHOD__, '1.22' );
+               return IRCColourfulRCFeedFormatter::cleanupForIRC( $text );
+       }
+
+       /**
+        * Mark a given change as patrolled
+        *
+        * @param $change Mixed: RecentChange or corresponding rc_id
+        * @param $auto Boolean: for automatic patrol
+        * @return Array See doMarkPatrolled(), or null if $change is not an existing rc_id
+        */
+       public static function markPatrolled( $change, $auto = false ) {
+               global $wgUser;
+
+               $change = $change instanceof RecentChange
+                       ? $change
+                       : RecentChange::newFromId( $change );
+
+               if ( !$change instanceof RecentChange ) {
+                       return null;
+               }
+               return $change->doMarkPatrolled( $wgUser, $auto );
+       }
+
+       /**
+        * Mark this RecentChange as patrolled
+        *
+        * NOTE: Can also return 'rcpatroldisabled', 'hookaborted' and 'markedaspatrollederror-noautopatrol' as errors
+        * @param $user User object doing the action
+        * @param $auto Boolean: for automatic patrol
+        * @return array of permissions errors, see Title::getUserPermissionsErrors()
+        */
+       public function doMarkPatrolled( User $user, $auto = false ) {
+               global $wgUseRCPatrol, $wgUseNPPatrol;
+               $errors = array();
+               // If recentchanges patrol is disabled, only new pages
+               // can be patrolled
+               if ( !$wgUseRCPatrol && ( !$wgUseNPPatrol || $this->getAttribute( 'rc_type' ) != RC_NEW ) ) {
+                       $errors[] = array( 'rcpatroldisabled' );
+               }
+               // Automatic patrol needs "autopatrol", ordinary patrol needs "patrol"
+               $right = $auto ? 'autopatrol' : 'patrol';
+               $errors = array_merge( $errors, $this->getTitle()->getUserPermissionsErrors( $right, $user ) );
+               if ( !wfRunHooks( 'MarkPatrolled', array( $this->getAttribute( 'rc_id' ), &$user, false ) ) ) {
+                       $errors[] = array( 'hookaborted' );
+               }
+               // Users without the 'autopatrol' right can't patrol their
+               // own revisions
+               if ( $user->getName() == $this->getAttribute( 'rc_user_text' ) && !$user->isAllowed( 'autopatrol' ) ) {
+                       $errors[] = array( 'markedaspatrollederror-noautopatrol' );
+               }
+               if ( $errors ) {
+                       return $errors;
+               }
+               // If the change was patrolled already, do nothing
+               if ( $this->getAttribute( 'rc_patrolled' ) ) {
+                       return array();
+               }
+               // Actually set the 'patrolled' flag in RC
+               $this->reallyMarkPatrolled();
+               // Log this patrol event
+               PatrolLog::record( $this, $auto, $user );
+               wfRunHooks( 'MarkPatrolledComplete', array( $this->getAttribute( 'rc_id' ), &$user, false ) );
+               return array();
+       }
+
+       /**
+        * Mark this RecentChange patrolled, without error checking
+        * @return Integer: number of affected rows
+        */
+       public function reallyMarkPatrolled() {
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->update(
+                       'recentchanges',
+                       array(
+                               'rc_patrolled' => 1
+                       ),
+                       array(
+                               'rc_id' => $this->getAttribute( 'rc_id' )
+                       ),
+                       __METHOD__
+               );
+               // Invalidate the page cache after the page has been patrolled
+               // to make sure that the Patrol link isn't visible any longer!
+               $this->getTitle()->invalidateCache();
+               return $dbw->affectedRows();
+       }
+
+       /**
+        * Makes an entry in the database corresponding to an edit
+        *
+        * @param $timestamp
+        * @param $title Title
+        * @param $minor
+        * @param $user User
+        * @param $comment
+        * @param $oldId
+        * @param $lastTimestamp
+        * @param $bot
+        * @param $ip string
+        * @param $oldSize int
+        * @param $newSize int
+        * @param $newId int
+        * @param $patrol int
+        * @return RecentChange
+        */
+       public static function notifyEdit( $timestamp, &$title, $minor, &$user, $comment, $oldId,
+               $lastTimestamp, $bot, $ip = '', $oldSize = 0, $newSize = 0, $newId = 0, $patrol = 0 ) {
+               $rc = new RecentChange;
+               $rc->mTitle = $title;
+               $rc->mPerformer = $user;
+               $rc->mAttribs = array(
+                       'rc_timestamp'  => $timestamp,
+                       'rc_cur_time'   => $timestamp,
+                       'rc_namespace'  => $title->getNamespace(),
+                       'rc_title'      => $title->getDBkey(),
+                       'rc_type'       => RC_EDIT,
+                       'rc_minor'      => $minor ? 1 : 0,
+                       'rc_cur_id'     => $title->getArticleID(),
+                       'rc_user'       => $user->getId(),
+                       'rc_user_text'  => $user->getName(),
+                       'rc_comment'    => $comment,
+                       'rc_this_oldid' => $newId,
+                       'rc_last_oldid' => $oldId,
+                       'rc_bot'        => $bot ? 1 : 0,
+                       'rc_ip'         => self::checkIPAddress( $ip ),
+                       'rc_patrolled'  => intval( $patrol ),
+                       'rc_new'        => 0,  # obsolete
+                       'rc_old_len'    => $oldSize,
+                       'rc_new_len'    => $newSize,
+                       'rc_deleted'    => 0,
+                       'rc_logid'      => 0,
+                       'rc_log_type'   => null,
+                       'rc_log_action' => '',
+                       'rc_params'     => ''
+               );
+
+               $rc->mExtra = array(
+                       'prefixedDBkey' => $title->getPrefixedDBkey(),
+                       'lastTimestamp' => $lastTimestamp,
+                       'oldSize'       => $oldSize,
+                       'newSize'       => $newSize,
+                       'pageStatus'   => 'changed'
+               );
+               $rc->save();
+               return $rc;
+       }
+
+       /**
+        * Makes an entry in the database corresponding to page creation
+        * Note: the title object must be loaded with the new id using resetArticleID()
+        * @todo Document parameters and return
+        *
+        * @param $timestamp
+        * @param $title Title
+        * @param $minor
+        * @param $user User
+        * @param $comment
+        * @param $bot
+        * @param $ip string
+        * @param $size int
+        * @param $newId int
+        * @param $patrol int
+        * @return RecentChange
+        */
+       public static function notifyNew( $timestamp, &$title, $minor, &$user, $comment, $bot,
+               $ip = '', $size = 0, $newId = 0, $patrol = 0 ) {
+               $rc = new RecentChange;
+               $rc->mTitle = $title;
+               $rc->mPerformer = $user;
+               $rc->mAttribs = array(
+                       'rc_timestamp'      => $timestamp,
+                       'rc_cur_time'       => $timestamp,
+                       'rc_namespace'      => $title->getNamespace(),
+                       'rc_title'          => $title->getDBkey(),
+                       'rc_type'           => RC_NEW,
+                       'rc_minor'          => $minor ? 1 : 0,
+                       'rc_cur_id'         => $title->getArticleID(),
+                       'rc_user'           => $user->getId(),
+                       'rc_user_text'      => $user->getName(),
+                       'rc_comment'        => $comment,
+                       'rc_this_oldid'     => $newId,
+                       'rc_last_oldid'     => 0,
+                       'rc_bot'            => $bot ? 1 : 0,
+                       'rc_ip'             => self::checkIPAddress( $ip ),
+                       'rc_patrolled'      => intval( $patrol ),
+                       'rc_new'            => 1, # obsolete
+                       'rc_old_len'        => 0,
+                       'rc_new_len'        => $size,
+                       'rc_deleted'        => 0,
+                       'rc_logid'          => 0,
+                       'rc_log_type'       => null,
+                       'rc_log_action'     => '',
+                       'rc_params'         => ''
+               );
+
+               $rc->mExtra = array(
+                       'prefixedDBkey' => $title->getPrefixedDBkey(),
+                       'lastTimestamp' => 0,
+                       'oldSize' => 0,
+                       'newSize' => $size,
+                       'pageStatus' => 'created'
+               );
+               $rc->save();
+               return $rc;
+       }
+
+       /**
+        * @param $timestamp
+        * @param $title
+        * @param $user
+        * @param $actionComment
+        * @param $ip string
+        * @param $type
+        * @param $action
+        * @param $target
+        * @param $logComment
+        * @param $params
+        * @param $newId int
+        * @param $actionCommentIRC string
+        * @return bool
+        */
+       public static function notifyLog( $timestamp, &$title, &$user, $actionComment, $ip, $type,
+               $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = '' )
+       {
+               global $wgLogRestrictions;
+               # Don't add private logs to RC!
+               if ( isset( $wgLogRestrictions[$type] ) && $wgLogRestrictions[$type] != '*' ) {
+                       return false;
+               }
+               $rc = self::newLogEntry( $timestamp, $title, $user, $actionComment, $ip, $type, $action,
+                       $target, $logComment, $params, $newId, $actionCommentIRC );
+               $rc->save();
+               return true;
+       }
+
+       /**
+        * @param $timestamp
+        * @param $title Title
+        * @param $user User
+        * @param $actionComment
+        * @param $ip string
+        * @param $type
+        * @param $action
+        * @param $target Title
+        * @param $logComment
+        * @param $params
+        * @param $newId int
+        * @param $actionCommentIRC string
+        * @return RecentChange
+        */
+       public static function newLogEntry( $timestamp, &$title, &$user, $actionComment, $ip,
+               $type, $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = '' ) {
+               global $wgRequest;
+
+               ## Get pageStatus for email notification
+               switch ( $type . '-' . $action ) {
+                       case 'delete-delete':
+                               $pageStatus = 'deleted';
+                               break;
+                       case 'move-move':
+                       case 'move-move_redir':
+                               $pageStatus = 'moved';
+                               break;
+                       case 'delete-restore':
+                               $pageStatus = 'restored';
+                               break;
+                       case 'upload-upload':
+                               $pageStatus = 'created';
+                               break;
+                       case 'upload-overwrite':
+                       default:
+                               $pageStatus = 'changed';
+                               break;
+               }
+
+               $rc = new RecentChange;
+               $rc->mTitle = $target;
+               $rc->mPerformer = $user;
+               $rc->mAttribs = array(
+                       'rc_timestamp'  => $timestamp,
+                       'rc_cur_time'   => $timestamp,
+                       'rc_namespace'  => $target->getNamespace(),
+                       'rc_title'      => $target->getDBkey(),
+                       'rc_type'       => RC_LOG,
+                       'rc_minor'      => 0,
+                       'rc_cur_id'     => $target->getArticleID(),
+                       'rc_user'       => $user->getId(),
+                       'rc_user_text'  => $user->getName(),
+                       'rc_comment'    => $logComment,
+                       'rc_this_oldid' => 0,
+                       'rc_last_oldid' => 0,
+                       'rc_bot'        => $user->isAllowed( 'bot' ) ? $wgRequest->getBool( 'bot', true ) : 0,
+                       'rc_ip'         => self::checkIPAddress( $ip ),
+                       'rc_patrolled'  => 1,
+                       'rc_new'        => 0, # obsolete
+                       'rc_old_len'    => null,
+                       'rc_new_len'    => null,
+                       'rc_deleted'    => 0,
+                       'rc_logid'      => $newId,
+                       'rc_log_type'   => $type,
+                       'rc_log_action' => $action,
+                       'rc_params'     => $params
+               );
+
+               $rc->mExtra = array(
+                       'prefixedDBkey' => $title->getPrefixedDBkey(),
+                       'lastTimestamp' => 0,
+                       'actionComment' => $actionComment, // the comment appended to the action, passed from LogPage
+                       'pageStatus'    => $pageStatus,
+                       'actionCommentIRC' => $actionCommentIRC
+               );
+               return $rc;
+       }
+
+       /**
+        * Initialises the members of this object from a mysql row object
+        *
+        * @param $row
+        */
+       public function loadFromRow( $row ) {
+               $this->mAttribs = get_object_vars( $row );
+               $this->mAttribs['rc_timestamp'] = wfTimestamp( TS_MW, $this->mAttribs['rc_timestamp'] );
+               $this->mAttribs['rc_deleted'] = $row->rc_deleted; // MUST be set
+       }
+
+       /**
+        * Makes a pseudo-RC entry from a cur row
+        *
+        * @deprected in 1.22
+        * @param $row
+        */
+       public function loadFromCurRow( $row ) {
+               wfDeprecated( __METHOD__, '1.22' );
+               $this->mAttribs = array(
+                       'rc_timestamp' => wfTimestamp( TS_MW, $row->rev_timestamp ),
+                       'rc_cur_time' => $row->rev_timestamp,
+                       'rc_user' => $row->rev_user,
+                       'rc_user_text' => $row->rev_user_text,
+                       'rc_namespace' => $row->page_namespace,
+                       'rc_title' => $row->page_title,
+                       'rc_comment' => $row->rev_comment,
+                       'rc_minor' => $row->rev_minor_edit ? 1 : 0,
+                       'rc_type' => $row->page_is_new ? RC_NEW : RC_EDIT,
+                       'rc_cur_id' => $row->page_id,
+                       'rc_this_oldid' => $row->rev_id,
+                       'rc_last_oldid' => isset( $row->rc_last_oldid ) ? $row->rc_last_oldid : 0,
+                       'rc_bot' => 0,
+                       'rc_ip' => '',
+                       'rc_id' => $row->rc_id,
+                       'rc_patrolled' => $row->rc_patrolled,
+                       'rc_new' => $row->page_is_new, # obsolete
+                       'rc_old_len' => $row->rc_old_len,
+                       'rc_new_len' => $row->rc_new_len,
+                       'rc_params' => isset( $row->rc_params ) ? $row->rc_params : '',
+                       'rc_log_type' => isset( $row->rc_log_type ) ? $row->rc_log_type : null,
+                       'rc_log_action' => isset( $row->rc_log_action ) ? $row->rc_log_action : null,
+                       'rc_logid' => isset( $row->rc_logid ) ? $row->rc_logid : 0,
+                       'rc_deleted' => $row->rc_deleted // MUST be set
+               );
+       }
+
+       /**
+        * Get an attribute value
+        *
+        * @param string $name Attribute name
+        * @return mixed
+        */
+       public function getAttribute( $name ) {
+               return isset( $this->mAttribs[$name] ) ? $this->mAttribs[$name] : null;
+       }
+
+       /**
+        * @return array
+        */
+       public function getAttributes() {
+               return $this->mAttribs;
+       }
+
+       /**
+        * Gets the end part of the diff URL associated with this object
+        * Blank if no diff link should be displayed
+        * @param $forceCur
+        * @return string
+        */
+       public function diffLinkTrail( $forceCur ) {
+               if ( $this->mAttribs['rc_type'] == RC_EDIT ) {
+                       $trail = "curid=" . (int)( $this->mAttribs['rc_cur_id'] ) .
+                               "&oldid=" . (int)( $this->mAttribs['rc_last_oldid'] );
+                       if ( $forceCur ) {
+                               $trail .= '&diff=0';
+                       } else {
+                               $trail .= '&diff=' . (int)( $this->mAttribs['rc_this_oldid'] );
+                       }
+               } else {
+                       $trail = '';
+               }
+               return $trail;
+       }
+
+       /**
+        * Returns the change size (HTML).
+        * The lengths can be given optionally.
+        * @param $old int
+        * @param $new int
+        * @return string
+        */
+       public function getCharacterDifference( $old = 0, $new = 0 ) {
+               if ( $old === 0 ) {
+                       $old = $this->mAttribs['rc_old_len'];
+               }
+               if ( $new === 0 ) {
+                       $new = $this->mAttribs['rc_new_len'];
+               }
+               if ( $old === null || $new === null ) {
+                       return '';
+               }
+               return ChangesList::showCharacterDifference( $old, $new );
+       }
+
+       /**
+        * Purge expired changes from the recentchanges table
+        * @since 1.22
+        */
+       public static function purgeExpiredChanges() {
+               if ( wfReadOnly() ) {
+                       return;
+               }
+
+               $method = __METHOD__;
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->onTransactionIdle( function() use ( $dbw, $method ) {
+                       global $wgRCMaxAge;
+
+                       $cutoff = $dbw->timestamp( time() - $wgRCMaxAge );
+                       $dbw->delete(
+                               'recentchanges',
+                               array( 'rc_timestamp < ' . $dbw->addQuotes( $cutoff ) ),
+                               $method
+                       );
+               } );
+       }
+
+       private static function checkIPAddress( $ip ) {
+               global $wgRequest;
+               if ( $ip ) {
+                       if ( !IP::isIPAddress( $ip ) ) {
+                               throw new MWException( "Attempt to write \"" . $ip . "\" as an IP address into recent changes" );
+                       }
+               } else {
+                       $ip = $wgRequest->getIP();
+                       if ( !$ip ) {
+                               $ip = '';
+                       }
+               }
+               return $ip;
+       }
+
+       /**
+        * Check whether the given timestamp is new enough to have a RC row with a given tolerance
+        * as the recentchanges table might not be cleared out regularly (so older entries might exist)
+        * or rows which will be deleted soon shouldn't be included.
+        *
+        * @param $timestamp mixed MWTimestamp compatible timestamp
+        * @param $tolerance integer Tolerance in seconds
+        * @return bool
+        */
+       public static function isInRCLifespan( $timestamp, $tolerance = 0 ) {
+               global $wgRCMaxAge;
+               return wfTimestamp( TS_UNIX, $timestamp ) > time() - $tolerance - $wgRCMaxAge;
+       }
+}
index a8fa9ed..2a92e23 100644 (file)
@@ -938,7 +938,7 @@ abstract class ContentHandler {
         * @return ParserOptions
         */
        public function makeParserOptions( $context ) {
-               global $wgContLang;
+               global $wgContLang, $wgEnableParserLimitReporting;
 
                if ( $context instanceof IContextSource ) {
                        $options = ParserOptions::newFromContext( $context );
@@ -950,7 +950,7 @@ abstract class ContentHandler {
                        throw new MWException( "Bad context for parser options: $context" );
                }
 
-               $options->enableLimitReport(); // show inclusion/loop reports
+               $options->enableLimitReport( $wgEnableParserLimitReporting ); // show inclusion/loop reports
                $options->setTidy( true ); // fix bad HTML
 
                return $options;
index df2877f..c3850b9 100644 (file)
@@ -662,6 +662,18 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
 
        /**
         * Constructor.
+        *
+        * FIXME: It is possible to construct a Database object with no associated
+        * connection object, by specifying no parameters to __construct(). This
+        * feature is deprecated and should be removed.
+        *
+        * FIXME: The long list of formal parameters here is not really appropriate
+        * for MySQL, and not at all appropriate for any other DBMS. It should be
+        * replaced by named parameters as in DatabaseBase::factory().
+        *
+        * DatabaseBase subclasses should not be constructed directly in external
+        * code. DatabaseBase::factory() should be used instead.
+        *
         * @param string $server database server host
         * @param string $user database user name
         * @param string $password database user password
@@ -717,7 +729,7 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
        /**
         * Given a DB type, construct the name of the appropriate child class of
         * DatabaseBase. This is designed to replace all of the manual stuff like:
-        *      $class = 'Database' . ucfirst( strtolower( $type ) );
+        *      $class = 'Database' . ucfirst( strtolower( $dbType ) );
         * as well as validate against the canonical list of DB types we have
         *
         * This factory function is mostly useful for when you need to connect to a
@@ -732,17 +744,47 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
         *
         * @param string $dbType A possible DB type
         * @param array $p An array of options to pass to the constructor.
-        *    Valid options are: host, user, password, dbname, flags, tablePrefix
+        *    Valid options are: host, user, password, dbname, flags, tablePrefix, driver
         * @return DatabaseBase subclass or null
         */
        final public static function factory( $dbType, $p = array() ) {
                $canonicalDBTypes = array(
-                       'mysql', 'postgres', 'sqlite', 'oracle', 'mssql'
+                       'mysql'    => array( 'mysqli', 'mysql' ),
+                       'postgres' => array(),
+                       'sqlite'   => array(),
+                       'oracle'   => array(),
+                       'mssql'    => array(),
                );
+
+               $driver = false;
                $dbType = strtolower( $dbType );
-               $class = 'Database' . ucfirst( $dbType );
+               if ( isset( $canonicalDBTypes[$dbType] ) && $canonicalDBTypes[$dbType] ) {
+                       $possibleDrivers = $canonicalDBTypes[$dbType];
+                       if ( !empty( $p['driver'] ) ) {
+                               if ( in_array( $p['driver'], $possibleDrivers ) ) {
+                                       $driver = $p['driver'];
+                               } else {
+                                       throw new MWException( __METHOD__ .
+                                               " cannot construct Database with type '$dbType' and driver '{$p['driver']}'" );
+                               }
+                       } else {
+                               foreach ( $possibleDrivers as $posDriver ) {
+                                       if ( extension_loaded( $posDriver ) ) {
+                                               $driver = $posDriver;
+                                               break;
+                                       }
+                               }
+                       }
+               } else {
+                       $driver = $dbType;
+               }
+               if ( $driver === false ) {
+                       throw new MWException( __METHOD__ .
+                               " no viable database extension found for type '$dbType'" );
+               }
 
-               if ( in_array( $dbType, $canonicalDBTypes ) || ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) ) {
+               $class = 'Database' . ucfirst( $driver );
+               if ( class_exists( $class ) && is_subclass_of( $class, 'DatabaseBase' ) ) {
                        return new $class(
                                isset( $p['host'] ) ? $p['host'] : false,
                                isset( $p['user'] ) ? $p['user'] : false,
index d33d7c7..49579b6 100644 (file)
@@ -1006,7 +1006,7 @@ abstract class DatabaseMysqlBase extends DatabaseBase {
  */
 class MySQLField implements Field {
        private $name, $tablename, $default, $max_length, $nullable,
-               $is_pk, $is_unique, $is_multiple, $is_key, $type;
+               $is_pk, $is_unique, $is_multiple, $is_key, $type, $binary;
 
        function __construct( $info ) {
                $this->name = $info->name;
@@ -1019,6 +1019,7 @@ class MySQLField implements Field {
                $this->is_multiple = $info->multiple_key;
                $this->is_key = ( $this->is_pk || $this->is_unique || $this->is_multiple );
                $this->type = $info->type;
+               $this->binary = isset( $info->binary ) ? $info->binary : false;
        }
 
        /**
@@ -1066,6 +1067,10 @@ class MySQLField implements Field {
        function isMultipleKey() {
                return $this->is_multiple;
        }
+
+       function isBinary() {
+               return $this->binary;
+       }
 }
 
 class MySQLMasterPos implements DBMasterPos {
diff --git a/includes/db/DatabaseMysqli.php b/includes/db/DatabaseMysqli.php
new file mode 100644 (file)
index 0000000..7761abe
--- /dev/null
@@ -0,0 +1,194 @@
+<?php
+/**
+ * This is the MySQLi database abstraction layer.
+ *
+ * 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 Database
+ */
+
+/**
+ * Database abstraction object for PHP extension mysqli.
+ *
+ * @ingroup Database
+ * @since 1.22
+ * @see Database
+ */
+class DatabaseMysqli extends DatabaseMysqlBase {
+
+       /**
+        * @param $sql string
+        * @return resource
+        */
+       protected function doQuery( $sql ) {
+               if ( $this->bufferResults() ) {
+                       $ret = $this->mConn->query( $sql );
+               } else {
+                       $ret = $this->mConn->query( $sql, MYSQLI_USE_RESULT );
+               }
+               return $ret;
+       }
+
+       protected function mysqlConnect( $realServer ) {
+               # Fail now
+               # Otherwise we get a suppressed fatal error, which is very hard to track down
+               if ( !function_exists( 'mysqli_init' ) ) {
+                       throw new DBConnectionError( $this, "MySQLi functions missing,"
+                               . " have you compiled PHP with the --with-mysqli option?\n" );
+               }
+
+               $connFlags = 0;
+               if ( $this->mFlags & DBO_SSL ) {
+                       $connFlags |= MYSQLI_CLIENT_SSL;
+               }
+               if ( $this->mFlags & DBO_COMPRESS ) {
+                       $connFlags |= MYSQLI_CLIENT_COMPRESS;
+               }
+               if ( $this->mFlags & DBO_PERSISTENT ) {
+                       $realServer = 'p:' . $realServer;
+               }
+
+               $mysqli = mysqli_init();
+               $numAttempts = 2;
+
+               for ( $i = 0; $i < $numAttempts; $i++ ) {
+                       if ( $i > 1 ) {
+                               usleep( 1000 );
+                       }
+                       if ( $mysqli->real_connect( $realServer, $this->mUser,
+                               $this->mPassword, $this->mDBname, null, null, $connFlags ) )
+                       {
+                               return $mysqli;
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * @return bool
+        */
+       protected function closeConnection() {
+               return $this->mConn->close();
+       }
+
+       /**
+        * @return int
+        */
+       function insertId() {
+               return $this->mConn->insert_id;
+       }
+
+       /**
+        * @return int
+        */
+       function lastErrno() {
+               if ( $this->mConn ) {
+                       return $this->mConn->errno;
+               } else {
+                       return mysqli_connect_errno();
+               }
+       }
+
+       /**
+        * @return int
+        */
+       function affectedRows() {
+               return $this->mConn->affected_rows;
+       }
+
+       /**
+        * @param $db
+        * @return bool
+        */
+       function selectDB( $db ) {
+               $this->mDBname = $db;
+               return $this->mConn->select_db( $db );
+       }
+
+       /**
+        * @return string
+        */
+       function getServerVersion() {
+               return $this->mConn->server_info;
+       }
+
+       protected function mysqlFreeResult( $res ) {
+               $res->free_result();
+               return true;
+       }
+
+       protected function mysqlFetchObject( $res ) {
+               $object = $res->fetch_object();
+               if ( $object === null ) {
+                       return false;
+               }
+               return $object;
+       }
+
+       protected function mysqlFetchArray( $res ) {
+               $array = $res->fetch_array();
+               if ( $array === null ) {
+                       return false;
+               }
+               return $array;
+       }
+
+       protected function mysqlNumRows( $res ) {
+               return $res->num_rows;
+       }
+
+       protected function mysqlNumFields( $res ) {
+               return $res->field_count;
+       }
+
+       protected function mysqlFetchField( $res, $n ) {
+               $field = $res->fetch_field_direct( $n );
+               $field->not_null = $field->flags & MYSQLI_NOT_NULL_FLAG;
+               $field->primary_key = $field->flags & MYSQLI_PRI_KEY_FLAG;
+               $field->unique_key = $field->flags & MYSQLI_UNIQUE_KEY_FLAG;
+               $field->multiple_key = $field->flags & MYSQLI_MULTIPLE_KEY_FLAG;
+               $field->binary = $field->flags & MYSQLI_BINARY_FLAG;
+               return $field;
+       }
+
+       protected function mysqlFieldName( $res, $n ) {
+               $field = $res->fetch_field_direct( $n );
+               return $field->name;
+       }
+
+       protected function mysqlDataSeek( $res, $row ) {
+               return $res->data_seek( $row );
+       }
+
+       protected function mysqlError( $conn = null ) {
+               if ($conn === null) {
+                       return mysqli_connect_error();
+               } else {
+                       return $conn->error;
+               }
+       }
+
+       protected function mysqlRealEscapeString( $s ) {
+               return $this->mConn->real_escape_string( $s );
+       }
+
+       protected function mysqlPing() {
+               return $this->mConn->ping();
+       }
+
+}
index a8270bf..4a51226 100644 (file)
@@ -52,7 +52,7 @@ class DatabaseSqlite extends DatabaseBase {
                $this->mName = $dbName;
                parent::__construct( $server, $user, $password, $dbName, $flags );
                // parent doesn't open when $user is false, but we can work with $dbName
-               if ( $dbName ) {
+               if ( $dbName && !$this->isOpen() ) {
                        global $wgSharedDB;
                        if ( $this->open( $server, $user, $password, $dbName ) && $wgSharedDB ) {
                                $this->attachDatabase( $wgSharedDB );
@@ -90,6 +90,7 @@ class DatabaseSqlite extends DatabaseBase {
        function open( $server, $user, $pass, $dbName ) {
                global $wgSQLiteDataDir;
 
+               $this->close();
                $fileName = self::generateFileName( $wgSQLiteDataDir, $dbName );
                if ( !is_readable( $fileName ) ) {
                        $this->mConn = false;
@@ -655,7 +656,11 @@ class DatabaseSqlite extends DatabaseBase {
                if ( $this->mTrxLevel == 1 ) {
                        $this->commit( __METHOD__ );
                }
-               $this->mConn->beginTransaction();
+               try {
+                       $this->mConn->beginTransaction();
+               } catch ( PDOException $e ) {
+                       throw new DBUnexpectedError( $this, 'Error in BEGIN query: ' . $e->getMessage() );
+               }
                $this->mTrxLevel = 1;
        }
 
@@ -663,7 +668,11 @@ class DatabaseSqlite extends DatabaseBase {
                if ( $this->mTrxLevel == 0 ) {
                        return;
                }
-               $this->mConn->commit();
+               try {
+                       $this->mConn->commit();
+               } catch ( PDOException $e ) {
+                       throw new DBUnexpectedError( $this, 'Error in COMMIT query: ' . $e->getMessage() );
+               }
                $this->mTrxLevel = 0;
        }
 
index bdeb578..f586578 100644 (file)
@@ -1183,8 +1183,10 @@ abstract class FileBackend {
         * Once the return value goes out scope, the locks will be released and
         * the status updated. Unlock fatals will not change the status "OK" value.
         *
-        * @param array $paths Storage paths
-        * @param integer $type LockManager::LOCK_* constant
+        * @see ScopedLock::factory()
+        *
+        * @param array $paths List of storage paths or map of lock types to path lists
+        * @param integer|string $type LockManager::LOCK_* constant or "mixed"
         * @param Status $status Status to update on lock/unlock
         * @return ScopedLock|null Returns null on failure
         */
index 7d35487..97584a7 100644 (file)
@@ -141,17 +141,10 @@ class FileBackendMultiWrite extends FileBackend {
 
                $mbe = $this->backends[$this->masterIndex]; // convenience
 
-               // Get the paths to lock from the master backend
-               $realOps = $this->substOpBatchPaths( $ops, $mbe );
-               $paths = $mbe->getPathsToLockForOpsInternal( $mbe->getOperationsInternal( $realOps ) );
-               // Get the paths under the proxy backend's name
-               $paths['sh'] = $this->unsubstPaths( $paths['sh'] );
-               $paths['ex'] = $this->unsubstPaths( $paths['ex'] );
                // Try to lock those files for the scope of this function...
                if ( empty( $opts['nonLocking'] ) ) {
                        // Try to lock those files for the scope of this function...
-                       $scopeLockS = $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status );
-                       $scopeLockE = $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status );
+                       $scopeLock = $this->getScopedLocksForOps( $ops, $status );
                        if ( !$status->isOK() ) {
                                return $status; // abort
                        }
@@ -178,6 +171,7 @@ class FileBackendMultiWrite extends FileBackend {
                        }
                }
                // Actually attempt the operation batch on the master backend...
+               $realOps = $this->substOpBatchPaths( $ops, $mbe );
                $masterStatus = $mbe->doOperations( $realOps, $opts );
                $status->merge( $masterStatus );
                // Propagate the operations to the clone backends if there were no unexpected errors
@@ -624,15 +618,16 @@ class FileBackendMultiWrite extends FileBackend {
        }
 
        public function getScopedLocksForOps( array $ops, Status $status ) {
-               $fileOps = $this->backends[$this->masterIndex]->getOperationsInternal( $ops );
+               $realOps = $this->substOpBatchPaths( $ops, $this->backends[$this->masterIndex] );
+               $fileOps = $this->backends[$this->masterIndex]->getOperationsInternal( $realOps );
                // Get the paths to lock from the master backend
                $paths = $this->backends[$this->masterIndex]->getPathsToLockForOpsInternal( $fileOps );
                // Get the paths under the proxy backend's name
-               $paths['sh'] = $this->unsubstPaths( $paths['sh'] );
-               $paths['ex'] = $this->unsubstPaths( $paths['ex'] );
-               return array(
-                       $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status ),
-                       $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status )
+               $pbPaths = array(
+                       LockManager::LOCK_UW => $this->unsubstPaths( $paths[LockManager::LOCK_UW] ),
+                       LockManager::LOCK_EX => $this->unsubstPaths( $paths[LockManager::LOCK_EX] )
                );
+               // Actually acquire the locks
+               return array( $this->getScopedFileLocks( $pbPaths, 'mixed', $status ) );
        }
 }
index 8ff383b..0921e99 100644 (file)
@@ -953,12 +953,13 @@ abstract class FileBackendStore extends FileBackend {
 
        /**
         * Get a list of storage paths to lock for a list of operations
-        * Returns an array with 'sh' (shared) and 'ex' (exclusive) keys,
-        * each corresponding to a list of storage paths to be locked.
-        * All returned paths are normalized.
+        * Returns an array with LockManager::LOCK_UW (shared locks) and
+        * LockManager::LOCK_EX (exclusive locks) keys, each corresponding
+        * to a list of storage paths to be locked. All returned paths are
+        * normalized.
         *
         * @param array $performOps List of FileOp objects
-        * @return Array ('sh' => list of paths, 'ex' => list of paths)
+        * @return Array (LockManager::LOCK_UW => path list, LockManager::LOCK_EX => path list)
         */
        final public function getPathsToLockForOpsInternal( array $performOps ) {
                // Build up a list of files to lock...
@@ -972,15 +973,15 @@ abstract class FileBackendStore extends FileBackend {
                // Get a shared lock on the parent directory of each path changed
                $paths['sh'] = array_merge( $paths['sh'], array_map( 'dirname', $paths['ex'] ) );
 
-               return $paths;
+               return array(
+                       LockManager::LOCK_UW => $paths['sh'],
+                       LockManager::LOCK_EX => $paths['ex']
+               );
        }
 
        public function getScopedLocksForOps( array $ops, Status $status ) {
                $paths = $this->getPathsToLockForOpsInternal( $this->getOperationsInternal( $ops ) );
-               return array(
-                       $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status ),
-                       $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status )
-               );
+               return array( $this->getScopedFileLocks( $paths, 'mixed', $status ) );
        }
 
        final protected function doOperationsInternal( array $ops, array $opts ) {
@@ -998,8 +999,7 @@ abstract class FileBackendStore extends FileBackend {
                        // Build up a list of files to lock...
                        $paths = $this->getPathsToLockForOpsInternal( $performOps );
                        // Try to lock those files for the scope of this function...
-                       $scopeLockS = $this->getScopedFileLocks( $paths['sh'], LockManager::LOCK_UW, $status );
-                       $scopeLockE = $this->getScopedFileLocks( $paths['ex'], LockManager::LOCK_EX, $status );
+                       $scopeLock = $this->getScopedFileLocks( $paths, 'mixed', $status );
                        if ( !$status->isOK() ) {
                                return $status; // abort
                        }
index 0c34f3e..fe769be 100644 (file)
@@ -1507,18 +1507,27 @@ class LocalFile extends File {
 
                wfDebugLog( 'imagemove', "Finished moving {$this->name}" );
 
-               $this->purgeEverything();
-               foreach ( $archiveNames as $archiveName ) {
-                       $this->purgeOldThumbnails( $archiveName );
-               }
+               // Purge the source and target files...
+               $oldTitleFile = wfLocalFile( $this->title );
+               $newTitleFile = wfLocalFile( $target );
+               // Hack: the lock()/unlock() pair is nested in a transaction so the locking is not
+               // tied to BEGIN/COMMIT. To avoid slow purges in the transaction, move them outside.
+               $this->getRepo()->getMasterDB()->onTransactionIdle(
+                       function() use ( $oldTitleFile, $newTitleFile, $archiveNames ) {
+                               $oldTitleFile->purgeEverything();
+                               foreach ( $archiveNames as $archiveName ) {
+                                       $oldTitleFile->purgeOldThumbnails( $archiveName );
+                               }
+                               $newTitleFile->purgeEverything();
+                       }
+               );
+
                if ( $status->isOK() ) {
                        // Now switch the object
                        $this->title = $target;
                        // Force regeneration of the name and hashpath
                        unset( $this->name );
                        unset( $this->hashPath );
-                       // Purge the new image
-                       $this->purgeEverything();
                }
 
                return $status;
index 16ec21c..5e420b6 100644 (file)
@@ -72,7 +72,7 @@ class MysqlInstaller extends DatabaseInstaller {
         * @return Bool
         */
        public function isCompiled() {
-               return self::checkExtension( 'mysql' );
+               return self::checkExtension( 'mysql' ) || self::checkExtension( 'mysqli' );
        }
 
        /**
@@ -149,14 +149,13 @@ class MysqlInstaller extends DatabaseInstaller {
        public function openConnection() {
                $status = Status::newGood();
                try {
-                       $db = new DatabaseMysql(
-                               $this->getVar( 'wgDBserver' ),
-                               $this->getVar( '_InstallUser' ),
-                               $this->getVar( '_InstallPassword' ),
-                               false,
-                               0,
-                               $this->getVar( 'wgDBprefix' )
-                       );
+                       $db = DatabaseBase::factory( 'mysql', array(
+                               'host' => $this->getVar( 'wgDBserver' ),
+                               'user' => $this->getVar( '_InstallUser' ),
+                               'password' => $this->getVar( '_InstallPassword' ),
+                               'dbname' => false,
+                               'flags' => 0,
+                               'tablePrefix' => $this->getVar( 'wgDBprefix' ) ) );
                        $status->value = $db;
                } catch ( DBConnectionError $e ) {
                        $status->fatal( 'config-connection-error', $e->getMessage() );
@@ -436,14 +435,13 @@ class MysqlInstaller extends DatabaseInstaller {
                if ( !$create ) {
                        // Test the web account
                        try {
-                               new DatabaseMysql(
-                                       $this->getVar( 'wgDBserver' ),
-                                       $this->getVar( 'wgDBuser' ),
-                                       $this->getVar( 'wgDBpassword' ),
-                                       false,
-                                       0,
-                                       $this->getVar( 'wgDBprefix' )
-                               );
+                               $db = DatabaseBase::factory( 'mysql', array(
+                                       'host' => $this->getVar( 'wgDBserver' ),
+                                       'user' => $this->getVar( 'wgDBuser' ),
+                                       'password' => $this->getVar( 'wgDBpassword' ),
+                                       'dbname' => false,
+                                       'flags' => 0,
+                                       'tablePrefix' => $this->getVar( 'wgDBprefix' ) ) );
                        } catch ( DBConnectionError $e ) {
                                return Status::newFatal( 'config-connection-error', $e->getMessage() );
                        }
@@ -514,14 +512,13 @@ class MysqlInstaller extends DatabaseInstaller {
                if ( $this->getVar( '_CreateDBAccount' ) ) {
                        // Before we blindly try to create a user that already has access,
                        try { // first attempt to connect to the database
-                               new DatabaseMysql(
-                                       $server,
-                                       $dbUser,
-                                       $password,
-                                       false,
-                                       0,
-                                       $this->getVar( 'wgDBprefix' )
-                               );
+                               $db = DatabaseBase::factory( 'mysql', array(
+                                       'host' => $server,
+                                       'user' => $dbUser,
+                                       'password' => $password,
+                                       'dbname' => false,
+                                       'flags' => 0,
+                                       'tablePrefix' => $this->getVar( 'wgDBprefix' ) ) );
                                $grantableNames[] = $this->buildFullUserName( $dbUser, $server );
                                $tryToCreate = false;
                        } catch ( DBConnectionError $e ) {
index 1d2c8a5..93ea773 100644 (file)
@@ -155,8 +155,8 @@ class MysqlUpdater extends DatabaseUpdater {
                        // 1.15
                        array( 'doUniquePlTlIl' ),
                        array( 'addTable', 'change_tag',                        'patch-change_tag.sql' ),
-                       /* array( 'addTable', 'tag_summary',                       'patch-change_tag.sql' ), */
-                       /* array( 'addTable', 'valid_tag',                         'patch-change_tag.sql' ), */
+                       array( 'addTable', 'tag_summary',                       'patch-tag_summary.sql' ),
+                       array( 'addTable', 'valid_tag',                         'patch-valid_tag.sql' ),
 
                        // 1.16
                        array( 'addTable', 'user_properties',                   'patch-user_properties.sql' ),
@@ -231,7 +231,8 @@ class MysqlUpdater extends DatabaseUpdater {
                        // 1.22
                        array( 'doIwlinksIndexNonUnique' ),
                        array( 'addIndex', 'iwlinks', 'iwl_prefix_from_title',  'patch-iwlinks-from-title-index.sql' ),
-                       array( 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ),
+                       array( 'addField',      'archive',      'ar_id',                    'patch-archive-ar_id.sql' ),
+                       array( 'addField',      'externallinks',  'el_id',  'patch-externallinks-el_id.sql' ),
                );
        }
 
@@ -248,11 +249,8 @@ class MysqlUpdater extends DatabaseUpdater {
                        return true;
                }
 
-               $tableName = $this->db->tableName( $table );
-               $res = $this->db->query( "SELECT $field FROM $tableName LIMIT 0", __METHOD__ );
-               $flags = explode( ' ', mysql_field_flags( $res->result, 0 ) );
-
-               if ( in_array( 'binary', $flags ) ) {
+               $fieldInfo = $this->db->fieldInfo( $table, $field );
+               if ( $fieldInfo->isBinary() ) {
                        $this->output( "...$table table has correct $field encoding.\n" );
                } else {
                        $this->applyPatch( $patchFile, false, "Fixing $field encoding on $table table" );
index f3f86eb..8484189 100644 (file)
@@ -73,7 +73,9 @@ class OracleUpdater extends DatabaseUpdater {
                        array( 'addField',      'revision',     'rev_content_format',           'patch-revision-rev_content_format.sql' ),
                        array( 'addField',      'revision',     'rev_content_model',            'patch-revision-rev_content_model.sql' ),
                        array( 'addField',      'archive',      'ar_content_format',            'patch-archive-ar_content_format.sql' ),
-                       array( 'addField',      'archive',      'ar_content_model',                 'patch-archive-ar_content_model.sql' ),
+                       array( 'addField',      'archive',      'ar_content_model',             'patch-archive-ar_content_model.sql' ),
+                       array( 'addField',      'archive',      'ar_id',                        'patch-archive-ar_id.sql' ),
+                       array( 'addField',      'externallinks',        'el_id',                'patch-externallinks-el_id.sql' ),
                        array( 'addField',      'page',     'page_content_model',               'patch-page-page_content_model.sql' ),
                        array( 'dropField', 'site_stats', 'ss_admins',  'patch-ss_admins.sql' ),
                        array( 'dropField', 'recentchanges', 'rc_moved_to_title', 'patch-rc_moved.sql' ),
index 40c8126..f0e4aec 100644 (file)
@@ -103,8 +103,8 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'addPgField', 'archive',       'ar_content_model',     'TEXT' ),
                        array( 'addPgField', 'archive',       'ar_content_format',    'TEXT' ),
                        array( 'addPgField', 'categorylinks', 'cl_sortkey_prefix',    "TEXT NOT NULL DEFAULT ''"),
-                       array( 'addPgField', 'categorylinks', 'cl_collation',         "TEXT NOT NULL DEFAULT 0"),
-                       array( 'addPgField', 'categorylinks', 'cl_type',              "TEXT NOT NULL DEFAULT 'page'"),
+                       array( 'addPgField', 'categorylinks', 'cl_collation',         "TEXT NOT NULL DEFAULT 0" ),
+                       array( 'addPgField', 'categorylinks', 'cl_type',              "TEXT NOT NULL DEFAULT 'page'" ),
                        array( 'addPgField', 'image',         'img_sha1',             "TEXT NOT NULL DEFAULT ''" ),
                        array( 'addPgField', 'ipblocks',      'ipb_allow_usertalk',   'SMALLINT NOT NULL DEFAULT 0' ),
                        array( 'addPgField', 'ipblocks',      'ipb_anon_only',        'SMALLINT NOT NULL DEFAULT 0' ),
@@ -159,6 +159,9 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'addPgField', 'job',           'job_token',            "TEXT NOT NULL DEFAULT ''" ),
                        array( 'addPgField', 'job',           'job_token_timestamp',  "TIMESTAMPTZ" ),
                        array( 'addPgField', 'job',           'job_sha1',             "TEXT NOT NULL DEFAULT ''" ),
+                       array( 'addPgField', 'archive',       'ar_id',                "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('archive_ar_id_seq')" ),
+                       array( 'addPgField', 'externallinks', 'el_id',                "INTEGER NOT NULL PRIMARY KEY DEFAULT nextval('externallinks_el_id_seq')" ),
+
 
                        # type changes
                        array( 'changeField', 'archive',       'ar_deleted',      'smallint', '' ),
@@ -355,9 +358,6 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'addInterwikiType' ),
                        # end
                        array( 'tsearchFixes' ),
-
-                       // 1.22
-                       array( 'addPgField', 'recentchanges', 'rc_source',            "TEXT NOT NULL DEFAULT ''" ),
                );
        }
 
index 978a784..0b572f6 100644 (file)
@@ -39,8 +39,8 @@ class SqliteUpdater extends DatabaseUpdater {
 
                        // 1.15
                        array( 'addTable', 'change_tag',                        'patch-change_tag.sql' ),
-                       array( 'addTable', 'tag_summary',                       'patch-change_tag.sql' ),
-                       array( 'addTable', 'valid_tag',                         'patch-change_tag.sql' ),
+                       array( 'addTable', 'tag_summary',                       'patch-tag_summary.sql' ),
+                       array( 'addTable', 'valid_tag',                         'patch-valid_tag.sql' ),
 
                        // 1.16
                        array( 'addTable', 'user_properties',                   'patch-user_properties.sql' ),
@@ -95,7 +95,6 @@ class SqliteUpdater extends DatabaseUpdater {
                        array( 'addField', 'archive',  'ar_content_format',  'patch-archive-ar_content_format.sql' ),
                        array( 'addField', 'archive',  'ar_content_model',   'patch-archive-ar_content_model.sql' ),
                        array( 'addField', 'page',     'page_content_model', 'patch-page-page_content_model.sql' ),
-
                        array( 'dropField', 'site_stats',    'ss_admins',         'patch-drop-ss_admins.sql' ),
                        array( 'dropField', 'recentchanges', 'rc_moved_to_title', 'patch-rc_moved.sql' ),
                        array( 'addTable', 'sites',                            'patch-sites.sql' ),
@@ -109,9 +108,8 @@ class SqliteUpdater extends DatabaseUpdater {
                        array( 'addIndex', 'page_props', 'pp_propname_page',  'patch-page_props-propname-page-index.sql' ),
                        array( 'addIndex', 'image', 'img_media_mime', 'patch-img_media_mime-index.sql' ),
                        array( 'addIndex', 'iwlinks', 'iwl_prefix_from_title',  'patch-iwlinks-from-title-index.sql' ),
-
-                       // 1.22
-                       array( 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ),
+                       array( 'addField', 'archive', 'ar_id', 'patch-archive-ar_id.sql' ),
+                       array( 'addField', 'externallinks', 'el_id', 'patch-externallinks-el_id.sql' ),
                );
        }
 
index 10f2c97..6556ee8 100644 (file)
@@ -40,7 +40,6 @@ abstract class JobQueue {
        protected $dupCache;
 
        const QOS_ATOMIC = 1; // integer; "all-or-nothing" job insertions
-       const QoS_Atomic = 1; // integer; "all-or-nothing" job insertions (b/c)
 
        const ROOTJOB_TTL = 2419200; // integer; seconds to remember root jobs (28 days)
 
index d788c98..d3ce164 100644 (file)
  *
  * If used for performance, then $wgMainCacheType should be set to memcached/redis.
  * Note that "fifo" cannot be used for the ordering, since the data is distributed.
- * One can still use "timestamp" instead, as in "roughly timestamp ordered".
+ * One can still use "timestamp" instead, as in "roughly timestamp ordered". Also,
+ * queue classes used by this should ignore down servers (with TTL) to avoid slowness.
  *
  * @ingroup JobQueue
  * @since 1.22
  */
 class JobQueueFederated extends JobQueue {
-       /** @var Array (wiki ID => section name) */
-       protected $sectionsByWiki = array();
-       /** @var Array (section name => (partition name => weight)) */
-       protected $partitionsBySection = array();
-       /** @var Array (section name => config array) */
-       protected $configByPartition = array();
-       /** @var Array (partition names => integer) */
-       protected $partitionsNoPush = array();
-
-       /** @var HashRing */
-       protected $partitionRing;
-       /** @var Array (partition name => JobQueue) */
+       /** @var Array (partition name => weight) reverse sorted by weight */
+       protected $partitionMap = array();
+       /** @var Array (partition name => JobQueue) reverse sorted by weight */
        protected $partitionQueues = array();
+       /** @var HashRing */
+       protected $partitionPushRing;
        /** @var BagOStuff */
        protected $cache;
 
@@ -82,36 +76,41 @@ class JobQueueFederated extends JobQueue {
         */
        protected function __construct( array $params ) {
                parent::__construct( $params );
-               $this->sectionsByWiki = isset( $params['sectionsByWiki'] )
-                       ? $params['sectionsByWiki']
-                       : array(); // all in "default" section
-               $this->partitionsBySection = $params['partitionsBySection'];
-               $this->configByPartition = $params['configByPartition'];
+               $section = isset( $params['sectionsByWiki'][$this->wiki] )
+                       ? $params['sectionsByWiki'][$this->wiki]
+                       : 'default';
+               if ( !isset( $params['partitionsBySection'][$section] ) ) {
+                       throw new MWException( "No configuration for section '$section'." );
+               }
+               // Get the full partition map
+               $this->partitionMap = $params['partitionsBySection'][$section];
+               arsort( $this->partitionMap, SORT_NUMERIC );
+               // Get the partitions jobs can actually be pushed to
+               $partitionPushMap = $this->partitionMap;
                if ( isset( $params['partitionsNoPush'] ) ) {
-                       $this->partitionsNoPush = array_flip( $params['partitionsNoPush'] );
+                       foreach ( $params['partitionsNoPush'] as $partition ) {
+                               unset( $partitionPushMap[$partition] );
+                       }
                }
+               // Get the config to pass to merge into each partition queue config
                $baseConfig = $params;
                foreach ( array( 'class', 'sectionsByWiki',
                        'partitionsBySection', 'configByPartition', 'partitionsNoPush' ) as $o )
                {
                        unset( $baseConfig[$o] );
                }
-               foreach ( $this->getPartitionMap() as $partition => $w ) {
-                       if ( !isset( $this->configByPartition[$partition] ) ) {
+               // Get the partition queue objects
+               foreach ( $this->partitionMap as $partition => $w ) {
+                       if ( !isset( $params['configByPartition'][$partition] ) ) {
                                throw new MWException( "No configuration for partition '$partition'." );
                        }
                        $this->partitionQueues[$partition] = JobQueue::factory(
-                               $baseConfig + $this->configByPartition[$partition]
-                       );
+                               $baseConfig + $params['configByPartition'][$partition] );
                }
-               // Get the ring of partitions to push job de-duplication information into
-               $partitionsTry = array_diff_key(
-                       $this->getPartitionMap(),
-                       $this->partitionsNoPush
-               ); // (partition => weight)
-               $this->partitionRing = new HashRing( $partitionsTry );
+               // Get the ring of partitions to push jobs into
+               $this->partitionPushRing = new HashRing( $partitionPushMap );
                // Aggregate cache some per-queue values if there are multiple partition queues
-               $this->cache = $this->isFederated() ? wfGetMainCache() : new EmptyBagOStuff();
+               $this->cache = count( $this->partitionMap ) > 1 ? wfGetMainCache() : new EmptyBagOStuff();
        }
 
        protected function supportedOrders() {
@@ -144,7 +143,7 @@ class JobQueueFederated extends JobQueue {
                                        return false;
                                }
                        } catch ( JobQueueError $e ) {
-                               wfDebugLog( 'exception', $e->getLogMessage() );
+                               MWExceptionHandler::logException( $e );
                        }
                }
 
@@ -186,7 +185,7 @@ class JobQueueFederated extends JobQueue {
                        try {
                                $count += $queue->$method();
                        } catch ( JobQueueError $e ) {
-                               wfDebugLog( 'exception', $e->getLogMessage() );
+                               MWExceptionHandler::logException( $e );
                        }
                }
 
@@ -198,36 +197,26 @@ class JobQueueFederated extends JobQueue {
                if ( !count( $jobs ) ) {
                        return true; // nothing to do
                }
-
-               $partitionsTry = array_diff_key(
-                       $this->getPartitionMap(),
-                       $this->partitionsNoPush
-               ); // (partition => weight)
-
+               // Local ring variable that may be changed to point to a new ring on failure
+               $partitionRing = $this->partitionPushRing;
                // Try to insert the jobs and update $partitionsTry on any failures
-               $jobsLeft = $this->tryJobInsertions( $jobs, $partitionsTry, $flags );
+               $jobsLeft = $this->tryJobInsertions( $jobs, $partitionRing, $flags );
                if ( count( $jobsLeft ) ) { // some jobs failed to insert?
                        // Try to insert the remaning jobs once more, ignoring the bad partitions
-                       return !count( $this->tryJobInsertions( $jobsLeft, $partitionsTry, $flags ) );
-               } else {
-                       return true;
+                       return !count( $this->tryJobInsertions( $jobsLeft, $partitionRing, $flags ) );
                }
+               return true;
        }
 
        /**
         * @param array $jobs
-        * @param array $partitionsTry
+        * @param HashRing $partitionRing
         * @param integer $flags
         * @return array List of Job object that could not be inserted
         */
-       protected function tryJobInsertions( array $jobs, array &$partitionsTry, $flags ) {
-               if ( !count( $partitionsTry ) ) {
-                       return $jobs; // can't insert anything
-               }
-
+       protected function tryJobInsertions( array $jobs, HashRing &$partitionRing, $flags ) {
                $jobsLeft = array();
 
-               $partitionRing = new HashRing( $partitionsTry );
                // Because jobs are spread across partitions, per-job de-duplication needs
                // to use a consistent hash to avoid allowing duplicate jobs per partition.
                // When inserting a batch of de-duplicated jobs, QOS_ATOMIC is disregarded.
@@ -253,39 +242,42 @@ class JobQueueFederated extends JobQueue {
                foreach ( $uJobsByPartition as $partition => $jobBatch ) {
                        $queue = $this->partitionQueues[$partition];
                        try {
-                               $ok = $queue->doBatchPush( $jobBatch, $flags );
+                               $ok = $queue->doBatchPush( $jobBatch, $flags | self::QOS_ATOMIC );
                        } catch ( JobQueueError $e ) {
                                $ok = false;
-                               wfDebugLog( 'exception', $e->getLogMessage() );
+                               MWExceptionHandler::logException( $e );
                        }
                        if ( $ok ) {
                                $key = $this->getCacheKey( 'empty' );
                                $this->cache->set( $key, 'false', JobQueueDB::CACHE_TTL_LONG );
                        } else {
-                               unset( $partitionsTry[$partition] ); // blacklist partition
+                               $partitionRing = $partitionRing->newWithoutLocation( $partition ); // blacklist
+                               if ( !$partitionRing ) {
+                                       throw new JobQueueError( "Could not insert job(s), all partitions are down." );
+                               }
                                $jobsLeft = array_merge( $jobsLeft, $jobBatch ); // not inserted
                        }
                }
+
                // Insert the jobs that are not de-duplicated into the queues...
                foreach ( $nuJobBatches as $jobBatch ) {
-                       $partition = ArrayUtils::pickRandom( $partitionsTry );
-                       if ( $partition === false ) { // all partitions at 0 weight?
-                               $jobsLeft = array_merge( $jobsLeft, $jobBatch ); // not inserted
+                       $partition = ArrayUtils::pickRandom( $partitionRing->getLocationWeights() );
+                       $queue = $this->partitionQueues[$partition];
+                       try {
+                               $ok = $queue->doBatchPush( $jobBatch, $flags | self::QOS_ATOMIC );
+                       } catch ( JobQueueError $e ) {
+                               $ok = false;
+                               MWExceptionHandler::logException( $e );
+                       }
+                       if ( $ok ) {
+                               $key = $this->getCacheKey( 'empty' );
+                               $this->cache->set( $key, 'false', JobQueueDB::CACHE_TTL_LONG );
                        } else {
-                               $queue = $this->partitionQueues[$partition];
-                               try {
-                                       $ok = $queue->doBatchPush( $jobBatch, $flags );
-                               } catch ( JobQueueError $e ) {
-                                       $ok = false;
-                                       wfDebugLog( 'exception', $e->getLogMessage() );
-                               }
-                               if ( $ok ) {
-                                       $key = $this->getCacheKey( 'empty' );
-                                       $this->cache->set( $key, 'false', JobQueueDB::CACHE_TTL_LONG );
-                               } else {
-                                       unset( $partitionsTry[$partition] ); // blacklist partition
-                                       $jobsLeft = array_merge( $jobsLeft, $jobBatch ); // not inserted
+                               $partitionRing = $partitionRing->newWithoutLocation( $partition ); // blacklist
+                               if ( !$partitionRing ) {
+                                       throw new JobQueueError( "Could not insert job(s), all partitions are down." );
                                }
+                               $jobsLeft = array_merge( $jobsLeft, $jobBatch ); // not inserted
                        }
                }
 
@@ -300,7 +292,7 @@ class JobQueueFederated extends JobQueue {
                        return false;
                }
 
-               $partitionsTry = $this->getPartitionMap(); // (partition => weight)
+               $partitionsTry = $this->partitionMap; // (partition => weight)
 
                while ( count( $partitionsTry ) ) {
                        $partition = ArrayUtils::pickRandom( $partitionsTry );
@@ -312,7 +304,7 @@ class JobQueueFederated extends JobQueue {
                                $job = $queue->pop();
                        } catch ( JobQueueError $e ) {
                                $job = false;
-                               wfDebugLog( 'exception', $e->getLogMessage() );
+                               MWExceptionHandler::logException( $e );
                        }
                        if ( $job ) {
                                $job->metadata['QueuePartition'] = $partition;
@@ -335,10 +327,10 @@ class JobQueueFederated extends JobQueue {
 
        protected function doIsRootJobOldDuplicate( Job $job ) {
                $params = $job->getRootJobParams();
-               $partitions = $this->partitionRing->getLocations( $params['rootJobSignature'], 2 );
+               $partitions = $this->partitionPushRing->getLocations( $params['rootJobSignature'], 2 );
                try {
                        return $this->partitionQueues[$partitions[0]]->doIsRootJobOldDuplicate( $job );
-               } catch ( MWException $e ) {
+               } catch ( JobQueueError $e ) {
                        if ( isset( $partitions[1] ) ) { // check fallback partition
                                return $this->partitionQueues[$partitions[1]]->doIsRootJobOldDuplicate( $job );
                        }
@@ -348,10 +340,10 @@ class JobQueueFederated extends JobQueue {
 
        protected function doDeduplicateRootJob( Job $job ) {
                $params = $job->getRootJobParams();
-               $partitions = $this->partitionRing->getLocations( $params['rootJobSignature'], 2 );
+               $partitions = $this->partitionPushRing->getLocations( $params['rootJobSignature'], 2 );
                try {
                        return $this->partitionQueues[$partitions[0]]->doDeduplicateRootJob( $job );
-               } catch ( MWException $e ) {
+               } catch ( JobQueueError $e ) {
                        if ( isset( $partitions[1] ) ) { // check fallback partition
                                return $this->partitionQueues[$partitions[1]]->doDeduplicateRootJob( $job );
                        }
@@ -364,7 +356,7 @@ class JobQueueFederated extends JobQueue {
                        try {
                                $queue->doDelete();
                        } catch ( JobQueueError $e ) {
-                               wfDebugLog( 'exception', $e->getLogMessage() );
+                               MWExceptionHandler::logException( $e );
                        }
                }
        }
@@ -374,7 +366,7 @@ class JobQueueFederated extends JobQueue {
                        try {
                                $queue->waitForBackups();
                        } catch ( JobQueueError $e ) {
-                               wfDebugLog( 'exception', $e->getLogMessage() );
+                               MWExceptionHandler::logException( $e );
                        }
                }
        }
@@ -422,32 +414,44 @@ class JobQueueFederated extends JobQueue {
        }
 
        public function getCoalesceLocationInternal() {
-               return "JobQueueFederated:wiki:" . $this->wiki;
+               return "JobQueueFederated:wiki:{$this->wiki}" .
+                       sha1( serialize( array_keys( $this->partitionMap ) ) );
        }
 
        protected function doGetSiblingQueuesWithJobs( array $types ) {
                $result = array();
                foreach ( $this->partitionQueues as $queue ) {
-                       $nonEmpty = $queue->doGetSiblingQueuesWithJobs( $types );
-                       if ( is_array( $nonEmpty ) ) {
-                               $result = array_merge( $result, $nonEmpty );
-                       } else {
-                               return null; // not supported on all partitions; bail
+                       try {
+                               $nonEmpty = $queue->doGetSiblingQueuesWithJobs( $types );
+                               if ( is_array( $nonEmpty ) ) {
+                                       $result = array_unique( array_merge( $result, $nonEmpty ) );
+                               } else {
+                                       return null; // not supported on all partitions; bail
+                               }
+                               if ( count( $result ) == count( $types ) ) {
+                                       break; // short-circuit
+                               }
+                       } catch ( JobQueueError $e ) {
+                               MWExceptionHandler::logException( $e );
                        }
                }
-               return array_values( array_unique( $result ) );
+               return array_values( $result );
        }
 
        protected function doGetSiblingQueueSizes( array $types ) {
                $result = array();
                foreach ( $this->partitionQueues as $queue ) {
-                       $sizes = $queue->doGetSiblingQueueSizes( $types );
-                       if ( is_array( $sizes ) ) {
-                               foreach ( $sizes as $type => $size ) {
-                                       $result[$type] = isset( $result[$type] ) ? $result[$type] + $size : $size;
+                       try {
+                               $sizes = $queue->doGetSiblingQueueSizes( $types );
+                               if ( is_array( $sizes ) ) {
+                                       foreach ( $sizes as $type => $size ) {
+                                               $result[$type] = isset( $result[$type] ) ? $result[$type] + $size : $size;
+                                       }
+                               } else {
+                                       return null; // not supported on all partitions; bail
                                }
-                       } else {
-                               return null; // not supported on all partitions; bail
+                       } catch ( JobQueueError $e ) {
+                               MWExceptionHandler::logException( $e );
                        }
                }
                return $result;
@@ -459,26 +463,6 @@ class JobQueueFederated extends JobQueue {
                }
        }
 
-       /**
-        * @return Array Map of (partition name => weight)
-        */
-       protected function getPartitionMap() {
-               $section = isset( $this->sectionsByWiki[$this->wiki] )
-                       ? $this->sectionsByWiki[$this->wiki]
-                       : 'default';
-               if ( !isset( $this->partitionsBySection[$section] ) ) {
-                       throw new MWException( "No configuration for section '$section'." );
-               }
-               return $this->partitionsBySection[$section];
-       }
-
-       /**
-        * @return bool The queue is actually split up across multiple queue partitions
-        */
-       protected function isFederated() {
-               return ( count( $this->getPartitionMap() ) > 1 );
-       }
-
        /**
         * @return string
         */
index 0603a9b..221a630 100644 (file)
@@ -355,7 +355,7 @@ class Parser {
                 * to internalParse() which does all the real work.
                 */
 
-               global $wgUseTidy, $wgAlwaysUseTidy;
+               global $wgUseTidy, $wgAlwaysUseTidy, $wgShowHostnames;
                $fname = __METHOD__ . '-' . wfGetCaller();
                wfProfileIn( __METHOD__ );
                wfProfileIn( $fname );
@@ -532,6 +532,9 @@ class Parser {
                        wfRunHooks( 'ParserLimitReportPrepare', array( $this, $this->mOutput ) );
 
                        $limitReport = "NewPP limit report\n";
+                       if ( $wgShowHostnames ) {
+                               $limitReport .= 'Parsed by ' . wfHostname() . "\n";
+                       }
                        foreach ( $this->mOutput->getLimitReportData() as $key => $value ) {
                                if ( wfRunHooks( 'ParserLimitReportFormat',
                                        array( $key, $value, &$limitReport, false, false )
diff --git a/includes/specials/SpecialBlockme.php b/includes/specials/SpecialBlockme.php
deleted file mode 100644 (file)
index c3d6080..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * Implements Special:Blockme
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup SpecialPage
- */
-
-/**
- * A special page called by proxyCheck.php to block open proxies
- *
- * @ingroup SpecialPage
- */
-class SpecialBlockme extends UnlistedSpecialPage {
-
-       function __construct() {
-               parent::__construct( 'Blockme' );
-       }
-
-       function execute( $par ) {
-               global $wgBlockOpenProxies, $wgProxyKey;
-
-               $this->setHeaders();
-               $this->outputHeader();
-
-               $ip = $this->getRequest()->getIP();
-               if ( !$wgBlockOpenProxies || $this->getRequest()->getText( 'ip' ) != md5( $ip . $wgProxyKey ) ) {
-                       $this->getOutput()->addWikiMsg( 'proxyblocker-disabled' );
-
-                       return;
-               }
-
-               $user = User::newFromName( $this->msg( 'proxyblocker' )->inContentLanguage()->text() );
-               # FIXME: newFromName could return false on a badly configured wiki.
-               if ( !$user->isLoggedIn() ) {
-                       $user->addToDatabase();
-               }
-
-               $block = new Block();
-               $block->setTarget( $ip );
-               $block->setBlocker( $user );
-               $block->mReason = $this->msg( 'proxyblockreason' )->inContentLanguage()->text();
-
-               $block->insert();
-
-               $this->getOutput()->addWikiMsg( 'proxyblocksuccess' );
-       }
-
-       protected function getGroupName() {
-               return 'other';
-       }
-}
index 65aa07e..5d9e554 100644 (file)
@@ -974,7 +974,7 @@ class ContribsPager extends ReverseChronologicalPager {
                        # Show user names for /newbies as there may be different users.
                        # Note that we already excluded rows with hidden user names.
                        if ( $this->contribs == 'newbie' ) {
-                               $userlink = ' . . ' . Linker::userLink( $rev->getUser(), $rev->getUserText() );
+                               $userlink = ' . . ' . $lang->getDirMark() . Linker::userLink( $rev->getUser(), $rev->getUserText() );
                                $userlink .= ' ' . $this->msg( 'parentheses' )->rawParams(
                                        Linker::userTalkLink( $rev->getUser(), $rev->getUserText() ) )->escaped() . ' ';
                        } else {
index 79ddb6a..ccf9b1e 100644 (file)
@@ -1263,7 +1263,10 @@ class ConverterRule {
                $variants = $this->mConverter->mVariants;
                $varsep_pattern = $this->mConverter->getVarSeparatorPattern();
 
+               // Split according to $varsep_pattern, but ignore semicolons from HTML entities
+               $rules = preg_replace( '/(&[#a-zA-Z0-9]+);/', "$1\x01", $rules );
                $choice = preg_split( $varsep_pattern, $rules );
+               $choice = str_replace( "\x01", ';', $choice );
 
                foreach ( $choice as $c ) {
                        $v = explode( ':', $c, 2 );
index 06c9e60..bdc0268 100644 (file)
@@ -164,12 +164,12 @@ $messages = array(
 'tog-hidepatrolled' => 'Peusom neuandam teupatroli bak neuubah paléng barô',
 'tog-newpageshidepatrolled' => 'Peusom ôn teupatroli nibak dapeuta ôn barô',
 'tog-extendwatchlist' => 'Peuhah dapeuta keunalön keu peuleumah ban dum neuubah, kon nyang paléng barô mantöng',
-'tog-usenewrc' => 'Nguy neuleumah neuubah barô tingkat lanjut (peureulèë JavaScript)',
+'tog-usenewrc' => 'Peusaho neuandam bak neuleumah neuubah barô ngon dapeuta keunalon meunurot ôn',
 'tog-numberheadings' => 'Bôh numbô nan keudroë',
-'tog-showtoolbar' => 'Peuleumah <em>toolbar</em> (bateuëng alat) andam',
+'tog-showtoolbar' => 'Peuleumah bateuëng alat andam',
 'tog-editondblclick' => 'Andam ôn ngon duwa go teugon',
 'tog-editsection' => 'Peujeuet andam bideueng rot hubong [andam]',
-'tog-editsectiononrightclick' => 'Peujeuet andam bideueng ngon teugon blah uneun bak nan bideueng (peureulee JavaScript)',
+'tog-editsectiononrightclick' => 'Peujeuet andam bideueng ngon teugon blah uneun bak nan bideueng',
 'tog-showtoc' => 'Peuleumah dapeuta asoe (keu on-on nyang na leubeh nibak 3 boh aneuk ulee)',
 'tog-rememberpassword' => 'Ingat lôn tamong bak peuramban nyoë (keu paleng trep $1 {{PLURAL:$1|uroë|uroë}})',
 'tog-watchcreations' => 'Tamah halaman nyang lonpeugot u dapeuta keunalon',
@@ -187,7 +187,7 @@ $messages = array(
 'tog-shownumberswatching' => 'Peuleumah jumeulah ureueng kalon',
 'tog-oldsig' => 'Tanda jaroe jinoe:',
 'tog-fancysig' => 'Peujeuet tanda jaroe sibagoe naseukah wiki (hana hubong keudroe)',
-'tog-uselivepreview' => 'Nguy peuleumah hase langsong (JavaScript) (baci)',
+'tog-uselivepreview' => 'Nguy peuleumah hase langsong (baci)',
 'tog-forceeditsummary' => 'Peuingat lon meunyo plok neuringkaih neuandam mantong soh',
 'tog-watchlisthideown' => 'Peusöm nyang lôn andam nibak dapeuta keunalön',
 'tog-watchlisthidebots' => 'Peusöm nyang teu andam nibak sagoö nyang bak dapeuta keunalön',
@@ -264,9 +264,18 @@ $messages = array(
 'oct' => 'Siplôh',
 'nov' => 'Siblaih',
 'dec' => 'Duwa Blaih',
-'january-date' => 'Buleuën Sa',
-'february-date' => 'Buleuën Duwa',
-'march-date' => 'Buleuën Lhèë',
+'january-date' => '$1 Buleuën Sa',
+'february-date' => '$1 Buleuën Duwa',
+'march-date' => '$1 Buleuën Lhèë',
+'april-date' => '$1 Buleuën Peuët',
+'may-date' => '$1 Buleuën Limong',
+'june-date' => '$1 Buleuën Nam',
+'july-date' => '$1 Buleuën Tujôh',
+'august-date' => '$1 Buleuën Lapan',
+'september-date' => '$1 Buleuën Sikureuëng',
+'october-date' => '$1 Buleuën Siplôh',
+'november-date' => '$ Buleuën Siblaih',
+'december-date' => '$1 Buleuën Duwa Blaih',
 
 # Categories related messages
 'pagecategories' => '{{PLURAL:$1|Kawan|Kawan}}',
@@ -292,7 +301,7 @@ $messages = array(
 'newwindow' => '(peuhah bak tingkap barô)',
 'cancel' => 'Peubateuë',
 'moredotdotdot' => 'Lom...',
-'morenotlisted' => 'Lom...',
+'morenotlisted' => 'Dapeuta nyoe hana leungkap',
 'mypage' => 'Ôn',
 'mytalk' => 'Marit',
 'anontalk' => 'Peugah haba IP nyoë.',
@@ -395,7 +404,7 @@ $1",
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Bhaih {{SITENAME}}',
 'aboutpage' => 'Project:Bhaih',
-'copyright' => 'Asoë nyang na saban ngön',
+'copyright' => "Asoë na meunurot $1 keucuali meunyo na hay la'en nyang geupeugah",
 'copyrightpage' => '{{ns:project}}:Hak karang',
 'currentevents' => 'Haba barô',
 'currentevents-url' => 'Project:Haba barô',
@@ -476,6 +485,10 @@ Dapeuta on kusuih nyang sah jeuet neu'eu bak [[Special:SpecialPages|{{int:specia
 'error' => 'Seunalah',
 'databaseerror' => 'Kesalahan basis data',
 'databaseerror-text' => 'Saboh salah bak nè data ka teujadi. Nyoë meuhat na nyang han paih bak peukakaih droëneuh',
+'databaseerror-textcl' => 'Teunanyong basis data teungoh kacho',
+'databaseerror-query' => 'Teunanyong: $1',
+'databaseerror-function' => 'Guna: $1',
+'databaseerror-error' => 'Salah: $1',
 'laggedslavemode' => 'Peuneugah: On nyoe kadang hana neuubah baro',
 'readonly' => 'Basis data geurok',
 'enterlockreason' => 'Pasoe daleh neurok ngon pajan jeuet geupeuhah',
@@ -508,9 +521,10 @@ Kadang ka na soe sampoh.',
 'cannotdelete-title' => 'H\'an jeuet sampoh on "$1"',
 'delete-hook-aborted' => "Seunampoh geupeubateue le kaw'et parser.
 Hana jeuneulaih.",
+'no-null-revision' => 'H\'an jeuet peugot revisi null baro keu halaman "$1"',
 'badtitle' => 'Nan hana sah',
 'badtitletext' => 'Nan ôn nyang neulakèë hana sah, soh, atawa nan antarabahsa atawa antarawiki nyang salah sambông.',
-'perfcached' => 'Data di yup nyoe geucok nibak peuniyoh ngon kadang kon data baro. {{PLURAL:$1||}}$1 hase maksimum na bak beuen.',
+'perfcached' => 'Data di yup nyoe geupeusom ngon kadang kon data baro. {{PLURAL:$1|saboh hase|$1 hase}} maksimum na lam beujana.',
 'perfcachedts' => 'Data di yup nyoe geupeusom, ngon geupeubaro keuneulheueh bak $1. {{PLURAL:$1||}}$1 hase maksimal na lam beuen.',
 'querypage-no-updates' => "Beunaro keu on nyoe hat nyoe teungoh h'an jeuet.
 Data sinoe h'an geupasoe ulang.",
index 3c723b7..efb88bc 100644 (file)
@@ -370,8 +370,8 @@ Getæl gengra syndrigra trameta cann man findan be [[Special:SpecialPages|þǣm
 'cascadeprotected' => 'Þes trament wæs geborgen wiþ adihtunge, for þǣm þe hē is befangen in þissum {{PLURAL:$1|tramente, þe is| tramentum, þe sind}} geborgen settum wyrcende þǣm cyre "cascading": $2',
 
 # Virus scanner
-'virus-badscanner' => "Јастыра конфигурация: Јарты јок сканер ''$1''",
-'virus-unknownscanner' => 'Јарты јок антивирус:',
+'virus-badscanner' => '',
+'virus-unknownscanner' => '',
 
 # Login and logout pages
 'logouttext' => "'''Þū eart nū ūtmeldod.'''
@@ -401,8 +401,8 @@ Cnāw þæt sume trametas mihten gīet wesan geīwde swā þū wǣre gīet inmel
 'logout' => 'Ūtmeldian',
 'userlogout' => 'Ūtmeldian',
 'notloggedin' => 'Nā ingemeldod',
-'userlogin-noaccount' => 'Слерде аккаунт јок по?',
-'userlogin-joinproject' => '{{SITENAME}} кирер',
+'userlogin-noaccount' => '',
+'userlogin-joinproject' => '',
 'nologin' => 'Næfst þū reccinge? $1',
 'nologinlink' => 'Scieppan reccinge',
 'createaccount' => 'Scieppan reccinge',
@@ -497,12 +497,12 @@ Gif þū hider be misfēnge cōme, cnoca þīnes webbsēcendes '''on bæc''' cn
 'editingold' => "'''WARNUNG: Þū adihtest ealde fadunge þisses trametes.'''
 Gif þū hine hordie, ǣnga andwendunga þā wǣron gedōn æfter þisse fadunge bēoþ sōðes forloren.",
 'yourdiff' => 'Fǣgnessa',
-'copyrightwarning2' => "Bidde behielde þæt man mæȝ ealla forðunga tō {{SITENAME}}
-ādihtan, hƿeorfan, oþþe forniman.
-Ȝif þū ne ƿille man þīn ȝeƿrit ādihtan unmildheorte, þonne hīe hēr ne forþsendan.<br />
-Þū behǣtst ēac þæt þū selfa þis ƿrite, oþþe efenlǣhtest of sumre
-folcliċum āgnunge oþþe ȝelīċum frēom horde (sēo $1 for āscungum).
-'''Ne forþsend efenlǣhtscielded ƿeorc būtan þafunge!'''",
+'copyrightwarning2' => "Bidde behielde þæt man mæg ealla forðunga tō {{SITENAME}}
+ādihtan, hweorfan, oþþe forniman.
+Gif þū ne wille man þīn gewrit ādihtan unmildheorte, þonne hīe hēr ne forþsendan.<br />
+Þū behǣtst ēac þæt þū selfa þis write, oþþe efenlǣhtest of sumre
+folclicum āgnunge oþþe gelīcum frēom horde (sēo $1 for āscungum).
+'''Ne forþsend efenlǣhtscielded weorc būtan þafunge!'''",
 'templatesused' => '{{PLURAL:$1|Þēos bysen is|Þās bysena sind}} gebrocen on þissum tramete:',
 'templatesusedpreview' => '{{PLURAL:$1|Þēos bysen is|Þās bysena sind}} gebrocen on þisre fōrebysene:',
 'template-protected' => '(geborgen)',
@@ -1168,7 +1168,7 @@ Cēos ōðerne naman lā.',
 'exif-sharpness' => 'Scearpnes',
 'exif-gpslatituderef' => 'Norþ oþþe sūþ brǣdu',
 'exif-gpslatitude' => 'Brǣdu',
-'exif-gpslongituderef' => 'Ēast oþþe ƿest lengu',
+'exif-gpslongituderef' => 'Ēast oþþe west lengu',
 'exif-gpslongitude' => 'Lengu',
 'exif-gpsmeasuremode' => 'Mētungmōd',
 'exif-gpsimgdirection' => 'Rihtung þæs biliðes',
@@ -1177,7 +1177,7 @@ Cēos ōðerne naman lā.',
 'exif-compression-1' => 'Unȝeþrycced',
 
 'exif-meteringmode-0' => 'Uncūþ',
-'exif-meteringmode-1' => 'Ȝeþēaƿisc',
+'exif-meteringmode-1' => 'Geþēawisc',
 'exif-meteringmode-6' => 'Sām',
 'exif-meteringmode-255' => 'Ōðer',
 
@@ -1212,7 +1212,7 @@ Cēos ōðerne naman lā.',
 
 # Pseudotags used for GPSLongitudeRef and GPSDestLongitudeRef
 'exif-gpslongitude-e' => 'Ēast lengu',
-'exif-gpslongitude-w' => 'Ƿest lengu',
+'exif-gpslongitude-w' => 'West lengu',
 
 # Pseudotags used for GPSTrackRef, GPSImgDirectionRef and GPSDestBearingRef
 'exif-gpsdirection-t' => 'Sōþ rihtung',
index 16cc385..40978e7 100644 (file)
@@ -1744,7 +1744,7 @@ $1",
 'email-address-validity-invalid' => 'أدخل عنوان بريد إلكتروني صالح',
 
 # User rights
-'userrights' => 'إدارة ØµÙ\84احÙ\8aات Ø§Ù\84Ù\85ستخدÙ\85',
+'userrights' => 'صلاحيات المستخدم',
 'userrights-lookup-user' => 'أدِر مجموعات المستخدم',
 'userrights-user-editname' => 'أدخل اسم مستخدم:',
 'editusergroup' => 'عدل مجموعات المستخدم',
@@ -2438,8 +2438,9 @@ $1',
 'protectedtitlestext' => 'العناوين التالية محمية ضد الإنشاء',
 'protectedtitlesempty' => 'لا توجد عناوين محمية حاليا بهذه المحددات.',
 'listusers' => 'قائمة الأعضاء',
-'listusers-editsonly' => 'اعرض المستخدمين الذين قاموا بتعديلات فقط',
+'listusers-editsonly' => 'اعرض المستخدمين الذين أجروا تعديلات فقط',
 'listusers-creationsort' => 'رتب حسب تاريخ الإنشاء',
+'listusers-desc' => 'رتب تنازليا',
 'usereditcount' => '{{PLURAL:$1|لا تعديلات|تعديل واحد|تعديلان|$1 تعديلات|$1 تعديلًا|$1 تعديل}}',
 'usercreated' => '{{GENDER:$3|أنشأه|أنشأته}} في $1 الساعة $2',
 'newpages' => 'صفحات جديدة',
@@ -2564,7 +2565,7 @@ $1',
 # Email user
 'mailnologin' => 'لا يوجد عنوان للإرسال',
 'mailnologintext' => 'يجب أن تقوم [[Special:UserLogin|بتسجيل الدخول]] وإدخال بريد إلكتروني صالح في صفحة [[Special:Preferences|التفضيلات]] لتتمكن من إرسال الرسائل لمستخدمين آخرين.',
-'emailuser' => 'إرسال رسالة لهذا المستخدم',
+'emailuser' => 'مراسلة المستخدم',
 'emailuser-title-target' => 'راسل بالبريد الإلكتروني هذا  {{GENDER:$1| المستخدم}}',
 'emailuser-title-notarget' => 'مراسلة المستخدم',
 'emailpage' => 'إرسال رسالة للمستخدم',
@@ -2746,8 +2747,8 @@ $UNWATCHURL
 'modifiedarticleprotection' => 'غير مستوى حماية "[[$1]]"',
 'unprotectedarticle' => 'أزال الحماية من "[[$1]]"',
 'movedarticleprotection' => 'نقل إعدادات الحماية من "[[$2]]" إلى "[[$1]]"',
-'protect-title' => 'ضبط Ù\85ستÙ\88Ù\89 Ø§Ù\84Ø­Ù\85اÙ\8aØ© Ù\84"$1"',
-'protect-title-notallowed' => 'عرض Ù\85ستÙ\88Ù\89 Ø§Ù\84Ø­Ù\85اÙ\8aØ© Ù\84 "$1"',
+'protect-title' => 'ضبط Ù\85ستÙ\88Ù\89 Ø­Ù\85اÙ\8aØ© "$1"',
+'protect-title-notallowed' => 'عرض Ù\85ستÙ\88Ù\89 Ø­Ù\85اÙ\8aØ© "$1"',
 'prot_1movedto2' => 'نُقلت [[$1]] إلى [[$2]]',
 'protect-badnamespace-title' => 'نطاق لا يحمى',
 'protect-badnamespace-text' => 'صفحات هذا النطاق لا يمكن حمايتها',
@@ -2888,7 +2889,7 @@ $1',
 'sp-contributions-uploads' => 'مرفوعات',
 'sp-contributions-logs' => 'سجلات',
 'sp-contributions-talk' => 'نقاش',
-'sp-contributions-userrights' => 'إدارة ØµÙ\84احÙ\8aات Ø§Ù\84Ù\85ستخدÙ\85',
+'sp-contributions-userrights' => 'صلاحيات المستخدم',
 'sp-contributions-blocked-notice' => 'هذا المستخدم ممنوع حاليا.
 إن آخر مدخلة في سجل المنع موجودة أدناه كمرجع:',
 'sp-contributions-blocked-notice-anon' => 'عنوان الأيبي هذا ممنوع حاليا.
@@ -2921,7 +2922,7 @@ $1',
 'autoblockid' => 'منع تلقائي #$1',
 'block' => 'امنع المستخدم',
 'unblock' => 'إلغاء منع مستخدم',
-'blockip' => 'منع مستخدم',
+'blockip' => 'منع المستخدم',
 'blockip-title' => 'منع مستخدم',
 'blockip-legend' => 'منع المستخدم',
 'blockiptext' => 'استخدم النموذج التالي لمنع مستخدم، أو عنوان آيبي، معين من التعديل أو إنشاء حسابات جديدة. تُستخدم هذه العملية لمنع التخريب فقط، ويجب أن تتماشى مع [[{{MediaWiki:Policy-url}}|سياسة المنع]]. أدخل تعليلاً واضحًا لسبب المنع في الخانة المخصصة لذلك (مثلاً: ذكر صفحات محددة تمّ تخريبها من قبل المستخدم).',
@@ -4372,7 +4373,10 @@ $5
 'tags-tag' => 'اسم الوسم',
 'tags-display-header' => 'الظهور في قوائم التغييرات',
 'tags-description-header' => 'وصف كامل للمعنى',
+'tags-active-header' => 'نشط؟',
 'tags-hitcount-header' => 'تغييرات موسومة',
+'tags-active-yes' => 'نعم',
+'tags-active-no' => 'لا',
 'tags-edit' => 'عدل',
 'tags-hitcount' => '{{PLURAL:$1|لا تغييرات|تغيير واحد|تغييران|$1 تغييرات|$1 تغييرا|$1 تغيير}}',
 
index 3197a42..eb9d447 100644 (file)
@@ -8,6 +8,7 @@
  * @file
  *
  * @author AiseluRB
+ * @author Alfiya55
  * @author Assele
  * @author Comp1089
  * @author Haqmar
@@ -155,12 +156,12 @@ $messages = array(
 'tog-hidepatrolled' => 'Һуңғы үҙгәртеүҙәр исемлегендә тикшерелгән үҙгәртеүҙәрҙе йәшер',
 'tog-newpageshidepatrolled' => 'Яңы биттәр исемлегендә тикшерелгән үҙгәртеүҙәрҙе йәшер',
 'tog-extendwatchlist' => 'Барлыҡ үҙгәртеүҙәрҙе үҙ эсенә алған, киңәйтелгән күҙәтеү исемлеге',
-'tog-usenewrc' => 'Һуңғы төҙәтеүҙәр һәм күҙәтеү исемлегендәге үҙгәрештерҙе төркөмдәргә булергә (JavaScript)',
+'tog-usenewrc' => 'Һуңғы төҙәтеүҙәр һәм күҙәтеү исемлегендәге үҙгәрештәрҙе төркөмдәргә бүлергә',
 'tog-numberheadings' => 'Башисемдәрҙе автоматик рәүештә номерландыр',
-'tog-showtoolbar' => 'Мөхәррирләү ваҡытында өҫкө ҡоралдар панелен күрһәтергә (JavaScript кәрәкле)',
-'tog-editondblclick' => 'Биттәрҙе ике сиртеү менән мөхәррирләргә (JavaScript кәрәкле)',
+'tog-showtoolbar' => 'Мөхәррирләгән ваҡытта өҫкө ҡоралдар панелен күрһәтергә (JavaScript кәрәк)',
+'tog-editondblclick' => 'Биттәрҙе ике сиртеү менән мөхәррирләргә',
 'tog-editsection' => 'Һәр бүлек өсөн «үҙгәртеү» һылтанмаһын күрһәтергә',
-'tog-editsectiononrightclick' => 'Ð\91үлекÑ\82Ó\99Ñ\80Ò\99е Ð¸Ñ\81емдÓ\99Ñ\80енÓ\99 Ñ\81Ñ\8bÑ\81ҡан Ð¼ÐµÐ½Ó\99н Ñ\81иÑ\80Ñ\82еп Ò¯Ò\99гÓ\99Ñ\80Ñ\82еÑ\80гÓ\99 (JavaScript ÐºÓ\99Ñ\80Ó\99кле)',
+'tog-editsectiononrightclick' => 'Ð\91үлекÑ\82Ó\99Ñ\80Ò\99е Ð¸Ñ\81емдÓ\99Ñ\80енÓ\99 Ñ\82Ó©Ñ\80Ñ\82көнөң Ñ\83Ò£ Ñ\8fÒ\93Ñ\8bна Ñ\81иÑ\80Ñ\82еп Ò¯Ò\99гÓ\99Ñ\80Ñ\82еÑ\80гÓ\99',
 'tog-showtoc' => 'Эстәлек күрһәтелһен (3-тән күп башлығы булған биттәрҙә)',
 'tog-rememberpassword' => 'Был браузерҙа (иң күбендә $1 {{PLURAL:$1|көнгә}}) иҫәп яҙыуым хәтерләнһен',
 'tog-watchcreations' => 'Мин төҙөгән биттәрҙе һәм күсергән файлдарҙы күҙәтеү исемлегенә өҫтәргә',
@@ -191,6 +192,7 @@ $messages = array(
 'tog-showhiddencats' => 'Йәшерен категорияларҙы күрһәтергә',
 'tog-norollbackdiff' => 'Кире ҡайтарыуҙан һуң версия айырмалары күрһәтелмәһен',
 'tog-useeditwarning' => 'Мөхәррирләү битенән үҙгәртеүҙәрҙе һаҡламайынса сыҡҡан ваҡытта мине киҫәтергә',
+'tog-prefershttps' => 'Системаға танытылғандан һуң һәр ваҡыт һаҡланыулы тоташыу ҡулланырға',
 
 'underline-always' => 'Һәр ваҡыт',
 'underline-never' => 'Бер ҡасан да',
@@ -291,7 +293,7 @@ $messages = array(
 'newwindow' => '(яңы биттә)',
 'cancel' => 'Бөтөрөргә',
 'moredotdotdot' => 'Дауамы...',
-'morenotlisted' => 'Башҡа бер нимә лә юҡ...',
+'morenotlisted' => 'Был исемлек тулы түгел',
 'mypage' => 'Бит',
 'mytalk' => 'Әңгәмә',
 'anontalk' => 'Был IP-адресының фекер алышыу бите',
@@ -347,6 +349,7 @@ $messages = array(
 'create-this-page' => 'Был битте яһарға',
 'delete' => 'Юҡ  итергә',
 'deletethispage' => 'Был битте юйырға',
+'undeletethispage' => 'Юйылған был битте ҡабат тергеҙеү',
 'undelete_short' => '$1 {{PLURAL:$1|үҙгәртеүҙе}} тергеҙергә',
 'viewdeleted_short' => '{{PLURAL:$1|1 юйылған үҙгәртеүҙе|$1 юйылған үҙгәртеүҙе}} ҡарау',
 'protect' => 'Һаҡларға',
@@ -393,7 +396,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => '{{SITENAME}} тураһында',
 'aboutpage' => 'Project:Тасуирлама',
-'copyright' => '$1 ярашлы эстәлеге менән һәр кем файҙалана ала.',
+'copyright' => '$1 лицензияһына ярашлы, эстәлеге менән һәр кем файҙалана ала (башҡаһы күрһәтелмәһә)',
 'copyrightpage' => '{{ns:project}}:Авторлыҡ хоҡуҡтары',
 'currentevents' => 'Ағымдағы ваҡиғалар',
 'currentevents-url' => 'Project:Ағымдағы ваҡиғалар',
@@ -477,6 +480,11 @@ $1',
 # General errors
 'error' => 'Хата',
 'databaseerror' => 'Мәғлүмәттәр базаһы хатаһы',
+'databaseerror-text' => 'Бирелмәләр базаһында хата киткән.
+Был программа тәьминәтендә хата барлығы күрһәткесе булырға мөмкин.',
+'databaseerror-textcl' => 'Бирелмәләр базаһында хата бар.',
+'databaseerror-query' => 'Һоратыу: $1',
+'databaseerror-error' => 'Хата: $1',
 'laggedslavemode' => "'''Иғтибар:''' биттә һуңғы үҙгәртеүҙәр күрһәтелмәгән булырға мөмкин.",
 'readonly' => 'Мәғлүмәттәр базаһы бикләнгән',
 'enterlockreason' => 'Ябылыу сәбәбен һәм ваҡытын белдерегеҙ.',
@@ -511,6 +519,7 @@ $1',
 'cannotdelete-title' => '"$1" битен юйып булмай',
 'delete-hook-aborted' => 'Үҙгәртеүҙе махсус-процедура кире ҡаҡты.
 Өҫтәмә аңлатма килтерелмәй.',
+'no-null-revision' => '«$1» бите өсөн яңы нулле төҙәтеү яһап булманы',
 'badtitle' => 'Ярамаған исем',
 'badtitletext' => 'Биттең һоратылған исеме дөрөҫ түгел, буш йәки телдәр араһы йәки интервики исеме яңылыш күрһәтелгән. Исемдә тыйылған символдар булыуы ла мөмкин.',
 'perfcached' => 'Был мәғлүмәттәр кэштан алынған, уларҙа һуңғы үҙгәртеүҙәр булмаҫҡа мөмкин. Кэшта иң күбе {{PLURAL:$1|язма}} һаҡлана.',
@@ -537,6 +546,10 @@ $2',
 'namespaceprotected' => '«$1» исем арауығындағы биттәрҙе мөхәррирләү өсөн хоҡуҡтарығыҙ юҡ.',
 'customcssprotected' => 'Был CSS-битте үҙгәртеү хоҡуғығыҙ юҡ, сөнки унда башҡа ҡулланыусының шәхси көйләүҙәре бар.',
 'customjsprotected' => 'Был JavaScript-битте үҙгәртеү хоҡуғығыҙ юҡ, сөнки унда башҡа ҡулланыусының шәхси көйләүҙәре бар.',
+'mycustomcssprotected' => 'Биттең был CSS-ын  мөхәррирләргә хоҡуғығыҙ юҡ.',
+'mycustomjsprotected' => 'Был биттәге  JavaScript-ты мөхәррирләргә хоҡуғығыҙ юҡ.',
+'myprivateinfoprotected' => 'Һеҙгә шәхси мәғлүмәттәрегеҙҙе үҙгәртергә рөхсәт юҡ',
+'mypreferencesprotected' => 'Һеҙҙең көйләүҙәрегеҙҙе мөхәррирләргә хоҡуғығыҙ юҡ.',
 'ns-specialprotected' => '«{{ns:special}}» исем арауығындағы биттәрҙе үҙгәртеп булмай.',
 'titleprotected' => "Был исем менән бит яһау [[User:$1|$1]] тарафынан тыйылған.
 Белдерелгән сәбәп: ''$2''.",
@@ -554,21 +567,26 @@ $2',
 'virus-unknownscanner' => 'беленмәгән антивирус:',
 
 # Login and logout pages
-'logouttext' => "'''Һеҙ иҫәп яҙыуығыҙҙан сыҡтығыҙ.'''
+'logouttext' => "'''Һеҙ эш сеансын тамамланығыҙ.'''
 
-Һеҙ {{SITENAME}} проектында аноним рәүештә дауам итә йәки <span class='plainlinks'>[$1 яңынан таныла]</span> алаһығыҙ (үҙ йәки башҡа исем менән).
-Ҡайһы бер биттәр һеҙ системала танылған һымаҡ күренергә мөмкин, уны бөтөрөү өсөн браузер кэшын таҙартығыҙ.",
+Ҡайһы бер биттәр һеҙ системаға танылмаған кеүек күренеүен дауам итер. Быны бөтөрөү өсөн браузер кэшын таҙартығыҙ.",
 'welcomeuser' => 'Рәхим итегеҙ $1!',
 'welcomecreation-msg' => 'Иҫәп яҙыуығыҙ яһалды.
 Шәхси [[Special:Preferences|{{SITENAME}} көйләүҙәрен]] үҙегеҙгә уңайлы итеп үҙгәртергә онотмағыҙ.',
 'yourname' => 'Ҡатнашыусы исеме',
 'userlogin-yourname' => 'Ҡулланыусы исеме',
+'userlogin-yourname-ph' => 'Иҫәп яҙмағыҙҙың исемен яҙығыҙ',
+'createacct-another-username-ph' => 'Иҫәп яҙмағыҙҙың исемен яҙығыҙ',
 'yourpassword' => 'Серһүҙ',
 'userlogin-yourpassword' => 'Серһүҙ',
 'userlogin-yourpassword-ph' => 'Яңы серһүҙҙе яҙығыҙ',
+'createacct-yourpassword-ph' => 'Серһүҙҙе яҙығыҙ',
 'yourpasswordagain' => 'Серһүҙҙе ҡабаттан яҙыу',
-'remembermypassword' => 'Был компьютерҙа серһүҙемде иҫләргә ($1 {{PLURAL:$1|көндән|көндән}} күп түгел)',
+'createacct-yourpasswordagain' => 'Серһүҙҙе раҫлағыҙ',
+'createacct-yourpasswordagain-ph' => 'Серһүҙҙе тағы бер тапҡыр яҙығыҙ',
+'remembermypassword' => 'Был браузерҙа (иң күбендә $1 {{PLURAL:$1|көнгә}}) иҫәп яҙыуым хәтерләнһен',
 'userlogin-remembermypassword' => 'Системала ҡалырға',
+'userlogin-signwithsecure' => 'Һаҡланыулы тоташыу',
 'yourdomainname' => 'Һеҙҙең домен',
 'password-change-forbidden' => 'Был викила серһүҙегеҙҙе үҙгәртә алмайһығыҙ.',
 'externaldberror' => 'Тышҡы мәғлүмәт базаһы менән танылғанда хата барлыҡҡа килде йәки тышҡы үҙ көйләүҙәрегеҙҙе үҙгәртер өсөн хоҡуҡтарығыҙ етәрле түгел.',
@@ -580,15 +598,37 @@ $2',
 'logout' => 'Тамамлау',
 'userlogout' => 'Тамамлау',
 'notloggedin' => 'Танылмағанһығыҙ',
+'userlogin-noaccount' => 'Иҫәп яҙмағыҙ юҡмы?',
+'userlogin-joinproject' => 'Проектҡа ҡушылырға',
 'nologin' => "Һеҙ теркәлмәгәнһегеҙме әле? '''$1'''.",
 'nologinlink' => 'Иҫәп яҙыуын булдырырға',
 'createaccount' => 'Яңы ҡатнашыусыны теркәү',
 'gotaccount' => "Әгәр Һеҙ теркәлеү үткән булһағыҙ? '''$1'''.",
 'gotaccountlink' => 'Үҙегеҙ менән таныштырығыҙ',
 'userlogin-resetlink' => 'Танылыу мәғлүмәттәрен оноттоғоҙмо?',
-'createaccountmail' => 'эл. почта буйынса',
+'userlogin-resetpassword-link' => 'Серһүҙҙе ҡабул итмәү',
+'helplogin-url' => 'Ярҙам:Системаға танылыу',
+'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Help with logging in]]Системаға инеүҙә ярҙам',
+'userlogin-loggedin' => ' Һеҙ {{GENDER:$1|$1}} булараҡ индегеҙ инде. Башҡа файҙаланыусы булып инер өсөн аҫтағы ҡалыпты ҡулланығыҙ.',
+'userlogin-createanother' => 'Башҡа иҫәп яҙмаһын булдырырға',
+'createacct-join' => 'Аҫта мәғлүмәттәрегеҙҙе яҙығыҙ.',
+'createacct-another-join' => 'Аҫта яңы иҫәп яҙмағыҙҙың мәғлүмәттәрен яҙығыҙ.',
+'createacct-emailrequired' => 'Электрон почта адресы',
+'createacct-emailoptional' => 'Электрон почта адресы (мотлаҡ түгел)',
+'createacct-email-ph' => 'Электрон почта адресығыҙҙы яҙығыҙ',
+'createacct-another-email-ph' => 'Электрон почта адресығыҙҙы яҙығыҙ',
+'createaccountmail' => 'Осраҡлы рәүештә хасил ителгән ваҡытлыса серһүҙҙе файҙаланырға һәм уны миңә ошо электрон почтаһы адресына ебәрергә',
+'createacct-realname' => 'Ысын исемегеҙ (мотлаҡ түгел)',
 'createaccountreason' => 'Сәбәп:',
+'createacct-reason' => 'Сәбәп',
+'createacct-reason-ph' => 'Икенсе иҫәп яҙмаһы һеҙгә ни өсөн кәрәк?',
 'createacct-captcha' => 'Һаҡлылыҡты тикшереү',
+'createacct-imgcaptcha-ph' => 'Өҫтәге тексты индерегеҙ',
+'createacct-submit' => 'Иҫәп яҙмаһын булдырырға',
+'createacct-another-submit' => 'Тағы бер иҫәп яҙмаһын булдырырға',
+'createacct-benefit-heading' => '{{SITENAME}} һеҙҙең кеүек үк кешеләр тарафынан булдырылған',
+'createacct-benefit-body1' => '{{PLURAL:$1|үҙгәртеү}}',
+'createacct-benefit-body2' => '{{PLURAL:$1|мәҡәлә|мәҡәлә|мәҡәләнең}}',
 'badretype' => 'Һеҙ кереткән серһүҙҙәр тап килмәй.',
 'userexists' => 'Керетелгән исем ҡулланыла инде.
 Зинһар, башҡа исем һайлағыҙ.',
index fdf9f84..e729112 100644 (file)
@@ -2450,9 +2450,11 @@ $UNWATCHURL
 'deleteotherreason' => 'Іншая/дадатковая прычына:',
 'deletereasonotherlist' => 'Іншая прычына',
 'deletereason-dropdown' => '* Агульныя прычыны выдаленьня
-** Запыт аўтара/аўтаркі
+** Спам
+** Вандалізм
 ** Парушэньне аўтарскіх правоў
-** Вандалізм',
+** Запыт аўтара
+** Кепскае перанакіраваньне',
 'delete-edit-reasonlist' => 'Рэдагаваць прычыны выдаленьня',
 'delete-toobig' => 'Гэтая старонка мае доўгую гісторыю рэдагаваньняў, болей за $1 {{PLURAL:$1|вэрсію|вэрсіі|вэрсіяў}}.
 Выдаленьне такіх старонак было забароненае, каб пазьбегнуць праблемаў у працы {{GRAMMAR:родны|{{SITENAME}}}}.',
index 05b763d..d421698 100644 (file)
@@ -486,7 +486,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'За {{SITENAME}}',
 'aboutpage' => 'Project:За {{SITENAME}}',
-'copyright' => 'Съдържанието е достъпно при условията на $1.',
+'copyright' => 'Ð\9eÑ\81вен Ð°ÐºÐ¾ Ð½Ðµ Ðµ Ð¿Ð¾Ñ\81оÑ\87ено Ð´Ñ\80Ñ\83го, Ñ\81ъдържанието е достъпно при условията на $1.',
 'copyrightpage' => '{{ns:project}}:Авторски права',
 'currentevents' => 'Текущи събития',
 'currentevents-url' => 'Project:Текущи събития',
@@ -570,6 +570,8 @@ $1',
 # General errors
 'error' => 'Грешка',
 'databaseerror' => 'Грешка при работа с базата от данни',
+'databaseerror-text' => 'Възникна грешка при заявката за базата данни.
+Това може да означава бъг в софтуера.',
 'databaseerror-query' => 'Заявка: $1',
 'databaseerror-function' => 'Функция: $1',
 'databaseerror-error' => 'Грешка: $1',
@@ -603,6 +605,7 @@ $1',
 'badarticleerror' => 'Действието не може да се изпълни върху страницата.',
 'cannotdelete' => 'Указаната страница или файл "$1" не можа да бъде изтрит(а). Възможно е вече да е бил(а) изтрит(а) от някой друг.',
 'cannotdelete-title' => 'Страницата „$1“ не може да бъде изтрита',
+'no-null-revision' => 'Не може да бъде създадена празна версия на страницата „$1“',
 'badtitle' => 'Невалидно заглавие',
 'badtitletext' => 'Желаното заглавие на страница е невалидно, празно или неправилна препратка към друго уики. Възможно е да съдържа знаци, които не са позволени в заглавия.',
 'perfcached' => 'Следните данни са извлечени от склада и затова може да не отговарят на текущото състояние. В складираното копие {{PLURAL:$1|е допустим най-много един резултат|са допустими най-много $1 резултата}}.',
@@ -629,6 +632,7 @@ $2',
 'customjsprotected' => 'Нямате права за редактиране на тази Джаваскрипт страница, защото тя съдържа чужди потребителски настройки.',
 'mycustomcssprotected' => 'Нямате права за редактиране на тази CSS страница.',
 'mycustomjsprotected' => 'Нямате права за редактиране на тази JavaScript страница.',
+'mypreferencesprotected' => 'Нямате права да редактирате настройките си.',
 'ns-specialprotected' => 'Специалните страници не могат да бъдат редактирани.',
 'titleprotected' => "Тази страница е била защитена срещу създаване от [[User:$1|$1]].
 Посочената причина е ''$2''.",
@@ -784,9 +788,11 @@ $2',
 'resetpass-wrong-oldpass' => 'Невалидна временна или текуща парола.
 Възможно е вече успешно да сте сменили паролата си или да сте поискали нова временна парола.',
 'resetpass-temp-password' => 'Временна парола:',
+'resetpass-abort-generic' => 'Промяната на паролата беше прекъсната от използвано разширение.',
 
 # Special:PasswordReset
 'passwordreset' => 'Възстановяване на парола',
+'passwordreset-text-many' => '{{PLURAL:$1|За възстановяване на паролата е необходимо да се попълни едно от полетата.}}',
 'passwordreset-legend' => 'Възстановяване на парола',
 'passwordreset-disabled' => 'Възстановяването на паролата е изключено в това уики.',
 'passwordreset-username' => 'Потребителско име:',
index 70d1bed..66db1f2 100644 (file)
@@ -1150,7 +1150,7 @@ $1",
 'searchprofile-images-tooltip' => 'ফাইলের জন্য অনুসন্ধান',
 'searchprofile-everything-tooltip' => 'সকল বিষয়বস্তু অনুসন্ধান করো (আলাপের পাতা সহ)',
 'searchprofile-advanced-tooltip' => 'স্বনির্ধারিত নামস্থানে অনুসন্ধান করো',
-'search-result-size' => '$1 ({{PLURAL:$2|1 শব্দ|$2 শব্দসমূহ}})',
+'search-result-size' => '$1 ({{PLURAL:$2|১টি শব্দ|$2টি শব্দ}})',
 'search-result-category-size' => '{{PLURAL: $1 | 1 সদস্য | $1 সদস্যবৃন্দ}} ({{PLURAL: $2 | 1 উপবিষয়শ্রেণীটি | $2 টি}}, {{PLURAL: $3 | 1 ফাইল | $3 ফাইল}})',
 'search-result-score' => 'মিলেছে: $1%',
 'search-redirect' => '(পুনর্নিদেশনা $1)',
index 1dd3e15..ef91ec6 100644 (file)
@@ -2647,7 +2647,7 @@ Setu aze an abeg(où) m\'eo bet stanket $1 : "\'\'$2\'\'"',
 'blocklogpage' => 'Roll ar stankadennoù',
 'blocklog-showlog' => "Stanket eo bet an implijer-mañ c'hoazh. A-is emañ marilh ar stankadennoù, d'ho titouriñ :",
 'blocklog-showsuppresslog' => "Stanket ha kuzhet eo bet an implijer-mañ c'hoazh. A-is emañ marilh ar diverkadennoù, d'ho titouriñ :",
-'blocklogentry' => 'en/he deus stanket [[$1]] betek an $2 $3',
+'blocklogentry' => 'en/he deus stanket [[$1]] gant ur pad termen a $2 $3',
 'reblock-logentry' => "en deus kemmet an arventennoù stankañ evit [[$1]] gant un termen d'an $2 $3",
 'blocklogtext' => "Setu roud stankadennoù ha distankadennoù an implijerien. N'eo ket bet rollet ar chomlec'hioù IP bet stanket ent emgefre. Sellet ouzh [[Special:BlockList|roll an implijerien stanket]] evit gwelet piv zo stanket e gwirionez.",
 'unblocklogentry' => 'distanket "$1"',
index a5d49ac..38b8712 100644 (file)
@@ -733,6 +733,7 @@ Ne zaboravite da prilagodite sebi svoja [[Special:Preferences|{{SITENAME}} pode
 'userlogin-resetpassword-link' => 'Resetirajte svoju šifru/lozinku',
 'helplogin-url' => 'Help:Prijavljivanje',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoć pri prijavljivanju]]',
+'userlogin-createanother' => 'Napravi još jedan račun',
 'createacct-join' => 'Unesite svoje podatke ispod.',
 'createacct-another-join' => 'Unesite informacije o novom računu ispod.',
 'createacct-emailrequired' => 'Adresa e-pošte',
@@ -2610,7 +2611,7 @@ $1',
 'contributions' => 'Doprinosi {{GENDER:$1|korisnika|korisnice|korisnika}}',
 'contributions-title' => 'Doprinosi korisnika $1',
 'mycontris' => 'Doprinos',
-'contribsub2' => 'Za $1 ($2)',
+'contribsub2' => 'Za {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Nisu nađene promjene koje zadovoljavaju ove uslove.',
 'uctop' => '(trenutno)',
 'month' => 'Od mjeseca (i ranije):',
index a85c937..64cf86e 100644 (file)
@@ -456,7 +456,7 @@ $messages = array(
 'index-category' => 'Меттигтерахьйо агlонаш',
 'noindex-category' => 'ДӀахьушйоцу агӀонаш',
 'broken-file-category' => '{{#switch:{{NAMESPACE}}
- |{{ns:0}}=Ð\91олÑ\85 Ñ\86абеÑ\88 Ñ\84айланÑ\88и Ñ\85Ñ\8cажоÑ\80игаш йолу агӀонаш}}',
+ |{{ns:0}}=Ð\91олÑ\85 Ñ\86абеÑ\88 Ñ\84айлийн Ñ\85Ñ\8cажоÑ\80агаш йолу агӀонаш}}',
 
 'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD',
 
@@ -506,8 +506,8 @@ $messages = array(
 'help' => 'ГӀо',
 'search' => 'Лаха',
 'searchbutton' => 'Лаха',
-'go' => 'Дехьа хьажа',
-'searcharticle' => 'Дехьа хьажа',
+'go' => 'Дехьа гӀо',
+'searcharticle' => 'Дехьа гӀо',
 'history' => 'Истори',
 'history_short' => 'Истори',
 'updatedmarker' => 'Керла яккхина сона гинчултӀаьхьа',
@@ -553,7 +553,7 @@ $messages = array(
 'lastmodifiedat' => 'ХӀокху агӀон тӀаьххьаралера хийцам: $2, $1.',
 'viewcount' => 'ХӀокху агӀонга хьойсина $1 {{PLURAL:$1|за|за|за}}.',
 'protectedpage' => 'ГӀароллийца йолу агӀо',
-'jumpto' => 'ДехьагӀо оцу:',
+'jumpto' => 'Дехьа гӀо:',
 'jumptonavigation' => 'Навигаци',
 'jumptosearch' => 'лаха',
 'view-pool-error' => 'Бехк цабиллар доьха, хӀинц гӀулкхдириг йоьттина ю.
@@ -655,11 +655,11 @@ $1',
 'laggedslavemode' => 'Тергам бе: агӀона чохь керла йаьхинарш ца хила мега.',
 'readonly' => 'Блоктоьхна дӀайаздар хаамийн бухе',
 'enterlockreason' => 'Билгал де блоктохаран бахьна а и чекх йолу хан а.',
-'missing-article' => 'Хlокху чохь кароезаш йолу хьан дехарца йозан агlонаш цакарийна «$1» $2.
+'missing-article' => 'ХӀокху чохь кароезаш йолу хьан дехарца йозан агӀонаш цакарийна «$1» $2.
 
\98Ñ\88Ñ\82наÑ\80г Ð½Ð°Ð³Ð³Ð°Ñ\85Ñ\8c Ñ\85Ñ\83Ñ\8cлÑ\83 Ñ\85Ñ\8cажоÑ\80иг Ð´lайаÑ\8cккÑ\85ина Ð¹Ð°Ð»Ñ\85Ñ\8c Ð¹Ð° Ñ\85ийÑ\86ам Ð±Ð¸Ð½Ð° Ñ\82иÑ\88а Ñ\85Ñ\8cажоÑ\80игÑ\86а Ð´ÐµÑ\85Ñ\8cа Ð²Ð°Ð»Ð° Ð³lоьртича.
\98Ñ\88Ñ\82наÑ\80г Ð½Ð°Ð³Ð³Ð°Ñ\85Ñ\8c Ñ\85Ñ\83Ñ\8cлÑ\83 Ñ\85Ñ\8cажоÑ\80аг Ð´Ó\80аÑ\8fÑ\8cккÑ\85ина ÐµÐ»Ð°Ñ\85Ñ\8c Ñ\8f Ñ\85ийÑ\86ам Ð±Ð¸Ð½Ð° Ñ\82иÑ\88а Ñ\85Ñ\8cажоÑ\80агÑ\86а Ð´ÐµÑ\85Ñ\8cа Ð³Ó\80о Ð³Ó\80оьртича.
 
-Нагахьсан гlулкх цуьнах доьзна дацахь, хьуна карийна гlирс латточехь гlалат.
+Нагахьсан гӀулкх цуьнах доьзна дацахь, хьуна карийна гӀирс латточехь гӀалат.
 Дехар до, хаам бе оцуьнах [[Special:ListUsers/sysop|куьйгалхога]], гойтуш URL.',
 'missingarticle-rev' => '(верси № $1)',
 'missingarticle-diff' => '(тейп тайпнара: $1, $2)',
@@ -684,6 +684,8 @@ $1',
 'badtitletext' => 'Дехарца йолу агlонан цlе нийса яц, йаьсса ю, хила мега нийса ца хlоттийна меттаюкъар йа юкъарвики цlе. Хила мега, цlарца цамагош йолу саберг.',
 'perfcached' => 'Лахара хаам схьаэца кэша чура цундела тӀехьарлаьра хийцамаш гойтуш бац. Кэша чохь латтаё оцул $1  кӀезиг {{PLURAL:$1|дӀаяздар|дӀаяздарш}}.',
 'perfcachedts' => 'Лахара хаам схьаэца кэша чура иза тӀаьхьара карла ялла $1. Кэша чохь латта до оцул $4 кӀезиг {{PLURAL:$4|дӀаяздар|дӀаяздарш}}.',
+'querypage-no-updates' => 'ХӀинца хӀара агӀо карлаякхар дӀадайина ду.
+Кхузахь гайтина болу хаамаш карла боккхур бац.',
 'wrong_wfQuery_params' => 'Хилийта йиш йоцу параметраш хӀокху функцин wfQuery()<br />
 Функци: $1<br />
 Жоп дехар: $2',
@@ -812,11 +814,25 @@ $1',
 
 # Special:ChangeEmail
 'changeemail' => 'Хийца электрони почт',
+'changeemail-header' => 'Электрони почтан адрес хийцар',
+'changeemail-text' => 'Юза хӀара форма хьайн электрони почтан адрес хуьйцуш. Ахьа хийцар бакъдан язъян еза пароль.',
 'changeemail-no-info' => 'ХӀара агӀо лело системин чугӀо.',
+'changeemail-oldemail' => 'Карара электронни почтан адрес:',
+'changeemail-newemail' => 'Электрони почтан керла адрес:',
 'changeemail-none' => '(яц)',
+'changeemail-password' => 'Хьан пароль «{{SITENAME}}» проектан:',
 'changeemail-submit' => 'Хийца email',
 'changeemail-cancel' => 'Цаоьшу',
 
+# Special:ResetTokens
+'resettokens-text' => 'Хьан йиш ю токенаш кхосса, цара йиш хуьлуьйту цхьаболу долара хаамашна тӀекхача, уьш ю хьан дӀаяздар ца вовшахтесна. 
+
+Хьона иза оьшу, ахьа хьай токенаш цхьам гучу яьхна елахь я хьан аккаунт йохийна елахь.',
+'resettokens-legend' => 'Токенаш кхоссар',
+'resettokens-token-label' => '$1 (карара маьӀна: $2)',
+'resettokens-watchlist-token' => 'Веб-каналан (Atom/RSS) токен  [[Special:Watchlist|хьан тергаме могӀам чура агӀонашна хийцамаш бар]]',
+'resettokens-resetbutton' => 'Къастина токенаш кхоссар',
+
 # Edit page toolbar
 'bold_sample' => 'Дерстино до йоза',
 'bold_tip' => 'Дерстино до йоза',
@@ -825,7 +841,7 @@ $1',
 'link_sample' => 'Хьажориган коьрта могlа',
 'link_tip' => 'Чоьхьа хьажориг',
 'extlink_sample' => 'http://www.example.com хьажориг корта',
-'extlink_tip' => 'Арахьа хьажориг (йиц ма йе хlотталушерг http://)',
+'extlink_tip' => 'Арахьара хьажораг (йиц ма йе хӀотталушерг http://)',
 'headline_sample' => 'Йозан корта',
 'headline_tip' => 'Корта 2-гlа локхаллийца',
 'nowiki_sample' => 'Чудиллийша кхузе барамхlоттонза йоза.',
@@ -1312,6 +1328,7 @@ $1',
 'filesource' => 'Хьост:',
 'ignorewarning' => 'ХӀума дац чуяккха файл',
 'ignorewarnings' => 'ДӀахедар тергал ца дан',
+'badfilename' => 'Файлан цӀе оцу $1.',
 'emptyfile' => 'Ахьа чуйоккхуш йолу файл еса хийла там бу. Иза гӀалат хийла мега файлан цӀе нийса язйина йоцу дела. Дехар до хьажа бакъалла и юьй ахьа чуйоккхуш йолу файл.',
 'file-deleted-duplicate' => 'Иштта файл ([[:$1]]) хӀинцале дӀаяьккхина хилла. Дехар до, юху файл чуяккхале файл дӀаяккхаран историга хьажа.',
 'uploadwarning' => 'Дlахьедар',
@@ -1407,6 +1424,7 @@ PICT # тайп тайпан
 
 # MIME search
 'mimesearch' => 'MIME хула лаха',
+'mimesearch-summary' => 'ХӀокху агӀоно йиш хуьлуьйту MIME-тайпан файлаш харжа. Яздеш долу формат: чулацаман тайп/бухара тайп, масала  <code>image/jpeg</code>.',
 'mimetype' => 'MIME-тайп:',
 
 # Unwatched pages
@@ -1417,10 +1435,17 @@ PICT # тайп тайпан
 
 # Unused templates
 'unusedtemplates' => 'Лелош доцу кепаш',
+'unusedtemplatestext' => 'Кхузахь дагар йина «{{ns:template}}» цӀерийн меттиган агӀонаш, кхечу агӀонийн юкъа тоьхна йоцу.
+Диц ма делахь хьажа кеп агӀонашкахь лелош юй.',
 
 # Random page
 'randompage' => 'Цахууш нисйелла агӀо',
 
+# Random page in category
+'randomincategory' => 'Категори чу цахууш нийса елла агӀо',
+'randomincategory-selectcategory' => 'Категори чу цахууш нийса елла агӀона чу гӀо: $1 $2.',
+'randomincategory-selectcategory-submit' => 'Дехьа гӀо',
+
 # Random redirect
 'randomredirect' => 'Цахууш нисделла дIасахьажор',
 
@@ -1451,7 +1476,8 @@ PICT # тайп тайпан
 'brokenredirects-edit' => 'нисйé',
 'brokenredirects-delete' => 'дӀаяккха',
 
-'withoutinterwiki' => 'Кхечу меттанашан хьажориг йоцу агIонаш',
+'withoutinterwiki' => 'Юкъарвики-хьажорагаш йоцу агӀонаш',
+'withoutinterwiki-summary' => 'Лахара агӀонийн юкъарвики-хьажорагаш яц:',
 'withoutinterwiki-submit' => 'Гайта',
 
 'fewestrevisions' => 'ЧIогIа кIезиг башхонаш йолу агIонаш',
@@ -1475,6 +1501,9 @@ PICT # тайп тайпан
 'wantedcategories' => 'Оьшуш йолу категореш',
 'wantedpages' => 'Оьшуш йолу агIонаш',
 'wantedfiles' => 'Оьшуш йолу файлаш',
+'wantedfiletext-cat' => 'Лахара йоцу файлаш лело гӀерта. Оцу могӀам юкъа ца хууш файлаш кхета там бу, кхечу проекташ чохь йолу. Ишта ца хууш юкъа нийса елачарна тӀехула <del>сиз</del> хира ду.
+Кхин йоцу файлаш гойту [[:$1]] чохь',
+'wantedfiletext-nocat' => 'Лахара йоцу файлаш лело гӀерта. Оцу могӀам юкъа ца хууш файлаш кхета там бу, кхечу проекташ чохь йолу. Ишта ца хууш юкъа нийса елачарна тӀехула <del>сиз</del> хира ду.',
 'wantedtemplates' => 'Оьшуш долу кепаш',
 'mostlinked' => 'Дуккха хьажоригаш тIе тоьхна йолу агIонаш',
 'mostlinkedcategories' => 'Дуккха тӀе хьажораш йолу категореш',
@@ -1485,6 +1514,7 @@ PICT # тайп тайпан
 'mostrevisions' => 'Сих сиха нисйина йолу агIонаш',
 'prefixindex' => 'Хьалха агlонашан цlераш хlотто йеза',
 'prefixindex-namespace' => 'Хьалха агӀонашан цӀераш хӀотто еза («{{ns:$1}}»)',
+'prefixindex-strip' => 'Хиламийн могӀам чура префикс къайлаяккха',
 'shortpages' => 'Боцоа яззамаш',
 'longpages' => 'Беха яззамаш',
 'deadendpages' => 'Дика йоцу агIонаш',
@@ -1493,6 +1523,7 @@ PICT # тайп тайпан
 'listusers' => 'Декъашхой могlам',
 'listusers-editsonly' => 'Цхаъ мукъане а хийцам бина декъашхой гайта',
 'listusers-creationsort' => 'Кхолларан хене хьаьжна нисъяр',
+'listusers-desc' => 'Харжа къезиг хиларца',
 'usercreated' => '{{GENDER:$3|дӀавазвелла|дӀаязелла}} $1 $2',
 'newpages' => 'Керла агlонаш',
 'newpages-username' => 'Декъашхо:',
@@ -1549,8 +1580,12 @@ PICT # тайп тайпан
 'sp-deletedcontributions-contribs' => 'къинхьегам',
 
 # Special:LinkSearch
-'linksearch' => 'Арахьа хьажориг',
+'linksearch' => 'Арахьара хьажораг',
+'linksearch-pat' => 'Лаха кеп:',
 'linksearch-ok' => 'Лаха',
+'linksearch-text' => 'Лело мега хӀоттош йолу символаш, масала, <code>*.wikipedia.org</code>.
+Лакхара даржан домен мукъа хила еза , масала<code>*.org</code><br />
+Ловш йолу {{PLURAL:$2|протокол|протоколаш}}: <code>$1</code> (Iад йитарца http://, протокол бакъалла язъен яцахь).',
 'linksearch-line' => '$2 — хьажориг кху $1',
 
 # Special:ListUsers
@@ -1958,6 +1993,8 @@ PICT # тайп тайпан
 'allmessagesname' => 'Хаам',
 'allmessagesdefault' => 'Шаьшха йоза',
 'allmessagescurrent' => 'Карарчу хенан йоза',
+'allmessagestext' => 'ХӀара «MediaWiki» цӀерийн меттигера системан хаамийн могӀа бу.
+Хьайна MediaWiki тая лууш делахь, дехар до, проект [//translatewiki.net translatewiki.net] [//www.mediawiki.org/wiki/Localisation юьйцучу хьажа].',
 'allmessages-filter-legend' => 'Литтар',
 'allmessages-filter' => 'Литтар оцу хьола хийцамца:',
 'allmessages-filter-unmodified' => 'Хийцан йоцурш',
@@ -1965,7 +2002,7 @@ PICT # тайп тайпан
 'allmessages-filter-modified' => 'Хийцнарш',
 'allmessages-prefix' => 'Литтар оцу дешахьалхе:',
 'allmessages-language' => 'Мотт:',
-'allmessages-filter-submit' => 'Ð\94еÑ\85Ñ\8cа Ð²Ð°Ð»Ð°',
+'allmessages-filter-submit' => 'Ð\94еÑ\85Ñ\8cа Ð³Ó\80о',
 
 # Thumbnails
 'thumbnail-more' => 'Доккха де',
@@ -1976,6 +2013,7 @@ PICT # тайп тайпан
 'import-interwiki-source' => 'Вики-хьост/агlо:',
 'import-interwiki-templates' => 'Лата де массо кепаш',
 'import-upload-filename' => 'Файлан цӀе:',
+'import-comment' => 'Билгалдаккхар:',
 'importnosources' => 'Юкъаравики-импортан хьост хаьржина яцара, дуьхьала хийцамашан истори чуяккхар дӀадайина ду.',
 
 # Import log
@@ -2001,11 +2039,11 @@ PICT # тайп тайпан
 'tooltip-ca-watch' => 'Тlетоха хlара агlо сан тергаме могlам юкъа',
 'tooltip-ca-unwatch' => 'Дlайаккха хlара агlо хьай тергаме могlам юкъар',
 'tooltip-search' => 'Лаха иза дош',
-'tooltip-search-go' => 'Билгала и санна цlе йолучу агlон чу дехьа вала',
+'tooltip-search-go' => 'Билгала и санна цӀе йолучу агӀон чу дехьа гӀо',
 'tooltip-search-fulltext' => 'Лаха агlонаш ше чулацамехь хlара йоза долуш',
-'tooltip-p-logo' => 'Коьрта агIо',
-'tooltip-n-mainpage' => 'Ð\94еÑ\85Ñ\8cавалаÑ\80 ÐºÐ¾Ñ\8cÑ\80Ñ\82а Ð°Ð³lонÑ\87Ñ\83',
-'tooltip-n-mainpage-description' => 'Ð\94еÑ\85Ñ\8cавалаÑ\80 ÐºÐ¾Ñ\8cÑ\80Ñ\82а Ð°Ð³lонÑ\87Ñ\83',
+'tooltip-p-logo' => 'Коьрта агӀона дехьа гӀо',
+'tooltip-n-mainpage' => 'Ð\9aоÑ\8cÑ\80Ñ\82а Ð°Ð³Ó\80она Ð´ÐµÑ\85Ñ\8cа Ð³Ó\80о',
+'tooltip-n-mainpage-description' => 'Ð\9aоÑ\8cÑ\80Ñ\82а Ð°Ð³Ó\80она Ð´ÐµÑ\85Ñ\8cа Ð³Ó\80о',
 'tooltip-n-portal' => 'Оцу кхолламах, мичахь хlу йу лаьташ а хlудалур ду шуьга',
 'tooltip-n-currentevents' => 'Дlаоьхуш болу хаамашна могlам',
 'tooltip-n-recentchanges' => 'Тlаьххьаралера хийцаман могlам',
@@ -2164,6 +2202,7 @@ PICT # тайп тайпан
 'exif-gpsaltitude' => 'Локхалла',
 'exif-gpsdestlatitude' => 'Объектан дохалла',
 'exif-gpsdatestamp' => 'Терахь',
+'exif-jpegfilecomment' => 'JPEG-файлан билгалдаккхар',
 'exif-keywords' => 'Коьрта дешнаш',
 'exif-objectname' => 'Йоцца цӀе',
 'exif-specialinstructions' => 'Къаьсттина тӀехьажор',
@@ -2175,6 +2214,8 @@ PICT # тайп тайпан
 'exif-originaltransmissionref' => 'ДӀадолалун меттиган код',
 'exif-label' => 'Билгало',
 'exif-datetimemetadata' => 'ТӀехьара метахаамаш хийцина терахь',
+'exif-pngfilecomment' => 'PNG-файлан билгалдаккхар',
+'exif-giffilecomment' => 'GIF-файлан билгалдаккхар',
 
 # Exif attributes
 'exif-compression-1' => 'ТIеIовдан яц',
@@ -2277,11 +2318,17 @@ PICT # тайп тайпан
 'redirect' => 'Декъашхочун файлан тӀера дӀасхьажор',
 'redirect-legend' => 'Файлан я агӀона тӀера дӀасхьажор',
 'redirect-summary' => 'ХӀара агӀо лело йиш ю файлан я агӀона тӀера дӀасхьажош.',
+'redirect-submit' => 'Дехьа гӀо',
+'redirect-lookup' => 'Лаха:',
 'redirect-value' => 'МаьӀна:',
 'redirect-user' => 'Декъашхочун ID',
+'redirect-revision' => 'АгӀона верси',
+'redirect-file' => 'Файлан цӀе',
 
 # Special:FileDuplicateSearch
 'fileduplicatesearch' => 'Лаха цхьатера йолу файлаш',
+'fileduplicatesearch-summary' => 'Лаха цхьатера йолу файлаш хэш-кодаца.',
+'fileduplicatesearch-legend' => 'Цхьатера ерш лахар',
 'fileduplicatesearch-filename' => 'Файлан цӀе:',
 'fileduplicatesearch-submit' => 'Лаха',
 'fileduplicatesearch-info' => '$1 × $2 {{PLURAL:$2|пиксель|пикселш|пикселш}}<br />Файлан барам: $3<br />MIME-тайп: $4',
@@ -2339,6 +2386,7 @@ PICT # тайп тайпан
 'dberr-outofdate' => 'Хьуна хаалахь, цуьна йолу меттиг хила мега тишйелла черахь.',
 
 # HTML forms
+'htmlform-invalid-input' => 'Ахьа яздинчу цхьан дакхано гӀалат далина',
 'htmlform-submit' => 'ДӀадахьийта',
 'htmlform-reset' => 'Цаоьшу хийцамаш',
 'htmlform-selectorother-other' => 'Кхин',
@@ -2352,6 +2400,7 @@ PICT # тайп тайпан
 'logentry-move-move_redir-noredirect' => '$1 {{GENDER:$2|цӀе хийцина|цӀе хийцина}} $3 оцу $4 дӀасахьажоран тӀохул а дӀасахьажийнарг цаюьтуш а',
 'logentry-newusers-newusers' => '{{GENDER:$2|ДӀавазвелла|ДӀаязелла}} керла декъашхо $1',
 'logentry-newusers-create' => '{{GENDER:$2|ДӀавазвелла|ДӀаязелла}} керла декъашхо $1',
+'logentry-newusers-autocreate' => 'Автоматически кхоьллина {{GENDER:$2|декъашхочун}} $1 дӀаяздар',
 'logentry-rights-rights' => '$1 {{GENDER:$2|хийцина}} хӀокхуна $3 бакъо $4 → $5',
 'logentry-rights-rights-legacy' => '$1 {{GENDER:$2|хийцина}} хӏокхуна $3 бакъо',
 'rightsnone' => '(яц)',
index 93a28bb..5ffe6a2 100644 (file)
@@ -2288,9 +2288,11 @@ Gwelwch y $2 am gofnod o\'r dileuon diweddar.',
 'deleteotherreason' => 'Rheswm arall:',
 'deletereasonotherlist' => 'Rheswm arall',
 'deletereason-dropdown' => "*Rhesymau arferol dros ddileu
-** Ar gais yr awdur
+** Sbam
+** Fandaliaeth
 ** Torri'r hawlfraint
-** Fandaliaeth",
+** Ar gais yr awdur
+** Ailgyfeiriad wedi torri",
 'delete-edit-reasonlist' => 'Golygu rhestr y rhesymau dros ddileu',
 'delete-toobig' => "Cafwyd dros $1 {{PLURAL:$1|o olygiadau}} i'r dudalen hon.
 Cyfyngwyd ar y gallu i ddileu tudalennau sydd wedi eu golygu cymaint â hyn, er mwyn osgoi amharu ar weithrediad databas {{SITENAME}} yn ddamweiniol.",
index afea089..fad1fea 100644 (file)
@@ -4208,12 +4208,12 @@ Bei entsprechender Einstellung können die Missbrauchfilter beliebige Markierung
 'revdelete-uname-unhid' => 'Benutzername freigegeben',
 'revdelete-restricted' => 'Einschränkungen gelten auch für Administratoren',
 'revdelete-unrestricted' => 'Einschränkungen für Administratoren aufgehoben',
-'logentry-move-move' => '$1 {{GENDER:$2|verschob}} Seite $3 nach $4',
-'logentry-move-move-noredirect' => '$1 {{GENDER:$2|verschob}} Seite $3 nach $4, ohne dabei eine Weiterleitung anzulegen',
-'logentry-move-move_redir' => '$1 {{GENDER:$2|verschob}} Seite $3 nach $4 und überschrieb dabei eine Weiterleitung',
-'logentry-move-move_redir-noredirect' => '$1 {{GENDER:$2|verschob}} Seite $3 nach $4 und überschrieb dabei eine Weiterleitung, ohne selbst eine Weiterleitung anzulegen',
-'logentry-patrol-patrol' => '$1 {{GENDER:$2|markierte}} Version $4 von Seite $3 als kontrolliert',
-'logentry-patrol-patrol-auto' => '$1 {{GENDER:$2|markierte}} automatisch Version $4 von Seite $3 als kontrolliert',
+'logentry-move-move' => '$1 {{GENDER:$2|verschob}} die Seite $3 nach $4',
+'logentry-move-move-noredirect' => '$1 {{GENDER:$2|verschob}} die Seite $3 nach $4, ohne dabei eine Weiterleitung anzulegen',
+'logentry-move-move_redir' => '$1 {{GENDER:$2|verschob}} die Seite $3 nach $4 und überschrieb dabei eine Weiterleitung',
+'logentry-move-move_redir-noredirect' => '$1 {{GENDER:$2|verschob}} die Seite $3 nach $4 und überschrieb dabei eine Weiterleitung, ohne selbst eine Weiterleitung anzulegen',
+'logentry-patrol-patrol' => '$1 {{GENDER:$2|markierte}} die Version $4 von Seite $3 als kontrolliert',
+'logentry-patrol-patrol-auto' => '$1 {{GENDER:$2|markierte}} automatisch die Version $4 von Seite $3 als kontrolliert',
 'logentry-newusers-newusers' => 'Benutzerkonto $1 wurde {{GENDER:$2|erstellt}}',
 'logentry-newusers-create' => 'Benutzerkonto $1 wurde {{GENDER:$2|erstellt}}',
 'logentry-newusers-create2' => 'Benutzerkonto $3 wurde von $1 {{GENDER:$2|erstellt}}',
index e247442..ec8f0f2 100644 (file)
@@ -392,7 +392,6 @@ $specialPageAliases = array(
        'Badtitle'                  => array( 'Badtitle' ),
        'Blankpage'                 => array( 'BlankPage' ),
        'Block'                     => array( 'Block', 'BlockIP', 'BlockUser' ),
-       'Blockme'                   => array( 'BlockMe' ),
        'Booksources'               => array( 'BookSources' ),
        'BrokenRedirects'           => array( 'BrokenRedirects' ),
        'Categories'                => array( 'Categories' ),
@@ -874,7 +873,7 @@ future releases. Also note that since each list value is wrapped in a unique
 'articlepage'        => 'View content page',
 'talk'               => 'Discussion',
 'views'              => 'Views',
-'toolbox'            => 'Toolbox',
+'toolbox'            => 'Tools',
 'userpage'           => 'View user page',
 'projectpage'        => 'View project page',
 'imagepage'          => 'View file page',
@@ -3400,12 +3399,9 @@ See the [[Special:BlockList|block list]] for the list of currently operational b
 It is, however, blocked as part of the range $2, which can be unblocked.',
 'ip_range_invalid'                => 'Invalid IP range.',
 'ip_range_toolarge'               => 'Range blocks larger than /$1 are not allowed.',
-'blockme'                         => 'Block me',
 'proxyblocker'                    => 'Proxy blocker',
-'proxyblocker-disabled'           => 'This function is disabled.',
 'proxyblockreason'                => 'Your IP address has been blocked because it is an open proxy.
 Please contact your Internet service provider or technical support of your organization and inform them of this serious security problem.',
-'proxyblocksuccess'               => 'Done.',
 'sorbs'                           => 'DNSBL', # only translate this message to other languages if you have to change it
 'sorbsreason'                     => 'Your IP address is listed as an open proxy in the DNSBL used by {{SITENAME}}.',
 'sorbs_create_account_reason'     => 'Your IP address is listed as an open proxy in the DNSBL used by {{SITENAME}}.
index 74f1332..38be071 100644 (file)
@@ -749,6 +749,9 @@ Pane tähele, et seni kuni sa pole oma võrgulehitseja puhvrit tühjendanud, võ
 'userlogin-resetpassword-link' => 'Lähtesta oma parool',
 'helplogin-url' => 'Help:Sisselogimine',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Sisselogimisabi]]',
+'userlogin-loggedin' => 'Oled juba sisse logitud nimega {{GENDER:$1|$1}}.
+Kasuta allolevat vormi, et logida sisse teise kasutajaga.',
+'userlogin-createanother' => 'Loo teine konto',
 'createacct-join' => 'Sisesta allapoole oma andmed.',
 'createacct-another-join' => 'Sisesta allpool uue konto andmed.',
 'createacct-emailrequired' => 'E-posti aadress',
@@ -2452,10 +2455,12 @@ Palun kinnita, et tahad seda tõepoolest teha, et sa mõistad tagajärgi ja et s
 'deletecomment' => 'Põhjus:',
 'deleteotherreason' => 'Muu või täiendav põhjus:',
 'deletereasonotherlist' => 'Muu põhjus',
-'deletereason-dropdown' => '*Harilikud kustutamise põhjused
-** Autori palve
+'deletereason-dropdown' => '* Harilikud kustutamise põhjused
+** Rämpspostitus
+** Vandalism
 ** Autoriõiguse rikkumine
-** Vandalism',
+** Autori palve
+** Katkine ümbersuunamine',
 'delete-edit-reasonlist' => 'Redigeeri kustutamise põhjuseid',
 'delete-toobig' => 'See lehekülg on pika redigeerimisajalooga – üle {{PLURAL:$1|ühe muudatuse|$1 muudatuse}}.
 Selle kustutamine on keelatud, et ära hoida ekslikku {{GRAMMAR:genitive|{{SITENAME}}}} töö häirimist.',
index 825466d..fa3051c 100644 (file)
@@ -2510,7 +2510,7 @@ Kasu horietan orrialdea eskuz mugitu edo bestearekin bateratu beharko duzu.",
 'move-subpages' => 'Azpiorrialde guztiak ($1-tik gora) mugitu',
 'move-talk-subpages' => 'Azpiorrialdeen eztabaida orrialde guztiak ($1-tik gora) mugitu',
 'movepage-page-exists' => '$1 orrialdea jada badago eta ezin da automatikoki gainetik idatzi.',
-'movepage-page-moved' => '$1 orria $2 izenera aldatu da.',
+'movepage-page-moved' => '«$1» orria «$2» izenera aldatu da.',
 'movepage-page-unmoved' => '$1 orrialdea ezin da $2(e)ra mugitu.',
 'movepage-max-pages' => '$1 {{PLURAL:$1|orrialderen|orrialdeen}} maximoa mugitu da eta jada ez dira gehiago mugituko modu automatikoan.',
 'movelogpage' => 'Mugimendu erregistroa',
@@ -3584,9 +3584,9 @@ Halaber [[Special:EditWatchlist|aldatzaile estandarra]] erabil dezakezu.',
 'revdelete-uname-unhid' => 'lankide ezkutua erakutsi',
 'revdelete-restricted' => 'administratzaileentzako mugak ezarri dira',
 'revdelete-unrestricted' => 'administratzaileentzako mugak kendu dira',
-'logentry-move-move' => '$1 {{GENDER:$2|wikilariak}} $3 orria $4 izenera aldatu du',
+'logentry-move-move' => '$1 {{GENDER:$2|wikilariak}} «$3» orria «$4» izenera aldatu du',
 'logentry-move-move-noredirect' => '$1 {{GENDER:$2|wikilariak}} $3 orria $4 izenera aldatu du, birzuzenketarik utzi gabe',
-'logentry-move-move_redir' => '$1 {{GENDER:wikilariak}} «$3» orria «$4» izenera aldatu du, birzuzenketaren gainetik',
+'logentry-move-move_redir' => '$1 {{GENDER:$2|wikilariak}} «$3» orria «$4» izenera aldatu du, birzuzenketaren gainetik',
 'logentry-move-move_redir-noredirect' => '$1 {{GENDER:wikilariak}} «$3» orria «$4» izenera aldatu du, birzuzenketa bat gainidatzita, birzuzenketarik utzi gabe',
 'logentry-patrol-patrol' => '$1(e)k $3 orrialdearen $4 berrikuzpena patruilatutzat {{GENDER:$2|markatu}} du',
 'logentry-newusers-newusers' => '$1 erabiltzaile kontua sortu da',
index de17d41..0fb4204 100644 (file)
@@ -1665,7 +1665,7 @@ $1",
 'yourvariant' => 'گویش زبان محتوا:',
 'prefs-help-variant' => 'گویش انتخابی شما برای نمایش محتوای صفحه‌ها در این ویکی.',
 'yournick' => 'امضای جدید:',
-'prefs-help-signature' => 'نظرهای نوشته شده در صفحهٔ بحث باید با «<nowiki>~~~~</nowiki>» امضا شوند؛ این علامت به طور خودکار به امضای شما و مهر تاریخ تبدیل خواهد شد.',
+'prefs-help-signature' => 'نظرهای نوشته‌شده در صفحهٔ بحث باید با «<nowiki>~~~~</nowiki>» امضا شوند؛ این علامت به‌صورت خودکار به امضای شما و مهر تاریخ تبدیل خواهد شد.',
 'badsig' => 'امضای خام غیرمجاز.
 لطفاً برچسب‌های اچ‌تی‌ام‌ال را بررسی کنید.',
 'badsiglength' => 'امضای شما بیش از اندازه طولانی است.
@@ -1675,7 +1675,7 @@ $1",
 'gender-male' => 'مرد',
 'gender-female' => 'زن',
 'prefs-help-gender' => 'انجام این تنظیم اختیاری است.
-نرم‌افزار از این مقدار برای اشارهٔ صحیح به جنسیت شما و ذکر شما برای دیگران با استفاده از قوائد درست دستور زبان استفاده می‌کند.
+نرم‌افزار از این مقدار برای اشارهٔ صحیح به جنسیت و ذکر شما برای دیگران با استفاده از دستور زبان درست استفاده می‌کند.
 این اطلاعات عمومی خواهند بود.',
 'email' => 'رایانامه',
 'prefs-help-realname' => 'نام واقعی اختیاری است.
@@ -1809,7 +1809,7 @@ $1",
 'right-editmyprivateinfo' => 'داده‌های خصوصی خود را ویرایش کنید (مانند رایانشانی و نام واقعی)',
 'right-editmyoptions' => 'ترجیحات خود را ویرایش',
 'right-rollback' => 'واگردانی سریع ویرایش‌های آخرین کاربری که یک صفحه را ویرایش کرده‌است',
-'right-markbotedits' => 'علامت زدن ویرایش‌های واگردانی شده به عنوان ویرایش ربات',
+'right-markbotedits' => 'علامت‌زدن ویرایش‌های واگردانی‌شده به‌عنوان ویرایش ربات',
 'right-noratelimit' => 'تاثیر نپذیرفتن از محدودیت سرعت',
 'right-import' => 'وارد کردن صفحه از ویکی‌های دیگر',
 'right-importupload' => 'وارد کردن صفحه از طریق بارگذاری پرونده',
@@ -3068,7 +3068,7 @@ $1',
 'movenotallowedfile' => 'شما اجازهٔ انتقال پرونده‌ها را ندارید.',
 'cant-move-user-page' => 'شما اجازه ندارید صفحه‌های کاربری سرشاخه را انتقال دهید.',
 'cant-move-to-user-page' => 'شما اجازه ندارید که یک صفحه را به یک صفحهٔ کاربر انتقال دهید (به استثنای زیر صفحه‌های کاربری).',
-'newtitle' => 'به عنوان جدید',
+'newtitle' => 'بهعنوان جدید',
 'move-watch' => 'پی‌گیری صفحه‌های مبدأ و مقصد',
 'movepagebtn' => 'صفحه منتقل شود',
 'pagemovedsub' => 'انتقال با موفقیت انجام شد',
@@ -3436,7 +3436,7 @@ $1',
 'mediawarning' => "'''هشدار''': این پرونده ممکن است حاوی کدهای مخرب باشد.
 با اجرای آن رایانهٔ شما ممکن است آسیب ببیند.",
 'imagemaxsize' => "محدودیت ابعاد تصویر:<br />''(برای صفحه‌های توصیف پرونده)''",
-'thumbsize' => 'اندازهٔ Thumbnail:',
+'thumbsize' => 'اندازهٔ بنداگشتی:',
 'widthheight' => '$1 در $2',
 'widthheightpage' => '$1×$2، $3 {{PLURAL:$3|صفحه|صفحه}}',
 'file-info' => 'اندازهٔ پرونده: $1، نوع  MIME $2',
index 804552b..9a55700 100644 (file)
@@ -360,7 +360,7 @@ $messages = array(
 'tog-noconvertlink' => 'Älä muunna linkkien otsikoita toiseen kirjoitusjärjestelmään',
 'tog-norollbackdiff' => 'Älä näytä eroavaisuuksia, kun olet palauttanut muokkauksen palauta-työkalulla',
 'tog-useeditwarning' => 'Varoita minua, kun poistun muokkaussivulta tallentamatta muutoksia',
-'tog-prefershttps' => 'Käytä aina turvallista yhteyttä kun olet kirjautunut sisään',
+'tog-prefershttps' => 'Käytä aina suojattua yhteyttä, kun olet kirjautunut sisään',
 
 'underline-always' => 'Aina',
 'underline-never' => 'Ei koskaan',
@@ -809,7 +809,7 @@ Salli evästeiden käyttö, ja sen jälkeen kirjaudu sisään juuri luomallasi k
 'nocookiesfornew' => 'Käyttäjätunnusta ei luotu, koska sen lähdettä ei kyetty varmistamaan. Varmista, että selaimessasi on käytössä evästeet, päivitä tämä sivu ja yritä uudelleen.',
 'noname' => 'Et ole määritellyt kelvollista käyttäjänimeä.',
 'loginsuccesstitle' => 'Sisäänkirjautuminen onnistui',
-'loginsuccess' => "'''Olet nyt kirjautunut sivustolle {{SITENAME}} käyttäjänä $1.'''",
+'loginsuccess' => "'''Olet kirjautunut sivustolle {{SITENAME}} käyttäjänä $1.'''",
 'nosuchuser' => 'Käyttäjää ”$1” ei ole olemassa. Nimet ovat kirjainkoosta riippuvaisia. Tarkista kirjoititko nimen oikein, tai [[Special:UserLogin/signup|luo uusi käyttäjätunnus]].',
 'nosuchusershort' => 'Käyttäjää nimeltä ”$1” ei ole. Kirjoititko nimen oikein?',
 'nouserspecified' => 'Käyttäjätunnusta ei ole määritelty.',
@@ -854,8 +854,8 @@ Odota $1 ennen kuin yrität uudelleen.',
 'login-abort-generic' => 'Kirjautuminen epäonnistui – keskeytetty',
 'loginlanguagelabel' => 'Kieli: $1',
 'suspicious-userlogout' => 'Pyyntösi kirjautua ulos evättiin, koska se näytti rikkinäisen selaimen tai välimuistipalvelimen lähettämältä.',
-'createacct-another-realname-tip' => 'Oikea nimi on vapaaehtoinen.
-Jos kuitenkin kerrot sen, nimeä käytetään jotta voidaan kertoa kuka sisältöä on tuottanut.',
+'createacct-another-realname-tip' => 'Vapaaehtoinen.
+Nimesi näytetään käyttäjätunnuksesi sijasta sivun tekijäluettelossa.',
 
 # Email sending
 'php-mail-error-unknown' => 'Tuntematon virhe PHP:n mail()-funktiossa',
@@ -871,7 +871,7 @@ Jos kuitenkin kerrot sen, nimeä käytetään jotta voidaan kertoa kuka sisält
 'newpassword' => 'Uusi salasana:',
 'retypenew' => 'Uusi salasana uudelleen:',
 'resetpass_submit' => 'Aseta salasana ja kirjaudu sisään',
-'changepassword-success' => 'Salasanasi vaihtaminen onnistui.',
+'changepassword-success' => 'Salasanan vaihto onnistui.',
 'resetpass_forbidden' => 'Salasanoja ei voi vaihtaa.',
 'resetpass-no-info' => 'Et voi nähdä tätä sivua kirjautumatta sisään.',
 'resetpass-submit-loggedin' => 'Muuta salasana',
@@ -884,7 +884,7 @@ Olet saattanut jo onnistuneesti vaihtaa salasanasi tai pyytää uutta väliaikai
 # Special:PasswordReset
 'passwordreset' => 'Salasanan vaihto',
 'passwordreset-text-one' => 'Täytä tämä lomake vaihtaaksesi salasanasi.',
-'passwordreset-text-many' => '{{PLURAL:$1|Täytä yksi kentistä nollataksesi salasanasi.}}',
+'passwordreset-text-many' => '{{PLURAL:$1|Täytä yksi kentistä alustaaksesi salasanasi.}}',
 'passwordreset-legend' => 'Salasanan vaihto',
 'passwordreset-disabled' => 'Salasanojen vaihtaminen ei ole mahdollista tässä wikissä.',
 'passwordreset-emaildisabled' => 'Sähköpostitoiminnot on poistettu käytöstä tässä wikissä.',
@@ -932,15 +932,15 @@ Väliaikainen salasana: $2',
 'changeemail-cancel' => 'Peruuta',
 
 # Special:ResetTokens
-'resettokens' => 'Uudista tunnisteet',
-'resettokens-text' => 'Tällä sivulla voit uudistaa tunnisteesi, joiden avulla hallitaan pääsyä käyttäjätiliisi liittyviin yksityisiin tietoihin.
+'resettokens' => 'Uudista avaimet',
+'resettokens-text' => 'Tällä sivulla voit uudistaa avaimesi, jotka mahdollistavat pääsyn käyttäjätunnukseesi liittyviin tiettyihin yksityisiin tietoihin.
 
-Sinun pitää tehdä tämä jos olet vahingossa jakanut ne jonkun kanssa tai käyttäjätiliisi on saatettu kajota.',
-'resettokens-no-tokens' => 'Tunnisteita ei löydy uudistettavaksi.',
-'resettokens-legend' => 'Uudista tunnisteet',
-'resettokens-tokens' => 'Tunnisteet:',
+Sinun pitäisi tehdä tämä, jos olet vahingossa jakanut avaimet jonkun kanssa tai jos käyttäjätunnuksesi on vaarannettu.',
+'resettokens-no-tokens' => 'Avaimia ei ole uudistettavaksi.',
+'resettokens-legend' => 'Uudista avaimet',
+'resettokens-tokens' => 'Avaimet:',
 'resettokens-token-label' => '$1 (nykyinen arvo: $2)',
-'resettokens-watchlist-token' => 'Tarkkailulistan verkkosyötteen tunniste',
+'resettokens-watchlist-token' => '[[Special:Watchlist|Tarkkailulistan]] verkkosyötteen (Atom tai RSS) avain',
 'resettokens-done' => 'Avaimet on uudistettu.',
 'resettokens-resetbutton' => 'Uudista valitut avaimet',
 
@@ -1445,7 +1445,7 @@ Kokeile lisätä haun alkuun ''all:'', niin haku kohdistuu kaikkeen sisältöön
 'prefs-rendering' => 'Ulkoasu',
 'saveprefs' => 'Tallenna asetukset',
 'resetprefs' => 'Tyhjennä tallentamattomat muutokset',
-'restoreprefs' => 'Palauta kaikki oletusasetuksiin (kaikissa asetusten osastoissa)',
+'restoreprefs' => 'Palauta kaikki oletusasetuksiin (kaikissa osioissa)',
 'prefs-editing' => 'Muokkaus',
 'rows' => 'Rivejä',
 'columns' => 'Sarakkeita',
@@ -1457,7 +1457,9 @@ Kokeile lisätä haun alkuun ''all:'', niin haku kohdistuu kaikkeen sisältöön
 'recentchangesdays-max' => 'Enintään $1 {{PLURAL:$1|päivä|päivää}}',
 'recentchangescount' => 'Näytettävien muutoksien määrä oletuksena',
 'prefs-help-recentchangescount' => 'Tämä sisältää tuoreet muutokset, muutoshistoriat ja lokit.',
-'prefs-help-watchlist-token2' => 'Tämä on salainen avain tarkkailulistasi verkkosyötteeseen. Kuka tahansa joka tietää sen voi lukea tarkkailulistaasi, joten älä paljasta sitä. [[Special:ResetTokens|Napsauta tästä jos sinun pitää uudistaa se]].',
+'prefs-help-watchlist-token2' => 'Tämä on salainen avain tarkkailulistasi verkkosyötteeseen.
+Kuka tahansa, joka tietää sen voi lukea tarkkailulistaasi, joten älä paljasta sitä.
+[[Special:ResetTokens|Napsauta tästä, jos sinun pitää uudistaa se]].',
 'savedprefs' => 'Asetuksesi tallennettiin onnistuneesti.',
 'timezonelegend' => 'Aikavyöhyke',
 'localtime' => 'Paikallinen aika',
@@ -1545,10 +1547,10 @@ Tämä tieto on julkinen.',
 'saveusergroups' => 'Tallenna nämä käyttäjäryhmät',
 'userrights-groupsmember' => 'Jäsenenä ryhmissä:',
 'userrights-groupsmember-auto' => 'Automaattisesti jäsenenä ryhmissä:',
-'userrights-groups-help' => 'Voit muuttaa ryhmiä, joissa tämä käyttäjä on:
+'userrights-groups-help' => 'Voit muuttaa ryhmiä, joissa tämä käyttäjä on.
 * Merkattu valintaruutu tarkoittaa, että käyttäjä on kyseisessä ryhmässä.
 * Merkkaamaton valintaruutu tarkoittaa, että käyttäjä ei ole kyseisessä ryhmässä.
-* <nowiki>*</nowiki> tarkoittaa, että et pysty poistamaan käyttäjää tästä ryhmästä kun olet hänet siihen lisännyt tai päinvastoin',
+* <nowiki>*</nowiki> tarkoittaa, että et pysty poistamaan käyttäjää tästä ryhmästä, kun olet sen siihen lisännyt tai päinvastoin',
 'userrights-reason' => 'Syy:',
 'userrights-no-interwiki' => 'Sinulla ei ole oikeutta muokata käyttöoikeuksia muissa wikeissä.',
 'userrights-nodatabase' => 'Tietokantaa $1 ei ole tai se ei ole paikallinen.',
@@ -2097,11 +2099,11 @@ Syöte: sisältötyyppi/alatyyppi, esimerkiksi <code>image/jpeg</code>.',
 'randompage-nopages' => '{{PLURAL:$2|Nimiavaruudessa|Nimiavaruuksissa}} $1 ei ole sivuja.',
 
 # Random page in category
-'randomincategory' => 'Satunnainen sivu, joka kuuluu luokkaan',
-'randomincategory-invalidcategory' => '" $1 " ei ole kelvollinen luokan nimi.',
-'randomincategory-nopages' => 'Luokassa [[:Category:$1]] ei ole sivuja.',
-'randomincategory-selectcategory' => 'Etsi satunnainen sivu luokasta: $1 $2.',
-'randomincategory-selectcategory-submit' => 'Etsi',
+'randomincategory' => 'Satunnainen sivu luokasta',
+'randomincategory-invalidcategory' => '$1 ei ole kelvollinen luokan nimi.',
+'randomincategory-nopages' => 'Luokassa [[:Category:$1|$1]] ei ole sivuja.',
+'randomincategory-selectcategory' => 'Hae satunnainen sivu luokasta: $1 $2',
+'randomincategory-selectcategory-submit' => 'Hae',
 
 # Random redirect
 'randomredirect' => 'Satunnainen ohjaus',
@@ -2314,7 +2316,8 @@ Vaaditaan vähintään ylätason verkkotunnus, esimerkiksi "*.org".<br />
 'listgrouprights' => 'Käyttäjäryhmien oikeudet',
 'listgrouprights-summary' => 'Tämä lista sisältää tämän wikin käyttäjäryhmät sekä ryhmiin liitetyt käyttöoikeudet.
 Lisätietoa yksittäisistä käyttäjäoikeuksista saattaa löytyä [[{{MediaWiki:Listgrouprights-helppage}}|erilliseltä ohjesivulta]].',
-'listgrouprights-key' => '* <span class="listgrouprights-granted">Myönnetyt oikeudet</span>
+'listgrouprights-key' => 'Selite:
+* <span class="listgrouprights-granted">Myönnetyt oikeudet</span>
 * <span class="listgrouprights-revoked">Kumotut oikeudet</span>',
 'listgrouprights-group' => 'Ryhmä',
 'listgrouprights-rights' => 'Oikeudet',
@@ -2467,10 +2470,12 @@ Sivulla $2 on lista viimeaikaisista poistoista.',
 'deletecomment' => 'Syy',
 'deleteotherreason' => 'Muu syy tai tarkennus',
 'deletereasonotherlist' => 'Muu syy',
-'deletereason-dropdown' => '*Yleiset poistosyyt
-** Tekijän poistopyyntö
+'deletereason-dropdown' => '* Yleiset poistosyyt
 ** Tekijänoikeusrikkomus
-** Vandalismi',
+** Tekijän poistopyyntö
+** Testisivu
+** Vandalismi
+** Virheellinen ohjaus',
 'delete-edit-reasonlist' => 'Muokkaa poistosyitä',
 'delete-toobig' => 'Tällä sivulla on pitkä muutoshistoria – yli $1 {{PLURAL:$1|versio|versiota}}. Näin suurien muutoshistorioiden poistamista on rajoitettu suorituskykysyistä.',
 'delete-warning-toobig' => 'Tällä sivulla on pitkä muutoshistoria – yli $1 {{PLURAL:$1|versio|versiota}}. Näin suurien muutoshistorioiden poistaminen voi haitata sivuston suorituskykyä.',
@@ -2996,7 +3001,7 @@ Tallenna tiedot koneellesi ja tuo ne tällä sivulla.',
 'import-error-interwiki' => 'Sivua $1 ei voitu tuoda, koska sen nimi on varattu ulkoisen linkittämisen (interwiki).',
 'import-error-special' => 'Sivua $1 ei tuoda, koska se kuuluu nimitilaan, joka ei salli sivuja.',
 'import-error-invalid' => 'Sivua $1 ei tuoda, koska sen nimi ei kelpaa.',
-'import-error-unserialize' => 'Revisiota $2 sivusta "$1" ei voida jakaa osiin. Revision kerrottiin käyttävän sisältömallia $3 ja sarjoitusmuotoilua $4.',
+'import-error-unserialize' => 'Versiota $2 sivusta $1 ei voida jakaa osiin. Version ilmoitettiin käyttävän sisältömallia $3 ja sarjoitusmuotoilua $4.',
 'import-options-wrong' => '{{PLURAL:$2|Väärä asetus|Väärät asetukset}}: <nowiki>$1</nowiki>',
 'import-rootpage-invalid' => 'Annettu sivun nimi ei kelpaa.',
 'import-rootpage-nosubpage' => 'Annetun sivun nimiavaruus $1 ei salli alasivuja.',
@@ -3885,7 +3890,7 @@ Voit myös muokata listaa [[Special:EditWatchlist|tavalliseen tapaan]].',
 'version-license' => 'Lisenssi',
 'version-poweredby-credits' => "Tämä wiki käyttää '''[//www.mediawiki.org/ MediaWikiä]'''. Copyright © 2001–$1 $2.",
 'version-poweredby-others' => 'muut',
-'version-poweredby-translators' => 'translatewiki.net kääntäjät',
+'version-poweredby-translators' => 'translatewiki.net-kääntäjät',
 'version-credits-summary' => 'Haluaisimme kiittää seuraavia henkilöitä heidän panoksestaan [[Special:Version|MediaWiki-ohjelmistoon]].',
 'version-license-info' => 'MediaWiki on vapaa ohjelmisto – voit levittää sitä ja/tai muokata sitä Free Software Foundationin GNU General Public Licensen ehdoilla, joko version 2 tai halutessasi minkä tahansa myöhemmän version mukaisesti.
 
@@ -3900,13 +3905,13 @@ Sinun olisi pitänyt saada [{{SERVER}}{{SCRIPTPATH}}/COPYING kopio GNU General P
 'version-entrypoints-header-url' => 'URL',
 
 # Special:Redirect
-'redirect' => 'Ohjaus tiedostonimen, käyttäjänumeron tai versionumeron mukaan',
-'redirect-legend' => 'Uudelleenohjaa tiedostoon tai sivulle',
-'redirect-summary' => 'Tämä toimintosivu ohjaa tiedostoon (tiedoston nimen mukaan), sivulle (sivun versionumeron mukaan) tai käyttäjäsivulle (käyttäjätunnuksen numeron mukaan).',
+'redirect' => 'Ohjaus tiedostonimen, käyttäjätunnisteen tai versiotunnisteen mukaan',
+'redirect-legend' => 'Ohjaus tiedostoon tai sivulle',
+'redirect-summary' => 'Tämä toimintosivu ohjaa tiedostoon (tiedostonimen mukaan), sivulle (versiotunnisteen mukaan) tai käyttäjäsivulle (käyttäjätunnisteen numeron mukaan).',
 'redirect-submit' => 'Siirry',
 'redirect-lookup' => 'Hae:',
 'redirect-value' => 'Arvo:',
-'redirect-user' => 'Käyttäjän tunnusnumero',
+'redirect-user' => 'Käyttäjätunniste',
 'redirect-revision' => 'Sivun versio',
 'redirect-file' => 'Tiedostonimi',
 'redirect-not-exists' => 'Arvoa ei löytynyt',
index d0de77e..1206c92 100644 (file)
@@ -2582,7 +2582,7 @@ Voir $2 pour une liste des suppressions récentes.',
 ** Vandalisme
 ** Violation des droits d’auteur
 ** Demande de l’auteur
-** Redirection erronée',
+** Redirection cassée',
 'delete-edit-reasonlist' => 'Modifier les motifs de suppression de page',
 'delete-toobig' => 'Cette page possède un historique important de modifications, dépassant $1 version{{PLURAL:$1||s}}.
 La suppression de telles pages a été restreinte pour prévenir des perturbations accidentelles de {{SITENAME}}.',
index 16904d7..4019ee7 100644 (file)
@@ -402,7 +402,7 @@ $messages = array(
 'specialpage' => 'विशेष पृष्ठ',
 'personaltools' => 'वैयक्तिक औज़ार',
 'postcomment' => 'नया अनुभाग',
-'articlepage' => 'लà¥\87à¤\96 देखें',
+'articlepage' => 'सामà¤\97à¥\8dरà¥\80 à¤ªà¥\83षà¥\8dठ देखें',
 'talk' => 'चर्चा',
 'views' => 'दर्शाव',
 'toolbox' => 'साधन पेटी',
@@ -436,7 +436,7 @@ $1',
 'aboutsite' => '{{SITENAME}} के बारे में',
 'aboutpage' => 'Project:परिचय',
 'copyright' => 'उपलब्ध सामग्री $1 के अधीन है जब तक अलग से उल्लेख ना किया गया हो।',
-'copyrightpage' => '{{ns:project}}:सरà¥\8dवाधिà¤\95ार',
+'copyrightpage' => '{{ns:project}}:à¤\95à¥\89पà¥\80राà¤\87à¤\9f',
 'currentevents' => 'हाल की घटनाएँ',
 'currentevents-url' => 'Project:हाल की घटनाएँ',
 'disclaimers' => 'अस्वीकरण',
@@ -480,7 +480,7 @@ $1',
 'hidetoc' => 'छिपाएँ',
 'collapsible-collapse' => 'छोटा करें',
 'collapsible-expand' => 'विस्तार करें',
-'thisisdeleted' => '$1 à¤¦à¥\87à¤\96à¥\87à¤\82 à¤¯à¤¾ à¤¬à¤¦à¤²à¥\87à¤\82?',
+'thisisdeleted' => '$1 à¤¦à¥\87à¤\96à¥\87à¤\82 à¤¯à¤¾ à¤µà¤¾à¤ªà¤¿à¤¸ à¤²à¤¾à¤\8fà¤\81?',
 'viewdeleted' => '$1 दिखायें?',
 'restorelink' => '{{PLURAL:$1|एक हटाया हुआ|$1 हटाये हुए}} बदलाव',
 'feedlinks' => 'फ़ीड:',
@@ -1344,10 +1344,10 @@ $1",
 'prefs-personal' => 'सदस्य व्यक्तिरेखा',
 'prefs-rc' => 'हाल में हुए बदलाव',
 'prefs-watchlist' => 'ध्यानसूची',
-'prefs-watchlist-days' => 'ध्यानसूचीमें दिखाने के दिन:',
+'prefs-watchlist-days' => 'ध्यानसूची में दिखाने के दिन:',
 'prefs-watchlist-days-max' => 'अधिकतम $1 {{PLURAL:$1|दिन}}',
 'prefs-watchlist-edits' => 'बढ़ाई हुई ध्यानसूची में दिखाने हेतु अधिकतम बदलाव:',
-'prefs-watchlist-edits-max' => 'à¤\85धिà¤\95तम à¤¸à¤\82à¤\96à¥\8dया: à¥§à¥¦à¥¦à¥¦',
+'prefs-watchlist-edits-max' => 'à¤\85धिà¤\95तम à¤¸à¤\82à¤\96à¥\8dया: à¤\8fà¤\95 à¤¹à¤\9c़ार',
 'prefs-watchlist-token' => 'ध्यानसूची टोकन',
 'prefs-misc' => 'अन्य',
 'prefs-resetpass' => 'कूटशब्द बदलें',
@@ -1576,7 +1576,7 @@ HTML टैग की जाँच करें।',
 'newuserlogpagetext' => 'यह सदस्य खातों के निर्माण का लॉग है।',
 
 # User rights log
-'rightslog' => 'सदसà¥\8dय à¤\85धिà¤\95ार à¤¸à¥\82à¤\9aà¥\80',
+'rightslog' => 'सदसà¥\8dय à¤\85धिà¤\95ार à¤²à¥\89à¤\97',
 'rightslogtext' => 'यह सदस्य अधिकारों में हुए बदलावों की सूची है।',
 
 # Associated actions - in the sentence "You do not have permission to X"
@@ -1605,7 +1605,7 @@ HTML टैग की जाँच करें।',
 'action-block' => 'इस सदस्य को संपादन करने से ब्लॉक करने',
 'action-protect' => 'इस पृष्ठ के सुरक्षा स्तर बदलने',
 'action-rollback' => 'किसी पृष्ठ का अंतिम सम्पादन करने वाले सदस्य के सम्पादन वापिस लेने',
-'action-import' => 'à¤\95िसà¥\80 à¤\94र à¤µà¤¿à¤\95ि à¤¸à¥\87 à¤¯à¤¹ à¤ªà¥\83षà¥\8dठ à¤\86यात à¤\95रनà¥\87',
+'action-import' => 'किसी और विकि से पृष्ठ आयात करने',
 'action-importupload' => 'फ़ाइल अपलोड द्वारा यह पृष्ठ आयात करे',
 'action-patrol' => 'अन्य सदस्यों के सम्पादन परीक्षित करने',
 'action-autopatrol' => 'अपने सम्पादन स्वचालित रूप से परीक्षित करने',
@@ -2086,7 +2086,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization देखें।',
 
 'withoutinterwiki' => 'बिना अंतरविकि कड़ियों वाले पृष्ठ',
 'withoutinterwiki-summary' => 'निम्न पृष्ठ अन्य भाषाओं के अवतरणों से नहीं जुड़ते हैं।',
-'withoutinterwiki-legend' => 'à¤\89पपद',
+'withoutinterwiki-legend' => 'à¤\89पसरà¥\8dà¤\97',
 'withoutinterwiki-submit' => 'दिखायें',
 
 'fewestrevisions' => 'सबसे कम अवतरणों वाले पृष्ठ',
@@ -2143,7 +2143,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization देखें।',
 'listusers' => 'सदस्यसूची',
 'listusers-editsonly' => 'केवल संपादन कर चुके सदस्य दिखाएँ',
 'listusers-creationsort' => 'निर्माण तिथि के आधार पर क्रमांकन करें',
-'usereditcount' => '$1 {{PLURAL:$1|सà¤\82पादन|सà¤\82पादन}}',
+'usereditcount' => '$1 {{PLURAL:$1|समà¥\8dपादन}}',
 'usercreated' => '$1 को $2 बजे बनाया गया, सदस्यनाम $3 है',
 'newpages' => 'नए पृष्ठ',
 'newpages-username' => 'सदस्यनाम:',
@@ -2172,7 +2172,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization देखें।',
 
 # Special:Log
 'specialloguserlabel' => 'कर्ता:',
-'speciallogtitlelabel' => 'प्रयोजन (शीर्षक):',
+'speciallogtitlelabel' => 'प्रयोजन (शीर्षक अथवा सदस्यनाम):',
 'log' => 'लॉग',
 'all-logs-page' => 'सभी सार्वजनिक लॉग',
 'alllogstext' => '{{SITENAME}} की सभी उपलब्ध लॉगों की प्रविष्टियों का मिला-जुला प्रदर्शन।
@@ -2406,9 +2406,11 @@ $UNWATCHURL
 'deleteotherreason' => 'अन्य/अतिरिक्त कारण:',
 'deletereasonotherlist' => 'अन्य कारण',
 'deletereason-dropdown' => '*हटाने के सामान्य कारण
-** लेखक की बिनती
+** स्पैम
+** बर्बरता
 ** कॉपीराइट उल्लंघन
-** बर्बरता',
+** लेखक का अनुरोध
+** टूटा अनुप्रेषण',
 'delete-edit-reasonlist' => 'हटाने के कारण संपादित करें',
 'delete-toobig' => 'इस पृष्ठ का संपादन इतिहास $1 से अधिक {{PLURAL:$1|अवतरण}} होने की वजह से बहुत बड़ा है।
 {{SITENAME}} के अनपेक्षित रूप से बंद होने से रोकने के लिये ऐसे पृष्ठों को हटाने की अनुमति नहीं है।',
@@ -2431,7 +2433,7 @@ $UNWATCHURL
 इस पृष्ठ का अन्तिम संपादन [[User:$3|$3]] ([[User talk:$3|वार्ता]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]) ने किया है।',
 'editcomment' => "संपादन सारांश था: \"''\$1''\"।",
 'revertpage' => '[[Special:Contributions/$2|$2]] ([[User talk:$2|Talk]]) के संपादनों को हटाकर [[User:$1|$1]] के अन्तिम अवतरण को पूर्ववत किया',
-'revertpage-nouser' => '(सदसà¥\8dय à¤¨à¤¾à¤® à¤¹à¤\9fाया à¤\97या à¤¹à¥\88) à¤¦à¥\8dवारा à¤\95िà¤\8f à¤\97à¤\8f à¤¸à¤\82पादन à¤\95à¥\8b à¤µà¤¾à¤ªà¤¿à¤¸ à¤ªà¥\81रानà¥\80 à¤¸à¥\8dथिति à¤®à¥\87à¤\82 à¤²à¤¾ à¤\95र à¤\87सà¤\95à¥\87 à¤ªà¤¹à¤²à¥\87 à¤\95à¥\87 [[User:$1|$1]] à¤¦à¥\8dवारा à¤¬à¤¨à¥\87 à¤\85वतरण à¤\95à¥\8b à¤«à¤¿à¤° à¤¸à¥\87 à¤¤à¤¾à¤\9c़ा à¤\85वतरण à¤¬à¤¨à¤¾या।',
+'revertpage-nouser' => '(सदसà¥\8dय à¤¨à¤¾à¤® à¤¹à¤\9fाया à¤\97या à¤¹à¥\88) à¤\95à¥\87 à¤¸à¤\82पादनà¥\8bà¤\82 à¤\95à¥\8b à¤¹à¤\9fाà¤\95र {{GENDER:$1|[[User:$1|$1]]}} à¤\95à¥\87 à¤\85नà¥\8dतिम à¤\85वतरण à¤\95à¥\8b à¤ªà¥\82रà¥\8dववत à¤\95िया।',
 'rollback-success' => '$1 के संपादन हटाए;
 $2 द्वारा संपादित अन्तिम अवतरण को पुनर्स्थापित किया।',
 
@@ -2489,7 +2491,7 @@ $2 द्वारा संपादित अन्तिम अवतरण 
 **अफलदायी सम्पादन युद्ध
 **अधिक यातायात वाला पृष्ठ',
 'protect-edit-reasonlist' => 'सुरक्षा के कारण बदलें',
-'protect-expiry-options' => '१ à¤\98à¤\82à¤\9fा:1 hour,१ à¤¦à¤¿à¤¨:1 day,१ à¤¸à¤ªà¥\8dताह:1 week,२ à¤¸à¤ªà¥\8dताह:2 weeks,१ à¤®à¤¹à¥\80ना:1 month,३ à¤®à¤¹à¥\80नà¥\87:3 months,६ à¤®à¤¹à¥\80नà¥\87:6 months,१ साल:1 year,हमेशा के लिए:infinite',
+'protect-expiry-options' => 'à¤\8fà¤\95 à¤\98à¤\82à¤\9fा:1 hour,à¤\8fà¤\95 à¤¦à¤¿à¤¨:1 day,à¤\8fà¤\95 à¤¸à¤ªà¥\8dताह:1 week,दà¥\8b à¤¸à¤ªà¥\8dताह:2 weeks,à¤\8fà¤\95 à¤®à¤¹à¥\80ना:1 month,तà¥\80न à¤®à¤¹à¥\80नà¥\87:3 months,à¤\9bà¤\83 à¤®à¤¹à¥\80नà¥\87:6 months,à¤\8fà¤\95 साल:1 year,हमेशा के लिए:infinite',
 'restriction-type' => 'अधिकार:',
 'restriction-level' => 'सुरक्षा-स्तर:',
 'minimum-size' => 'न्यूनतम आकार',
@@ -2508,7 +2510,7 @@ $2 द्वारा संपादित अन्तिम अवतरण 
 'restriction-level-all' => 'कोई भी स्तर',
 
 # Undelete
-'undelete' => 'हà¤\9fाया पृष्ठ देखें',
+'undelete' => 'हà¤\9fाà¤\8f पृष्ठ देखें',
 'undeletepage' => 'हटाए गए पृष्ठ देखें और पुनर्स्थापित करें',
 'undeletepagetitle' => "'''नीचे [[:$1|$1]] के हटाए गए अवतरण दर्शाए गये हैं।'''",
 'viewdeletedpage' => 'हटाए गए पृष्ठ देखें',
@@ -2517,46 +2519,46 @@ $2 द्वारा संपादित अन्तिम अवतरण 
 'undelete-fieldset-title' => 'अवतरण पुरानी स्थिति पर लाएँ',
 'undeleteextrahelp' => "पृष्ठ का संपूर्ण इतिहास वापस लाने के लिए सभी बक्सों से सही का निशान हटा दें और '''''{{int:undeletebtn}}''''' पर क्लिक करें।
 चुनिंदा इतिहास को वापस लाने के लिए उन अवतरणों के बगल के बक्सों पर सही का निशान लगाएँ और '''''{{int:undeletebtn}}''''' पर क्लिक करें।",
-'undeleterevisions' => '$1 {{PLURAL:$1|अवतरण}} लेखागार में हैं',
+'undeleterevisions' => '$1 अवतरण लेखागार में {{PLURAL:$1|है|हैं}}',
 'undeletehistory' => 'यदि आप पृष्ठ को पुनर्स्थापित करते हैं तो सभी अवतरण इतिहास में पुनर्स्थापित हो जायेंगे।
 हटाने के बाद यदि एक नया पृष्ठ उसी नाम से बनाया गया है तो पुनर्स्थापित अवतरण पिछले इतिहास में दर्शित होंगे।',
 'undeleterevdel' => 'यदि पुनर्स्थापन के फलस्वरूप शीर्ष पृष्ठ या फ़ाइल अवतरण आंशिक रूप से मिट सकता है, तो इसे नहीं किया जायेगा।
 ऐसी स्थिति में, आपको नवीनतम मिटाए गए अवतरण को बिना सही के निशान लगाये हुए या बिना छुपाये रखना होगा।',
-'undeletehistorynoadmin' => 'यह à¤ªà¥\83षà¥\8dठ à¤¨à¤¿à¤\95ाल दिया गया है।
-निà¤\95ालà¥\87 à¤\9cानà¥\87 à¤\95ा à¤\95ारन à¤¨à¥\80à¤\9aà¥\87 à¤¸à¤¾à¤°à¤¾à¤\82श à¤®à¥\87à¤\82 à¤¦à¤¿à¤¯à¤¾ à¤\97या à¤¹à¥\88, à¤\94र à¤¸à¤¾à¤¥ à¤¹à¥\80 à¤\89न à¤¸à¤¦à¤¸à¥\8dयà¥\8bà¤\82 à¤\95à¥\87 à¤¬à¤¾à¤°à¥\87 à¤®à¥\87à¤\82 à¤µà¤¿à¤¸à¥\8dतार à¤­à¥\80 à¤¦à¤¿à¤¯à¤¾ à¤\97या à¤¹à¥\88, à¤\9cिनà¥\8dहà¥\8bà¤\82नà¥\87 à¤¨à¤¿à¤\95ालà¥\87 à¤\9cानà¥\87 à¤¸à¥\87 à¤ªà¤¹à¤²à¥\87 à¤\87स à¤ªà¥\83षà¥\8dठ à¤\95à¥\8b à¤¸à¤\82पादित à¤\95िया à¤¹à¥\88
-à¤\87न à¤¹à¤\9fायà¥\87 à¤\97à¤\8f à¤\85वतरणà¥\8bà¤\82 à¤\95à¥\87 à¤µà¤¿à¤¦à¥\8dयमान à¤µà¤¿à¤·à¤¯ à¤µà¤¸à¥\8dतà¥\81 à¤\95à¥\87वल à¤ªà¥\8dरशासकों को ही उपलब्ध है।',
+'undeletehistorynoadmin' => 'यह à¤ªà¥\83षà¥\8dठ à¤¹à¤\9fा दिया गया है।
+हà¤\9fाà¤\8f à¤\9cानà¥\87 à¤\95ा à¤\95ारन à¤¨à¥\80à¤\9aà¥\87 à¤¸à¤¾à¤°à¤¾à¤\82श à¤®à¥\87à¤\82 à¤¦à¤¿à¤¯à¤¾ à¤\97या à¤¹à¥\88, à¤\94र à¤¸à¤¾à¤¥ à¤¹à¥\80 à¤\89न à¤¸à¤¦à¤¸à¥\8dयà¥\8bà¤\82 à¤\95à¥\87 à¤¬à¤¾à¤°à¥\87 à¤®à¥\87à¤\82 à¤µà¤¿à¤¸à¥\8dतार à¤­à¥\80 à¤¦à¤¿à¤¯à¤¾ à¤\97या à¤¹à¥\88, à¤\9cिनà¥\8dहà¥\8bà¤\82नà¥\87 à¤¹à¤\9fाà¤\8f à¤\9cानà¥\87 à¤¸à¥\87 à¤ªà¤¹à¤²à¥\87 à¤\87स à¤ªà¥\83षà¥\8dठ à¤\95à¥\8b à¤¸à¤\82पादित à¤\95िया à¤¥à¤¾
+à¤\87न à¤¹à¤\9fायà¥\87 à¤\97à¤\8f à¤\85वतरणà¥\8bà¤\82 à¤\95ा à¤ªà¤¾à¤  à¤\95à¥\87वल à¤ªà¥\8dरबà¤\82धकों को ही उपलब्ध है।',
 'undelete-revision' => '$1 ($4 को $5 बजे $3 द्वारा बनाया गया) का मिटाया हुआ संस्करण:',
 'undeleterevision-missing' => 'अमान्य अथवा अनुपस्थित अवतरण।
-या à¤¤à¥\8b à¤\86प à¤\97़लत à¤¸à¤®à¥\8dपरà¥\8dà¤\95 à¤ªà¥\8dरयà¥\8bà¤\97 à¤\95र à¤°à¤¹à¥\87 à¤¹à¥\88à¤\82, à¤¯à¤¾ à¤¯à¤¹ à¤\85वतरण à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤\95िया à¤\9cा à¤\9aà¥\81à¤\95ा à¤¹à¥\88, à¤\85थवा à¤\87सà¥\87 à¤²à¥\87à¤\96ाà¤\97ार à¤¸à¥\87 à¤¨à¤¿à¤\95ाल दिया गया है।',
-'undelete-nodiff' => 'पà¥\81रान à¤\85वतरण à¤¨à¤¹à¥\80à¤\82 à¤¹à¥\88à¤\82।',
+या à¤¤à¥\8b à¤\86प à¤\97़लत à¤\95ड़à¥\80 à¤ªà¥\8dरयà¥\8bà¤\97 à¤\95र à¤°à¤¹à¥\87 à¤¹à¥\88à¤\82, à¤¯à¤¾ à¤¯à¤¹ à¤\85वतरण à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤\95िया à¤\9cा à¤\9aà¥\81à¤\95ा à¤¹à¥\88, à¤\85थवा à¤\87सà¥\87 à¤²à¥\87à¤\96ाà¤\97ार à¤¸à¥\87 à¤¹à¤\9fा दिया गया है।',
+'undelete-nodiff' => 'à¤\95à¥\8bà¤\88 à¤ªà¥\81राना à¤\85वतरण à¤¨à¤¹à¥\80à¤\82 à¤®à¤¿à¤²à¤¾।',
 'undeletebtn' => 'वापस ले आयें',
-'undeletelink' => 'दà¥\87à¤\96à¥\87à¤\82/पà¥\81रानà¥\80 à¤¸à¥\8dथिति à¤ªà¤° à¤²à¤¾à¤\8fà¤\81',
+'undeletelink' => 'दà¥\87à¤\96à¥\87à¤\82/पà¥\81नरà¥\8dसà¥\8dथापित à¤\95रà¥\87à¤\82',
 'undeleteviewlink' => 'देखें',
 'undeletereset' => 'पूर्ववत करें',
 'undeleteinvert' => 'चुनाव उलटें',
-'undeletecomment' => 'à¤\9fिपà¥\8dपणà¥\80 à¤¹à¤\9fाना',
-'undeletedrevisions' => '{{PLURAL:$1|à¤\8fà¤\95 à¤°à¥\82पानà¥\8dतर à¤µà¤¾à¤ªà¤¸ à¤²à¤¾à¤¯à¤¾ à¤\97या|$1 à¤°à¥\82पानà¥\8dतर à¤µà¤¾à¤ªà¤¸ à¤²à¤¾à¤¯à¥\87 à¤\97यà¥\87}} à¤¹à¥\88',
-'undeletedrevisions-files' => '{{PLURAL:$1|1 à¤\85वतरण|$1 à¤\85वतरण}} à¤\94र {{PLURAL:$2|1 à¤«à¤¼à¤¾à¤\88ल|$2 à¤«à¤¼à¤¾à¤\87लà¥\87à¤\82}} à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤\95र à¤¦à¤¿à¤¯à¥\87ं',
-'undeletedfiles' => '{{PLURAL:$1|1 à¤«à¤¼à¤¾à¤\88ल|$1 à¤«à¤¼à¤¾à¤\88लें}} पुनर्स्थापित',
+'undeletecomment' => 'à¤\95ारण:',
+'undeletedrevisions' => '{{PLURAL:$1|à¤\8fà¤\95 à¤\85वतरण à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤\95िया|$1 à¤\85वतरण à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤\95ियà¥\87}}',
+'undeletedrevisions-files' => '{{PLURAL:$1|1 à¤\85वतरण|$1 à¤\85वतरण}} à¤\94र {{PLURAL:$2|1 à¤«à¤¼à¤¾à¤\87ल|$2 à¤«à¤¼à¤¾à¤\87लà¥\87à¤\82}} à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤\95र à¤¦à¥\80ं',
+'undeletedfiles' => '{{PLURAL:$1|1 à¤«à¤¼à¤¾à¤\87ल|$1 à¤«à¤¼à¤¾à¤\87लें}} पुनर्स्थापित',
 'cannotundelete' => 'पुनर्स्थापित नहीं कर सके:
 $1',
 'undeletedpage' => "'''$1 को पुनर्स्थापित कर दिया गया है'''
 
 हाल में हटाये गये तथा पुनर्स्थापित किये गए पन्नों की जानकारी के लिये [[Special:Log/delete|हटाने की लॉग]] देखें।",
-'undelete-header' => 'हाल में हटाये गये पृष्ठ देखने के लियें [[Special:Log/delete|हटाने की सूची]] देखें।',
+'undelete-header' => 'हाल में हटाये गये पृष्ठ देखने के लिये [[Special:Log/delete|हटाने का लॉग]] देखें।',
 'undelete-search-title' => 'हटाये गये पृष्ठ खोजें',
 'undelete-search-box' => 'हटाये गये पृष्ठ खोजें',
 'undelete-search-prefix' => 'शुरूआती शब्द अनुसार पृष्ठ खोजें:',
 'undelete-search-submit' => 'खोजें',
-'undelete-no-results' => 'हà¤\9fायà¥\87à¤\82 à¤\97यà¥\87à¤\82 à¤ªà¤¨à¥\8dनà¥\8bà¤\82à¤\95à¥\87 à¤\86रà¥\8dà¤\9aिवà¥\8dहमà¥\87à¤\82 à¤®à¥\87ल à¤\96ानà¥\87 à¤µà¤¾à¤²à¥\87 à¤ªà¥\83षà¥\8dठ à¤®à¤¿à¤²à¥\87 à¤¨à¤¹à¥\80à¤\82।',
-'undelete-filename-mismatch' => '$1 à¤¸à¤®à¤¯à¤\95à¥\87 à¤«à¤¼à¤¾à¤\87लà¤\95à¥\87 à¤¹à¤\9fायà¥\87 à¤\97यà¥\87 à¤\85वतरणà¤\95à¥\8b à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤¨à¤¹à¥\80à¤\82 à¤\95िया à¤\9cा à¤¸à¤\95ता: à¤«à¤¼à¤¾à¤\88ल का नाम मेल नहीं खाता',
-'undelete-bad-store-key' => '$1 à¤¸à¤®à¤¯à¤\95ा à¤«à¤¼à¤¾à¤\88ल à¤\85वतरण à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤¨à¤¹à¥\80à¤\82 à¤\95र à¤¸à¤\95तà¥\87à¤\82 à¤¹à¥\88à¤\82: à¤¹à¤\9fानà¥\87 à¤¸à¥\87 à¤ªà¤¹à¤²à¥\87 à¤«à¤¼à¤¾à¤\88ल à¤\85सà¥\8dतितà¥\8dवमà¥\87à¤\82 नहीं थी।',
-'undelete-cleanup-error' => 'à¤\87सà¥\8dतà¥\87मालमà¥\87à¤\82 à¤¨ à¤²à¤¾à¤\88 à¤\97à¤\88 "$1" à¤\86रà¥\8dà¤\9aिवà¥\8dह à¤«à¤¼à¤¾à¤\88ल à¤¹à¤\9fानà¥\87 à¤®à¥\87à¤\82 à¤¸à¤®à¤¸à¥\8dया à¤¹à¥\81à¤\88 à¤¹à¥\88à¤\82।',
-'undelete-missing-filearchive' => 'सिà¤\9aिà¤\95ा à¤ªà¥\81रालà¥\87à¤\96 à¤\95à¥\8dरमाà¤\82à¤\95 $1 à¤\95à¥\8b à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤\95रनà¥\87 à¤®à¥\87à¤\82 à¤\85सà¤\95à¥\8dषम à¤¹à¥\88à¤\82, à¤\95à¥\8dयà¥\8bà¤\82à¤\95ि à¤¯à¤¹ à¤\86à¤\81à¤\95ड़ाà¤\95à¥\8bष में उपलब्ध नहीं है।
+'undelete-no-results' => 'हà¤\9fाà¤\8f à¤\97à¤\8f à¤ªà¥\83षà¥\8dठà¥\8bà¤\82 à¤\95à¥\87 à¤²à¥\87à¤\96ाà¤\97ार à¤®à¥\87à¤\82 à¤®à¥\87ल à¤\96ातà¥\87 à¤\95à¥\8bà¤\88 à¤ªà¥\83षà¥\8dठ à¤¨à¤¹à¥\80à¤\82 à¤®à¤¿à¤²à¥\87।',
+'undelete-filename-mismatch' => '$1 à¤\95à¥\87 à¤«à¤¼à¤¾à¤\87ल à¤\95à¥\87 à¤¹à¤\9fायà¥\87 à¤\97यà¥\87 à¤\85वतरण à¤\95à¥\8b à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤¨à¤¹à¥\80à¤\82 à¤\95िया à¤\9cा à¤¸à¤\95ता: à¤«à¤¼à¤¾à¤\87ल का नाम मेल नहीं खाता',
+'undelete-bad-store-key' => '$1 à¤\95ा à¤«à¤¼à¤¾à¤\87ल à¤\85वतरण à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤¨à¤¹à¥\80à¤\82 à¤\95र à¤¸à¤\95तà¥\87 à¤¹à¥\88à¤\82: à¤¹à¤\9fानà¥\87 à¤¸à¥\87 à¤ªà¤¹à¤²à¥\87 à¤­à¥\80 à¤«à¤¼à¤¾à¤\87ल à¤®à¥\8cà¤\9cà¥\82द नहीं थी।',
+'undelete-cleanup-error' => 'पà¥\81रालà¥\87à¤\96 à¤®à¥\87à¤\82 à¤¸à¥\87 à¤\85पà¥\8dरयà¥\81à¤\95à¥\8dत à¤«à¤¼à¤¾à¤\87ल "$1" à¤¹à¤\9fानà¥\87 à¤®à¥\87à¤\82 à¤¤à¥\8dरà¥\81à¤\9fि।',
+'undelete-missing-filearchive' => 'फ़ाà¤\87ल à¤ªà¥\81रालà¥\87à¤\96 à¤\86à¤\88॰डà¥\80 $1 à¤\95à¥\8b à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤\95रनà¥\87 à¤®à¥\87à¤\82 à¤\85सà¤\95à¥\8dषम à¤¹à¥\88à¤\82, à¤\95à¥\8dयà¥\8bà¤\82à¤\95ि à¤¯à¤¹ à¤¡à¤¾à¤\9fाबà¥\87स में उपलब्ध नहीं है।
 या ऐसा भी हो सकता है कि इसे पहले से ही पुनर्स्थापित किया जा चुका हो।',
-'undelete-error' => 'पà¥\83षà¥\8dठ à¤\85विलà¥\8bपन में त्रुटि',
-'undelete-error-short' => 'फ़ाà¤\88ल à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤\95रनà¥\87 à¤®à¥\87à¤\82 à¤¸à¤®à¤¸à¥\8dया: $1',
-'undelete-error-long' => 'फ़ाà¤\88ल à¤ªà¥\81नरà¥\8dसà¥\8dथापित à¤\95रनà¥\87 à¤®à¥\87à¤\82 à¤\86à¤\88 à¤¹à¥\81à¤\88 à¤¸à¤®à¤¸à¥\8dयाà¤\8fà¤\82:
+'undelete-error' => 'पà¥\83षà¥\8dठ à¤ªà¥\81नरà¥\8dसà¥\8dथापन में त्रुटि',
+'undelete-error-short' => 'फ़ाà¤\87ल à¤ªà¥\81नरà¥\8dसà¥\8dथापन à¤®à¥\87à¤\82 à¤¤à¥\8dरà¥\81à¤\9fि: $1',
+'undelete-error-long' => 'फ़ाà¤\87ल à¤ªà¥\81नरà¥\8dसà¥\8dथापन à¤®à¥\87à¤\82 à¤\86à¤\88 à¤¤à¥\8dरà¥\81à¤\9fियाà¤\81:
 
 $1',
 'undelete-show-file-confirm' => 'क्या आप वाकई फ़ाइल "<nowiki>$1</nowiki>" के $2 को $3 बजे बने, हटाए जा चुके अवतरण को देखना चाहते हैं?',
@@ -2644,7 +2646,7 @@ $1',
 'ipbenableautoblock' => 'इस सदस्यद्वारा इस्तेमाल किया गया आखिरी आईपी एड्रेस और यहां से आगे इस सदस्य द्वारा इस्तेमालमें लाये जाने वाले सभी एड्रेस ब्लॉक करें।',
 'ipbsubmit' => 'इस सदस्य को और बदलाव करने से रोकें',
 'ipbother' => 'अन्य समय:',
-'ipboptions' => '२ à¤\98à¤\82à¤\9fà¥\87:2 hours,१ à¤¦à¤¿à¤¨:1 day,३ à¤¦à¤¿à¤¨:3 days,१ à¤¹à¤«à¥\8dता:1 week,२ à¤¹à¤«à¥\8dतà¥\87:2 weeks,१ à¤®à¤¹à¤¿à¤¨à¤¾:1 month,३ à¤®à¤¹à¤¿à¤¨à¥\87:3 months,६ à¤®à¤¹à¤¿à¤¨à¥\87:6 months,१ साल:1 year,हमेशा के लिये:infinite',
+'ipboptions' => 'दà¥\8b à¤\98à¤\82à¤\9fà¥\87:2 hours,à¤\8fà¤\95 à¤¦à¤¿à¤¨:1 day,तà¥\80न à¤¦à¤¿à¤¨:3 days,à¤\8fà¤\95 à¤¸à¤ªà¥\8dताह:1 week,दà¥\8b à¤¸à¤ªà¥\8dताह:2 weeks,à¤\8fà¤\95 à¤®à¤¹à¥\80ना:1 month,तà¥\80न à¤®à¤¹à¥\80नà¥\87:3 months,à¤\9bà¤\83 à¤®à¤¹à¥\80नà¥\87:6 months,à¤\8fà¤\95 साल:1 year,हमेशा के लिये:infinite',
 'ipbotheroption' => 'अन्य',
 'ipbotherreason' => 'अन्य/दूसरा कारण:',
 'ipbhidename' => 'संपादन व सूचियों से सदस्य नाम छिपाएँ',
@@ -3005,7 +3007,7 @@ $1 को बाध्य करने का कारण है: "$2"',
 'tooltip-ca-protect' => 'इस पृष्ठको सुरक्षित किजीयें',
 'tooltip-ca-unprotect' => 'इस पृष्ठ की सुरक्षा बदलें ।',
 'tooltip-ca-delete' => 'इस पृष्ठ को हटाएं',
-'tooltip-ca-undelete' => 'इस पृष्ठको हटाने से पहले किये गये बदलाव पुनर्स्थापित करें',
+'tooltip-ca-undelete' => 'इस पृष्ठ को हटाने से पहले किये गये बदलाव पुनर्स्थापित करें',
 'tooltip-ca-move' => 'यह पृष्ठ स्थानांतरित करें',
 'tooltip-ca-watch' => 'इस पृष्ठ को अपनी ध्यानसूची में डालें',
 'tooltip-ca-unwatch' => 'यह पृष्ठ अपने ध्यानसूचीसे हटाएं',
index 833749e..10adff5 100644 (file)
@@ -367,7 +367,7 @@ $messages = array(
 'underline-default' => 'Prema postavkama preglednika',
 
 # Font style option in Special:Preferences
-'editfont-style' => 'Uredi područje font stila:',
+'editfont-style' => 'Font u okviru za uređivanje',
 'editfont-default' => 'Prema postavkama preglednika',
 'editfont-monospace' => 'Font s jednakim razmakom',
 'editfont-sansserif' => 'Font Sans-serif',
@@ -829,7 +829,7 @@ Da bi spriječili zloupotrebu, moguće je poslati samo jedan e-mail za promjenu
 'mailerror' => 'Pogrješka pri slanju e-pošte: $1',
 'acct_creation_throttle_hit' => 'Posjetitelji ovog wikija koji rabe Vašu IP adresu napravili su {{PLURAL:$1|1 račun|$1 računa}} u posljednjem danu, što je najveći dopušteni broj u tom vremenskom razdoblju.
 Zbog toga posjetitelji s ove IP adrese trenutačno ne mogu otvoriti nove suradničke račune.',
-'emailauthenticated' => 'Vaša e-mail adresa je ovjerena $2 u $3.',
+'emailauthenticated' => 'vaša e-mail adresa je ovjerena $2 u $3.',
 'emailnotauthenticated' => 'Vaša e-mail adresa još nije ovjerena.
 Ne možemo poslati e-mail ni u jednoj od sljedećih naredbi.',
 'noemailprefs' => 'Nije navedena adresa elektroničke pošte, stoga sljedeće naredbe ne će raditi.',
@@ -879,6 +879,7 @@ Možda ste već uspješno promijenili Vašu lozinku ili ste zatražili novu priv
 'passwordreset-text-one' => 'Ispunite ovaj obrazac ako želite ponovno postaviti Vašu zaporku.',
 'passwordreset-legend' => 'Poništi lozinku',
 'passwordreset-disabled' => 'Poništavanje lozinke je onemogućeno na ovom wikiju.',
+'passwordreset-emaildisabled' => 'Funkcija e-pošte je onemogućena na ovom wikiju.',
 'passwordreset-username' => 'Suradničko ime:',
 'passwordreset-domain' => 'Domena:',
 'passwordreset-capture' => 'Pogledati krajnju poruku?',
@@ -1440,7 +1441,7 @@ Više informacija možete pronaći u [{{fullurl:{{#Special:Log}}/delete|page={{F
 'stub-threshold-disabled' => 'Onemogućeno',
 'recentchangesdays' => 'Broj dana prikazanih u nedavnim promjenama:',
 'recentchangesdays-max' => '(maksimalno $1 {{PLURAL:$1|dan|dana}})',
-'recentchangescount' => 'Broj izmjena za prikaz kao zadano:',
+'recentchangescount' => 'Zadani broj izmjena koje se prikazuju:',
 'prefs-help-recentchangescount' => 'Ovo uključuje nedavne promjene, stare izmjene, i evidencije.',
 'savedprefs' => 'Vaše postavke su sačuvane.',
 'timezonelegend' => 'Vremenska zona:',
@@ -1504,9 +1505,9 @@ Ne smije biti duži od $1 {{PLURAL:$1|znaka|znaka|znakova}}.',
 'prefs-editor' => 'Uređivač',
 'prefs-preview' => 'Prikaži kako će izgledati',
 'prefs-advancedrc' => 'Napredne mogućnosti',
-'prefs-advancedrendering' => 'Napredne opcije',
-'prefs-advancedsearchoptions' => 'Napredne opcije',
-'prefs-advancedwatchlist' => 'Napredne opcije',
+'prefs-advancedrendering' => 'Napredne mogućnosti',
+'prefs-advancedsearchoptions' => 'Napredne mogućnosti',
+'prefs-advancedwatchlist' => 'Napredne mogućnosti',
 'prefs-displayrc' => 'Prikaži opcije',
 'prefs-displaysearchoptions' => 'Mogućnosti prikaza',
 'prefs-displaywatchlist' => 'Mogućnosti prikaza',
@@ -2589,7 +2590,7 @@ $1',
 'contributions' => 'Doprinosi {{GENDER:$1|suradnika|suradnice}}',
 'contributions-title' => 'Suradnički doprinosi za $1',
 'mycontris' => 'Moji doprinosi',
-'contribsub2' => 'Za $1 ($2)',
+'contribsub2' => 'Za {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Nema promjena koje udovoljavaju ovim kriterijima.',
 'uctop' => '(vrh)',
 'month' => 'Od mjeseca (i ranije):',
index 7ef3b09..be06b1d 100644 (file)
@@ -182,8 +182,8 @@ $messages = array(
 'help' => 'Куцтохкам',
 'search' => 'Лахаp',
 'searchbutton' => 'Хьалаха',
-'go' => 'Дехьавала',
-'searcharticle' => 'Дехьавала',
+'go' => 'Дехьа гӀо',
+'searcharticle' => 'Дехьа гӀо',
 'history' => 'искар',
 'history_short' => 'Искар',
 'updatedmarker' => 'Со ханача денца хувцамаш хиннaд',
@@ -228,7 +228,7 @@ $messages = array(
 'lastmodifiedat' => 'Укх оагӀув тӀехьара  хувцам: $2, $1.',
 'viewcount' => 'Укх оагӀув тӀа бӀаргтасса хиннад {{PLURAL:$1|цхьазза|$1 шозза}}.',
 'protectedpage' => 'Лорама оагӀув',
-'jumpto' => 'Укхаза дехьавала/яла:',
+'jumpto' => 'Укхаза дехьа гӀо:',
 'jumptonavigation' => 'никътохкарг',
 'jumptosearch' => 'леха',
 'pool-timeout' => 'ЧIегатохара сабаран ха чакхаяьннай',
@@ -956,7 +956,7 @@ $messages = array(
 'allmessagesdefault' => 'Сатийна улла яздам',
 'allmessages-filter-all' => 'Дерригаш',
 'allmessages-language' => 'Мотт:',
-'allmessages-filter-submit' => 'Дехьавала/яла',
+'allmessages-filter-submit' => 'Дехьа гӀо',
 
 # Thumbnails
 'thumbnail-more' => 'Хьадоккхаде',
@@ -1102,7 +1102,7 @@ $messages = array(
 'confirm_purge_button' => 'ХIаа',
 
 # Multipage image navigation
-'imgmultigo' => 'Дехьавала/яла!',
+'imgmultigo' => 'Дехьа гӀо!',
 'imgmultigoto' => '$1 оагIув тIа дехьавала',
 
 # Table pager
index b43f299..8f588b9 100644 (file)
@@ -15,6 +15,7 @@
  * @author Dawid Deutschland
  * @author ITshnik
  * @author Kaganer
+ * @author MIKHEIL
  * @author Malafaya
  * @author Nemo bis
  * @author Nodar Kherkheulidze
@@ -1583,7 +1584,7 @@ $1",
 'enhancedrc-history' => 'ისტორია',
 'recentchanges' => 'ბოლო ცვლილებები',
 'recentchanges-legend' => 'ბოლო ცვლილებების პარამეტრები',
-'recentchanges-summary' => 'á\83£á\83\97á\83\95á\83\90á\83\9aá\83\97á\83\95á\83\90á\83\9aá\83\94á\83\97 á\83\95á\83\98á\83\99á\83\98á\83¨á\83\98 ბოლო ცვლილებებს ამ გვერდზე.',
+'recentchanges-summary' => 'á\83£á\83\97á\83\95á\83\90á\83\9aá\83\97á\83\95á\83\90á\83\9aá\83\94á\83\97 á\83\95á\83\98á\83\99á\83\98á\83¡ ბოლო ცვლილებებს ამ გვერდზე.',
 'recentchanges-feed-description' => 'ვიკის უახლესი ცვლილებების მეთვალყურეობა ამ არხში.',
 'recentchanges-label-newpage' => 'ამ რედაქტირებით შეიქმნა ახალი გვერდი',
 'recentchanges-label-minor' => 'ეს არის მცირე შესწორება',
@@ -1877,6 +1878,7 @@ $1',
 'listfiles_description' => 'აღწერილობა',
 'listfiles_count' => 'ვერსიები',
 'listfiles-show-all' => 'სურათების ძველი ვერსიების ჩართვა',
+'listfiles-latestversion' => 'მიმდინარე ვერსია',
 'listfiles-latestversion-yes' => 'დიახ',
 'listfiles-latestversion-no' => 'არა',
 
index fc1999f..9497dc9 100644 (file)
@@ -805,7 +805,7 @@ $2',
 'userlogin-noaccount' => '계정이 없나요?',
 'userlogin-joinproject' => '{{SITENAME}}에 가입하세요',
 'nologin' => '계정이 없나요? $1.',
-'nologinlink' => 'ê³\84ì \95ì\9d\84 ë§\8cë\93\9cì\84¸ì\9a\94',
+'nologinlink' => 'ê³\84ì \95ì\9d\84 ë§\8cë\93¤ê¸°',
 'createaccount' => '계정 만들기',
 'gotaccount' => '계정이 이미 있다면, $1.',
 'gotaccountlink' => '로그인하세요',
@@ -2598,7 +2598,7 @@ $UNWATCHURL
 'deletereasonotherlist' => '다른 이유',
 'deletereason-dropdown' => '* 일반적인 삭제 이유
 ** 스팸
-** 훼손 행위
+** 문서 훼손 행위
 ** 저작권 침해
 ** 작성자의 요청
 ** 깨진 넘겨주기',
index 5e71254..13e7b8e 100644 (file)
@@ -464,7 +464,7 @@ $1',
 # All link text and link target definitions of links into project namespace that get used by other message strings, with the exception of user group pages (see grouppage).
 'aboutsite' => 'Mombamomba ny {{SITENAME}}',
 'aboutpage' => 'Project:Mombamomba',
-'copyright' => '$1 no mifehy ny fampiasana ny votoatin-kevitra eto.',
+'copyright' => 'Ny lisansa $1 no mamehy ny fampiasana ity voatoatiny ity.',
 'copyrightpage' => '{{ns:project}}:Copyright',
 'currentevents' => 'Ny vaovao',
 'currentevents-url' => 'Project:Vaovao',
@@ -1428,7 +1428,7 @@ Tokony mba manana lohavy ambanimbany kokoa non'ny $1",
 'gender-unknown' => 'Tsy tia hanome ny antsipirihany aho',
 'gender-male' => 'Manova pejy wiki izy (lehilahy)',
 'gender-female' => 'Manova pejy wiki izy (vehivavy)',
-'prefs-help-gender' => "Ankifidy : Ampiasaina mba hifanaraka amin'ny lahi-vavy. Ho sarababem-bahoaka io fampahalalàna io.",
+'prefs-help-gender' => "Ankifidy : ampiasaina ho an'ny fifandraisan'ny rindrankajy aminao. Ho sarababem-bahoaka ity fampahalalana ity.",
 'email' => 'Imailaka',
 'prefs-help-realname' => "Anarana marina (afaka tsy fenoina): raha fenoinao ity dia hampiasaina hanomezana anao tambin'ny asa izay efainao eto.",
 'prefs-help-email' => 'Azo tsy omena ny adiresy imailaka, fa ilaina izy io raha sendra hadino ny tenimiafinao.',
@@ -1610,8 +1610,8 @@ Tsy haseho ny adiresy imailakao rehefa manoratra any aminao ny mpikambana hafa."
 'action-block' => 'manakana am-panoratana ny mpikambana iray',
 'action-protect' => "manova ny fanovàn'ity pejy ity",
 'action-rollback' => "Manafoana haingana ny fanovan'ny mpikambana farany nanova pejy iray",
-'action-import' => 'mampiditra ity pejy ity avy amina wiki hafa',
-'action-importupload' => 'hampiditra ity pejy ity avy amina rakitra nampidirina',
+'action-import' => "hampiditra ity pejy ity avy amin'ny wiki hafa",
+'action-importupload' => "hampiditra ity pejy ity amin'ny fampidirana rakitra",
 'action-patrol' => 'marihana ho hita ity version ity',
 'action-autopatrol' => 'manana ny fanovanao voamarina',
 'action-unwatchedpages' => 'hijery ny lisitry ny pejy tsy arahina',
@@ -2519,7 +2519,7 @@ $1',
 'contributions' => "Fandraisan'anjaran'ny mpikambana{{GENDER:$1}}",
 'contributions-title' => "Fandraisan'anjaran'i $1",
 'mycontris' => "Fandraisan'anjara",
-'contribsub2' => "ho an'ny $1 ($2)",
+'contribsub2' => "Ho an'ny $1 ($2)",
 'nocontribs' => "Tsy misy fanovana mifanaraka amin'ireo critères ireo.",
 'uctop' => '(ankehitriny)',
 'month' => "Tamin'ny volana (sy teo aloha) :",
@@ -2594,8 +2594,8 @@ Fenoy etsy ambany ny antony manokana (ohatra, mitanisà pejy nosomparana).",
 'ipb-confirm' => 'Sakanana marina',
 'badipaddress' => 'Tsy mety ny adiresy IP (invalid)',
 'blockipsuccesssub' => 'Vita soa aman-tsara ny sakana',
-'blockipsuccesstext' => 'Voasakana i [[Special:Contributions/$1|$1]].
-<br />Jereo ny [[Special:BlockList|lisitry ny IP voasakana]] raha hanala ny sakana efa misy.',
+'blockipsuccesstext' => 'Voasakana i [[Special:Contributions/$1|$1]].<br />
+Jereo ny [[Special:BlockList|lisitry ny voasakana]] raha hanala ny sakana efa misy.',
 'ipb-blockingself' => 'Hanakana ny kaontinao ianao ! Tena hanao izany ve ?',
 'ipb-confirmhideuser' => "Eo ampanakanana mpikambana miaraka amin'ny \"fanakonana mpikambana\" ampiasaina. Izany dia mamafa ny anaran'ilay mpikambana amin'ny listra ary amin'ny iditra laogy. Tena hanao izany ve ianao?",
 'ipb-edit-dropdown' => 'Hanova ny antony fanakanana tsipalotra',
@@ -2648,9 +2648,9 @@ Eo ambany ny laogim-panakanana.',
 Eo ambany ny laogim-pamafana.',
 'blocklogentry' => 'voasakana i "[[$1]]" mandritra ny $2 ; antony : $3',
 'reblock-logentry' => "nanova ny parametatry ny sakan'i [[$1]], ary tapitra amin'ny $2. Ny antony dia ''$3''",
-'blocklogtext' => "Eto no ahitana ny tantaran'ny hetsika momba ny fisakanana sy ny fanafoanana fisakanana mpandray anjara.
-Ireo adiresy IP voasakana ho azy dia tsy miseho eto. Jereo ao amin'ny [[Special:BlockList|lisitry ny IP voasakana]]
-ny lisitry ny fisakanana sy fandrarana na tanteraka misy ankehitriny.",
+'blocklogtext' => "Eto no ahitana ny tantaran'ny hetsika momba ny fisakanana sy ny famoanana ny fisakanana mpandray anjara.
+Tsy aseho eto ny adiresy IP voasakana ho azy.
+Jereo ao amin'ny [[Special:BlockList|lisitry ny sakana]] hahitana ny lisitry ny sakana mihatra amin'izao fotoana izao",
 'unblocklogentry' => "voaaisotra ny sakana an'i $1",
 'block-log-flags-anononly' => 'mpikambana tsy nisoratra anarana ihany',
 'block-log-flags-nocreate' => 'tsy mahazo manokatra kaonty',
@@ -2718,15 +2718,15 @@ Mba hahafahany manidy na mamoha ny banky angona, mila azo soratan'ny lohamilin-t
 # Move page
 'move-page' => "Hanova anarana an'i $1",
 'move-page-legend' => 'Afindrao toerana ny pejy',
-'movepagetext' => "Ampiasao ilay formulaire eo ambany eo mba hamindra azy toerana, voakisaka any amin'ny anarany ankehitriny ny tantarany. Lasa pejy-na redirection ilay pejy taloha, (manondro makany amin'ny anarany ankehitriny ilay pejy).
-Afaka manao ''update'' ny redirect ianao.
+'movepagetext' => "Ampiasao ilay fôrmiolera eo ambany eo mba hamindra azy toerana, voakisaka any amin'ny anarany ankehitriny ny tantarany. Lasa pejy fihodinana ilay pejy taloha, (manondro makany amin'ny anarany ankehitriny ilay pejy).
+
+Afaka manavao ho azy ny fihodinana mankany amin'ny lohateny taloha ianao. Raha tsy fidinao ny manao izany, marino tsara ny fisian'ireo [[Special:DoubleRedirects|fihodinana roa]] na [[Special:BrokenRedirects|fihodinana tapaka]]. Ianao no manana andrikitra amin'ny fanamarinana ny tsi-fitapahan'ireo rohy.
 
 Jereo koa fa '''tsy afaka''' akisaka ilay pejy ra mitovy anarana amin'ny pejy efa misy ny anarana ny anarana vaovaon'ilay pejy tianao akisaka, fa mety atao ihany io asa io ra tsy misy nininona ilay pejy. Afaka manolo anarana pejy efa manondro ny fihisiny taloha ianao ra diso ianao, fa tsy afaka ataonao no manitsaka pejy efa misy.
 
 '''TANDREMO'''
 
-Mety fanom-panona ihany ny mpitsidika ra be mpitsidika io pejy ovainao anarana io ;
-Tsy maintsy fantatrao tsara ny vokany aloha no mitohy.",
+Mety ho fiovana lehibe ary tsy ampoizina ny fanaovana izany ho an'ny pejy voatsidika mateetika ; fantaro tsara ny fiantraika alohan'ny manao izany.",
 'movepagetalktext' => "Voasikaka koa ny pejin-dresak'ity pejy ity '''ra''' :
 
 * Efa misy pejin-dresaka efa misy votoatiny amin'ilay anarana vaovao, na
@@ -3029,7 +3029,7 @@ Mamerina ny version taloha io asa io ary afaka manometraka ny antony anatin'ny a
 'pageinfo-recent-authors' => "Isa vao haingan'ny mpanoratra misongadina",
 'pageinfo-hidden-categories' => 'Sokajy nafenina{{PLURAL:$1}} ($1)',
 'pageinfo-templates' => 'Endrika natsofoka{{PLURAL:$1}} ($1)',
-'pageinfo-transclusions' => "Pejy natsofoka tanatin'i ($1)",
+'pageinfo-transclusions' => "Pejy natsofoka tanatin'i ($1){{PLURAL:}}",
 'pageinfo-toolboxlink' => 'Fampahalalana mikasika ny pejy',
 'pageinfo-redirectsto' => "Fihdinana mankany amin'ny",
 'pageinfo-redirectsto-info' => 'fampahalalana',
@@ -3089,7 +3089,7 @@ Raha alefanao ilay izy, mety ho simban'io renifango io ny solosainao.",
 'svg-long-error' => 'Rakitra SVG tsy ekena : $1',
 'show-big-image' => "Hijery ny tena haben'ny sary",
 'show-big-image-preview' => "Haben'ny topi-maso: $1.",
-'show-big-image-other' => 'Habe hafa: $1{{PLURAL:$1}}',
+'show-big-image-other' => 'Habe hafa: $1{{PLURAL:$2}}',
 'show-big-image-size' => '$1 × $2 teboka',
 'file-info-gif-looped' => 'miverimberina',
 'file-info-gif-frames' => 'sary{{PLURAL:$1}} $1',
@@ -3354,7 +3354,9 @@ raha vao nanokatra kaonty ianao, dia miandrasa minitra vitsivitsy mba ho tonga i
 'confirmemail_sent' => 'Lasa ny fanamarinana ny imailaka.',
 'confirmemail_oncreate' => "Nalefa tany amin'ny adiresy imailakao ny kaody fanamarinana.
 Tsy ilaina ampaisaina io tenimiafina io rehefa hiditra eto amin'ity wiki ity ianao, fa tsy maintsy omenao ilay izy rehefa mampiasa tao mifototra amin'ny imailaka.",
-'confirmemail_sendfailed' => 'Tsy lasa ny fanamarinana ny imailaka. Hamarino ny adiresy fandrao misy litera tsy mety.',
+'confirmemail_sendfailed' => "Tsy lasa ny fanamarinana ny imailaka. Hamarino ny adiresy fandrao misy litera tsy mety.
+
+Ity no naverin'ny mpandefa mailaka : $1",
 'confirmemail_invalid' => 'Tsy mety ilay fango fanamarinana. Angamba efa lany daty?',
 'confirmemail_needlogin' => 'Mila $1 ianao raha hanamarina ny adiresy imailakao.',
 'confirmemail_success' => 'Voamarina ny adiresy imailakao. Afaka [[Special:UserLogin|miditra]] ianao ankehitriny ary mankafia ny wiki.',
@@ -3481,7 +3483,7 @@ Andramo ny topi-maso tsotra',
 'specialpages-group-highuse' => 'Pejy ampiasaina mafy',
 'specialpages-group-pages' => 'Lisitra ny pejy',
 'specialpages-group-pagetools' => "Fitaovna ho an'ny pejy",
-'specialpages-group-wiki' => "Datan'ny wiki sy fitaovana",
+'specialpages-group-wiki' => 'Data sy fitaovana',
 'specialpages-group-redirects' => 'Pejy manokana voaodina',
 'specialpages-group-spam' => 'Fitaovana fanalana spam',
 
@@ -3546,13 +3548,13 @@ Andramo ny topi-maso tsotra',
 'revdelete-restricted' => "nametraka fanerena ho an'ny mpandrindra",
 'revdelete-unrestricted' => "fanerena nesorina tamin'ny mpandrindra",
 'logentry-move-move' => "nanova ny anaran'i $3 ho $4 i $1",
-'logentry-newusers-newusers' => 'Noforonina ny kaontim-pikambana $1',
-'logentry-newusers-create' => 'Noforonina ny kaontim-pikambana $1',
-'logentry-newusers-create2' => "Noforonin'i $1 ny kaomtim-pikambana $3",
-'logentry-newusers-autocreate' => 'Noforonina ho azy ny kaontim-pikambana $1',
-'logentry-rights-rights' => "$1 dia nanova ny sokajim-pikambana isian'i $3 avy amin'ny $4 lasa $5",
-'logentry-rights-rights-legacy' => "$1 nanova ny vonodrom-pikambana isian'i $3",
-'logentry-rights-autopromote' => 'Lasa $5 ho azy i $1 izay $4 taloha',
+'logentry-newusers-newusers' => '{{GENDER:$2|Noforonina}} ny kaontim-pikambana $1',
+'logentry-newusers-create' => '{{GENDER:$2|Noforonina}} ny kaontim-pikambana $1',
+'logentry-newusers-create2' => "{{GENDER:$2|Noforonin}}'i $1 ny kaomtim-pikambana $3",
+'logentry-newusers-autocreate' => '{{GENDER:$2|Noforonina}} ho azy ny kaontim-pikambana $1',
+'logentry-rights-rights' => "$1 dia nanova ny sokajim-pikambana isian'i $3 avy amin'ny $4 lasa $5{{GENDER:$2}}",
+'logentry-rights-rights-legacy' => "{{GENDER:$2}}$1 nanova ny vonodrom-pikambana isian'i $3",
+'logentry-rights-autopromote' => '{{GENDER:$2}}Lasa $5 ho azy i $1 izay $4 taloha',
 'rightsnone' => '(tsy misy)',
 
 # Feedback
index 5e17a4d..f60d56a 100644 (file)
@@ -2243,7 +2243,7 @@ Invoer: inhoudstype/subtype, bijvoorbeeld <code>image/jpeg</code>.',
 # Unused templates
 'unusedtemplates' => 'Ongebruikte sjablonen',
 'unusedtemplatestext' => 'Deze pagina geeft alle pagina\'s weer in de naamruimte {{ns:template}} die op geen enkele pagina gebruikt worden.
-Vergeet niet de "Koppelingen naar deze pagina" te controleren alvorens deze sjabloon te verwijderen.',
+Vergeet niet de "Koppelingen naar deze pagina" te controleren alvorens dit sjabloon te verwijderen.',
 'unusedtemplateswlh' => 'andere koppelingen',
 
 # Random page
index 57ba7ef..84f9f0c 100644 (file)
@@ -808,26 +808,26 @@ test ch'a-i é già. Sossì dle vire a riva quand un a deuvra un servent anònim
 'edit_form_incomplete' => "'''Chèiche part dël formolari ëd modìfica a son pa rivà al servent; ch'a contròla për da bin che soe modìfiche a-i sio ancora e ch'a preuva torna.'''",
 'editing' => 'Modìfica ëd $1',
 'creating' => 'Creé $1',
-'editingsection' => 'I soma dapress a modifiché $1 (session)',
-'editingcomment' => 'I soma dapress a modifiché $1 (neuva session)',
-'editconflict' => "Conflit d'edission: $1",
-'explainconflict' => "Cheidun d'àutr a l'ha salvà soa version dl'artìcol antramentré che chiel (chila) as prontava la soa.
+'editingsection' => 'Modìfica ëd $1 (session)',
+'editingcomment' => 'Modìfica ëd $1 (neuva session)',
+'editconflict' => 'Conflit ëd modìfica: $1',
+'explainconflict' => "Cheidun d'àutr a l'ha salvà soa version dl'artìcol antramentre che chiel as prontava la soa.
 Ël quàder ëd modìfica dë dzora a mostra ël test ëd l'artìcol coma a resta adess (visadì, lòn che a-i é ant sla Ragnà). Soe modìfiche a stan ant ël quàder dë sota.
 Ën volend a peul gionté soe modìfiche ant ël quàder dë dzora.
 '''Mach''' ël test ant ël quàder dë dzora a sarà salvà, ën sgnacand ël boton \"{{int:savearticle}}\".",
 'yourtext' => 'Sò test',
-'storedversion' => 'Version memorisà',
-'nonunicodebrowser' => "'''A L'EUJ! Sò programa ëd navigassion (browser) a travaja pa giust con lë stàndard unicode. I soma obligà a dovré dij truschin përchè a peula salvesse sò artìcoj sensa problema: ij caràter che a son nen ASCII a jë s-ciairerà ant ël quàder ëd modìfica test coma còdes esadecimaj.'''",
-'editingold' => "'''CHE A FASA MACH ATENSION: che a sta fasend-je dle modìfiche a na version nen agiornà dl'artìcol.<br />
+'storedversion' => 'La version memorisà',
+'nonunicodebrowser' => "'''A L'EUJ! Sò programa ëd navigassion a marcia pa giust con lë stàndard Unicode. I soma obligà a dovré dij truschin përchè a peula salvesse sò artìcoj sensa problema: ij caràter che a son nen ASCII a jë s-ciairerà ant ël quàder ëd modìfica dël test coma còdes esadecimaj.'''",
+'editingold' => "'''CHE A FASA MACH ATENSION: che a l'é an camin ch'a modìfica na version nen agiornà dl'artìcol.<br />
 Se a la salva parèj, lòn che a l'era stàit fàit dapress a sta revision-sì as perdrà d'autut.'''",
 'yourdiff' => 'Diferense',
-'copyrightwarning' => "Che a ten-a për piasì present che tute le contribussion a {{SITENAME}} as consìdero dàite sota a na licensa ëd la sòrt $2 (che a varda $1 për avèj pì 'd detaj).
-Se a veul nen che sò test a peula esse modificà e distribuì da qualsëssìa përson-a sensa gnun-a limitassion ëd gnun-a sòrt, che a lo buta pa ansima a {{SITENAME}}, ma pitòst che as lo pùblica ansima a un sò sit përsonal.<br />
-Ën mandand ës test-sì chiel (chila) as fa garant sota soa responsabilità che ël test a l'ha scrivusslo despërchiel (daspërchila) coma original, ò pura che a l'ha tracopialo da na sorgiss ëd pùblich domini, ò da n'àutra sorgiss dla midema sòrt, ò pura che chiel (chila) a l'ha arseivù autorisassion scrita a dovré sto test e che sòn a peul dimostrelo.<br />
-'''DOVRÉ PA MAI DËL MATERIAL COATÀ DA DRIT D'AUTOR (c) SENSA AVÈJ N'AUTORISASSION SCRITA PËR FELO!!!'''",
-'copyrightwarning2' => "Për piasì, che a ten-a present che tute le contribussion a {{SITENAME}} a peulo esse modificà ò scancelà da dj'àutri contributor. Se a veul nen che lòn che a scriv a ven-a modificà sensa limitassion ëd gnun-a sòrt, che a lo manda nen ambelessì.<br />
-Ant l'istess temp, ën mandand dël material un as pija la responsabilità dë dì che a l'ha scrivusslo daspërchiel (ò daspërchila), ò pura che a l'ha copialo da na sorgiss ëd domini pùblich, ò pura da 'nt n'àutra sorgiss dla midema sòrt (che a varda $1 për avèj pì d'anformassion).
-'''CHE A MANDA PA DËL MATERIAL COATÀ DA DRIT D'AUTOR SENSA AVÈJ AVÙ ËL PËRMESS SCRIT DË FELO!'''",
+'copyrightwarning' => "Che a ten-a për piasì da ment che tute le contribussion a {{SITENAME}} as consìdero dàite sota a na licensa ëd la sòrt $2 (che a varda $1 për avèj pì 'd detaj).
+Se a veul nen che sò test a peula esse modificà e distribuì da qualsëssìa përson-a sensa gnun-a limitassion ëd gnun-a sòrt, che a lo buta pa ambelessì.<br />
+Ën mandand ës test-sì chiel as fa garant sota soa responsabilità che ël test a l'ha scrivusslo despërchiel, ò pura che a l'ha tracopialo da na sorgiss ëd pùblich domini, ò da n'àutra sorgiss dla midema sòrt.
+'''Anserì mai dël material coatà da drit d'autor sensa avèj n'autorisassion për felo!'''",
+'copyrightwarning2' => "Për piasì, che a ten-a da ment che tute le contribussion a {{SITENAME}} a peulo esse modificà ò scancelà da d'àutri contributor. Se a veul nen che lòn che a scriv a ven-a modificà sensa limitassion ëd gnun-a sòrt, che a lo manda nen ambelessì.<br />
+Ant l'istess temp, ën mandand dël material un as pija la responsabilità dë dì che a l'ha scrivusslo daspërchiel, ò pura che a l'ha copialo da na sorgiss ëd domini pùblich, ò pura da 'nt n'àutra sorgiss dla midema sòrt (che a varda $1 për avèj pì d'anformassion).
+'''Che a manda pa dël material coata da drit d'autor sensa avèj avù ël përmess ëd felo!'''",
 'longpageerror' => "'''EROR: Ël test che a l'ha mandà a l'é longh {{PLURAL:$1|un kilobyte|$1 kilobyte}} , che a resta pì che ël
 lìmit màssim ëd {{PLURAL:$2|un kilobyte|$2 kilobyte}}. Parèj as peul pa salvesse.",
 'readonlywarning' => "'''Avis: La base dat a l'é stàita blocà për manutension, e donca a podrà pa salvesse soe modìfiche tut sùbit.'''
index 378334f..9d706f3 100644 (file)
@@ -652,7 +652,7 @@ $1',
 'mailerror' => 'د برېښليک د لېږلو ستونزه: $1',
 'acct_creation_throttle_hit' => 'د همدې ويکي کارنانو په وروستيو ورځو کې ستاسې د IP پتې په کارولو سره {{PLURAL:$1|1 گڼون|$1 گڼونونه}} جوړ کړي، چې دا په همدې مودې کې د گڼونونو د جوړولو تر ټولو ډېر شمېر دی چې اجازه يې ورکړ شوې.
 نو په همدې خاطر د اوس لپاره د همدې IP پتې کارنان نه شي کولای چې نور گڼونونه جوړ کړي.',
-'emailauthenticated' => 'ستاسÛ\90 Ø¨Ø±Û\90Ú\9aÙ\84Ù\8aÚ© Ù¾ØªÙ\87 Ù¾Ù\87 $2 Ù\86Û\90Ù¼Ù\87 Ù¾Ù\87 $3 Ø¨Ø¬Ù\88 Ø¯ Ù\85Ù\86Ù\84Ù\88 Ù\88Ú\93 Ù\88Ú«رځېده.',
+'emailauthenticated' => 'ستاسÛ\90 Ø¨Ø±Û\90Ú\9aÙ\84Ù\8aÚ© Ù¾ØªÙ\87 Ù¾Ù\87 $2 Ù\86Û\90Ù¼Ù\87 Ù¾Ù\87 $3 Ø¨Ø¬Ù\88 Ø¯ Ù\85Ù\86Ù\84Ù\88 Ù\88Ú\93 Ù\88Ú¯رځېده.',
 'emailnotauthenticated' => 'لا تر اوسه ستاسې برېښليک پته د منلو وړ نه ده ګرځېدلې. د لاندې ځانګړتياو لپاره به تاسې ته هېڅ کوم برېښليک و نه لېږل شي.',
 'noemailprefs' => 'ددې لپاره چې دا کړنې کار وکړي نو تاسو يو برېښليک وټاکۍ.',
 'emailconfirmlink' => 'د خپل د برېښليک پتې پخلی وکړی',
index 25fdbd5..9839617 100644 (file)
@@ -1556,8 +1556,9 @@ Não deverá conter mais de $1 {{PLURAL:$1|carácter|caracteres}}.',
 'yourgender' => 'Como prefere ser descrito?',
 'gender-unknown' => 'Prefiro não dizer',
 'gender-male' => 'Ele edita páginas wiki',
-'gender-female' => 'Feminino',
-'prefs-help-gender' => 'Opcional: usado pelo programa para ajuste das mensagens ao género do utilizador.
+'gender-female' => 'Ela edita páginas wiki',
+'prefs-help-gender' => 'Esta preferência é opcional.
+O software usa o seu valor para o endereçar e para o mencionar a outros usando o género gramatical apropriado.
 Esta informação será pública.',
 'email' => 'Correio electrónico',
 'prefs-help-realname' => 'O fornecimento do nome verdadeiro é opcional.
@@ -1796,7 +1797,7 @@ As suas [[Special:Watchlist|páginas vigiadas]] aparecem a '''negrito'''.",
 'reuploaddesc' => 'Cancelar o envio e voltar ao formulário de carregamento',
 'upload-tryagain' => 'Submeta a descrição do ficheiro modificado',
 'uploadnologin' => 'Não autenticado',
-'uploadnologintext' => 'Tem de estar [[Special:UserLogin|autenticado]] para enviar ficheiros.',
+'uploadnologintext' => 'Tem de $1 para enviar ficheiros.',
 'upload_directory_missing' => 'O diretório de carregamento de ficheiros ($1) não existe e o servidor de internet não conseguiu criá-lo.',
 'upload_directory_read_only' => 'O servidor de internet não possui permissão de escrita no diretório de carregamento de ficheiros ($1).',
 'uploaderror' => 'Erro ao carregar',
@@ -2246,7 +2247,7 @@ Agora redirecciona para [[$2]].',
 'mostinterwikis' => 'Páginas com mais interwikis',
 'mostrevisions' => 'Páginas com mais revisões',
 'prefixindex' => 'Todas as páginas iniciadas por',
-'prefixindex-namespace' => 'Todas as páginas com prefixo (domínio $1)',
+'prefixindex-namespace' => 'Todas as páginas com prefixo (espaço nominal $1)',
 'prefixindex-strip' => 'Remover prefixo',
 'shortpages' => 'Páginas curtas',
 'longpages' => 'Páginas longas',
@@ -2526,9 +2527,11 @@ Consulte $2 para um registo de eliminações recentes.',
 'deleteotherreason' => 'Outro/motivo adicional:',
 'deletereasonotherlist' => 'Outro motivo',
 'deletereason-dropdown' => '* Motivos de eliminação comuns
-** Pedido do autor
+** Spam
+** Vandalismo
 ** Violação de direitos de autor
-** Vandalismo',
+** Pedido do autor
+** Redirecionamento quebrado',
 'delete-edit-reasonlist' => 'Editar motivos de eliminação',
 'delete-toobig' => 'Esta página tem um histórico longo, com mais de $1 {{PLURAL:$1|edição|edições}}.
 A eliminação de páginas como esta foi restringida na {{SITENAME}}, para evitar problemas acidentais.',
@@ -4208,7 +4211,7 @@ Caso contrário, pode facilmente usar o formulário abaixo. O seu comentário se
 
 # Limit report
 'limitreport-cputime-value' => '$1 {{PLURAL:$1|segundo|segundos}}',
-'limitreport-postexpandincludesize-value' => '$1/$2 bytes',
-'limitreport-templateargumentsize-value' => '$1/$2 bytes',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
 
 );
index 108a817..c7d1660 100644 (file)
@@ -789,6 +789,8 @@ Não se esqueça de personalizar as suas [[Special:Preferences|preferências no
 'userlogin-resetpassword-link' => 'Troque sua senha',
 'helplogin-url' => 'Help:Iniciar sessão',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Ajuda para iniciar sessão]]',
+'userlogin-loggedin' => 'Você já está conectado como {{GENDER:$1|$1}}.
+Use o formulário abaixo para iniciar sessão como outro usuário.',
 'createacct-join' => 'Insira suas informações abaixo.',
 'createacct-another-join' => 'Preeencha as informações para a nova conta',
 'createacct-emailrequired' => 'Endereço de e-mail',
@@ -2539,10 +2541,12 @@ Consulte $2 para um registro de eliminações recentes.',
 'deletecomment' => 'Motivo:',
 'deleteotherreason' => 'Justificativa adicional:',
 'deletereasonotherlist' => 'Outro motivo',
-'deletereason-dropdown' => '* Motivos de eliminação comuns
-** Pedido do autor
+'deletereason-dropdown' => '* Motivos comuns para eliminação
+** Spam
+** Vandalismo
 ** Violação de direitos de autor
-** Vandalismo',
+** A pedido do autor
+** Redirecionamento inválido',
 'delete-edit-reasonlist' => 'Editar motivos de eliminação',
 'delete-toobig' => 'Esta página possui um longo histórico de edições, com mais de $1 {{PLURAL:$1|edição|edições}}.
 A eliminação de tais páginas foi restrita, a fim de se evitarem problemas acidentais em {{SITENAME}}.',
@@ -2703,7 +2707,7 @@ $1',
 'contributions' => 'Contribuições {{GENDER:$1|do usuário|da usuária}}',
 'contributions-title' => 'Contribuições {{GENDER:$1|do usuário|da usuária}} $1',
 'mycontris' => 'Contribuições',
-'contribsub2' => 'Para $1 ($2)',
+'contribsub2' => 'Para {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Não foram encontradas mudanças com este critério.',
 'uctop' => '(atual)',
 'month' => 'Mês (inclusive anteriores):',
@@ -4047,7 +4051,10 @@ Em conjunto com este programa deve ter recebido [{{SERVER}}{{SCRIPTPATH}}/COPYIN
 'tags-tag' => 'Nome da etiqueta',
 'tags-display-header' => 'Aparência nas listas de modificações',
 'tags-description-header' => 'Descrição completa do significado',
+'tags-active-header' => 'Ativo?',
 'tags-hitcount-header' => 'Modificações etiquetadas',
+'tags-active-yes' => 'Sim',
+'tags-active-no' => 'Não',
 'tags-edit' => 'editar',
 'tags-hitcount' => '$1 {{PLURAL:$1|modificação|modificações}}',
 
@@ -4212,8 +4219,11 @@ Caso contrário, você poderá usar o formulário simplificado a seguir. Seu com
 'limitreport-walltime-value' => '$1 {{PLURAL:$1|segundo|segundos}}',
 'limitreport-ppvisitednodes' => 'Número de nós visitados pelo pré-processador',
 'limitreport-ppgeneratednodes' => 'Número de nós gerados pelo pré-processador',
+'limitreport-postexpandincludesize' => 'Tamanho de inclusão pós-expansão',
 'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
+'limitreport-templateargumentsize' => 'Argumento do tamanho da predefinição',
 'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
 'limitreport-expansiondepth' => 'Máxima profundidade de expansão',
+'limitreport-expensivefunctioncount' => 'Conta da função expansiva do analizador',
 
 );
index 74d13ac..6e0798a 100644 (file)
@@ -6619,24 +6619,20 @@ See also:
 * {{msg-mw|Ip range toolarge}}',
 'blockme' => '{{doc-special|BlockMe|unlisted=1}}
 This feature is disabled by default.',
-'proxyblocker' => 'Used in [[Special:BlockMe]].
+'proxyblocker' => 'Username for blocking IP addresses listed in [[mw:Manual:$wgProxyList|$wgProxyList]].
 
 See also:
-* {{msg-mw|proxyblocker-disabled}}
-* {{msg-mw|proxyblockreason}}
-* {{msg-mw|proxyblocksuccess}}',
+* {{msg-mw|proxyblockreason}}',
 'proxyblocker-disabled' => 'Used in [[Special:BlockMe]].
 
 See also:
 * {{msg-mw|proxyblocker}}
 * {{msg-mw|proxyblockreason}}
 * {{msg-mw|proxyblocksuccess}}',
-'proxyblockreason' => 'Used as explanation of the reason in [[Special:BlockMe]].
+'proxyblockreason' => 'Reason for blocking IP addresses listed in [[mw:Manual:$wgProxyList|$wgProxyList]].
 
 See also:
-* {{msg-mw|proxyblocker-disabled}}
-* {{msg-mw|proxyblocker}}
-* {{msg-mw|proxyblocksuccess}}',
+* {{msg-mw|proxyblocker}}',
 'proxyblocksuccess' => 'Used in [[Special:BlockMe]].
 
 See also:
index afcb7f7..0dc8417 100644 (file)
@@ -55,6 +55,7 @@
  * @author Incnis Mrsi
  * @author Iniquity
  * @author Innv
+ * @author Ivan Shmakov
  * @author Jackie
  * @author JenVan
  * @author Jl
@@ -1449,9 +1450,10 @@ $1",
 'revdelete-concurrent-change' => 'Ошибка изменения записи от $2, $1: её статус был изменён кем-то другим, пока вы пытались изменить его.
 Пожалуйста, проверьте журналы.',
 'revdelete-only-restricted' => 'Ошибка сокрытия записи от $2 $1: вы не можете скрыть запись от просмотра администраторами без выбора одной из других настроек сокрытия.',
-'revdelete-reason-dropdown' => 'Стандартные причины удаления
+'revdelete-reason-dropdown' => 'Стандартные причины удаления
 ** Нарушение авторских прав
 ** Неуместные личные сведения
+** Неуместное имя участника
 ** Потенциально клеветнические сведения',
 'revdelete-otherreason' => 'Другая/дополнительная причина:',
 'revdelete-reasonotherlist' => 'Другая причина',
index b36c9c4..5873c0e 100644 (file)
@@ -863,11 +863,11 @@ $2',
 'createacct-join' => 'Унесите своје податке испод',
 'createacct-another-join' => 'Унесите податке за нови налог испод.',
 'createacct-emailrequired' => 'Адреса е-поште',
-'createacct-emailoptional' => 'Адреса е-поште (опцијоно)',
+'createacct-emailoptional' => 'Адреса е-поште (опционо)',
 'createacct-email-ph' => 'Унесите вашу адресу е-поште',
 'createacct-another-email-ph' => 'Унесите адресу е-поште',
 'createaccountmail' => 'Користите привремену, случајно створену лозинку и пошаљите на наведену адресу електронске поште',
-'createacct-realname' => 'Право име (опцијоно)',
+'createacct-realname' => 'Право име (опционо)',
 'createaccountreason' => 'Разлог:',
 'createacct-reason' => 'Разлог',
 'createacct-reason-ph' => 'Зашто правите још један налог?',
@@ -4103,7 +4103,7 @@ $5
 
 # Auto-summaries
 'autosumm-blank' => 'Потпуно обрисана страница',
-'autosumm-replace' => 'Замена садржаја са „$1“',
+'autosumm-replace' => 'Замена садржаја странице са „$1“',
 'autoredircomment' => 'Преусмерење на [[$1]]',
 'autosumm-new' => 'Направљена страница са: „$1“',
 
index f1ea384..b26ba33 100644 (file)
@@ -759,10 +759,10 @@ Imajte na umu da neke stranice mogu nastaviti da se prikazuju kao da ste još pr
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoć pri prijavljivanju]]',
 'createacct-join' => 'Unesite svoje podatke ispod.',
 'createacct-emailrequired' => 'Adresa e-pošte',
-'createacct-emailoptional' => 'Adresa e-pošte (opcijono)',
+'createacct-emailoptional' => 'Adresa e-pošte (opciono)',
 'createacct-email-ph' => 'Unesite vašu adresu e-pоšte',
 'createaccountmail' => 'Koristite privremenu, slučajno stvorenu lozinku i pošaljite na navedenu adresu elektronske pošte',
-'createacct-realname' => 'Pravo ime (opcijono)',
+'createacct-realname' => 'Pravo ime (opciono)',
 'createaccountreason' => 'Razlog:',
 'createacct-reason' => 'Razlog',
 'createacct-reason-ph' => 'Zašto pravite još jedan nalog?',
@@ -3938,7 +3938,7 @@ Potvrdite da stvarno želite da napravite stranicu.",
 
 # Auto-summaries
 'autosumm-blank' => 'Potpuno obrisana stranica',
-'autosumm-replace' => 'Zamena sadržaja sa „$1“',
+'autosumm-replace' => 'Zamena sadržaja stranice sa „$1“',
 'autoredircomment' => 'Preusmerenje na [[$1]]',
 'autosumm-new' => 'Napravljena stranica sa: „$1“',
 
index 1ca2f1a..1887bf5 100644 (file)
@@ -334,17 +334,17 @@ $messages = array(
 'tog-newpageshidepatrolled' => 'Göm patrullerade sidor från listan över nya sidor',
 'tog-extendwatchlist' => 'Utöka bevakningslistan till att visa alla ändringar, inte bara den senaste',
 'tog-usenewrc' => 'Gruppera ändringar efter sida i senaste ändringar och bevakningslistan',
-'tog-numberheadings' => 'Numrerade rubriker',
-'tog-showtoolbar' => 'Visa redigerings-verktygsraden',
+'tog-numberheadings' => 'Automatisk numrerade rubriker',
+'tog-showtoolbar' => 'Visa redigeringsverktygsraden',
 'tog-editondblclick' => 'Redigera sidor med dubbelklick',
 'tog-editsection' => 'Aktivera redigering av avsnitt genom [redigera]-länkar',
 'tog-editsectiononrightclick' => 'Aktivera redigering av avsnitt genom högerklick på underrubriker',
-'tog-showtoc' => 'Visa innehållsförteckning (för sidor som har minst fyra rubriker)',
+'tog-showtoc' => 'Visa innehållsförteckning (för sidor med minst fyra rubriker)',
 'tog-rememberpassword' => 'Kom ihåg min inloggning på den här webbläsaren (i maximalt $1 {{PLURAL:$1|dygn|dygn}})',
-'tog-watchcreations' => 'Lägg till sidor jag skapar i min bevakningslista',
-'tog-watchdefault' => 'Lägg till sidor jag redigerar i min bevakningslista',
-'tog-watchmoves' => 'Lägg till sidor jag flyttar i min bevakningslista',
-'tog-watchdeletion' => 'Lägg till sidor jag raderar i min bevakningslista',
+'tog-watchcreations' => 'Lägg till sidor jag skapar och filer jag laddar upp till min bevakningslista',
+'tog-watchdefault' => 'Lägg till sidor och filer jag redigerar i min bevakningslista',
+'tog-watchmoves' => 'Lägg till sidor och filer jag flyttar i min bevakningslista',
+'tog-watchdeletion' => 'Lägg till sidor och filer jag raderar i min bevakningslista',
 'tog-minordefault' => 'Markera automatiskt ändringar som mindre',
 'tog-previewontop' => 'Visa förhandsgranskningen ovanför redigeringsrutan',
 'tog-previewonfirst' => 'Visa förhandsgranskning när redigering påbörjas',
@@ -1599,7 +1599,7 @@ Om du väljer att ange ditt riktiga namn, kommer det att användas för att till
 # Groups
 'group' => 'Grupp:',
 'group-user' => 'Användare',
-'group-autoconfirmed' => 'Bekräftade användare',
+'group-autoconfirmed' => 'Automatiskt bekräftade användare',
 'group-bot' => 'Robotar',
 'group-sysop' => 'Administratörer',
 'group-bureaucrat' => 'Byråkrater',
index c955c06..428480a 100644 (file)
@@ -647,6 +647,8 @@ $1',
 'userlogin-resetpassword-link' => 'ตั้งรหัสผ่านใหม่',
 'helplogin-url' => 'Help:การล็อกอิน',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|คำอธิบายเรื่องการล็อกอิน]]',
+'userlogin-loggedin' => 'คุณล็อกอินในชื่อ {{GENDER:$1|$1}} แล้ว
+ใช้แบบด้านล่างเพื่อล็อกอินเป็นอีกผู้ใช้หนึ่ง',
 'userlogin-createanother' => 'สร้างอีกบัญชี',
 'createacct-join' => 'กรอกสารสนเทศของคุณด้านล่าง',
 'createacct-another-join' => 'กรอกข้อมูลของบัญชีใหม่ด้านล่าง',
@@ -1105,7 +1107,7 @@ $2
 คุณสามารถดูผลต่างนี้ได้ รายละเอียดพบได้ใน[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} ปูมการลบ]",
 'rev-suppressed-diff-view' => "รุ่นหนึ่งของผลต่างนี้'''ถูกยับยั้ง'''
 คุณสามารถดูผลต่างนี้ได้ รายละเอียดพบได้ใน[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} ปูมการยับยั้ง]",
-'rev-delundel' => 'à¹\81สà¸\94à¸\87/à¸\8bà¹\88อà¸\99',
+'rev-delundel' => 'à¹\80à¸\9bลีà¹\88ยà¸\99à¸\97ัศà¸\99วิสัย',
 'rev-showdeleted' => 'แสดง',
 'revisiondelete' => 'ลบ/กู้คืนรุ่น',
 'revdelete-nooldid-title' => 'ไม่มีรุ่นที่ต้องการ',
@@ -1429,6 +1431,8 @@ $1",
 'userrights-notallowed' => 'บัญชีของคุณไม่ได้รับอนุญาตให้เพิ่มหรือลดสิทธิผู้ใช้',
 'userrights-changeable-col' => 'กลุ่มที่คุณสามารถเปลี่ยนได้',
 'userrights-unchangeable-col' => 'กลุ่มที่คุณไม่สามารถเปลี่ยนได้',
+'userrights-conflict' => 'พบการเปลี่ยนแปลงสิทธิผู้ใช้ขัดกัน! โปรดทบทวนและยืนยันการเปลี่ยนแปลงของคุณ',
+'userrights-removed-self' => 'คุณเพิกถอนสิทธิของคุณสำเร็จแล้ว ฉะนั้น คุณจึงไม่สามารถเข้าถึงหน้านี้ได้อีกต่อไป',
 
 # Groups
 'group' => 'กลุ่ม:',
@@ -2107,6 +2111,8 @@ $1',
 'allpages-hide-redirects' => 'ซ่อนการเปลี่ยนทาง',
 
 # SpecialCachedPage
+'cachedspecial-viewing-cached-ttl' => 'คุณกำลังดูรุ่นที่เก็บหน่วยความจำแคชของหน้านี้ ซึ่งอาจมีอายุ $1',
+'cachedspecial-viewing-cached-ts' => 'คุณกำลังดูรุ่นที่เก็บหน่วยความจำแคชของหน้านี้ ซึ่งอาจไม่เป็นจริงทั้งหมด',
 'cachedspecial-refresh-now' => 'ดูล่าสุด',
 
 # Special:Categories
@@ -2130,7 +2136,7 @@ $1',
 'linksearch-ok' => 'ค้นหา',
 'linksearch-text' => 'สามารถใช้ตัวแทนเช่น "*.wikipedia.org" ได้
 ต้องการโดเมนระดับบนสุดเป็นอย่างน้อย เช่น "*.org"<br />
-{PLURAL:$2|โพรโทคอล}}ที่รองรับ: <code>$1</code> (ค่าโดยปริยายเป็น http:// หากไม่ระบุโพรโทคอล)',
+{{PLURAL:$2|โพรโทคอล}}ที่รองรับ: <code>$1</code> (ค่าโดยปริยายเป็น http:// หากไม่ระบุโพรโทคอล)',
 'linksearch-line' => '$1 ถูกลิงก์จาก $2',
 'linksearch-error' => 'อักขระตัวแทนอยู่ได้เฉพาะหน้าชื่อโฮสต์เท่านั้น',
 
@@ -2309,9 +2315,11 @@ $UNWATCHURL
 'deleteotherreason' => 'เหตุผลอื่น/เพิ่มเติม:',
 'deletereasonotherlist' => 'เหตุผลอื่น',
 'deletereason-dropdown' => '* เหตุผลการลบทั่วไป
-** รับแจ้งจากผู้เขียน
+** สแปม
+** ก่อกวน
 ** ละเมิดลิขสิทธิ์
-** ก่อกวน',
+** ผู้เขียนร้องขอ
+** การเปลี่ยนทางเสีย',
 'delete-edit-reasonlist' => 'แก้ไขเหตุผลการลบ',
 'delete-toobig' => 'หน้านี้มีประวัติการแก้ไขนาดใหญ่ คือ กว่า $1 รุ่น การลบหน้าเช่นนี้ถูกจำกัดเพื่อป้องกันการรบกวน{{SITENAME}}โดยบังเอิญ',
 'delete-warning-toobig' => 'หน้านี้มีประวัติการแก้ไขขนาดใหญ่ กว่า $1 รุ่น การลบหน้านี้อาจรบกวนการทำงานของฐานข้อมูลของ {{SITENAME}} โปรดดำเนินการด้วยความระมัดระวัง',
@@ -2329,7 +2337,7 @@ $UNWATCHURL
 ผู้แก้ไขล่าสุดของหน้านี้คือ [[User:$3|$3]] ([[User talk:$3|พูดคุย]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]])',
 'editcomment' => "คำอธิบายอย่างย่อคือ: \"''\$1''\"",
 'revertpage' => 'ย้อนการแก้ไขของ [[Special:Contributions/$2|$2]] ([[User talk:$2|Talk]]) ไปยังรุ่นของ [[User:$1|$1]]',
-'revertpage-nouser' => 'ยà¹\89อà¸\99à¸\81ารà¹\81à¸\81à¹\89à¹\84à¸\82à¹\82à¸\94ยà¸\9cูà¹\89à¹\83à¸\8aà¹\89à¹\84มà¹\88ระà¸\9aุà¸\8aืà¹\88อà¹\84à¸\9bยัà¸\87รุà¹\88à¸\99ลà¹\88าสุà¸\94à¹\82à¸\94ย [[User:$1|$1]]',
+'revertpage-nouser' => 'ยà¹\89อà¸\99à¸\81ารà¹\81à¸\81à¹\89à¹\84à¸\82à¹\82à¸\94ยà¸\9cูà¹\89à¹\83à¸\8aà¹\89à¹\84มà¹\88ระà¸\9aุà¸\8aืà¹\88อà¹\84à¸\9bยัà¸\87รุà¹\88à¸\99สุà¸\94à¸\97à¹\89ายà¹\82à¸\94ย {{GENDER:$1|[[User:$1|$1]]}}',
 'rollback-success' => 'ย้อนรุ่นที่แก้ไขโดย $1 ไปยังรุ่นล่าสุดโดย $2',
 
 # Edit tokens
@@ -2471,7 +2479,7 @@ $1',
 'contributions' => 'เรื่องที่เขียนโดย{{GENDER:$1|ผู้ใช้}}นี้',
 'contributions-title' => 'เรื่องที่เขียนโดย $1',
 'mycontris' => 'เรื่องที่เขียน',
-'contribsub2' => 'สำหรับ $1 ($2)',
+'contribsub2' => 'สำหรับ {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'ไม่พบการเปลี่ยนแปลงตรงกับเงื่อนไขเหล่านี้',
 'uctop' => '(ปัจจุบัน)',
 'month' => 'จากเดือน (และก่อนหน้า):',
@@ -2659,6 +2667,7 @@ $1',
 'unlockdbsuccesstext' => 'ปลดล็อกฐานข้อมูลเรียบร้อย',
 'lockfilenotwritable' => 'ไม่สามารถล็อกฐานข้อมูลได้ เนื่องจากการเขียนลงฐานข้อมูล การล็อกและการปลดล็อกจำเป็นต้องทำที่เว็บเซิร์ฟเวอร์',
 'databasenotlocked' => 'ฐานข้อมูลไม่ได้ล็อก',
+'lockedbyandtime' => '(โดย {{GENDER:$1|$1}} เมื่อวันที่ $2 เวลา $3)',
 
 # Move page
 'move-page' => 'ย้าย $1',
@@ -2898,7 +2907,7 @@ $1',
 'tooltip-t-recentchangeslinked' => 'รายการปรับปรุงล่าสุดในหน้าที่ลิงก์จากหน้านี้',
 'tooltip-feed-rss' => 'ฟีดชนิดอาร์เอสเอส (RSS) ของหน้านี้',
 'tooltip-feed-atom' => 'ฟีดอะตอม (Atom) ของหน้านี้',
-'tooltip-t-contributions' => 'รายà¸\81ารà¹\80รืà¹\88อà¸\87à¸\97ีà¹\88à¹\80à¸\82ียà¸\99à¹\82à¸\94ยà¸\9cูà¹\89à¹\83à¸\8aà¹\89à¸\84à¸\99à¸\99ีà¹\89',
+'tooltip-t-contributions' => 'รายการเรื่องที่เขียนโดยผู้ใช้นี้',
 'tooltip-t-emailuser' => 'ส่งอีเมลถึงผู้ใช้นี้',
 'tooltip-t-upload' => 'อัปโหลดไฟล์',
 'tooltip-t-specialpages' => 'รายการหน้าพิเศษทั้งหมด',
@@ -3057,8 +3066,8 @@ $1',
 'filedelete-archive-read-only' => 'ไดเรกทอรีกรุชื่อ "$1" ไม่สามารถเขียนลงได้โดยเว็บเซิร์ฟเวอร์',
 
 # Browsing diffs
-'previousdiff' => 'â\86\90 à¹\81à¸\95à¸\81à¸\95à¹\88าà¸\87ก่อนหน้า',
-'nextdiff' => 'à¹\81à¸\95à¸\81à¸\95à¹\88าà¸\87ถัดไป →',
+'previousdiff' => 'â\86\90 à¸\81ารà¹\81à¸\81à¹\89à¹\84à¸\82ก่อนหน้า',
+'nextdiff' => 'à¸\81ารà¹\81à¸\81à¹\89à¹\84à¸\82ถัดไป →',
 
 # Media information
 'mediawarning' => "'''คำเตือน''': ไฟล์รูปแบบนี้อาจมีโค้ดที่ไม่พึงประสงค์
index 4a29ea1..a0c3f67 100644 (file)
@@ -778,8 +778,8 @@ $3 tarapyndan görkezilen sebäp: ''$2''",
 '''({{int:last}})''' = öňündäki wersiýadan tapawudy, '''{{int:minoreditletter}}''' = ujypsyzja özgerdiş.",
 'history-fieldset-title' => 'Geçmişe göz aýla',
 'history-show-deleted' => 'Diňe öçürilenler',
-'histfirst' => 'Iň irki',
-'histlast' => 'Ýaňy-ýakyndaky',
+'histfirst' => 'iň köne',
+'histlast' => 'iň täze',
 'historysize' => '({{PLURAL:$1|1 baýt|$1 baýt}})',
 'historyempty' => '(boş)',
 
@@ -1282,7 +1282,7 @@ $1 {{PLURAL:$1|simwoldan|simwoldan}} köp bolmaly däl.',
 'rc_categories' => 'Kategoriýalar bilen çäklendir ("|" bilen aýyr)',
 'rc_categories_any' => 'Islendik',
 'newsectionsummary' => '/* $1 */ täze bölüm',
-'rc-enhanced-expand' => 'Jikme-jiklikleri görkez (JavaScript gerekli)',
+'rc-enhanced-expand' => 'Jikme-jikligi görkez',
 'rc-enhanced-hide' => 'Jikme-jiklikleri gizle',
 
 # Recent changes linked
@@ -2055,9 +2055,9 @@ $1',
 'contributions' => '{{GENDER:$1|Ulanyjy}} goşantlary',
 'contributions-title' => '$1 üçin ulanyjy goşantlary',
 'mycontris' => 'Goşantlar',
-'contribsub2' => '$1 ($2)',
+'contribsub2' => '{{GENDER:$3|$1}} üçin ($2)',
 'nocontribs' => 'Bu kriteriýlere gabat gelýän üýtgeşme ýok.',
-'uctop' => '(iň soňky)',
+'uctop' => '(häzirki)',
 'month' => 'Aý:',
 'year' => 'Ýyl:',
 
index 5fd2c79..aade31c 100644 (file)
@@ -1595,9 +1595,9 @@ $1",
 'badsiglength' => 'Ваш підпис дуже довгий.
 Повинно бути не більше $1 {{PLURAL:$1|символу|символів|символів}}.',
 'yourgender' => 'Як ви волієте бути описаним?',
-'gender-unknown' => 'Я Ð½Ðµ Ð²Ð¾Ð»Ñ\96Ñ\8e Ð´ÐµÑ\82алÑ\96зÑ\83ваÑ\82и',
-'gender-male' => 'Ð\92Ñ\96н Ñ\80едагÑ\83Ñ\94 Ð²Ñ\96кÑ\96\81Ñ\82оÑ\80Ñ\96нки',
-'gender-female' => 'Ð\92она Ñ\80едагÑ\83Ñ\94 Ð²Ñ\96кÑ\96\81Ñ\82оÑ\80Ñ\96нки',
+'gender-unknown' => 'Ð\9dе Ð²Ð¸Ð·Ð½Ð°Ñ\87ена',
+'gender-male' => 'ЧоловÑ\96Ñ\87а',
+'gender-female' => 'Ð\96Ñ\96ноÑ\87а',
 'prefs-help-gender' => "Задання цього параметру - необов'язкове. Застосовується рушієм у тих звертаннях до користувача, які залежать від статі.
 Ця інформація загальнодоступна.",
 'email' => 'Електронна пошта',
index f95d7c6..300eaa1 100644 (file)
@@ -854,8 +854,7 @@ Hãy nhập một địa chỉ có định dạng đúng hoặc bỏ trống ô
 
 Xin hãy bỏ qua thông điệp này nếu tài khoản này không phải do bạn tạo ra.',
 'usernamehasherror' => 'Tên người dùng không thể chứa dấu rào',
-'login-throttled' => 'Bạn đã thử quá nhiều mật khẩu của tài khoản này.
-Xin hãy đợi $1 rồi thử lại.',
+'login-throttled' => 'Bạn đã hết quyền thử mật khẩu tài khoản này vì bạn đã nhập sai quá nhiều. Xin hãy đợi $1 giây rồi hãy thử lại.',
 'login-abort-generic' => 'Thất bại khi đăng nhập',
 'loginlanguagelabel' => 'Ngôn ngữ: $1',
 'suspicious-userlogout' => 'Đã bỏ qua yêu cầu đăng xuất bạn, hình như được gửi từ trình duyệt hoặc máy proxy nhớ đệm hư.',
@@ -1031,7 +1030,7 @@ Có thể nó đã bị di chuyển hoặc xóa đi trong khi bạn đang xem tr
 'accmailtitle' => 'Đã gửi mật khẩu.',
 'accmailtext' => "Một mật khẩu được tạo ngẫu nhiên cho [[User talk:$1|$1]] đã được gửi đến $2. Có thể đổi mật khẩu tại trang ''[[Special:ChangePassword|đổi mật khẩu]]'' sau khi đã đăng nhập.",
 'newarticle' => '(Mới)',
-'newarticletext' => '<div style="margin-top: 0px;" class="emptymwmsg mediawiki_newarticletext"></div>',
+'newarticletext' => '<div style="margin-top: 0px;" class="emptymwmsg mediawiki_newarticletext[[{{MediaWiki:Helppage}}|help page]]"></div>',
 'anontalkpagetext' => "----''Đây là trang thảo luận của một người dùng vô danh chưa tạo tài khoản hoặc có tài khoản nhưng không đăng nhập.
 Do đó chúng ta phải dùng một dãy số gọi là địa chỉ IP để xác định anh/chị ta.
 Một địa chỉ IP như vậy có thể có nhiều người cùng dùng chung.
@@ -2510,9 +2509,11 @@ Xin xác nhận việc bạn định làm, và hiểu rõ những hệ lụy c
 'deleteotherreason' => 'Lý do khác/bổ sung:',
 'deletereasonotherlist' => 'Lý do khác',
 'deletereason-dropdown' => '*Các lý do xóa phổ biến
-** Tác giả yêu cầu
-** Vi phạm bản quyền
-** Phá hoại',
+** Đăng tỉ thư rác
+** Phá hoại
+** Vi phạ bản quyền
+** Thiếu tôn trọng tác giả
+** Chuyển hướng sai',
 'delete-edit-reasonlist' => 'Sửa lý do xóa',
 'delete-toobig' => 'Trang này có lịch sử sửa đổi lớn, đến hơn {{PLURAL:$1|lần|lần}} sửa đổi.
 Việc xóa các trang như vậy bị hạn chế để ngăn ngừa phá hoại do vô ý cho {{SITENAME}}.',
@@ -2730,13 +2731,17 @@ $1',
 'ipbreason' => 'Lý do:',
 'ipbreasonotherlist' => 'Lý do khác',
 'ipbreason-dropdown' => '*Một số lý do cấm thường gặp
-** Thêm thông tin sai lệch
-** Xóa nội dung trang
-** Đăng liên kết thư rác đến trang Web bên ngoài
+** Phá hoại
+** Thêm thông tin nội dung sai lệch
+** Tẩy trống nội dung trang
+** Quảng cáo vớ vẩn
+** Đăng liên kết thư rác đến trang web bên ngoài
 ** Cho thông tin rác vào trang
 ** Có thái độ dọa dẫm/quấy rối
-** Lạm dụng nhiều tài khoản
-** Tên thành viên không thể chấp nhận',
+** Tên thành viên không được chấp nhận
+** Tạo nhiều trang mới vi phạm bản quyền, bỏ qua thảo luận và cảnh báo
+** Truyền nhiều hình ảnh thiếu nguồn gốc hoặc bản quyền
+** Con rối của thành viên bị cấm',
 'ipb-hardblock' => 'Ngăn không cho thành viên đã đăng nhập sửa đổi từ địa chỉ IP này',
 'ipbcreateaccount' => 'Cấm mở tài khoản',
 'ipbemailban' => 'Không cho gửi thư điện tử',
index ae7b6dc..832415e 100644 (file)
@@ -151,7 +151,9 @@ $messages = array(
 'category-subcat-count' => 'Di ghadegorii umfasd {{PLURAL:$2|bloos a undâr-ghadegorii|dsam $2 undâr-ghadegoriâ, wofoo {{PLURAL:$1|nôr ôône| $1}}}} undn ôôdsajchd wärn.',
 'category-article-count' => 'Di ghadegorii umfasd {{PLURAL:$2|bloos a sajdn|$2 sajdn, wofoo hiir {{PLURAL:$1|aane undn ôôdsajchd wärd|l$1 ôôdsajchd undn wärn}}}}.',
 'listingcontinuesabbrev' => '(Fôrdsedsung)',
+'noindex-category' => 'Seidn, wou net indexierd sin',
 
+'about' => 'Ieber',
 'newwindow' => '(Wärd in am najn fenschdâ daargschdeld)',
 'cancel' => 'Abbrechn',
 'mytalk' => 'Disghusjoonssajdn',
@@ -327,6 +329,7 @@ Wen des basiird, dan massdn`s, wemma â dsu alde bearbajdung ôôschaua wil odâ
 
 Wen's des ned is, bisd womeeglich iwa ân feela in dr sofdwäâr gschdolbäd. In dämm Fall melds´däs, bidde mid där URL, am [[Special:ListUsers/sysop|Administrator]].",
 'missingarticle-rev' => '(wärsjoonsnumâr: $1)',
+'badtitle' => 'Ungüldicher Addigl',
 'badtitletext' => "Dii fârlangde sajdn gibd's ned, odâr sii had ân uugildichn sajdnnôôma ghabd, odâr s'wôôr â gschlambdâr fârwajs fonâm andârn wighi häär. Filajchd is aa â buuchschdôôb drin'n, däär in sajdnnôôm gôôr ned schdena däf.",
 'viewsource' => 'Gwäl-dhägsd ôôgugn',
 
@@ -336,9 +339,11 @@ Wen's des ned is, bisd womeeglich iwa ân feela in dr sofdwäâr gschdolbäd. In
 'remembermypassword' => 'Af dem ghombjuudâr schdändich ôôgmäld blajm (for a maximum of $1 {{PLURAL:$1|day|days}})',
 'login' => 'Ôômeldn',
 'nav-login-createaccount' => 'Oomeldn / Ghondoo ooleeng',
+'loginprompt' => 'Zum Omelldn mäin Guggies agdivierd sei.',
 'userlogin' => 'Ôômeldn / Als Bajdräächâr ajschrajm',
 'logout' => 'Abmeldn',
 'userlogout' => 'Abmeldn',
+'nologin' => 'Du hast ka Nutzergonto? $1',
 'nologinlink' => 'Sich als najâr Ôôgmeldâr ôômäldn',
 'gotaccountlink' => 'Omeldn',
 'mailmypassword' => ' najs passwôrd iwâr iimejl dsuschign lasn',
@@ -431,8 +436,8 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 * '''({{int:cur}})''' = undârschiid dsur geechnwärdichn wärsjoon, '''({{int:last}})''' = undârschiid dsur foorichn wärsjoon
 * Uurdsajd/Daadum = wärsjoon dsu dära dsajd, '''{{int:minoreditletter}}''' = glane ändärung.",
 'history-fieldset-title' => 'Suchng in där wärsjoonsfolche',
-'histfirst' => 'Ã\84ldâschde',
-'histlast' => 'Najsde',
+'histfirst' => 'älldsde',
+'histlast' => 'neisde',
 
 # Revision deletion
 'rev-delundel' => 'ôôdsajng/fârbärng',
@@ -459,12 +464,20 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'notextmatches' => 'Närchnds gfundn.',
 'prevn' => '{{PLURAL:$1|foorichâr|fooriche $1}}',
 'nextn' => '{{PLURAL:$1|nägschdâr|nägschde $1}}',
+'prevn-title' => '{{PLURAL:$1|Vuurherichs Ergebnis|Vuurheriche $1 Ergebniss}}',
+'shown-title' => 'Zeich mer $1 {{PLURAL:$1|Ergebnis|Ergebniss}} bro Seidn',
 'viewprevnext' => 'Dsajch ($1 {{int:pipe-separator}} $2) ($3)',
 'searchmenu-new' => "'''Derschdell dai Seidn „[[:$1]]“ in diesn Wigi.'''",
 'searchprofile-articles' => 'Inhaldsseidn',
+'searchprofile-project' => 'Hilf- un Brojegdseidn',
 'searchprofile-images' => 'Muldimedia',
 'searchprofile-everything' => 'Alls',
 'searchprofile-advanced' => 'Erweiderd',
+'searchprofile-articles-tooltip' => 'Soung in $1',
+'searchprofile-project-tooltip' => 'Soung in $1',
+'searchprofile-images-tooltip' => 'Nach Daddein soung',
+'searchprofile-everything-tooltip' => 'Gsamdn Inhald durchsoung (aa Disgussionsseidn)',
+'searchprofile-advanced-tooltip' => 'Soung in weidere Namensraim',
 'search-result-size' => '$1 ({{PLURAL:$2|1 wôrd|$2 wärdâr}})',
 'search-result-score' => 'Âjschleechich: $1 %',
 'search-redirect' => '(Wajdalajdung fon „$1“ häa)',
@@ -546,6 +559,7 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'timezoneregion-pacific' => 'Bhadsiifischâr Oodseaan',
 'allowemail' => 'Iimejl-embfang fon andrâ ôôschdeln',
 'youremail' => 'E-mail:',
+'yourrealname' => 'Bürcherlicher Noma:',
 
 # Groups
 'group-sysop' => 'Adminisdradoorn',
@@ -596,6 +610,7 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 # Upload
 'upload' => 'Nauflôôdn',
 'uploadlogpage' => 'Brodoghol fom dadaj-hoochlôôdn',
+'filedesc' => 'Bschreibung',
 'uploadedimage' => 'had „[[$1]]“ naufglôôdn',
 
 'license' => 'Lizenz',
@@ -673,7 +688,7 @@ S'gajd awâr aa â [[Special:WhatLinksHere/$2|lisdn mid alâ fârwajs]].",
 'emailuser' => 'Dem ôôgmeldn â iimejl schign',
 
 # Watchlist
-'watchlist' => 'Maj beoobachdungs-lisdn',
+'watchlist' => 'Beoobachdungslisdn',
 'mywatchlist' => 'Beoobachdungslisdn',
 'addedwatchtext' => "Di sajdn „[[:$1]]“ schdäd eds mid af dajnâr [[Special:Watchlist|beoobachdungs-lisdn]] .
 
@@ -752,12 +767,14 @@ Wenns'd dii sajdn irchendwan amôl nimä fârfolchn wilsd, musd bloos af „{{in
 'contributions-title' => 'Bajdrääch fo „$1“',
 'mycontris' => 'Bajdreech',
 'contribsub2' => 'Fär $1 ($2)',
-'uctop' => '(ledsdâr schdand)',
+'uctop' => '(agduell)',
 'month' => 'bis moonad:',
 'year' => 'bis dsum jôôr:',
 
 'sp-contributions-newbies' => 'Bloos bajdrääch fo naj Ôôgmeldâ dsajchn',
 'sp-contributions-blocklog' => 'Schbär-brodoghol',
+'sp-contributions-uploads' => 'Houchglodne Daddein',
+'sp-contributions-logs' => 'Logbäicher',
 'sp-contributions-talk' => 'Disgussion',
 'sp-contributions-search' => 'Bajdreech suchng',
 'sp-contributions-username' => 'IP-adresn odär nôômâ fom Ôôgmeldn:',
@@ -770,7 +787,7 @@ Wenns'd dii sajdn irchendwan amôl nimä fârfolchn wilsd, musd bloos af „{{in
 'linkshere' => "Dii afgfiirdn sajdn fârwajsn af ''„[[:$1]]“''':",
 'isredirect' => 'Wajdârlajdungssajdn',
 'istemplate' => 'Foorlaachn-ajbindung',
-'isimage' => 'fârwajs af des bild hiir',
+'isimage' => 'Daddeilink',
 'whatlinkshere-prev' => '{{PLURAL:$1|vorhäärichâr|vorhääriche $1}}',
 'whatlinkshere-next' => '{{PLURAL:$1|nägschdâr|nägschde $1}}',
 'whatlinkshere-links' => '← fârwajse hiirhäär',
@@ -785,7 +802,7 @@ Wenns'd dii sajdn irchendwan amôl nimä fârfolchn wilsd, musd bloos af „{{in
 'blockip-title' => 'Bearbajdâr aus-schbärn',
 'blockip-legend' => 'IP-Adresn odr Bearbajdâr aus-schbärn',
 'ipboptions' => '2 schdund:2 hours,1 dooch:1 day,3 dooch:3 days,1 wochng:1 week,2 wochng:2 weeks,1 moonad:1 month,3 moonad:3 months,6 moonad:6 months,1 jôôr:1 year,oone dsajdschrangng:infinite',
-'ipblocklist' => 'Gschbärde IP-adresn un Ôôgmelde',
+'ipblocklist' => 'Gschberrder Nutzer',
 'blocklink' => 'Schbärn',
 'unblocklink' => 'frajgeem',
 'change-blocklink' => 'Schbärn ändârn',
@@ -827,6 +844,10 @@ Schrajb bide den '''naja'' nôômâ fo dâr sajdn undârals '''Dsiil'' nâj un '
 # Export
 'export' => 'Sajdn ägsbhôrdiirn',
 
+# Namespace 8 related
+'allmessagesname' => 'Noma',
+'allmessagesdefault' => 'Schdandaddexd',
+
 # Thumbnails
 'thumbnail-more' => 'Grässär machng',
 
@@ -923,7 +944,7 @@ Bloos  dsajln, dii mi´m dsajchn * ôôfanga, wärn berigsichdichd. Un dä ärsc
 'metadata-help' => 'Dii dadaj umfasd annäre ôôgam, dii normaalârwajs fo där digidaal-ghamâraa odär fo am sghänâr häärghumma. Wen dii dadaj indswischn fârändârd wôrn is, meechn dii nimä dsum bild basn.',
 'metadata-expand' => 'Ajdslhajdn dsajchn',
 'metadata-collapse' => 'Ajdslhajdn ausblendn',
-'metadata-fields' => 'Hiir afgfiirde fäldâr fo dâ EXIF-medha-daadn wärn af alle bildbeschrajwungs-sajdn afgfiird, aa wen dii medhadaadn-dabelln ajgfalded is. Annäre sin ärschdâmôôl fârschdegd.
+'metadata-fields' => 'Folgnde Felder vo däi EXIF-Medadaden, däi wou in den MediaWigi-Sysdemdexd ogeem sin, werrn af Bildbeschreibungsseidn miid eiglabbder Medadadndabelln ozeichd. Weidere werrn schdandaddmäßich net ozeichd.
 * make
 * model
 * datetimeoriginal
index 0a90da4..22e160f 100644 (file)
@@ -658,6 +658,8 @@ $2',
 'userlogin-resetpassword-link' => 'צוריקשטעלן אײַער פאַסווארט',
 'helplogin-url' => 'Help:אריינלאגירן',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|הילף מיט אריינלאגירן]]',
+'userlogin-loggedin' => 'איר זענט שוין אריינלאגירט ווי {{GENDER:$1|$1}}.
+ניצט די פארעם אונטן כדי אריינלאגירן ווי אן אנדער באניצער.',
 'userlogin-createanother' => 'שאפֿן נאך א קאנטע',
 'createacct-join' => 'גיט ארײַן אײַער אינפֿארמאציע אונטן.',
 'createacct-another-join' => 'ארײַנגעבן דער נײַער קאנטעס אינפארמאציע אונטן.',
index 44dcda4..6682da4 100644 (file)
@@ -1712,8 +1712,8 @@ $1",
 'recentchanges-label-bot' => '该编辑由机器人进行',
 'recentchanges-label-unpatrolled' => '该编辑尚未巡查',
 'rcnote' => "下面是过去'''$2'''天的最后'''$1'''个更改,截至$4 $5。",
-'rcnotefrom' => "下面是自'''$2'''起的更改(最多显示'''$1'''个)。",
-'rclistfrom' => '显示自$1起的新更改',
+'rcnotefrom' => "下面是'''$2'''之后的更改(最多显示'''$1'''个)。",
+'rclistfrom' => '显示$1之后的新更改',
 'rcshowhideminor' => '$1小编辑',
 'rcshowhidebots' => '$1机器人的编辑',
 'rcshowhideliu' => '$1登录用户的编辑',
@@ -2443,10 +2443,10 @@ $UNWATCHURL
 'deletereasonotherlist' => '其他原因',
 'deletereason-dropdown' => '*常见删除原因
 ** 广告
-** 作者申请
-** 侵犯著作权
 ** 破坏行为
-** 损坏重定向',
+** 侵犯著作权
+** 作者申请
+** 损坏的重定向',
 'delete-edit-reasonlist' => '编辑删除原因',
 'delete-toobig' => '这个页面有一个十分大量的编辑历史,超过$1次修订。删除此类页面的动作已经被限制,以防止在{{SITENAME}}上的意外扰乱。',
 'delete-warning-toobig' => '这个页面有一个十分大量的编辑历史,超过$1次修订。删除它可能会扰乱{{SITENAME}}的数据库操作;在继续此动作前请小心。',
index 3bc7673..540808f 100644 (file)
@@ -2405,10 +2405,12 @@ $UNWATCHURL
 'deletecomment' => '理由:',
 'deleteotherreason' => '其它/附加的理由:',
 'deletereasonotherlist' => '其它理由',
-'deletereason-dropdown' => '*常用刪除理由
-** 作者請求
+'deletereason-dropdown' => '* 常見刪除理由
+** 濫發電郵
+** 破壞
 ** 侵犯版權
-** 破壞',
+** 作者請求
+** 損壞重定向頁',
 'delete-edit-reasonlist' => '編輯刪除理由',
 'delete-toobig' => '這個頁面有一個十分大量的編輯歷史,超過$1次修訂。刪除此類頁面的動作已經被限制,以防止在{{SITENAME}}上的意外擾亂。',
 'delete-warning-toobig' => '這個頁面有一個十分大量的編輯歷史,超過$1次修訂。刪除它可能會擾亂{{SITENAME}}的資料庫操作;在繼續此動作前請小心。',
@@ -3939,6 +3941,8 @@ MediaWiki是基於使用目的而加以發佈,然而不負任何擔保責任
 'tags-description-header' => '解釋完整描述',
 'tags-active-header' => '存檔?',
 'tags-hitcount-header' => '已加上標籤的更改',
+'tags-active-yes' => '是',
+'tags-active-no' => '否',
 'tags-edit' => '編輯',
 'tags-hitcount' => '$1次更改',
 
diff --git a/maintenance/archives/patch-archive-ar_id.sql b/maintenance/archives/patch-archive-ar_id.sql
new file mode 100644 (file)
index 0000000..ddd1d7b
--- /dev/null
@@ -0,0 +1,8 @@
+--
+-- patch-archive-ar_id.sql
+--
+-- Bug 39675. Add archive.ar_id.
+
+ALTER TABLE /*$wgDBprefix*/archive
+    ADD COLUMN ar_id int unsigned NOT NULL AUTO_INCREMENT FIRST,
+    ADD PRIMARY KEY (ar_id);
index 030e086..3079a5b 100644 (file)
@@ -13,20 +13,3 @@ CREATE UNIQUE INDEX /*i*/change_tag_log_tag ON /*_*/change_tag (ct_log_id,ct_tag
 CREATE UNIQUE INDEX /*i*/change_tag_rev_tag ON /*_*/change_tag (ct_rev_id,ct_tag);
 -- Covering index, so we can pull all the info only out of the index.
 CREATE INDEX /*i*/change_tag_tag_id ON /*_*/change_tag (ct_tag,ct_rc_id,ct_rev_id,ct_log_id);
-
--- Rollup table to pull a LIST of tags simply without ugly GROUP_CONCAT that only works on MySQL 4.1+
-CREATE TABLE /*_*/tag_summary (
-       ts_rc_id int NULL,
-       ts_log_id int NULL,
-       ts_rev_id int NULL,
-       ts_tags BLOB NOT NULL
-) /*$wgDBTableOptions*/;
-
-CREATE UNIQUE INDEX /*i*/tag_summary_rc_id ON /*_*/tag_summary (ts_rc_id);
-CREATE UNIQUE INDEX /*i*/tag_summary_log_id ON /*_*/tag_summary (ts_log_id);
-CREATE UNIQUE INDEX /*i*/tag_summary_rev_id ON /*_*/tag_summary (ts_rev_id);
-
-
-CREATE TABLE /*_*/valid_tag (
-       vt_tag varchar(255) NOT NULL PRIMARY KEY
-) /*$wgDBTableOptions*/;
diff --git a/maintenance/archives/patch-externallinks-el_id.sql b/maintenance/archives/patch-externallinks-el_id.sql
new file mode 100644 (file)
index 0000000..d4b51b5
--- /dev/null
@@ -0,0 +1,8 @@
+--
+-- patch-extenallinks-el_id.sql
+--
+-- Bug 15441. Add externallinks.el_id.
+
+ALTER TABLE /*$wgDBprefix*/externallinks
+    ADD COLUMN el_id int unsigned NOT NULL AUTO_INCREMENT FIRST,
+    ADD PRIMARY KEY (el_id);
diff --git a/maintenance/archives/patch-rc_source.sql b/maintenance/archives/patch-rc_source.sql
deleted file mode 100644 (file)
index 7dedd74..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
--- first step of migrating recentchanges rc_type to rc_source
-ALTER TABLE /*$wgDBprefix*/recentchanges
-  ADD rc_source varbinary(16) NOT NULL default '';
-
--- Populate rc_source field with the data from rc_type
--- Large wiki's might prefer the PopulateRecentChangeSource maintenance
--- script to batch updates into groups rather than all at once.
-UPDATE /*$wgDBprefix*/recentchanges
-  SET rc_source = CASE
-    WHEN rc_type = 0 THEN 'mw.edit'
-    WHEN rc_type = 1 THEN 'mw.new'
-    WHEN rc_type = 3 THEN 'mw.log'
-    WHEN rc_type = 5 THEN 'mw.external'
-    ELSE ''
-  END
-WHERE rc_source = '';
diff --git a/maintenance/archives/patch-tag_summary.sql b/maintenance/archives/patch-tag_summary.sql
new file mode 100644 (file)
index 0000000..a81b368
--- /dev/null
@@ -0,0 +1,12 @@
+-- Rollup table to pull a LIST of tags simply without ugly GROUP_CONCAT that only works on MySQL 4.1+
+-- Andrew Garrett, 2009-01
+CREATE TABLE /*_*/tag_summary (
+       ts_rc_id int NULL,
+       ts_log_id int NULL,
+       ts_rev_id int NULL,
+       ts_tags BLOB NOT NULL
+) /*$wgDBTableOptions*/;
+
+CREATE UNIQUE INDEX /*i*/tag_summary_rc_id ON /*_*/tag_summary (ts_rc_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_log_id ON /*_*/tag_summary (ts_log_id);
+CREATE UNIQUE INDEX /*i*/tag_summary_rev_id ON /*_*/tag_summary (ts_rev_id);
diff --git a/maintenance/archives/patch-valid_tag.sql b/maintenance/archives/patch-valid_tag.sql
new file mode 100644 (file)
index 0000000..994a5d5
--- /dev/null
@@ -0,0 +1,4 @@
+-- Andrew Garrett, 2009-01
+CREATE TABLE /*_*/valid_tag (
+       vt_tag varchar(255) NOT NULL PRIMARY KEY
+) /*$wgDBTableOptions*/;
index 1e36363..8175891 100644 (file)
@@ -70,7 +70,13 @@ class DeleteEqualMessages extends Maintenance {
                                $default = wfMessage( $key )->inLanguage( $langCode )->useDatabase( false )->plain();
 
                                $messageInfo['relevantPages']++;
-                               if ( $actual === $default ) {
+
+                               if (
+                                       // Exclude messages that are empty by default, such as sitenotice, specialpage
+                                       // summaries and accesskeys.
+                                       $default !== '' && $default !== '-' &&
+                                               $actual === $default
+                               ) {
                                        $hasTalk = isset( $statuses['talks'][$key] );
                                        $messageInfo['results'][] = array(
                                                'title' => $key . $titleSuffix,
index 6b7f38a..548bb2f 100644 (file)
@@ -118,7 +118,6 @@ Wiki configuration for testing:
   // Enable weird and wonderful options:
                                                          // Increase default error reporting level.
   error_reporting (E_ALL);    // At a later date could be increased to E_ALL | E_STRICT
-  $wgBlockOpenProxies = true; // Some block pages require this to be true in order to test.
   $wgEnableUploads = true;    // enable uploads.
   $wgDBerrorLog = "/root/mediawiki-db-error-log.txt";  // log DB errors, replace with suitable path.
   $wgShowSQLErrors = true;    // Show SQL errors (instead of saying the query was hidden).
@@ -1483,24 +1482,6 @@ class watchlistTest extends pageTest {
        }
 }
 
-
-/**
- ** a page test for "Special:Blockme"
- */
-class specialBlockmeTest extends pageTest {
-       function __construct() {
-               $this->pagePath = "index.php?title=Special:Blockme";
-
-               $this->params = array ();
-
-               // sometimes we specify "ip", and sometimes we don't.
-               if ( wikiFuzz::randnum( 1 ) == 0 ) {
-                       $this->params["ip"] = wikiFuzz::chooseInput( array( "10.12.41.213", wikiFuzz::randnum( 8134, -10 ), wikiFuzz::makeFuzz( 2 ) ) );
-               }
-       }
-}
-
-
 /**
  ** a page test for "Special:Movepage"
  */
@@ -2161,7 +2142,7 @@ class GeSHi_Test extends pageTest {
 /**
  ** selects a page test to run.
  * @param $count
- * @return \api|\confirmEmail|\contributionsTest|\editPageTest|\imagelistTest|\imagepageTest|\ipblocklistTest|\listusersTest|\mimeSearchTest|\newImagesTest|\pageDeletion|\pageHistoryTest|\pageProtectionForm|\prefixindexTest|\profileInfo|\recentchangesTest|\redirectTest|\searchTest|\specialAllmessagesTest|\specialAllpagesTest|\specialBlockip|\specialBlockmeTest|\specialBooksourcesTest|\specialCategoryTree|\specialChemicalsourcesTest|\specialCitePageTest|\specialExportTest|\specialFilepathPageTest|\specialImportPageTest|\specialLinksearch|\specialLockdbPageTest|\specialLogTest|\specialMovePage|\specialNewpagesPageTest|\specialRenameuserPageTest|\specialRevisionDeletePageTest|\specialUndeletePageTest|\specialUnlockdbPageTest|\specialUserrights|\successfulUserLoginTest|\thumbTest|\userLoginTest|\viewPageTest|\watchlistTest
+ * @return \api|\confirmEmail|\contributionsTest|\editPageTest|\imagelistTest|\imagepageTest|\ipblocklistTest|\listusersTest|\mimeSearchTest|\newImagesTest|\pageDeletion|\pageHistoryTest|\pageProtectionForm|\prefixindexTest|\profileInfo|\recentchangesTest|\redirectTest|\searchTest|\specialAllmessagesTest|\specialAllpagesTest|\specialBlockip|\specialBooksourcesTest|\specialCategoryTree|\specialChemicalsourcesTest|\specialCitePageTest|\specialExportTest|\specialFilepathPageTest|\specialImportPageTest|\specialLinksearch|\specialLockdbPageTest|\specialLogTest|\specialMovePage|\specialNewpagesPageTest|\specialRenameuserPageTest|\specialRevisionDeletePageTest|\specialUndeletePageTest|\specialUnlockdbPageTest|\specialUserrights|\successfulUserLoginTest|\thumbTest|\userLoginTest|\viewPageTest|\watchlistTest
  */
 function selectPageTest( $count ) {
 
@@ -2197,7 +2178,6 @@ function selectPageTest( $count ) {
                case 20: return new redirectTest();
                case 21: return new confirmEmail();
                case 22: return new watchlistTest();
-               case 23: return new specialBlockmeTest();
                case 24: return new specialUndeletePageTest();
                case 25: return new specialMovePage();
                case 26: return new specialUnlockdbPageTest();
index cbbcf0f..54fd4e2 100644 (file)
@@ -230,14 +230,14 @@ if ( $count > 0 ) {
                } else {
                        $props = FSFile::getPropsFromPath( $file );
                        $flags = 0;
-                       $options = array();
+                       $publishOptions = array();
                        $handler = MediaHandler::getHandler( $props['mime'] );
                        if ( $handler ) {
-                               $options['headers'] = $handler->getStreamHeaders( $props['metadata'] );
+                               $publishOptions['headers'] = $handler->getStreamHeaders( $props['metadata'] );
                        } else {
-                               $options['headers'] = array();
+                               $publishOptions['headers'] = array();
                        }
-                       $archive = $image->publish( $file, $flags, $options );
+                       $archive = $image->publish( $file, $flags, $publishOptions );
                        if ( !$archive->isGood() ) {
                                echo "failed. (" .
                                        $archive->getWikiText() .
@@ -248,7 +248,7 @@ if ( $count > 0 ) {
                }
 
                $commentText = SpecialUpload::getInitialPageText( $commentText, $license );
-               if ( !$summary ) {
+               if ( !isset( $options['summary'] ) ) {
                        $summary = $commentText;
                }
 
index ca3a4f5..a94b6d5 100644 (file)
@@ -2378,11 +2378,8 @@ $wgMessageStructure = array(
                'ipb_blocked_as_range',
                'ip_range_invalid',
                'ip_range_toolarge',
-               'blockme',
                'proxyblocker',
-               'proxyblocker-disabled',
                'proxyblockreason',
-               'proxyblocksuccess',
                'sorbs',
                'sorbsreason',
                'sorbs_create_account_reason',
index c474f00..7356c38 100644 (file)
@@ -159,6 +159,7 @@ CREATE TABLE /*$wgDBprefix*/text (
 -- Cannot reasonably create views on this table, due to the presence of TEXT
 -- columns.
 CREATE TABLE /*$wgDBprefix*/archive (
+   ar_id NOT NULL PRIMARY KEY clustered IDENTITY,
    ar_namespace SMALLINT NOT NULL DEFAULT 0,
    ar_title NVARCHAR(255) NOT NULL DEFAULT '',
    ar_text NVARCHAR(MAX) NOT NULL,
@@ -298,6 +299,7 @@ CREATE INDEX /*$wgDBprefix*/lc_lang_key ON /*$wgDBprefix*/l10n_cache (lc_lang, l
 -- Track links to external URLs
 -- IE >= 4 supports no more than 2083 characters in a URL
 CREATE TABLE /*$wgDBprefix*/externallinks (
+   el_id INT NOT NULL PRIMARY KEY clustered IDENTITY,
    el_from INT NOT NULL DEFAULT '0',
    el_to VARCHAR(2083) NOT NULL,
    el_index VARCHAR(896) NOT NULL,
diff --git a/maintenance/oracle/archives/patch-archive-ar_id.sql b/maintenance/oracle/archives/patch-archive-ar_id.sql
new file mode 100644 (file)
index 0000000..a43f760
--- /dev/null
@@ -0,0 +1,6 @@
+define mw_prefix='{$wgDBprefix}';
+
+ALTER TABLE &mw_prefix.archive ADD (
+ar_id NUMBER NOT NULL,
+);
+ALTER TABLE &mw_prefix.archive ADD CONSTRAINT &mw_prefix.archive_pk PRIMARY KEY (ar_id);
diff --git a/maintenance/oracle/archives/patch-externallinks-el_id.sql b/maintenance/oracle/archives/patch-externallinks-el_id.sql
new file mode 100644 (file)
index 0000000..a8c443f
--- /dev/null
@@ -0,0 +1,4 @@
+define mw_prefix='{$wgDBprefix}';
+
+ALTER TABLE &mw_prefix.externallinks ADD el_id NUMBER NOT NULL;
+ALTER TABLE &mw_prefix.externallinks ADD CONSTRAINT &mw_prefix.externallinks_pk PRIMARY KEY (el_id);
\ No newline at end of file
index 57b6e7e..acfabc3 100644 (file)
@@ -129,7 +129,9 @@ CREATE TABLE &mw_prefix.pagecontent ( -- replaces reserved word 'text'
 );
 ALTER TABLE &mw_prefix.pagecontent ADD CONSTRAINT &mw_prefix.pagecontent_pk PRIMARY KEY (old_id);
 
+CREATE SEQUENCE archive_ar_id_seq;
 CREATE TABLE &mw_prefix.archive (
+  ar_id          NUMBER NOT NULL,
   ar_namespace   NUMBER    DEFAULT 0 NOT NULL,
   ar_title       VARCHAR2(255)         NOT NULL,
   ar_text        CLOB,
@@ -149,6 +151,7 @@ CREATE TABLE &mw_prefix.archive (
   ar_content_model VARCHAR2(32),
   ar_content_format VARCHAR2(64)
 );
+ALTER TABLE &mw_prefix.archive ADD CONSTRAINT &mw_prefix.archive_pk PRIMARY KEY (ar_id);
 ALTER TABLE &mw_prefix.archive ADD CONSTRAINT &mw_prefix.archive_fk1 FOREIGN KEY (ar_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED;
 CREATE INDEX &mw_prefix.archive_i01 ON &mw_prefix.archive (ar_namespace,ar_title,ar_timestamp);
 CREATE INDEX &mw_prefix.archive_i02 ON &mw_prefix.archive (ar_user_text,ar_timestamp);
@@ -208,11 +211,14 @@ ALTER TABLE &mw_prefix.category ADD CONSTRAINT &mw_prefix.category_pk PRIMARY KE
 CREATE UNIQUE INDEX &mw_prefix.category_u01 ON &mw_prefix.category (cat_title);
 CREATE INDEX &mw_prefix.category_i01 ON &mw_prefix.category (cat_pages);
 
+CREATE SEQUENCE externallinks_el_id_seq;
 CREATE TABLE &mw_prefix.externallinks (
+  el_id     NUMBER  NOT NULL,
   el_from   NUMBER  NOT NULL,
   el_to     VARCHAR2(2048) NOT NULL,
   el_index  VARCHAR2(2048) NOT NULL
 );
+ALTER TABLE &mw_prefix.externallinks ADD CONSTRAINT &mw_prefix.externallinks_pk PRIMARY KEY (el_id);
 ALTER TABLE &mw_prefix.externallinks ADD CONSTRAINT &mw_prefix.externallinks_fk1 FOREIGN KEY (el_from) REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
 CREATE INDEX &mw_prefix.externallinks_i01 ON &mw_prefix.externallinks (el_from, el_to);
 CREATE INDEX &mw_prefix.externallinks_i02 ON &mw_prefix.externallinks (el_to, el_from);
diff --git a/maintenance/populateRecentChangesSource.php b/maintenance/populateRecentChangesSource.php
deleted file mode 100644 (file)
index 0e8e501..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-/**
- * Upgrade script to populate the rc_source field
- *
- * 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 Maintenance
- */
-
-require_once dirname( __FILE__ ) . '/Maintenance.php';
-
-/**
- * Maintenance script to populate the rc_source field.
- *
- * @ingroup Maintenance
- * @since 1.22
- */
-class PopulateRecentChangesSource extends LoggedUpdateMaintenance {
-       public function __construct() {
-               parent::__construct();
-               $this->mDescription = "Populates rc_source field of the recentchanges table with the data in rc_type.";
-               $this->setBatchSize( 100 );
-       }
-
-       protected function doDBUpdates() {
-               $dbw = $this->getDB( DB_MASTER );
-               if ( !$dbw->fieldExists( 'recentchanges', 'rc_source' ) ) {
-                       $this->error( 'rc_source field in recentchanges table does not exist.' );
-               }
-
-               $start = $dbw->selectField( 'recentchanges', 'MIN(rc_id)', false, __METHOD__ );
-               if ( !$start ) {
-                       $this->output( "Nothing to do.\n" );
-                       return true;
-               }
-               $end = $dbw->selectField( 'recentchanges', 'MAX(rc_id)', false, __METHOD__ );
-               $end += $this->mBatchSize - 1;
-               $blockStart = $start;
-               $blockEnd = $start + $this->mBatchSize - 1;
-
-               $updatedValues = $this->buildUpdateCondition( $dbw );
-
-               while ( $blockEnd <= $end ) {
-                       $cond = "rc_id BETWEEN $blockStart AND $blockEnd";
-
-                       $dbw->update(
-                               'recentchanges',
-                               array( $updatedValues ),
-                               array(
-                                       "rc_source = ''",
-                                       "rc_id BETWEEN $blockStart AND $blockEnd"
-                               ),
-                               __METHOD__
-                       );
-
-                       $this->output( "." );
-                       wfWaitForSlaves();
-
-                       $blockStart += $this->mBatchSize;
-                       $blockEnd += $this->mBatchSize;
-               }
-
-               $this->output( "\nDone.\n" );
-       }
-
-       protected function getUpdateKey() {
-               return __CLASS__;
-       }
-
-       protected function buildUpdateCondition( DatabaseBase $dbw ) {
-               $rcNew = $dbw->addQuotes( RC_NEW );
-               $rcSrcNew = $dbw->addQuotes( RecentChange::SRC_NEW );
-               $rcEdit = $dbw->addQuotes( RC_EDIT );
-               $rcSrcEdit = $dbw->addQuotes( RecentChange::SRC_EDIT );
-               $rcLog = $dbw->addQuotes( RC_LOG );
-               $rcSrcLog = $dbw->addQuotes( RecentChange::SRC_LOG );
-               $rcExternal = $dbw->addQuotes( RC_EXTERNAL );
-               $rcSrcExternal = $dbw->addQuotes( RecentChange::SRC_EXTERNAL );
-
-               return "rc_source = CASE
-                                       WHEN rc_type = $rcNew THEN $rcSrcNew
-                                       WHEN rc_type = $rcEdit THEN $rcSrcEdit
-                                       WHEN rc_type = $rcLog THEN $rcSrcLog
-                                       WHEN rc_type = $rcExternal THEN $rcSrcExternal
-                                       ELSE ''
-                               END";
-       }
-}
-
-$maintClass = "PopulateRecentChangesSource";
-require_once RUN_MAINTENANCE_IF_MAIN;
index 7c86354..bc2428e 100644 (file)
@@ -18,6 +18,8 @@ DROP SEQUENCE IF EXISTS recentchanges_rc_id_seq CASCADE;
 DROP SEQUENCE IF EXISTS logging_log_id_seq CASCADE;
 DROP SEQUENCE IF EXISTS job_job_id_seq CASCADE;
 DROP SEQUENCE IF EXISTS category_cat_id_seq CASCADE;
+DROP SEQUENCE IF EXISTS archive_ar_id_seq CASCADE;
+DROP SEQUENCE IF EXISTS externallinks_el_id_seq CASCADE;
 DROP FUNCTION IF EXISTS page_deleted() CASCADE;
 DROP FUNCTION IF EXISTS ts2_page_title() CASCADE;
 DROP FUNCTION IF EXISTS ts2_page_text() CASCADE;
@@ -156,7 +158,9 @@ ALTER TABLE page_props ADD CONSTRAINT page_props_pk PRIMARY KEY (pp_page,pp_prop
 CREATE INDEX page_props_propname ON page_props (pp_propname);
 CREATE UNIQUE INDEX pp_propname_page ON page_props (pp_propname,pp_page);
 
+CREATE SEQUENCE archive_ar_id_seq;
 CREATE TABLE archive (
+  ar_id             INTEGER      NOT NULL  PRIMARY KEY DEFAULT nextval('archive_ar_id_seq'),
   ar_namespace      SMALLINT     NOT NULL,
   ar_title          TEXT         NOT NULL,
   ar_text           TEXT, -- technically should be bytea, but not used anymore
@@ -224,7 +228,9 @@ CREATE TABLE categorylinks (
 CREATE UNIQUE INDEX cl_from ON categorylinks (cl_from, cl_to);
 CREATE INDEX cl_sortkey     ON categorylinks (cl_to, cl_sortkey, cl_from);
 
+CREATE SEQUENCE externallinks_id_seq;
 CREATE TABLE externallinks (
+  el_id     INTEGER  NOT NULL  PRIMARY KEY DEFAULT nextval('externallinks_id_seq'),
   el_from   INTEGER  NOT NULL  REFERENCES page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
   el_to     TEXT     NOT NULL,
   el_index  TEXT     NOT NULL
@@ -405,7 +411,6 @@ CREATE TABLE recentchanges (
   rc_this_oldid      INTEGER      NOT NULL,
   rc_last_oldid      INTEGER      NOT NULL,
   rc_type            SMALLINT     NOT NULL  DEFAULT 0,
-  rc_source          TEXT         NOT NULL,
   rc_patrolled       SMALLINT     NOT NULL  DEFAULT 0,
   rc_ip              CIDR,
   rc_old_len         INTEGER,
diff --git a/maintenance/proxyCheck.php b/maintenance/proxyCheck.php
deleted file mode 100644 (file)
index b52f20f..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-/**
- * Command line script to check for an open proxy at a specified location.
- *
- * 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 Maintenance
- */
-
-if ( PHP_SAPI != 'cli' ) {
-       die( 1 );
-}
-
-/**
- *
- */
-$output = '';
-
-/**
- * Exit if there are not enough parameters, or if it's not command line mode
- */
-if ( ( isset( $_REQUEST ) && array_key_exists( 'argv', $_REQUEST ) ) || count( $argv ) < 4 ) {
-       $output .= "Incorrect parameters\n";
-} else {
-       /**
-        * Get parameters
-        */
-       $ip = $argv[1];
-       $port = $argv[2];
-       $url = $argv[3];
-       $host = trim( `hostname` );
-       $output = "Connecting to $ip:$port, target $url, this hostname $host\n";
-
-       # Open socket
-       $sock = @fsockopen( $ip, $port, $errno, $errstr, 5 );
-       if ( $errno == 0 ) {
-               $output .= "Connected\n";
-               # Send payload
-               $request = "GET $url HTTP/1.0\r\n";
-#              $request .= "Proxy-Connection: Keep-Alive\r\n";
-#              $request .= "Pragma: no-cache\r\n";
-#              $request .= "Host: ".$url."\r\n";
-#              $request .= "User-Agent: MediaWiki open proxy check\r\n";
-               $request .= "\r\n";
-               @fputs( $sock, $request );
-               $response = fgets( $sock, 65536 );
-               $output .= $response;
-               @fclose( $sock );
-       } else {
-               $output .= "No connection\n";
-       }
-}
-
-$output = escapeshellarg( $output );
-
-#`echo $output >> /home/tstarling/open/proxy.log`;
index 315176c..1834825 100644 (file)
@@ -83,7 +83,6 @@ class RebuildRecentchanges extends Maintenance {
                                'rc_this_oldid' => 'rev_id',
                                'rc_last_oldid' => 0, // is this ok?
                                'rc_type'       => $dbw->conditional( 'page_is_new != 0', RC_NEW, RC_EDIT ),
-                               'rc_source'     => $dbw->conditional( 'page_is_new != 0', $dbw->addQuotes( RecentChange::SRC_NEW ), $dbw->addQuotes( RecentChange::SRC_EDIT ) ),
                                'rc_deleted'    => 'rev_deleted'
                        ), array(
                                'rev_timestamp > ' . $dbw->addQuotes( $dbw->timestamp( $cutoff ) ),
index 73b008c..1a59be5 100644 (file)
@@ -28,6 +28,8 @@ DROP TABLE IF EXISTS /*_*/interwiki_tmp;
 DROP TABLE IF EXISTS /*_*/page_restrictions_tmp;
 DROP TABLE IF EXISTS /*_*/protected_titles_tmp;
 DROP TABLE IF EXISTS /*_*/page_props_tmp;
+DROP TABLE IF EXISTS /*_*/archive_tmp;
+DROP TABLE IF EXISTS /*_*/externallinks_tmp;
 
 --------------------------------------------------------------------------------
 -- Create new tables
@@ -268,6 +270,47 @@ CREATE TABLE /*_*/page_props_tmp (
 );
 CREATE UNIQUE INDEX /*i*/pp_page_propname ON /*_*/page_props_tmp (pp_page,pp_propname);
 
+--
+-- Holding area for deleted articles, which may be viewed
+-- or restored by admins through the Special:Undelete interface.
+-- The fields generally correspond to the page, revision, and text
+-- fields, with several caveats.
+-- Cannot reasonably create views on this table, due to the presence of TEXT
+-- columns.
+CREATE TABLE /*$wgDBprefix*/archive_tmp (
+   ar_id NOT NULL PRIMARY KEY clustered IDENTITY,
+   ar_namespace SMALLINT NOT NULL DEFAULT 0,
+   ar_title NVARCHAR(255) NOT NULL DEFAULT '',
+   ar_text NVARCHAR(MAX) NOT NULL,
+   ar_comment NVARCHAR(255) NOT NULL,
+   ar_user INT NULL REFERENCES /*$wgDBprefix*/[user](user_id) ON DELETE SET NULL,
+   ar_user_text NVARCHAR(255) NOT NULL,
+   ar_timestamp DATETIME NOT NULL DEFAULT GETDATE(),
+   ar_minor_edit BIT NOT NULL DEFAULT 0,
+   ar_flags NVARCHAR(255) NOT NULL,
+   ar_rev_id INT,
+   ar_text_id INT,
+   ar_deleted BIT NOT NULL DEFAULT 0,
+   ar_len INT DEFAULT NULL,
+   ar_page_id INT NULL,
+   ar_parent_id INT NULL
+);
+CREATE INDEX /*$wgDBprefix*/ar_name_title_timestamp ON /*$wgDBprefix*/archive_tmp(ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX /*$wgDBprefix*/ar_usertext_timestamp ON /*$wgDBprefix*/archive_tmp(ar_user_text,ar_timestamp);
+CREATE INDEX /*$wgDBprefix*/ar_user_text    ON /*$wgDBprefix*/archive_tmp(ar_user_text);
+
+--
+-- Track links to external URLs
+-- IE >= 4 supports no more than 2083 characters in a URL
+CREATE TABLE /*$wgDBprefix*/externallinks_tmp (
+   el_id INT NOT NULL PRIMARY KEY clustered IDENTITY,
+   el_from INT NOT NULL DEFAULT '0',
+   el_to VARCHAR(2083) NOT NULL,
+   el_index VARCHAR(896) NOT NULL,
+);
+-- Maximum key length ON SQL Server is 900 bytes
+CREATE INDEX /*$wgDBprefix*/externallinks_index   ON /*$wgDBprefix*/externallinks_tmp(el_index);
+
 --------------------------------------------------------------------------------
 -- Populate the new tables using INSERT SELECT
 --------------------------------------------------------------------------------
@@ -290,6 +333,8 @@ INSERT OR IGNORE INTO /*_*/interwiki_tmp SELECT * FROM /*_*/interwiki;
 INSERT OR IGNORE INTO /*_*/page_restrictions_tmp SELECT * FROM /*_*/page_restrictions;
 INSERT OR IGNORE INTO /*_*/protected_titles_tmp SELECT * FROM /*_*/protected_titles;
 INSERT OR IGNORE INTO /*_*/page_props_tmp SELECT * FROM /*_*/page_props;
+INSERT OR IGNORE INTO /*_*/archive_tmp SELECT * FROM /*_*/archive;
+INSERT OR IGNORE INTO /*_*/externallinks_tmp SELECT * FROM /*_*/externallinks;
 
 --------------------------------------------------------------------------------
 -- Do the table renames
@@ -331,6 +376,10 @@ DROP TABLE /*_*/protected_titles;
 ALTER TABLE /*_*/protected_titles_tmp RENAME TO /*_*/protected_titles;
 DROP TABLE /*_*/page_props;
 ALTER TABLE /*_*/page_props_tmp RENAME TO /*_*/page_props;
+DROP TABLE /*_*/archive;
+ALTER TABLE /*_*/archive_tmp RENAME TO /*_*/archive;
+DROP TABLE /*_*/externalllinks;
+ALTER TABLE /*_*/externallinks_tmp RENAME TO /*_*/externallinks;
 
 --------------------------------------------------------------------------------
 -- Drop and create tables with unique indexes but no valuable data
diff --git a/maintenance/sqlite/archives/patch-archive-ar_id.sql b/maintenance/sqlite/archives/patch-archive-ar_id.sql
new file mode 100644 (file)
index 0000000..00a9b07
--- /dev/null
@@ -0,0 +1,39 @@
+DROP TABLE IF EXISTS /*_*/archive_tmp;
+
+CREATE TABLE /*$wgDBprefix*/archive_tmp (
+  ar_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  ar_namespace int NOT NULL default 0,
+  ar_title varchar(255) binary NOT NULL default '',
+  ar_text mediumblob NOT NULL,
+  ar_comment tinyblob NOT NULL,
+  ar_user int unsigned NOT NULL default 0,
+  ar_user_text varchar(255) binary NOT NULL,
+  ar_timestamp binary(14) NOT NULL default '',
+  ar_minor_edit tinyint NOT NULL default 0,
+  ar_flags tinyblob NOT NULL,
+  ar_rev_id int unsigned,
+  ar_text_id int unsigned,
+  ar_deleted tinyint unsigned NOT NULL default 0,
+  ar_len int unsigned,
+  ar_page_id int unsigned,
+  ar_parent_id int unsigned default NULL,
+  ar_sha1 varbinary(32) NOT NULL default '',
+  ar_content_model varbinary(32) DEFAULT NULL,
+  ar_content_format varbinary(64) DEFAULT NULL
+);
+
+INSERT OR IGNORE INTO /*_*/archive_tmp (
+    ar_namespace, ar_title, ar_title, ar_text, ar_comment, ar_user, ar_user_text, ar_timestamp,
+    ar_minor_edit, ar_flags, ar_rev_id, ar_text_id, ar_deleted, ar_len, ar_page_id, ar_parent_id )
+    SELECT
+    ar_namespace, ar_title, ar_title, ar_text, ar_comment, ar_user, ar_user_text, ar_timestamp,
+    ar_minor_edit, ar_flags, ar_rev_id, ar_text_id, ar_deleted, ar_len, ar_page_id, ar_parent_id
+    FROM /*_*/archive;
+
+DROP TABLE /*_*/archive;
+
+ALTER TABLE /*_*/archive_tmp RENAME TO /*_*/archive;
+
+CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
+CREATE INDEX /*i*/ar_usertext_timestamp ON /*_*/archive (ar_user_text,ar_timestamp);
+CREATE INDEX /*i*/ar_revid ON /*_*/archive (ar_rev_id);
diff --git a/maintenance/sqlite/archives/patch-externallinks-el_id.sql b/maintenance/sqlite/archives/patch-externallinks-el_id.sql
new file mode 100644 (file)
index 0000000..0aad407
--- /dev/null
@@ -0,0 +1,19 @@
+DROP TABLE IF EXISTS /*_*/externallinks_tmp;
+
+CREATE TABLE /*$wgDBprefix*/externallinks_tmp (
+   el_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+   el_from int unsigned NOT NULL default 0,
+   el_to blob NOT NULL,
+   el_index blob NOT NULL
+);
+
+INSERT OR IGNORE INTO /*_*/externallinks_tmp (el_from, el_to, el_index) SELECT
+    el_from, el_to, el_index FROM /*_*/externallinks;
+
+DROP TABLE /*_*/externallinks;
+
+ALTER TABLE /*_*/externallinks_tmp RENAME TO /*_*/externallinks;
+
+CREATE INDEX /*i*/el_from ON /*_*/externallinks (el_from, el_to(40));
+CREATE INDEX /*i*/el_to ON /*_*/externallinks (el_to(60), el_from);
+CREATE INDEX /*i*/el_index ON /*_*/externallinks (el_index(60));
\ No newline at end of file
index 07fd14a..de92ef5 100644 (file)
@@ -380,6 +380,8 @@ CREATE TABLE /*_*/text (
 -- fields, with several caveats.
 --
 CREATE TABLE /*_*/archive (
+  -- Primary key
+  ar_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   ar_namespace int NOT NULL default 0,
   ar_title varchar(255) binary NOT NULL default '',
 
@@ -445,7 +447,6 @@ CREATE TABLE /*_*/archive (
 
   -- content format, see CONTENT_FORMAT_XXX constants
   ar_content_format varbinary(64) DEFAULT NULL
-
 ) /*$wgDBTableOptions*/;
 
 CREATE INDEX /*i*/name_title_timestamp ON /*_*/archive (ar_namespace,ar_title,ar_timestamp);
@@ -602,6 +603,9 @@ CREATE INDEX /*i*/cat_pages ON /*_*/category (cat_pages);
 -- Track links to external URLs
 --
 CREATE TABLE /*_*/externallinks (
+  -- Primary key
+  el_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+
   -- page_id of the referring page
   el_from int unsigned NOT NULL default 0,
 
@@ -1058,10 +1062,6 @@ CREATE TABLE /*_*/recentchanges (
   -- The type of change entry (RC_EDIT,RC_NEW,RC_LOG,RC_EXTERNAL)
   rc_type tinyint unsigned NOT NULL default 0,
 
-  -- The source of the change entry (replaces rc_type)
-  -- default of '' is temporary, needed for initial migration
-  rc_source varchar(16) binary not null default '',
-
   -- If the Recent Changes Patrol option is enabled,
   -- users may mark edits as having been reviewed to
   -- remove a warning flag on the RC list.
@@ -1080,7 +1080,7 @@ CREATE TABLE /*_*/recentchanges (
   -- Visibility of recent changes items, bitfield
   rc_deleted tinyint unsigned NOT NULL default 0,
 
-  -- Value corresonding to log_id, specific log entries
+  -- Value corresponding to log_id, specific log entries
   rc_logid int unsigned NOT NULL default 0,
   -- Store log type info here, or null
   rc_log_type varbinary(255) NULL default NULL,
index 381e172..cc83a4b 100644 (file)
@@ -3,9 +3,6 @@
  */
 ( function ( mw, $ ) {
 
-       // Cache token so we don't have to keep fetching new ones for every single request.
-       var cachedToken = null;
-
        $.extend( mw.Api.prototype, {
 
                /**
                 * @return {jQuery.Promise} See #post
                 */
                postWithEditToken: function ( params, ok, err ) {
-                       var useTokenToPost, getTokenIfBad,
-                               api = this;
-                       if ( cachedToken === null ) {
-                               // We don't have a valid cached token, so get a fresh one and try posting.
-                               // We do not trap any 'badtoken' or 'notoken' errors, because we don't want
-                               // an infinite loop. If this fresh token is bad, something else is very wrong.
-                               useTokenToPost = function ( token ) {
-                                       params.token = token;
-                                       api.post( params, { ok: ok, err: err } );
-                               };
-                               return api.getEditToken( useTokenToPost, err );
-                       } else {
-                               // We do have a token, but it might be expired. So if it is 'bad' then
-                               // start over with a new token.
-                               params.token = cachedToken;
-                               getTokenIfBad = function ( code, result ) {
-                                       if ( code === 'badtoken' ) {
-                                               // force a new token, clear any old one
-                                               cachedToken = null;
-                                               api.postWithEditToken( params, ok, err );
-                                       } else {
-                                               err( code, result );
-                                       }
-                               };
-                               return api.post( params, { ok: ok, err: getTokenIfBad } );
-                       }
+                       return this.postWithToken( 'edit', params ).done( ok ).fail( err );
                },
 
                /**
                 * @return {string} return.done.token Received token.
                 */
                getEditToken: function ( ok, err ) {
-                       var d = $.Deferred(),
-                               apiPromise;
-
-                       // Backwards compatibility (< MW 1.20)
-                       d.done( ok ).fail( err );
-
-                       apiPromise = this.get( {
-                                       action: 'tokens',
-                                       type: 'edit'
-                               }, {
-                                       // Due to the API assuming we're logged out if we pass the callback-parameter,
-                                       // we have to disable jQuery's callback system, and instead parse JSON string,
-                                       // by setting 'jsonp' to false.
-                                       // TODO: This concern seems genuine but no other module has it. Is it still
-                                       // needed and/or should we pass this by default?
-                                       jsonp: false
-                               } )
-                               .done( function ( data ) {
-                                       var token;
-                                       // If token type is not available for this user,
-                                       // key 'edittoken' is missing or can contain Boolean false
-                                       if ( data.tokens && data.tokens.edittoken ) {
-                                               token = data.tokens.edittoken;
-                                               cachedToken = token;
-                                               d.resolve( token );
-                                       } else {
-                                               d.reject( 'token-missing', data );
-                                       }
-                               } )
-                               .fail( d.reject );
-
-                       return d.promise( { abort: apiPromise.abort } );
+                       return this.getToken( 'edit' ).done( ok ).fail( err );
                },
 
                /**
                                text: message
                        }, ok, err );
                }
-
-        } );
+       } );
 
        /**
         * @class mw.Api
index 142c454..f9a14b0 100644 (file)
@@ -20,7 +20,8 @@
 
                                dataType: 'json'
                        }
-               };
+               },
+               tokenCache = {};
 
        /**
         * Constructor to create an object to interact with the API of a particular MediaWiki server.
                        return apiDeferred.promise( { abort: xhr.abort } ).fail( function ( code, details ) {
                                mw.log( 'mw.Api error: ', code, details );
                        } );
-               }
+               },
+
+               /**
+                * Post to API with specified type of token. If we have no token, get one and try to post.
+                * If we have a cached token try using that, and if it fails, blank out the
+                * cached token and start over. For example to change an user option you could do:
+                *
+                *     new mw.Api().postWithToken( 'options', {
+                *         action: 'options',
+                *         optionname: 'gender',
+                *         optionvalue: 'female'
+                *     } );
+                *
+                * @param {string} tokenType The name of the token, like options or edit.
+                * @param {Object} params API parameters
+                * @return {jQuery.Promise} See #post
+                */
+               postWithToken: function ( tokenType, params ) {
+                       var api = this, hasOwn = tokenCache.hasOwnProperty;
+                       if ( hasOwn.call( tokenCache, tokenType ) && tokenCache[tokenType] !== undefined ) {
+                               params.token = tokenCache[tokenType];
+                               return api.post( params ).then(
+                                       null,
+                                       function ( code ) {
+                                               if ( code === 'badtoken' ) {
+                                                       // force a new token, clear any old one
+                                                       tokenCache[tokenType] = params.token = undefined;
+                                                       return api.post( params );
+                                               }
+                                       }
+                               );
+                       } else {
+                               return api.getToken( tokenType ).then( function ( token ) {
+                                       tokenCache[tokenType] = params.token = token;
+                                       return api.post( params );
+                               } );
+                       }
+               },
 
+               /**
+                * Api helper to grab any token.
+                *
+                * @param {string} type Token type.
+                * @return {jQuery.Promise}
+                * @return {Function} return.done
+                * @return {string} return.done.token Received token.
+                */
+               getToken: function ( type ) {
+                       var apiPromise,
+                               d = $.Deferred();
+
+                       apiPromise = this.get( {
+                                       action: 'tokens',
+                                       type: type
+                               }, {
+                                       // Due to the API assuming we're logged out if we pass the callback-parameter,
+                                       // we have to disable jQuery's callback system, and instead parse JSON string,
+                                       // by setting 'jsonp' to false.
+                                       // TODO: This concern seems genuine but no other module has it. Is it still
+                                       // needed and/or should we pass this by default?
+                               } )
+                               .done( function ( data ) {
+                                       // If token type is not available for this user,
+                                       // key '...token' is missing or can contain Boolean false
+                                       if ( data.tokens && data.tokens[type + 'token'] ) {
+                                               d.resolve( data.tokens[type + 'token'] );
+                                       } else {
+                                               d.reject( 'token-missing', data );
+                                       }
+                               } )
+                               .fail( d.reject );
+
+                       return d.promise( { abort: apiPromise.abort } );
+               }
        };
 
        /**
index 70f639c..4ede809 100644 (file)
                 * @param {HTMLElement|jQuery|mw.Message|string} message
                 * @param {Object} options The options to use for the notification.
                 *  See #defaults for details.
+                * @return {Object} Object with a close function to close the notification
                 */
                notify: function ( message, options ) {
                        var notif;
                        } else {
                                preReadyNotifQueue.push( notif );
                        }
+                       return { close: $.proxy( notif.close, notif ) };
                },
 
                /**
index 83d95b6..743d651 100644 (file)
@@ -1,22 +1,23 @@
 /**
  * @class mw.plugin.notify
  */
-( function ( mw ) {
+( function ( mw, $ ) {
        'use strict';
 
        /**
         * @see mw.notification#notify
         * @param message
         * @param options
+        * @return {jQuery.Promise}
         */
        mw.notify = function ( message, options ) {
+               var d = $.Deferred();
                // Don't bother loading the whole notification system if we never use it.
                mw.loader.using( 'mediawiki.notification', function () {
-                       // Don't bother calling mw.loader.using a second time after we've already loaded mw.notification.
-                       mw.notify = mw.notification.notify;
                        // Call notify with the notification the user requested of us.
-                       mw.notify( message, options );
-               } );
+                       d.resolve( mw.notification.notify( message, options ) );
+               }, d.reject );
+               return d.promise();
        };
 
        /**
@@ -24,4 +25,4 @@
         * @mixins mw.plugin.notify
         */
 
-}( mediaWiki ) );
+}( mediaWiki, jQuery ) );
index 0f74899..1ba2d40 100644 (file)
@@ -56,6 +56,56 @@ class MessageTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'abcdefghijka2', $msg->params( $params )->plain(), 'Params > 9 are replaced correctly' );
        }
 
+       /**
+        * FIXME: This should not need database, but Language#formatExpiry does (bug 55912)
+        * @group Database
+        */
+       function testMessageParamTypes() {
+               $lang = Language::factory( 'en' );
+
+               $msg = new RawMessage( '$1' );
+               $this->assertEquals(
+                       $lang->formatNum( 123456.789 ),
+                       $msg->inLanguage( $lang )->numParams( 123456.789 )->plain(),
+                       'numParams is handled correctly'
+               );
+
+               $msg = new RawMessage( '$1' );
+               $this->assertEquals(
+                       $lang->formatDuration( 1234 ),
+                       $msg->inLanguage( $lang )->durationParams( 1234 )->plain(),
+                       'durationParams is handled correctly'
+               );
+
+               $msg = new RawMessage( '$1' );
+               $this->assertEquals(
+                       $lang->formatExpiry( wfTimestampNow() ),
+                       $msg->inLanguage( $lang )->expiryParams( wfTimestampNow() )->plain(),
+                       'expiryParams is handled correctly'
+               );
+
+               $msg = new RawMessage( '$1' );
+               $this->assertEquals(
+                       $lang->formatTimePeriod( 1234 ),
+                       $msg->inLanguage( $lang )->timeperiodParams( 1234 )->plain(),
+                       'timeperiodParams is handled correctly'
+               );
+
+               $msg = new RawMessage( '$1' );
+               $this->assertEquals(
+                       $lang->formatSize( 123456 ),
+                       $msg->inLanguage( $lang )->sizeParams( 123456 )->plain(),
+                       'sizeParams is handled correctly'
+               );
+
+               $msg = new RawMessage( '$1' );
+               $this->assertEquals(
+                       $lang->formatBitrate( 123456 ),
+                       $msg->inLanguage( $lang )->bitrateParams( 123456 )->plain(),
+                       'bitrateParams is handled correctly'
+               );
+       }
+
        function testInContentLanguageDisabled() {
                $this->setMwGlobals( 'wgLang', Language::factory( 'fr' ) );
 
index ee3db3e..2ac2942 100644 (file)
@@ -46,6 +46,7 @@ class GenderCacheTest extends MediaWikiLangTestCase {
         * test usernames
         *
         * @dataProvider provideUserGenders
+        * @covers GenderCache::getGenderOf
         */
        function testUserName( $username, $expectedGender ) {
                $genderCache = GenderCache::singleton();
@@ -57,6 +58,7 @@ class GenderCacheTest extends MediaWikiLangTestCase {
         * genderCache should work with user objects, too
         *
         * @dataProvider provideUserGenders
+        * @covers GenderCache::getGenderOf
         */
        function testUserObjects( $username, $expectedGender ) {
                $genderCache = GenderCache::singleton();
@@ -82,6 +84,7 @@ class GenderCacheTest extends MediaWikiLangTestCase {
         * against the never existing username
         *
         * @dataProvider provideStripSubpages
+        * @covers GenderCache::getGenderOf
         */
        function testStripSubpages( $pageWithSubpage, $expectedGender ) {
                $genderCache = GenderCache::singleton();
index c550150..3559f15 100644 (file)
@@ -3,6 +3,7 @@
 /**
  * @group Database
  * @group Cache
+ * @covers MessageCache
  */
 class MessageCacheTest extends MediaWikiLangTestCase {
 
index c345513..aedf594 100644 (file)
@@ -70,6 +70,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataGetDefaultModelFor
+        * @covers ContentHandler::getDefaultModelFor
         */
        public function testGetDefaultModelFor( $title, $expectedModelId ) {
                $title = Title::newFromText( $title );
@@ -78,6 +79,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataGetDefaultModelFor
+        * @covers ContentHandler::getForTitle
         */
        public function testGetForTitle( $title, $expectedContentModel ) {
                $title = Title::newFromText( $title );
@@ -97,6 +99,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataGetLocalizedName
+        * @covers ContentHandler::getLocalizedName
         */
        public function testGetLocalizedName( $id, $expected ) {
                $name = ContentHandler::getLocalizedName( $id );
@@ -131,6 +134,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataGetPageLanguage
+        * @covers ContentHandler::getPageLanguage
         */
        public function testGetPageLanguage( $title, $expected ) {
                if ( is_string( $title ) ) {
@@ -155,6 +159,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataGetContentText_Null
+        * @covers ContentHandler::getContentText
         */
        public function testGetContentText_Null( $contentHandlerTextFallback ) {
                $this->setMwGlobals( 'wgContentHandlerTextFallback', $contentHandlerTextFallback );
@@ -175,6 +180,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataGetContentText_TextContent
+        * @covers ContentHandler::getContentText
         */
        public function testGetContentText_TextContent( $contentHandlerTextFallback ) {
                $this->setMwGlobals( 'wgContentHandlerTextFallback', $contentHandlerTextFallback );
@@ -188,6 +194,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
        /**
         * ContentHandler::getContentText should have thrown an exception for non-text Content object
         * @expectedException MWException
+        * @covers ContentHandler::getContentText
         */
        public function testGetContentText_NonTextContent_fail() {
                $this->setMwGlobals( 'wgContentHandlerTextFallback', 'fail' );
@@ -197,6 +204,9 @@ class ContentHandlerTest extends MediaWikiTestCase {
                ContentHandler::getContentText( $content );
        }
 
+       /**
+        * @covers ContentHandler::getContentText
+        */
        public function testGetContentText_NonTextContent_serialize() {
                $this->setMwGlobals( 'wgContentHandlerTextFallback', 'serialize' );
 
@@ -206,6 +216,9 @@ class ContentHandlerTest extends MediaWikiTestCase {
                $this->assertEquals( $content->serialize(), $text );
        }
 
+       /**
+        * @covers ContentHandler::getContentText
+        */
        public function testGetContentText_NonTextContent_ignore() {
                $this->setMwGlobals( 'wgContentHandlerTextFallback', 'ignore' );
 
@@ -241,6 +254,7 @@ class ContentHandlerTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataMakeContent
+        * @covers ContentHandler::makeContent
         */
        public function testMakeContent( $data, $title, $modelId, $format, $expectedModelId, $expectedNativeData, $shouldFail ) {
                $title = Title::newFromText( $title );
@@ -270,6 +284,9 @@ class ContentHandlerTest extends MediaWikiTestCase {
        }
        */
 
+       /**
+        * @covers ContentHandler::runLegacyHooks
+        */
        public function testRunLegacyHooks() {
                Hooks::register( 'testRunLegacyHooks', __CLASS__ . '::dummyHookHandler' );
 
index 1c45820..bd6d41f 100644 (file)
@@ -50,12 +50,18 @@ class CssContentTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers CssContent::getModel
+        */
        public function testGetModel() {
                $content = $this->newContent( 'hello world.' );
 
                $this->assertEquals( CONTENT_MODEL_CSS, $content->getModel() );
        }
 
+       /**
+        * @covers CssContent::getContentHandler
+        */
        public function testGetContentHandler() {
                $content = $this->newContent( 'hello world.' );
 
@@ -73,6 +79,7 @@ class CssContentTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider dataEquals
+        * @covers CssContent::equals
         */
        public function testEquals( Content $a, Content $b = null, $equal = false ) {
                $this->assertEquals( $equal, $a->equals( $b ) );
index 5c1ff8f..c8616ff 100644 (file)
@@ -89,6 +89,9 @@ class JavaScriptContentTest extends TextContentTest {
                );
        }
 
+       /**
+        * @covers JavaScriptContent::addSectionHeader
+        */
        public function testAddSectionHeader() {
                $content = $this->newContent( 'hello world' );
                $c = $content->addSectionHeader( 'test' );
@@ -233,6 +236,9 @@ class JavaScriptContentTest extends TextContentTest {
                );
        }
 
+       /**
+        * @covers JavaScriptContent::matchMagicWord
+        */
        public function testMatchMagicWord() {
                $mw = MagicWord::get( "staticredirect" );
 
@@ -240,6 +246,9 @@ class JavaScriptContentTest extends TextContentTest {
                $this->assertFalse( $content->matchMagicWord( $mw ), "should not have matched magic word, since it's not wikitext" );
        }
 
+       /**
+        * @covers JavaScriptContent::updateRedirect
+        */
        public function testUpdateRedirect() {
                $target = Title::newFromText( "testUpdateRedirect_target" );
 
@@ -249,12 +258,18 @@ class JavaScriptContentTest extends TextContentTest {
                $this->assertTrue( $content->equals( $newContent ), "content should be unchanged since it's not wikitext" );
        }
 
+       /**
+        * @covers JavaScriptContent::getModel
+        */
        public function testGetModel() {
                $content = $this->newContent( "hello world." );
 
                $this->assertEquals( CONTENT_MODEL_JAVASCRIPT, $content->getModel() );
        }
 
+       /**
+        * @covers JavaScriptContent::getContentHandler
+        */
        public function testGetContentHandler() {
                $content = $this->newContent( "hello world." );
 
index c7138b7..a1f099f 100644 (file)
@@ -51,6 +51,7 @@ class TextContentTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataGetParserOutput
+        * @covers TextContent::getParserOutput
         */
        public function testGetParserOutput( $title, $model, $text, $expectedHtml, $expectedFields = null ) {
                $title = Title::newFromText( $title );
@@ -96,6 +97,7 @@ class TextContentTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataPreSaveTransform
+        * @covers TextContent::preSaveTransform
         */
        public function testPreSaveTransform( $text, $expected ) {
                global $wgContLang;
@@ -119,6 +121,7 @@ class TextContentTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataPreloadTransform
+        * @covers TextContent::preloadTransform
         */
        public function testPreloadTransform( $text, $expected ) {
                global $wgContLang;
@@ -140,6 +143,7 @@ class TextContentTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataGetRedirectTarget
+        * @covers TextContent::getRedirectTarget
         */
        public function testGetRedirectTarget( $text, $expected ) {
                $content = $this->newContent( $text );
@@ -154,6 +158,7 @@ class TextContentTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataGetRedirectTarget
+        * @covers TextContent::isRedirect
         */
        public function testIsRedirect( $text, $expected ) {
                $content = $this->newContent( $text );
@@ -209,6 +214,7 @@ class TextContentTest extends MediaWikiLangTestCase {
        /**
         * @dataProvider dataIsCountable
         * @group Database
+        * @covers TextContent::isCountable
         */
        public function testIsCountable( $text, $hasLinks, $mode, $expected ) {
                $this->setMwGlobals( 'wgArticleCountMethod', $mode );
@@ -240,6 +246,7 @@ class TextContentTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataGetTextForSummary
+        * @covers TextContent::getTextForSummary
         */
        public function testGetTextForSummary( $text, $maxlength, $expected ) {
                $content = $this->newContent( $text );
@@ -247,12 +254,18 @@ class TextContentTest extends MediaWikiLangTestCase {
                $this->assertEquals( $expected, $content->getTextForSummary( $maxlength ) );
        }
 
+       /**
+        * @covers TextContent::getTextForSearchIndex
+        */
        public function testGetTextForSearchIndex() {
                $content = $this->newContent( 'hello world.' );
 
                $this->assertEquals( 'hello world.', $content->getTextForSearchIndex() );
        }
 
+       /**
+        * @covers TextContent::copy
+        */
        public function testCopy() {
                $content = $this->newContent( 'hello world.' );
                $copy = $content->copy();
@@ -261,30 +274,45 @@ class TextContentTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'hello world.', $copy->getNativeData() );
        }
 
+       /**
+        * @covers TextContent::getSize
+        */
        public function testGetSize() {
                $content = $this->newContent( 'hello world.' );
 
                $this->assertEquals( 12, $content->getSize() );
        }
 
+       /**
+        * @covers TextContent::getNativeData
+        */
        public function testGetNativeData() {
                $content = $this->newContent( 'hello world.' );
 
                $this->assertEquals( 'hello world.', $content->getNativeData() );
        }
 
+       /**
+        * @covers TextContent::getWikitextForTransclusion
+        */
        public function testGetWikitextForTransclusion() {
                $content = $this->newContent( 'hello world.' );
 
                $this->assertEquals( 'hello world.', $content->getWikitextForTransclusion() );
        }
 
+       /**
+        * @covers TextContent::getModel
+        */
        public function testGetModel() {
                $content = $this->newContent( "hello world." );
 
                $this->assertEquals( CONTENT_MODEL_TEXT, $content->getModel() );
        }
 
+       /**
+        * @covers TextContent::getContentHandler
+        */
        public function testGetContentHandler() {
                $content = $this->newContent( "hello world." );
 
@@ -302,6 +330,7 @@ class TextContentTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataIsEmpty
+        * @covers TextContent::isEmpty
         */
        public function testIsEmpty( $text, $empty ) {
                $content = $this->newContent( $text );
@@ -321,6 +350,7 @@ class TextContentTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataEquals
+        * @covers TextContent::equals
         */
        public function testEquals( Content $a, Content $b = null, $equal = false ) {
                $this->assertEquals( $equal, $a->equals( $b ) );
@@ -342,6 +372,7 @@ class TextContentTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataGetDeletionUpdates
+        * @covers TextContent::getDeletionUpdates
         */
        public function testDeletionUpdates( $title, $model, $text, $expectedStuff ) {
                $ns = $this->getDefaultWikitextNS();
@@ -410,6 +441,7 @@ class TextContentTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider provideConvert
+        * @covers TextContent::convert
         */
        public function testConvert( $text, $model, $lossy, $expectedNative ) {
                $content = $this->newContent( $text );
index d213251..75a7278 100644 (file)
@@ -16,6 +16,9 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase {
                $this->handler = ContentHandler::getForModelID( CONTENT_MODEL_WIKITEXT );
        }
 
+       /**
+        * @covers WikitextContentHandler::serializeContent
+        */
        public function testSerializeContent() {
                $content = new WikitextContent( 'hello world' );
 
@@ -30,6 +33,9 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase {
                }
        }
 
+       /**
+        * @covers WikitextContentHandler::unserializeContent
+        */
        public function testUnserializeContent() {
                $content = $this->handler->unserializeContent( 'hello world' );
                $this->assertEquals( 'hello world', $content->getNativeData() );
@@ -45,6 +51,9 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase {
                }
        }
 
+       /**
+        * @covers WikitextContentHandler::makeEmptyContent
+        */
        public function testMakeEmptyContent() {
                $content = $this->handler->makeEmptyContent();
 
@@ -64,6 +73,7 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase {
         * @dataProvider provideMakeRedirectContent
         * @param Title|string $title Title object or string for Title::newFromText()
         * @param string $expected Serialized form of the content object built
+        * @covers WikitextContentHandler::makeRedirectContent
         */
        public function testMakeRedirectContent( $title, $expected ) {
                global $wgContLang;
@@ -92,6 +102,7 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataIsSupportedFormat
+        * @covers WikitextContentHandler::isSupportedFormat
         */
        public function testIsSupportedFormat( $format, $supported ) {
                $this->assertEquals( $supported, $this->handler->isSupportedFormat( $format ) );
@@ -131,6 +142,7 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataMerge3
+        * @covers WikitextContentHandler::merge3
         */
        public function testMerge3( $old, $mine, $yours, $expected ) {
                $this->checkHasDiff3();
@@ -188,6 +200,7 @@ class WikitextContentHandlerTest extends MediaWikiLangTestCase {
 
        /**
         * @dataProvider dataGetAutosummary
+        * @covers WikitextContentHandler::getAutosummary
         */
        public function testGetAutosummary( $old, $new, $flags, $expected ) {
                $oldContent = is_null( $old ) ? null : new WikitextContent( $old );
index 37b01fd..9f20073 100644 (file)
@@ -64,6 +64,7 @@ more stuff
        /**
         * @dataProvider dataGetSecondaryDataUpdates
         * @group Database
+        * @covers WikitextContent::getSecondaryDataUpdates
         */
        public function testGetSecondaryDataUpdates( $title, $model, $text, $expectedStuff ) {
                $ns = $this->getDefaultWikitextNS();
@@ -116,6 +117,7 @@ just a test"
 
        /**
         * @dataProvider dataGetSection
+        * @covers WikitextContent::getSection
         */
        public function testGetSection( $text, $sectionId, $expectedText ) {
                $content = $this->newContent( $text );
@@ -167,6 +169,7 @@ just a test"
 
        /**
         * @dataProvider dataReplaceSection
+        * @covers WikitextContent::replaceSection
         */
        public function testReplaceSection( $text, $section, $with, $sectionTitle, $expected ) {
                $content = $this->newContent( $text );
@@ -175,6 +178,9 @@ just a test"
                $this->assertEquals( $expected, is_null( $c ) ? null : $c->getNativeData() );
        }
 
+       /**
+        * @covers WikitextContent::addSectionHeader
+        */
        public function testAddSectionHeader() {
                $content = $this->newContent( 'hello world' );
                $content = $content->addSectionHeader( 'test' );
@@ -319,6 +325,9 @@ just a test"
                );
        }
 
+       /**
+        * @covers WikitextContent::matchMagicWord
+        */
        public function testMatchMagicWord() {
                $mw = MagicWord::get( "staticredirect" );
 
@@ -329,6 +338,9 @@ just a test"
                $this->assertFalse( $content->matchMagicWord( $mw ), "should not have matched magic word" );
        }
 
+       /**
+        * @covers WikitextContent::updateRedirect
+        */
        public function testUpdateRedirect() {
                $target = Title::newFromText( "testUpdateRedirect_target" );
 
@@ -348,12 +360,18 @@ just a test"
                $this->assertEquals( $target->getFullText(), $newContent->getRedirectTarget()->getFullText() );
        }
 
+       /**
+        * @covers WikitextContent::getModel
+        */
        public function testGetModel() {
                $content = $this->newContent( "hello world." );
 
                $this->assertEquals( CONTENT_MODEL_WIKITEXT, $content->getModel() );
        }
 
+       /**
+        * @covers WikitextContent::getContentHandler
+        */
        public function testGetContentHandler() {
                $content = $this->newContent( "hello world." );
 
index 8138b70..7d7a2bd 100644 (file)
@@ -58,6 +58,7 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideDiapers
+        * @covers DatabaseMysqlBase::addIdentifierQuotes
         */
        function testAddIdentifierQuotes( $expected, $in ) {
                $db = new FakeDatabaseMysqlBase();
index 46ccfe0..726d63a 100644 (file)
@@ -6,6 +6,9 @@
  */
 class DatabaseSQLTest extends MediaWikiTestCase {
 
+       /**
+        * @var DatabaseTestHelper
+        */
        private $database;
 
        protected function setUp() {
@@ -22,6 +25,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideSelect
+        * @covers DatabaseBase::select
         */
        function testSelect( $sql, $sqlText ) {
                $this->database->select(
@@ -123,6 +127,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideUpdate
+        * @covers DatabaseBase::update
         */
        function testUpdate( $sql, $sqlText ) {
                $this->database->update(
@@ -174,6 +179,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideDelete
+        * @covers DatabaseBase::delete
         */
        function testDelete( $sql, $sqlText ) {
                $this->database->delete(
@@ -206,6 +212,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideUpsert
+        * @covers DatabaseBase::upsert
         */
        function testUpsert( $sql, $sqlText ) {
                $this->database->upsert(
@@ -241,6 +248,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideDeleteJoin
+        * @covers DatabaseBase::deleteJoin
         */
        function testDeleteJoin( $sql, $sqlText ) {
                $this->database->deleteJoin(
@@ -287,6 +295,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideInsert
+        * @covers DatabaseBase::insert
         */
        function testInsert( $sql, $sqlText ) {
                $this->database->insert(
@@ -339,6 +348,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideInsertSelect
+        * @covers DatabaseBase::insertSelect
         */
        function testInsertSelect( $sql, $sqlText ) {
                $this->database->insertSelect(
@@ -401,6 +411,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideReplace
+        * @covers DatabaseBase::replace
         */
        function testReplace( $sql, $sqlText ) {
                $this->database->replace(
@@ -515,6 +526,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideNativeReplace
+        * @covers DatabaseBase::nativeReplace
         */
        function testNativeReplace( $sql, $sqlText ) {
                $this->database->nativeReplace(
@@ -541,6 +553,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideConditional
+        * @covers DatabaseBase::conditional
         */
        function testConditional( $sql, $sqlText ) {
                $this->assertEquals( trim( $this->database->conditional(
@@ -581,6 +594,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideBuildConcat
+        * @covers DatabaseBase::buildConcat
         */
        function testBuildConcat( $stringList, $sqlText ) {
                $this->assertEquals( trim( $this->database->buildConcat(
@@ -603,6 +617,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideBuildLike
+        * @covers DatabaseBase::buildLike
         */
        function testBuildLike( $array, $sqlText ) {
                $this->assertEquals( trim( $this->database->buildLike(
@@ -633,6 +648,7 @@ class DatabaseSQLTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideUnionQueries
+        * @covers DatabaseBase::unionQueries
         */
        function testUnionQueries( $sql, $sqlText ) {
                $this->assertEquals( trim( $this->database->unionQueries(
@@ -667,24 +683,36 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers DatabaseBase::commit
+        */
        function testTransactionCommit() {
                $this->database->begin( __METHOD__ );
                $this->database->commit( __METHOD__ );
                $this->assertLastSql( 'BEGIN; COMMIT' );
        }
 
+       /**
+        * @covers DatabaseBase::rollback
+        */
        function testTransactionRollback() {
                $this->database->begin( __METHOD__ );
                $this->database->rollback( __METHOD__ );
                $this->assertLastSql( 'BEGIN; ROLLBACK' );
        }
 
+       /**
+        * @covers DatabaseBase::dropTable
+        */
        function testDropTable() {
                $this->database->setExistingTables( array( 'table' ) );
                $this->database->dropTable( 'table', __METHOD__ );
                $this->assertLastSql( 'DROP TABLE table' );
        }
 
+       /**
+        * @covers DatabaseBase::dropTable
+        */
        function testDropNonExistingTable() {
                $this->assertFalse(
                        $this->database->dropTable( 'non_existing', __METHOD__ )
index 91ab33a..f151fb2 100644 (file)
@@ -27,6 +27,10 @@ class MockDatabaseSqlite extends DatabaseSqliteStandalone {
  * @group medium
  */
 class DatabaseSqliteTest extends MediaWikiTestCase {
+
+       /**
+        * @var MockDatabaseSqlite
+        */
        var $db;
 
        protected function setUp() {
@@ -83,6 +87,7 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
 
        /**
         * @dataProvider provideAddQuotes()
+        * @covers DatabaseSqlite::addQuotes
         */
        public function testAddQuotes( $value, $expected ) {
                // check quoting
@@ -105,6 +110,9 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                }
        }
 
+       /**
+        * @covers DatabaseSqlite::replaceVars
+        */
        public function testReplaceVars() {
                $this->assertEquals( 'foo', $this->replaceVars( 'foo' ), "Don't break anything accidentally" );
 
@@ -143,6 +151,9 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers DatabaseSqlite::tableName
+        */
        public function testTableName() {
                // @todo Moar!
                $db = new DatabaseSqliteStandalone( ':memory:' );
@@ -153,6 +164,9 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                $this->assertEquals( 'foobar', $db->tableName( 'bar' ) );
        }
 
+       /**
+        * @covers DatabaseSqlite::duplicateTableStructure
+        */
        public function testDuplicateTableStructure() {
                $db = new DatabaseSqliteStandalone( ':memory:' );
                $db->query( 'CREATE TABLE foo(foo, barfoo)' );
@@ -174,6 +188,9 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers DatabaseSqlite::duplicateTableStructure
+        */
        public function testDuplicateTableStructureVirtual() {
                $db = new DatabaseSqliteStandalone( ':memory:' );
                if ( $db->getFulltextSearchModule() != 'FTS3' ) {
@@ -194,6 +211,9 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @covers DatabaseSqlite::deleteJoin
+        */
        public function testDeleteJoin() {
                $db = new DatabaseSqliteStandalone( ':memory:' );
                $db->query( 'CREATE TABLE a (a_1)', __METHOD__ );
@@ -306,12 +326,18 @@ class DatabaseSqliteTest extends MediaWikiTestCase {
                }
        }
 
+       /**
+        * @covers DatabaseSqlite::insertId
+        */
        public function testInsertIdType() {
                $db = new DatabaseSqliteStandalone( ':memory:' );
-               $this->assertInstanceOf( 'ResultWrapper',
-                       $db->query( 'CREATE TABLE a ( a_1 )', __METHOD__ ), "Database creationg" );
-               $this->assertTrue( $db->insert( 'a', array( 'a_1' => 10 ), __METHOD__ ),
-                       "Insertion worked" );
+
+               $databaseCreation = $db->query( 'CREATE TABLE a ( a_1 )', __METHOD__ );
+               $this->assertInstanceOf( 'ResultWrapper', $databaseCreation, "Database creation" );
+
+               $insertion = $db->insert( 'a', array( 'a_1' => 10 ), __METHOD__ );
+               $this->assertTrue( $insertion, "Insertion worked" );
+
                $this->assertInternalType( 'integer', $db->insertId(), "Actual typecheck" );
                $this->assertTrue( $db->close(), "closing database" );
        }
index a3ef55a..5e8e7a4 100644 (file)
@@ -5,7 +5,11 @@
  * @group DatabaseBase
  */
 class DatabaseTest extends MediaWikiTestCase {
-       var $db, $functionTest = false;
+       /**
+        * @var DatabaseBase
+        */
+       var $db;
+       var $functionTest = false;
 
        protected function setUp() {
                parent::setUp();
@@ -19,7 +23,9 @@ class DatabaseTest extends MediaWikiTestCase {
                        $this->functionTest = false;
                }
        }
-
+       /**
+        * @covers DatabaseBase::dropTable
+        */
        function testAddQuotesNull() {
                $check = "NULL";
                if ( $this->db->getType() === 'sqlite' || $this->db->getType() === 'oracle' ) {