Merge "FormOptions: Document getValueReal(), consistency fixes"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sat, 19 Oct 2013 15:12:29 +0000 (15:12 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sat, 19 Oct 2013 15:12:29 +0000 (15:12 +0000)
162 files changed:
RELEASE-NOTES-1.22
docs/hooks.txt
includes/AutoLoader.php
includes/ChangesList.php [deleted file]
includes/DefaultSettings.php
includes/Exception.php
includes/HTMLForm.php
includes/HashRing.php
includes/Html.php
includes/IP.php
includes/LinksUpdate.php
includes/Message.php
includes/RecentChange.php [deleted file]
includes/SpecialPage.php
includes/api/ApiQuerySiteinfo.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/Installer.i18n.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/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/specials/SpecialBlockList.php
includes/specials/SpecialContributions.php
includes/specials/SpecialEditWatchlist.php
includes/specials/SpecialEmailuser.php
includes/specials/SpecialNewimages.php
includes/specials/SpecialPasswordReset.php
includes/specials/SpecialPreferences.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialTags.php
includes/specials/SpecialUpload.php
includes/specials/SpecialUploadStash.php
includes/specials/SpecialUserlogin.php
includes/specials/SpecialWatchlist.php
includes/templates/Usercreate.php
includes/templates/Userlogin.php
languages/messages/MessagesAr.php
languages/messages/MessagesAst.php
languages/messages/MessagesBg.php
languages/messages/MessagesBn.php
languages/messages/MessagesCa.php
languages/messages/MessagesCe.php
languages/messages/MessagesCs.php
languages/messages/MessagesCy.php
languages/messages/MessagesDa.php
languages/messages/MessagesDe.php
languages/messages/MessagesEn.php
languages/messages/MessagesEo.php
languages/messages/MessagesEs.php
languages/messages/MessagesFa.php
languages/messages/MessagesFi.php
languages/messages/MessagesFr.php
languages/messages/MessagesGl.php
languages/messages/MessagesHe.php
languages/messages/MessagesHi.php
languages/messages/MessagesHr.php
languages/messages/MessagesIo.php
languages/messages/MessagesIt.php
languages/messages/MessagesJa.php
languages/messages/MessagesKo.php
languages/messages/MessagesLa.php
languages/messages/MessagesLb.php
languages/messages/MessagesLt.php
languages/messages/MessagesLv.php
languages/messages/MessagesMhr.php
languages/messages/MessagesMk.php
languages/messages/MessagesNds_nl.php
languages/messages/MessagesNl.php
languages/messages/MessagesNn.php
languages/messages/MessagesOr.php
languages/messages/MessagesPl.php
languages/messages/MessagesPms.php
languages/messages/MessagesPs.php
languages/messages/MessagesPt.php
languages/messages/MessagesPt_br.php
languages/messages/MessagesRo.php
languages/messages/MessagesRu.php
languages/messages/MessagesSco.php
languages/messages/MessagesSk.php
languages/messages/MessagesSl.php
languages/messages/MessagesSv.php
languages/messages/MessagesTr.php
languages/messages/MessagesUk.php
languages/messages/MessagesVi.php
languages/messages/MessagesVmf.php
languages/messages/MessagesVo.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-tag_summary.sql [new file with mode: 0644]
maintenance/archives/patch-valid_tag.sql [new file with mode: 0644]
maintenance/copyFileBackend.php
maintenance/deleteEqualMessages.php
maintenance/importImages.php
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/postgres/archives/patch-profiling.sql
maintenance/postgres/tables.sql
maintenance/purgeChangedPages.php
maintenance/showJobs.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/Resources.php
resources/mediawiki.api/mediawiki.api.edit.js
resources/mediawiki.api/mediawiki.api.js
resources/mediawiki.ui/mediawiki.ui.default.css
resources/mediawiki.ui/mediawiki.ui.vector.css
resources/mediawiki.ui/sourcefiles/scss/components/default/_forms.scss
resources/mediawiki/mediawiki.Title.js
resources/mediawiki/mediawiki.notification.js
resources/mediawiki/mediawiki.notify.js
skins/Vector.php
skins/vector/beta/screen.less [new file with mode: 0644]
skins/vector/beta/variables.less [new file with mode: 0644]
skins/vector/collapsibleNav.less
skins/vector/screen-hd.css [deleted file]
skins/vector/screen-hd.less [new file with mode: 0644]
skins/vector/screen.less
skins/vector/styles-beta.less [new file with mode: 0644]
skins/vector/styles.less [new file with mode: 0644]
skins/vector/variables.less [new file with mode: 0644]
tests/phpunit/includes/LinksUpdateTest.php
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
thumb.php

index a365da6..b80d04a 100644 (file)
@@ -240,6 +240,14 @@ production.
 * ResourceLoader supports hashes as module cache invalidation trigger (instead
   of or in addition to timestamps).
 * 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
@@ -324,6 +332,7 @@ 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
 
 === API changes in 1.22 ===
 * (bug 25553) The JSON output formatter now leaves forward slashes unescaped
@@ -362,7 +371,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
@@ -489,6 +498,10 @@ changes to languages because of Bugzilla reports.
   of media handler overriding MediaHandler::parseParamString.
 * (bug 46512) The collapsibleNav feature from the Vector extension has been moved
   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 2d1001b..53993de 100644 (file)
@@ -432,6 +432,10 @@ sites general information.
 $module: the current ApiQuerySiteInfo module
 &$results: array of results, add things here
 
+'APIQuerySiteInfoStatisticsInfo': Use this hook to add extra information to the
+sites statistics information.
+&$results: array of results, add things here
+
 'APIQueryUsersTokens': Use this hook to add custom token to list=users. Every
 token has an action, which will be used in the ustoken parameter and in the
 output (actiontoken="..."), and a callback function which should return the
index 8d571ad..7fa9096 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',
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 21cd1ff..a0a1b3e 100644 (file)
@@ -1872,11 +1872,6 @@ $wgAllowSlowParserFunctions = false;
  */
 $wgAllowSchemaUpdates = true;
 
-/**
- * Do DELETE/INSERT for link updates instead of incremental
- */
-$wgUseDumbLinkUpdate = false;
-
 /**
  * Anti-lock flags - bitfield
  *   - ALF_NO_LINK_LOCK:
@@ -4640,13 +4635,20 @@ $wgRateLimits = array(
                'ip' => null,
                'subnet' => null,
        ),
-       'mailpassword' => array(
+       'mailpassword' => array( // triggering password resets emails
                'anon' => null,
        ),
-       'emailuser' => array(
+       'emailuser' => array( // emailing other users using MediaWiki
+               'user' => null,
+       ),
+       'linkpurge' => array( // purges of link tables
+               'anon' => null,
                'user' => null,
+               'newbie' => null,
+               'ip' => null,
+               'subnet' => null,
        ),
-       'linkpurge' => array(
+       'renderfile' => array( // files rendered via thumb.php or thumb_handler.php
                'anon' => null,
                'user' => null,
                'newbie' => null,
@@ -5934,6 +5936,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 6724c4a..fba857f 100644 (file)
@@ -30,7 +30,6 @@
  * @ingroup Exception
  */
 class MWException extends Exception {
-       var $logId;
 
        /**
         * Should the exception use $wgOut to output the error?
@@ -131,7 +130,7 @@ class MWException extends Exception {
                                "</p>\n";
                } else {
                        return "<div class=\"errorbox\">" .
-                               '[' . $this->getLogId() . '] ' .
+                               '[' . MWExceptionHandler::getLogId( $this ) . '] ' .
                                gmdate( 'Y-m-d H:i:s' ) .
                                ": Fatal exception of type " . get_class( $this ) . "</div>\n" .
                                "<!-- Set \$wgShowExceptionDetails = true; " .
@@ -169,43 +168,28 @@ class MWException extends Exception {
        }
 
        /**
-        * Get a random ID for this error.
-        * This allows to link the exception to its corresponding log entry when
-        * $wgShowExceptionDetails is set to false.
+        * Get a the ID for this error.
         *
+        * @since 1.20
+        * @deprecated since 1.22 Use MWExceptionHandler::getLogId instead.
         * @return string
         */
        function getLogId() {
-               if ( $this->logId === null ) {
-                       $this->logId = wfRandomString( 8 );
-               }
-               return $this->logId;
+               wfDeprecated( __METHOD__, '1.22' );
+               return MWExceptionHandler::getLogId( $this );
        }
 
        /**
         * Return the requested URL and point to file and line number from which the
         * exception occurred
         *
+        * @since 1.8
+        * @deprecated since 1.22 Use MWExceptionHandler::getLogMessage instead.
         * @return string
         */
        function getLogMessage() {
-               global $wgRequest;
-
-               $id = $this->getLogId();
-               $file = $this->getFile();
-               $line = $this->getLine();
-               $message = $this->getMessage();
-
-               if ( isset( $wgRequest ) && !$wgRequest instanceof FauxRequest ) {
-                       $url = $wgRequest->getRequestURL();
-                       if ( !$url ) {
-                               $url = '[no URL]';
-                       }
-               } else {
-                       $url = '[no req]';
-               }
-
-               return "[$id] $url   Exception from line $line of $file: $message";
+               wfDeprecated( __METHOD__, '1.22' );
+               return MWExceptionHandler::getLogMessage( $this );
        }
 
        /**
@@ -249,7 +233,7 @@ class MWException extends Exception {
        function report() {
                global $wgMimeType;
 
-               $this->logException();
+               MWExceptionHandler::logException( $this );
 
                if ( defined( 'MW_API' ) ) {
                        // Unhandled API exception, we can't be sure that format printer is alive
@@ -266,22 +250,6 @@ class MWException extends Exception {
                }
        }
 
-       /**
-        * Log the error message to the exception log (if enabled)
-        */
-       function logException() {
-               global $wgLogExceptionBacktrace;
-
-               $log = $this->getLogMessage();
-               if ( $log ) {
-                       if ( $wgLogExceptionBacktrace ) {
-                               wfDebugLog( 'exception', $log . "\n" . MWExceptionHandler::formatRedactedTrace( $this ) . "\n" );
-                       } else {
-                               wfDebugLog( 'exception', $log );
-                       }
-               }
-       }
-
        /**
         * Check whether we are in command line mode or not to report the exception
         * in the correct format.
@@ -752,17 +720,88 @@ class MWExceptionHandler {
                                $finalExceptionText .= $call['function'];
                        }
                        $args = array();
-                       foreach ( $call['args'] as $arg ) {
-                               if ( is_object( $arg ) ) {
-                                       $args[] = 'Object(' . get_class( $arg ) . ')';
-                               } elseif( is_array( $arg ) ) {
-                                       $args[] = 'Array';
-                               } else {
-                                       $args[] = var_export( $arg, true );
+                       if ( isset( $call['args'] ) ) {
+                               foreach ( $call['args'] as $arg ) {
+                                       if ( is_object( $arg ) ) {
+                                               $args[] = 'Object(' . get_class( $arg ) . ')';
+                                       } elseif( is_array( $arg ) ) {
+                                               $args[] = 'Array';
+                                       } else {
+                                               $args[] = var_export( $arg, true );
+                                       }
                                }
                        }
                        $finalExceptionText .=  '(' . implode( ', ', $args ) . ")\n";
                }
                return $finalExceptionText . '#' . ( $i + 1 ) . ' {main}';
        }
+
+
+       /**
+        * Get the ID for this error.
+        *
+        * The ID is saved so that one can match the one output to the user (when
+        * $wgShowExceptionDetails is set to false), to the entry in the debug log.
+        *
+        * @since 1.22
+        * @param Exception $e
+        * @return string
+        */
+       public static function getLogId( Exception $e ) {
+               if ( !isset( $e->_mwLogId ) ) {
+                       $e->_mwLogId = wfRandomString( 8 );
+               }
+               return $e->_mwLogId;
+       }
+
+       /**
+        * Return the requested URL and point to file and line number from which the
+        * exception occurred.
+        *
+        * @since 1.22
+        * @param Exception $e
+        * @return string
+        */
+       public static function getLogMessage( Exception $e ) {
+               global $wgRequest;
+
+               $id = self::getLogId( $e );
+               $file = $e->getFile();
+               $line = $e->getLine();
+               $message = $e->getMessage();
+
+               if ( isset( $wgRequest ) && !$wgRequest instanceof FauxRequest ) {
+                       $url = $wgRequest->getRequestURL();
+                       if ( !$url ) {
+                               $url = '[no URL]';
+                       }
+               } else {
+                       $url = '[no req]';
+               }
+
+               return "[$id] $url   Exception from line $line of $file: $message";
+       }
+
+       /**
+        * Log an exception to the exception log (if enabled).
+        *
+        * This method must not assume the exception is an MWException,
+        * it is also used to handle PHP errors or errors from other libraries.
+        *
+        * @since 1.22
+        * @param Exception $e
+        */
+       public static function logException( Exception $e ) {
+               global $wgLogExceptionBacktrace;
+
+               $log = self::getLogMessage( $e );
+               if ( $log ) {
+                       if ( $wgLogExceptionBacktrace ) {
+                               wfDebugLog( 'exception', $log . "\n" . self::formatRedactedTrace( $e ) . "\n" );
+                       } else {
+                               wfDebugLog( 'exception', $log );
+                       }
+               }
+       }
+
 }
index 70dc7c7..d260862 100644 (file)
@@ -187,6 +187,7 @@ class HTMLForm extends ContextSource {
                'table',
                'div',
                'raw',
+               'vform',
        );
 
        /**
@@ -223,8 +224,15 @@ class HTMLForm extends ContextSource {
                        }
 
                        $field = self::loadInputFromParameters( $fieldname, $info );
+                       // FIXME During field's construct, the parent form isn't available!
+                       // could add a 'parent' name-value to $info, could add a third parameter.
                        $field->mParent = $this;
 
+                       // vform gets too much space if empty labels generate HTML.
+                       if ( $this->isVForm() ) {
+                               $field->setShowEmptyLabel( false );
+                       }
+
                        $setSection =& $loadedDescriptor;
                        if ( $section ) {
                                $sectionParts = explode( '/', $section );
@@ -272,6 +280,15 @@ class HTMLForm extends ContextSource {
                return $this->displayFormat;
        }
 
+       /**
+        * Test if displayFormat is 'vform'
+        * @since 1.22
+        * @return Bool
+        */
+       public function isVForm() {
+               return $this->displayFormat === 'vform';
+       }
+
        /**
         * Add the HTMLForm-specific JavaScript, if it hasn't been
         * done already.
@@ -626,6 +643,11 @@ class HTMLForm extends ContextSource {
                # For good measure (it is the default)
                $this->getOutput()->preventClickjacking();
                $this->getOutput()->addModules( 'mediawiki.htmlform' );
+               if ( $this->isVForm() ) {
+                       $this->getOutput()->addModuleStyles( 'mediawiki.ui' );
+                       // TODO should vertical form set setWrapperLegend( false )
+                       // to hide ugly fieldsets?
+               }
 
                $html = ''
                        . $this->getErrors( $submitResult )
@@ -660,13 +682,16 @@ class HTMLForm extends ContextSource {
                $attribs = array(
                        'action' => $this->getAction(),
                        'method' => $this->getMethod(),
-                       'class' => 'visualClear',
+                       'class' => array( 'visualClear' ),
                        'enctype' => $encType,
                );
                if ( !empty( $this->mId ) ) {
                        $attribs['id'] = $this->mId;
                }
 
+               if ( $this->isVForm() ) {
+                       array_push( $attribs['class'], 'mw-ui-vform', 'mw-ui-container' );
+               }
                return Html::rawElement( 'form', $attribs, $html );
        }
 
@@ -717,9 +742,22 @@ class HTMLForm extends ContextSource {
                                $attribs += Linker::tooltipAndAccesskeyAttribs( $this->mSubmitTooltip );
                        }
 
-                       $attribs['class'] = 'mw-htmlform-submit';
+                       $attribs['class'] = array( 'mw-htmlform-submit' );
+
+                       if ( $this->isVForm() ) {
+                               // mw-ui-block is necessary because the buttons aren't necessarily in an 
+                               // immediate child div of the vform.
+                               array_push( $attribs['class'], 'mw-ui-button', 'mw-ui-big', 'mw-ui-primary', 'mw-ui-block' );
+                       }
 
                        $html .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n";
+
+                       // Buttons are top-level form elements in table and div layouts,
+                       // but vform wants all elements inside divs to get spaced-out block
+                       // styling.
+                       if ( $this->isVForm() ) {
+                               $html = Html::rawElement( 'div', null, "\n$html\n" );
+                       }
                }
 
                if ( $this->mShowReset ) {
@@ -913,7 +951,8 @@ class HTMLForm extends ContextSource {
        /**
         * Prompt the whole form to be wrapped in a "<fieldset>", with
         * this text as its "<legend>" element.
-        * @param string $legend HTML to go inside the "<legend>" element.
+        * @param string|false $legend HTML to go inside the "<legend>" element, or
+        * false for no <legend>
         *       Will be escaped
         * @return HTMLForm $this for chaining calls (since 1.20)
         */
@@ -982,7 +1021,7 @@ class HTMLForm extends ContextSource {
 
        /**
         * @todo Document
-        * @param $fields array[]|HTMLFormField[] array of fields (either arrays or objects)
+        * @param array[]|HTMLFormField[] $fields array of fields (either arrays or objects)
         * @param string $sectionName ID attribute of the "<table>" tag for this section, ignored if empty
         * @param string $fieldsetIDPrefix ID prefix for the "<fieldset>" tag of each subsection, ignored if empty
         * @param boolean &$hasUserVisibleFields Whether the section had user-visible fields
@@ -995,7 +1034,17 @@ class HTMLForm extends ContextSource {
                $subsectionHtml = '';
                $hasLabel = false;
 
-               $getFieldHtmlMethod = ( $displayFormat == 'table' ) ? 'getTableRow' : 'get' . ucfirst( $displayFormat );
+               switch( $displayFormat ) {
+                       case 'table':
+                               $getFieldHtmlMethod = 'getTableRow';
+                               break;
+                       case 'vform':
+                               // Close enough to a div.
+                               $getFieldHtmlMethod = 'getDiv';
+                               break;
+                       default:
+                               $getFieldHtmlMethod = 'get' . ucfirst( $displayFormat );
+               }
 
                foreach ( $fields as $key => $value ) {
                        if ( $value instanceof HTMLFormField ) {
@@ -1061,7 +1110,7 @@ class HTMLForm extends ContextSource {
                        if ( $displayFormat === 'table' ) {
                                $html = Html::rawElement( 'table', $attribs,
                                        Html::rawElement( 'tbody', array(), "\n$html\n" ) ) . "\n";
-                       } elseif ( $displayFormat === 'div' ) {
+                       } elseif ( $displayFormat === 'div' || $displayFormat === 'vform' ) {
                                $html = Html::rawElement( 'div', $attribs, "\n$html\n" );
                        }
                }
@@ -1268,6 +1317,18 @@ abstract class HTMLFormField {
                return true;
        }
 
+       /**
+        * Tell the field whether to generate a separate label element if its label
+        * is blank.
+        *
+        * @since 1.22
+        * @param bool $show Set to false to not generate a label.
+        * @return void
+        */
+       public function setShowEmptyLabel( $show ) {
+               $this->mShowEmptyLabels = $show;
+       }
+
        /**
         * Get the value that this input has been set to from a posted form,
         * or the input's default value if it has not been set.
@@ -1431,8 +1492,12 @@ abstract class HTMLFormField {
                        array( 'class' => $outerDivClass ) + $cellAttributes,
                        $inputHtml . "\n$errors"
                );
+               $divCssClasses = array( "mw-htmlform-field-$fieldType", $this->mClass, $errorClass );
+               if ( $this->mParent->isVForm() ) {
+                       $divCssClasses[] = 'mw-ui-vform-div';
+               }
                $html = Html::rawElement( 'div',
-                       array( 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass" ),
+                       array( 'class' => $divCssClasses ),
                        $label . $field );
                $html .= $helptext;
                return $html;
@@ -1876,8 +1941,25 @@ class HTMLCheckField extends HTMLFormField {
                        $attr['class'] = $this->mClass;
                }
 
-               return Xml::check( $this->mName, $value, $attr ) . '&#160;' .
-                       Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel );
+               if ( $this->mParent->isVForm() ) {
+                       // Nest checkbox inside label.
+                       return Html::rawElement(
+                                       'label',
+                                       array(
+                                               'class' => 'mw-ui-checkbox-label'
+                                       ),
+                                       Xml::check(
+                                               $this->mName,
+                                               $value,
+                                               $attr
+                                       ) .
+                                       // Html:rawElement doesn't escape contents.
+                                       htmlspecialchars( $this->mLabel )
+                               );
+               } else {
+                       return Xml::check( $this->mName, $value, $attr ) . '&#160;' .
+                               Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel );
+               }
        }
 
        /**
@@ -1889,6 +1971,13 @@ class HTMLCheckField extends HTMLFormField {
                return '&#160;';
        }
 
+       /**
+        * checkboxes don't need a label.
+        */
+       protected function needsLabel() {
+               return false;
+       }
+
        /**
         * @param  $request WebRequest
         * @return String
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 3fea3e1..932f753 100644 (file)
@@ -422,6 +422,7 @@ class Html {
                $ret = '';
                $attribs = (array)$attribs;
                foreach ( $attribs as $key => $value ) {
+                       // Support intuitive array( 'checked' => true/false ) form
                        if ( $value === false || is_null( $value ) ) {
                                continue;
                        }
index fc76310..73834a5 100644 (file)
@@ -39,18 +39,11 @@ define( 'RE_IPV6_ADD',
                ':(?::|(?::' . RE_IPV6_WORD . '){1,7})' .
        '|' . // ends with "::" (except "::")
                RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){0,6}::' .
-       '|' . // contains one "::" in the middle, ending in "::WORD"
-               RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){0,5}' . '::' . RE_IPV6_WORD .
-       '|' . // contains one "::" in the middle, not ending in "::WORD" (regex for PCRE 4.0+)
-               RE_IPV6_WORD . '(?::(?P<abn>:(?P<iabn>))?' . RE_IPV6_WORD . '(?!:(?P=abn))){1,5}' .
-                       ':' . RE_IPV6_WORD . '(?P=iabn)' .
-               // NOTE: (?!(?P=abn)) fails iff "::" used twice; (?P=iabn) passes iff a "::" was found.
+       '|' . // contains one "::" in the middle (the ^ makes the test fail if none found)
+               RE_IPV6_WORD . '(?::((?(-1)|:))?' . RE_IPV6_WORD . '){1,6}(?(-2)|^)' .
        '|' . // contains no "::"
                RE_IPV6_WORD . '(?::' . RE_IPV6_WORD . '){7}' .
        ')'
-       // NOTE: With PCRE 7.2+, we can combine the two '"::" in the middle' cases into:
-       //              RE_IPV6_WORD . '(?::((?(-1)|:))?' . RE_IPV6_WORD . '){1,6}(?(-2)|^)'
-       // This also improves regex concatenation by using relative references.
 );
 // An IPv6 block is an IP address and a prefix (d1 to d128)
 define( 'RE_IPV6_BLOCK', RE_IPV6_ADD . '\/' . RE_IPV6_PREFIX );
index 24f1679..fdd0e3c 100644 (file)
@@ -43,6 +43,16 @@ class LinksUpdate extends SqlDataUpdate {
                $mOptions,       //!< SELECT options to be used (array)
                $mRecursive;     //!< Whether to queue jobs for recursive updates
 
+       /**
+        * @var null|array Added links if calculated.
+        */
+       private $linkInsertions = null;
+
+       /**
+        * @var null|array Deleted links if calculated.
+        */
+       private $linkDeletions = null;
+
        /**
         * Constructor
         *
@@ -112,14 +122,8 @@ class LinksUpdate extends SqlDataUpdate {
         * Update link tables with outgoing links from an updated article
         */
        public function doUpdate() {
-               global $wgUseDumbLinkUpdate;
-
                wfRunHooks( 'LinksUpdate', array( &$this ) );
-               if ( $wgUseDumbLinkUpdate ) {
-                       $this->doDumbUpdate();
-               } else {
-                       $this->doIncrementalUpdate();
-               }
+               $this->doIncrementalUpdate();
                wfRunHooks( 'LinksUpdateComplete', array( &$this ) );
        }
 
@@ -128,8 +132,9 @@ class LinksUpdate extends SqlDataUpdate {
 
                # Page links
                $existing = $this->getExistingLinks();
-               $this->incrTableUpdate( 'pagelinks', 'pl', $this->getLinkDeletions( $existing ),
-                       $this->getLinkInsertions( $existing ) );
+               $this->linkDeletions = $this->getLinkDeletions( $existing );
+               $this->linkInsertions = $this->getLinkInsertions( $existing );
+               $this->incrTableUpdate( 'pagelinks', 'pl', $this->linkDeletions, $this->linkInsertions );
 
                # Image links
                $existing = $this->getExistingImages();
@@ -197,46 +202,6 @@ class LinksUpdate extends SqlDataUpdate {
                wfProfileOut( __METHOD__ );
        }
 
-       /**
-        * Link update which clears the previous entries and inserts new ones
-        * May be slower or faster depending on level of lock contention and write speed of DB
-        * Also useful where link table corruption needs to be repaired, e.g. in refreshLinks.php
-        */
-       protected function doDumbUpdate() {
-               wfProfileIn( __METHOD__ );
-
-               # Refresh category pages and image description pages
-               $existing = $this->getExistingCategories();
-               $categoryInserts = array_diff_assoc( $this->mCategories, $existing );
-               $categoryDeletes = array_diff_assoc( $existing, $this->mCategories );
-               $categoryUpdates = $categoryInserts + $categoryDeletes;
-               $existing = $this->getExistingImages();
-               $imageUpdates = array_diff_key( $existing, $this->mImages ) + array_diff_key( $this->mImages, $existing );
-
-               $this->dumbTableUpdate( 'pagelinks', $this->getLinkInsertions(), 'pl_from' );
-               $this->dumbTableUpdate( 'imagelinks', $this->getImageInsertions(), 'il_from' );
-               $this->dumbTableUpdate( 'categorylinks', $this->getCategoryInsertions(), 'cl_from' );
-               $this->dumbTableUpdate( 'templatelinks', $this->getTemplateInsertions(), 'tl_from' );
-               $this->dumbTableUpdate( 'externallinks', $this->getExternalInsertions(), 'el_from' );
-               $this->dumbTableUpdate( 'langlinks', $this->getInterlangInsertions(), 'll_from' );
-               $this->dumbTableUpdate( 'iwlinks', $this->getInterwikiInsertions(), 'iwl_from' );
-               $this->dumbTableUpdate( 'page_props', $this->getPropertyInsertions(), 'pp_page' );
-
-               # Update the cache of all the category pages and image description
-               # pages which were changed, and fix the category table count
-               $this->invalidateCategories( $categoryUpdates );
-               $this->updateCategoryCounts( $categoryInserts, $categoryDeletes );
-               $this->invalidateImageDescriptions( $imageUpdates );
-
-               # Refresh links of all pages including this page
-               # This will be in a separate transaction
-               if ( $this->mRecursive ) {
-                       $this->queueRecursiveJobs();
-               }
-
-               wfProfileOut( __METHOD__ );
-       }
-
        /**
         * Queue recursive jobs for this page
         *
@@ -296,21 +261,6 @@ class LinksUpdate extends SqlDataUpdate {
                $this->invalidatePages( NS_FILE, array_keys( $images ) );
        }
 
-       /**
-        * @param $table
-        * @param $insertions
-        * @param $fromField
-        */
-       private function dumbTableUpdate( $table, $insertions, $fromField ) {
-               $this->mDb->delete( $table, array( $fromField => $this->mId ), __METHOD__ );
-               if ( count( $insertions ) ) {
-                       # The link array was constructed without FOR UPDATE, so there may
-                       # be collisions. This may cause minor link table inconsistencies,
-                       # which is better than crippling the site with lock contention.
-                       $this->mDb->insert( $table, $insertions, __METHOD__, array( 'IGNORE' ) );
-               }
-       }
-
        /**
         * Update a table by doing a delete query then an insert query
         * @param $table
@@ -821,6 +771,40 @@ class LinksUpdate extends SqlDataUpdate {
                        }
                }
        }
+
+       /**
+        * Fetch page links added by this LinksUpdate.  Only available after the update is complete.
+        * @since 1.22
+        * @return null|array of Titles
+        */
+       public function getAddedLinks() {
+               if ( $this->linkInsertions === null ) {
+                       return null;
+               }
+               $result = array();
+               foreach ( $this->linkInsertions as $insertion ) {
+                       $result[] = Title::makeTitle( $insertion[ 'pl_namespace' ], $insertion[ 'pl_title' ] );
+               }
+               return $result;
+       }
+
+       /**
+        * Fetch page links removed by this LinksUpdate.  Only available after the update is complete.
+        * @since 1.22
+        * @return null|array of Titles
+        */
+       public function getRemovedLinks() {
+               if ( $this->linkDeletions === null ) {
+                       return null;
+               }
+               $result = array();
+               foreach ( $this->linkDeletions as $ns => $titles ) {
+                       foreach ( $titles as $title => $unused ) {
+                               $result[] = Title::makeTitle( $ns, $title );
+                       }
+               }
+               return $result;
+       }
 }
 
 /**
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 );
                }
        }
 
diff --git a/includes/RecentChange.php b/includes/RecentChange.php
deleted file mode 100644 (file)
index 980bd0a..0000000
+++ /dev/null
@@ -1,846 +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_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 61630a9..a6195fc 100644 (file)
@@ -982,7 +982,13 @@ abstract class FormSpecialPage extends SpecialPage {
 
                $form = new HTMLForm( $this->fields, $this->getContext(), $this->getMessagePrefix() );
                $form->setSubmitCallback( array( $this, 'onSubmit' ) );
-               $form->setWrapperLegendMsg( $this->getMessagePrefix() . '-legend' );
+               // If the form is a compact vertical form, then don't output this ugly
+               // fieldset surrounding it.
+               // XXX Special pages can setDisplayFormat to 'vform' in alterForm(), but that
+               // is called after this.
+               if ( !$form->isVForm() ) {
+                       $form->setWrapperLegendMsg( $this->getMessagePrefix() . '-legend' );
+               }
 
                $headerMsg = $this->msg( $this->getMessagePrefix() . '-text' );
                if ( !$headerMsg->isDisabled() ) {
@@ -1227,7 +1233,7 @@ class SpecialListBots extends SpecialRedirectToSpecial {
  */
 class SpecialCreateAccount extends SpecialRedirectToSpecial {
        function __construct() {
-               parent::__construct( 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) );
+               parent::__construct( 'CreateAccount', 'Userlogin', 'signup', array( 'returnto', 'returntoquery', 'uselang' ) );
        }
 
        // No reason to hide this link on Special:Specialpages
index 6aa311e..a776706 100644 (file)
@@ -421,6 +421,9 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                $data['activeusers'] = intval( SiteStats::activeUsers() );
                $data['admins'] = intval( SiteStats::numberingroup( 'sysop' ) );
                $data['jobs'] = intval( SiteStats::jobs() );
+
+               wfRunHooks( 'APIQuerySiteInfoStatisticsInfo', array( &$data ) );
+
                return $this->getResult()->addValue( 'query', $property, $data );
        }
 
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 4b25bd3..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,
@@ -2301,8 +2343,7 @@ abstract class DatabaseBase implements IDatabase, DatabaseType {
        }
 
        /**
-        * If it's a string, adds quotes and backslashes
-        * Otherwise returns as-is
+        * Adds quotes and backslashes.
         *
         * @param $s string
         *
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 627defd..fe769be 100644 (file)
@@ -539,7 +539,7 @@ class LocalFile extends File {
                                'img_media_type' => $this->media_type,
                                'img_major_mime' => $major,
                                'img_minor_mime' => $minor,
-                               'img_metadata' => $this->metadata,
+                               'img_metadata' => $dbw->encodeBlob($this->metadata),
                                'img_sha1' => $this->sha1,
                        ),
                        array( 'img_name' => $this->getName() ),
@@ -1225,7 +1225,7 @@ class LocalFile extends File {
                                'img_description' => $comment,
                                'img_user' => $user->getId(),
                                'img_user_text' => $user->getName(),
-                               'img_metadata' => $this->metadata,
+                               'img_metadata' => $dbw->encodeBlob($this->metadata),
                                'img_sha1' => $this->sha1
                        ),
                        __METHOD__,
@@ -1276,7 +1276,7 @@ class LocalFile extends File {
                                        'img_description' => $comment,
                                        'img_user'        => $user->getId(),
                                        'img_user_text'   => $user->getName(),
-                                       'img_metadata'    => $this->metadata,
+                                       'img_metadata'    => $dbw->encodeBlob($this->metadata),
                                        'img_sha1'        => $this->sha1
                                ),
                                array( 'img_name' => $this->getName() ),
@@ -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 b3cd019..edf5ff2 100644 (file)
@@ -3446,6 +3446,10 @@ MediaWiki vyžaduje ke správné funkci podporu UTF-8.",
 To je pravděpodobně příliš málo.
 Instalace může selhat!",
        'config-ctype' => "'''Kritická chyba''': PHP musí být přeloženo s podporou pro [http://www.php.net/manual/en/ctype.installation.php rozšíření Ctype].",
+       'config-json' => "'''Kritická chyba:''' PHP bylo přeloženo bez podpory JSON.
+Před instalací MediaWiki musíte buď nainstalovat rozšíření PHP JSON nebo rozšíření [http://pecl.php.net/package/jsonc PECL jsonc].
+* Rozšíření PHP je součástí Red Hat Enterprise Linux (CentOS) 5 a 6, avšak musí se povolit v <code>/etc/php.ini</code> nebo <code>/etc/php.d/json.ini</code>.
+* V některých linuxových distribucích vydaných po květnu 2013 může toto rozšíření PHP chybět a místo toho mohou používat rozšíření PECL jako <code>php5-json</code> nebo <code>php-pecl-jsonc</code>.",
        'config-xcache' => 'Je nainstalována [http://xcache.lighttpd.net/ XCache]',
        'config-apc' => 'Je nainstalováno [http://www.php.net/apc APC]',
        'config-wincache' => 'Je nainstalována [http://www.iis.net/download/WinCacheForPhp WinCache]',
@@ -18479,8 +18483,13 @@ Vnesite ime dovoljenja ročno.',
        'config-download-localsettings' => 'Prenesi <code>LocalSettings.php</code>',
        'config-help' => 'pomoč',
        'mainpagetext' => "'''Programje MediaWiki je bilo uspešno nameščeno.'''",
-       'mainpagedocfooter' => 'Za uporabo in pomoč pri nastavitvi, prosimo, preglejte [//meta.wikimedia.org/wiki/MediaWiki_localisation dokumentacijo za prilagajanje vmesnika]
-in [//meta.wikimedia.org/wiki/MediaWiki_User%27s_Guide Uporabniški priročnik].', # Fuzzy
+       'mainpagedocfooter' => 'Oglejte si [//meta.wikimedia.org/wiki/Help:Contents Uporabniški priročnik] za informacije o uporabi programja wiki.
+
+== Kako začeti ==
+* [//www.mediawiki.org/wiki/Manual:Configuration_settings Seznam konfiguracijskih nastavitev]
+* [//www.mediawiki.org/wiki/Manual:FAQ Poogsto zastavljena vprašanja MediaWiki]
+* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Poštni seznam izdaj MediaWiki]
+* [//www.mediawiki.org/wiki/Localisation#Translation_resources Prevedite MediaWiki v svoj jezik]',
 );
 
 /** Lower Silesian (Schläsch)
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 d92d186..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,6 +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',      'archive',      'ar_id',                    'patch-archive-ar_id.sql' ),
+                       array( 'addField',      'externallinks',  'el_id',  'patch-externallinks-el_id.sql' ),
                );
        }
 
@@ -247,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 a4c9d74..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', '' ),
@@ -205,6 +208,8 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'changeField', 'templatelinks', 'tl_namespace',    'smallint', 'tl_namespace::smallint' ),
                        array( 'changeField', 'user_newtalk',  'user_ip',         'text',     'host(user_ip)' ),
                        array( 'changeField', 'uploadstash',   'us_image_bits',   'smallint', '' ),
+                       array( 'changeField', 'profiling',     'pf_time',         'float', '' ),
+                       array( 'changeField', 'profiling',     'pf_memory',       'float', '' ),
 
                        # null changes
                        array( 'changeNullableField', 'oldimage', 'oi_bits',       'NULL' ),
index df69c0e..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,6 +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' ),
+                       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 )
index 81390dc..19d019d 100644 (file)
@@ -178,12 +178,12 @@ class ResourceLoader {
 
                        // Save filtered text to Memcached
                        $cache->set( $key, $result );
-               } catch ( Exception $exception ) {
-                       $exception->logException();
-                       wfDebugLog( 'resourceloader', __METHOD__ . ": minification failed: $exception" );
+               } catch ( Exception $e ) {
+                       MWExceptionHandler::logException( $e );
+                       wfDebugLog( 'resourceloader', __METHOD__ . ": minification failed: $e" );
                        $this->hasErrors = true;
                        // Return exception as a comment
-                       $result = self::formatException( $exception );
+                       $result = self::formatException( $e );
                }
 
                wfProfileOut( __METHOD__ );
@@ -477,7 +477,7 @@ class ResourceLoader {
                try {
                        $this->preloadModuleInfo( array_keys( $modules ), $context );
                } catch ( Exception $e ) {
-                       $e->logException();
+                       MWExceptionHandler::logException( $e );
                        wfDebugLog( 'resourceloader', __METHOD__ . ": preloading module info failed: $e" );
                        $this->hasErrors = true;
                        // Add exception to the output as a comment
@@ -497,7 +497,7 @@ class ResourceLoader {
                                // Calculate maximum modified time
                                $mtime = max( $mtime, $module->getModifiedTime( $context ) );
                        } catch ( Exception $e ) {
-                               $e->logException();
+                               MWExceptionHandler::logException( $e );
                                wfDebugLog( 'resourceloader', __METHOD__ . ": calculating maximum modified time failed: $e" );
                                $this->hasErrors = true;
                                // Add exception to the output as a comment
@@ -724,7 +724,7 @@ class ResourceLoader {
                        try {
                                $blobs = MessageBlobStore::get( $this, $modules, $context->getLanguage() );
                        } catch ( Exception $e ) {
-                               $e->logException();
+                               MWExceptionHandler::logException( $e );
                                wfDebugLog( 'resourceloader', __METHOD__ . ": pre-fetching blobs from MessageBlobStore failed: $e" );
                                $this->hasErrors = true;
                                // Add exception to the output as a comment
@@ -832,7 +832,7 @@ class ResourceLoader {
                                                break;
                                }
                        } catch ( Exception $e ) {
-                               $e->logException();
+                               MWExceptionHandler::logException( $e );
                                wfDebugLog( 'resourceloader', __METHOD__ . ": generating module package failed: $e" );
                                $this->hasErrors = true;
                                // Add exception to the output as a comment
index c9c7e4e..9ed181e 100644 (file)
@@ -722,21 +722,17 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
        /**
         * Generate a cache key for a LESS file.
         *
-        * The cache key varies on the file name, the names and values of global
-        * LESS variables, and the value of $wgShowExceptionDetails. Varying on
-        * $wgShowExceptionDetails ensures the CSS comment indicating compilation
-        * failure shows the right level of detail.
+        * The cache key varies on the file name and the names and values of global
+        * LESS variables.
         *
         * @since 1.22
         * @param string $fileName File name of root LESS file.
         * @return string: Cache key
         */
        protected static function getLESSCacheKey( $fileName ) {
-               global $wgShowExceptionDetails;
-
                $vars = json_encode( ResourceLoader::getLESSVars() );
                $hash = md5( $fileName . $vars );
-               return wfMemcKey( 'resourceloader', 'less', (string)$wgShowExceptionDetails, $hash );
+               return wfMemcKey( 'resourceloader', 'less', $hash );
        }
 
        /**
@@ -753,8 +749,6 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
         * @return string: CSS source
         */
        protected function compileLESSFile( $fileName ) {
-               global $wgShowExceptionDetails;
-
                $key = self::getLESSCacheKey( $fileName );
                $cache = wfGetCache( CACHE_ANYTHING );
 
@@ -767,34 +761,16 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                }
 
                $compiler = ResourceLoader::getLessCompiler();
-               $expire = 0;
-               try {
-                       $result = $compiler->cachedCompile( $source );
-                       if ( !is_array( $result ) ) {
-                               throw new Exception( 'LESS compiler result has type ' . gettype( $result ) . '; array expected.' );
-                       }
-               } catch ( Exception $e ) {
-                       // The exception might have been caused by an imported file rather
-                       // than the root node. But we don't know which files were imported,
-                       // because compilation failed; we thus cannot rely on file mtime to
-                       // know when to reattempt compilation. Expire in 5 mins. instead.
-                       $expire = 300;
-                       wfDebugLog( 'resourceloader', __METHOD__ . ": $e" );
-                       $result = array();
-                       $result['root'] = $fileName;
-
-                       if ( $wgShowExceptionDetails ) {
-                               $result['compiled'] = ResourceLoader::makeComment( 'LESS error: ' . $e->getMessage() );
-                       } else {
-                               $result['compiled'] = ResourceLoader::makeComment( 'LESS stylesheet compilation failed. ' .
-                                       'Set "$wgShowExceptionDetails = true;" to show detailed debugging information.' );
-                       }
+               $result = null;
 
-                       $result['files'] = array( $fileName => self::safeFilemtime( $fileName ) );
-                       $result['updated'] = time();
+               $result = $compiler->cachedCompile( $source );
+
+               if ( !is_array( $result ) ) {
+                       throw new MWException( 'LESS compiler result has type ' . gettype( $result ) . '; array expected.' );
                }
+
                $this->localFileRefs += array_keys( $result['files'] );
-               $cache->set( $key, $result, $expire );
+               $cache->set( $key, $result );
                return $result['compiled'];
        }
 }
index 784ad04..f1992c0 100644 (file)
@@ -96,8 +96,9 @@ class SpecialBlockList extends SpecialPage {
                                'default' => 50,
                        ),
                );
-               $form = new HTMLForm( $fields, $this->getContext() );
-               $form->setTitle( $this->getTitle() ); // Remove subpage
+               $context = new DerivativeContext( $this->getContext() );
+               $context->setTitle( $this->getTitle() ); // Remove subpage
+               $form = new HTMLForm( $fields, $context );
                $form->setMethod( 'get' );
                $form->setWrapperLegendMsg( 'ipblocklist-legend' );
                $form->setSubmitTextMsg( 'ipblocklist-submit' );
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 b6005de..501552e 100644 (file)
@@ -547,8 +547,9 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                        $this->toc = false;
                }
 
-               $form = new EditWatchlistNormalHTMLForm( $fields, $this->getContext() );
-               $form->setTitle( $this->getTitle() );
+               $context = new DerivativeContext( $this->getContext() );
+               $context->setTitle( $this->getTitle() ); // Remove subpage
+               $form = new EditWatchlistNormalHTMLForm( $fields, $context );
                $form->setSubmitTextMsg( 'watchlistedit-normal-submit' );
                # Used message keys: 'accesskey-watchlistedit-normal-submit', 'tooltip-watchlistedit-normal-submit'
                $form->setSubmitTooltip( 'watchlistedit-normal-submit' );
@@ -610,8 +611,9 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                                'default' => $titles,
                        ),
                );
-               $form = new HTMLForm( $fields, $this->getContext() );
-               $form->setTitle( $this->getTitle( 'raw' ) );
+               $context = new DerivativeContext( $this->getContext() );
+               $context->setTitle( $this->getTitle( 'raw' ) ); // Reset subpage
+               $form = new HTMLForm( $fields, $context );
                $form->setSubmitTextMsg( 'watchlistedit-raw-submit' );
                # Used message keys: 'accesskey-watchlistedit-raw-submit', 'tooltip-watchlistedit-raw-submit'
                $form->setSubmitTooltip( 'watchlistedit-raw-submit' );
index 27188c3..2e90d99 100644 (file)
@@ -148,11 +148,12 @@ class SpecialEmailUser extends UnlistedSpecialPage {
 
                $this->mTargetObj = $ret;
 
-               $form = new HTMLForm( $this->getFormFields(), $this->getContext() );
+               $context = new DerivativeContext( $this->getContext() );
+               $context->setTitle( $this->getTitle() ); // Remove subpage
+               $form = new HTMLForm( $this->getFormFields(), $context );
                // By now we are supposed to be sure that $this->mTarget is a user name
                $form->addPreText( $this->msg( 'emailpagetext', $this->mTarget )->parse() );
                $form->setSubmitTextMsg( 'emailsend' );
-               $form->setTitle( $this->getTitle() );
                $form->setSubmitCallback( array( __CLASS__, 'uiSubmit' ) );
                $form->setWrapperLegendMsg( 'email-legend' );
                $form->loadData();
index 814e213..37d2973 100644 (file)
@@ -181,8 +181,9 @@ class NewFilesPager extends ReverseChronologicalPager {
                        unset( $fields['like'] );
                }
 
-               $form = new HTMLForm( $fields, $this->getContext() );
-               $form->setTitle( $this->getTitle() );
+               $context = new DerivativeContext( $this->getContext() );
+               $context->setTitle( $this->getTitle() ); // Remove subpage
+               $form = new HTMLForm( $fields, $context );
                $form->setSubmitTextMsg( 'ilsubmit' );
                $form->setMethod( 'get' );
                $form->setWrapperLegendMsg( 'newimages-legend' );
index 69c4056..c486ba0 100644 (file)
@@ -105,6 +105,13 @@ class SpecialPasswordReset extends FormSpecialPage {
        public function alterForm( HTMLForm $form ) {
                global $wgPasswordResetRoutes;
 
+               $form->setDisplayFormat( 'vform' );
+               // Turn the old-school line around the form off.
+               // XXX This wouldn't be necessary here if we could set the format of
+               // the HTMLForm to 'vform' at its creation, but there's no way to do so
+               // from a FormSpecialPage class.
+               $form->setWrapperLegend( false );
+
                $i = 0;
                if ( isset( $wgPasswordResetRoutes['username'] ) && $wgPasswordResetRoutes['username'] ) {
                        $i++;
index cc7b8fa..ce7a45b 100644 (file)
@@ -75,10 +75,11 @@ class SpecialPreferences extends SpecialPage {
 
                $this->getOutput()->addWikiMsg( 'prefs-reset-intro' );
 
-               $htmlForm = new HTMLForm( array(), $this->getContext(), 'prefs-restore' );
+               $context = new DerivativeContext( $this->getContext() );
+               $context->setTitle( $this->getTitle( 'reset' ) ); // Reset subpage
+               $htmlForm = new HTMLForm( array(), $context, 'prefs-restore' );
 
                $htmlForm->setSubmitTextMsg( 'restoreprefs' );
-               $htmlForm->setTitle( $this->getTitle( 'reset' ) );
                $htmlForm->setSubmitCallback( array( $this, 'submitReset' ) );
                $htmlForm->suppressReset();
 
index 60cdb0e..a42a217 100644 (file)
@@ -41,16 +41,17 @@ class SpecialRecentChanges extends IncludableSpecialPage {
         */
        public function getDefaultOptions() {
                $opts = new FormOptions();
+               $user = $this->getUser();
 
-               $opts->add( 'days', $this->getUser()->getIntOption( 'rcdays' ) );
-               $opts->add( 'limit', $this->getUser()->getIntOption( 'rclimit' ) );
+               $opts->add( 'days', $user->getIntOption( 'rcdays' ) );
+               $opts->add( 'limit', $user->getIntOption( 'rclimit' ) );
                $opts->add( 'from', '' );
 
-               $opts->add( 'hideminor', $this->getUser()->getBoolOption( 'hideminor' ) );
+               $opts->add( 'hideminor', $user->getBoolOption( 'hideminor' ) );
                $opts->add( 'hidebots', true );
                $opts->add( 'hideanons', false );
                $opts->add( 'hideliu', false );
-               $opts->add( 'hidepatrolled', $this->getUser()->getBoolOption( 'hidepatrolled' ) );
+               $opts->add( 'hidepatrolled', $user->getBoolOption( 'hidepatrolled' ) );
                $opts->add( 'hidemyself', false );
 
                $opts->add( 'namespace', '', FormOptions::INTNULL );
@@ -154,7 +155,7 @@ class SpecialRecentChanges extends IncludableSpecialPage {
                $opts = $this->getOptions();
                $this->setHeaders();
                $this->outputHeader();
-               $this->addRecentChangesJS();
+               $this->addModules();
 
                // Fetch results, prepare a batch link existence check query
                $conds = $this->buildMainQueryConds( $opts );
@@ -911,9 +912,9 @@ class SpecialRecentChanges extends IncludableSpecialPage {
        }
 
        /**
-        * Add JavaScript to the page
+        * Add page-specific modules.
         */
-       function addRecentChangesJS() {
+       protected function addModules() {
                $this->getOutput()->addModules( array(
                        'mediawiki.special.recentchanges',
                ) );
index 7e34e98..077e7cb 100644 (file)
  * @ingroup SpecialPage
  */
 class SpecialTags extends SpecialPage {
+       /**
+        * @var array List of defined tags
+        */
+       public $definedTags;
 
        function __construct() {
                parent::__construct( 'Tags' );
@@ -55,7 +59,11 @@ class SpecialTags extends SpecialPage {
                        $html .= $this->doTagRow( $tag, $hitcount );
                }
 
-               $out->addHTML( Xml::tags( 'table', array( 'class' => 'wikitable sortable mw-tags-table' ), $html ) );
+               $out->addHTML( Xml::tags(
+                       'table',
+                       array( 'class' => 'wikitable sortable mw-tags-table' ),
+                       $html
+               ) );
        }
 
        function doTagRow( $tag, $hitcount ) {
@@ -66,7 +74,10 @@ class SpecialTags extends SpecialPage {
                $disp = ChangeTags::tagDescription( $tag );
                if ( $user->isAllowed( 'editinterface' ) ) {
                        $disp .= ' ';
-                       $editLink = Linker::link( Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag" ), $this->msg( 'tags-edit' )->escaped() );
+                       $editLink = Linker::link(
+                               Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag" ),
+                               $this->msg( 'tags-edit' )->escaped()
+                       );
                        $disp .= $this->msg( 'parentheses' )->rawParams( $editLink )->escaped();
                }
                $newRow .= Xml::tags( 'td', null, $disp );
@@ -75,16 +86,26 @@ class SpecialTags extends SpecialPage {
                $desc = !$msg->exists() ? '' : $msg->parse();
                if ( $user->isAllowed( 'editinterface' ) ) {
                        $desc .= ' ';
-                       $editDescLink = Linker::link( Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag-description" ), $this->msg( 'tags-edit' )->escaped() );
+                       $editDescLink = Linker::link(
+                               Title::makeTitle( NS_MEDIAWIKI, "Tag-$tag-description" ),
+                               $this->msg( 'tags-edit' )->escaped()
+                       );
                        $desc .= $this->msg( 'parentheses' )->rawParams( $editDescLink )->escaped();
                }
                $newRow .= Xml::tags( 'td', null, $desc );
 
-               $active = $this->msg( isset( $this->definedTags[$tag] ) ? 'tags-active-yes' : 'tags-active-no' )->escaped();
+               $active = isset( $this->definedTags[$tag] ) ? 'tags-active-yes' : 'tags-active-no';
+               $active = $this->msg( $active )->escaped();
                $newRow .= Xml::tags( 'td', null, $active );
 
                $hitcountLabel = $this->msg( 'tags-hitcount' )->numParams( $hitcount )->escaped();
-               $hitcountLink = Linker::link( SpecialPage::getTitleFor( 'Recentchanges' ), $hitcountLabel, array(), array( 'tagfilter' => $tag ) );
+               $hitcountLink = Linker::link(
+                       SpecialPage::getTitleFor( 'Recentchanges' ),
+                       $hitcountLabel,
+                       array(),
+                       array( 'tagfilter' => $tag )
+               );
+
                // add raw $hitcount for sorting, because tags-hitcount contains numbers and letters
                $newRow .= Xml::tags( 'td', array( 'data-sort-value' => $hitcount ), $hitcountLink );
 
index 51a0a86..09facf4 100644 (file)
@@ -221,6 +221,8 @@ class SpecialUpload extends SpecialPage {
         */
        protected function getUploadForm( $message = '', $sessionKey = '', $hideIgnoreWarning = false ) {
                # Initialize form
+               $context = new DerivativeContext( $this->getContext() );
+               $context->setTitle( $this->getTitle() ); // Remove subpage
                $form = new UploadForm( array(
                        'watch' => $this->getWatchCheck(),
                        'forreupload' => $this->mForReUpload,
@@ -232,8 +234,7 @@ class SpecialUpload extends SpecialPage {
                        'texttop' => $this->uploadFormTextTop,
                        'textaftersummary' => $this->uploadFormTextAfterSummary,
                        'destfile' => $this->mDesiredDestName,
-               ), $this->getContext() );
-               $form->setTitle( $this->getTitle() );
+               ), $context );
 
                # Check the token, but only if necessary
                if (
index 002e949..87b6442 100644 (file)
@@ -343,15 +343,16 @@ class SpecialUploadStash extends UnlistedSpecialPage {
                // create the form, which will also be used to execute a callback to process incoming form data
                // this design is extremely dubious, but supposedly HTMLForm is our standard now?
 
+               $context = new DerivativeContext( $this->getContext() );
+               $context->setTitle( $this->getTitle() ); // Remove subpage
                $form = new HTMLForm( array(
                        'Clear' => array(
                                'type' => 'hidden',
                                'default' => true,
                                'name' => 'clear',
                        )
-               ), $this->getContext(), 'clearStashedUploads' );
+               ), $context, 'clearStashedUploads' );
                $form->setSubmitCallback( array( __CLASS__, 'tryClearStashedUploads' ) );
-               $form->setTitle( $this->getTitle() );
                $form->setSubmitTextMsg( 'uploadstash-clear' );
 
                $form->prepareForm();
index 2b60ca2..90c4c35 100644 (file)
@@ -173,7 +173,8 @@ class LoginForm extends SpecialPage {
                        $query = array(
                                'returnto' => $this->mReturnTo,
                                'returntoquery' => $this->mReturnToQuery,
-                       );
+                               'title' => null,
+                       ) + $this->mRequest->getQueryValues();
                        $url = $title->getFullURL( $query, false, PROTO_HTTPS );
                        if ( $wgSecureLogin && wfCanIPUseHTTPS( $this->getRequest()->getIP() ) ) {
                                $url = wfAppendQuery( $url, 'fromhttp=1' );
index 59f0dfe..11632a3 100644 (file)
@@ -100,7 +100,7 @@ class SpecialWatchlist extends SpecialPage {
 
                // @todo use FormOptions!
                $defaults = array(
-               /* float */ 'days' => floatval( $user->getOption( 'watchlistdays' ) ), /* 3.0 or 0.5, watch further below */
+               /* float */ 'days' => floatval( $user->getOption( 'watchlistdays' ) ),
                /* bool  */ 'hideMinor' => (int)$user->getBoolOption( 'watchlisthideminor' ),
                /* bool  */ 'hideBots' => (int)$user->getBoolOption( 'watchlisthidebots' ),
                /* bool  */ 'hideAnons' => (int)$user->getBoolOption( 'watchlisthideanons' ),
@@ -121,7 +121,7 @@ class SpecialWatchlist extends SpecialPage {
                # Extract variables from the request, falling back to user preferences or
                # other default values if these don't exist
                $values = array();
-               $values['days'] = $request->getVal( 'days', $defaults['days'] );
+               $values['days'] = floatval( $request->getVal( 'days', $defaults['days'] ) );
                $values['hideMinor'] = (int)$request->getBool( 'hideMinor', $defaults['hideMinor'] );
                $values['hideBots'] = (int)$request->getBool( 'hideBots', $defaults['hideBots'] );
                $values['hideAnons'] = (int)$request->getBool( 'hideAnons', $defaults['hideAnons'] );
@@ -158,18 +158,6 @@ class SpecialWatchlist extends SpecialPage {
                $values['invert'] = $invert;
                $values['associated'] = $associated;
 
-               if ( is_null( $values['days'] ) || !is_numeric( $values['days'] ) ) {
-                       $big = 1000; /* The magical big */
-                       if ( $nitems > $big ) {
-                               # Set default cutoff shorter
-                               $values['days'] = $defaults['days'] = ( 12.0 / 24.0 ); # 12 hours...
-                       } else {
-                               $values['days'] = $defaults['days']; # default cutoff for shortlisters
-                       }
-               } else {
-                       $values['days'] = floatval( $values['days'] );
-               }
-
                // Dump everything here
                $nondefaults = array();
                foreach ( $defaults as $name => $defValue ) {
@@ -191,14 +179,6 @@ class SpecialWatchlist extends SpecialPage {
                        $conds[] = 'rc_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( time() - intval( $values['days'] * 86400 ) ) );
                }
 
-               # If the watchlist is relatively short, it's simplest to zip
-               # down its entirety and then sort the results.
-
-               # If it's relatively long, it may be worth our while to zip
-               # through the time-sorted page list checking for watched items.
-
-               # Up estimate of watched items by 15% to compensate for talk pages...
-
                # Toggles
                if ( $values['hideOwn'] ) {
                        $conds[] = 'rc_user != ' . $user->getId();
index 50dfaec..7a4b9f2 100644 (file)
  * @ingroup Templates
  */
 
-if ( !defined( 'MEDIAWIKI' ) ) {
-       die( -1 );
-}
-
 class UsercreateTemplate extends BaseTemplate {
 
        /**
@@ -46,245 +42,242 @@ class UsercreateTemplate extends BaseTemplate {
                $expirationDays = ceil( $wgCookieExpiration / ( 3600 * 24 ) );
 ?>
 <div class="mw-ui-container">
-       <?php
-       if ( $this->haveData( 'languages' ) ) {
-       ?>
+       <?php if ( $this->haveData( 'languages' ) ) { ?>
                <div id="languagelinks">
                        <p><?php $this->html( 'languages' ); ?></p>
                </div>
-       <?php
-       }
-       ?>
-<div id="userloginForm">
-<h2 class="createaccount-join">
-       <?php
-       $this->msg( $this->data['loggedin'] ?
-               'createacct-another-join' : 'createacct-join' );
-       ?>
-</h2>
-<form name="userlogin2" id="userlogin2" class="mw-ui-vform" method="post" action="<?php $this->text( 'action' ); ?>">
-       <section class="mw-form-header">
-               <?php $this->html( 'header' ); /* extensions such as ConfirmEdit add form HTML here */ ?>
-       </section>
-       <?php
-       if ( $this->data['message'] ) {
-?>
-               <div class="<?php $this->text( 'messagetype' ); ?>box">
-               <?php if ( $this->data['messagetype'] == 'error' ) { ?>
-                       <strong><?php $this->msg( 'createacct-error' ); ?></strong><br />
-               <?php } ?>
-               <?php $this->html( 'message' ); ?>
-               </div>
        <?php } ?>
-               <div>
-                       <label for='wpName2'>
-                               <?php $this->msg( 'userlogin-yourname' ); ?>
+       <div id="userloginForm">
+               <h2 class="createaccount-join">
+                       <?php $this->msg( $this->data['loggedin'] ? 'createacct-another-join' : 'createacct-join' ); ?>
+               </h2>
+               <form name="userlogin2" id="userlogin2" class="mw-ui-vform" method="post" action="<?php $this->text( 'action' ); ?>">
+                       <section class="mw-form-header">
+                               <?php $this->html( 'header' ); /* extensions such as ConfirmEdit add form HTML here */ ?>
+                       </section>
+                       <?php if ( $this->data['message'] ) { ?>
+                               <div class="<?php $this->text( 'messagetype' ); ?>box">
+                                       <?php if ( $this->data['messagetype'] == 'error' ) { ?>
+                                               <strong><?php $this->msg( 'createacct-error' ); ?></strong>
+                                               <br />
+                                       <?php } ?>
+                                       <?php $this->html( 'message' ); ?>
+                               </div>
+                       <?php } ?>
 
-                               <span class="mw-ui-flush-right"><?php echo $this->getMsg( 'createacct-helpusername' )->parse(); ?></span>
-                       </label>
-                       <?php
-                       echo Html::input( 'wpName', $this->data['name'], 'text', array(
-                               'class' => 'mw-input loginText',
-                               'id' => 'wpName2',
-                               'tabindex' => '1',
-                               'size' => '20',
-                               'required',
-                               'placeholder' => $this->getMsg( $this->data['loggedin'] ?
-                                       'createacct-another-username-ph' : 'userlogin-yourname-ph' )->text(),
-                       ) );
-                       ?>
-               </div>
-               <div>
-               <?php if ( $this->data['createemail'] ) { ?>
-                       <label class="mw-ui-checkbox-label">
-                               <input name="wpCreateaccountMail" type="checkbox" value="1" id="wpCreateaccountMail" tabindex="2"
-                                       <?php if ( $this->data['createemailset'] ) {
-                                               echo 'checked="checked"';
-                                       } ?>
-                               >
-                               <?php $this->msg( 'createaccountmail' ); ?>
-                       </label>
-               <?php } ?>
-               </div>
-               <div class="mw-row-password">
-                       <label for='wpPassword2'><?php $this->msg( 'userlogin-yourpassword' ); ?></label>
-                       <?php echo Html::input( 'wpPassword', null, 'password', array(
-                               'class' => 'mw-input loginPassword',
-                               'id' => 'wpPassword2',
-                               'tabindex' => '3',
-                               'size' => '20',
-                               'required',
-                               'placeholder' => $this->getMsg( 'createacct-yourpassword-ph' )->text()
-                       ) + User::passwordChangeInputAttribs() ); ?>
-               </div>
-       <?php if ( $this->data['usedomain'] ) {
-               $doms = "";
-               foreach ( $this->data['domainnames'] as $dom ) {
-                       $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
-               }
-       ?>
-               <div id="mw-user-domain-section">
-                       <label for="wpDomain"><?php $this->msg( 'yourdomainname' ); ?></label>
-                       <div class="mw-input">
-                               <select name="wpDomain" value="<?php $this->text( 'domain' ); ?>"
-                                       tabindex="4">
-                                       <?php echo $doms ?>
-                               </select>
-                       </div>
-               </div>
-       <?php } ?>
-               <div class="mw-row-password">
-                       <label for='wpRetype'><?php $this->msg( 'createacct-yourpasswordagain' ); ?></label>
-                       <?php
-                       echo Html::input( 'wpRetype', null, 'password', array(
-                               'class' => 'mw-input loginPassword',
-                               'id' => 'wpRetype',
-                               'tabindex' => '5',
-                               'size' => '20',
-                               'required',
-                               'placeholder' => $this->getMsg( 'createacct-yourpasswordagain-ph' )->text()
-                               ) + User::passwordChangeInputAttribs() );
-                       ?>
-               </div>
-               <div>
-               <?php if ( $this->data['useemail'] ) { ?>
-                       <label for='wpEmail'>
+                       <div>
+                               <label for='wpName2'>
+                                       <?php $this->msg( 'userlogin-yourname' ); ?>
+
+                                       <span class="mw-ui-flush-right"><?php echo $this->getMsg( 'createacct-helpusername' )->parse(); ?></span>
+                               </label>
                                <?php
-                                       $this->msg( $this->data['emailrequired'] ?
-                                               'createacct-emailrequired' :
-                                               'createacct-emailoptional'
-                                       );
-                               ?>
-                       </label>
-                       <?php
-                               echo Html::input( 'wpEmail', $this->data['email'], 'email', array(
+                               echo Html::input( 'wpName', $this->data['name'], 'text', array(
                                        'class' => 'mw-input loginText',
-                                       'id' => 'wpEmail',
-                                       'tabindex' => '6',
+                                       'id' => 'wpName2',
+                                       'tabindex' => '1',
                                        'size' => '20',
+                                       'required',
                                        'placeholder' => $this->getMsg( $this->data['loggedin'] ?
-                                               'createacct-another-email-ph' : 'createacct-email-ph' )->text()
-                               ) + ( $this->data['emailrequired'] ? array() : array( 'required' => '' ) ) );
-                       ?>
-               <?php } ?>
-               </div>
-               <?php if ( $this->data['userealname'] ) { ?>
-                       <div>
-                               <label for='wpRealName'><?php $this->msg( 'createacct-realname' ); ?></label>
-                               <input type='text' class='mw-input loginText' name="wpRealName" id="wpRealName"
-                                       tabindex="7"
-                                       value="<?php $this->text( 'realname' ); ?>" size='20' />
-                               <div class="prefsectiontip">
-                                       <?php $this->msgWiki( $this->data['loggedin'] ? 'createacct-another-realname-tip' : 'prefs-help-realname' ); ?>
-                               </div>
-                       </div>
-               <?php }
-               if ( $this->data['usereason'] ) { ?>
-                       <div>
-                               <label for='wpReason'><?php $this->msg( 'createacct-reason' ); ?></label>
-                               <?php echo Html::input( 'wpReason', $this->data['reason'], 'text', array(
-                                       'class' => 'mw-input loginText',
-                                       'id' => 'wpReason',
-                                       'tabindex' => '8',
-                                       'size' => '20',
-                                       'placeholder' => $this->getMsg( 'createacct-reason-ph' )->text()
-                               ) ); ?>
+                                               'createacct-another-username-ph' : 'userlogin-yourname-ph' )->text(),
+                               ) );
+                               ?>
                        </div>
-               <?php }
-               $tabIndex = 9;
-               if ( isset( $this->data['extraInput'] ) && is_array( $this->data['extraInput'] ) ) {
-                       foreach ( $this->data['extraInput'] as $inputItem ) { ?>
+
                        <div>
-                               <?php
-                               // If it's a checkbox, output the whole thing (assume it has a msg).
-                               if ( $inputItem['type'] == 'checkbox' ) {
-                               ?>
+                               <?php if ( $this->data['createemail'] ) { ?>
                                        <label class="mw-ui-checkbox-label">
-                                               <input
-                                                       name="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
-                                                       id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
-                                                       type="checkbox" value="1"
-                                                       tabindex="<?php echo $tabIndex++; ?>"
-                                                       <?php if ( !empty( $inputItem['value'] ) ) {
+                                               <input name="wpCreateaccountMail" type="checkbox" value="1" id="wpCreateaccountMail" tabindex="2"
+                                                       <?php if ( $this->data['createemailset'] ) {
                                                                echo 'checked="checked"';
                                                        } ?>
                                                >
-                                               <?php $this->msg( $inputItem['msg'] ); ?>
+                                               <?php $this->msg( 'createaccountmail' ); ?>
                                        </label>
+                               <?php } ?>
+                       </div>
+
+                       <div class="mw-row-password">
+                               <label for='wpPassword2'><?php $this->msg( 'userlogin-yourpassword' ); ?></label>
                                <?php
-                               } else {
-                                       // Not a checkbox.
-                                       // TODO (bug 31909) support other input types, e.g. select boxes.
-                                       if ( !empty( $inputItem['msg'] ) ) {
-                                               // Output the message label
-                                       ?>
-                                               <label for="<?php echo htmlspecialchars( $inputItem['name'] ); ?>">
-                                                       <?php $this->msgWiki( $inputItem['msg'] ); ?>
-                                               </label>
-                                       <?php
-                                       }
-                                       ?>
-                                       <input
-                                               type="<?php echo htmlspecialchars( $inputItem['type'] ); ?>"
-                                               class="mw-input"
-                                               name="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
-                                               tabindex="<?php echo $tabIndex++; ?>"
-                                               value="<?php echo htmlspecialchars( $inputItem['value'] ); ?>"
-                                               id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
-                                       />
-                               <?php
-                               }
-                               if ( $inputItem['helptext'] !== false ) {
+                               echo Html::input( 'wpPassword', null, 'password', array(
+                                       'class' => 'mw-input loginPassword',
+                                       'id' => 'wpPassword2',
+                                       'tabindex' => '3',
+                                       'size' => '20',
+                                       'required',
+                                       'placeholder' => $this->getMsg( 'createacct-yourpassword-ph' )->text()
+                               ) + User::passwordChangeInputAttribs() );
                                ?>
-                                       <div class="prefsectiontip">
-                                               <?php $this->msgWiki( $inputItem['helptext'] ); ?>
+                       </div>
+
+                       <?php
+                       if ( $this->data['usedomain'] ) {
+                               $doms = "";
+                               foreach ( $this->data['domainnames'] as $dom ) {
+                                       $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
+                               }
+                       ?>
+                               <div id="mw-user-domain-section">
+                                       <label for="wpDomain"><?php $this->msg( 'yourdomainname' ); ?></label>
+                                       <div class="mw-input">
+                                               <select name="wpDomain" value="<?php $this->text( 'domain' ); ?>" tabindex="4">
+                                                       <?php echo $doms ?>
+                                               </select>
                                        </div>
+                               </div>
+                       <?php } ?>
+
+                       <div class="mw-row-password">
+                               <label for='wpRetype'><?php $this->msg( 'createacct-yourpasswordagain' ); ?></label>
                                <?php
-                               }
+                               echo Html::input( 'wpRetype', null, 'password', array(
+                                       'class' => 'mw-input loginPassword',
+                                       'id' => 'wpRetype',
+                                       'tabindex' => '5',
+                                       'size' => '20',
+                                       'required',
+                                       'placeholder' => $this->getMsg( 'createacct-yourpasswordagain-ph' )->text()
+                                       ) + User::passwordChangeInputAttribs() );
                                ?>
+                       </div>
+
+                       <div>
+                               <?php if ( $this->data['useemail'] ) { ?>
+                                       <label for='wpEmail'>
+                                               <?php
+                                                       $this->msg( $this->data['emailrequired'] ?
+                                                               'createacct-emailrequired' :
+                                                               'createacct-emailoptional'
+                                                       );
+                                               ?>
+                                       </label>
+                                       <?php
+                                               echo Html::input( 'wpEmail', $this->data['email'], 'email', array(
+                                                       'class' => 'mw-input loginText',
+                                                       'id' => 'wpEmail',
+                                                       'tabindex' => '6',
+                                                       'size' => '20',
+                                                       'placeholder' => $this->getMsg( $this->data['loggedin'] ?
+                                                               'createacct-another-email-ph' : 'createacct-email-ph' )->text()
+                                               ) + ( $this->data['emailrequired'] ? array() : array( 'required' => '' ) ) );
+                                       ?>
+                               <?php } ?>
+                       </div>
+
+                       <?php if ( $this->data['userealname'] ) { ?>
+                               <div>
+                                       <label for='wpRealName'><?php $this->msg( 'createacct-realname' ); ?></label>
+                                       <input type='text' class='mw-input loginText' name="wpRealName" id="wpRealName"
+                                               tabindex="7"
+                                               value="<?php $this->text( 'realname' ); ?>" size='20' />
+                                       <div class="prefsectiontip">
+                                               <?php $this->msgWiki( $this->data['loggedin'] ? 'createacct-another-realname-tip' : 'prefs-help-realname' ); ?>
+                                       </div>
                                </div>
+                       <?php } ?>
+
+                       <?php if ( $this->data['usereason'] ) { ?>
+                               <div>
+                                       <label for='wpReason'><?php $this->msg( 'createacct-reason' ); ?></label>
+                                       <?php echo Html::input( 'wpReason', $this->data['reason'], 'text', array(
+                                               'class' => 'mw-input loginText',
+                                               'id' => 'wpReason',
+                                               'tabindex' => '8',
+                                               'size' => '20',
+                                               'placeholder' => $this->getMsg( 'createacct-reason-ph' )->text()
+                                       ) ); ?>
+                               </div>
+                       <?php } ?>
+
                        <?php
+                       $tabIndex = 9;
+                       if ( isset( $this->data['extraInput'] ) && is_array( $this->data['extraInput'] ) ) {
+                               foreach ( $this->data['extraInput'] as $inputItem ) { ?>
+                                       <div>
+                                               <?php
+                                               // If it's a checkbox, output the whole thing (assume it has a msg).
+                                               if ( $inputItem['type'] == 'checkbox' ) {
+                                               ?>
+                                                       <label class="mw-ui-checkbox-label">
+                                                               <input
+                                                                       name="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
+                                                                       id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
+                                                                       type="checkbox" value="1"
+                                                                       tabindex="<?php echo $tabIndex++; ?>"
+                                                                       <?php if ( !empty( $inputItem['value'] ) ) {
+                                                                               echo 'checked="checked"';
+                                                                       } ?>
+                                                               >
+                                                               <?php $this->msg( $inputItem['msg'] ); ?>
+                                                       </label>
+                                               <?php
+                                               } else {
+                                                       // Not a checkbox.
+                                                       // TODO (bug 31909) support other input types, e.g. select boxes.
+                                               ?>
+                                                       <?php if ( !empty( $inputItem['msg'] ) ) { ?>
+                                                               <label for="<?php echo htmlspecialchars( $inputItem['name'] ); ?>">
+                                                                       <?php $this->msgWiki( $inputItem['msg'] ); ?>
+                                                               </label>
+                                                       <?php } ?>
+                                                       <input
+                                                               type="<?php echo htmlspecialchars( $inputItem['type'] ); ?>"
+                                                               class="mw-input"
+                                                               name="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
+                                                               tabindex="<?php echo $tabIndex++; ?>"
+                                                               value="<?php echo htmlspecialchars( $inputItem['value'] ); ?>"
+                                                               id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
+                                                       />
+                                               <?php } ?>
+                                               <?php if ( $inputItem['helptext'] !== false ) { ?>
+                                                       <div class="prefsectiontip">
+                                                               <?php $this->msgWiki( $inputItem['helptext'] ); ?>
+                                                       </div>
+                                               <?php } ?>
+                                       </div>
+                               <?php
+                               }
                        }
-               }
-               // JS attempts to move the image CAPTCHA below this part of the form,
-               // so skip one index.
-               $tabIndex++;
-               ?>
-               <div class="mw-submit">
+
+                       // JS attempts to move the image CAPTCHA below this part of the form,
+                       // so skip one index.
+                       $tabIndex++;
+                       ?>
+                       <div class="mw-submit">
+                               <?php
+                               echo Html::input(
+                                       'wpCreateaccount',
+                                       $this->getMsg( $this->data['loggedin'] ? 'createacct-another-submit' : 'createacct-submit' ),
+                                       'submit',
+                                       array(
+                                               'class' => "mw-ui-button mw-ui-big mw-ui-block mw-ui-primary",
+                                               'id' => 'wpCreateaccount',
+                                               'tabindex' => $tabIndex++
+                                       )
+                               );
+                               ?>
+                       </div>
+                       <?php if ( $this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
+                       <?php if ( $this->haveData( 'token' ) ) { ?><input type="hidden" name="wpCreateaccountToken" value="<?php $this->text( 'token' ); ?>" /><?php } ?>
+               </form>
+       </div>
+       <div class="mw-createacct-benefits-container">
+               <h2><?php $this->msg( 'createacct-benefit-heading' ); ?></h2>
+               <div class="mw-createacct-benefits-list">
                        <?php
-                       echo Html::input( 'wpCreateaccount',
-                               $this->getMsg( $this->data['loggedin'] ?
-                                       'createacct-another-submit' : 'createacct-submit' ),
-                               'submit',
-                               array(
-                                       'class' => "mw-ui-button mw-ui-big mw-ui-block mw-ui-primary",
-                                       'id' => 'wpCreateaccount',
-                                       'tabindex' => $tabIndex++
-                               ) );
+                       for ( $benefitIdx = 1; $benefitIdx <= $this->data['benefitCount']; $benefitIdx++ ) {
+                               // Pass each benefit's head text (by default a number) as a parameter to the body's message for PLURAL handling.
+                               $headUnescaped = $this->getMsg( "createacct-benefit-head$benefitIdx" )->text();
                        ?>
+                               <div class="mw-number-text <?php $this->msg( "createacct-benefit-icon$benefitIdx" ); ?>">
+                                       <h3><?php $this->msg( "createacct-benefit-head$benefitIdx" ); ?></h3>
+                                       <p><?php echo $this->getMsg( "createacct-benefit-body$benefitIdx" )->params( $headUnescaped )->escaped(); ?></p>
+                               </div>
+                       <?php } ?>
                </div>
-<?php if ( $this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
-<?php if ( $this->haveData( 'token' ) ) { ?><input type="hidden" name="wpCreateaccountToken" value="<?php $this->text( 'token' ); ?>" /><?php } ?>
-</form>
-</div>
-<div class="mw-createacct-benefits-container">
-       <h2><?php $this->msg( 'createacct-benefit-heading' ); ?></h2>
-       <div class="mw-createacct-benefits-list">
-       <?php
-       for ( $benefitIdx = 1; $benefitIdx <= $this->data['benefitCount']; $benefitIdx++ ) {
-               // Pass each benefit's head text (by default a number) as a parameter to the body's message for PLURAL handling.
-               $headUnescaped = $this->getMsg( "createacct-benefit-head$benefitIdx" )->text();
-       ?>
-               <div class="mw-number-text <?php $this->msg( "createacct-benefit-icon$benefitIdx" ); ?>">
-                       <h3><?php $this->msg( "createacct-benefit-head$benefitIdx" ); ?></h3>
-                       <p><?php echo $this->getMsg( "createacct-benefit-body$benefitIdx" )->params( $headUnescaped )->escaped(); ?></p>
-               </div>
-       <?php
-       }
-       ?>
        </div>
 </div>
-</div>
 <?php
 
        }
index 202ec55..f5ae353 100644 (file)
@@ -28,163 +28,159 @@ class UserloginTemplate extends BaseTemplate {
                $expirationDays = ceil( $wgCookieExpiration / ( 3600 * 24 ) );
 ?>
 <div class="mw-ui-container">
-       <?php
-       if ( $this->haveData( 'languages' ) ) {
-       ?>
+       <?php if ( $this->haveData( 'languages' ) ) { ?>
                <div id="languagelinks">
                        <p><?php $this->html( 'languages' ); ?></p>
                </div>
-       <?php
-       }
-       ?>
-<div id="userloginForm">
-<form name="userlogin" class="mw-ui-vform" method="post" action="<?php $this->text( 'action' ); ?>">
-       <?php if ( $this->data['loggedin'] ) { ?>
-               <div class="warningbox">
-                       <?php echo $this->getMsg( 'userlogin-loggedin' )->params( $this->data['loggedinuser'] )->parse(); ?>
-               </div>
        <?php } ?>
-       <section class="mw-form-header">
-               <?php $this->html( 'header' ); /* extensions such as ConfirmEdit add form HTML here */ ?>
-       </section>
-       <?php
+       <div id="userloginForm">
+               <form name="userlogin" class="mw-ui-vform" method="post" action="<?php $this->text( 'action' ); ?>">
+                       <?php if ( $this->data['loggedin'] ) { ?>
+                               <div class="warningbox">
+                                       <?php echo $this->getMsg( 'userlogin-loggedin' )->params( $this->data['loggedinuser'] )->parse(); ?>
+                               </div>
+                       <?php } ?>
+                       <section class="mw-form-header">
+                               <?php $this->html( 'header' ); /* extensions such as ConfirmEdit add form HTML here */ ?>
+                       </section>
 
-       if ( $this->data['message'] ) {
-       ?>
-               <div class="<?php $this->text( 'messagetype' ); ?>box">
-               <?php
-               if ( $this->data['messagetype'] == 'error' ) {
-               ?>
-                       <strong><?php $this->msg( 'loginerror' ) ?></strong><br />
-               <?php
-               }
-               $this->html( 'message' );
-               ?>
-               </div>
-       <?php
-       }
-       ?>
-               <div>
-                       <label for='wpName1'>
-                               <?php
-                               $this->msg( 'userlogin-yourname' );
-                               if ( $this->data['secureLoginUrl'] ) {
-                                       echo Html::element( 'a', array(
+                       <?php if ( $this->data['message'] ) { ?>
+                               <div class="<?php $this->text( 'messagetype' ); ?>box">
+                                       <?php if ( $this->data['messagetype'] == 'error' ) { ?>
+                                               <strong><?php $this->msg( 'loginerror' ); ?></strong>
+                                               <br />
+                                       <?php } ?>
+                                       <?php $this->html( 'message' ); ?>
+                               </div>
+                       <?php } ?>
+
+                       <div>
+                               <label for='wpName1'>
+                                       <?php
+                                       $this->msg( 'userlogin-yourname' );
+
+                                       if ( $this->data['secureLoginUrl'] ) {
+                                               echo Html::element( 'a', array(
                                                        'href' => $this->data['secureLoginUrl'],
                                                        'class' => 'mw-ui-flush-right mw-secure',
                                                ), $this->getMsg( 'userlogin-signwithsecure' )->text() );
-                               } ?>
-                       </label>
+                                       }
+                                       ?>
+                               </label>
+                               <?php
+                               $extraAttrs = array();
+                               // Set focus to this field if it's blank.
+                               if ( !$this->data['name'] ) {
+                                       $extraAttrs['autofocus'] = '';
+                               }
+                               echo Html::input( 'wpName', $this->data['name'], 'text', array(
+                                       'class' => 'loginText',
+                                       'id' => 'wpName1',
+                                       'tabindex' => '1',
+                                       'size' => '20',
+                                       // 'required' is blacklisted for now in Html.php due to browser issues.
+                                       // Keeping here in case that changes.
+                                       'required',
+                                       'placeholder' => $this->getMsg( 'userlogin-yourname-ph' )->text()
+                               ) + $extraAttrs );
+                               ?>
+                       </div>
+
+                       <div>
+                               <label for='wpPassword1'>
+                                       <?php
+                                       $this->msg( 'userlogin-yourpassword' );
+
+                                       if ( $this->data['useemail'] && $this->data['canreset'] && $this->data['resetlink'] === true ) {
+                                               echo ' ' . Linker::link(
+                                                       SpecialPage::getTitleFor( 'PasswordReset' ),
+                                                       $this->getMsg( 'userlogin-resetpassword-link' )->parse(),
+                                                       array( 'class' => 'mw-ui-flush-right' )
+                                               );
+                                       }
+                                       ?>
+                               </label>
+                               <?php
+                               $extraAttrs = array();
+                               // Set focus to this field if username is filled in.
+                               if ( $this->data['name'] ) {
+                                       $extraAttrs['autofocus'] = '';
+                               }
+                               echo Html::input( 'wpPassword', null, 'password', array(
+                                       'class' => 'loginPassword',
+                                       'id' => 'wpPassword1',
+                                       'tabindex' => '2',
+                                       'size' => '20',
+                                       'placeholder' => $this->getMsg( 'userlogin-yourpassword-ph' )->text()
+                               ) + $extraAttrs );
+                               ?>
+                       </div>
+
                        <?php
-                       $extraAttrs = array();
-                       // Set focus to this field if its blank.
-                       if ( !$this->data['name'] ) {
-                               $extraAttrs['autofocus'] = '';
-                       }
-                       echo Html::input( 'wpName', $this->data['name'], 'text', array(
-                               'class' => 'loginText',
-                               'id' => 'wpName1',
-                               'tabindex' => '1',
-                               'size' => '20',
-                               // 'required' is blacklisted for now in Html.php due to browser issues.
-                               // Keeping here in case that changes
-                               'required',
-                               'placeholder' => $this->getMsg( 'userlogin-yourname-ph' )->text()
-                       ) + $extraAttrs );
+                       if ( isset( $this->data['usedomain'] ) && $this->data['usedomain'] ) {
+                               $doms = "";
+                               foreach ( $this->data['domainnames'] as $dom ) {
+                                       $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
+                               }
                        ?>
-               </div>
-               <div>
-                       <label for='wpPassword1'>
-                       <?php
-                       $this->msg( 'userlogin-yourpassword' );
+                               <div id="mw-user-domain-section">
+                                       <label for='wpDomain'><?php $this->msg( 'yourdomainname' ); ?></label>
+                                       <select name="wpDomain" value="<?php $this->text( 'domain' ); ?>" tabindex="3">
+                                               <?php echo $doms; ?>
+                                       </select>
+                               </div>
+                       <?php } ?>
 
-                       if ( $this->data['useemail'] && $this->data['canreset'] && $this->data['resetlink'] === true ) {
-                               echo ' ' . Linker::link(
-                                       SpecialPage::getTitleFor( 'PasswordReset' ),
-                                       $this->getMsg( 'userlogin-resetpassword-link' )->parse(),
-                                       array( 'class' => 'mw-ui-flush-right' )
-                                       );
-                       }
-                       ?>
-                       </label>
                        <?php
-                       $extraAttrs = array();
-                       // Set focus to this field if username is filled in.
-                       if ( $this->data['name'] ) {
-                               $extraAttrs['autofocus'] = '';
+                       if ( $this->haveData( 'extrafields' ) ) {
+                               echo $this->data['extrafields'];
                        }
-                       echo Html::input( 'wpPassword', null, 'password', array(
-                               'class' => 'loginPassword',
-                               'id' => 'wpPassword1',
-                               'tabindex' => '2',
-                               'size' => '20',
-                               'placeholder' => $this->getMsg( 'userlogin-yourpassword-ph' )->text()
-                       ) + $extraAttrs );
                        ?>
-               </div>
-       <?php
-       if ( isset( $this->data['usedomain'] ) && $this->data['usedomain'] ) {
-               $doms = "";
-               foreach ( $this->data['domainnames'] as $dom ) {
-                       $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
-               }
-       ?>
-               <div id="mw-user-domain-section">
-                       <label for='wpDomain'><?php $this->msg( 'yourdomainname' ); ?></label>
-                               <select name="wpDomain" value="<?php $this->text( 'domain' ); ?>"
-                                       tabindex="3">
-                                       <?php echo $doms ?>
-                               </select>
-               </div>
-       <?php }
-
-       if ( $this->haveData( 'extrafields' ) ) {
-               echo $this->data['extrafields'];
-       } ?>
 
-               <div>
+                       <div>
+                               <?php if ( $this->data['canremember'] ) { ?>
+                                       <label class="mw-ui-checkbox-label">
+                                               <input name="wpRemember" type="checkbox" value="1" id="wpRemember" tabindex="4"
+                                                       <?php if ( $this->data['remember'] ) {
+                                                               echo 'checked="checked"';
+                                                       } ?>
+                                               >
+                                               <?php echo $this->getMsg( 'userlogin-remembermypassword' )->numParams( $expirationDays )->escaped(); ?>
+                                       </label>
+                               <?php } ?>
+                       </div>
 
-       <?php if ( $this->data['canremember'] ) { ?>
-               <label class="mw-ui-checkbox-label">
-                       <input name="wpRemember" type="checkbox" value="1" id="wpRemember" tabindex="4"
-                               <?php if ( $this->data['remember'] ) {
-                                       echo 'checked="checked"';
-                               } ?>
-                       >
-                       <?php echo $this->getMsg( 'userlogin-remembermypassword' )->numParams( $expirationDays )->escaped(); ?>
-               </label>
-       <?php } ?>
-               </div>
+                       <div>
+                               <?php
+                               echo Html::input( 'wpLoginAttempt', $this->getMsg( 'login' )->text(), 'submit', array(
+                                       'id' => 'wpLoginAttempt',
+                                       'tabindex' => '6',
+                                       'class' => 'mw-ui-button mw-ui-big mw-ui-block mw-ui-primary'
+                               ) );
+                               ?>
+                       </div>
 
-               <div>
-                       <?php
-                       echo Html::input( 'wpLoginAttempt', $this->getMsg( 'login' )->text(), 'submit', array(
-                               'id' => 'wpLoginAttempt',
-                               'tabindex' => '6',
-                               'class' => 'mw-ui-button mw-ui-big mw-ui-block mw-ui-primary'
-                       ) );
-                       ?>
-               </div>
-               <div id="mw-userlogin-help">
-                       <?php echo $this->getMsg( 'userlogin-helplink' )->parse(); ?>
-               </div>
-               <?php if ( $this->haveData( 'createOrLoginHref' ) ) { ?>
-                       <?php if ( $this->data['loggedin'] ) { ?>
-                               <div id="mw-createaccount-another">
-                                       <h3 id="mw-userloginlink"><a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7"  class="mw-ui-button"><?php $this->msg( 'userlogin-createanother' ); ?></a></h3>
-                               </div>
-                       <?php } else { ?>
-                               <div id="mw-createaccount-cta">
-                                       <h3 id="mw-userloginlink"><?php $this->msg( 'userlogin-noaccount' ); ?><a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7"  class="mw-ui-button mw-ui-constructive"><?php $this->msg( 'userlogin-joinproject' ); ?></a></h3>
-                               </div>
+                       <div id="mw-userlogin-help">
+                               <?php echo $this->getMsg( 'userlogin-helplink' )->parse(); ?>
+                       </div>
+                       <?php if ( $this->haveData( 'createOrLoginHref' ) ) { ?>
+                               <?php if ( $this->data['loggedin'] ) { ?>
+                                       <div id="mw-createaccount-another">
+                                               <h3 id="mw-userloginlink"><a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7"  class="mw-ui-button"><?php $this->msg( 'userlogin-createanother' ); ?></a></h3>
+                                       </div>
+                               <?php } else { ?>
+                                       <div id="mw-createaccount-cta">
+                                               <h3 id="mw-userloginlink"><?php $this->msg( 'userlogin-noaccount' ); ?><a href="<?php $this->text( 'createOrLoginHref' ); ?>" id="mw-createaccount-join" tabindex="7"  class="mw-ui-button mw-ui-constructive"><?php $this->msg( 'userlogin-joinproject' ); ?></a></h3>
+                                       </div>
+                               <?php } ?>
                        <?php } ?>
-               <?php } ?>
-<?php if ( $this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
-<?php if ( $this->haveData( 'token' ) ) { ?><input type="hidden" name="wpLoginToken" value="<?php $this->text( 'token' ); ?>" /><?php } ?>
-<?php if ( $this->data['cansecurelogin'] ) {?><input type="hidden" name="wpForceHttps" value="<?php $this->text( 'stickhttps' ); ?>" /><?php } ?>
-</form>
-</div>
+                       <?php if ( $this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
+                       <?php if ( $this->haveData( 'token' ) ) { ?><input type="hidden" name="wpLoginToken" value="<?php $this->text( 'token' ); ?>" /><?php } ?>
+                       <?php if ( $this->data['cansecurelogin'] ) {?><input type="hidden" name="wpForceHttps" value="<?php $this->text( 'stickhttps' ); ?>" /><?php } ?>
+               </form>
+       </div>
 </div>
 <?php
+
        }
 }
index d6a0d08..1a1baa2 100644 (file)
@@ -67,6 +67,7 @@
  * @author عصام بايزيدي
  * @author عمرو
  * @author محمد الجداوي
+ * @author مشعل الحربي
  * @author نصوح
  * @author وهراني
  */
@@ -937,6 +938,8 @@ $2',
 'userlogin-resetpassword-link' => 'صفّر كلمة سرّك',
 'helplogin-url' => 'Help:تسجيل الدخول',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|المساعدة في تسجيل الدخول]]',
+'userlogin-loggedin' => 'أنت {{GENDER:$1|مسجل|مسجلة}} الدخول مسبقًا باسم $1. {{GENDER:$1|استخدم|استخدمي}} النموذج بالأسفل لتسجيل الدخول بحساب آخر.',
+'userlogin-createanother' => 'إنشاء حساب آخر',
 'createacct-join' => 'قم بإدخال المعلومات الخاصة بك أدناه.',
 'createacct-another-join' => 'أدخل معلومات الحساب الجديد أدناه.',
 'createacct-emailrequired' => 'عنوان البريد الإلكتروني',
@@ -1741,7 +1744,7 @@ $1",
 'email-address-validity-invalid' => 'أدخل عنوان بريد إلكتروني صالح',
 
 # User rights
-'userrights' => 'إدارة ØµÙ\84احÙ\8aات Ø§Ù\84Ù\85ستخدÙ\85',
+'userrights' => 'صلاحيات المستخدم',
 'userrights-lookup-user' => 'أدِر مجموعات المستخدم',
 'userrights-user-editname' => 'أدخل اسم مستخدم:',
 'editusergroup' => 'عدل مجموعات المستخدم',
@@ -2885,7 +2888,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' => 'عنوان الأيبي هذا ممنوع حاليا.
@@ -4369,7 +4372,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 37e02fe..b2782db 100644 (file)
@@ -525,6 +525,9 @@ Nun t'escaezas de camudar les tos [[Special:Preferences|preferencies de {{SITENA
 'userlogin-resetpassword-link' => 'Reaniciar la contraseña',
 'helplogin-url' => 'Help:Aniciar sesión',
 'userlogin-helplink' => "[[{{MediaWiki:helplogin-url}}|Ayuda p'aniciar sesión]]",
+'userlogin-loggedin' => "Yá anició sesión como {{GENDER:$1|$1}}.
+Utilice'l formulariu de más abaxo p'aniciar sesión como otru usuariu.",
+'userlogin-createanother' => 'Crear otra cuenta',
 'createacct-join' => 'Escriba abaxo la so información.',
 'createacct-another-join' => 'Escriba abaxo la información de la cuenta nueva.',
 'createacct-emailrequired' => 'Direición de corréu electrónicu',
@@ -2288,10 +2291,12 @@ Mira en $2 la llista de les últimes páxines esborraes.',
 'deletecomment' => 'Motivu:',
 'deleteotherreason' => 'Motivu distintu/adicional:',
 'deletereasonotherlist' => 'Otru motivu',
-'deletereason-dropdown' => "*Motivos comunes d'esborráu
+'deletereason-dropdown' => "*Motivos comúnes d'esborráu
+** Puxarra
+** Vandalismu
+** Violación de drechos d'autor
 ** A pidimientu del autor
-** Violación de Copyright
-** Vandalismu",
+** Redireición frañada",
 'delete-edit-reasonlist' => "Editar los motivos d'esborráu",
 'delete-toobig' => "Esta páxina tien un historial d'ediciones grande, más de $1 {{PLURAL:$1|revisión|revisiones}}.
 Restrinxóse l'esborráu d'estes páxines pa evitar perturbaciones accidentales de {{SITENAME}}.",
@@ -2455,7 +2460,7 @@ $1",
 'contributions' => 'Collaboraciones {{GENDER:$1|del usuariu|de la usuaria}}',
 'contributions-title' => "Contribuciones d'usuariu pa $1",
 'mycontris' => 'Collaboraciones',
-'contribsub2' => 'Pa $1 ($2)',
+'contribsub2' => 'Pa {{GENDER:$3|$1}} ($2)',
 'nocontribs' => "Nun s'atoparon cambeos que coincidan con esi criteriu.",
 'uctop' => '(actual)',
 'month' => "Dende'l mes (y anteriores):",
index ad62950..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''.",
@@ -686,6 +690,7 @@ $2',
 'userlogin-resetpassword-link' => 'Възстановяване на паролата',
 'helplogin-url' => 'Help:Влизане',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Помощ за влизане]] в системата',
+'userlogin-createanother' => 'Създаване на друга сметка',
 'createacct-join' => 'Въведете своите данни по-долу.',
 'createacct-emailrequired' => 'Адрес за електронна поща',
 'createacct-emailoptional' => 'Адрес за електронна поща (незадължително)',
@@ -695,6 +700,7 @@ $2',
 'createacct-reason' => 'Причина',
 'createacct-imgcaptcha-ph' => 'Въведете текста, който виждате по-горе',
 'createacct-submit' => 'Създаване на сметката',
+'createacct-another-submit' => 'Създаване на друга сметка',
 'createacct-benefit-heading' => '{{SITENAME}} се създава от хора като вас.',
 'createacct-benefit-body1' => '{{PLURAL:$1|редакция|редакции}}',
 'createacct-benefit-body2' => '{{PLURAL:$1|страница|страници}}',
@@ -702,6 +708,7 @@ $2',
 'userexists' => 'Въведеното потребителско име вече се използва.
 Изберете друго име.',
 'loginerror' => 'Грешка при влизане',
+'createacct-error' => 'Грешка при създаване на сметка',
 'createaccounterror' => 'Не може да бъде създадена сметка: $1',
 'nocookiesnew' => 'Потребителската сметка беше създадена, но все още не сте влезли. {{SITENAME}} използва бисквитки при влизането на потребителите. Разрешете бисквитките в браузъра си, тъй като те са забранени, а след това влезте с потребителското си име и парола.',
 'nocookieslogin' => '{{SITENAME}} използва бисквитки (cookies) за запис на влизанията. Разрешете бисквитките в браузъра си, тъй като те са забранени, и опитайте отново.',
@@ -781,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' => 'Потребителско име:',
@@ -1084,8 +1093,8 @@ $2
 <em>Легенда:</em> (<strong>тек</strong>) = разлика с текущата версия, (<strong>пред</strong>) = разлика с предишната версия, <strong>м</strong>&nbsp;=&nbsp;малка промяна',
 'history-fieldset-title' => 'Търсене в историята',
 'history-show-deleted' => 'Само изтритите',
-'histfirst' => 'Ð\9fÑ\8aÑ\80ви',
-'histlast' => 'Ð\9fоÑ\81ледни',
+'histfirst' => 'най-Ñ\81Ñ\82аÑ\80и',
+'histlast' => 'най-нови',
 'historysize' => '({{PLURAL:$1|1 байт|$1 байта}})',
 'historyempty' => '(празна)',
 
@@ -1391,9 +1400,9 @@ $1",
 'badsiglength' => 'Вашият подпис е твърде дълъг.
 Подписите не могат да надвишават $1 {{PLURAL:$1|знак|знака}}.',
 'yourgender' => 'Пол:',
-'gender-unknown' => 'Ð\9dе Ðµ Ð¿Ð¾Ñ\81оÑ\87ено',
-'gender-male' => 'Ð\9cÑ\8aж',
-'gender-female' => 'Ð\96ена',
+'gender-unknown' => 'Ð\9fÑ\80едпоÑ\87иÑ\82ам Ð´Ð° Ð½Ðµ Ð¿Ð¾Ñ\81оÑ\87а',
+'gender-male' => 'Той Ñ\80едакÑ\82иÑ\80а Ñ\83ики Ñ\81Ñ\82Ñ\80аниÑ\86иÑ\82е',
+'gender-female' => 'ТÑ\8f Ñ\80едакÑ\82иÑ\80а Ñ\83ики Ñ\81Ñ\82Ñ\80аниÑ\86иÑ\82е',
 'prefs-help-gender' => 'По желание: използва се за коректно обръщение по род в системните съобщения на софтуера. Тази информация е публично достъпна.',
 'email' => 'Е-поща',
 'prefs-help-realname' => '* <strong>Истинско име</strong> <em>(незадължително)</em>: Ако го посочите, на него ще бъдат приписани вашите приноси.',
@@ -1406,7 +1415,7 @@ $1",
 'prefs-signature' => 'Подпис',
 'prefs-dateformat' => 'Формат на датата',
 'prefs-timeoffset' => 'Часово отместване',
-'prefs-advancedediting' => 'РазÑ\88иÑ\80ени настройки',
+'prefs-advancedediting' => 'Ð\9eбÑ\89и настройки',
 'prefs-preview' => 'Преглед',
 'prefs-advancedrc' => 'Разширени настройки',
 'prefs-advancedrendering' => 'Разширени настройки',
@@ -1416,6 +1425,7 @@ $1",
 'prefs-displaysearchoptions' => 'Настройки на изгледа',
 'prefs-displaywatchlist' => 'Видими настройки',
 'prefs-diffs' => 'Разлики',
+'prefs-help-prefershttps' => 'Това предпочитание ще бъде активирано при следващото влизане.',
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => 'Адресът за е-поща изглежда валиден',
@@ -2266,9 +2276,12 @@ $UNWATCHURL
 'deleteotherreason' => 'Друга/допълнителна причина:',
 'deletereasonotherlist' => 'Друга причина',
 'deletereason-dropdown' => '*Стандартни причини за изтриване
+** Спам
 ** По молба на автора
 ** Нарушение на авторски права
-** Вандализъм',
+** Вандализъм
+** По желание на автора
+** Грешно пренасочване',
 'delete-edit-reasonlist' => 'Редактиране на причините за изтриване',
 'delete-toobig' => 'Тази страница има голяма редакционна история с над $1 {{PLURAL:$1|версия|версии}}. Изтриването на такива страници е ограничено, за да се предотвратят евентуални поражения на {{SITENAME}}.',
 'delete-warning-toobig' => 'Тази страница има голяма редакционна история с над $1 {{PLURAL:$1|версия|версии}}. Възможно е изтриването да наруши някои операции в базата данни на {{SITENAME}}; необходимо е особено внимание при продължаване на действието.',
@@ -2418,7 +2431,7 @@ $1',
 'contributions' => '{{GENDER:$1|Потребителски}} приноси',
 'contributions-title' => 'Потребителски приноси за $1',
 'mycontris' => 'Приноси',
-'contribsub2' => 'За $1 ($2)',
+'contribsub2' => 'За {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Не са намерени промени, отговарящи на критерия.',
 'uctop' => '(текуща)',
 'month' => 'Месец:',
@@ -3662,6 +3675,8 @@ MediaWiki се разпространява с надеждата, че ще б
 'tags-display-header' => 'Изглед в списъците с промени',
 'tags-description-header' => 'Пълно описание на значението',
 'tags-hitcount-header' => 'Отбелязани промени',
+'tags-active-yes' => 'Да',
+'tags-active-no' => 'Не',
 'tags-edit' => 'редактиране',
 'tags-hitcount' => '$1 {{PLURAL:$1|промяна|промени}}',
 
@@ -3810,7 +3825,7 @@ $1 е автоматично повишен от $4 до $5',
 # Limit report
 'limitreport-cputime-value' => '$1 {{PLURAL:$1|секунда|секунди}}',
 'limitreport-walltime-value' => '$1 {{PLURAL:$1|секунда|секунди}}',
-'limitreport-postexpandincludesize-value' => '$1/$2 байта',
-'limitreport-templateargumentsize-value' => '$1/$2 байта',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|байт|байта}}',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|байт|байта}}',
 
 );
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 088e6a8..ee209f9 100644 (file)
@@ -658,6 +658,9 @@ No oblideu de canviar les vostres [[Special:Preferences|preferències de {{SITEN
 'userlogin-resetpassword-link' => 'Reinicia la contrasenya',
 'helplogin-url' => 'Help:Registrar-se',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Ajuda]]',
+'userlogin-loggedin' => 'Heu iniciat una sessió com {{GENDER:$1|$1}}.
+Feu servir el formulari de sota per iniciar la sessió com un altre usuari.',
+'userlogin-createanother' => 'Crea un altre compte',
 'createacct-join' => 'Introduïu les vostres dades.',
 'createacct-another-join' => 'Introdueix la informació del nou compte a continuació:',
 'createacct-emailrequired' => 'Adreça de correu electrònic',
@@ -812,6 +815,9 @@ Contrasenya temporal: $2",
 
 # Special:ResetTokens
 'resettokens' => 'Reinicia els testimonis',
+'resettokens-text' => "Des d'aquí podeu reiniciar els testimonis que permeten l'accés a certes dades privades associades amb el vostre compte.
+
+Ho hauríeu de fer si accidentalment els heu compartit amb algú o si el vostre compte ha estat compromès.",
 'resettokens-no-tokens' => 'No hi ha testimonis per reiniciar.',
 'resettokens-legend' => 'Reinicia els testimonis',
 'resettokens-tokens' => 'Testimonis:',
@@ -1450,6 +1456,8 @@ Ha de tenir com a molt {{PLURAL:$1|un caràcter|$1 caràcters}}.',
 'userrights-notallowed' => "No teniu autorització per concedir o retirar permisos d'usuari.",
 'userrights-changeable-col' => 'Grups que podeu canviar',
 'userrights-unchangeable-col' => 'Grups que no podeu canviar',
+'userrights-conflict' => "Conflicte de canvis dels permisos d'usuari. Reviseu i confirmeu els canvis.",
+'userrights-removed-self' => 'Heu suprimit els propis permisos correctament. Per tant, ja no podreu tornar a accedir a aquesta pàgina.',
 
 # Groups
 'group' => 'Grup:',
@@ -1516,11 +1524,17 @@ Ha de tenir com a molt {{PLURAL:$1|un caràcter|$1 caràcters}}.',
 'right-unblockself' => 'Desblocar-se a si mateixos',
 'right-protect' => 'Canviar el nivell de protecció i modificar pàgines protegides',
 'right-editprotected' => 'Modificar pàgines protegides (sense protecció de cascada)',
+'right-editsemiprotected' => 'Edita les pàgines protegides com «{{int:protect-level-autoconfirmed}}»',
 'right-editinterface' => "Editar la interfície d'usuari",
 'right-editusercssjs' => "Editar els fitxers de configuració CSS i JS d'altres usuaris",
 'right-editusercss' => "Editar els fitxers de configuració CSS d'altres usuaris",
 'right-edituserjs' => "Editar els fitxers de configuració JS d'altres usuaris",
+'right-editmyusercss' => 'Editeu els fitxers CSS propis',
+'right-editmyuserjs' => 'Editeu els propis fitxers de JavaScript',
 'right-viewmywatchlist' => 'Mostra la llista de seguiment pròpia',
+'right-editmywatchlist' => 'Edita la llista de seguiment pròpia. Tingueu en compte que algunes accions encara afegiran pàgina fins i tot sense aquest permís.',
+'right-viewmyprivateinfo' => 'Mostra les dades privades (p. ex., adreça electrònica o nom real)',
+'right-editmyprivateinfo' => 'Modifica les dades privades  (p. ex., adreça electrònica o nom real)',
 'right-editmyoptions' => 'Edita les pròpies preferències',
 'right-rollback' => "Revertir ràpidament l'últim editor d'una pàgina particular",
 'right-markbotedits' => 'Marcar les reversions com a edicions de bot',
@@ -1573,8 +1587,8 @@ Ha de tenir com a molt {{PLURAL:$1|un caràcter|$1 caràcters}}.',
 'action-block' => 'blocar aquest usuari per a què no pugui editar',
 'action-protect' => "canviar els nivells de protecció d'aquesta pàgina",
 'action-rollback' => "desfer ràpidament les modificacions de l'últim usuari que va editar una determinada pàgina",
-'action-import' => "importar aquesta pàgina des d'un altre wiki",
-'action-importupload' => "importar aquesta pàgina mitjançant la càrrega des d'un fitxer",
+'action-import' => "importa pàgines des d'un altre wiki",
+'action-importupload' => "importa pàgines mitjançant la càrrega d'un fitxer",
 'action-patrol' => 'marcar les edicions dels altres com a supervisades',
 'action-autopatrol' => 'marcar les vostres edicions com a supervisades',
 'action-unwatchedpages' => 'visualitzar la llista de pàgines no vigilades',
@@ -2011,6 +2025,8 @@ Potser voleu modificar-ne la descripció en la seva [$2 pàgina de descripció].
 'pageswithprop-text' => 'Aquesta pàgina llista les pàgines que utilitzen una propietat de pàgina en particular.',
 'pageswithprop-prop' => 'Nom de la propietat:',
 'pageswithprop-submit' => 'Vés',
+'pageswithprop-prophidden-long' => 'valor de propietat text llarg ocult ($1)',
+'pageswithprop-prophidden-binary' => 'valor de propietat binària oculta ($1)',
 
 'doubleredirects' => 'Redireccions dobles',
 'doubleredirectstext' => 'Aquesta pàgina llista les pàgines que redirigeixen a altres pàgines de redirecció.
@@ -2068,6 +2084,7 @@ Les entrades <del>ratllades</del> s\'han resolt.',
 'mostrevisions' => 'Pàgines més modificades',
 'prefixindex' => 'Totes les pàgines per prefix',
 'prefixindex-namespace' => 'Totes les pàgines amb prefix (espai de noms $1)',
+'prefixindex-strip' => 'Suprimeix el prefix a la llista',
 'shortpages' => 'Pàgines curtes',
 'longpages' => 'Pàgines llargues',
 'deadendpages' => 'Pàgines atzucac',
@@ -2083,6 +2100,7 @@ Les entrades <del>ratllades</del> s\'han resolt.',
 'listusers' => "Llista d'usuaris",
 'listusers-editsonly' => 'Mostra només usuaris amb edicions',
 'listusers-creationsort' => 'Ordena per data de creació',
+'listusers-desc' => 'Ordena en ordre descendent',
 'usereditcount' => '$1 {{PLURAL:$1|modificació|modificacions}}',
 'usercreated' => '{{GENDER:$3|Creat}}: $1 a les $2',
 'newpages' => 'Pàgines noves',
@@ -2346,9 +2364,11 @@ Vegeu $2 per a un registre dels esborrats més recents.',
 'deleteotherreason' => 'Motiu diferent o addicional:',
 'deletereasonotherlist' => 'Altres motius',
 'deletereason-dropdown' => "*Motius freqüents d'esborrat
-** Demanada per l'autor
+** Brossa
+** Vandalisme
 ** Violació del copyright
-** Vandalisme",
+** Demanada per l'autor
+** Redirecció trencada",
 'delete-edit-reasonlist' => "Edita els motius d'eliminació",
 'delete-toobig' => "Aquesta pàgina té un historial d'edicions molt gran, amb més de $1 {{PLURAL:$1|canvi|canvis}}. L'eliminació d'aquestes pàgines està restringida per a prevenir que hi pugui haver un desajustament seriós de la base de dades de tot el projecte {{SITENAME}} per accident.",
 'delete-warning-toobig' => "Aquesta pàgina té un historial d'edicions molt gran, amb més de $1 {{PLURAL:$1|canvi|canvis}}. Eliminar-la podria suposar un seriós desajustament de la base de dades de tot el projecte {{SITENAME}}; aneu en compte abans dur a terme l'acció.",
@@ -2367,7 +2387,7 @@ de l'usuari [[User:$2|$2]] ([[User talk:$2|Discussió]]{{int:pipe-separator}}[[S
 La darrera modificació ha estat feta per l'usuari [[User:$3|$3]] ([[User talk:$3|Discussió]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
 'editcomment' => "El resum d'edició ha estat: «$1».",
 'revertpage' => "Revertides les edicions de [[Special:Contributions/$2|$2]] ([[User talk:$2|discussió]]) a l'última versió de [[User:$1|$1]]",
-'revertpage-nouser' => "Les edicions realitzades per un usuari ocult s'han eliminat fins a l'última revisió de [[User:$1|$1]]",
+'revertpage-nouser' => "Edicions revertides per un usuari ocult a l'última revisió de {{GENDER:$1|[[User:$1|$1]]}}",
 'rollback-success' => "Edicions revertides de $1; s'ha canviat a la darrera versió de $2.",
 
 # Edit tokens
@@ -2507,7 +2527,7 @@ $1",
 'contributions' => "Contribucions de {{GENDER:$1|l'usuari|la usuària}}",
 'contributions-title' => "Contribucions de l'usuari $1",
 'mycontris' => 'Contribucions',
-'contribsub2' => 'Per $1 ($2)',
+'contribsub2' => 'Per a {{GENDER:$3|$1}} ($2)',
 'nocontribs' => "No s'ha trobat canvis que encaixessin amb aquests criteris.",
 'uctop' => '(actual)',
 'month' => 'Mes (i anteriors):',
@@ -3791,6 +3811,7 @@ Amb aquest programa heu d'haver rebut [{{SERVER}}{{SCRIPTPATH}}/COPYING una còp
 # Special:Redirect
 'redirect' => 'Redirigeix per fitxer, usuari o ID de la revisió',
 'redirect-legend' => 'Redirigeix a un fitxer o a una pàgina',
+'redirect-summary' => "Aquesta pàgina especial redirigeix a un fitxer (donat el nom del fitxer), una pàgina (donada un ID de la revisió), o a una pàgina d'usuari (donat un ID numèric d'usuari).",
 'redirect-submit' => 'Vés-hi',
 'redirect-lookup' => 'Consulta:',
 'redirect-value' => 'Valor:',
@@ -3853,7 +3874,9 @@ Amb aquest programa heu d'haver rebut [{{SERVER}}{{SCRIPTPATH}}/COPYING una còp
 'tags-tag' => "Nom de l'etiqueta",
 'tags-display-header' => 'Aparença de la llista de canvis',
 'tags-description-header' => 'Descripció completa del significat',
+'tags-active-header' => 'Actiu?',
 'tags-hitcount-header' => 'Canvis etiquetats',
+'tags-active-yes' => 'Sí',
 'tags-edit' => 'modifica',
 'tags-hitcount' => '$1 {{PLURAL:$1|canvi|canvis}}',
 
@@ -4019,9 +4042,9 @@ Altrament, podeu fer servir un senzill formulari a continuació. El vostre comen
 'limitreport-ppvisitednodes' => 'Nombre de nodes visitats pel preprocessador',
 'limitreport-ppgeneratednodes' => 'Nombre de nodes generats pel preprocessador',
 'limitreport-postexpandincludesize' => "Mida d'inclusió post-expansió",
-'limitreport-postexpandincludesize-value' => '$1/$2 bytes',
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
 'limitreport-templateargumentsize' => "Mida de l'argument de plantilla",
-'limitreport-templateargumentsize-value' => '$1/$2 bytes',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
 'limitreport-expansiondepth' => "Profunditat màxima d'expansió",
 'limitreport-expensivefunctioncount' => "Número de funcions d'anàlisi dispendioses",
 
index 8745984..80ae8e2 100644 (file)
@@ -1421,6 +1421,10 @@ PICT # тайп тайпан
 # Random page
 'randompage' => 'Цахууш нисйелла агӀо',
 
+# Random page in category
+'randomincategory' => 'Категори чу цахууш нийса елла агӀо',
+'randomincategory-selectcategory' => 'Категори чу цахууш нийса елла агӀона чу гӀо: $1 $2.',
+
 # Random redirect
 'randomredirect' => 'Цахууш нисделла дIасахьажор',
 
@@ -1521,13 +1525,13 @@ PICT # тайп тайпан
 'logempty' => 'Тептарш чохь хӀокху агӀона дӀаяздарш дац.',
 
 # Special:AllPages
-'allpages' => 'Массо агlонаш',
+'allpages' => 'Массо агӀонаш',
 'alphaindexline' => 'оцу $1 кху $2',
 'nextpage' => 'Тlаьхьа йогlу агlо ($1)',
 'prevpage' => 'Хьалхалера агlо ($1)',
 'allpagesfrom' => 'Гучé яха агlонаш, йуьлалуш йолу оцу:',
 'allpagesto' => 'Арайахар сацадé оцу:',
-'allarticles' => 'Массо агlонаш',
+'allarticles' => 'Массо агӀонаш',
 'allinnamespace' => 'Массо агlонаш оцу цlери анахь «$1»',
 'allpagesnext' => 'Тlаьхьайогlурш',
 'allpagessubmit' => 'Кхочушдé',
@@ -2003,9 +2007,9 @@ PICT # тайп тайпан
 'tooltip-search' => 'Лаха иза дош',
 'tooltip-search-go' => 'Билгала и санна цlе йолучу агlон чу дехьа вала',
 '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ам',
@@ -2339,6 +2343,7 @@ PICT # тайп тайпан
 'dberr-outofdate' => 'Хьуна хаалахь, цуьна йолу меттиг хила мега тишйелла черахь.',
 
 # HTML forms
+'htmlform-invalid-input' => 'Ахьа яздинчу цхьан дакхано гӀалат далина',
 'htmlform-submit' => 'ДӀадахьийта',
 'htmlform-reset' => 'Цаоьшу хийцамаш',
 'htmlform-selectorother-other' => 'Кхин',
index 603a903..81ba616 100644 (file)
@@ -2542,10 +2542,12 @@ Rady a kontakt:
 'deletecomment' => 'Důvod:',
 'deleteotherreason' => 'Jiný/další důvod:',
 'deletereasonotherlist' => 'Jiný důvod',
-'deletereason-dropdown' => '*Obvyklé důvody smazání
-** Na žádost autora
+'deletereason-dropdown' => '* Obvyklé důvody smazání
+** Spam
+** Vandalismus
 ** Porušení autorských práv
-** Vandalismus',
+** Na žádost autora
+** Rozbité přesměrování',
 'delete-edit-reasonlist' => 'Editovat důvody smazání',
 'delete-toobig' => 'Tato stránka má velkou historii editací, přes $1 {{plural:$1|verzi|verze|verzí}}. Mazání takových stránek je omezeno, aby se předešlo nechtěnému narušení {{grammar:2sg|{{SITENAME}}}}.',
 'delete-warning-toobig' => 'Tato stránka má velkou historii editací, přes $1 {{plural:$1|verzi|verze|verzí}}. Mazání takových stránek může narušit databázové operace {{grammar:2sg|{{SITENAME}}}}; postupujte opatrně.',
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 aa95b97..04e3d8e 100644 (file)
@@ -476,7 +476,7 @@ $1',
 'youhavenewmessagesmulti' => 'Du har nye beskeder på $1',
 'editsection' => 'redigér',
 'editold' => 'redigér',
-'viewsourceold' => 'vis kildekode',
+'viewsourceold' => 'vis wikikode',
 'editlink' => 'redigér',
 'viewsourcelink' => 'vis kildetekst',
 'editsectionhint' => 'Rediger afsnit: $1',
index a660912..fad1fea 100644 (file)
@@ -2603,9 +2603,11 @@ Rückmeldungen und weitere Hilfe: {{canonicalurl:{{MediaWiki:Helppage}}}}',
 'deleteotherreason' => 'Anderer/ergänzender Grund:',
 'deletereasonotherlist' => 'Anderer Grund',
 'deletereason-dropdown' => '* Allgemeine Löschgründe
-** Wunsch des Autors
+** Spam
+** Vandalismus
 ** Urheberrechtsverletzung
-** Vandalismus',
+** Wunsch des Autors
+** Defekte Weiterleitung',
 'delete-edit-reasonlist' => 'Löschgründe bearbeiten',
 'delete-toobig' => 'Diese Seite hat mit mehr als $1 {{PLURAL:$1|Version|Versionen}} eine sehr lange Versionsgeschichte. Das Löschen solcher Seiten wurde eingeschränkt, um eine versehentliche Überlastung der Server zu verhindern.',
 'delete-warning-toobig' => 'Diese Seite hat mit mehr als $1 {{PLURAL:$1|Version|Versionen}} eine sehr lange Versionsgeschichte. Das Löschen kann zu Störungen im Datenbankbetrieb führen.',
@@ -2882,8 +2884,8 @@ Zur Aufhebung der Sperre siehe die [[Special:BlockList|Liste aller aktiven Sperr
 'blocklist-nousertalk' => 'darf eigene Diskussionsseite nicht bearbeiten',
 'ipblocklist-empty' => 'Die Liste enthält keine Einträge.',
 'ipblocklist-no-results' => 'Die gesuchte IP-Adresse/der Benutzername ist nicht gesperrt.',
-'blocklink' => 'sperren',
-'unblocklink' => 'freigeben',
+'blocklink' => 'Sperren',
+'unblocklink' => 'Freigeben',
 'change-blocklink' => 'Sperre ändern',
 'contribslink' => 'Beiträge',
 'emaillink' => 'E-Mail senden',
@@ -4206,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 faaccd2..e796087 100644 (file)
@@ -874,7 +874,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',
@@ -3056,10 +3056,12 @@ See $2 for a record of recent deletions.',
 'deletecomment'          => 'Reason:',
 'deleteotherreason'      => 'Other/additional reason:',
 'deletereasonotherlist'  => 'Other reason',
-'deletereason-dropdown'  => '*Common delete reasons
-** Author request
+'deletereason-dropdown'  => '* Common delete reasons
+** Spam
+** Vandalism
 ** Copyright violation
-** Vandalism',
+** Author request
+** Broken redirect',
 'delete-edit-reasonlist' => 'Edit deletion reasons',
 'delete-toobig'          => 'This page has a large edit history, over $1 {{PLURAL:$1|revision|revisions}}.
 Deletion of such pages has been restricted to prevent accidental disruption of {{SITENAME}}.',
@@ -3321,8 +3323,8 @@ Fill in a specific reason below (for example, citing particular pages that were
 'blockipsuccesssub'               => 'Block succeeded',
 'blockipsuccesstext'              => '[[Special:Contributions/$1|$1]] has been blocked.<br />
 See the [[Special:BlockList|block list]] to review blocks.',
-'ipb-blockingself'                => 'You are about to block yourself!  Are you sure you want to do that?',
-'ipb-confirmhideuser'             => 'You are about to block a user with "hide user" enabled.  This will suppress the user\'s name in all lists and log entries.  Are you sure you want to do that?',
+'ipb-blockingself'                => 'You are about to block yourself! Are you sure you want to do that?',
+'ipb-confirmhideuser'             => 'You are about to block a user with "hide user" enabled. This will suppress the user\'s name in all lists and log entries. Are you sure you want to do that?',
 'ipb-edit-dropdown'               => 'Edit block reasons',
 'ipb-unblock-addr'                => 'Unblock $1',
 'ipb-unblock'                     => 'Unblock a username or IP address',
@@ -4620,7 +4622,7 @@ This confirmation code will expire at $4.',
 'confirmrecreate'          => "User [[User:$1|$1]] ([[User talk:$1|talk]]) deleted this page after you started editing with reason:
 : ''$2''
 Please confirm that you really want to recreate this page.",
-'confirmrecreate-noreason' => 'User [[User:$1|$1]] ([[User talk:$1|talk]]) deleted this page after you started editing.  Please confirm that you really want to recreate this page.',
+'confirmrecreate-noreason' => 'User [[User:$1|$1]] ([[User talk:$1|talk]]) deleted this page after you started editing. Please confirm that you really want to recreate this page.',
 'recreate'                 => 'Recreate',
 
 'unit-pixel' => 'px', # only translate this message to other languages if you have to change it
index c10e236..fb5be50 100644 (file)
@@ -2449,10 +2449,12 @@ Vidu la paĝon $2 por registro de lastatempaj forigoj.',
 'deletecomment' => 'Kialo:',
 'deleteotherreason' => 'Alia/plua kialo:',
 'deletereasonotherlist' => 'Alia kialo',
-'deletereason-dropdown' => '*Oftaj kialoj por forigo
-** Peto de aŭtoro
+'deletereason-dropdown' => '* Oftaj kialoj por forigo
+** Trudmesaĝoj
+** Vandalismo
 ** Neglekto de aŭtorrajto
-** Vandalismo',
+** Postulo de la aŭtoro
+** Nefunkcianta alidirektilo',
 'delete-edit-reasonlist' => 'Redakti kialojn de forigo',
 'delete-toobig' => 'Ĉi tiu paĝo havas grandan redakto-historion, pli ol $1 {{PLURAL:$1|version|versiojn}}. Forigo de ĉi tiaj paĝoj estis limigitaj por preventi akcidentan disrompigon de {{SITENAME}}.',
 'delete-warning-toobig' => 'Ĉi tiu paĝo havas grandan redakto-historion, pli ol $1 {{PLURAL:$1|version|versiojn}}. Forigo de ĝi povas disrompigi operacion de {{SITENAME}}; forigu singarde.',
index 814f04b..be82bc2 100644 (file)
@@ -2575,9 +2575,11 @@ Véase $2 para un registro de los borrados recientes.',
 'deleteotherreason' => 'Otro motivo:',
 'deletereasonotherlist' => 'Otro motivo',
 'deletereason-dropdown' => '*Razones comunes de borrado
-** A petición del mismo autor
+** Spam
+** Vandalismo
 ** Violación de copyright
-** Vandalismo',
+** A petición del mismo autor
+** Redirección incorrecta',
 'delete-edit-reasonlist' => 'Editar razones de borrado',
 'delete-toobig' => 'Esta página tiene un historial muy grande, con más de $1 {{PLURAL:$1|revisión|revisiones}}. Borrar este tipo de páginas ha sido restringido para prevenir posibles problemas en {{SITENAME}}.',
 'delete-warning-toobig' => 'Esta página tiene un historial de más de $1 {{PLURAL:$1|revisión|revisiones}}. Eliminarla puede perturbar las operaciones de la base de datos de {{SITENAME}}. Ten cuidado al borrar.',
index 712463b..ca5e467 100644 (file)
@@ -444,7 +444,7 @@ $messages = array(
 'tog-hidepatrolled' => 'ویرایش‌های گشت‌خورده از فهرست تغییرات اخیر پنهان شوند',
 'tog-newpageshidepatrolled' => 'صفحه‌های نهگبانی‌شده از فهرست صفحه‌های تازه پنهان شوند',
 'tog-extendwatchlist' => 'گسترش فهرست پی‌گیری‌ها برای نمایش همهٔ تغییرات، نه فقط آخرین‌ها',
-'tog-usenewrc' => 'گروه‌بندی تغییرات بر پایه صفحه در تغییرات اخیر و فهرست پیگیری‌ها (نیازمند جاوااسکریپت)',
+'tog-usenewrc' => 'گروه‌بندی تغییرات بر پایهٔ صفحه‌های تغییرات اخیر و فهرست پیگیری‌ها (نیازمند جاوااسکریپت)',
 'tog-numberheadings' => 'شماره‌گذاری خودکار عنوان‌ها',
 'tog-showtoolbar' => 'نوار ابزار جعبهٔ ویرایش نمایش یابد',
 'tog-editondblclick' => 'ویرایش صفحه‌ها با دوکلیک (نیازمند جاوااسکریپت)',
@@ -582,7 +582,7 @@ $messages = array(
 'newwindow' => '(در پنجرهٔ جدید باز می‌شود)',
 'cancel' => 'لغو',
 'moredotdotdot' => 'بیشتر...',
-'morenotlisted' => 'اÛ\8cÙ\86 Ù\84Û\8cست کامل نیست.',
+'morenotlisted' => 'اÛ\8cÙ\86 Ù\81Ù\87رست کامل نیست.',
 'mypage' => 'صفحه',
 'mytalk' => 'بحث',
 'anontalk' => 'بحث برای این آی‌پی',
@@ -685,7 +685,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:رویدادهای کنونی',
@@ -772,10 +772,10 @@ $1',
 # General errors
 'error' => 'خطا',
 'databaseerror' => 'خطای پایگاه داده',
-'databaseerror-text' => 'مشکلی در پایگاه داده ها رخ داده است. 
-این ممکن است نشان دهنده یک مشکل در نرم افزار باشد.',
-'databaseerror-textcl' => 'خطای پایگاه داده پرس و جو رخ داده است.',
-'databaseerror-query' => 'پرس و جو:$1',
+'databaseerror-text' => 'مشکلی در پایگاه‌داده‌ها رخ داده‌است. 
+این ممکن است نشان‌دهندهٔ ایرادی در نرم‌افزار باشد.',
+'databaseerror-textcl' => 'یک خطای پرس‌وجوی پایگاه داده‌های رخ داده‌است.',
+'databaseerror-query' => 'پرس‌وجو: $1',
 'databaseerror-function' => 'تابع: $1',
 'databaseerror-error' => 'خطا: $1',
 'laggedslavemode' => "'''هشدار:''' صفحه ممکن است به‌روزرسانی‌های اخیر را شامل نشود.",
@@ -871,7 +871,7 @@ $2',
 'userlogin-yourname-ph' => 'نام کاربریتان را وارد کنید',
 'createacct-another-username-ph' => 'نام کاربریتان را وارد کنید',
 'yourpassword' => 'رمز عبور:',
-'userlogin-yourpassword' => 'رمز عبور',
+'userlogin-yourpassword' => 'گذرواژه',
 'userlogin-yourpassword-ph' => 'گذرواژه را وارد کنید',
 'createacct-yourpassword-ph' => 'یک گذرواژه وارد کنید',
 'yourpasswordagain' => 'تکرار گذرواژه:',
@@ -902,8 +902,8 @@ $2',
 'userlogin-resetpassword-link' => 'گذرواژه‌تان را فراموش کردید؟',
 'helplogin-url' => 'Help:ورود به سامانه',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|راهنمای ورود به سامانه]]',
-'userlogin-loggedin' => 'شما در حال حاضر به عنوان {{GENDER:$1|$1}} وارد سیستم شده اید.
-از فرم پایین برای ورود به عنوان یک کاربر دیگر استفاده کنید.',
+'userlogin-loggedin' => 'شما در حال حاضر به‌عنوان {{GENDER:$1|$1}} وارد سیستم شده‌اید.
+از فرم پایین برای ورود بهعنوان یک کاربر دیگر استفاده کنید.',
 'userlogin-createanother' => 'ایجاد یک حساب کاربری دیگر',
 'createacct-join' => 'اطلاعاتتان را در زیر وارد کنید',
 'createacct-another-join' => 'در زیر اطلاعات کاربری جدیدتان را وارد کنید.',
@@ -1004,7 +1004,7 @@ $2',
 # Email sending
 'php-mail-error-unknown' => 'خطای ناشناخته در تابع  mail()‎ پی‌اچ‌پی',
 'user-mail-no-addy' => 'تلاش برای ارسال نامه بدون یک نشانی رایانامه.',
-'user-mail-no-body' => 'تلاش برای فرستادن پست‌الکترونیک بی‌دلیل کوتاه یا خالی',
+'user-mail-no-body' => 'تلاش برای فرستادن رایانامه بی‌دلیل کوتاه یا خالی',
 
 # Change password dialog
 'resetpass' => 'تغییر گذرواژه',
@@ -1445,7 +1445,7 @@ $1",
 'revdel-restore-visible' => 'نسخه‌های پیدا',
 'pagehist' => 'تاریخچهٔ صفحه',
 'deletedhist' => 'تاریخچهٔ حذف‌شده',
-'revdelete-hide-current' => 'خطا در پنهان کردن مورد مورخ $2 ساعت $1: این نسخه، نسخهٔ اخیر می‌باشد و قابل پنهان کردن نیست.',
+'revdelete-hide-current' => 'خطا در پنهان‌کردن مورد مورخ $2 ساعت $1: این نسخه، نسخهٔ اخیر است و قابل پنهان‌کردن نیست.',
 'revdelete-show-no-access' => 'خطا در پنهان کردن مورد مورخ $2 ساعت $1: این نسخه علامت «محدودیت» دارد و شما به آن دسترسی ندارید.',
 'revdelete-modify-no-access' => 'خطا در پنهان کردن مورد مورخ $2 ساعت $1: این نسخه علامت «محدودیت» دارد و شما به آن دسترسی ندارید.',
 'revdelete-modify-missing' => 'خطا در پنهان کردن مورد شمارهٔ $1: این نسخه در پایگاه داده وجود ندارد!',
@@ -1611,7 +1611,7 @@ $1",
 'prefs-rendering' => 'نمایش صفحه',
 'saveprefs' => 'ذخیره',
 'resetprefs' => 'صفرکردن ترجیحات',
-'restoreprefs' => 'برگرداندن تمام تنظیمات پیش‌فرض (در تمامی قسمت ها)',
+'restoreprefs' => 'برگرداندن تمام تنظیمات پیش‌فرض (در تمامی قسمتها)',
 'prefs-editing' => 'ویرایش',
 'rows' => 'تعداد سطرها:',
 'columns' => 'تعداد ستون‌ها:',
@@ -1675,8 +1675,8 @@ $1",
 'gender-male' => 'مرد',
 'gender-female' => 'زن',
 'prefs-help-gender' => 'انجام این تنظیم اختیاری است.
-نرم افزار از این تنظیم استفاده می کند که جنسیت گرامری شما را بدرستی بیان کند.
-این اطلاعات عمومی خواهد بود.',
+نرم‌افزار از این مقدار برای اشارهٔ صحیح به جنسیت شما و ذکر شما برای دیگران با استفاده از قوائد درست دستور زبان استفاده می‌کند.
+این اطلاعات عمومی خواهند بود.',
 'email' => 'رایانامه',
 'prefs-help-realname' => 'نام واقعی اختیاری است.
 اگر آن را وارد کنید هنگام ارجاع به آثارتان و انتساب آن‌ها به شما از نام واقعی‌تان استفاده خواهد شد.',
@@ -1701,7 +1701,7 @@ $1",
 'prefs-displaywatchlist' => 'گزینه‌های نمایش',
 'prefs-tokenwatchlist' => 'نشانه',
 'prefs-diffs' => 'تفاوت‌ها',
-'prefs-help-prefershttps' => 'تاثیر این ترجیح بعد از ورود بعدی شما اعمال خواهد شد.',
+'prefs-help-prefershttps' => 'تأثیر این ترجیح بعد از ورود بعدی شما اعمال خواهد شد.',
 
 # User preference: email validation using jQuery
 'email-address-validity-valid' => 'نشانی رایانامه معتبر به نظر می‌رسد',
@@ -1728,7 +1728,7 @@ $1",
 'userrights-notallowed' => 'شما مجوز برای افزودن یا حذف حقوق کاربر را ندارید.',
 'userrights-changeable-col' => 'گروه‌هایی که می‌توانید تغییر دهید',
 'userrights-unchangeable-col' => 'گروه‌هایی که نمی‌توانید تغییر دهید',
-'userrights-conflict' => 'تعارض دسترسی‌های کاربری! لطفا بررسی کنید و تغییرات را تایید کنید.',
+'userrights-conflict' => 'تعارض دسترسی‌های کاربری! لطفاً بررسی کنید و تغییرات را تأیید کنید.',
 'userrights-removed-self' => 'شما با موفقیت دسترسی‌های خود را واستاندید. به این ترتیب شما دیگر به این صفحه دسترسی ندارید.',
 
 # Groups
@@ -1809,7 +1809,7 @@ $1",
 'right-editmyprivateinfo' => 'داده‌های خصوصی خود را ویرایش کنید (مانند رایانشانی و نام واقعی)',
 'right-editmyoptions' => 'ترجیحات خود را ویرایش',
 'right-rollback' => 'واگردانی سریع ویرایش‌های آخرین کاربری که یک صفحه را ویرایش کرده‌است',
-'right-markbotedits' => 'علامت زدن ویرایش‌های واگردانی شده به عنوان ویرایش ربات',
+'right-markbotedits' => 'علامت‌زدن ویرایش‌های واگردانی‌شده به‌عنوان ویرایش ربات',
 'right-noratelimit' => 'تاثیر نپذیرفتن از محدودیت سرعت',
 'right-import' => 'وارد کردن صفحه از ویکی‌های دیگر',
 'right-importupload' => 'وارد کردن صفحه از طریق بارگذاری پرونده',
@@ -1860,7 +1860,7 @@ $1",
 'action-protect' => 'تغییر سطح محافظت این صفحه',
 'action-rollback' => 'واگردانی سریع ویرایش‌های آخرین کاربری که یک صفحه را ویرایش کرده‌است',
 'action-import' => 'وارد کردن صفحه از ویکی های دیگر',
-'action-importupload' => 'وارد کردن صفحه از طریق بارگذاری پرونده',
+'action-importupload' => 'واردکردن صفحه از طریق بارگذاری پرونده',
 'action-patrol' => 'گشت زدن ویرایش دیگران',
 'action-autopatrol' => 'گشت زدن ویرایش خودتان',
 'action-unwatchedpages' => 'مشاهدهٔ صفحه‌های پی‌گیری نشده',
@@ -2173,7 +2173,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 'upload_source_file' => '(پرونده‌ای در رایانهٔ شما)',
 
 # Special:ListFiles
-'listfiles-summary' => 'این صفحهٔ ویژه تمام پرونده‌های بارگذاری شده را نمایش می‌دهد.',
+'listfiles-summary' => 'این صفحهٔ ویژه تمام پرونده‌های بارگذاریشده را نمایش می‌دهد.',
 'listfiles_search_for' => 'جستجو به دنبال نام پرونده چندرسانه‌ای:',
 'imgfile' => 'پرونده',
 'listfiles' => 'فهرست پرونده‌ها',
@@ -2184,8 +2184,8 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 'listfiles_size' => 'اندازه',
 'listfiles_description' => 'توضیح',
 'listfiles_count' => 'نسخه‌ها',
-'listfiles-show-all' => 'شامل نسخه های قدیمی عکس ها',
-'listfiles-latestversion' => 'نسخه فعلی',
+'listfiles-show-all' => 'شامل نسخه‌های قدیمی عکس‌ها',
+'listfiles-latestversion' => 'نسخهٔ فعلی',
 'listfiles-latestversion-yes' => 'بله',
 'listfiles-latestversion-no' => 'خیر',
 
@@ -2287,9 +2287,9 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 
 # Random page in category
 'randomincategory' => 'صفحهٔ تصادفی در رده',
-'randomincategory-invalidcategory' => '"$1" نام یک رده معتبر نمی باشد.',
-'randomincategory-nopages' => 'هیج صفحه ای در رده  [[:Category:$1|$1]] وجود ندارد.',
-'randomincategory-selectcategory' => 'دریافت صفحه تصادفی از دسته بندی:  $1   $2 .',
+'randomincategory-invalidcategory' => '«$1» نامی معتبر برای یک ردهٔ نیست.',
+'randomincategory-nopages' => 'هیج صفحه‌ای در رده [[:Category:$1|$1]] وجود ندارد.',
+'randomincategory-selectcategory' => 'دریافت صفحه‌ای تصادفی از دسته‌بندی: $1 $2.',
 'randomincategory-selectcategory-submit' => 'برو',
 
 # Random redirect
@@ -2381,7 +2381,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 'mostrevisions' => 'صفحه‌های دارای بیشترین نسخه',
 'prefixindex' => 'تمام صفحه‌ها با پیشوند',
 'prefixindex-namespace' => 'همهٔ صفحه‌های دارای پیشوند (فضای‌نام $1)',
-'prefixindex-strip' => ' حذف پیشوند در فهرست',
+'prefixindex-strip' => 'حذف پیشوند در فهرست',
 'shortpages' => 'صفحه‌های کوتاه',
 'longpages' => 'صفحه‌های بلند',
 'deadendpages' => 'صفحه‌های بن‌بست',
@@ -2503,8 +2503,8 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 'listgrouprights' => 'اختیارات گروه‌های کاربری',
 'listgrouprights-summary' => 'فهرست زیر شامل گروه‌های کاربری تعریف شده در این ویکی و اختیارات داده شده به آن‌ها است.
 اطلاعات بیشتر در مورد هر یک از اختیارات را در [[{{MediaWiki:Listgrouprights-helppage}}]] بیابید.',
-'listgrouprights-key' => '* <span class="listgrouprights-granted">اختیارات داده شده</span>
-* <span class="listgrouprights-revoked">اختیارات گرفته شده</span>',
+'listgrouprights-key' => '* <span class="listgrouprights-granted">اختیارات دادهشده</span>
+* <span class="listgrouprights-revoked">اختیارات گرفتهشده</span>',
 'listgrouprights-group' => 'گروه',
 'listgrouprights-rights' => 'دسترسی‌ها',
 'listgrouprights-helppage' => 'Help:دسترسی‌های گروهی',
@@ -2659,9 +2659,11 @@ $PAGEINTRO $NEWPAGE
 'deleteotherreason' => 'دلیل دیگر/اضافی:',
 'deletereasonotherlist' => 'دلیل دیگر',
 'deletereason-dropdown' => '*دلایل متداول حذف
-** درخواست کاربر
+** هرزنگار
+** خرابکاری
 ** نقض حق تکثیر
-** خرابکاری',
+** درخواست کاربر
+** تغییرمسیر شکسته',
 'delete-edit-reasonlist' => 'ویرایش دلایل حذف',
 'delete-toobig' => 'این صفحه تاریخچهٔ ویرایشی بزرگی دارد، که شامل بیش از $1 {{PLURAL:$1|نسخه|نسخه}} است.
 به منظور جلوگیری از اختلال ناخواسته در {{SITENAME}} حذف این گونه صفحه‌ها محدود شده‌است.',
@@ -3066,7 +3068,7 @@ $1',
 'movenotallowedfile' => 'شما اجازهٔ انتقال پرونده‌ها را ندارید.',
 'cant-move-user-page' => 'شما اجازه ندارید صفحه‌های کاربری سرشاخه را انتقال دهید.',
 'cant-move-to-user-page' => 'شما اجازه ندارید که یک صفحه را به یک صفحهٔ کاربر انتقال دهید (به استثنای زیر صفحه‌های کاربری).',
-'newtitle' => 'به عنوان جدید',
+'newtitle' => 'بهعنوان جدید',
 'move-watch' => 'پی‌گیری صفحه‌های مبدأ و مقصد',
 'movepagebtn' => 'صفحه منتقل شود',
 'pagemovedsub' => 'انتقال با موفقیت انجام شد',
@@ -3357,7 +3359,7 @@ $2',
 'pageinfo-length' => 'حجم صفحه  (بایت)',
 'pageinfo-article-id' => 'شناسهٔ صفحه',
 'pageinfo-language' => 'زبان محتوای صفحه',
-'pageinfo-robot-policy' => 'فهرست کردن توسط روبات ها',
+'pageinfo-robot-policy' => '‌فهرست‌کردن توسط ربات‌ها',
 'pageinfo-robot-index' => 'مجاز',
 'pageinfo-robot-noindex' => 'غیر مجاز',
 'pageinfo-views' => 'شمار بازدیدها',
@@ -4268,7 +4270,7 @@ $5
 'dberr-problems' => 'شرمنده! این تارنما از مشکلات فنی رنج می‌برد.',
 'dberr-again' => 'چند دقیقه صبر کند و دوباره صفحه را بارگیری کنید.',
 'dberr-info' => '(امکان برقراری ارتباط با کارساز پایگاه داده وجود ندارد: $1)',
-'dberr-info-hidden' => '(امکان تماس با پایگاه داده سرور امکان پذیر نیست)',
+'dberr-info-hidden' => '(امکان تماس با پایگاه‌داده‌ها کارساز امکان‌پذیر نیست)',
 'dberr-usegoogle' => 'شما در این مدت می‌توانید با استفاده از گوگل جستجو کنید.',
 'dberr-outofdate' => 'توجه کنید که نمایه‌های آن‌ها از محتوای ما ممکن است به روز نباشد.',
 'dberr-cachederror' => 'آن‌چه در ادامه می‌آید یک کپی از صفحهٔ درخواست شده است که در کاشه قرار دارد، و ممکن است به روز نباشد.',
@@ -4405,17 +4407,17 @@ $5
 
 # Limit report
 'limitreport-title' => 'داده‌های رخ‌نمانگاری تجزیه‌کننده:',
-'limitreport-cputime' => 'زمان مصرف سی پی یو',
-'limitreport-cputime-value' => '$1 {{PLURAL:$1|ثانیه|ثانیه}}',
+'limitreport-cputime' => 'زمان مصرف سی‌پی‌یو',
+'limitreport-cputime-value' => '$1 {{PLURAL:$1|ثانیه}}',
 'limitreport-walltime' => 'استفاده زمان واقعی',
 'limitreport-walltime-value' => '$1 {{PLURAL:$1|ثانیه|ثانیه}}',
-'limitreport-ppvisitednodes' => 'شمارش گره پیش پردازنده مشاهده شده',
-'limitreport-ppgeneratednodes' => 'شمارش گره پیش پردازنده تولید شده',
+'limitreport-ppvisitednodes' => 'شمارش گرهٔ پیش‌پردازنده مشاهده‌شده',
+'limitreport-ppgeneratednodes' => 'شمارش گره پیش‌پردازنده تولیدشده',
 'limitreport-postexpandincludesize' => 'شامل اندازه پس گسترش',
 'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|بایت|بایت}}',
 'limitreport-templateargumentsize' => 'اندازه عملگر الگو',
 'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|بایت|بایت}}',
 'limitreport-expansiondepth' => 'بیشترین عمق گسترش',
-'limitreport-expensivefunctioncount' => 'تعداد تابع تجزیه گر پرمصرف',
+'limitreport-expensivefunctioncount' => 'تعداد تابع تجزیهگر پرمصرف',
 
 );
index dfeff0d..804552b 100644 (file)
@@ -2572,9 +2572,12 @@ Voit palauttaa versioita valikoivasti valitsemalla vain niiden versioiden valint
 'undeletehistory' => 'Jos palautat sivun, kaikki versiot lisätään sivun historiaan. Jos uusi sivu samalla nimellä on luotu poistamisen jälkeen, palautetut versiot lisätään sen historiaan.',
 'undeleterevdel' => 'Palautusta ei tehdä, jos sen seurauksena sivun uusin versio olisi osittain piilotettu. 
 Tässä tilanteessa älä valitse palautettavaksi näkyviin viimeisintä poistettua versiota tai poista version piilotus.',
-'undeletehistorynoadmin' => 'Tämä sivu on poistettu. Syy sivun poistamiseen näkyy yhteenvedossa, jossa on myös tiedot, ketkä ovat muokanneet tätä sivua ennen poistamista. Sivujen varsinainen sisältö on vain ylläpitäjien luettavissa.',
+'undeletehistorynoadmin' => 'Tämä sivu on poistettu. 
+Syy sivun poistamiseen näkyy alla olevassa yhteenvedossa, jossa on myös tiedot, ketkä olivat muokanneet tätä sivua ennen poistamista. 
+Näiden poistettujen versioiden varsinainen tekstisisältö on vain ylläpitäjien luettavissa.',
 'undelete-revision' => 'Poistettu sivu $1 hetkellä $4 kello $5. Tekijä: $3.',
-'undeleterevision-missing' => 'Virheellinen tai puuttuva versio. Se on saatettu palauttaa tai poistaa arkistosta.',
+'undeleterevision-missing' => 'Virheellinen tai puuttuva versio. 
+Sinulla on kenties käytössä väärä linkki, tai sitten versio on saatettu palauttaa tai poistaa arkistosta.',
 'undelete-nodiff' => 'Aikaisempaa versiota ei löytynyt.',
 'undeletebtn' => 'Palauta',
 'undeletelink' => 'näytä tai palauta',
index c364468..1206c92 100644 (file)
@@ -2577,10 +2577,12 @@ Voir $2 pour une liste des suppressions récentes.',
 'deletecomment' => 'Motif :',
 'deleteotherreason' => 'Motif autre ou supplémentaire :',
 'deletereasonotherlist' => 'Autre motif',
-'deletereason-dropdown' => "* Motifs de suppression les plus courants
-** Demande de l'auteur
-** Violation des droits d'auteur
-** Vandalisme",
+'deletereason-dropdown' => '* Motifs de suppression les plus courants
+** Pourriel
+** Vandalisme
+** Violation des droits d’auteur
+** Demande de l’auteur
+** 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}}.',
@@ -3304,7 +3306,7 @@ Vous pouvez toutefois en visualiser la source.',
 'pageinfo-magic-words' => '{{PLURAL:$1|Mot magique|Mots magiques}} ($1)',
 'pageinfo-hidden-categories' => '{{PLURAL:$1|Catégorie cachée|Catégories cachées}} ($1)',
 'pageinfo-templates' => '{{PLURAL:$1|Modèle inclu|Modèles inclus}} ($1)',
-'pageinfo-transclusions' => '{{PLURAL:$1|Page traduite|Pages traduites}} sur ($1)',
+'pageinfo-transclusions' => '{{PLURAL:$1|Page dans laquelle|Pages dans lesquelles}} elle est incluse ($1)',
 'pageinfo-toolboxlink' => 'Information sur la page',
 'pageinfo-redirectsto' => 'Rediriger vers',
 'pageinfo-redirectsto-info' => 'info',
index 1cd5667..3683045 100644 (file)
@@ -2485,10 +2485,12 @@ No $2 pode ver unha lista cos borrados máis recentes.',
 'deletecomment' => 'Motivo:',
 'deleteotherreason' => 'Outro motivo:',
 'deletereasonotherlist' => 'Outro motivo',
-'deletereason-dropdown' => '*Motivos frecuentes para borrar
-** Solicitado pola persoa que o creou
+'deletereason-dropdown' => '* Motivos frecuentes para borrar
+** Spam
+** Vandalismo
 ** Violación dos dereitos de autoría
-** Vandalismo',
+** Solicitado pola persoa que creou a páxina
+** Redirección rota',
 'delete-edit-reasonlist' => 'Editar os motivos de borrado',
 'delete-toobig' => 'Esta páxina conta cun historial longo, de máis {{PLURAL:$1|dunha revisión|de $1 revisións}}.
 Limitouse a eliminación destas páxinas para previr problemas de funcionamento accidentais en {{SITENAME}}.',
index f0b16ca..d26e1b7 100644 (file)
@@ -2581,9 +2581,11 @@ $UNWATCHURL
 'deleteotherreason' => 'סיבה נוספת/אחרת:',
 'deletereasonotherlist' => 'סיבה אחרת',
 'deletereason-dropdown' => '* סיבות מחיקה נפוצות
-** לבקשת הכותב
+** ספאם
+** השחתה
 ** הפרת זכויות יוצרים
-** השחתה',
+** לבקשת הכותב
+** הפניה שבורה',
 'delete-edit-reasonlist' => 'עריכת סיבות המחיקה',
 'delete-toobig' => 'דף זה כולל מעל {{PLURAL:$1|גרסה אחת|$1 גרסאות}} בהיסטוריית העריכות שלו. מחיקת דפים כאלה הוגבלה כדי למנוע פגיעה בביצועי האתר.',
 'delete-warning-toobig' => 'דף זה כולל מעל {{PLURAL:$1|גרסה אחת|$1 גרסאות}} בהיסטוריית העריכות שלו. מחיקה שלו עלולה להפריע לפעולות בבסיס הנתונים; אנא שקלו שנית את המחיקה.',
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 4214829..0869055 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',
@@ -461,6 +461,7 @@ $messages = array(
 'newwindow' => '(otvara se u novom prozoru)',
 'cancel' => 'Odustani',
 'moredotdotdot' => 'Više...',
+'morenotlisted' => 'Ovaj popis nije potpun.',
 'mypage' => 'Stranica',
 'mytalk' => 'Moj razgovor',
 'anontalk' => 'Razgovor za ovu IP adresu',
@@ -516,6 +517,7 @@ $messages = array(
 'create-this-page' => 'Započni ovu stranicu',
 'delete' => 'Izbriši',
 'deletethispage' => 'Izbriši ovu stranicu',
+'undeletethispage' => 'Vrati ovu stranicu',
 'undelete_short' => 'Vrati {{PLURAL:$1|$1 uređivanje|$1 uređivanja}}',
 'viewdeleted_short' => 'Prikaži $1 {{plural: $1|izbrisano uređivanje|izbrisana uređivanja|izbrisanih uređivanja}}',
 'protect' => 'Zaštiti',
@@ -827,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.',
@@ -877,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?',
@@ -1438,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:',
@@ -1502,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',
@@ -1678,6 +1681,7 @@ Ne smije biti duži od $1 {{PLURAL:$1|znaka|znaka|znakova}}.',
 
 # Recent changes
 'nchanges' => '{{PLURAL:$1|$1 promjena|$1 promjene|$1 promjena}}',
+'enhancedrc-history' => 'povijest',
 'recentchanges' => 'Nedavne promjene',
 'recentchanges-legend' => 'Izbornik nedavnih promjena',
 'recentchanges-summary' => 'Na ovoj stranici možete pratiti nedavne promjene u wikiju.',
@@ -1965,6 +1969,8 @@ Kad je filtriran po suradniku, popis prikazuje samo one datoteke čije je poslje
 'listfiles_size' => 'Veličina (u bajtovima)',
 'listfiles_description' => 'Opis',
 'listfiles_count' => 'Inačice',
+'listfiles-latestversion-yes' => 'Da',
+'listfiles-latestversion-no' => 'Ne',
 
 # File description page
 'file-anchor-link' => 'Slika',
@@ -2058,6 +2064,9 @@ Možda želite urediti njen opis na [$2 stranici opisa datoteke].',
 'randompage' => 'Slučajna stranica',
 'randompage-nopages' => 'Nema stranica u {{PLURAL:$2|imenskom prostoru|imenskim prostorima}}: $1.',
 
+# Random page in category
+'randomincategory-selectcategory-submit' => 'Idi',
+
 # Random redirect
 'randomredirect' => 'Slučajno preusmjeravanje',
 'randomredirect-nopages' => 'Nema preusmjeravanja u imenskom prostoru "$1".',
index 5ce7498..e93d0c2 100644 (file)
@@ -1395,6 +1395,9 @@ Vu darfos adjuntar kauso en la rezumo.',
 # Spam protection
 'spamprotectiontitle' => 'Filtrilo kontre spamo',
 
+# Info page
+'pageinfo-toolboxlink' => 'Informo di ca pagino',
+
 # Browsing diffs
 'previousdiff' => '← Plu anciena versiono',
 'nextdiff' => 'Plu recenta versiono →',
index 5d131bf..a625b42 100644 (file)
@@ -25,6 +25,7 @@
  * @author Dakrismeno
  * @author Danmaz74
  * @author Darth Kule
+ * @author DexterMorgan
  * @author F. Cosoleto
  * @author Felis
  * @author FollowTheMedia
@@ -291,7 +292,7 @@ $linkTrail = '/^([a-zàéèíîìóòúù]+)(.*)$/sDu';
 $messages = array(
 # User preference toggles
 'tog-underline' => 'Sottolinea i collegamenti:',
-'tog-justify' => 'Allineamento dei paragrafi giustificato',
+'tog-justify' => 'Allineamento giustificato dei paragrafi',
 'tog-hideminor' => 'Nascondi le modifiche minori nelle ultime modifiche',
 'tog-hidepatrolled' => 'Nascondi le modifiche verificate nelle ultime modifiche',
 'tog-newpageshidepatrolled' => "Nascondi le pagine verificate dall'elenco delle pagine più recenti",
@@ -2450,10 +2451,12 @@ Consultare il log delle $2 per un elenco delle pagine cancellate di recente.',
 'deletecomment' => 'Motivo:',
 'deleteotherreason' => 'Altra motivazione o motivazione aggiuntiva:',
 'deletereasonotherlist' => 'Altra motivazione',
-'deletereason-dropdown' => "*Motivazioni più comuni per la cancellazione
-** Richiesta dell'autore
+'deletereason-dropdown' => "* Motivazioni più comuni per la cancellazione
+** Spam
+** Vandalismo
 ** Violazione di copyright
-** Vandalismo",
+** Richiesta dell'autore
+** Redirect rotto",
 'delete-edit-reasonlist' => 'Modifica i motivi di cancellazione',
 'delete-toobig' => 'La cronologia di questa pagina è molto lunga (oltre $1 {{PLURAL:$1|revisione|revisioni}}). La sua cancellazione è stata limitata per evitare di creare accidentalmente dei problemi di funzionamento al database di {{SITENAME}}.',
 'delete-warning-toobig' => 'La cronologia di questa pagina è molto lunga (oltre $1 {{PLURAL:$1|revisione|revisioni}}). La sua cancellazione può creare dei problemi di funzionamento al database di {{SITENAME}}; procedere con cautela.',
index c31763d..fddfca2 100644 (file)
@@ -2636,9 +2636,11 @@ $UNWATCHURL
 'deleteotherreason' => '他の、または追加の理由:',
 'deletereasonotherlist' => 'その他の理由',
 'deletereason-dropdown' => '*よくある削除理由
-** 投稿者依頼
+** スパム
+** 荒らし
 ** 著作権侵害
-** 荒らし',
+** 投稿者依頼
+** 破損リダイレクト',
 'delete-edit-reasonlist' => '削除理由を編集',
 'delete-toobig' => 'このページには、$1版を超える編集履歴があります。
 このようなページの削除は、{{SITENAME}}の偶発的な問題を避けるため、制限されています。',
index d23ba98..9497dc9 100644 (file)
@@ -805,7 +805,7 @@ $2',
 'userlogin-noaccount' => '계정이 없나요?',
 'userlogin-joinproject' => '{{SITENAME}}에 가입하세요',
 'nologin' => '계정이 없나요? $1.',
-'nologinlink' => '계정을 만들 수 있습니다',
+'nologinlink' => '계정을 만들',
 'createaccount' => '계정 만들기',
 'gotaccount' => '계정이 이미 있다면, $1.',
 'gotaccountlink' => '로그인하세요',
@@ -1183,7 +1183,7 @@ IP 주소는 여러 사용자가 공유할 수 있습니다.
 'hiddencategories' => '이 문서는 다음 {{PLURAL:$1|숨은 분류 1개|숨은 분류 $1개}}에 속해 있습니다:',
 'edittools' => '<!-- 이 문서는 편집 창과 파일 올리기 창에 출력됩니다. -->',
 'nocreatetext' => '{{SITENAME}}에서 새로운 문서를 만드는 것은 제한되어 있습니다.
-이미 존재하는 다른 문서를 편집하거나, [[Special:UserLogin|로그인하거나 계정을 만들 수 있습니다]].',
+이미 존재하는 다른 문서를 편집하거나, [[Special:UserLogin|로그인하거나 계정을 만들]] 수 있습니다.',
 'nocreate-loggedin' => '새 문서를 만들 권한이 없습니다.',
 'sectioneditnotsupported-title' => '부분 편집 지원 안됨',
 'sectioneditnotsupported-text' => '이 문서에서는 문단 편집을 지원하지 않습니다.',
@@ -2596,10 +2596,12 @@ $UNWATCHURL
 'deletecomment' => '이유:',
 'deleteotherreason' => '다른 이유/추가적인 이유:',
 'deletereasonotherlist' => '다른 이유',
-'deletereason-dropdown' => '*일반적인 삭제 이유
-** 작성자의 요청
+'deletereason-dropdown' => '* 일반적인 삭제 이유
+** 스팸
+** 문서 훼손 행위
 ** 저작권 침해
-** 훼손 행위',
+** 작성자의 요청
+** 깨진 넘겨주기',
 'delete-edit-reasonlist' => '삭제 이유 편집',
 'delete-toobig' => '이 문서에는 {{PLURAL:$1|편집 역사}}가 $1개 있습니다.
 편집 역사가 긴 문서를 삭제하면 {{SITENAME}}에 큰 혼란을 줄 수 있기 때문에 삭제할 수 없습니다.',
index e850a2e..ff7a910 100644 (file)
@@ -1610,9 +1610,11 @@ Adfirma quaesumus te paginam re vera delere velle, te consequentias intellere, e
 'deleteotherreason' => 'Causa alia vel explicatio:',
 'deletereasonotherlist' => 'Causa alia',
 'deletereason-dropdown' => '*Causae deletionum communes
-** Desiderium auctoris
+** Spam
+** Vandalismus
 ** Violatio verborum privatorum
-** Vandalismus',
+** Desiderium auctoris
+** Redirectio fracta',
 'delete-edit-reasonlist' => 'Causas deletionum recensere',
 
 # Rollback
index d003476..73cc82c 100644 (file)
@@ -2367,7 +2367,9 @@ W.e.g. confirméiert, datt Dir dëst wierklech wëllt, datt Dir d'Konsequenze ve
 'deletereason-dropdown' => '* Heefegst Grënn fir eng Säit ze läschen
 ** Wonsch vum Auteur
 ** Verletzung vun engem Copyright
-** Vandalismus',
+** Vandalismus
+** Futtis Viruleedung
+** Spam',
 'delete-edit-reasonlist' => 'Läschgrënn änneren',
 'delete-toobig' => "Dës Säit huet e laangen Historique, méi wéi $1 {{PLURAL:$1|Versioun|Versiounen}}.
 D'Läsche vu sou Säite gouf limitéiert fir ongewollte Stéierungen op {{SITENAME}} ze verhënneren.",
@@ -2877,6 +2879,7 @@ All Transwiki-Import-Aktioune ginn am [[Special:Log/import|Import-Logbuch]] prot
 'import-interwiki-templates' => 'Mat alle Schablounen',
 'import-interwiki-submit' => 'Import',
 'import-interwiki-namespace' => 'Zil-Nummraum:',
+'import-interwiki-rootpage' => 'Zil-Stamm-Säit (fakultativ):',
 'import-upload-filename' => 'Numm vum Fichier:',
 'import-comment' => 'Bemierkung:',
 'importtext' => 'Exportéiert de Fichier w.e.g. vun der Source-Wiki mat der [[Special:Export|Export-Funktioun]].
index 673991c..0efe4a3 100644 (file)
@@ -28,6 +28,7 @@
  * @author Siggis
  * @author Tomasdd
  * @author Urhixidur
+ * @author Vilius2001
  * @author Vpovilaitis
  * @author לערי ריינהארט
  */
@@ -1526,8 +1527,8 @@ teisės",
 'action-block' => 'neleisti šiam naudotojui redaguoti',
 'action-protect' => 'pakeisti apsaugos lygius šiam puslapiui',
 'action-rollback' => 'greitai atmesti paskutinio naudotojo atliktų tam tikro puslapio pakeitimų',
-'action-import' => 'importuoti šį puslapį iš kitos wiki',
-'action-importupload' => 'importuoti šį puslapį iš įkelto failo',
+'action-import' => 'importuoti puslapius iš kitos wiki',
+'action-importupload' => 'importuoti puslapius iš įkelto failo',
 'action-patrol' => 'pažymėti kitų keitimus kaip patikrintus',
 'action-autopatrol' => 'savo keitimų pažymėjimas patikrintais',
 'action-unwatchedpages' => 'žiūrėti nestebimų puslapių sąrašą',
@@ -2302,7 +2303,7 @@ kažkas jau pakeitė puslapį arba suspėjo pirmas atmesti keitimą.
 Paskutimas keitimas darytas naudotojo [[User:$3|$3]] ([[User talk:$3|Aptarimas]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).',
 'editcomment' => "Redagavimo komentaras: „''$1''“.",
 'revertpage' => 'Atmestas [[Special:Contributions/$2|$2]] ([[User talk:$2|Aptarimas]]) pakeitimas; sugrąžinta [[User:$1|$1]] versija',
-'revertpage-nouser' => 'Atmesti (naudotojo vardas pašalintas) pakeitimai, grąžinta prieš tai buvusi [[User:$1|$1]] versija',
+'revertpage-nouser' => 'Atversti pakeitimai paslėpto vartotojo, grąžino prieš tai buvusią versiją {{GENDER:$1|[[User:$1|$1]]}}',
 'rollback-success' => 'Atmesti $1 pakeitimai;
 grąžinta prieš tai buvusi $2 versija.',
 
@@ -2442,7 +2443,7 @@ $1',
 'contributions' => '{{GENDER:$1|Naudotojo}} įndėlis',
 'contributions-title' => '{{GENDER:$1|Naudotojo|Naudotojos}} $1 indėlis',
 'mycontris' => 'Įnašai',
-'contribsub2' => 'Naudotojo $1 ($2)',
+'contribsub2' => 'Dėl {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Jokie keitimai neatitiko šių kriterijų.',
 'uctop' => '(dabartinis)',
 'month' => 'Nuo mėnesio (ir anksčiau):',
index d763ea8..c97461d 100644 (file)
@@ -1195,10 +1195,10 @@ Ja tu izvēlies to norādīt, tas tiks izmantots, lai identificētu tavu darbu (
 'right-move-rootuserpages' => 'Pārvietot saknes lietotāja lapas',
 'right-movefile' => 'Pārvietot failus',
 'right-suppressredirect' => 'Neveidot pāradresāciju no vecā nosaukuma, pārvietojot lapu',
-'right-upload' => 'Augšuplādēt failus',
+'right-upload' => 'Augšupielādēt failus',
 'right-reupload' => 'Pārrakstīt esošu failu',
 'right-reupload-own' => 'Pārrakstīt paša augšuplādētu esošu failu',
-'right-upload_by_url' => 'Augšuplādēt failu no URL',
+'right-upload_by_url' => 'Augšupielādēt failus no URL',
 'right-autoconfirmed' => 'Izmainīt daļēji aizsargātas lapas',
 'right-delete' => 'Dzēst lapas',
 'right-bigdelete' => 'Dzēst lapas ar lielām hronoloģijām',
@@ -1328,8 +1328,8 @@ Lapas, kas ir tavā [[Special:Watchlist|uzraugāmo rakstu sarakstā]] ir '''trek
 'recentchangeslinked-to' => 'Rādīt izmaiņas lapās, kurās ir saites uz šo lapu (nevis lapās uz kurām ir saites no šīs lapas)',
 
 # Upload
-'upload' => 'Augšuplādēt failu',
-'uploadbtn' => 'Augšuplādēt',
+'upload' => 'Augšupielādēt failu',
+'uploadbtn' => 'Augšupielādēt',
 'reuploaddesc' => 'Atcelt augšupielādi un atgriezties pie augšupielādes veidnes.',
 'upload-tryagain' => 'Iesniegt izmainīto faila aprakstu',
 'uploadnologin' => 'Neesi iegājis',
@@ -1342,7 +1342,7 @@ Lapas, kas ir tavā [[Special:Watchlist|uzraugāmo rakstu sarakstā]] ir '''trek
  Dzēšanas un pārvietošanas reģistri šai lapai ir uzskaitīti šeit:",
 'uploadtext' => "Pirms tu kaut ko augšupielādē, noteikti izlasi un ievēro [[Project:Attēlu izmantošanas noteikumi|attēlu izmantošanas noteikumus]].
 
-Lai aplūkotu vai meklētu agrāk augšuplādētus attēlus,
+Lai aplūkotu vai meklētu agrāk augšupielādētus attēlus,
 dodies uz [[Special:FileList|augšupielādēto attēlu sarakstu]].
 Augšupielādes un dzēšanas tiek reģistrētas [[Special:Log/upload|augšupielādes reģistrā]] un [[Special:Log/delete|dzēšanas reģistrā]].
 
@@ -1350,7 +1350,7 @@ Izmanto šo veidni, lai augšupielādētu jaunus attēlu failus, ar kuriem ilust
 Gandrīz visos pārlūkos tev vajadzētu redzēt pogu '''\"Choose...\",''' kuru spiežot parādīsies faila atvēršanas dialogs.
 Izvēloties kādu failu, tā adrese parādīsies ailītē blakus šai pogai.
 Tev ir arī jāatzīmē ailīte, kas apstiprina, ka tu nepārkāp nekādas autortiesības, augšupielādējot šo failu.
-Spied pogu '''Augšuplādēt''', lai pabeigtu augšupielādi.
+Spied pogu '''Augšupielādēt''', lai pabeigtu augšupielādi.
 Tas var ieilgt, ja tavs interneta pieslēgums ir lēns.
 
 Ieteicamie formāti ir:
@@ -1364,7 +1364,7 @@ Lūdzu, pārliecinies, ka faila nosaukums ir pietiekami aprakstošs, lai izvair
 vai skaņām
 * '''<nowiki>[[</nowiki>{{ns:media}}<nowiki>:Fails.ogg]]</nowiki>'''
 
-Lūdzu, ņem vērā, ka tāpat kā citas wiki lapas arī tevis augšuplādētos failus citi var mainīt vai dzēst, ja uzskata, ka tas nāktu par labu šim projektam, kā arī atceries, ka tev var tikt liegta augšupielādes iespēja, ja tu šo sistēmu.",
+Lūdzu, ņem vērā, ka tāpat kā citas wiki lapas arī tevis augšupielādētos failus citi var mainīt vai dzēst, ja uzskata, ka tas nāktu par labu šim projektam, kā arī atceries, ka tev var tikt liegta augšupielādes iespēja, ja tu šo sistēmu.",
 'upload-permitted' => 'Atļautie failu tipi: $1.',
 'upload-preferred' => 'Ieteicamie failu tipi: $1.',
 'upload-prohibited' => 'Aizliegtie failu tipi: $1.',
@@ -1382,11 +1382,11 @@ Pārskatāmāka versija ir pieejama [[Special:NewFiles|jauno attēlu galerijā]]
 'ignorewarning' => 'Ignorēt brīdinājumu un saglabāt failu',
 'ignorewarnings' => 'Ignorēt visus brīdinājumus',
 'minlength1' => 'Failu vārdiem jābūt vismaz vienu simbolu gariem.',
-'illegalfilename' => 'Faila nosaukumā "$1" ir simboli, kas nav atļauti virsrakstos. Lūdzu, pārdēvē failu un mēģini to vēlreiz augšuplādēt.',
+'illegalfilename' => 'Faila nosaukumā "$1" ir simboli, kas nav atļauti virsrakstos. Lūdzu, pārdēvējiet failu un mēģiniet to vēlreiz augšupielādēt.',
 'filename-toolong' => 'Failu nosaukumi nedrīkst pārsniegt 240 baitus.',
 'badfilename' => 'Attēla nosaukums ir nomainīts, tagad tas ir "$1".',
 'filetype-mime-mismatch' => 'Faila paplašinājums ".$1" neatbilst noteiktajam MIME tipam ($2).',
-'filetype-badmime' => 'Šeit nav atļauts augšuplādēt failus ar MIME tipu "$1".',
+'filetype-badmime' => 'Šeit nav atļauts augšupielādēt failus ar MIME tipu "$1".',
 'filetype-bad-ie-mime' => 'Nevar augšupielādēt šo failu, jo Internet Explorer to uzskatītu kā "$1", kas ir neatļauts un potenciāli bīstams faila tips.',
 'filetype-unwanted-type' => "'''\".\$1\"''' ir nevēlams failu tips.  {{PLURAL:\$3|Ieteicamais faila tips|Ieteicamie failu tipi}} ir \$2.",
 'filetype-banned-type' => "'''\".\$1\"''' nav atļautais failu tips.  {{PLURAL:\$3|Atļautais faila tips|Atļautie failu tipi}} ir \$2.",
@@ -1404,7 +1404,9 @@ Pārskatāmāka versija ir pieejama [[Special:NewFiles|jauno attēlu galerijā]]
 'large-file' => 'Ieteicams, lai faili nebūtu lielāki par $1;
 šī faila izmērs ir $2.',
 'largefileserver' => 'Šis fails ir lielāks nekā serveris ņem pretī.',
-'emptyfile' => 'Šķiet, ka tu esi augšuplādējis tukšu failu. Iespējams, faila nosaukumā esi pieļāvis kļūdu. Lūdzu, pārbaudi, vai tiešām tu vēlies augšuplādēt tieši šo failu.',
+'emptyfile' => 'Šķiet, ka jūs esat augšupielādējis tukšu failu.
+Iespējams, faila nosaukumā esat pieļāvis kļūdu.
+Lūdzu, pārbaudiet, vai tiešām jūs vēlaties augšupielādēt tieši šo failu.',
 'windows-nonascii-filename' => 'Šī viki neatbalsta failu nosaukumus ar īpašām rakstzīmēm.',
 'fileexists' => 'Fails ar šādu nosaukumu jau pastāv, lūdzu, pārbaudi <strong>[[:$1]]</strong>, ja neesi drošs, ka vēlies to mainīt.
 [[$1|thumb]]',
@@ -1416,7 +1418,8 @@ Lūdzu, izvēlieties citu nosaukumu.',
 Izskatās, ka šis ir samazināts attēls ''(thumbnail)''.
 Ja tev ir šis pats attēls pilnā izmērā, augšuplādē to, ja nav, tad nomaini faila vārdu.",
 'fileexists-forbidden' => 'Fails ar šādu nosaukumu jau eksistē un to nevar aizvietot ar jaunu.
-Ja tu joprojām gribi augšuplādēt šo failu, tad mēģini vēlreiz, ar citu faila vārdu. [[File:$1|thumb|center|$1]]',
+Ja jūs joprojām gribat augšupielādēt šo failu, tad mēģiniet vēlreiz ar citu faila nosaukumu.
+[[File:$1|thumb|center|$1]]',
 'file-exists-duplicate' => 'Fails ir kopija {{PLURAL:$1|šim failam|šiem failiem}}:',
 'uploadwarning' => 'Augšupielādes brīdinājums',
 'uploadwarning-text' => 'Lūdzu, pārveido zemāk esošo faila aprakstu un mēģini vēlreiz.',
@@ -1441,8 +1444,8 @@ Java failu augšupielāde nav atļauta, jo tas var radīt iespējas apiet droš
 'upload-description' => 'Faila apraksts',
 'upload-options' => 'Augšupielādes iestatījumi',
 'watchthisupload' => 'Uzraudzīt šo failu',
-'filewasdeleted' => 'Fails ar šādu nosaukumu jau ir bijis augšuplādēts un pēc tam izdzēsts.
-Apskaties $1 pirms turpini šo failu augšuplādēt atkārtoti.',
+'filewasdeleted' => 'Fails ar šādu nosaukumu jau ir bijis augšupielādēts un pēc tam izdzēsts.
+Apskatiet $1 pirms turpiniet šo failu augšupielādēt atkārtoti.',
 'filename-bad-prefix' => "Faila vārds failam, kuru tu mēģini augšpulādēt, sākas ar '''\"\$1\"''', kas ir neaprakstošs vārds, kādu parasti uzģenerē digitālais fotoaparāts.
 Lūdzu izvēlies aprakstošāku vārdu šim failam.",
 'upload-success-subj' => 'Augšupielāde veiksmīga',
@@ -2018,7 +2021,7 @@ Pašreizējie lapas '''$1''' iestatījumi ir:",
 'restriction-edit' => 'Izmainīt',
 'restriction-move' => 'Pārvietot',
 'restriction-create' => 'Izveidot',
-'restriction-upload' => 'Augšuplādēt',
+'restriction-upload' => 'Augšupielādēt',
 
 # Restriction levels
 'restriction-level-sysop' => 'pilnā aizsardzība',
@@ -2462,7 +2465,7 @@ Lūdzu, mēģiniet vēlreiz.',
 'tooltip-feed-atom' => 'Šīs lapas Atom barotne',
 'tooltip-t-contributions' => 'Apskatīt šā lietotāja ieguldījumu uzskaitījumu.',
 'tooltip-t-emailuser' => 'Sūtīt e-pastu šim lietotājam',
-'tooltip-t-upload' => 'Augšuplādēt attēlus vai multimēdiju failus',
+'tooltip-t-upload' => 'Augšupielādēt failus',
 'tooltip-t-specialpages' => 'Visu īpašo lapu uzskaitījums',
 'tooltip-t-print' => 'Drukājama lapas versija',
 'tooltip-t-permalink' => 'Paliekoša saite uz šo lapas versiju',
index ed3100a..5952fe5 100644 (file)
@@ -483,8 +483,8 @@ $messages = array(
 'histlegend' => "Таҥастарашлаш ӱлыл версийыште ойырымаш полдышым да Enter-ым темдал.<br />
 Умылтарымаш: (кызыт) = кызытсе версий деч ойыртем, (ончычсо) = ончычсо версий деч ойыртем, '''и''' = изи тӧрлатымаш.",
 'history-fieldset-title' => 'Эртымгорным ончыкташ',
-'histfirst' => 'Эн тошто',
-'histlast' => 'Эн у',
+'histfirst' => 'эн тошто',
+'histlast' => 'эн у',
 'historyempty' => '(яра)',
 
 # Revision feed
@@ -526,6 +526,8 @@ $messages = array(
 'notextmatches' => 'Лаштык-влакыште икгайлык возымо уке',
 'prevn' => 'кодшо {{PLURAL:$1|$1}}',
 'nextn' => 'весе {{PLURAL:$1|$1}}',
+'prevn-title' => 'Кодшо $1 {{PLURAL:$1|результат}}',
+'nextn-title' => 'Весе $1 {{PLURAL:$1|результат}}',
 'shown-title' => 'Лаштыкыште $1 {{PLURAL:$1|возымаш|возымашым}} ончыкташ',
 'viewprevnext' => 'Ончал ($1 {{int:pipe-separator}} $2) ($3)',
 'searchmenu-new' => "'''Тиде вики-проектыште «[[:$1]]» лӱман лаштыкым ышташ!'''",
@@ -549,6 +551,7 @@ $messages = array(
 'search-interwiki-more' => '(эше)',
 'searchrelated' => 'кылдалтше',
 'searchall' => 'чыла',
+'showingresultsheader' => "'''$4'''лан {{PLURAL:$5|'''$3''' гыч '''$1''' результат|'''$3''' гыч '''$1 - $2''' результат}}",
 'nonefound' => "'''Ешартыш''':  Посна палемдыме огыл гын, кычалмаш южо лӱм-влак коклаште гына эрта. Чыла лаштык-влак коклаште кычалашлан (каҥашымаш, ямдылык-влак да т.м.) шке йодмашыштет ''all:'' префиксым кучылт, але кӱлешан лӱм-влакым палемде.",
 'search-nonefound' => 'Тыйын йодышет почеш нимо муалтын огыл',
 'powersearch' => 'Сайынрак кычал',
@@ -661,7 +664,7 @@ $messages = array(
 'minoreditletter' => 'и',
 'newpageletter' => 'У',
 'boteditletter' => 'б',
-'rc-enhanced-expand' => 'Ð\9fоказаÑ\82Ñ\8c Ð´ÐµÑ\82али  (JavaScript ÐºÓ±Ð»ÐµÑ\88)',
+'rc-enhanced-expand' => 'ТиÑ\87маÑ\88Ñ\8bн Ð¾Ð½Ñ\87Ñ\8bкÑ\82аÑ\88',
 'rc-enhanced-hide' => 'Рашлык-влакым шылташ',
 
 # Recent changes linked
@@ -793,6 +796,7 @@ $messages = array(
 'linksearch' => 'Ӧрдыж кылвер-влак',
 'linksearch-ns' => 'Лӱм-влакын кумдыкышт:',
 'linksearch-ok' => 'Кычал',
+'linksearch-line' => '$2 лаштыкыште $1 ончыкталтын',
 
 # Special:ListUsers
 'listusers-submit' => 'ончыкташ',
@@ -821,7 +825,7 @@ $messages = array(
 'watchthispage' => 'Тиде лаштыкым эскераш',
 'unwatch' => 'Эскерыман огыл',
 'unwatchthispage' => 'Эскерымым чарнаш',
-'watchlist-details' => 'Эскерымаш лӱмерыштет $1 {{PLURAL:$1|лаштык|лаштык}} (каҥашымаш лаштык-влакым шотлыде)',
+'watchlist-details' => 'Эскерымаш лӱмерыштет $1 {{PLURAL:$1|лаштык}}, каҥашымаш лаштык-влакым шотлыде',
 'watchlistcontains' => 'Тыйын лӱмерыште $1 {{PLURAL:$1|лаштык|лаштык}}.',
 'wlshowlast' => 'Пытартыш $1 шагат $2 кечылан $3 ончыкташ',
 'watchlist-options' => 'Эскерыме лӱмерын келыштарымаш',
@@ -877,6 +881,7 @@ $messages = array(
 
 # Undelete
 'undeletelink' => 'ончалаш/тӧрлатен шындаш',
+'undeleteviewlink' => 'ончыкташ',
 'undelete-search-submit' => 'Кычал',
 
 # Namespace form on various pages
@@ -984,6 +989,7 @@ $messages = array(
 
 # Thumbnails
 'thumbnail-more' => 'Кугемдаш',
+'thumbnail_error' => 'Изи сӱретым ыштыме годым йоҥылыш: $1',
 
 # Tooltip help for the actions
 'tooltip-pt-userpage' => 'Тыйын лаштыкет',
index 7931083..d467be4 100644 (file)
@@ -2591,10 +2591,12 @@ $UNWATCHURL
 'deletecomment' => 'Причина:',
 'deleteotherreason' => 'Друга/дополнителна причина:',
 'deletereasonotherlist' => 'Друга причина',
-'deletereason-dropdown' => '*Вообичаени причини за бришење
-** На барање на авторот
+'deletereason-dropdown' => '* Вообичаени причини за бришење
+** Спам
+** Вандализам
 ** Прекршување на авторски права
-** Вандализам',
+** На барање на авторот
+** Прекинато пренасочување',
 'delete-edit-reasonlist' => 'Уреди причини за бришење',
 'delete-toobig' => 'Оваа страница има долга историја на уредување, преку $1 {{PLURAL:$1|ревизија|ревизии}}.
 Бришењето на ваквии страници е забрането со цел {{SITENAME}} да се заштити од оштетувања.',
index 11c4d6f..4c6ba0e 100644 (file)
@@ -762,6 +762,9 @@ Vergeet niet joew [[Special:Preferences|veurkeuren veur {{SITENAME}}]] an te pas
 'userlogin-resetpassword-link' => 'Joew wachtwoord opniej instellen',
 'helplogin-url' => 'Help:Anmelden',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Hulpe bie t anmelden]]',
+'userlogin-loggedin' => 'Je bin al an-emeld as {{GENDER:$1|$1}}.
+Gebruuk t onderstaonde formulier um an te melden as n aandere gebruker.',
+'userlogin-createanother' => 'n Aandere gebrukerskonto anmaken',
 'createacct-join' => 'Geef joew gegevens hieronder op.',
 'createacct-another-join' => 'Vul hieronder de informasie van de nieje gebruker in.',
 'createacct-emailrequired' => 'Netpostadres',
index 46a975c..5e17a4d 100644 (file)
@@ -2630,9 +2630,11 @@ Zie het $2 voor een overzicht van recente verwijderingen.',
 'deleteotherreason' => 'Andere reden:',
 'deletereasonotherlist' => 'Andere reden',
 'deletereason-dropdown' => '*Veel voorkomende verwijderredenen
-** Op aanvraag van auteur
+** Spam
+** Vandalisme
 ** Schending van auteursrechten
-** Vandalisme',
+** Op aanvraag van auteur
+** Kapotte doorverwijzing',
 'delete-edit-reasonlist' => 'Redenen voor verwijderen bewerken',
 'delete-toobig' => "Deze pagina heeft een lange bewerkingsgeschiedenis, meer dan $1 {{PLURAL:$1|versie|versies}}.
 Het verwijderen van dit soort pagina's is met rechten beperkt om het per ongeluk verstoren van de werking van {{SITENAME}} te voorkomen.",
index 64d0725..74eddc5 100644 (file)
@@ -3889,7 +3889,10 @@ Du skal ha motteke [{{SERVER}}{{SCRIPTPATH}}/COPYING ein kopi av GNU General Pub
 'tags-tag' => 'Merkenamn',
 'tags-display-header' => 'Utsjånad på endringslister',
 'tags-description-header' => 'Tyding',
+'tags-active-header' => 'Verksamt?',
 'tags-hitcount-header' => 'Merkte endringar',
+'tags-active-yes' => 'Ja',
+'tags-active-no' => 'Nei',
 'tags-edit' => 'endra',
 'tags-hitcount' => '{{PLURAL:$1|éi endring|$1 endringar}}',
 
index 850153d..cb687eb 100644 (file)
@@ -672,6 +672,8 @@ $2',
 ନିଜର [[Special:Preferences|{{SITENAME}} ପସନ୍ଦସବୁକୁ]] ବଦଳାଇବାକୁ ଭୁଲିବେ ନାହିଁ ।',
 'yourname' => 'ବ୍ୟବହାରକାରୀଙ୍କ ନାମ:',
 'userlogin-yourname' => 'ବ୍ୟବହାରକାରୀଙ୍କ ନାମ',
+'userlogin-yourname-ph' => 'ଆପଣଙ୍କ ଇଉଜର ନାମ ଟାଇପ କରନ୍ତୁ',
+'createacct-another-username-ph' => 'ଆପଣଙ୍କ ଇଉଜର ନାମ ଟାଇପ କରନ୍ତୁ',
 'yourpassword' => 'ପାସୱାର୍ଡ଼',
 'userlogin-yourpassword' => 'ପାସୱାର୍ଡ଼',
 'userlogin-yourpassword-ph' => 'ଆପଣଙ୍କ ପାସୱାର୍ଡ଼ ନିବେଶ କରନ୍ତୁ',
@@ -680,6 +682,8 @@ $2',
 'createacct-yourpasswordagain' => 'ପାସୱର୍ଡ଼ ନିଶ୍ଚିତ କରିବେ',
 'createacct-yourpasswordagain-ph' => 'ପୁଣି ପାସୱର୍ଡ଼ ନିବେଶ କରନ୍ତୁ',
 'remembermypassword' => 'ଏହି ବ୍ରାଉଜରରେ (ସବୁଠୁ ଅଧିକ ହେଲେ $1 {{PLURAL:$1|day|ଦିନ}}) ପାଇଁ ମୋ ଲଗଇନ ମନେ ରଖିଥିବେ',
+'userlogin-remembermypassword' => 'ମୋତେ ଲଗ-ଇନ କରି ରଖିଥାନ୍ତୁ',
+'userlogin-signwithsecure' => 'ନିରାପଦ କନେକସନ ବ୍ୟବ‌ହାର କରନ୍ତୁ',
 'yourdomainname' => 'ଆପଣଙ୍କ ଡୋମେନ:',
 'password-change-forbidden' => 'ଆପଣ ଏହି ଉଇକିରେ ପାସୱାର୍ଡ ବଦଳାଇ ପାରିବେ ନାହିଁ ।',
 'externaldberror' => 'ବୋଧ ହୁଏ ଚିହ୍ନଟ ଡାଟାବେସ ଭୁଲଟିଏ ହୋଇଥିଲା ବା ଆପଣଙ୍କୁ ନିଜର ବାହାର ଖାତା ଅପଡେଟ କରିବା ନିମନ୍ତେ ଅନୁମତି ମିଳିନାହିଁ ।',
@@ -692,20 +696,30 @@ $2',
 'userlogout' => 'ଲଗ ଆଉଟ',
 'notloggedin' => 'ଲଗ‌‌ ଇନ କରିନାହାନ୍ତି',
 'userlogin-noaccount' => 'ଖାତାଟିଏ ନାହିଁ?',
+'userlogin-joinproject' => '{{SITENAME}}ରେ ଯୋଗଦିଅନ୍ତୁ',
 'nologin' => 'ଖାତାଟିଏ ନାହିଁ? $1।',
 'nologinlink' => 'ନୂଆ ଖାତାଟିଏ ଖୋଲନ୍ତୁ',
 'createaccount' => 'ନୂଆ ଖାତାଟିଏ ଖୋଲନ୍ତୁ',
 'gotaccount' => 'ଆଗରୁ ଖାତାଟିଏ ଅଛି କି? $1.',
 'gotaccountlink' => 'ଲଗ ଇନ (Log in)',
 'userlogin-resetlink' => 'ଲଗଇନ ତଥ୍ୟ ସବୁ ଭୁଲିଗେଲେକି?',
+'userlogin-resetpassword-link' => 'ପାସୱାର୍ଡ଼ ରିସେଟ କରନ୍ତୁ',
+'helplogin-url' => 'Help:Logging_in',
+'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|ଲଗ-ଇନ କରିବାରେ ସ‌ହ‌ଯୋଗ]]',
 'createacct-emailrequired' => 'ଇମେଲ ଠିକଣା',
 'createacct-emailoptional' => 'ଇମେଲ ଠିକଣା (ଇଚ୍ଛାଧୀନ)',
 'createacct-email-ph' => 'ଆପଣଙ୍କ ଇମେଲ ଠିକଣା ନିବେଶ କରନ୍ତୁ',
-'createaccountmail' => 'ଗୋଟିଏ ସାମୟିକ ଜାହିତାହି ପାସୱାର୍ଡ ବ୍ୟବହାର କରନ୍ତୁ ଏବଂ ଏହାକୁ ତଳେ ଦିଆଯାଇଥିବା ଇ-ମେଲ ଠିକଣାକୁ ପଠେଇ ଦିଅନ୍ତୁ',
+'createacct-another-email-ph' => 'ଆପଣଙ୍କ ଇ-ମେଲ ଠିକଣା ଦିଅନ୍ତୁ',
+'createaccountmail' => 'ଏକ ଅସ୍ଥାୟୀ ପାସୱାର୍ଡ଼ ବ୍ୟବହାର କରନ୍ତୁ ଏବଂ ଏହାକୁ ତଳେ ଦିଆଯାଇଥିବା ଇ-ମେଲ ଠିକଣାକୁ ପଠେଇ ଦିଅନ୍ତୁ',
 'createacct-realname' => 'ପ୍ରକୃତ ନାମ (ଇଚ୍ଛାଧୀନ)',
 'createaccountreason' => 'କାରଣ:',
 'createacct-reason' => 'କାରଣ',
 'createacct-reason-ph' => 'ଆପଣ ଅନ୍ୟଏକ ଖାତା କାହିଁକି ତିଆରି କରୁଛନ୍ତି',
+'createacct-imgcaptcha-ph' => 'ଉପରେ ଲେଖାଥିବା ଲେଖାଟି ଲେଖନ୍ତୁ',
+'createacct-submit' => 'ନିଜର ନୂଆ ଖାତାଟିଏ ଖୋଲନ୍ତୁ',
+'createacct-another-submit' => 'ଆଉ ଏକ ଖାତା ଖୋଲନ୍ତୁ',
+'createacct-benefit-heading' => '{{SITENAME}} ଆପଣଙ୍କ ଭଳି ଲୋକମାନଙ୍କ ଦ୍ୱାରା ଗଢ଼ା ।',
+'createacct-benefit-body1' => '{{PLURAL:$1|ସମ୍ପାଦନା|ସମ୍ପାଦନାମାନ}}',
 'badretype' => 'ଆପଣ ଦେଇଥିବା ପାସବାର୍ଡ଼ଟି ମେଳଖାଉନାହିଁ ।',
 'userexists' => 'ଆପଣ ଦେଇଥିବା ଇଉଜର ନାମ ଆଗରୁ ଅଛି ।
 ଦୟାକରି ଅଲଗା ନାମଟିଏ ବାଛନ୍ତୁ ।',
index a00080c..839037b 100644 (file)
@@ -907,13 +907,13 @@ Być może właśnie zmienił{{GENDER:|eś|aś|eś(‐aś)}} swoje hasło lub po
 $2
 
 {{PLURAL:$3|Tymczasowego hasła|Tymczasowych haseł}} można użyć w ciągu {{PLURAL:$5|jednego dnia|$5 dni}}.
-Powinieneś zalogować się i zmienić hasło na nowe. Jeśli to ktoś inni poprosił o wysłanie przypomnienia lub jeśli pamiętasz aktualne hasło i nie chce go zmieniać wystarczy, że zignorujesz tę wiadomość i będziesz nadal korzystał ze swojego starego hasła.',
+Powinieneś zalogować się i zmienić hasło na nowe. Jeśli to ktoś inny poprosił o wysłanie przypomnienia lub jeśli pamiętasz aktualne hasło i nie chcesz go zmieniać wystarczy, że zignorujesz tę wiadomość i będziesz nadal korzystać ze swojego starego hasła.',
 'passwordreset-emailtext-user' => 'Użytkownik $1 poprosił o zresetowanie twojego hasła w {{GRAMMAR:MS.lp{{SITENAME}}}} ($4). Z tym adresem e‐mailowym powiązane {{PLURAL:$3|jest konto użytkownika|są następujące konta użytkowników:}}
 
 $2
 
 {{PLURAL:$3|Tymczasowego hasła|Tymczasowych haseł}} można użyć w ciągu {{PLURAL:$5|jednego dnia|$5 dni}}.
-Powinieneś zalogować się i zmienić hasło na nowe. Jeśli to ktoś inni poprosił o wysłanie przypomnienia lub jeśli pamiętasz aktualne hasło i nie chce go zmieniać wystarczy, że zignorujesz tę wiadomość i będziesz nadal korzystał ze swojego starego hasła.',
+Powinieneś zalogować się i zmienić hasło na nowe. Jeśli to ktoś inny poprosił o wysłanie przypomnienia lub jeśli pamiętasz aktualne hasło i nie chcesz go zmieniać wystarczy, że zignorujesz tę wiadomość i będziesz nadal korzystać ze swojego starego hasła.',
 'passwordreset-emailelement' => 'Nazwa użytkownika – $1
 Tymczasowe hasło – $2',
 'passwordreset-emailsent' => 'E‐mail pozwalający na zresetowanie hasła został wysłany.',
@@ -2518,10 +2518,12 @@ Zobacz na stronie $2 rejestr ostatnio wykonanych usunięć.',
 'deletecomment' => 'Powód',
 'deleteotherreason' => 'Inny lub dodatkowy powód:',
 'deletereasonotherlist' => 'Inny powód',
-'deletereason-dropdown' => '* Najczęstsze powody usunięcia
+'deletereason-dropdown' => '* Najczęstsze przyczyny usunięcia
+** Spam
+** Wandalizm
+** Naruszenia praw autorskich
 ** Prośba autora
-** Naruszenie praw autorskich
-** Wandalizm',
+** Zerwane przekierowanie',
 'delete-edit-reasonlist' => 'Edytuj listę przyczyn usunięcia',
 'delete-toobig' => 'Ta strona ma bardzo długą historię edycji – ponad $1 {{PLURAL:$1|zmianę|zmiany|zmian}}.<br />
 Usuwanie jej zostało ograniczone ze względu na możliwość zakłócenia pracy {{GRAMMAR:D.lp|{{SITENAME}}}}.',
index 547135c..008c59d 100644 (file)
@@ -779,44 +779,45 @@ Sòn a l'é normalment causà da l'andèje dapress a na vej liura stòrica a na
 * '''Internet Explorer:''' Che a ten-a sgnacà ''Ctrl'' antramentre che a sgnaca col rat ansima a ''Agiorné'', ò pura che a sgnaca tut ansema ''Ctrl-F5''
 *'''Opera''' Ch'a dësveuida soa memorisassion andrinta a ''Utiss → Gust''",
 'usercssyoucanpreview' => "'''Drita:''' che a deuvra ël boton «{{int:showpreview}}» për controlé l'efet ëd sò neuv còdes CSS dnans ëd salvelo.",
-'userjsyoucanpreview' => "'''Drita:''' che a deuvra ël boton «{{int:showpreview}}» për controlé l'efet ëd sò còdes JS dnans ëd salvelo.",
-'usercsspreview' => "'''Che a varda che lòn che a s-ciàira a l'é nomach na preuva ëd sò CSS.'''
+'userjsyoucanpreview' => "'''Drita:''' che a deuvra ël boton «{{int:showpreview}}» për controlé l'efet ëd sò neuv còdes JavaScript dnans ëd salvelo.",
+'usercsspreview' => "'''Che a varda che lòn che a s-ciàira a l'é nomach na preuva ëd sò feuj CSS.'''
 '''A l'é ancó nen stàit salvà!'''",
-'userjspreview' => "'''Che as visa che a l'é mach antramentr che as fa na preuva ëd sò Javascript, che a l'é ancó pa stàit salvà!'''",
-'sitecsspreview' => "'''Che a varda che a l'é mach an mente ch'a preuva sto CSS.'''
-'''A l'é ancó pa stàit salvà!'''",
-'sitejspreview' => "'''Che a varda che a l'é mach an mente ch'a preuva sto còdes JavaScript.'''
-'''A l'é ancó pa stàit salvà!'''",
-'userinvalidcssjstitle' => "'''Avis:''' A-i é pa gnun-a pel \"\$1\". Che as visa che le pàgine .css e .js che un as fa daspërchiel a deuvro tute minùscole për tìtol, pr'esempi {{ns:user}}:Scaramacaj/vector.css nopà che {{ns:user}}:Scaramacaj/Vector.css.",
+'userjspreview' => "'''Che as visa che a l'é mach antramentre che as fa na preuva ëd sò còdes Javascript e che a l'é ancó pa stàit salvà!'''",
+'sitecsspreview' => "'''Che a varda che a l'é mach an camin ch'a preuva cost CSS.'''
+'''A l'é pa ancora stàit salvà!'''",
+'sitejspreview' => "'''Che a varda che a l'é mach an camin ch'a preuva cost còdes JavaScript.'''
+'''A l'é pa ancora stàit salvà!'''",
+'userinvalidcssjstitle' => "'''Atension:''' A-i é gnun-a pel «$1». Che as visa che le pàgine .css e .js che un as fa daspërchiel a deuvro tute minùscole për tìtol, pr'esempi {{ns:user}}:Scaramacaj/vector.css nopà che {{ns:user}}:Scaramacaj/Vector.css.",
 'updated' => '(Agiornà)',
-'note' => "'''NÃ\92TA:'''",
-'previewnote' => "'''Che a ten-a present che costa-sì a l'é mach na preuva.'''
-Ij sò cambi a son anco' pa stàit salvà!",
+'note' => "'''Nòta:'''",
+'previewnote' => "'''Che a ten-a da ment che costa-sì a l'é mach na preuva.'''
+Soe modìfiche a son pa ancora stàite salvà!",
 'continue-editing' => 'Andé a la zòna ëd modìfica',
-'previewconflict' => "Costa preuva a-j mostra ël test dl'artìcol ambelessì dzora. Se a sërn dë salvelo, a l'é parèj che a lo s-ciairëran ëdcò tuti j'àutri Utent.",
-'session_fail_preview' => "'''Darmagi! I l'oma pa podù processé soa modìfica per via che a son përdusse për la stra ij dat ëd session.
-Për piasì che a preuva n'àutra vira. Se a dovèissa mai torna riveje sossì, che a preuva a seurte dal sistema e peuj torna a rintré.'''",
+'previewconflict' => "Costa preuva a mostra ël test dl'artìcol ambelessì-dzora. Se a sern dë salvelo, a l'é parèj che a lo s-ciairëran ëdcò tuti j'àutri Utent.",
+'session_fail_preview' => "'''Darmagi! I l'oma pa podù processé soa modìfica per via che a son përdusse për la stra ij dat ëd session.'''
+Për piasì che a preuva n'àutra vira. Se a dovèissa torna nen marcé, che a preuva a [[Special:UserLogout|seurte dal sistema]] e peuj torna a rintré.",
 'session_fail_preview_html' => "'''Darmagi! I l'oma nen podù processé soa modìfica ën essend che a son përdusse për la stra ij dat ëd session.'''
 
 ''Për via che {{SITENAME}} a lassa mostré còdes HTML nen filtrà, la preuva a l'é stërmà coma precaussion contra a dij possìbij atach fàit an Javascript.''
 
-'''Se sòn a l'era na modìfica normal, për piasì che a preuva a fela n'àutra vira. Se a dovèissa mai torna deje dle gran-e, che a preuva a [[Special:UserLogout|seurte da 'nt ël sistema]] e peuj torna a rintré.'''",
+'''Se costa a l'era na modìfica normal, për piasì che a preuva a fela n'àutra vira. Se a dovèissa mai torna deje dle gran-e, che a preuva a [[Special:UserLogout|seurte da 'nt ël sistema]] e peuj torna a rintré.'''",
 'token_suffix_mismatch' => "'''Soa modìfica a l'é nen stàita acetà përché sò navigator a l'hai fàit ciadel con ij pont e le vìrgole
-ant ël quàder ëd modìfica. La rason che a l'é nen stàit acetà a l'é për evité ch'a-i fasa darmagi al
-test ch'a-i é già. Sossì dle vire a riva quand un a deuvra un programa proxy ëd coj un pòch dla Bajòna.'''",
-'edit_form_incomplete' => "'''Quàich part dël formolari ëd modìfica a l'é pa rivà al sërvent; contròla doe vire che toe modìfiche a-i sio anco' e preuva torna.'''",
+ant ël quàder ëd modìfica.'''
+La rason che a l'é nen stàit acetà a l'é për evité ch'a-j fasa darmagi al
+test ch'a-i é già. Sossì dle vire a riva quand un a deuvra un servent anònim an sl'Aragnà ëd coj un pòch dla Bajòna.",
+'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.'''",
+'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 sta fasend-je dle modìfiche a 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',
@@ -2246,9 +2247,11 @@ Che a varda $2 për na lista dle pàgine scancelà ant j'ùltim temp.",
 'deleteotherreason' => 'Rason àutra/adissional:',
 'deletereasonotherlist' => 'Àutra rason',
 'deletereason-dropdown' => "*Rason sòlite ch'a së scancela la ròba
-** A lo ciama l'àutor
+** Rumenta
+** Vandalism
 ** Violassion dij drit d'autor
-** Vandalism",
+** A lo ciama l'àutor
+** Ridiression cioca",
 'delete-edit-reasonlist' => 'Modifiché la rason dlë scancelament',
 'delete-toobig' => "Sta pàgina-sì a l'ha na stòria motobin longa, bele pì che $1 {{PLURAL:$1|revision|revision}}.
 Lë scancelassion ëd pàgine parèj a l'é stàita limità për evité ch'as fasa darmagi për eror a {{SITENAME}}.",
@@ -3055,7 +3058,7 @@ An fasend-lo marcé ansima a sò ordinator chiel a podrìa porteje ëd dann a s
 
 # Human-readable timestamps
 'minutes-ago' => '$1 {{PLURAL:$1|minuta|minute}} fa',
-'seconds-ago' => '$1 {{PLURAL:$1|second|second}} fa',
+'seconds-ago' => '$1 {{PLURAL:$1second}} fa',
 'monday-at' => 'Lùn-es a $1',
 'tuesday-at' => 'Màrtes a $1',
 'wednesday-at' => 'Merco a $1',
@@ -3819,23 +3822,26 @@ A dovrìa avèj arseivù [{{SERVER}}{{SCRIPTPATH}}/COPYING na còpia dla Licensa
 'htmlform-submit' => 'Mandé',
 'htmlform-reset' => 'Gavé le modìfiche',
 'htmlform-selectorother-other' => 'Àutr',
+'htmlform-no' => 'Nò',
+'htmlform-yes' => 'É',
+'htmlform-chosen-placeholder' => "Serne n'opsion",
 
 # SQLite database support
 'sqlite-has-fts' => '$1 con arserca an test pien mantnùa',
 'sqlite-no-fts' => '$1 sensa arserca an test pien mantnùa',
 
 # New logging system
-'logentry-delete-delete' => "$1 a l'ha scancelà la pàgina $3",
-'logentry-delete-restore' => "$1 a l'ha ripristinà la pàgina $3",
-'logentry-delete-event' => "$1 a l'ha modificà la visibilità ëd {{PLURAL:$5|n'event dël registr|$5 event dël registr}} dzora $3: $4",
-'logentry-delete-revision' => "$1 a l'ha modificà la visibilità ëd {{PLURAL:$5|na revision|$5 revision}} dzora la pàgina $3: $4",
-'logentry-delete-event-legacy' => "$1 a l'ha modificà la visibilità dj'eveniment dël registr dzora $3",
-'logentry-delete-revision-legacy' => "$1 a l'ha modificà la visibilità dle revision dzora la pàgina $3",
-'logentry-suppress-delete' => "$1 a l'ha eliminà la pàgina $3",
-'logentry-suppress-event' => "$1 a l'ha modificà segretament la visibilità ëd {{PLURAL:$5|n'eveniment dël registr|$5 eveniment dël registr}} dzora $3: $4",
-'logentry-suppress-revision' => "$1 a l'ha modificà segretament la visibilità ëd {{PLURAL:$5|na revision|$5 revision}} dzora la pàgina $3: $4",
-'logentry-suppress-event-legacy' => "$1 l'ha modificà segretament la visibilità dj'evenimentt dël registr dzora $3",
-'logentry-suppress-revision-legacy' => "$1 a l'ha modificà segretament la visibilità dle revision dzora la pàgina $3",
+'logentry-delete-delete' => "$1 a l'ha {{GENDER:$2|scancelà}} la pàgina $3",
+'logentry-delete-restore' => "$1 {{GENDER:$2|a l'ha ripristinà}} la pàgina $3",
+'logentry-delete-event' => "$1 {{GENDER:$2|a l'ha modificà}} la visibilità ëd {{PLURAL:$5|n'event dël registr|$5 event dël registr}} dzora $3: $4",
+'logentry-delete-revision' => "$1 {{GENDER:$2|a l'ha modificà}} la visibilità ëd {{PLURAL:$5|na revision|$5 revision}} dzora la pàgina $3: $4",
+'logentry-delete-event-legacy' => "$1 {{GENDER:$2|a l'ha modificà}} la visibilità dj'eveniment dël registr dzora $3",
+'logentry-delete-revision-legacy' => "$1 {{GENDER:$2|a l'ha modificà}} la visibilità dle revision dzora la pàgina $3",
+'logentry-suppress-delete' => "$1 {{GENDER:$2|a l'ha eliminà}} la pàgina $3",
+'logentry-suppress-event' => "$1 {{GENDER:$2|a l'ha modificà}} da stërmà la visibilità ëd {{PLURAL:$5|n'eveniment dël registr|$5 eveniment dël registr}} dzora $3: $4",
+'logentry-suppress-revision' => "$1 {{GENDER:$2|a l'ha modificà}} da stërmà la visibilità ëd {{PLURAL:$5|na revision|$5 revision}} dzora la pàgina $3: $4",
+'logentry-suppress-event-legacy' => "$1 {{GENDER:$2|a l'ha modificà}} da stërmà la visibilità dj'eveniment dël registr dzora $3",
+'logentry-suppress-revision-legacy' => "$1 {{GENDER:$2|a l'ha modificà}} da stërmà la visibilità dle revision dzora la pàgina $3",
 'revdelete-content-hid' => 'contnù stërmà',
 'revdelete-summary-hid' => 'resumé dle modìfiche stërmà',
 'revdelete-uname-hid' => 'stranòm stërmà',
@@ -3844,19 +3850,20 @@ A dovrìa avèj arseivù [{{SERVER}}{{SCRIPTPATH}}/COPYING na còpia dla Licensa
 'revdelete-uname-unhid' => 'stranòm dëscoatà',
 'revdelete-restricted' => "restrission aplicà a j'aministrator",
 'revdelete-unrestricted' => "restrission për j'aministrator gavà",
-'logentry-move-move' => "$1 a l'ha tramudà la pàgina $3 a $4",
-'logentry-move-move-noredirect' => "$1 a l'ha tramudà la pàgina $3 a $4 sensa lassé na ridiression",
-'logentry-move-move_redir' => "$1 a l'ha tramudà la pàgina $3 a $4 ansima a na ridiression",
-'logentry-move-move_redir-noredirect' => "$1 a l'ha tramudà la pàgina $3 a $4 ansima a na ridiression sensa lassé na ridiression",
-'logentry-patrol-patrol' => "$1 a l'ha marcà la revision $4 dla pàgina $3 'me controlà",
-'logentry-patrol-patrol-auto' => "$1 a l'ha marcà automaticament la revision $4 dla pàgina $3 'me controlà",
-'logentry-newusers-newusers' => "Ël cont utent $1 a l'é stàit creà",
-'logentry-newusers-create' => "Ël cont utent $1 a l'é stàit creà",
-'logentry-newusers-create2' => "Ël cont utent $3 a l'é stàit creà da $1",
-'logentry-newusers-autocreate' => "Ël cont $1 a l'é stàit creà an automàtich",
-'logentry-rights-rights' => "$1 a l'ha tramudà l'apartenesa a la partìa për $3 da $4 a $5",
-'logentry-rights-rights-legacy' => "$1 a l'ha tramudà l'apartenensa a la partìa për $3",
-'logentry-rights-autopromote' => "$1 a l'é stàit automaticament promovù da $4 a $5",
+'logentry-move-move' => "$1 {{GENDER:$2|a l'ha tramudà}} la pàgina $3 a $4",
+'logentry-move-move-noredirect' => "$1 {{GENDER:$2|a l'ha tramudà}} la pàgina $3 a $4 sensa lassé na ridiression",
+'logentry-move-move_redir' => "$1 {{GENDER:$2|a l'ha tramudà}} la pàgina $3 a $4 ansima a na ridiression",
+'logentry-move-move_redir-noredirect' => "$1 {{GENDER:$2|a l'ha tramudà}} la pàgina $3 a $4 ansima a na ridiression sensa lassé na ridiression",
+'logentry-patrol-patrol' => "$1 {{GENDER:$2|a l'ha marcà}} la revision $4 dla pàgina $3 'me controlà",
+'logentry-patrol-patrol-auto' => "$1 {{GENDER:$2|a l'ha marcà}} an automàtich la revision $4 dla pàgina $3 'me controlà",
+'logentry-newusers-newusers' => "Ël cont utent $1 {{GENDER:$2|a l'é stàit creà}}",
+'logentry-newusers-create' => "Ël cont utent $1 {{GENDER:$2|a l'é stàit creà}}",
+'logentry-newusers-create2' => "Ël cont utent $3 {{GENDER:$2|a l'é stàit creà}} da $1",
+'logentry-newusers-byemail' => "Ël cont utent $3 a l'é {{GENDER:$2|stàit creà}} da $1 e la ciav a l'é stàita mandà për pòsta eletrònica",
+'logentry-newusers-autocreate' => "Ël cont $1 a l'é {{GENDER:$2|stàit creà}} an automàtich",
+'logentry-rights-rights' => "$1 {{GENDER:$2|a l'ha modificà}} l'apartenensa a la partìa për $3 da $4 a $5",
+'logentry-rights-rights-legacy' => "$1 {{GENDER:$2|a l'ha modificà}} l'apartenensa a la partìa për $3",
+'logentry-rights-autopromote' => "$1 a l'é {{GENDER:$2|stàit promovù}} an automàtich da $4 a $5",
 'rightsnone' => '(gnun)',
 
 # Feedback
@@ -3911,6 +3918,7 @@ Dësnò, a peul dovré ël formolari semplificà sì-sota. Sò coment a sarà gi
 'api-error-ok-but-empty' => 'Eror antern: Gnun-a rispòsta dal servent.',
 'api-error-overwrite' => "Dzorascrive ansima a n'archivi esistent a l'é nen përmëttù.",
 'api-error-stashfailed' => "Eror antern: ël servent a l'ha pa podù memorisé l'archivi a temp.",
+'api-error-publishfailed' => "Eror antern: Ël servent a l'ha pa podù publiché l'archivi provisòri.",
 'api-error-timeout' => "Ël servent a l'ha pa rëspondù ant ël temp ëspetà.",
 'api-error-unclassified' => "A l'é capitaje n'eror nen conossù.",
 'api-error-unknown-code' => 'Eror sconossù: «$1».',
@@ -3931,4 +3939,22 @@ Dësnò, a peul dovré ël formolari semplificà sì-sota. Sò coment a sarà gi
 'duration-centuries' => '$1 {{PLURAL:$1|sécol|sécoj}}',
 'duration-millennia' => '$1 {{PLURAL:$1|milenari|milenari}}',
 
+# Image rotation
+'rotate-comment' => 'Plancià virà ëd $1 {{PLURAL:$1|gre}} an sens orari',
+
+# Limit report
+'limitreport-title' => "Dàit d'otimisassion ëd l'analisator:",
+'limitreport-cputime' => "Temp CPU d'utilisassion",
+'limitreport-cputime-value' => '$1 {{PLURAL:$1|second}}',
+'limitreport-walltime' => "Temp real d'utilisassion",
+'limitreport-walltime-value' => '$1 {{PLURAL:$1|second}}',
+'limitreport-ppvisitednodes' => 'Cont dij neu ëd prepocessor visità',
+'limitreport-ppgeneratednodes' => 'Cont dij neu ëd preprocessor generà',
+'limitreport-postexpandincludesize' => "Taja d'anclusion dòp espansion",
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
+'limitreport-templateargumentsize' => "Taja dl'argoment ëd lë stamp",
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|byte|bytes}}',
+'limitreport-expansiondepth' => "Pi granda përfondità d'espansion",
+'limitreport-expensivefunctioncount' => "Cont ëd le fonsion d'anàlisi care",
+
 );
index e790e10..378334f 100644 (file)
@@ -165,7 +165,7 @@ $messages = array(
 'tog-hidepatrolled' => 'په وروستيو بدلونونو کې څارل شوې سمونونه پټول',
 'tog-newpageshidepatrolled' => 'د نوؤ مخونو په لړليک کې کتل شوي مخونه پټول',
 'tog-extendwatchlist' => 'يوازې د وروستني بدلونونو د ښکاره کولو لپاره نه بلکه د ټولو بدلونونو د ښکاره کولو لپاره کتنلړ غځول',
-'tog-usenewrc' => 'په کتنلړ او وروستي بدلونو مخ باندې ډله ايز بدلونونه (جاوا سکرېپټ ته اړتيا ده)',
+'tog-usenewrc' => 'په کتنلړ او وروستي بدلونو مخ باندې ډله ايز بدلونونه',
 'tog-numberheadings' => 'د سرليکونو خپلکاره شمېرايښودنه',
 'tog-showtoolbar' => 'د سمون اوزارپټه ښکاره کول',
 'tog-editondblclick' => 'په دوه کلېک سره د مخونو سمون',
@@ -201,6 +201,7 @@ $messages = array(
 'tog-showhiddencats' => 'پټې وېشنيزې ښکاره کول',
 'tog-norollbackdiff' => 'پرشاتمبولو وروسته توپيرونه نه ښودل',
 'tog-useeditwarning' => 'کله چې يو سمون مخ څخه د بدلونونو د خوندي کولو پرته وځم خبر دې شم',
+'tog-prefershttps' => 'د ننوتلو پر مهال تل يوه خوندي اړيکتيا کارول',
 
 'underline-always' => 'تل',
 'underline-never' => 'هېڅکله',
@@ -976,6 +977,7 @@ $1',
 'compareselectedversions' => 'ټاکلې بڼې سره پرتلل',
 'showhideselectedversions' => 'ټاکلې بڼې ښکاره کول/پټول',
 'editundo' => 'ناکړ',
+'diff-empty' => '(بې توپيره)',
 'diff-multi' => ' د ({{PLURAL:$2| يو کارن|$2 کارنانو}} لخوا {{PLURAL:$1|يوه منځګړې بڼه|$1 منځګړې بڼې}}د  نه ده ښکاره شوې)',
 
 # Search results
@@ -1626,7 +1628,7 @@ $1',
 
 # Special:LinkSearch
 'linksearch' => 'د باندنيو تړنو پلټنه',
-'linksearch-pat' => 'د Ù¾Ù\84Ù¼Ù\86Û\90 Ù\85خبÛ\90Ù\84Ú«ه:',
+'linksearch-pat' => 'د Ù¾Ù\84Ù¼Ù\86Û\90 Ù\85خبÛ\90Ù\84Ú¯ه:',
 'linksearch-ns' => 'نوم-تشيال:',
 'linksearch-ok' => 'پلټل',
 'linksearch-line' => '$1 د $2 سره تړل شوی',
@@ -1890,8 +1892,8 @@ $UNWATCHURL  نه ليدنه وکړۍ
 'month' => 'له مياشتې د (او پخواني):',
 'year' => 'له کال د (او پخواني):',
 
-'sp-contributions-newbies' => 'د Ù\86Ù\88Ù\88 Ú«ڼونونو ونډې ښکاره کول',
-'sp-contributions-newbies-sub' => 'د Ù\86Ù\88Ù\88 Ú«ڼونونو لپاره',
+'sp-contributions-newbies' => 'د Ù\86Ù\88Ù\88 Ú¯ڼونونو ونډې ښکاره کول',
+'sp-contributions-newbies-sub' => 'د Ù\86Ù\88Ù\88 Ú¯ڼونونو لپاره',
 'sp-contributions-blocklog' => 'د بنديز يادښت',
 'sp-contributions-deleted' => 'ړنګې شوې ونډې',
 'sp-contributions-uploads' => 'پورته کېدنې',
@@ -2387,7 +2389,7 @@ $1',
 
 'exif-meteringmode-0' => 'ناجوت',
 'exif-meteringmode-1' => 'منځالی',
-'exif-meteringmode-5' => 'Ù\85خبÛ\90Ù\84Ú«ه',
+'exif-meteringmode-5' => 'Ù\85خبÛ\90Ù\84Ú¯ه',
 'exif-meteringmode-255' => 'نور',
 
 'exif-lightsource-0' => 'ناجوت',
index 214a5d8..f9fe2b2 100644 (file)
@@ -654,6 +654,7 @@ Encontra uma lista das páginas especiais válidas em [[Special:SpecialPages|{{i
 'databaseerror-text' => 'Ocorreu um erro na consulta à base de dados.
 Isto pode indicar um defeito no programa.',
 'databaseerror-textcl' => 'Ocorreu um erro na consulta à base de dados.',
+'databaseerror-query' => 'Consulta:$1',
 'databaseerror-error' => 'Erro: $1',
 'laggedslavemode' => "'''Aviso:''' A página pode não conter as atualizações mais recentes.",
 'readonly' => 'Base de dados bloqueada (limitada a leituras)',
@@ -1205,6 +1206,7 @@ Depois grave as alterações, para finalizar e desfazer a edição.',
 'undo-failure' => 'Não foi possível desfazer a edição por conflito com alterações intermédias.',
 'undo-norev' => 'Não foi possível desfazer a edição porque ela não existe ou foi apagada.',
 'undo-summary' => 'Desfeita a edição $1 de [[Special:Contributions/$2|$2]] ([[User talk:$2|Discussão]])',
+'undo-summary-username-hidden' => 'Desfazer a revisão  $1  por um usuário oculto',
 
 # Account creation failure
 'cantcreateaccounttitle' => 'Não é possível criar uma conta',
@@ -1551,11 +1553,12 @@ Esta operação não pode ser desfeita.',
 'badsig' => 'Assinatura inválida; verifique o código HTML utilizado.',
 'badsiglength' => 'A sua assinatura é demasiado longa.
 Não deverá conter mais de $1 {{PLURAL:$1|carácter|caracteres}}.',
-'yourgender' => 'Sexo:',
-'gender-unknown' => 'Não especificado',
-'gender-male' => 'Masculino',
-'gender-female' => 'Feminino',
-'prefs-help-gender' => 'Opcional: usado pelo programa para ajuste das mensagens ao género do utilizador.
+'yourgender' => 'Como prefere ser descrito?',
+'gender-unknown' => 'Prefiro não dizer',
+'gender-male' => 'Ele edita páginas wiki',
+'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.
@@ -1794,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',
@@ -2524,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.',
@@ -4206,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 1a72451..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',
@@ -1307,7 +1309,7 @@ Outros administradores no {{SITENAME}} continuarão podendo acessar ao conteúdo
 'revdelete-unsuppress' => 'Remover restrições das edições restauradas',
 'revdelete-log' => 'Motivo:',
 'revdelete-submit' => 'Aplicar {{PLURAL:$1|à revisão selecionada|às revisões selecionadas}}',
-'revdelete-success' => "'''A visibilidade da revisão foi definida com sucesso.'''",
+'revdelete-success' => "'''A visibilidade da revisão foi atualizada.'''",
 'revdelete-failure' => "'''A visibilidade da revisão não foi atualizada:'''
 $1",
 'logdelete-success' => "'''Visibilidade de evento definida com sucesso.'''",
@@ -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 46e6c67..81d6ae0 100644 (file)
@@ -2501,10 +2501,12 @@ Accesați $2 pentru o listă cu elementele recent șterse.',
 'deletecomment' => 'Motiv:',
 'deleteotherreason' => 'Motiv diferit/suplimentar:',
 'deletereasonotherlist' => 'Alt motiv',
-'deletereason-dropdown' => '*Motive uzuale
-** La cererea autorului
+'deletereason-dropdown' => '*Motive uzuale de ștergere
+** Spam
+** Vandalism
 ** Violarea drepturilor de autor
-** Vandalism',
+** La cererea autorului
+** Redirecționare nefuncțională',
 'delete-edit-reasonlist' => 'Modifică motivele ștergerii',
 'delete-toobig' => 'Această pagină are un istoric al modificărilor important, cu mai mult de $1 {{PLURAL:$1|versiune|versiuni|de versiuni}}.
 Ștergerea unei astfel de pagini a fost restricționată pentru a preveni apariția unor erori în {{SITENAME}}.',
@@ -3935,7 +3937,7 @@ Puteți folosi în schimb [[Special:EditWatchlist|editorul standard]].',
 'version-credits-summary' => 'Am dori să amintim următoarele persoane pentru contribuțiile aduse proiectului [[Special:Version|MediaWiki]].',
 'version-license-info' => 'MediaWiki este un software liber pe care îl puteți redistribui și/sau modifica sub termenii Licenței Publice Generale GNU publicată de Free Software Foundation – fie a doua versiune a acesteia, fie, la alegerea dumneavoastră, orice altă versiune ulterioară. 
 
-MediaWiki este distribuit în speranța că va fi folositor, dar FĂRĂ VREO GARANȚIE, nici măcar cea implicită de COMERCIALIZARE sau de ADAPTARE PENTRU UN UN SCOP ANUME. Vedeți Licența Publică Generală GNU pentru mai multe detalii. 
+MediaWiki este distribuit în speranța că va fi folositor, dar FĂRĂ VREO GARANȚIE, nici măcar cea implicită de COMERCIALIZARE sau de ADAPTARE PENTRU UN SCOP ANUME. Vedeți Licența Publică Generală GNU pentru mai multe detalii. 
 
 În cazul în care nu ați primit [{{SERVER}}{{SCRIPTPATH}}/COPYING o copie a  Licenței Publice Generale GNU] împreună cu acest program, scrieți la Free Software Foundation, Inc, 51, Strada Franklin, etajul cinci, Boston, MA 02110-1301, Statele Unite ale Americii sau [//www.gnu.org/licenses/old-licenses/gpl-2.0.html citiți-o online].',
 'version-software' => 'Software instalat',
index f328768..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' => 'Другая причина',
@@ -1864,7 +1866,7 @@ $1",
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|изменение|изменения|изменений}}',
-'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|с последнего посещения}}',
+'enhancedrc-since-last-visit' => '$1 с последнего посещения',
 'enhancedrc-history' => 'история',
 'recentchanges' => 'Свежие правки',
 'recentchanges-legend' => 'Настройки свежих правок',
@@ -2646,9 +2648,11 @@ $UNWATCHURL
 'deleteotherreason' => 'Другая причина/дополнение:',
 'deletereasonotherlist' => 'Другая причина',
 'deletereason-dropdown' => '* Типовые причины удаления
+** спам
 ** вандализм
+** нарушение авторских прав
 ** по запросу автора
-** Ð½Ð°Ñ\80Ñ\83Ñ\88ение Ð°Ð²Ñ\82оÑ\80Ñ\81киÑ\85 Ð¿Ñ\80ав',
+** Ð½ÐµÑ\80абоÑ\82аÑ\8eÑ\89ее Ð¿ÐµÑ\80енапÑ\80авление',
 'delete-edit-reasonlist' => 'Править список причин',
 'delete-toobig' => 'У этой страницы очень длинная история изменений, более $1 {{PLURAL:$1|версии|версий|версий}}.
 Удаление таких страниц было запрещено во избежание нарушений в работе сайта {{SITENAME}}.',
index 79d3704..a0e2972 100644 (file)
@@ -14,6 +14,7 @@
  * @author OchAyeTheNoo
  * @author Omnipaedista
  * @author Purodha
+ * @author Shirayuki
  * @author The Evil IP address
  * @author Urhixidur
  * @author Ushanka
index 703abf7..eb8b9e4 100644 (file)
@@ -293,10 +293,10 @@ $messages = array(
 'tog-extendwatchlist' => 'Rozšíriť zoznam sledovaných stránok, aby zobrazoval všetky zmeny, nie len posledné',
 'tog-usenewrc' => 'Zoskupiť v posledných úpravách a na zozname sledovaných stránok podľa stránky (vyžaduje JavaScript)',
 'tog-numberheadings' => 'Automaticky číslovať nadpisy',
-'tog-showtoolbar' => 'Zobraziť panel nástrojov úprav (vyžaduje JavaSkript)',
-'tog-editondblclick' => 'Upravovať stránky po dvojitom kliknutí (vyžaduje JavaScript)',
+'tog-showtoolbar' => 'Zobraziť panel nástrojov úprav',
+'tog-editondblclick' => 'Upravovať stránky po dvojitom kliknutí',
 'tog-editsection' => 'Umožniť upravovanie sekcie prostredníctvom odkazov [upraviť]',
-'tog-editsectiononrightclick' => 'Umožniť upravovanie sekcie pravým kliknutím na nadpisy sekcií (vyžaduje JavaScript)',
+'tog-editsectiononrightclick' => 'Umožniť upravovanie sekcie pravým kliknutím na nadpisy sekcií',
 'tog-showtoc' => 'Zobrazovať tabuľku s obsahom (pre stránky s viac ako 3 nadpismi)',
 'tog-rememberpassword' => 'Zapamätať si prihlásenie na tomto počítači (najviac $1 {{PLURAL:$1|deň|dni|dní}})',
 'tog-watchcreations' => 'Pridávať stránky, ktoré vytvorím a súbory, ktoré nahrám medzi sledované',
@@ -314,7 +314,7 @@ $messages = array(
 'tog-shownumberswatching' => 'Zobraziť počet používateľov sledujúcich stránku',
 'tog-oldsig' => 'Súčasný podpis:',
 'tog-fancysig' => 'Považovať podpisy za wikitext (bez automatických odkazov)',
-'tog-uselivepreview' => 'Používať živý náhľad (JavaScript) (experimentálna funkcia)',
+'tog-uselivepreview' => 'Používať živý náhľad(experimentálna funkcia)',
 'tog-forceeditsummary' => 'Upozoriť ma, keď nevyplním zhrnutie úprav',
 'tog-watchlisthideown' => 'Skryť moje úpravy zo zoznamu sledovaných',
 'tog-watchlisthidebots' => 'Skryť úpravy botov zo zoznamu sledovaných',
@@ -328,6 +328,7 @@ $messages = array(
 'tog-noconvertlink' => 'Vypnúť konverziu názvov odkazov',
 'tog-norollbackdiff' => 'Vynechať rozdiel po vykonaní rollbacku',
 'tog-useeditwarning' => 'Upozorniť ma, keď opúšťam upravovaciu stránku s neuloženými zmenami',
+'tog-prefershttps' => 'Po prihlásení používať vždy zabezpečené pripojenie',
 
 'underline-always' => 'Vždy',
 'underline-never' => 'Nikdy',
@@ -428,7 +429,7 @@ $messages = array(
 'newwindow' => '(otvorí v novom okne)',
 'cancel' => 'Zrušiť',
 'moredotdotdot' => 'Viac...',
-'morenotlisted' => 'Ďalšie neuvedené...',
+'morenotlisted' => 'Tento zoznam nie je úplný.',
 'mypage' => 'Stránka',
 'mytalk' => 'Diskusia',
 'anontalk' => 'Diskusia k tejto IP adrese',
@@ -531,7 +532,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' => 'O {{GRAMMAR:lokál|{{SITENAME}}}}',
 'aboutpage' => 'Project:Úvod',
-'copyright' => 'Obsah je k dispozícii za licenčných podmienok $1.',
+'copyright' => 'Obsah je dostupný pod $1, pokiaľ nie je uvedené inak.',
 'copyrightpage' => '{{ns:project}}:Autorské práva',
 'currentevents' => 'Aktuality',
 'currentevents-url' => 'Project:Aktuality',
@@ -614,6 +615,11 @@ Zoznam platných špeciálnych stránok nájdete na [[Special:SpecialPages|{{int
 # General errors
 'error' => 'Chyba',
 'databaseerror' => 'Chyba v databáze',
+'databaseerror-text' => 'Došlo k chybe pri otázke do databázy.
+Môže to byť spôsobené chybou v softvéri.',
+'databaseerror-query' => 'Otázka: $1',
+'databaseerror-function' => 'Funkcia: $1',
+'databaseerror-error' => 'Chyba: $1',
 'laggedslavemode' => 'Upozornenie: Je možné, že stránka neobsahuje posledné aktualizácie.',
 'readonly' => 'Databáza je zamknutá',
 'enterlockreason' => 'Zadajte dôvod požadovaného zamknutia vrátane odhadu, kedy očakávate odomknutie',
@@ -647,6 +653,7 @@ Možno ju už zmazal nieto iný.',
 'cannotdelete-title' => 'Nemôžete zmazať stránku „$1“',
 'delete-hook-aborted' => 'Zmazanie zrušila prídavná funkcia (prípojný bod syntaktického analyzátora).
 Neudala vysvetlenie.',
+'no-null-revision' => 'Nepodarilo sa vytvoriť novú prázdnu revíziu stránky „$1“',
 'badtitle' => 'Neplatný nadpis',
 'badtitletext' => 'Požadovaný nadpis bol neplatný, nezadaný, alebo nesprávne odkazovaný z inej jazykovej verzie {{GRAMMAR:genitív|{{SITENAME}}}}. Mohol tiež obsahovať jeden alebo viac znakov, ktoré nie je možné použiť v nadpisoch.',
 'perfcached' => 'Nasledujúce údaje pochádzajú z vyrovnávacej pamäte a nemusia byť úplne aktuálne. Vo vyrovnávacej pamäti {{PLURAL:$1|je dostupný|sú dostupné|je dostupných}} najviac {{PLURAL:$1|jeden výsledok|$1 výsledky|$1 výsledkov}}.',
@@ -695,7 +702,6 @@ Správca, ktorý ho zamkol ponúkol toto vysvetlenie: „$3“.',
 # Login and logout pages
 'logouttext' => "'''Práve ste sa odhlásili.'''
 
-Odteraz môžete používať {{GRAMMAR:akuzatív|{{SITENAME}}}} ako anonymný používateľ alebo sa môžete opäť <span class='plainlinks'>[$1 prihlásiť]</span> pod rovnakým alebo odlišným používateľským menom.
 Uvedomte si, že niektoré stránky sa môžu naďalej zobrazovať ako keby ste boli prihlásený, až kým nevymažete vyrovnávaciu pamäť vášho prehliadača.",
 'welcomeuser' => 'Vitajte,  $1 !',
 'welcomecreation-msg' => 'Váš účet bol vytvorený.
@@ -736,13 +742,16 @@ Nezabudnite zmeniť svoje [[Special:Preferences|Predvoľby {{GRAMMAR:genitív|{{
 'userlogin-resetpassword-link' => 'Obnoviť heslo',
 'helplogin-url' => 'Help:Prihlasovanie',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoc s prihlásením]]',
+'userlogin-loggedin' => 'Ste už {{GENDER:$1|prihĺasený|prihlásená}} ako $1.
+Pomocou formulára nižšie sa môžete prihlásiť ako iný redaktor.',
+'userlogin-createanother' => 'Vytvoriť ďalší účet',
 'createacct-join' => 'Vyplňte svoje údaje.',
 'createacct-another-join' => 'Vyplňte údaje nového účtu.',
 'createacct-emailrequired' => 'E-mailová adresa',
 'createacct-emailoptional' => 'E-mailová adresa (nepovinné)',
 'createacct-email-ph' => 'Zadajte vašu e-mailovú adresu',
 'createacct-another-email-ph' => 'Zadajte vašu e-mailovú adresu',
-'createaccountmail' => 'Použiť dočasné náhodné heslo a poslať ho na nižšie uvedenú emailovú adresu',
+'createaccountmail' => 'Použiť dočasné náhodné heslo a poslať ho na uvedenú emailovú adresu',
 'createacct-realname' => 'Skutočné meno (nepovinné)',
 'createaccountreason' => 'Dôvod:',
 'createacct-reason' => 'Dôvod',
@@ -810,15 +819,15 @@ Z tohto dôvodu nemôžu návštevníci z tejto IP adresy momentálne vytvoriť
 'cannotchangeemail' => 'Na tejto wiki nie je možné meniť e-mailové adresy používateľského účtu.',
 'emaildisabled' => 'Táto lokalita nedokáže posielať emaily.',
 'accountcreated' => 'Účet vytvorený',
-'accountcreatedtext' => 'Používateľský účet $1 bol vytvorený.',
+'accountcreatedtext' => 'Používateľský účet [[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|diskusia]]) bol vytvorený.',
 'createaccount-title' => 'Vytvorenie účtu na {{GRAMMAR:lokál|{{SITENAME}}}}',
 'createaccount-text' => 'Niekto vytvoril účet pre vašu emailovú adresu na {{GRAMMAR:lokál|{{SITENAME}}}}
 ($4) s názvom „$2“, s heslom „$3“. Mali by ste sa prihlásiť a svoje heslo teraz zmeniť.
 
 Ak bol účet vytvorený omylom, túto správu môžete ignorovať.',
 'usernamehasherror' => 'Používateľské meno nemôže obsahovať znak mriežky.',
-'login-throttled' => 'Nedávno ste uskutočnili príliš mnoho neúspešných pokusov o prihlásenie.
-Prosím, počkajte predtým, než to skúsite znova.',
+'login-throttled' => 'Uskutočnili ste príliš mnoho neúspešných pokusov o prihlásenie.
+Prosím, počkajte $1 predtým, než to skúsite znova.',
 'login-abort-generic' => 'Vaše prihlásenie nebolo úspešné - zrušené',
 'loginlanguagelabel' => 'Jazyk: $1',
 'suspicious-userlogout' => 'Vaša požiadavka odhlásiť sa bola zamietnutá, pretože to vyzerá, že ju poslal pokazený prehliadač alebo proxy server.',
@@ -1666,9 +1675,11 @@ Musí obsahovať menej ako $1 {{PLURAL:$1|znak|znaky|znakov}}.',
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|zmena|zmeny|zmien}}',
+'enhancedrc-history' => 'história',
 'recentchanges' => 'Posledné úpravy',
 'recentchanges-legend' => 'Možnosti posledných zmien',
 'recentchanges-summary' => 'Pomocou tejto stránky sledujete posledné úpravy wiki.',
+'recentchanges-noresult' => 'V danom období nie sú zmeny spĺňajúce tieto kritériá.',
 'recentchanges-feed-description' => 'Sledovať posledné úpravy tejto wiki týmto kanálom.',
 'recentchanges-label-newpage' => 'Táto úprava vytvorila novú stránku.',
 'recentchanges-label-minor' => 'Toto je drobná úprava',
@@ -1959,8 +1970,7 @@ Aby bolo zabezpečenie optimálne, img_auth.php je vypnutý.',
 'upload_source_file' => ' (súbor na vašom počítači)',
 
 # Special:ListFiles
-'listfiles-summary' => 'Táto špeciálna stránka zobrazuje všetky nahrané súbory.
-Pri filtrovaní podľa používateľa sa zobrazia iba súbory, ktorých najnovšiu verziu nahral dotyčný používateľ.',
+'listfiles-summary' => 'Táto špeciálna stránka zobrazuje všetky nahrané súbory.',
 'listfiles_search_for' => 'Hľadať názov súboru:',
 'imgfile' => 'súbor',
 'listfiles' => 'Zoznam obrázkov',
@@ -1971,6 +1981,10 @@ Pri filtrovaní podľa používateľa sa zobrazia iba súbory, ktorých najnovš
 'listfiles_size' => 'Veľkosť (v bajtoch)',
 'listfiles_description' => 'Popis',
 'listfiles_count' => 'Verzie',
+'listfiles-show-all' => 'Vrátane starších verzií obrázkov',
+'listfiles-latestversion' => 'Aktuálna verzia',
+'listfiles-latestversion-yes' => 'Áno',
+'listfiles-latestversion-no' => 'Nie',
 
 # File description page
 'file-anchor-link' => 'Súbor',
@@ -2063,6 +2077,13 @@ Možno chcete upraviť popis na jeho [$2 popisnej stránke súboru] tam.',
 'randompage' => 'Náhodná stránka',
 'randompage-nopages' => '{{PLURAL:$2|V mennom priestore „$1“ nie sú žiadne stránky.|V nasledovných menných priestoroch nie sú žiadne stránky: $1}}',
 
+# Random page in category
+'randomincategory' => 'Náhodná stránka v kategórii',
+'randomincategory-invalidcategory' => '"$1" nie je platný názov kategórie.',
+'randomincategory-nopages' => 'V [[:Category:$1|kategórii $1]] nie sú žiadne stránky.',
+'randomincategory-selectcategory' => 'Získať náhodnú stránku z kategórie: $1 $2',
+'randomincategory-selectcategory-submit' => 'Ísť na',
+
 # Random redirect
 'randomredirect' => 'Náhodná presmerovacia stránka',
 'randomredirect-nopages' => 'V mennom „$1“ priestore nie sú žiadne presmerovania.',
@@ -3959,6 +3980,8 @@ Spolu s týmto programom by ste obdržať [{{SERVER}}{{SCRIPTPATH}}/COPYING kóp
 'tags-display-header' => 'Vzhľad v zoznamoch úprav',
 'tags-description-header' => 'Úplný popis významu',
 'tags-hitcount-header' => 'Označené úpravy',
+'tags-active-yes' => 'Áno',
+'tags-active-no' => 'Nie',
 'tags-edit' => 'upraviť',
 'tags-hitcount' => '$1 {{PLURAL:$1|zmena|zmeny|zmien}}',
 
@@ -3979,6 +4002,7 @@ Spolu s týmto programom by ste obdržať [{{SERVER}}{{SCRIPTPATH}}/COPYING kóp
 'dberr-problems' => 'Prepáčte! Táto stránka má práve technické problémy.',
 'dberr-again' => 'Skúste niekoľko minút počkať a potom opäť načítať stránku.',
 'dberr-info' => '(Spojenie s databázovým serverom neúspešné: $1)',
+'dberr-info-hidden' => '(Nie je možné kontaktovať databázový server)',
 'dberr-usegoogle' => 'Zatiaľ môžete skúsiť hľadať pomocou Google.',
 'dberr-outofdate' => 'Pamätajte, že ich indexy nemusia byť aktuálne.',
 'dberr-cachederror' => 'Toto je kópia požadovanej stránky z vyrovnávacej pamäte a nemusí byť aktuálna.',
@@ -4010,10 +4034,10 @@ Spolu s týmto programom by ste obdržať [{{SERVER}}{{SCRIPTPATH}}/COPYING kóp
 'logentry-delete-event-legacy' => '$1 {{GENDER:$2|zmenil|zmenila}} viditeľnosť záznamov udalostí k stránke $3',
 'logentry-delete-revision-legacy' => '$1 {{GENDER:$2|zmenil|zmenila}} viditeľnosť revízií na stránke $3',
 'logentry-suppress-delete' => '$1 {{GENDER:$2|utajil|utajila}} stránku $3',
-'logentry-suppress-event' => '$1 utajene zmenil viditeľnosť {{PLURAL:$5|záznamu udalostí|$5 záznamov udalostí}} k stránke $3: $4',
+'logentry-suppress-event' => '$1 utajene {{GENDER:$2|zmenil|zmenila}} viditeľnosť {{PLURAL:$5|záznamu udalostí|$5 záznamov udalostí}} k stránke $3: $4',
 'logentry-suppress-revision' => '$1 utajene zmenil viditeľnosť {{PLURAL:$5|revízie|$5 revízií}} na stránke $3: $4',
-'logentry-suppress-event-legacy' => '$1 utajene zmenil viditeľnosť záznamov udalostí k stránke $3',
-'logentry-suppress-revision-legacy' => '$1 utajene zmenil viditeľnosť revízií na stránke $3',
+'logentry-suppress-event-legacy' => '$1 utajene {{GENDER:$2|zmenil|zmenila}} viditeľnosť záznamov udalostí k stránke $3',
+'logentry-suppress-revision-legacy' => '$1 utajene {{GENDER:$2|zmenil|zmenila}} viditeľnosť revízií na stránke $3',
 'revdelete-content-hid' => 'obsah skrytý',
 'revdelete-summary-hid' => 'zhrnutie editácie skryté',
 'revdelete-uname-hid' => 'používateľské meno skryté',
@@ -4026,13 +4050,13 @@ Spolu s týmto programom by ste obdržať [{{SERVER}}{{SCRIPTPATH}}/COPYING kóp
 'logentry-move-move-noredirect' => '$1 premiestnil stránku $3 na $4, ale neponechal presmerovanie',
 'logentry-move-move_redir' => '$1 premiestnil stránku $3 na $4 prostredníctvom presmerovania',
 'logentry-move-move_redir-noredirect' => '$1 premiestnil stránku $3 na $4 prostredníctvom presmerovania, ale neponechal presmerovanie',
-'logentry-patrol-patrol' => '$1 označil revíziu $4 stránky $3 ako stráženú',
-'logentry-patrol-patrol-auto' => '$1 automaticky označil revíziu $4 stránky $3 ako stráženú',
-'logentry-newusers-newusers' => 'Bol vytvorený používateľský účet $1',
+'logentry-patrol-patrol' => '$1 {{GENDER:$2|označil|označila}} revíziu $4 stránky $3 ako overenú',
+'logentry-patrol-patrol-auto' => '$1 automaticky {{GENDER:$2|označil|označila}} revíziu $4 stránky $3 ako overenú',
+'logentry-newusers-newusers' => 'Bol {{GENDER:$2|vytvorený}} používateľský účet $1',
 'logentry-newusers-create' => 'Bol vytvorený používateľský účet $1',
 'logentry-newusers-create2' => '$1 vytvoril používateľský účet $3',
 'logentry-newusers-byemail' => '$1 vytvoril používateľský účet $3 a heslo bolo poslané emailom',
-'logentry-newusers-autocreate' => 'Používateľský účet $1 bol vytvorený automaticky',
+'logentry-newusers-autocreate' => 'Používateľský účet $1 bol {{GENDER:$2|vytvorený}} automaticky',
 'logentry-rights-rights' => '$1 zmenil členstvo $3 v skupinách z $4 na $5',
 'logentry-rights-rights-legacy' => '$1 zmenil členstvo $3 v skupinách',
 'logentry-rights-autopromote' => '$1 bol automaticky povýšený z $4 na $5',
@@ -4114,4 +4138,11 @@ V opačnom prípade môžete použiť zjednodušený formulár nižšie. Váš k
 # Image rotation
 'rotate-comment' => 'Obrázok otočený o $1 {{PLURAL:$1|stupeň|stupne|stupňov}} v smere hodinových ručičiek',
 
+# Limit report
+'limitreport-postexpandincludesize-value' => '$1/$2 {{PLURAL:$2|bajt|bajty|bajtov}}',
+'limitreport-templateargumentsize' => 'Veľkosť argumentov šablón',
+'limitreport-templateargumentsize-value' => '$1/$2 {{PLURAL:$2|bajt|bajty|bajtov}}',
+'limitreport-expansiondepth' => 'Najväčšia hĺbka expanzie',
+'limitreport-expensivefunctioncount' => 'Počet náročných funkcií parseru',
+
 );
index c715b9c..bad752f 100644 (file)
@@ -650,6 +650,9 @@ Ne pozabite si prilagoditi vaših [[Special:Preferences|nastavitev {{GRAMMAR:rod
 'userlogin-resetpassword-link' => 'Ponastavite svoje geslo',
 'helplogin-url' => 'Help:Prijava',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoč pri prijavi]]',
+'userlogin-loggedin' => 'Ste že prijavljeni kot {{GENDER:$1|$1}}.
+Uporabite spodnji obrazec, da se prijavite kot drug uporabnik.',
+'userlogin-createanother' => 'Ustvari drug račun',
 'createacct-join' => 'Spodaj vnesite svoje informacije.',
 'createacct-another-join' => 'Spodaj vnesite informacije o novem računu.',
 'createacct-emailrequired' => 'E-poštni naslov',
@@ -1017,7 +1020,7 @@ Lahko se vrnete nazaj in urejate že obstoječe strani, ali pa se [[Special:User
 'sectioneditnotsupported-title' => 'Urejanje razdelkov ni podprto',
 'sectioneditnotsupported-text' => 'Urejanje razdelkov ni podprto na tej strani.',
 'permissionserrors' => 'Napaka dovoljenja',
-'permissionserrorstext' => 'Za izvedbo dejanja nimate dovoljenja zaradi {{PLURAL:$1|naslednjega razloga|naslednjih razlogov|naslednjih razlogov|naslednjih razlogov|naslednjih razlogov}}:',
+'permissionserrorstext' => 'Za izvedbo dejanja nimate dovoljenja zaradi {{PLURAL:$1|naslednjega razloga|naslednjih razlogov}}:',
 'permissionserrorstext-withaction' => 'Za $2 zaradi {{PLURAL:$1|naslednjega razloga|naslednjih razlogov}} nimate dovoljenja:',
 'recreate-moveddeleted-warn' => "'''Opozorilo: Pišete stran, ki je bila nekoč že izbrisana.'''
 
@@ -2415,9 +2418,11 @@ Za zapise nedavnih brisanj glej $2.',
 'deleteotherreason' => 'Drugi/dodatni razlogi:',
 'deletereasonotherlist' => 'Drug razlog',
 'deletereason-dropdown' => '* Pogosti razlogi za brisanje
-** zahteva avtorja
+** smetenje
+** vandalizem
 ** kršitev avtorskih pravic
-** vandalizem',
+** zahteva avtorja
+** pretrgana preusmeritev',
 'delete-edit-reasonlist' => 'Uredi razloge za brisanje',
 'delete-toobig' => 'Ta stran ima obsežno zgodovino urejanja, tj. čez $1 {{PLURAL:$1|redakcijo|redakciji|redakcije|redakcij}}.
 Izbris takšnih strani je bil omejen v izogib neželenim motnjam {{GRAMMAR:dative|{{SITENAME}}}}.',
@@ -2583,7 +2588,7 @@ $1',
 'contributions' => '{{GENDER:$1|Uporabnikovi|Uporabničini}} prispevki',
 'contributions-title' => 'Prispevki uporabnika $1',
 'mycontris' => 'Prispevki',
-'contribsub2' => 'Uporabnik: $1 ($2)',
+'contribsub2' => 'Za {{GENDER:$3|$1}} ($2)',
 'nocontribs' => 'Ne najdem nobene merilom ustrezajoče spremembe.',
 'uctop' => '(trenutno)',
 'month' => 'Od meseca (in prej):',
@@ -3936,7 +3941,10 @@ Skupaj s programom bi morali bi prejeti [{{SERVER}}{{SCRIPTPATH}}/COPYING kopijo
 'tags-tag' => 'Ime oznake',
 'tags-display-header' => 'Prikaz na seznamu sprememb',
 'tags-description-header' => 'Polni opis pomena',
+'tags-active-header' => 'Dejavno?',
 'tags-hitcount-header' => 'Etiketirane spremembe',
+'tags-active-yes' => 'Da',
+'tags-active-no' => 'Ne',
 'tags-edit' => 'uredi',
 'tags-hitcount' => '$1 {{PLURAL:$1|sprememba|spremembi|spremembe|sprememb|sprememb}}',
 
index 479775e..1e4181f 100644 (file)
@@ -334,7 +334,7 @@ $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-numberheadings' => 'Automatisk numrerade rubriker',
 'tog-showtoolbar' => 'Visa redigerings-verktygsraden',
 'tog-editondblclick' => 'Redigera sidor med dubbelklick',
 'tog-editsection' => 'Aktivera redigering av avsnitt genom [redigera]-länkar',
@@ -2523,10 +2523,12 @@ Se $2 för noteringar om de senaste raderingarna.',
 'deletecomment' => 'Anledning:',
 'deleteotherreason' => 'Annan/ytterligare anledning:',
 'deletereasonotherlist' => 'Annan anledning',
-'deletereason-dropdown' => '*Vanliga anledningar till radering
-** Författarens begäran
+'deletereason-dropdown' => '* Vanliga anledningar till radering
+** Spam
+** Vandalism
 ** Upphovsrättsbrott
-** Vandalism',
+** Författarens begäran
+** Trasig omdirigering',
 'delete-edit-reasonlist' => 'Redigera anledningar för radering',
 'delete-toobig' => 'Denna sida har en lång redigeringshistorik med mer än $1 {{PLURAL:$1|sidversion|sidversioner}}. Borttagning av sådana sidor har begränsats för att förhindra oavsiktliga driftstörningar på {{SITENAME}}.',
 'delete-warning-toobig' => 'Denna sida har en lång redigeringshistorik med mer än $1 {{PLURAL:$1|sidversion|sidversioner}}. Att radera sidan kan skapa problem med hanteringen av databasen på {{SITENAME}}; var försiktig.',
index 0ae34e2..d9accb7 100644 (file)
@@ -359,7 +359,7 @@ $messages = array(
 'tog-hidepatrolled' => 'Son değişikliklerde gözden geçirilen düzenlemeleri gizle',
 'tog-newpageshidepatrolled' => 'Kontrol edilmiş sayfaları yeni sayfalar listesinde gizle',
 'tog-extendwatchlist' => 'İzleme listesini sadece en son değil, tüm değişiklikleri göstermek için genişlet',
-'tog-usenewrc' => 'Son değişiklikler sayfasındaki ve izleme listesindeki değişiklikleri gruplandırma',
+'tog-usenewrc' => 'Son değişiklikler sayfasındaki ve izleme listesindeki değişiklikleri gruplandır',
 'tog-numberheadings' => 'Başlıkları otomatik numaralandır',
 'tog-showtoolbar' => 'Düzenleme yaparken araç çubuğunu göster',
 'tog-editondblclick' => 'Çift tıklayarak sayfaları düzenle',
@@ -1673,6 +1673,7 @@ Diğer kullanıcılar sizinle bu yolla iletişime geçtiğinde e-posta adresiniz
 'right-editusercssjs' => 'Diğer kullanıcıların CSS ve JS dosyalarında değişiklik yap',
 'right-editusercss' => 'Diğer kullanıcıların CSS dosyalarında değişiklik yap',
 'right-edituserjs' => 'Diğer kullanıcıların JS dosyalarında değişiklik yap',
+'right-editmyoptions' => 'tercihlerini düzenle',
 'right-rollback' => 'Belirli bir sayfayı değiştiren son kullanıcının değişikliklerini hızlıca geri döndür',
 'right-markbotedits' => 'Geri döndürülen değişiklikleri, bot değişiklikleri olarak işaretle',
 'right-noratelimit' => 'Derecelendirme sınırlamalarından etkilenme',
@@ -1734,9 +1735,13 @@ Diğer kullanıcılar sizinle bu yolla iletişime geçtiğinde e-posta adresiniz
 'action-userrights-interwiki' => 'diğer vikilerde kullanıcıların, kullanıcı haklarını değiştirmeye',
 'action-siteadmin' => 'veritabanını kilitleyip açmaya',
 'action-sendemail' => 'e-posta gönder',
+'action-editmywatchlist' => 'izleme listeni düzenle',
+'action-viewmywatchlist' => 'izleme listeni gör',
 
 # Recent changes
 'nchanges' => '$1 {{PLURAL:$1|değişiklik|değişiklik}}',
+'enhancedrc-since-last-visit' => '$1 {{PLURAL:$1|son ziyaretten bu yana}}',
+'enhancedrc-history' => 'geçmiş',
 'recentchanges' => 'Son değişiklikler',
 'recentchanges-legend' => 'Son değişiklikler seçenekleri',
 'recentchanges-summary' => 'Yapılan en son değişiklikleri bu sayfadan izleyin.',
@@ -1768,7 +1773,7 @@ Diğer kullanıcılar sizinle bu yolla iletişime geçtiğinde e-posta adresiniz
 'rc_categories_any' => 'Herhangi',
 'rc-change-size-new' => '$1 {{PLURAL:$1|bayt|bayt}} değişiklikten sonra',
 'newsectionsummary' => '/* $1 */ yeni başlık',
-'rc-enhanced-expand' => 'Ayrıntıları göster (JavaScript gerekir)',
+'rc-enhanced-expand' => 'Ayrıntıları göster',
 'rc-enhanced-hide' => 'Ayrıntıları gizle',
 'rc-old-title' => 'ilk olarak oluşturulan "$1"',
 
@@ -1788,7 +1793,7 @@ Diğer kullanıcılar sizinle bu yolla iletişime geçtiğinde e-posta adresiniz
 'reuploaddesc' => 'Yükleme formuna geri dön.',
 'upload-tryagain' => 'Değiştirilmiş dosya açıklamasını gönder',
 'uploadnologin' => 'Oturum açık değil',
-'uploadnologintext' => 'Dosya yükleyebilmek için [[Special:UserLogin|oturum aç]]manız gerekiyor.',
+'uploadnologintext' => 'Dosya yükleyebilmek için [[$1|oturum aç]]manız gerekiyor.',
 'upload_directory_missing' => 'Yükleme dizini ($1) kayıp ve websunucusu tarafından oluşturulamıyor.',
 'upload_directory_read_only' => 'Dosya yükleme dizinine ($1) web sunucusunun yazma izni yok.',
 'uploaderror' => 'Yükleme hatası',
@@ -2017,6 +2022,9 @@ Kullanıcı tarafından filtrelendiğinde, sadece o kullanıcı dosyanın en son
 'listfiles_size' => 'Boyut (bayt)',
 'listfiles_description' => 'Tanım',
 'listfiles_count' => 'Sürümler',
+'listfiles-latestversion' => 'Geçerli sürüm',
+'listfiles-latestversion-yes' => 'Evet',
+'listfiles-latestversion-no' => 'Hayır',
 
 # File description page
 'file-anchor-link' => 'Dosya',
@@ -2110,6 +2118,7 @@ Dosya açıklamasını düzenlemek isterseniz, [$2 dosya açıklama sayfası] bu
 # Random page in category
 'randomincategory' => 'Kategoriye göre rastgele sayfa',
 'randomincategory-selectcategory' => 'Rastgele sayfa alınacak kategori: $1 $2.',
+'randomincategory-selectcategory-submit' => 'Getir',
 
 # Random redirect
 'randomredirect' => 'Rastgele yönlendirme',
@@ -3942,7 +3951,10 @@ Bu programla birlikte [{{SERVER}}{{SCRIPTPATH}}/COPYING GNU Genel Kamu Lisansın
 'tags-tag' => 'Etiket adı',
 'tags-display-header' => 'Değişiklik listelerindeki görünüm',
 'tags-description-header' => 'Anlamının tam açıklaması',
+'tags-active-header' => 'Etkin?',
 'tags-hitcount-header' => 'Etiketli değişiklikler',
+'tags-active-yes' => 'Evet',
+'tags-active-no' => 'Hayır',
 'tags-edit' => 'değiştir',
 'tags-hitcount' => '$1 {{PLURAL:$1|değişiklik|değişiklik}}',
 
index 892124b..5fd2c79 100644 (file)
@@ -2595,9 +2595,11 @@ $UNWATCHURL
 'deleteotherreason' => 'Інша/додаткова причина:',
 'deletereasonotherlist' => 'Інша причина',
 'deletereason-dropdown' => '* Типові причини вилучення
+** спам
 ** вандалізм
+** порушення авторських прав
 ** за запитом автора
-** Ð¿Ð¾Ñ\80Ñ\83Ñ\88еннÑ\8f Ð°Ð²Ñ\82оÑ\80Ñ\81Ñ\8cкиÑ\85 Ð¿Ñ\80ав',
+** Ð\97ламанÑ\96 Ð¿ÐµÑ\80енапÑ\80авленнÑ\8f',
 'delete-edit-reasonlist' => 'Редагувати причини вилучення',
 'delete-toobig' => 'У цієї сторінки дуже довга історія редагувань, більше $1 {{PLURAL:$1|версії|версій|версій}}.
 Вилучення таких сторінок було заборонене з метою уникнення порушень у роботі сайту {{SITENAME}}.',
index bc4ff3e..f95d7c6 100644 (file)
@@ -1031,7 +1031,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' => "Bạn đi đến đây từ một liên kết đến một trang chưa tồn tại. Để tạo trang, hãy bắt đầu gõ vào ô bên dưới (xem [[{{MediaWiki:Helppage}}|trang trợ giúp]] để có thêm thông tin). Nếu bạn đến đây do nhầm lẫn, chỉ cần nhấn vào nút '''Lùi''' (''Back'') trong trình duyệt của bạn.",
+'newarticletext' => '<div style="margin-top: 0px;" class="emptymwmsg mediawiki_newarticletext"></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.
@@ -2730,17 +2730,13 @@ $1',
 'ipbreason' => 'Lý do:',
 'ipbreasonotherlist' => 'Lý do khác',
 'ipbreason-dropdown' => '*Một số lý do cấm thường gặp
-** 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
+** 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
 ** Cho thông tin rác vào trang
 ** Có thái độ dọa dẫm/quấy rối
-** 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',
+** Lạm dụng nhiều tài khoản
+** Tên thành viên không thể chấp nhận',
 '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 8cad007..832415e 100644 (file)
@@ -95,8 +95,15 @@ $messages = array(
 'tog-showhiddencats' => 'Fârschdegde ghadegoriin dsajchn',
 
 # Dates
-'sunday' => 'Sundooch',
+'sunday' => 'Sunndooch',
+'monday' => 'Monndooch',
+'tuesday' => 'Dinnsdooch',
+'wednesday' => 'Miidwoch',
+'thursday' => 'Dunnerschdooch',
+'friday' => 'Freidooch',
+'saturday' => 'Samsdooch',
 'sun' => 'Su',
+'thu' => 'Du',
 'january' => 'Januaar',
 'february' => 'Feebruaar',
 'march' => 'Märds',
@@ -144,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',
@@ -173,7 +182,7 @@ $messages = array(
 'vector-view-history' => 'Wärsjoonsfolche',
 'vector-view-view' => 'Leesn',
 'vector-view-viewsource' => 'Gwäl-dhägsd ôôgugn',
-'actions' => 'Aggdsione',
+'actions' => 'Agdsiona',
 'namespaces' => 'Nôômsrajm',
 'variants' => 'Warjandn',
 
@@ -320,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',
 
@@ -329,11 +339,13 @@ 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' => 'Ôômeldn',
+'gotaccountlink' => 'Omeldn',
 'mailmypassword' => ' najs passwôrd iwâr iimejl dsuschign lasn',
 'loginlanguagelabel' => 'Sproch: $1',
 
@@ -408,8 +420,8 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'permissionserrorstext-withaction' => 'Du däfsd ned $2, des{{PLURAL:$1||}}dâsweechn:',
 
 # Parser/template warnings
-'post-expand-template-inclusion-warning' => "'''Obachd:''' dii Gräiß vo dii ajbundna Vorlââng is zgrouß, ajniche Vorlââng kenna ned ajbundn wärrn.",
-'post-expand-template-inclusion-category' => 'Sajdn, ba denne vo dii ajbundna Vorlââng dii Gräiß üba da gräißdmöchlichn Gräiß iss',
+'post-expand-template-inclusion-warning' => "'''Wannung''': Däi Gräiss vo eibundne Vuurloong is zu grouss, einiche Vuurloong könna net eibundn werrn.",
+'post-expand-template-inclusion-category' => 'Seidn, in dena wou däi maximale Gräiss vo eibundne Vuurloong ieberschriddn is.',
 
 # History pages
 'viewpagelogs' => 'Logbicher fär dii sajdn dsajchn',
@@ -424,12 +436,14 @@ 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',
 'revdel-restore' => 'Ändârn, was oodsajchd wäd',
+'revdel-restore-deleted' => 'glöschde Versiona',
+'revdel-restore-visible' => 'sichdbore Versiona',
 
 # Merge log
 'revertmerge' => 'Dsrig fôr dii fârajnichung',
@@ -450,11 +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)',
-'searchprofile-articles' => 'Sajdn dii ann Inhald zajng',
-'searchprofile-images' => 'Muldimedjâ',
-'searchprofile-everything' => 'Âlls',
-'searchprofile-advanced' => 'Erwajderd',
+'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)',
@@ -536,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',
@@ -577,7 +601,7 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 
 # Recent changes linked
 'recentchangeslinked' => 'Ändärunga af sajdn, af dii fo hiir fârwiisn wäd',
-'recentchangeslinked-toolbox' => 'Ändärunga af sajdn, af dii fo hiir fârwiisn wäd',
+'recentchangeslinked-toolbox' => 'Änderunga an velingde Seidn',
 'recentchangeslinked-title' => 'Ändrunga an sajdn, af dii fo „$1“ aus fârwiisn wärd.',
 'recentchangeslinked-summary' => "Dii sôndârsajdn fiird di ledsdn ändrunga fon sajdn af, dii wo an däär hiir drôôhänga. Alles, was de dâfoo in daj [[Special:Watchlist|beoobachdunglisdn]] aufgnumma hasd, wäd aa no '''fäd''' ôôdsajchd.",
 'recentchangeslinked-page' => 'Sajdn:',
@@ -586,10 +610,13 @@ 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',
+
 # File description page
-'file-anchor-link' => 'Dadhaj',
+'file-anchor-link' => 'Daddei',
 'filehist' => 'Wärsjoona bis eds',
 'filehist-help' => 'Glig af ân dsajdbhungd, um dii dôômôôliche fasung ôôdsuschaua',
 'filehist-current' => 'agduäl',
@@ -601,7 +628,7 @@ Hirmid sagsd, das Du den dhägsd '''selbâr gschriim''' hasd, das däär dhägsd
 'filehist-filesize' => 'Dadajgräâs',
 'filehist-comment' => 'Sembf dâdsuâ',
 'filehist-missing' => 'Dadaj fääld',
-'imagelinks' => 'Dsajchn, wo dii dadaj als benudsd wärd',
+'imagelinks' => 'Daddeiverwendung',
 'linkstoimage' => 'Dii dadaj wäd fo {{PLURAL:$1|därâ |denâ $1 }} sajdn benudsd:',
 'linkstoimage-more' => "Määr wii {{PLURAL:$1|ane |$1 }} sajdn fârwajsn uf diâ dadaj.
 Dii lisdn undn dsajch dâfâu nôr äärschd môôl {{PLURAL:$1|an|$1}} fârwajs.
@@ -611,10 +638,11 @@ S'gajd awâr aa â [[Special:WhatLinksHere/$2|lisdn mid alâ fârwajs]].",
 'duplicatesoffile' => 'Dii {{PLURAL:$1|folchende dadaj is â dublighaad|folchende $1 dadajâ sn dublighaade}} fon dâr dadaj ([[Special:FileDuplicateSearch/$2|wajdâre ôôndlshajdâ]]):',
 'sharedupload' => 'Dii dadaj ghumd fo $1, un mär däf se fär annäre brojägd aa ´heernemâ.',
 'sharedupload-desc-there' => 'Dii dadaj ghumd fon $1, un mr däf se fir andârâ brojägd aa nemâ. Genauârs schded uf dr [$2 beschrajwungssajdâ fon dr dadaj].',
+'sharedupload-desc-here' => 'Däi Daddei schdamm aus $1 un ko vo andre Brojegde verwendt werrn. Däi Beschreibung vo dera ihr [$2 Daddeibeschreibungsseidn] wärrd undn ozeichd.',
 'uploadnewversion-linktext' => ' naje wärsjoon fo derä dadaj nauflôôdn',
 
 # Random page
-'randompage' => 'Zufälliche Sajdn',
+'randompage' => 'Zoufälliche Seidn',
 
 # Statistics
 'statistics' => 'Schdadisdig',
@@ -647,6 +675,9 @@ S'gajd awâr aa â [[Special:WhatLinksHere/$2|lisdn mid alâ fârwajs]].",
 'allarticles' => 'Ale sajdn',
 'allpagessubmit' => "Loos gäd's.",
 
+# Special:Categories
+'categories' => 'Gadegorien',
+
 # Special:LinkSearch
 'linksearch' => 'Linggs nach ausârhalb',
 
@@ -657,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]] .
 
@@ -724,6 +755,7 @@ Wenns'd dii sajdn irchendwan amôl nimä fârfolchn wilsd, musd bloos af „{{in
 
 # Undelete
 'undeletelink' => 'ôôgugn/dsrighooln',
+'undeleteviewlink' => 'oschaun',
 
 # Namespace form on various pages
 'namespace' => 'Nôômâraum:',
@@ -735,13 +767,15 @@ 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-talk' => 'Disghusjoon',
+'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:',
 'sp-contributions-submit' => 'Suchng',
@@ -753,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',
@@ -768,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',
@@ -810,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',
 
@@ -873,7 +911,7 @@ Bidde gug's mi´m foorschau-gnobf ôô fôrm schbajchan",
 'tooltip-upload' => 'Loos midm nauflaadn',
 'tooltip-rollback' => 'Hiir glign machd mid am môl alâs riggängich, was däär benudsâr dsledschd af där sajdn gmachd had.',
 'tooltip-undo' => 'Hiir glign machd dii aane ändärung riggängich un dsajchd dan ôô, wiis dan ausschaua dääd. Dann koosd aa no â dsamfassung wisoo un warum dâdsuuschrajm.',
-'tooltip-summary' => 'Dibb schnell a glaane Zusammafassung nei.',
+'tooltip-summary' => 'Gib a korze Zammfassung ei.',
 
 # Stylesheets
 'common.css' => '/* CSS hiir beâjflusd ale schelfn */',
@@ -906,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 d2abf7e..4136315 100644 (file)
@@ -2337,6 +2337,7 @@ Pad luveratiko ninädon yümi lü bevüresodatopäd plödik in blägalised.',
 
 # Info page
 'pageinfo-header-edits' => 'Jenotem redakamas',
+'pageinfo-toolboxlink' => 'Nüns pada',
 'pageinfo-contentpage-yes' => 'Si',
 'pageinfo-protect-cascading-yes' => 'Si',
 
index 96ab825..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' => 'ארײַנגעבן דער נײַער קאנטעס אינפארמאציע אונטן.',
@@ -2356,10 +2358,12 @@ $UNWATCHURL
 'deleteotherreason' => 'אנדער/נאך אן אורזאך:',
 'deletereasonotherlist' => 'אנדער אורזאך',
 'deletereason-dropdown' => '* געוויינטלעכע אויסמעקן אורזאכן
+** ספאַם
 ** פֿארלאנג פֿון שרייבער
 ** קאפירעכט ברעכונג
 ** וואנדאליזם
-** נישט יידיש',
+** נישט יידיש
+** צעבראכענע ווייטערפירונג',
 'delete-edit-reasonlist' => 'רעדאַקטירן די אויסמעקן סיבות',
 'delete-toobig' => 'דער בלאַט האט א גרויסע רעדאקטירונג היסטאריע, מער ווי $1 {{PLURAL:$1|רעוויזיע|רעוויזיעס}}. אויסמעקן אזעלכע בלעטער איז באַגרענעצט געווארן בכדי צו פֿאַרמײַדן א צופֿעליגע פֿאַרשטערונג פֿון  {{SITENAME}}.',
 'delete-warning-toobig' => 'דער בלאַט האט א גרויסע רעדאקטירונג היסטאריע, מער ווי $1 {{PLURAL:$1|רעוויזיע|רעוויזיעס}}. אויסמעקן אים קען פֿאַרשטערן דאַטנבאַזע אפעראַציעס פֿון {{SITENAME}}; זײַט פֿארזיכטיג איידער איר מעקט אויס.',
index 3dc962d..6682da4 100644 (file)
@@ -1041,7 +1041,7 @@ $2
 'loginreqlink' => '登录',
 'loginreqpagetext' => '您必须$1才能查看其它页面。',
 'accmailtitle' => '密码已寄出',
-'accmailtext' => "为[[User talk:$1|$1]]随机生成的密码已送至$2。登后可以在''[[Special:ChangePassword|更改密码]]''页面中修改。",
+'accmailtext' => "为[[User talk:$1|$1]]随机生成的密码已送至$2。登后可以在''[[Special:ChangePassword|更改密码]]''页面中修改。",
 'newarticle' => '(新页面)',
 'newarticletext' => '您进入了一个尚未创建的页面。
 要创建该页面,请在下面的编辑框中输入内容(详情参见[[{{MediaWiki:Helppage}}|帮助页]])。
@@ -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登录用户的编辑',
@@ -2442,9 +2442,11 @@ $UNWATCHURL
 'deleteotherreason' => '其他/附加原因:',
 'deletereasonotherlist' => '其他原因',
 'deletereason-dropdown' => '*常见删除原因
-** 作者申请
+** 广告
+** 破坏行为
 ** 侵犯著作权
-** 破坏行为',
+** 作者申请
+** 损坏的重定向',
 'delete-edit-reasonlist' => '编辑删除原因',
 'delete-toobig' => '这个页面有一个十分大量的编辑历史,超过$1次修订。删除此类页面的动作已经被限制,以防止在{{SITENAME}}上的意外扰乱。',
 'delete-warning-toobig' => '这个页面有一个十分大量的编辑历史,超过$1次修订。删除它可能会扰乱{{SITENAME}}的数据库操作;在继续此动作前请小心。',
index 187c995..3bc7673 100644 (file)
@@ -33,6 +33,7 @@
  * @author Liangent
  * @author Liflon
  * @author Littletung
+ * @author Liuxinyu970226
  * @author Mark85296341
  * @author Oapbtommy
  * @author Openerror
@@ -723,6 +724,9 @@ $2',
 'userlogin-resetpassword-link' => '重設密碼',
 'helplogin-url' => 'Help:登入',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|登入幫助]]',
+'userlogin-loggedin' => '您已作為{{GENDER:$1|$1}}登錄。
+利用以下表單以作為另一賬戶登錄。',
+'userlogin-createanother' => '建立另一賬戶',
 'createacct-join' => '輸入您的基本資料:',
 'createacct-another-join' => '在下方輸入新帳號的資訊。',
 'createacct-emailrequired' => '電子郵件',
@@ -2422,7 +2426,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|對話]])的編輯至[[User:$1|$1]]的最後一個修訂版本',
-'revertpage-nouser' => '已由隱藏的使用者恢復編輯到上個 {{GENDER:$1|[[使用者:$1|$1]]}} 的修訂版本',
+'revertpage-nouser' => '已由隱藏的使用者恢復編輯到上個{{GENDER:$1|[[User:$1|$1]]}}的修訂版本',
 'rollback-success' => '已恢復 $1 的編輯;
 更變更回 $2 的最後修訂版本。',
 
@@ -3933,6 +3937,7 @@ MediaWiki是基於使用目的而加以發佈,然而不負任何擔保責任
 'tags-tag' => '標籤名稱',
 'tags-display-header' => '在更改清單中的出現方式',
 'tags-description-header' => '解釋完整描述',
+'tags-active-header' => '存檔?',
 'tags-hitcount-header' => '已加上標籤的更改',
 '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-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 13301ed..21ef4ff 100644 (file)
@@ -48,6 +48,7 @@ class CopyFileBackend extends Maintenance {
                $this->addOption( 'prestat', 'Stat the destination files first (try to use listings)' );
                $this->addOption( 'skiphash', 'Skip SHA-1 sync checks for files' );
                $this->addOption( 'missingonly', 'Only copy files missing from destination listing' );
+               $this->addOption( 'syncviadelete', 'Delete destination files missing from source listing' );
                $this->addOption( 'utf8only', 'Skip source files that do not have valid UTF-8 names' );
                $this->setBatchSize( 50 );
        }
@@ -64,7 +65,6 @@ class CopyFileBackend extends Maintenance {
                        $this->error( "Cannot check for UTF-8, mbstring extension missing.", 1 ); // die
                }
 
-               $count = 0;
                foreach ( $containers as $container ) {
                        if ( $subDir != '' ) {
                                $backendRel = "$container/$subDir";
@@ -74,40 +74,23 @@ class CopyFileBackend extends Maintenance {
                                $this->output( "Doing container '$container'...\n" );
                        }
 
-                       $srcPathsRel = $src->getFileList( array(
-                               'dir' => $src->getRootStoragePath() . "/$backendRel",
-                               'adviseStat' => !$this->hasOption( 'missingonly' ) // avoid HEADs
-                       ) );
-                       if ( $srcPathsRel === null ) {
-                               $this->error( "Could not list files in $container.", 1 ); // die
-                       }
-
                        if ( $this->hasOption( 'missingonly' ) ) {
-                               $dstPathsRel = $dst->getFileList( array(
-                                       'dir' => $dst->getRootStoragePath() . "/$backendRel" ) );
-                               if ( $dstPathsRel === null ) {
+                               $this->output( "\tBuilding list of missing files..." );
+                               $srcPathsRel = $this->getListingDiffRel( $src, $dst, $backendRel );
+                               $this->output( count( $srcPathsRel ) . " file(s) need to be copied.\n" );
+                       } else {
+                               $srcPathsRel = $src->getFileList( array(
+                                       'dir' => $src->getRootStoragePath() . "/$backendRel",
+                                       'adviseStat' => true // avoid HEADs
+                               ) );
+                               if ( $srcPathsRel === null ) {
                                        $this->error( "Could not list files in $container.", 1 ); // die
                                }
-                               // Get the list of destination files
-                               $relFilesDstSha1 = array();
-                               foreach ( $dstPathsRel as $dstPathRel ) {
-                                       $relFilesDstSha1[sha1( $dstPathRel )] = 1;
-                               }
-                               unset( $dstPathsRel ); // free
-                               // Get the list of missing files
-                               $missingPathsRel = array();
-                               foreach ( $srcPathsRel as $srcPathRel ) {
-                                       if ( !isset( $relFilesDstSha1[sha1( $srcPathRel )] ) ) {
-                                               $missingPathsRel[] = $srcPathRel;
-                                       }
-                               }
-                               unset( $srcPathsRel ); // free
-                               // Only copy the missing files over in the next loop
-                               $srcPathsRel = $missingPathsRel;
-                               $this->output( count( $srcPathsRel ) . " file(s) need to be copied.\n" );
-                       } elseif ( $this->getOption( 'prestat' ) ) {
+                       }
+
+                       if ( $this->getOption( 'prestat' ) && !$this->hasOption( 'missingonly' ) ) {
                                // Build the stat cache for the destination files
-                               $this->output( "Building destination stat cache..." );
+                               $this->output( "\tBuilding destination stat cache..." );
                                $dstPathsRel = $dst->getFileList( array(
                                        'dir' => $dst->getRootStoragePath() . "/$backendRel",
                                        'adviseStat' => true // avoid HEADs
@@ -123,12 +106,14 @@ class CopyFileBackend extends Maintenance {
                                $this->output( "done [" . count( $this->statCache ) . " file(s)]\n" );
                        }
 
+                       $this->output( "\tCopying file(s)...\n" );
+                       $count = 0;
                        $batchPaths = array();
                        foreach ( $srcPathsRel as $srcPathRel ) {
                                // Check up on the rate file periodically to adjust the concurrency
                                if ( $rateFile && ( !$count || ( $count % 500 ) == 0 ) ) {
                                        $this->mBatchSize = max( 1, (int)file_get_contents( $rateFile ) );
-                                       $this->output( "Batch size is now {$this->mBatchSize}.\n" );
+                                       $this->output( "\tBatch size is now {$this->mBatchSize}.\n" );
                                }
                                $batchPaths[$srcPathRel] = 1; // remove duplicates
                                if ( count( $batchPaths ) >= $this->mBatchSize ) {
@@ -141,6 +126,36 @@ class CopyFileBackend extends Maintenance {
                                $this->copyFileBatch( array_keys( $batchPaths ), $backendRel, $src, $dst );
                                $batchPaths = array(); // done
                        }
+                       $this->output( "\tCopied $count file(s).\n" );
+
+                       if ( $this->hasOption( 'syncviadelete' ) ) {
+                               $this->output( "\tBuilding list of excess destination files..." );
+                               $delPathsRel = $this->getListingDiffRel( $dst, $src, $backendRel );
+                               $this->output( count( $delPathsRel ) . " file(s) need to be deleted.\n" );
+
+                               $this->output( "\tDeleting file(s)...\n" );
+                               $count = 0;
+                               $batchPaths = array();
+                               foreach ( $delPathsRel as $delPathRel ) {
+                                       // Check up on the rate file periodically to adjust the concurrency
+                                       if ( $rateFile && ( !$count || ( $count % 500 ) == 0 ) ) {
+                                               $this->mBatchSize = max( 1, (int)file_get_contents( $rateFile ) );
+                                               $this->output( "\tBatch size is now {$this->mBatchSize}.\n" );
+                                       }
+                                       $batchPaths[$delPathRel] = 1; // remove duplicates
+                                       if ( count( $batchPaths ) >= $this->mBatchSize ) {
+                                               $this->delFileBatch( array_keys( $batchPaths ), $backendRel, $dst );
+                                               $batchPaths = array(); // done
+                                       }
+                                       ++$count;
+                               }
+                               if ( count( $batchPaths ) ) { // left-overs
+                                       $this->delFileBatch( array_keys( $batchPaths ), $backendRel, $dst );
+                                       $batchPaths = array(); // done
+                               }
+
+                               $this->output( "\tDeleted $count file(s).\n" );
+                       }
 
                        if ( $subDir != '' ) {
                                $this->output( "Finished container '$container', directory '$subDir'.\n" );
@@ -149,9 +164,51 @@ class CopyFileBackend extends Maintenance {
                        }
                }
 
-               $this->output( "Done [$count file(s)].\n" );
+               $this->output( "Done.\n" );
        }
 
+       /**
+        * @param FileBackend $src
+        * @param FileBackend $dst
+        * @param string $backendRel
+        * @return array (rel paths in $src minus those in $dst)
+        */
+       protected function getListingDiffRel( FileBackend $src, FileBackend $dst, $backendRel ) {
+               $srcPathsRel = $src->getFileList( array(
+                       'dir' => $src->getRootStoragePath() . "/$backendRel" ) );
+               if ( $srcPathsRel === null ) {
+                       $this->error( "Could not list files in source container.", 1 ); // die
+               }
+               $dstPathsRel = $dst->getFileList( array(
+                       'dir' => $dst->getRootStoragePath() . "/$backendRel" ) );
+               if ( $dstPathsRel === null ) {
+                       $this->error( "Could not list files in destination container.", 1 ); // die
+               }
+               // Get the list of destination files
+               $relFilesDstSha1 = array();
+               foreach ( $dstPathsRel as $dstPathRel ) {
+                       $relFilesDstSha1[sha1( $dstPathRel )] = 1;
+               }
+               unset( $dstPathsRel ); // free
+               // Get the list of missing files
+               $missingPathsRel = array();
+               foreach ( $srcPathsRel as $srcPathRel ) {
+                       if ( !isset( $relFilesDstSha1[sha1( $srcPathRel )] ) ) {
+                               $missingPathsRel[] = $srcPathRel;
+                       }
+               }
+               unset( $srcPathsRel ); // free
+
+               return $missingPathsRel;
+       }
+
+       /**
+        * @param array $srcPathsRel
+        * @param string $backendRel
+        * @param FileBackend $src
+        * @param FileBackend $dst
+        * @return void
+        */
        protected function copyFileBatch(
                array $srcPathsRel, $backendRel, FileBackend $src, FileBackend $dst
        ) {
@@ -169,8 +226,8 @@ class CopyFileBackend extends Maintenance {
                        $t_start = microtime( true );
                        $fsFiles = $src->getLocalReferenceMulti( array( 'srcs' => $srcPaths, 'latest' => 1 ) );
                        $ellapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
-                       $this->output( "\nDownloaded these file(s) [{$ellapsed_ms}ms]:\n" .
-                               implode( "\n", $srcPaths ) . "\n\n" );
+                       $this->output( "\n\tDownloaded these file(s) [{$ellapsed_ms}ms]:\n\t" .
+                               implode( "\n\t", $srcPaths ) . "\n\n" );
                }
 
                // Determine what files need to be copied over...
@@ -183,7 +240,7 @@ class CopyFileBackend extends Maintenance {
                        } elseif ( !$this->hasOption( 'missingonly' )
                                && $this->filesAreSame( $src, $dst, $srcPath, $dstPath ) )
                        {
-                               $this->output( "Already have $srcPathRel.\n" );
+                               $this->output( "\tAlready have $srcPathRel.\n" );
                                continue; // assume already copied...
                        }
                        $fsFile = array_key_exists( $srcPath, $fsFiles )
@@ -228,11 +285,55 @@ class CopyFileBackend extends Maintenance {
                        $this->error( print_r( $status->getErrorsArray(), true ) );
                        $this->error( "$wikiId: Could not copy file batch.", 1 ); // die
                } elseif ( count( $copiedRel ) ) {
-                       $this->output( "\nCopied these file(s) [{$ellapsed_ms}ms]:\n" .
-                               implode( "\n", $copiedRel ) . "\n\n" );
+                       $this->output( "\n\tCopied these file(s) [{$ellapsed_ms}ms]:\n\t" .
+                               implode( "\n\t", $copiedRel ) . "\n\n" );
+               }
+       }
+
+       /**
+        * @param array $dstPathsRel
+        * @param string $backendRel
+        * @param FileBackend $dst
+        * @return void
+        */
+       protected function delFileBatch(
+               array $dstPathsRel, $backendRel, FileBackend $dst
+       ) {
+               $ops = array();
+               $deletedRel = array(); // for output message
+               $wikiId = $dst->getWikiId();
+
+               // Determine what files need to be copied over...
+               foreach ( $dstPathsRel as $dstPathRel ) {
+                       $dstPath = $dst->getRootStoragePath() . "/$backendRel/$dstPathRel";
+                       $ops[] = array( 'op' => 'delete', 'src' => $dstPath );
+                       $deletedRel[] = $dstPathRel;
+               }
+
+               // Delete the batch of source files...
+               $t_start = microtime( true );
+               $status = $dst->doQuickOperations( $ops, array( 'bypassReadOnly' => 1 ) );
+               if ( !$status->isOK() ) {
+                       sleep( 10 ); // wait and retry copy again
+                       $status = $dst->doQuickOperations( $ops, array( 'bypassReadOnly' => 1 ) );
+               }
+               $ellapsed_ms = floor( ( microtime( true ) - $t_start ) * 1000 );
+               if ( !$status->isOK() ) {
+                       $this->error( print_r( $status->getErrorsArray(), true ) );
+                       $this->error( "$wikiId: Could not delete file batch.", 1 ); // die
+               } elseif ( count( $deletedRel ) ) {
+                       $this->output( "\n\tDeleted these file(s) [{$ellapsed_ms}ms]:\n\t" .
+                               implode( "\n\t", $deletedRel ) . "\n\n" );
                }
        }
 
+       /**
+        * @param FileBackend $src
+        * @param FileBackend $dst
+        * @param string $sPath
+        * @param string $dPath
+        * @return bool
+        */
        protected function filesAreSame( FileBackend $src, FileBackend $dst, $sPath, $dPath ) {
                $skipHash = $this->hasOption( 'skiphash' );
                $srcStat = $src->getFileStat( array( 'src' => $sPath ) );
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 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 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);
index 1c4dce4..5a2710a 100644 (file)
@@ -1,6 +1,7 @@
 CREATE TABLE profiling (
   pf_count   INTEGER         NOT NULL DEFAULT 0,
-  pf_time    NUMERIC(18,10)  NOT NULL DEFAULT 0,
+  pf_time    FLOAT           NOT NULL DEFAULT 0,
+  pf_memory  FLOAT           NOT NULL DEFAULT 0,
   pf_name    TEXT            NOT NULL,
   pf_server  TEXT            NULL
 );
index 4d44705..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
@@ -585,8 +591,8 @@ $mw$;
 -- This table is not used unless profiling is turned on
 CREATE TABLE profiling (
   pf_count   INTEGER         NOT NULL DEFAULT 0,
-  pf_time    NUMERIC(18,10)  NOT NULL DEFAULT 0,
-  pf_memory  NUMERIC(18,10)  NOT NULL DEFAULT 0,
+  pf_time    FLOAT           NOT NULL DEFAULT 0,
+  pf_memory  FLOAT           NOT NULL DEFAULT 0,
   pf_name    TEXT            NOT NULL,
   pf_server  TEXT            NULL
 );
index e1c6ab6..071ac09 100644 (file)
@@ -41,6 +41,7 @@ class PurgeChangedPages extends Maintenance {
                $this->addOption( 'starttime', 'Starting timestamp', true, true );
                $this->addOption( 'endtime', 'Ending timestamp', true, true );
                $this->addOption( 'htcp-dest', 'HTCP announcement destination (IP:port)', false, true );
+               $this->addOption( 'sleep-per-batch', 'Milliseconds to sleep between batches', false, true );
                $this->addOption( 'dry-run', 'Do not send purge requests' );
                $this->addOption( 'verbose', 'Show more output', false, false, 'v' );
                $this->setBatchSize( 100 );
@@ -135,8 +136,13 @@ class PurgeChangedPages extends Maintenance {
                        }
 
                        // Send batch of purge requests out to squids
-                       $squid = new SquidUpdate( $urls );
+                       $squid = new SquidUpdate( $urls, count( $urls ) );
                        $squid->doUpdate();
+
+                       if ( $this->hasOption( 'sleep-per-batch' ) ) {
+                               // sleep-per-batch is milliseconds, usleep wants micro seconds.
+                               usleep( 1000 * (int)$this->getOption( 'sleep-per-batch' ) );
+                       }
                }
 
                $this->output( "Done!\n" );
index 78c2e48..afd7c74 100644 (file)
@@ -59,7 +59,7 @@ class ShowJobs extends Maintenance {
                                $pending = $queue->getSize();
                                $claimed = $queue->getAcquiredCount();
                                $abandoned = $queue->getAbandonedCount();
-                               $active = $claimed - $abandoned;
+                               $active = max( 0, $claimed - $abandoned );
                                if ( ( $pending + $claimed ) > 0 ) {
                                        $this->output(
                                                "{$type}: $pending queued; " .
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 d37ca47..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,
 
@@ -1076,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 6f4b9f7..c033647 100644 (file)
@@ -92,9 +92,18 @@ return array(
                        'common/commonElements.css' => array( 'media' => 'screen' ),
                        'common/commonContent.css' => array( 'media' => 'screen' ),
                        'common/commonInterface.css' => array( 'media' => 'screen' ),
-                       'vector/screen.less' => array( 'media' => 'screen' ),
-                       'vector/externalLinks.less' => array( 'media' => 'screen' ),
-                       'vector/screen-hd.css' => array( 'media' => 'screen and (min-width: 982px)' ),
+                       'vector/styles.less',
+               ),
+               'remoteBasePath' => $GLOBALS['wgStylePath'],
+               'localBasePath' => $GLOBALS['wgStyleDirectory'],
+       ),
+       'skins.vector.beta' => array(
+               // Keep in sync with skins.vector
+               'styles' => array(
+                       'common/commonElements.css' => array( 'media' => 'screen' ),
+                       'common/commonContent.css' => array( 'media' => 'screen' ),
+                       'common/commonInterface.css' => array( 'media' => 'screen' ),
+                       'vector/styles-beta.less' => array( 'media' => 'screen' ),
                ),
                'remoteBasePath' => $GLOBALS['wgStylePath'],
                'localBasePath' => $GLOBALS['wgStyleDirectory'],
@@ -110,9 +119,6 @@ return array(
                'localBasePath' => $GLOBALS['wgStyleDirectory'],
        ),
        'skins.vector.collapsibleNav' => array(
-               'styles' => array(
-                       'vector/collapsibleNav.less',
-               ),
                'scripts' => array(
                        'vector/collapsibleNav.js',
                ),
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 498d134..b072616 100644 (file)
@@ -1,3 +1,4 @@
+@charset "UTF-8";
 /**
  * Provide Agora appearance for mw-ui-* classes when using a skin other than
  * Vector.
@@ -140,14 +141,14 @@ a.mw-ui-button {
   box-sizing: border-box;
   width: 290px;
 }
-/* line 19, sourcefiles/scss/components/default/_forms.scss */
+/* line 20, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div {
   display: block;
   margin: 0 0 15px 0;
   padding: 0;
   width: 100%;
 }
-/* line 27, sourcefiles/scss/components/default/_forms.scss */
+/* line 28, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div input,
 .mw-ui-vform > div .mw-ui-button {
   display: block;
@@ -157,7 +158,7 @@ a.mw-ui-button {
   margin: 0;
   width: 100%;
 }
-/* line 36, sourcefiles/scss/components/default/_forms.scss */
+/* line 37, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]) {
   border-style: solid;
   border-width: 1px;
@@ -174,7 +175,7 @@ a.mw-ui-button {
 .mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus:not([type=checkbox]):not([type=radio]) {
   outline: 0;
 }
-/* line 40, sourcefiles/scss/components/default/_forms.scss */
+/* line 41, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div label {
   display: block;
   -webkit-box-sizing: border-box;
@@ -190,7 +191,7 @@ a.mw-ui-button {
 .mw-ui-vform > div label * {
   font-weight: normal;
 }
-/* line 51, sourcefiles/scss/components/default/_forms.scss */
+/* line 52, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div input[type="checkbox"],
 .mw-ui-vform > div input[type="radio"] {
   display: inline;
@@ -199,8 +200,30 @@ a.mw-ui-button {
   box-sizing: content-box;
   width: auto;
 }
+/* line 63, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform .error {
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  font-size: 0.9em;
+  margin: 0 0 1em 0;
+  padding: 0.5em;
+  color: #cc0000;
+  border: 1px solid #fac5c5;
+  background-color: #fae3e3;
+  text-shadow: 0 1px #fae3e3;
+  word-wrap: break-word;
+}
+
+/* line 86, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform-div {
+  display: block;
+  margin: 0 0 15px 0;
+  padding: 0;
+  width: 100%;
+}
 
-/* line 67, sourcefiles/scss/components/default/_forms.scss */
+/* line 96, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-input {
   border-style: solid;
   border-width: 1px;
@@ -218,7 +241,7 @@ a.mw-ui-button {
   outline: 0;
 }
 
-/* line 74, sourcefiles/scss/components/default/_forms.scss */
+/* line 103, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-label {
   font-size: 0.9em;
   color: #4a4a4a;
@@ -228,7 +251,7 @@ a.mw-ui-button {
   font-weight: normal;
 }
 
-/* line 83, sourcefiles/scss/components/default/_forms.scss */
+/* line 112, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-checkbox-label, .mw-ui-radio-label {
   margin-bottom: 0.5em;
   cursor: pointer;
index 6aa7f89..fd9e091 100644 (file)
@@ -1,3 +1,4 @@
+@charset "UTF-8";
 /**
  * Provide Agora appearance for mw-ui-* classes when using the Vector skin.
  * Compass builds these Agora styles from source Sass files in
@@ -268,14 +269,14 @@ a.mw-ui-button {
   box-sizing: border-box;
   width: 290px;
 }
-/* line 19, sourcefiles/scss/components/default/_forms.scss */
+/* line 20, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div {
   display: block;
   margin: 0 0 15px 0;
   padding: 0;
   width: 100%;
 }
-/* line 27, sourcefiles/scss/components/default/_forms.scss */
+/* line 28, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div input,
 .mw-ui-vform > div .mw-ui-button {
   display: block;
@@ -285,7 +286,7 @@ a.mw-ui-button {
   margin: 0;
   width: 100%;
 }
-/* line 36, sourcefiles/scss/components/default/_forms.scss */
+/* line 37, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]) {
   border-style: solid;
   border-width: 1px;
@@ -302,7 +303,7 @@ a.mw-ui-button {
 .mw-ui-vform > div input:not([type=button]):not([type=submit]):not([type=file]):focus:not([type=checkbox]):not([type=radio]) {
   outline: 0;
 }
-/* line 40, sourcefiles/scss/components/default/_forms.scss */
+/* line 41, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div label {
   display: block;
   -webkit-box-sizing: border-box;
@@ -318,7 +319,7 @@ a.mw-ui-button {
 .mw-ui-vform > div label * {
   font-weight: normal;
 }
-/* line 51, sourcefiles/scss/components/default/_forms.scss */
+/* line 52, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-vform > div input[type="checkbox"],
 .mw-ui-vform > div input[type="radio"] {
   display: inline;
@@ -327,8 +328,30 @@ a.mw-ui-button {
   box-sizing: content-box;
   width: auto;
 }
+/* line 63, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform .error {
+  -webkit-box-sizing: border-box;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  font-size: 0.9em;
+  margin: 0 0 1em 0;
+  padding: 0.5em;
+  color: #cc0000;
+  border: 1px solid #fac5c5;
+  background-color: #fae3e3;
+  text-shadow: 0 1px #fae3e3;
+  word-wrap: break-word;
+}
+
+/* line 86, sourcefiles/scss/components/default/_forms.scss */
+.mw-ui-vform-div {
+  display: block;
+  margin: 0 0 15px 0;
+  padding: 0;
+  width: 100%;
+}
 
-/* line 67, sourcefiles/scss/components/default/_forms.scss */
+/* line 96, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-input {
   border-style: solid;
   border-width: 1px;
@@ -346,7 +369,7 @@ a.mw-ui-button {
   outline: 0;
 }
 
-/* line 74, sourcefiles/scss/components/default/_forms.scss */
+/* line 103, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-label {
   font-size: 0.9em;
   color: #4a4a4a;
@@ -356,7 +379,7 @@ a.mw-ui-button {
   font-weight: normal;
 }
 
-/* line 83, sourcefiles/scss/components/default/_forms.scss */
+/* line 112, sourcefiles/scss/components/default/_forms.scss */
 .mw-ui-checkbox-label, .mw-ui-radio-label {
   margin-bottom: 0.5em;
   cursor: pointer;
index dfcd36f..a9cec39 100644 (file)
@@ -16,6 +16,7 @@ $defaultFormWidth: $captchaContainerWidth;
 
     width: $defaultFormWidth;
 
+    // Immediate divs in a vform are block and spaced-out.
     & > div {
         display: block;
         margin: 0 0 15px 0;
@@ -55,12 +56,40 @@ $defaultFormWidth: $captchaContainerWidth;
         }
 
     }
+
+    // HTMLForm uses error, SpecialUserlogin (login and create account) uses
+    // errorbox.
+    // TODO move errorbox from mediawiki.special.vforms.css into here.
+    .error {
+        -webkit-box-sizing: border-box;
+        -moz-box-sizing: border-box;
+        box-sizing: border-box;
+        font-size: 0.9em;
+        margin: 0 0 1em 0;
+        padding: 0.5em;
+        color: #cc0000;
+        border: 1px solid #fac5c5;
+        background-color: #fae3e3;
+        text-shadow: 0 1px #fae3e3;
+        word-wrap: break-word;
+    }
 }
 
 // --------------------------------------------------------------------------
 // Elements
 // --------------------------------------------------------------------------
 
+// Apply this to individual elements to style them.
+// You generally don't need to use this class on divs within an Agora
+// form container such as mw-ui-vform
+// XXX DRY: This repeats earlier styling, use an @include agora-div-styling ?
+.mw-ui-vform-div {
+       display: block;
+       margin: 0 0 15px 0;
+       padding: 0;
+       width: 100%;
+}
+
 // Apply mw-ui-input to individual input fields to style them.
 // You generally don't need to use this class if <input> is within an Agora
 // form container such as mw-ui-vform
index 346ab33..b236019 100644 (file)
         *     var title = mw.Title.newFromImg( $( 'img:first' ) );
         *
         * @static
-        * @param {HTMLImageElement|jQuery} img The image to use as a base.
-        * @return {mw.Title|null} The file title - null if unsuccessful.
+        * @param {HTMLElement|jQuery} img The image to use as a base
+        * @return {mw.Title|null} The file title or null if unsuccessful
         */
        Title.newFromImg = function ( img ) {
                var matches, i, regex, src, decodedSrc,
 
                        recount = regexes.length;
 
-               img = img.jquery ? img.get( 0 ) : img;
-
-               src = img.src;
+               src = img.jquery ? img[0].src : img.src;
 
                matches = src.match( thumbPhpRegex );
 
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 c07a593..288b5fd 100644 (file)
@@ -66,7 +66,10 @@ class SkinVector extends SkinTemplate {
         */
        function setupSkinUserCss( OutputPage $out ) {
                parent::setupSkinUserCss( $out );
-               $out->addModuleStyles( 'skins.vector' );
+
+               $styles = array( 'skins.vector' );
+               wfRunHooks( 'SkinVectorStyleModules', array( &$this, &$styles ) );
+               $out->addModuleStyles( $styles );
        }
 
        /**
diff --git a/skins/vector/beta/screen.less b/skins/vector/beta/screen.less
new file mode 100644 (file)
index 0000000..6d56cd5
--- /dev/null
@@ -0,0 +1,75 @@
+/* Content */
+#content {
+       line-height: 1.5em;
+       .mw-editsection {
+               font-family: @content-font-family;
+       }
+
+       h1,
+       #firstHeading {
+               font-family: @content-heading-font-family;
+               font-size: 1.833em;
+               line-height: 22pt;
+               padding: 0;
+               margin-bottom: 4pt;
+       }
+
+       h2 {
+               font-size: 1.5em;
+               line-height: 22pt;
+       }
+
+       h2,
+       h3,
+       h4,
+       h5,
+       h6 {
+               font-family: @content-heading-font-family;
+               padding: 0;
+               margin-bottom: 4pt;
+               margin-top: 14pt;
+       }
+
+       h3 {
+               font-size: 1.17em;
+               line-height: 22pt;
+       }
+
+       h3,
+       h4 {
+               font-weight: bold;
+       }
+
+       h4,
+       h5,
+       h6 {
+               font-size: 100%; /* (reset) */
+       }
+
+       h6 {
+               font-style: italic;
+       }
+
+       p {
+               margin-bottom: 8pt;
+       }
+
+       // FIXME: this is hacky
+       #toc h2 {
+               font-size: 100%;
+       }
+}
+
+/* Personal menu */
+#p-personal a {
+       color: #555;
+}
+
+/* Main menu */
+div#mw-panel div.portal {
+       margin-left: 1.25em;
+       h3 {
+               margin: 0;
+               line-height: 1;
+       }
+}
diff --git a/skins/vector/beta/variables.less b/skins/vector/beta/variables.less
new file mode 100644 (file)
index 0000000..3462795
--- /dev/null
@@ -0,0 +1,37 @@
+@html-font-size: 90%;
+
+@body-font-size: inherit;
+@body-font-color: inherit;
+
+// Page content
+@content-font-family: "Helvetica Neue", "Helvetica", "Nimbus Sans L", "Arial", "Liberation Sans", sans-serif;
+@content-font-size: 0.9em;
+@content-line-height: inherit;
+@content-padding: 1em;
+@content-heading-font-size: 1.6em;
+@content-heading-font-family: Georgia, "DejaVu Serif", serif;
+
+// Common menu
+@menu-link-color: #555;
+
+// Main menu
+@menu-main-font-size: 0.82em;
+@menu-main-heading-font-size: 100%;
+@menu-main-heading-padding: 5px 0;
+
+@menu-main-body-font-size: inherit;
+@menu-main-body-link-color: inherit;
+@menu-main-body-link-visited-color: inherit;
+@menu-main-body-margin: 0;
+@menu-main-body-padding: 0 0 10px;
+@menu-main-logo-left: 1.6em;
+
+// Personal menu
+@menu-personal-font-size: 0.75em;
+
+// Collapsible nav
+@collapsible-nav-heading-color: #555;
+@collapsible-nav-heading-collapsed-color: inherit;
+
+@collapsible-nav-heading-padding: 4px 0 3px 1.5em;
+@collapsible-nav-body-margin: 0 0 0 1.25em;
index 25ebec7..e6f5c9a 100644 (file)
                margin: -11px 9px 10px 11px;
 
                h3 {
-                       color: #4D4D4D;
+                       font-size: @menu-main-heading-font-size;
+                       color: @collapsible-nav-heading-color;
                        font-weight: normal;
                        background-position: left center;
                        background-repeat: no-repeat;
                        .background-image-svg('images/arrow-expanded.svg', 'images/arrow-expanded.png');
-                       padding: 4px 0 3px 1.5em;
+                       padding: @collapsible-nav-heading-padding;
                        margin-bottom: 0;
 
                        &:hover {
                        }
 
                        a {
-                               color: #4D4D4D;
+                               color: @collapsible-nav-heading-color;
                                text-decoration: none;
                        }
                }
 
                .body {
+                       margin: @collapsible-nav-body-margin;
                        background-image: none !important;
                        padding-top: 0;
                        display: none;
@@ -70,7 +72,7 @@
                /* Collapsed */
                &.collapsed {
                        h3 {
-                               color: #0645AD;
+                               color: @collapsible-nav-heading-collapsed-color;
                                background-position: left center;
                                background-repeat: no-repeat;
                                .background-image-svg('images/arrow-collapsed-ltr.svg', 'images/arrow-collapsed-ltr.png');
@@ -81,7 +83,7 @@
                                }
 
                                a {
-                                       color: #0645AD;
+                                       color: @collapsible-nav-heading-collapsed-color;
                                }
                        }
                }
diff --git a/skins/vector/screen-hd.css b/skins/vector/screen-hd.css
deleted file mode 100644 (file)
index 7dbb1ba..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Vector screen styles for high definition displays */
-
-div#content {
-       margin-left: 11em;
-       padding: 1.25em 1.5em 1.5em 1.5em;
-}
-#p-logo {
-       left: 0.5em;
-}
-div#footer {
-       margin-left: 11em;
-       padding: 1.25em;
-}
-#mw-panel {
-       padding-left: 0.5em;
-}
-#p-search {
-       margin-right: 1em;
-}
-#left-navigation {
-       margin-left: 11em;
-}
-#p-personal {
-       right: 1em;
-}
-#mw-head-base {
-       margin-left: 11em;
-}
diff --git a/skins/vector/screen-hd.less b/skins/vector/screen-hd.less
new file mode 100644 (file)
index 0000000..5a1fc05
--- /dev/null
@@ -0,0 +1,28 @@
+/* Vector screen styles for high definition displays */
+
+div#content {
+       margin-left: 11em;
+       padding: 1.25em 1.5em 1.5em 1.5em;
+}
+#p-logo {
+       left: @menu-main-logo-left;
+}
+div#footer {
+       margin-left: 11em;
+       padding: 1.25em;
+}
+#mw-panel {
+       padding-left: 0.5em;
+}
+#p-search {
+       margin-right: 1em;
+}
+#left-navigation {
+       margin-left: 11em;
+}
+#p-personal {
+       right: 1em;
+}
+#mw-head-base {
+       margin-left: 11em;
+}
index 0b65627..f5cf5e5 100644 (file)
@@ -1,40 +1,43 @@
 /*
  * Any rules which should not be flipped automatically in right-to-left situations should be
- * prepended with @noflip in a comment block. Images that should be embedded as base64 data-URLs
- * should be prepended with @embed in a comment block.
+ * prepended with @noflip in a comment block.
  *
- * This style-sheet employs a few CSS trick to accomplish compatibility with a wide range of web
+ * This stylesheet employs a few CSS trick to accomplish compatibility with a wide range of web
  * browsers. The most common trick is to use some styles in IE6 only. This is accomplished by using
  * a rule that makes things work in IE6, and then following it with a rule that begins with
  * "html > body" or use a child selector ">", which is ignored by IE6 because it does not support
  * the child selector. You can spot this by looking for the "OVERRIDDEN BY COMPLIANT BROWSERS" and
  * "IGNORED BY IE6" comments.
  */
-@import "mediawiki.mixins.less";
+@import "mediawiki.mixins";
 
 /* Framework */
+html {
+       font-size: @html-font-size;
+}
 html,
 body {
        height: 100%;
        margin: 0;
        padding: 0;
-       font-family: sans-serif;
-       font-size: 1em;
+       font-family: @content-font-family;
 }
 body {
        background-color: #f6f6f6;
+       font-size: @body-font-size;
 }
 /* Content */
 div#content {
+       line-height: @content-line-height;
        margin-left: 10em;
-       padding: 1em;
+       padding: @content-padding;
        /* Border on top, left, and bottom side */
        border: 1px solid #a7d7f9;
        border-right-width: 0;
        /* Merge the border with tabs' one (in their background image) */
        margin-top: -1px;
        background-color: white;
-       color: black;
+       color: @body-font-color;
        direction: ltr;
 }
 /* Hide, but keep accessible for screen-readers */
@@ -86,16 +89,13 @@ div.emptyPortlet {
        margin: 0;
        padding-left: 10em; /* Keep from overlapping logo */
 }
-/* @noflip */
 #p-personal li {
        line-height: 1.125em;
+       /* @noflip */
        float: left;
-}
-/* This one flips! */
-#p-personal li {
        margin-left: 0.75em;
        margin-top: 0.5em;
-       font-size: 0.75em;
+       font-size: @menu-personal-font-size;
        white-space: nowrap;
 }
 /* Navigation Containers */
@@ -122,8 +122,8 @@ div.vectorMenu h3 span {
        display: none;
 }
 /* Namespaces and Views */
-/* @noflip */
 div.vectorTabs {
+       /* @noflip */
        float: left;
        height: 2.5em;
 }
@@ -133,23 +133,19 @@ div.vectorTabs {
        background-repeat: no-repeat;
        padding-left: 1px;
 }
-/* @noflip */
 div.vectorTabs ul {
+       /* @noflip */
        float: left;
-}
-div.vectorTabs ul {
        height: 100%;
        list-style-type: none;
        list-style-image: none;
        margin: 0;
        padding: 0;
 }
-/* @noflip */
-div.vectorTabs ul li {
-       float: left;
-}
 /* OVERRIDDEN BY COMPLIANT BROWSERS */
 div.vectorTabs ul li {
+       /* @noflip */
+       float: left;
        line-height: 1.125em;
        display: inline-block;
        height: 100%;
@@ -174,7 +170,7 @@ div.vectorTabs li a {
        height: 1.9em;
        padding-left: 0.5em;
        padding-right: 0.5em;
-       color: #0645ad;
+       color: @menu-link-color;
        cursor: pointer;
        font-size: 0.8em;
 }
@@ -192,8 +188,8 @@ div.vectorTabs span a  {
        padding-top: 1.25em;
 }
 /* IGNORED BY IE6 */
-/* @noflip */
 div.vectorTabs span > a {
+       /* @noflip */
        float: left;
        display: block;
 }
@@ -213,13 +209,15 @@ div.vectorTabs li.new a:visited{
        color: #a55858;
 }
 /* Variants and Actions */
-/* @noflip */
 div.vectorMenu {
+       /* @noflip */
        direction: ltr;
+       /* @noflip */
        float: left;
        /* SVG support using a transparent gradient to guarantee cross-browser
         * compatibility (browsers able to understand gradient syntax support also SVG) */
        .background-image-svg('images/arrow-down-icon.svg', 'images/arrow-down-icon.png');
+       /* @noflip */
        background-position: 100% 60%;
        background-repeat: no-repeat;
        cursor: pointer;
@@ -230,19 +228,16 @@ div.vectorMenuFocus {
        .background-image-svg('images/arrow-down-focus-icon.svg', 'images/arrow-down-focus-icon.png');
        background-position: 100% 60%;
 }
-/* @noflip */
 body.rtl div.vectorMenu {
+       /* @noflip */
        direction: rtl;
 }
 /* OVERRIDDEN BY COMPLIANT BROWSERS */
-/* @noflip */
 div#mw-head div.vectorMenu h3 {
+       /* @noflip */
        float: left;
        .background-image('images/tab-break.png');
        background-repeat: no-repeat;
-}
-/* This will be flipped - unlike the one above it */
-div#mw-head div.vectorMenu h3 {
        background-position: bottom left;
        margin-left: -1px;
 }
@@ -261,7 +256,6 @@ div.vectorMenu#p-variants #mw-vector-current-variant {
        border: none;
 }
 /* OVERRIDDEN BY COMPLIANT BROWSERS */
-/* @noflip */
 div.vectorMenu h3 a {
        display: inline-block;
        width: 24px;
@@ -269,9 +263,6 @@ div.vectorMenu h3 a {
        text-decoration: none;
        .background-image('images/tab-break.png');
        background-repeat: no-repeat;
-}
-/* This will be flipped - unlike the one above it */
-div.vectorMenu h3 a {
        background-position: bottom right;
 }
 /* IGNORED BY IE6 */
@@ -285,20 +276,20 @@ div.vectorMenu div.menu {
        text-align: left;
 }
 /* OVERRIDDEN BY COMPLIANT BROWSERS */
-/* @noflip */
 body.rtl div.vectorMenu div.menu {
+       /* @noflip */
        margin-left: 24px;
 }
 /* IGNORED BY IE6 */
-/* @noflip */
 body.rtl div.vectorMenu > div.menu {
+       /* @noflip */
        margin-left: auto;
 }
 /* IGNORED BY IE6 */
 /* Also fixes old versions of FireFox */
-/* @noflip */
 body.rtl div.vectorMenu > div.menu,
 x:-moz-any-link {
+       /* @noflip */
        margin-left: 23px;
 }
 /* Enable forcing showing of the menu for accessibility */
@@ -340,7 +331,7 @@ div.vectorMenu li a {
        display: inline-block;
        padding: 0.5em;
        white-space: nowrap;
-       color: #0645ad;
+       color: @menu-link-color;
        cursor: pointer;
        font-size: 0.8em;
 }
@@ -357,8 +348,8 @@ div.vectorMenu li.selected a:visited {
 #p-search h3 {
        display: none;
 }
-/* @noflip */
 #p-search {
+       /* @noflip */
        float: left;
 }
 #p-search {
@@ -448,6 +439,7 @@ div#simpleSearch button#searchButton > img {
 }
 /* Panel */
 div#mw-panel {
+       font-size: @menu-main-font-size;
        position: absolute;
        top: 160px;
        padding-top: 1em;
@@ -461,17 +453,15 @@ div#mw-panel div.portal {
 div#mw-panel div.portal h3 {
        font-weight: normal;
        color: #444;
-       padding: 0.25em;
-       padding-top: 0;
-       padding-left: 1.75em;
+       padding: @menu-main-heading-padding;
        cursor: default;
        border: none;
-       font-size: 0.75em;
+       font-size: @menu-main-heading-font-size;
 }
 div#mw-panel div.portal div.body {
-       margin: 0;
        padding-top: 0.5em;
-       margin-left: 1.25em;
+       margin: @menu-main-body-margin;
+
        .background-image('images/portal-break.png');
        background-repeat: no-repeat;
        background-position: top left;
@@ -479,7 +469,7 @@ div#mw-panel div.portal div.body {
 div#mw-panel div.portal div.body ul {
        list-style-type: none;
        list-style-image: none;
-       padding: 0;
+       padding: @menu-main-body-padding;
        margin: 0;
 }
 div#mw-panel div.portal div.body ul li {
@@ -487,15 +477,16 @@ div#mw-panel div.portal div.body ul li {
        padding: 0;
        padding-bottom: 0.5em;
        margin: 0;
-       font-size: 0.75em;
+       font-size: @menu-main-body-font-size;
        word-wrap: break-word;
 }
 div#mw-panel div.portal div.body ul li a {
-       color: #0645ad;
-}
-div#mw-panel div.portal div.body ul li a:visited {
-       color: #0b0080;
+       color: @menu-main-body-link-color;
+       &:visited {
+               color: @menu-main-body-link-visited-color;
+       }
 }
+
 /* Footer */
 div#footer {
        margin-left: 10em;
@@ -520,8 +511,9 @@ div#footer ul li {
 div#footer #footer-icons {
        float: right;
 }
-/* @noflip */
+
 body.ltr div#footer #footer-places {
+       /* @noflip */
        float: left;
 }
 div#footer #footer-info li {
@@ -596,7 +588,7 @@ div#footer #footer-places li {
        #preftoc a:active {
                display: inline-block;
                position: relative;
-               color: #0645ad;
+               color: @menu-link-color;
                padding: 0.5em;
                text-decoration: none;
                background-image: none;
@@ -653,16 +645,6 @@ div#footer #footer-places li {
        margin-right: 0.25em;
 }
 
-/**
- * The following code is slightly modified from monobook
- */
-div#content {
-       line-height: 1.5em;
-}
-#bodyContent {
-       font-size: 0.8em;
-}
-
 ul {
        list-style-type: disc;
        .list-style-image('images/bullet-icon.png');
@@ -679,7 +661,7 @@ pre, .mw-code {
 #firstHeading {
        padding-top: 0;
        margin-top: 0;
-       font-size: 1.6em;
+       font-size: @content-heading-font-size;
 }
 
 /* Icon for Usernames */
@@ -705,9 +687,8 @@ pre, .mw-code {
 #bodyContent {
        position: relative;
        width: 100%;
-}
-div#bodyContent {
        line-height: 1.5em;
+       font-size: @content-font-size;
 }
 
 /* mediawiki.notification */
@@ -783,21 +764,26 @@ body.vector-animateLayout {
        div#content,
        div#footer,
        #left-navigation {
-               .transition( margin-left 250ms, padding 250ms; );
+               .transition(margin-left 250ms, padding 250ms;);
        }
+
        #p-logo {
-               .transition( left 250ms );
+               .transition(left 250ms);
        }
+
        #mw-panel {
-               .transition( padding-right 250ms );
+               .transition(padding-right 250ms);
        }
+
        #p-search {
-               .transition( margin-right 250ms );
+               .transition(margin-right 250ms);
        }
+
        #p-personal {
-               .transition( right 250ms );
+               .transition(right 250ms);
        }
+
        #mw-head-base {
-               .transition( margin-left 250ms );
+               .transition(margin-left 250ms);
        }
 }
diff --git a/skins/vector/styles-beta.less b/skins/vector/styles-beta.less
new file mode 100644 (file)
index 0000000..62e3d55
--- /dev/null
@@ -0,0 +1,9 @@
+@import "variables.less";
+@import "beta/variables.less";
+@import "screen.less";
+@import "beta/screen.less";
+@import "externalLinks.less";
+@import "collapsibleNav.less";
+@media screen and (min-width: 982px) {
+       @import "screen-hd.less";
+}
diff --git a/skins/vector/styles.less b/skins/vector/styles.less
new file mode 100644 (file)
index 0000000..a5db63c
--- /dev/null
@@ -0,0 +1,8 @@
+@import "variables.less";
+@import "screen.less";
+@import "externalLinks.less";
+@import "collapsibleNav.less";
+
+@media screen and (min-width: 982px) {
+       @import "screen-hd.less";
+}
diff --git a/skins/vector/variables.less b/skins/vector/variables.less
new file mode 100644 (file)
index 0000000..fac7f82
--- /dev/null
@@ -0,0 +1,37 @@
+@html-font-size: 1em;
+
+@body-font-size: 1em;
+@body-font-color: #252525;
+
+// Page content
+@content-font-family: sans-serif;
+@content-font-size: 0.8em;
+@content-line-height: 1.5em;
+@content-padding: 1.5em 1.5em 1.5em 1.75em;
+@content-heading-font-size: 1.6em;
+@content-heading-font-family: sans-serif;
+
+// Common menu
+@menu-link-color: #0645ad;
+
+// Main menu
+@menu-main-font-size: inherit;
+@menu-main-heading-font-size: 0.75em;
+@menu-main-heading-padding: 0 1.75em 0.25em 0.25em;
+
+@menu-main-body-font-size: 0.75em;
+@menu-main-body-link-color: #0645ad;
+@menu-main-body-link-visited-color: #0b0080;
+@menu-main-body-margin: 0 0 0 1.25em;
+@menu-main-body-padding: 0;
+@menu-main-logo-left: 0.5em;
+
+// Personal menu
+@menu-personal-font-size: 0.75em;
+
+// Collapsible nav
+@collapsible-nav-heading-color: #4D4D4D;
+@collapsible-nav-heading-collapsed-color: #0645AD;
+
+@collapsible-nav-heading-padding: 4px 0 3px 1.5em;
+@collapsible-nav-body-margin: 0 0 0 1.25em;
index 4e6d3ea..5ade250 100644 (file)
@@ -60,18 +60,30 @@ class LinksUpdateTest extends MediaWikiTestCase {
                $po->addLink( Title::newFromText( "linksupdatetest:Foo" ) ); // interwiki link should be ignored
                $po->addLink( Title::newFromText( "#Foo" ) ); // hash link should be ignored
 
-               $this->assertLinksUpdate( $t, $po, 'pagelinks', 'pl_namespace, pl_title', 'pl_from = 111', array(
+               $update = $this->assertLinksUpdate( $t, $po, 'pagelinks', 'pl_namespace, pl_title', 'pl_from = 111', array(
                        array( NS_MAIN, 'Foo' ),
                ) );
+               $this->assertArrayEquals( array(
+                       Title::makeTitle( NS_MAIN, 'Foo' ),  // newFromText doesn't yield the same internal state....
+               ), $update->getAddedLinks() );
 
                $po = new ParserOutput();
                $po->setTitleText( $t->getPrefixedText() );
 
                $po->addLink( Title::newFromText( "Bar" ) );
+               $po->addLink( Title::newFromText( "Talk:Bar" ) );
 
-               $this->assertLinksUpdate( $t, $po, 'pagelinks', 'pl_namespace, pl_title', 'pl_from = 111', array(
+               $update = $this->assertLinksUpdate( $t, $po, 'pagelinks', 'pl_namespace, pl_title', 'pl_from = 111', array(
                        array( NS_MAIN, 'Bar' ),
+                       array( NS_TALK, 'Bar' ),
                ) );
+               $this->assertArrayEquals( array(
+                       Title::makeTitle( NS_MAIN, 'Bar' ),
+                       Title::makeTitle( NS_TALK, 'Bar' ),
+               ), $update->getAddedLinks() );
+               $this->assertArrayEquals( array(
+                       Title::makeTitle( NS_MAIN, 'Foo' ),
+               ), $update->getRemovedLinks() );
        }
 
        public function testUpdate_externallinks() {
@@ -158,5 +170,6 @@ class LinksUpdateTest extends MediaWikiTestCase {
                $update->commitTransaction();
 
                $this->assertSelect( $table, $fields, $condition, $expectedRows );
+               return $update;
        }
 }
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' ) {
index 4ffefb6..913adc1 100644 (file)
--- a/thumb.php
+++ b/thumb.php
@@ -304,6 +304,12 @@ function wfStreamThumb( array $params ) {
                return;
        }
 
+       $user = RequestContext::getMain()->getUser();
+       if ( $user->pingLimiter( 'renderfile' ) ) {
+               wfThumbError( 500, wfMessage( 'actionthrottledtext' ) );
+               return;
+       }
+
        // Thumbnail isn't already there, so create the new thumbnail...
        try {
                $thumb = $img->transform( $params, File::RENDER_NOW );