Merge "Don't offer create link for searches with syntax"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 5 Feb 2014 17:59:14 +0000 (17:59 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 5 Feb 2014 17:59:14 +0000 (17:59 +0000)
76 files changed:
CREDITS
RELEASE-NOTES-1.23
includes/AjaxResponse.php
includes/AutoLoader.php
includes/DefaultSettings.php
includes/EditPage.php
includes/Exception.php
includes/GlobalFunctions.php
includes/OutputPage.php
includes/Preferences.php
includes/SquidPurgeClient.php
includes/User.php
includes/WikiPage.php
includes/api/ApiBase.php
includes/api/ApiMain.php
includes/api/ApiOptions.php
includes/api/ApiPageSet.php
includes/api/ApiPurge.php
includes/api/ApiQueryAllCategories.php
includes/api/ApiQueryAllLinks.php
includes/api/ApiQueryAllPages.php
includes/api/ApiQueryDeletedrevs.php
includes/api/ApiQueryImageInfo.php
includes/api/ApiQuerySiteinfo.php
includes/api/ApiQueryUserContributions.php
includes/api/ApiRevisionDelete.php [new file with mode: 0644]
includes/api/ApiWatch.php
includes/cache/HTMLFileCache.php
includes/cache/LocalisationCache.php
includes/clientpool/RedisConnectionPool.php
includes/db/DatabaseMssql.php
includes/db/DatabasePostgres.php
includes/db/LoadBalancer.php
includes/debug/Debug.php
includes/deferred/SquidUpdate.php
includes/externalstore/ExternalStoreDB.php
includes/filebackend/SwiftFileBackend.php
includes/htmlform/HTMLForm.php
includes/installer/MysqlUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/media/Exif.php
includes/objectcache/RedisBagOStuff.php
includes/revisiondelete/RevisionDelete.php
includes/revisiondelete/RevisionDeleteAbstracts.php
includes/specialpage/SpecialPageFactory.php
includes/specials/SpecialActiveusers.php
includes/specials/SpecialPasswordReset.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialSearch.php
includes/upload/UploadFromChunks.php
languages/messages/MessagesBe_tarask.php
languages/messages/MessagesCe.php
languages/messages/MessagesDe.php
languages/messages/MessagesDiq.php
languages/messages/MessagesEn.php
languages/messages/MessagesFrr.php
languages/messages/MessagesKk_cyrl.php
languages/messages/MessagesLad.php
languages/messages/MessagesMl.php
languages/messages/MessagesQqq.php
languages/messages/MessagesQu.php
languages/messages/MessagesRo.php
languages/messages/MessagesTe.php
languages/messages/MessagesVi.php
maintenance/benchmarks/benchmarkParse.php [new file with mode: 0644]
maintenance/language/messages.inc
maintenance/updateSpecialPages.php
resources/mediawiki.ui/components/default/buttons.less
skins/vector/components/search.less
skins/vector/images/search-ltr.svg
skins/vector/images/search-rtl.svg
skins/vector/screen-hd.less
skins/vector/variables.less
tests/phpunit/includes/TitleTest.php
tests/phpunit/includes/api/ApiOptionsTest.php

diff --git a/CREDITS b/CREDITS
index eee4794..6e28d6d 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -129,6 +129,7 @@ following names for their contribution to the product.
 * Ebrahim Byagowi
 * Edward Z. Yang
 * Elvis Stansvik
+* Eranroz
 * Erwin Dokter
 * Federico Leva
 * FunPika
index cd602a1..59118bd 100644 (file)
@@ -117,6 +117,8 @@ production.
 * (bug 56199) Raw option of parser functions must now match complete word,
   to take effect.
 * (bug 60543) Special:PrefixIndex forgot stripprefix=1 for "Next page" link
+* (bug 29762) Undoing an already-undone edit will now display an appropriate
+  message instead of leading the user to make a null edit.
 
 === Web API changes in 1.23 ===
 * (bug 54884) action=parse&prop=categories now indicates hidden and missing
@@ -153,6 +155,9 @@ production.
   list=usercontribs.
 * list=watchlist now uses the querying user's rights rather than the wlowner's
   rights when checking whether wlprop=patrol is allowed.
+* (bug 32151) ApiWatch now has pageset capabilities (titles/pageids/generators).
+  Title parameter is now deprecated.
+* (bug 23005) Added action=revisiondelete.
 
 === Languages updated in 1.23 ===
 
index 037ef9a..ac8a9f0 100644 (file)
@@ -220,7 +220,7 @@ class AjaxResponse {
                }
 
                if ( !$wgCachePages ) {
-                       wfDebug( "$fname: CACHE DISABLED\n", false );
+                       wfDebug( "$fname: CACHE DISABLED\n", 'log' );
                        return false;
                }
 
@@ -234,8 +234,8 @@ class AjaxResponse {
                        $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
                        $modsinceTime = strtotime( $modsince );
                        $ismodsince = wfTimestamp( TS_MW, $modsinceTime ? $modsinceTime : 1 );
-                       wfDebug( "$fname: -- client send If-Modified-Since: " . $modsince . "\n", false );
-                       wfDebug( "$fname: --  we might send Last-Modified : $lastmod\n", false );
+                       wfDebug( "$fname: -- client send If-Modified-Since: " . $modsince . "\n", 'log' );
+                       wfDebug( "$fname: --  we might send Last-Modified : $lastmod\n", 'log' );
 
                        if ( ( $ismodsince >= $timestamp ) && $wgUser->validateCache( $ismodsince ) && $ismodsince >= $wgCacheEpoch ) {
                                ini_set( 'zlib.output_compression', 0 );
@@ -243,15 +243,15 @@ class AjaxResponse {
                                $this->disable();
                                $this->mLastModified = $lastmod;
 
-                               wfDebug( "$fname: CACHED client: $ismodsince ; user: {$wgUser->getTouched()} ; page: $timestamp ; site $wgCacheEpoch\n", false );
+                               wfDebug( "$fname: CACHED client: $ismodsince ; user: {$wgUser->getTouched()} ; page: $timestamp ; site $wgCacheEpoch\n", 'log' );
 
                                return true;
                        } else {
-                               wfDebug( "$fname: READY  client: $ismodsince ; user: {$wgUser->getTouched()} ; page: $timestamp ; site $wgCacheEpoch\n", false );
+                               wfDebug( "$fname: READY  client: $ismodsince ; user: {$wgUser->getTouched()} ; page: $timestamp ; site $wgCacheEpoch\n", 'log' );
                                $this->mLastModified = $lastmod;
                        }
                } else {
-                       wfDebug( "$fname: client did not send If-Modified-Since header\n", false );
+                       wfDebug( "$fname: client did not send If-Modified-Since header\n", 'log' );
                        $this->mLastModified = $lastmod;
                }
                return false;
index 0f1cded..61e0941 100644 (file)
@@ -365,6 +365,7 @@ $wgAutoloadLocalClasses = array(
        'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php',
        'ApiQueryWatchlistRaw' => 'includes/api/ApiQueryWatchlistRaw.php',
        'ApiResult' => 'includes/api/ApiResult.php',
+       'ApiRevisionDelete' => 'includes/api/ApiRevisionDelete.php',
        'ApiRollback' => 'includes/api/ApiRollback.php',
        'ApiRsd' => 'includes/api/ApiRsd.php',
        'ApiSetNotificationTimestamp' => 'includes/api/ApiSetNotificationTimestamp.php',
index 326a1c2..d0ceafc 100644 (file)
@@ -6162,7 +6162,8 @@ $wgJobQueueAggregator = array(
  * Expensive Querypages are already updated.
  */
 $wgSpecialPageCacheUpdates = array(
-       'Statistics' => array( 'SiteStatsUpdate', 'cacheUpdate' )
+       'Statistics' => array( 'SiteStatsUpdate', 'cacheUpdate' ),
+       'Activeusers' => array( 'SpecialActiveUsers', 'cacheUpdate' ),
 );
 
 /**
index fbfe3ed..9a27d23 100644 (file)
@@ -892,7 +892,7 @@ class EditPage {
         * @since 1.21
         */
        protected function getContentObject( $def_content = null ) {
-               global $wgOut, $wgRequest;
+               global $wgOut, $wgRequest, $wgUser, $wgContLang;
 
                wfProfileIn( __METHOD__ );
 
@@ -947,34 +947,45 @@ class EditPage {
                                                        # Warn the user that something went wrong
                                                        $undoMsg = 'failure';
                                                } else {
-                                                       # Inform the user of our success and set an automatic edit summary
-                                                       $undoMsg = 'success';
-
-                                                       # If we just undid one rev, use an autosummary
-                                                       $firstrev = $oldrev->getNext();
-                                                       if ( $firstrev && $firstrev->getId() == $undo ) {
-                                                               $userText = $undorev->getUserText();
-                                                               if ( $userText === '' ) {
-                                                                       $undoSummary = wfMessage(
-                                                                               'undo-summary-username-hidden',
-                                                                               $undo
-                                                                       )->inContentLanguage()->text();
-                                                               } else {
-                                                                       $undoSummary = wfMessage(
-                                                                               'undo-summary',
-                                                                               $undo,
-                                                                               $userText
-                                                                       )->inContentLanguage()->text();
+                                                       $oldContent = $this->mArticle->getPage()->getContent( Revision::RAW );
+                                                       $popts = ParserOptions::newFromUserAndLang( $wgUser, $wgContLang );
+                                                       $newContent = $content->preSaveTransform( $this->mTitle, $wgUser, $popts );
+
+                                                       if ( $newContent->equals( $oldContent ) ) {
+                                                               # Tell the user that the undo results in no change,
+                                                               # i.e. the revisions were already undone.
+                                                               $undoMsg = 'nochange';
+                                                               $content = false;
+                                                       } else {
+                                                               # Inform the user of our success and set an automatic edit summary
+                                                               $undoMsg = 'success';
+
+                                                               # If we just undid one rev, use an autosummary
+                                                               $firstrev = $oldrev->getNext();
+                                                               if ( $firstrev && $firstrev->getId() == $undo ) {
+                                                                       $userText = $undorev->getUserText();
+                                                                       if ( $userText === '' ) {
+                                                                               $undoSummary = wfMessage(
+                                                                                       'undo-summary-username-hidden',
+                                                                                       $undo
+                                                                               )->inContentLanguage()->text();
+                                                                       } else {
+                                                                               $undoSummary = wfMessage(
+                                                                                       'undo-summary',
+                                                                                       $undo,
+                                                                                       $userText
+                                                                               )->inContentLanguage()->text();
+                                                                       }
+                                                                       if ( $this->summary === '' ) {
+                                                                               $this->summary = $undoSummary;
+                                                                       } else {
+                                                                               $this->summary = $undoSummary . wfMessage( 'colon-separator' )
+                                                                                       ->inContentLanguage()->text() . $this->summary;
+                                                                       }
+                                                                       $this->undidRev = $undo;
                                                                }
-                                                               if ( $this->summary === '' ) {
-                                                                       $this->summary = $undoSummary;
-                                                               } else {
-                                                                       $this->summary = $undoSummary . wfMessage( 'colon-separator' )
-                                                                               ->inContentLanguage()->text() . $this->summary;
-                                                               }
-                                                               $this->undidRev = $undo;
+                                                               $this->formtype = 'diff';
                                                        }
-                                                       $this->formtype = 'diff';
                                                }
                                        } else {
                                                // Failed basic sanity checks.
@@ -983,7 +994,7 @@ class EditPage {
                                                $undoMsg = 'norev';
                                        }
 
-                                       // Messages: undo-success, undo-failure, undo-norev
+                                       // Messages: undo-success, undo-failure, undo-norev, undo-nochange
                                        $class = ( $undoMsg == 'success' ? '' : 'error ' ) . "mw-undo-{$undoMsg}";
                                        $this->editFormPageTop .= $wgOut->parse( "<div class=\"{$class}\">" .
                                                wfMessage( 'undo-' . $undoMsg )->plain() . '</div>', true, /* interface */true );
index d8c7b6a..45b314d 100644 (file)
@@ -901,14 +901,14 @@ class MWExceptionHandler {
                if ( !( $e instanceof MWException ) || $e->isLoggable() ) {
                        $log = self::getLogMessage( $e );
                        if ( $wgLogExceptionBacktrace ) {
-                               wfDebugLog( 'exception', $log . "\n" . $e->getTraceAsString() . "\n" );
+                               wfDebugLog( 'exception', $log . "\n" . $e->getTraceAsString() );
                        } else {
                                wfDebugLog( 'exception', $log );
                        }
 
                        $json = self::jsonSerializeException( $e, false, FormatJson::ALL_OK );
                        if ( $json !== false ) {
-                               wfDebugLog( 'exception-json', $json, false );
+                               wfDebugLog( 'exception-json', $json, 'private' );
                        }
                }
 
index 2dda695..6785d0c 100644 (file)
@@ -924,21 +924,33 @@ function wfMatchesDomainList( $url, $domains ) {
  * $wgDebugComments - if on, some debug items may appear in comments in the HTML output.
  *
  * @param $text String
- * @param bool $logonly set true to avoid appearing in HTML when $wgDebugComments is set
- */
-function wfDebug( $text, $logonly = false ) {
+ * @param string|bool $dest Destination of the message:
+ *     - 'all': both to the log and HTML (debug toolbar or HTML comments)
+ *     - 'log': only to the log and not in HTML
+ *   For backward compatibility, it can also take a boolean:
+ *     - true: same as 'all'
+ *     - false: same as 'log'
+ */
+function wfDebug( $text, $dest = 'all' ) {
        global $wgDebugLogFile, $wgProfileOnly, $wgDebugRawPage, $wgDebugLogPrefix;
 
        if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
                return;
        }
 
+       // Turn $dest into a string if it's a boolean (for b/c)
+       if ( $dest === true ) {
+               $dest = 'all';
+       } elseif ( $dest === false ) {
+               $dest = 'log';
+       }
+
        $timer = wfDebugTimer();
        if ( $timer !== '' ) {
                $text = preg_replace( '/[^\n]/', $timer . '\0', $text, 1 );
        }
 
-       if ( !$logonly ) {
+       if ( $dest === 'all' ) {
                MWDebug::debugMsg( $text );
        }
 
@@ -1019,18 +1031,38 @@ function wfDebugMem( $exact = false ) {
  * @param $text String
  * @param bool $public whether to log the event in the public log if no private
  *                     log file is specified, (default true)
- */
-function wfDebugLog( $logGroup, $text, $public = true ) {
+ * @param string|bool $dest Destination of the message:
+ *     - 'all': both to the log and HTML (debug toolbar or HTML comments)
+ *     - 'log': only to the log and not in HTML
+ *     - 'private': only to the specifc log if set in $wgDebugLogGroups and
+ *       discarded otherwise
+ *   For backward compatibility, it can also take a boolean:
+ *     - true: same as 'all'
+ *     - false: same as 'private'
+ */
+function wfDebugLog( $logGroup, $text, $dest = 'all' ) {
        global $wgDebugLogGroups;
+
        $text = trim( $text ) . "\n";
 
+       // Turn $dest into a string if it's a boolean (for b/c)
+       if ( $dest === true ) {
+               $dest = 'all';
+       } elseif ( $dest === false ) {
+               $dest = 'private';
+       }
+
        if ( !isset( $wgDebugLogGroups[$logGroup] ) ) {
-               if ( $public === true ) {
-                       wfDebug( "[$logGroup] $text", false );
+               if ( $dest !== 'private' ) {
+                       wfDebug( "[$logGroup] $text", $dest );
                }
                return;
        }
 
+       if ( $dest === 'all' ) {
+               MWDebug::debugMsg( "[$logGroup] $text" );
+       }
+
        $logConfig = $wgDebugLogGroups[$logGroup];
        if ( is_array( $logConfig ) ) {
                if ( isset( $logConfig['sample'] ) && mt_rand( 1, $logConfig['sample'] ) !== 1 ) {
@@ -2836,7 +2868,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
        $pipes = null;
        $proc = proc_open( $cmd, $desc, $pipes );
        if ( !$proc ) {
-               wfDebugLog( 'exec', "proc_open() failed: $cmd\n" );
+               wfDebugLog( 'exec', "proc_open() failed: $cmd" );
                $retval = -1;
                return '';
        }
@@ -2951,7 +2983,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
        }
 
        if ( $logMsg !== false ) {
-               wfDebugLog( 'exec', "$logMsg: $cmd\n" );
+               wfDebugLog( 'exec', "$logMsg: $cmd" );
        }
 
        return $outBuffer;
index 1972d2d..aff4b16 100644 (file)
@@ -685,7 +685,7 @@ class OutputPage extends ContextSource {
                        return false;
                }
                if ( !$wgCachePages ) {
-                       wfDebug( __METHOD__ . ": CACHE DISABLED\n", false );
+                       wfDebug( __METHOD__ . ": CACHE DISABLED\n", 'log' );
                        return false;
                }
 
@@ -706,7 +706,7 @@ class OutputPage extends ContextSource {
 
                $clientHeader = $this->getRequest()->getHeader( 'If-Modified-Since' );
                if ( $clientHeader === false ) {
-                       wfDebug( __METHOD__ . ": client did not send If-Modified-Since header\n", false );
+                       wfDebug( __METHOD__ . ": client did not send If-Modified-Since header\n", 'log' );
                        return false;
                }
 
@@ -734,17 +734,17 @@ class OutputPage extends ContextSource {
                }
 
                wfDebug( __METHOD__ . ": client sent If-Modified-Since: " .
-                       wfTimestamp( TS_ISO_8601, $clientHeaderTime ) . "\n", false );
+                       wfTimestamp( TS_ISO_8601, $clientHeaderTime ) . "\n", 'log' );
                wfDebug( __METHOD__ . ": effective Last-Modified: " .
-                       wfTimestamp( TS_ISO_8601, $maxModified ) . "\n", false );
+                       wfTimestamp( TS_ISO_8601, $maxModified ) . "\n", 'log' );
                if ( $clientHeaderTime < $maxModified ) {
-                       wfDebug( __METHOD__ . ": STALE, $info\n", false );
+                       wfDebug( __METHOD__ . ": STALE, $info\n", 'log' );
                        return false;
                }
 
                # Not modified
                # Give a 304 response code and disable body output
-               wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", false );
+               wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", 'log' );
                ini_set( 'zlib.output_compression', 0 );
                $this->getRequest()->response()->header( "HTTP/1.1 304 Not Modified" );
                $this->sendCacheControl();
@@ -1919,7 +1919,7 @@ class OutputPage extends ContextSource {
                                        # We'll purge the proxy cache explicitly, but require end user agents
                                        # to revalidate against the proxy on each visit.
                                        # Surrogate-Control controls our Squid, Cache-Control downstream caches
-                                       wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **\n", false );
+                                       wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **\n", 'log' );
                                        # start with a shorter timeout for initial testing
                                        # header( 'Surrogate-Control: max-age=2678400+2678400, content="ESI/1.0"');
                                        $response->header( 'Surrogate-Control: max-age=' . $wgSquidMaxage . '+' . $this->mSquidMaxage . ', content="ESI/1.0"' );
@@ -1929,7 +1929,7 @@ class OutputPage extends ContextSource {
                                        # to revalidate against the proxy on each visit.
                                        # IMPORTANT! The Squid needs to replace the Cache-Control header with
                                        # Cache-Control: s-maxage=0, must-revalidate, max-age=0
-                                       wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **\n", false );
+                                       wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **\n", 'log' );
                                        # start with a shorter timeout for initial testing
                                        # header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" );
                                        $response->header( 'Cache-Control: s-maxage=' . $this->mSquidMaxage . ', must-revalidate, max-age=0' );
@@ -1937,7 +1937,7 @@ class OutputPage extends ContextSource {
                        } else {
                                # We do want clients to cache if they can, but they *must* check for updates
                                # on revisiting the page.
-                               wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **\n", false );
+                               wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **\n", 'log' );
                                $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
                                $response->header( "Cache-Control: private, must-revalidate, max-age=0" );
                        }
@@ -1945,7 +1945,7 @@ class OutputPage extends ContextSource {
                                $response->header( "Last-Modified: {$this->mLastModified}" );
                        }
                } else {
-                       wfDebug( __METHOD__ . ": no caching **\n", false );
+                       wfDebug( __METHOD__ . ": no caching **\n", 'log' );
 
                        # In general, the absence of a last modified header should be enough to prevent
                        # the client from using its cache. We send a few other things just to make sure.
index 44520e8..ba1aae8 100644 (file)
@@ -62,6 +62,13 @@ class Preferences {
                'emailaddress',
        );
 
+       /**
+        * @return array
+        */
+       static function getSaveBlacklist() {
+               return self::$saveBlacklist;
+       }
+
        /**
         * @throws MWException
         * @param $user User
index f5fd195..eadf256 100644 (file)
@@ -373,7 +373,7 @@ class SquidPurgeClient {
         * @param $msg string
         */
        protected function log( $msg ) {
-               wfDebugLog( 'squid', __CLASS__ . " ($this->host): $msg\n" );
+               wfDebugLog( 'squid', __CLASS__ . " ($this->host): $msg" );
        }
 }
 
index ca3f79b..d863a06 100644 (file)
@@ -1418,11 +1418,11 @@ class User {
                                $ipList = gethostbynamel( $host );
 
                                if ( $ipList ) {
-                                       wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $base!\n" );
+                                       wfDebugLog( 'dnsblacklist', "Hostname $host is {$ipList[0]}, it's a proxy says $base!" );
                                        $found = true;
                                        break;
                                } else {
-                                       wfDebugLog( 'dnsblacklist', "Requested $host, not found in $base.\n" );
+                                       wfDebugLog( 'dnsblacklist', "Requested $host, not found in $base." );
                                }
                        }
                }
@@ -2450,6 +2450,8 @@ class User {
         * - 'registered-checkmatrix' - as above, using the 'checkmatrix' type.
         * - 'userjs' - preferences with names starting with 'userjs-', intended to
         *              be used by user scripts.
+        * - 'special' - "preferences" that are not accessible via User::getOptions
+        *               or User::setOptions.
         * - 'unused' - preferences about which MediaWiki doesn't know anything.
         *              These are usually legacy options, removed in newer versions.
         *
@@ -2466,6 +2468,7 @@ class User {
                        'registered-multiselect',
                        'registered-checkmatrix',
                        'userjs',
+                       'special',
                        'unused'
                );
        }
@@ -2490,6 +2493,13 @@ class User {
                $prefs = Preferences::getPreferences( $this, $context );
                $mapping = array();
 
+               // Pull out the "special" options, so they don't get converted as
+               // multiselect or checkmatrix.
+               $specialOptions = array_fill_keys( Preferences::getSaveBlacklist(), true );
+               foreach ( $specialOptions as $name => $value ) {
+                       unset( $prefs[$name] );
+               }
+
                // Multiselect and checkmatrix options are stored in the database with
                // one key per option, each having a boolean value. Extract those keys.
                $multiselectOptions = array();
@@ -2532,6 +2542,8 @@ class User {
                                $mapping[$key] = 'registered-multiselect';
                        } elseif ( isset( $checkmatrixOptions[$key] ) ) {
                                $mapping[$key] = 'registered-checkmatrix';
+                       } elseif ( isset( $specialOptions[$key] ) ) {
+                               $mapping[$key] = 'special';
                        } elseif ( substr( $key, 0, 7 ) === 'userjs-' ) {
                                $mapping[$key] = 'userjs';
                        } else {
index 99a70d9..a191983 100644 (file)
@@ -3608,12 +3608,12 @@ class PoolWorkArticleView extends PoolCounterWork {
                $this->parserOutput = ParserCache::singleton()->getDirty( $this->page, $this->parserOptions );
 
                if ( $this->parserOutput === false ) {
-                       wfDebugLog( 'dirty', "dirty missing\n" );
+                       wfDebugLog( 'dirty', 'dirty missing' );
                        wfDebug( __METHOD__ . ": no dirty cache\n" );
                        return false;
                } else {
                        wfDebug( __METHOD__ . ": sending dirty output\n" );
-                       wfDebugLog( 'dirty', "dirty output {$this->cacheKey}\n" );
+                       wfDebugLog( 'dirty', "dirty output {$this->cacheKey}" );
                        $this->isDirty = true;
                        return true;
                }
index e610d19..900c7be 100644 (file)
@@ -1372,13 +1372,13 @@ abstract class ApiBase extends ContextSource {
        }
 
        /**
-        * Throw a UsageException based on the errors in the Status object.
+        * Get error (as code, string) from a Status object.
         *
-        * @since 1.22
+        * @since 1.23
         * @param Status $status Status object
-        * @throws MWException
+        * @return array of code and error string
         */
-       public function dieStatus( $status ) {
+       public function getErrorFromStatus( $status ) {
                if ( $status->isGood() ) {
                        throw new MWException( 'Successful status passed to ApiBase::dieStatus' );
                }
@@ -1406,7 +1406,20 @@ abstract class ApiBase extends ContextSource {
                        // Translate message to code, for backwards compatability
                        $code = ApiBase::$messageMap[$code]['code'];
                }
-               $this->dieUsage( $msg->inLanguage( 'en' )->useDatabase( false )->plain(), $code );
+               return array( $code, $msg->inLanguage( 'en' )->useDatabase( false )->plain() );
+       }
+
+       /**
+        * Throw a UsageException based on the errors in the Status object.
+        *
+        * @since 1.22
+        * @param Status $status Status object
+        * @throws MWException
+        */
+       public function dieStatus( $status ) {
+
+               list( $code, $msg ) = $this->getErrorFromStatus( $status );
+               $this->dieUsage( $msg, $code );
        }
 
        // @codingStandardsIgnoreStart Allow long lines. Cannot split these.
index ea61932..2684f51 100644 (file)
@@ -39,7 +39,6 @@
  * @ingroup API
  */
 class ApiMain extends ApiBase {
-
        /**
         * When no format parameter is given, this format will be used
         */
@@ -84,6 +83,7 @@ class ApiMain extends ApiBase {
                'userrights' => 'ApiUserrights',
                'options' => 'ApiOptions',
                'imagerotate' => 'ApiImageRotate',
+               'revisiondelete' => 'ApiRevisionDelete',
        );
 
        /**
@@ -899,7 +899,7 @@ class ApiMain extends ApiBase {
                        }
                }
                $s .= "\n";
-               wfDebugLog( 'api', $s, false );
+               wfDebugLog( 'api', $s, 'private' );
        }
 
        /**
@@ -1147,7 +1147,10 @@ class ApiMain extends ApiBase {
                        array( 'code' => 'maxlag', 'info' => 'Waiting for host: x seconds lagged' ),
                        array( 'code' => 'maxlag', 'info' => 'Waiting for a database server: x seconds lagged' ),
                        array( 'code' => 'assertuserfailed', 'info' => 'Assertion that the user is logged in failed' ),
-                       array( 'code' => 'assertbotfailed', 'info' => 'Assertion that the user has the bot right failed' ),
+                       array(
+                               'code' => 'assertbotfailed',
+                               'info' => 'Assertion that the user has the bot right failed'
+                       ),
                ) );
        }
 
index 929b0b6..fb441a3 100644 (file)
@@ -98,6 +98,9 @@ class ApiOptions extends ApiBase {
                                                $validation = true;
                                        }
                                        break;
+                               case 'special':
+                                       $validation = "cannot be set by this module";
+                                       break;
                                case 'unused':
                                default:
                                        $validation = "not a valid preference";
index 4095fe6..4ecf029 100644 (file)
@@ -39,7 +39,6 @@
  * @since 1.21 derives from ApiBase instead of ApiQueryBase
  */
 class ApiPageSet extends ApiBase {
-
        /**
         * Constructor flag: The new instance of ApiPageSet will ignore the 'generator=' parameter
         * @since 1.21
@@ -74,6 +73,30 @@ class ApiPageSet extends ApiBase {
         */
        private $mDefaultNamespace = NS_MAIN;
 
+       /**
+        * Add all items from $values into the result
+        * @param array $result output
+        * @param array $values values to add
+        * @param string $flag the name of the boolean flag to mark this element
+        * @param string $name if given, name of the value
+        */
+       private static function addValues( array &$result, $values, $flag = null, $name = null ) {
+               foreach ( $values as $val ) {
+                       if ( $val instanceof Title ) {
+                               $v = array();
+                               ApiQueryBase::addTitleInfo( $v, $val );
+                       } elseif ( $name !== null ) {
+                               $v = array( $name => $val );
+                       } else {
+                               $v = $val;
+                       }
+                       if ( $flag !== null ) {
+                               $v[$flag] = '';
+                       }
+                       $result[] = $v;
+               }
+       }
+
        /**
         * Constructor
         * @param $dbSource ApiBase Module implementing getDB().
@@ -498,6 +521,45 @@ class ApiPageSet extends ApiBase {
                return $values;
        }
 
+       /**
+        * Get an array of invalid/special/missing titles.
+        *
+        * @param $invalidChecks List of types of invalid titles to include.
+        *   Recognized values are:
+        *   - invalidTitles: Titles from $this->getInvalidTitles()
+        *   - special: Titles from $this->getSpecialTitles()
+        *   - missingIds: ids from $this->getMissingPageIDs()
+        *   - missingRevIds: ids from $this->getMissingRevisionIDs()
+        *   - missingTitles: Titles from $this->getMissingTitles()
+        *   - interwikiTitles: Titles from $this->getInterwikiTitlesAsResult()
+        * @return array Array suitable for inclusion in the response
+        * @since 1.23
+        */
+       public function getInvalidTitlesAndRevisions( $invalidChecks = array( 'invalidTitles',
+               'special', 'missingIds', 'missingRevIds', 'missingTitles', 'interwikiTitles' )
+       ) {
+               $result = array();
+               if ( in_array( "invalidTitles", $invalidChecks ) ) {
+                       self::addValues( $result, $this->getInvalidTitles(), 'invalid', 'title' );
+               }
+               if ( in_array( "special", $invalidChecks ) ) {
+                       self::addValues( $result, $this->getSpecialTitles(), 'special', 'title' );
+               }
+               if ( in_array( "missingIds", $invalidChecks ) ) {
+               self::addValues( $result, $this->getMissingPageIDs(), 'missing', 'pageid' );
+               }
+               if ( in_array( "missingRevIds", $invalidChecks ) ) {
+                       self::addValues( $result, $this->getMissingRevisionIDs(), 'missing', 'revid' );
+               }
+               if ( in_array( "missingTitles", $invalidChecks ) ) {
+                       self::addValues( $result, $this->getMissingTitles(), 'missing' );
+               }
+               if ( in_array( "interwikiTitles", $invalidChecks ) ) {
+                       self::addValues( $result, $this->getInterwikiTitlesAsResult() );
+               }
+               return $result;
+       }
+
        /**
         * Get the list of revision IDs (requested with the revids= parameter)
         * @return array revID (int) => pageID (int)
index c0dd808..e5d6a3c 100644 (file)
 class ApiPurge extends ApiBase {
        private $mPageSet;
 
-       /**
-        * Add all items from $values into the result
-        * @param array $result output
-        * @param array $values values to add
-        * @param string $flag the name of the boolean flag to mark this element
-        * @param string $name if given, name of the value
-        */
-       private static function addValues( array &$result, $values, $flag = null, $name = null ) {
-               foreach ( $values as $val ) {
-                       if ( $val instanceof Title ) {
-                               $v = array();
-                               ApiQueryBase::addTitleInfo( $v, $val );
-                       } elseif ( $name !== null ) {
-                               $v = array( $name => $val );
-                       } else {
-                               $v = $val;
-                       }
-                       if ( $flag !== null ) {
-                               $v[$flag] = '';
-                       }
-                       $result[] = $v;
-               }
-       }
-
        /**
         * Purges the cache of a page
         */
@@ -67,13 +43,7 @@ class ApiPurge extends ApiBase {
                $pageSet = $this->getPageSet();
                $pageSet->execute();
 
-               $result = array();
-               self::addValues( $result, $pageSet->getInvalidTitles(), 'invalid', 'title' );
-               self::addValues( $result, $pageSet->getSpecialTitles(), 'special', 'title' );
-               self::addValues( $result, $pageSet->getMissingPageIDs(), 'missing', 'pageid' );
-               self::addValues( $result, $pageSet->getMissingRevisionIDs(), 'missing', 'revid' );
-               self::addValues( $result, $pageSet->getMissingTitles(), 'missing' );
-               self::addValues( $result, $pageSet->getInterwikiTitlesAsResult() );
+               $result = $pageSet->getInvalidTitlesAndRevisions();
 
                foreach ( $pageSet->getGoodTitles() as $title ) {
                        $r = array();
index 6bf8075..44bf0cb 100644 (file)
@@ -67,8 +67,12 @@ class ApiQueryAllCategories extends ApiQueryGeneratorBase {
                }
 
                $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
-               $from = ( $params['from'] === null ? null : $this->titlePartToKey( $params['from'], NS_CATEGORY ) );
-               $to = ( $params['to'] === null ? null : $this->titlePartToKey( $params['to'], NS_CATEGORY ) );
+               $from = ( $params['from'] === null
+                       ? null
+                       : $this->titlePartToKey( $params['from'], NS_CATEGORY ) );
+               $to = ( $params['to'] === null
+                       ? null
+                       : $this->titlePartToKey( $params['to'], NS_CATEGORY ) );
                $this->addWhereRange( 'cat_title', $dir, $from, $to );
 
                $min = $params['min'];
index 5be304d..13f766e 100644 (file)
@@ -154,7 +154,6 @@ class ApiQueryAllLinks extends ApiQueryGeneratorBase {
                        $this->titlePartToKey( $params['to'], $params['namespace'] ) );
                $this->addWhereRange( $pfx . $fieldTitle, 'newer', $from, $to );
 
-
                if ( isset( $params['prefix'] ) ) {
                        $this->addWhere( $pfx . $fieldTitle . $db->buildLike( $this->titlePartToKey(
                                $params['prefix'], $params['namespace'] ), $db->anyString() ) );
index 430dd51..501154a 100644 (file)
@@ -87,8 +87,12 @@ class ApiQueryAllPages extends ApiQueryGeneratorBase {
 
                $this->addWhereFld( 'page_namespace', $params['namespace'] );
                $dir = ( $params['dir'] == 'descending' ? 'older' : 'newer' );
-               $from = ( $params['from'] === null ? null : $this->titlePartToKey( $params['from'], $params['namespace'] ) );
-               $to = ( $params['to'] === null ? null : $this->titlePartToKey( $params['to'], $params['namespace'] ) );
+               $from = ( $params['from'] === null
+                       ? null
+                       : $this->titlePartToKey( $params['from'], $params['namespace'] ) );
+               $to = ( $params['to'] === null
+                       ? null
+                       : $this->titlePartToKey( $params['to'], $params['namespace'] ) );
                $this->addWhereRange( 'page_title', $dir, $from, $to );
 
                if ( isset( $params['prefix'] ) ) {
index 7585ba7..365fe3f 100644 (file)
@@ -176,8 +176,12 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                } elseif ( $mode == 'all' ) {
                        $this->addWhereFld( 'ar_namespace', $params['namespace'] );
 
-                       $from = $params['from'] === null ? null : $this->titlePartToKey( $params['from'], $params['namespace'] );
-                       $to = $params['to'] === null ? null : $this->titlePartToKey( $params['to'], $params['namespace'] );
+                       $from = $params['from'] === null
+                               ? null
+                               : $this->titlePartToKey( $params['from'], $params['namespace'] );
+                       $to = $params['to'] === null
+                               ? null
+                               : $this->titlePartToKey( $params['to'], $params['namespace'] );
                        $this->addWhereRange( 'ar_title', $dir, $from, $to );
 
                        if ( isset( $params['prefix'] ) ) {
index baee9b1..95c1420 100644 (file)
@@ -677,7 +677,8 @@ class ApiQueryImageInfo extends ApiQueryBase {
                                ' (requires url and param ' . $modulePrefix . 'urlwidth)',
                        'mediatype' =>      ' mediatype     - Adds the media type of the image',
                        'metadata' =>       ' metadata      - Lists Exif metadata for the version of the image',
-                       'commonmetadata' => ' commonmetadata - Lists file format generic metadata for the version of the image',
+                       'commonmetadata' => ' commonmetadata - Lists file format generic metadata ' .
+                               'for the version of the image',
                        'extmetadata' =>    ' extmetadata   - Lists formatted metadata combined ' .
                                'from multiple sources. Results are HTML formatted.',
                        'archivename' =>    ' archivename   - Adds the file name of the archive ' .
index 07561ca..a9a5f5f 100644 (file)
@@ -573,12 +573,20 @@ class ApiQuerySiteinfo extends ApiQueryBase {
                                                        $ret['vcs-url'] = isset( $svnInfo['viewvc-url'] ) ? $svnInfo['viewvc-url'] : '';
                                                }
                                        }
+
                                        if ( SpecialVersion::getExtLicenseFileName( $extensionPath ) ) {
                                                $ret['license-name'] = isset( $ext['license-name'] ) ? $ext['license-name'] : '';
-                                               $ret['license'] = SpecialPage::getTitleFor( 'Version', "License/{$ext['name']}" )->getLinkURL();
+                                               $ret['license'] = SpecialPage::getTitleFor(
+                                                       'Version',
+                                                       "License/{$ext['name']}"
+                                               )->getLinkURL();
                                        }
+
                                        if ( SpecialVersion::getExtAuthorsFileName( $extensionPath ) ) {
-                                               $ret['credits'] = SpecialPage::getTitleFor( 'Version', "Credits/{$ext['name']}" )->getLinkURL();
+                                               $ret['credits'] = SpecialPage::getTitleFor(
+                                                       'Version',
+                                                       "Credits/{$ext['name']}"
+                                               )->getLinkURL();
                                        }
                                }
                                $data[] = $ret;
index 7896a2c..b492d9a 100644 (file)
@@ -360,7 +360,13 @@ class ApiQueryContributions extends ApiQueryBase {
                                $vals['commenthidden'] = '';
                                $anyHidden = true;
                        }
-                       if ( Revision::userCanBitfield( $row->rev_deleted, Revision::DELETED_COMMENT, $this->getUser() ) ) {
+
+                       $userCanView = Revision::userCanBitfield(
+                               $row->rev_deleted,
+                               Revision::DELETED_COMMENT, $this->getUser()
+                       );
+
+                       if ( $userCanView ) {
                                if ( $this->fld_comment ) {
                                        $vals['comment'] = $row->rev_comment;
                                }
diff --git a/includes/api/ApiRevisionDelete.php b/includes/api/ApiRevisionDelete.php
new file mode 100644 (file)
index 0000000..9ba30d7
--- /dev/null
@@ -0,0 +1,252 @@
+<?php
+/**
+ * Created on Jun 25, 2013
+ *
+ * Copyright © 2013 Brad Jorsch <bjorsch@wikimedia.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @since 1.23
+ */
+
+/**
+ * API interface to RevDel. The API equivalent of Special:RevisionDelete.
+ * Requires API write mode to be enabled.
+ *
+ * @ingroup API
+ */
+class ApiRevisionDelete extends ApiBase {
+
+       public function execute() {
+               $params = $this->extractRequestParams();
+               $user = $this->getUser();
+
+               if ( !$user->isAllowed( RevisionDeleter::getRestriction( $params['type'] ) ) ) {
+                       $this->dieUsageMsg( 'badaccess-group0' );
+               }
+
+               if ( !$params['ids'] ) {
+                       $this->dieUsage( "At least one value is required for 'ids'", 'badparams' );
+               }
+
+               $hide = $params['hide'] ?: array();
+               $show = $params['show'] ?: array();
+               if ( array_intersect( $hide, $show ) ) {
+                       $this->dieUsage( "Mutually exclusive values for 'hide' and 'show'", 'badparams' );
+               } elseif ( !$hide && !$show ) {
+                       $this->dieUsage( "At least one value is required for 'hide' or 'show'", 'badparams' );
+               }
+               $bits = array(
+                       'content' => RevisionDeleter::getRevdelConstant( $params['type'] ),
+                       'comment' => Revision::DELETED_COMMENT,
+                       'user' => Revision::DELETED_USER,
+               );
+               $bitfield = array();
+               foreach ( $bits as $key => $bit ) {
+                       if ( in_array( $key, $hide ) ) {
+                               $bitfield[$bit] = 1;
+                       } elseif ( in_array( $key, $show ) ) {
+                               $bitfield[$bit] = 0;
+                       } else {
+                               $bitfield[$bit] = -1;
+                       }
+               }
+
+               if ( $params['suppress'] === 'yes' ) {
+                       if ( !$user->isAllowed( 'suppressrevision' ) ) {
+                               $this->dieUsageMsg( 'badaccess-group0' );
+                       }
+                       $bitfield[Revision::DELETED_RESTRICTED] = 1;
+               } elseif ( $params['suppress'] === 'no' ) {
+                       $bitfield[Revision::DELETED_RESTRICTED] = 0;
+               } else {
+                       $bitfield[Revision::DELETED_RESTRICTED] = -1;
+               }
+
+               $targetObj = null;
+               if ( $params['target'] ) {
+                       $targetObj = Title::newFromText( $params['target'] );
+               }
+               $targetObj = RevisionDeleter::suggestTarget( $params['type'], $targetObj, $params['ids'] );
+               if ( $targetObj === null ) {
+                       $this->dieUsage( 'A target title is required for this RevDel type', 'needtarget' );
+               }
+
+               $list = RevisionDeleter::createList(
+                       $params['type'], $this->getContext(), $targetObj, $params['ids']
+               );
+               $status = $list->setVisibility(
+                       array( 'value' => $bitfield, 'comment' => $params['reason'], 'perItemStatus' => true )
+               );
+
+               $result = $this->getResult();
+               $data = $this->extractStatusInfo( $status );
+               $data['target'] = $targetObj->getFullText();
+               $data['items'] = array();
+
+               foreach ( $status->itemStatuses as $id => $s ) {
+                       $data['items'][$id] = $this->extractStatusInfo( $s );
+                       $data['items'][$id]['id'] = $id;
+               }
+
+               $list->reloadFromMaster();
+               // @codingStandardsIgnoreStart Avoid function calls in a FOR loop test part
+               for ( $item = $list->reset(); $list->current(); $item = $list->next() ) {
+                       $data['items'][$item->getId()] += $item->getApiData( $this->getResult() );
+               }
+               // @codingStandardsIgnoreEnd
+
+               $data['items'] = array_values( $data['items'] );
+               $result->setIndexedTagName( $data['items'], 'i' );
+               $result->addValue( null, $this->getModuleName(), $data );
+       }
+
+       private function extractStatusInfo( $status ) {
+               $ret = array(
+                       'status' => $status->isOK() ? 'Success' : 'Fail',
+               );
+               $errors = $this->formatStatusMessages( $status->getErrorsByType( 'error' ) );
+               if ( $errors ) {
+                       $this->getResult()->setIndexedTagName( $errors, 'e' );
+                       $ret['errors'] = $errors;
+               }
+               $warnings = $this->formatStatusMessages( $status->getErrorsByType( 'warning' ) );
+               if ( $warnings ) {
+                       $this->getResult()->setIndexedTagName( $warnings, 'w' );
+                       $ret['warnings'] = $warnings;
+               }
+               return $ret;
+       }
+
+       private function formatStatusMessages( $messages ) {
+               if ( !$messages ) {
+                       return array();
+               }
+               $result = $this->getResult();
+               $ret = array();
+               foreach ( $messages as $m ) {
+                       $message = array();
+                       if ( $m['message'] instanceof Message ) {
+                               $msg = $m['message'];
+                               $message = array( 'message' => $msg->getKey() );
+                               if ( $msg->getParams() ) {
+                                       $message['params'] = $msg->getParams();
+                                       $result->setIndexedTagName( $message['params'], 'p' );
+                               }
+                       } else {
+                               $message = array( 'message' => $m['message'] );
+                               $msg = wfMessage( $m['message'] );
+                               if ( isset( $m['params'] ) ) {
+                                       $message['params'] = $m['params'];
+                                       $result->setIndexedTagName( $message['params'], 'p' );
+                                       $msg->params( $m['params'] );
+                               }
+                       }
+                       $message['rendered'] = $msg->useDatabase( false )->inLanguage( 'en' )->plain();
+                       $ret[] = $message;
+               }
+               return $ret;
+       }
+
+       public function mustBePosted() {
+               return true;
+       }
+
+       public function isWriteMode() {
+               return true;
+       }
+
+       public function getAllowedParams() {
+               return array(
+                       'type' => array(
+                               ApiBase::PARAM_TYPE => RevisionDeleter::getTypes(),
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
+                       'target' => null,
+                       'ids' => array(
+                               ApiBase::PARAM_ISMULTI => true,
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
+                       'hide' => array(
+                               ApiBase::PARAM_TYPE => array( 'content', 'comment', 'user' ),
+                               ApiBase::PARAM_ISMULTI => true,
+                       ),
+                       'show' => array(
+                               ApiBase::PARAM_TYPE => array( 'content', 'comment', 'user' ),
+                               ApiBase::PARAM_ISMULTI => true,
+                       ),
+                       'suppress' => array(
+                               ApiBase::PARAM_TYPE => array( 'yes', 'no', 'nochange' ),
+                               ApiBase::PARAM_DFLT => 'nochange',
+                       ),
+                       'token' => array(
+                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_REQUIRED => true
+                       ),
+                       'reason' => null,
+               );
+       }
+
+       public function getParamDescription() {
+               return array(
+                       'type' => 'Type of revision deletion being performed',
+                       'target' => 'Page title for the revision deletion, if required for the type',
+                       'ids' => 'Identifiers for the revisions to be deleted',
+                       'hide' => 'What to hide for each revision',
+                       'show' => 'What to unhide for each revision',
+                       'suppress' => 'Whether to suppress data from administrators as well as others',
+                       'token' => 'A delete token previously retrieved through action=tokens',
+                       'reason' => 'Reason for the deletion/undeletion',
+               );
+       }
+
+       public function getDescription() {
+               return 'Delete/undelete revisions';
+       }
+
+       public function getPossibleErrors() {
+               return array_merge( parent::getPossibleErrors(),
+                       array(
+                               'needtarget' => 'A target title is required for this RevDel type',
+                               'badparams' => 'Bad value for some parameter',
+                       )
+               );
+       }
+
+       public function needsToken() {
+               return true;
+       }
+
+       public function getTokenSalt() {
+               return '';
+       }
+
+       public function getExamples() {
+               return array(
+                       'api.php?action=revisiondelete&target=Main%20Page&type=revision&ids=12345&' .
+                               'hide=content&token=123ABC'
+                               => 'Hide content for revision 12345 on the Main Page',
+                       'api.php?action=revisiondelete&type=logging&ids=67890&hide=content|comment|user&' .
+                               'reason=BLP%20violation&token=123ABC'
+                               => 'Hide all data on log entry 67890 with the reason "BLP violation"',
+               );
+       }
+
+       public function getHelpUrls() {
+               return 'https://www.mediawiki.org/wiki/API:Revisiondelete';
+       }
+}
index 759afd7..d0049ff 100644 (file)
  * @ingroup API
  */
 class ApiWatch extends ApiBase {
+       private $mPageSet = null;
 
        public function execute() {
                $user = $this->getUser();
                if ( !$user->isLoggedIn() ) {
                        $this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' );
                }
+
                if ( !$user->isAllowed( 'editmywatchlist' ) ) {
                        $this->dieUsage( 'You don\'t have permission to edit your watchlist', 'permissiondenied' );
                }
 
                $params = $this->extractRequestParams();
-               $title = Title::newFromText( $params['title'] );
+               $pageSet = $this->getPageSet();
+               // by default we use pageset to extract the page to work on.
+               // title is still supported for backward compatibility
+               if ( !isset( $params['title'] ) ) {
+                       $pageSet->execute();
+                       $res = $pageSet->getInvalidTitlesAndRevisions( array(
+                               'invalidTitles',
+                               'special',
+                               'missingIds',
+                               'missingRevIds',
+                               'interwikiTitles'
+                       ) );
+
+                       foreach ( $pageSet->getMissingTitles() as $title ) {
+                               $r = $this->watchTitle( $title, $user, $params );
+                               $r['missing'] = 1;
+                               $res[] = $r;
+                       }
+
+                       foreach ( $pageSet->getGoodTitles() as $title ) {
+                               $r = $this->watchTitle( $title, $user, $params );
+                               $res[] = $r;
+                       }
+                       $this->getResult()->setIndexedTagName( $res, 'w' );
+               } else {
+                       // dont allow use of old title parameter with new pageset parameters.
+                       $extraParams = array_keys( array_filter( $pageSet->extractRequestParams(), function ( $x ) {
+                               return $x !== null && $x !== false;
+                       } ) );
+
+                       if ( $extraParams ) {
+                               $p = $this->getModulePrefix();
+                               $this->dieUsage(
+                                       "The parameter {$p}title can not be used with " . implode( ", ", $extraParams ),
+                                       'invalidparammix'
+                               );
+                       }
+
+                       $title = Title::newFromText( $params['title'] );
+                       if ( !$title || !$title->isWatchable() ) {
+                               $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) );
+                       }
+                       $res = $this->watchTitle( $title, $user, $params, true );
+               }
+               $this->getResult()->addValue( null, $this->getModuleName(), $res );
+       }
 
-               if ( !$title || !$title->isWatchable() ) {
-                       $this->dieUsageMsg( array( 'invalidtitle', $params['title'] ) );
+       private function watchTitle( Title $title, User $user, array $params,
+               $compatibilityMode = false
+       ) {
+               if ( !$title->isWatchable() ) {
+                       return array( 'title' => $title->getPrefixedText(), 'watchable' => 0 );
                }
 
                $res = array( 'title' => $title->getPrefixedText() );
@@ -61,15 +111,19 @@ class ApiWatch extends ApiBase {
                }
 
                if ( $params['unwatch'] ) {
-                       $res['unwatched'] = '';
-                       $res['message'] = $this->msg( 'removedwatchtext', $title->getPrefixedText() )
-                               ->title( $title )->parseAsBlock();
                        $status = UnwatchAction::doUnwatch( $title, $user );
+                       if ( $status->isOK() ) {
+                               $res['unwatched'] = '';
+                               $res['message'] = $this->msg( 'removedwatchtext', $title->getPrefixedText() )
+                                       ->title( $title )->parseAsBlock();
+                       }
                } else {
-                       $res['watched'] = '';
-                       $res['message'] = $this->msg( 'addedwatchtext', $title->getPrefixedText() )
-                               ->title( $title )->parseAsBlock();
                        $status = WatchAction::doWatch( $title, $user );
+                       if ( $status->isOK() ) {
+                               $res['watched'] = '';
+                               $res['message'] = $this->msg( 'addedwatchtext', $title->getPrefixedText() )
+                                       ->title( $title )->parseAsBlock();
+                       }
                }
 
                if ( !is_null( $oldLang ) ) {
@@ -77,9 +131,24 @@ class ApiWatch extends ApiBase {
                }
 
                if ( !$status->isOK() ) {
-                       $this->dieStatus( $status );
+                       if ( $compatibilityMode ) {
+                               $this->dieStatus( $status );
+                       }
+                       $res['error'] =  $this->getErrorFromStatus( $status );
                }
-               $this->getResult()->addValue( null, $this->getModuleName(), $res );
+               return $res;
+       }
+
+
+       /**
+        * Get a cached instance of an ApiPageSet object
+        * @return ApiPageSet
+        */
+       private function getPageSet() {
+               if ( $this->mPageSet === null ) {
+                       $this->mPageSet = new ApiPageSet( $this );
+               }
+               return $this->mPageSet;
        }
 
        public function mustBePosted() {
@@ -98,11 +167,11 @@ class ApiWatch extends ApiBase {
                return 'watch';
        }
 
-       public function getAllowedParams() {
-               return array(
+       public function getAllowedParams( $flags = 0 ) {
+               $result = array(
                        'title' => array(
                                ApiBase::PARAM_TYPE => 'string',
-                               ApiBase::PARAM_REQUIRED => true
+                               ApiBase::PARAM_DEPRECATED => true
                        ),
                        'unwatch' => false,
                        'uselang' => null,
@@ -111,11 +180,16 @@ class ApiWatch extends ApiBase {
                                ApiBase::PARAM_REQUIRED => true
                        ),
                );
+               if ( $flags ) {
+                       $result += $this->getPageSet()->getFinalParams( $flags );
+               }
+               return $result;
        }
 
        public function getParamDescription() {
-               return array(
-                       'title' => 'The page to (un)watch',
+               $psModule = $this->getPageSet();
+               return $psModule->getParamDescription() + array(
+                       'title' => 'The page to (un)watch. use titles instead',
                        'unwatch' => 'If set the page will be unwatched rather than watched',
                        'uselang' => 'Language to show the message in',
                        'token' => 'A token previously acquired via prop=info',
@@ -134,7 +208,7 @@ class ApiWatch extends ApiBase {
        }
 
        public function getDescription() {
-               return 'Add or remove a page from/to the current user\'s watchlist';
+               return 'Add or remove pages from/to the current user\'s watchlist';
        }
 
        public function getPossibleErrors() {
@@ -147,8 +221,8 @@ class ApiWatch extends ApiBase {
 
        public function getExamples() {
                return array(
-                       'api.php?action=watch&title=Main_Page' => 'Watch the page "Main Page"',
-                       'api.php?action=watch&title=Main_Page&unwatch=' => 'Unwatch the page "Main Page"',
+                       'api.php?action=watch&titles=Main_Page' => 'Watch the page "Main Page"',
+                       'api.php?action=watch&titles=Main_Page&unwatch=' => 'Unwatch the page "Main Page"',
                );
        }
 
index 2629995..3690b70 100644 (file)
@@ -166,7 +166,7 @@ class HTMLFileCache extends FileCacheBase {
                        return $text;
                }
 
-               wfDebug( __METHOD__ . "()\n", false );
+               wfDebug( __METHOD__ . "()\n", 'log' );
 
                $now = wfTimestampNow();
                if ( $this->useGzip() ) {
index 9047a47..915a43e 100644 (file)
@@ -575,7 +575,7 @@ class LocalisationCache {
                try {
                        $compiledRules = CLDRPluralRuleEvaluator::compile( $rules );
                } catch ( CLDRPluralRuleError $e ) {
-                       wfDebugLog( 'l10n', $e->getMessage() . "\n" );
+                       wfDebugLog( 'l10n', $e->getMessage() );
 
                        return array();
                }
index 8a6718f..983d90a 100644 (file)
@@ -210,7 +210,7 @@ class RedisConnectionPool {
                        }
                } catch ( RedisException $e ) {
                        $this->downServers[$server] = time() + self::SERVER_DOWN_TTL;
-                       wfDebugLog( 'redis', "Redis exception connecting to $server: " . $e->getMessage() . "\n" );
+                       wfDebugLog( 'redis', "Redis exception connecting to $server: " . $e->getMessage() );
 
                        return false;
                }
@@ -279,7 +279,7 @@ class RedisConnectionPool {
         * @param RedisException $e
         */
        public function handleException( $server, RedisConnRef $cref, RedisException $e ) {
-               wfDebugLog( 'redis', "Redis exception on server $server: " . $e->getMessage() . "\n" );
+               wfDebugLog( 'redis', "Redis exception on server $server: " . $e->getMessage() );
                foreach ( $this->connections[$server] as $key => $connection ) {
                        if ( $cref->isConnIdentical( $connection['conn'] ) ) {
                                $this->idlePoolSize -= $connection['free'] ? 1 : 0;
index 9636da5..8826683 100644 (file)
@@ -984,7 +984,9 @@ class DatabaseMssql extends DatabaseBase {
         * @param array $join_conds
         * @return string
         */
-       protected function tableNamesWithUseIndexOrJOIN( $tables, $use_index = array(), $join_conds = array() ) {
+       protected function tableNamesWithUseIndexOrJOIN( $tables, $use_index = array(),
+               $join_conds = array()
+       ) {
                $ret = array();
                $retJOIN = array();
                $use_index_safe = is_array( $use_index ) ? $use_index : array();
index 6475c8f..c8830d3 100644 (file)
@@ -827,7 +827,9 @@ __INDEXATTR__;
         * can be locked. That means tables in an outer join cannot be FOR UPDATE locked. Trying to do
         * so causes a DB error. This wrapper checks which tables can be locked and adjusts it accordingly.
         */
-       function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__, $options = array(), $join_conds = array() ) {
+       function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__,
+               $options = array(), $join_conds = array()
+       ) {
                if ( is_array( $options ) ) {
                        $forUpdateKey = array_search( 'FOR UPDATE', $options );
                        if ( $forUpdateKey !== false && $join_conds ) {
@@ -1063,7 +1065,7 @@ __INDEXATTR__;
 
        /**
         * Return the next in a sequence, save the value for retrieval via insertId()
-        * 
+        *
         * @param string $seqName
         * @return int|null
         */
@@ -1564,7 +1566,7 @@ SQL;
 
                if ( isset( $options['FOR UPDATE'] ) ) {
                        $postLimitTail .= ' FOR UPDATE OF ' . implode( ', ', $options['FOR UPDATE'] );
-               } else if ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
+               } elseif ( isset( $noKeyOptions['FOR UPDATE'] ) ) {
                        $postLimitTail .= ' FOR UPDATE';
                }
 
index c622afe..e35c4c4 100644 (file)
@@ -144,10 +144,10 @@ class LoadBalancer {
                foreach ( $lags as $i => $lag ) {
                        if ( $i != 0 ) {
                                if ( $lag === false ) {
-                                       wfDebugLog( 'replication', "Server #$i is not replicating\n" );
+                                       wfDebugLog( 'replication', "Server #$i is not replicating" );
                                        unset( $loads[$i] );
                                } elseif ( isset( $this->mServers[$i]['max lag'] ) && $lag > $this->mServers[$i]['max lag'] ) {
-                                       wfDebugLog( 'replication', "Server #$i is excessively lagged ($lag seconds)\n" );
+                                       wfDebugLog( 'replication', "Server #$i is excessively lagged ($lag seconds)" );
                                        unset( $loads[$i] );
                                }
                        }
@@ -240,7 +240,7 @@ class LoadBalancer {
                                $i = $this->getRandomNonLagged( $currentLoads, $wiki );
                                if ( $i === false && count( $currentLoads ) != 0 ) {
                                        # All slaves lagged. Switch to read-only mode
-                                       wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode\n" );
+                                       wfDebugLog( 'replication', "All slaves lagged. Switch to read-only mode" );
                                        $wgReadOnly = 'The database has been automatically locked ' .
                                                'while the slave database servers catch up to the master';
                                        $i = ArrayUtils::pickRandom( $currentLoads );
@@ -252,17 +252,17 @@ class LoadBalancer {
                                # pickRandom() returned false
                                # This is permanent and means the configuration or the load monitor
                                # wants us to return false.
-                               wfDebugLog( 'connect', __METHOD__ . ": pickRandom() returned false\n" );
+                               wfDebugLog( 'connect', __METHOD__ . ": pickRandom() returned false" );
 
                                return false;
                        }
 
                        wfDebugLog( 'connect', __METHOD__ .
-                               ": Using reader #$i: {$this->mServers[$i]['host']}...\n" );
+                               ": Using reader #$i: {$this->mServers[$i]['host']}..." );
 
                        $conn = $this->openConnection( $i, $wiki );
                        if ( !$conn ) {
-                               wfDebugLog( 'connect', __METHOD__ . ": Failed connecting to $i/$wiki\n" );
+                               wfDebugLog( 'connect', __METHOD__ . ": Failed connecting to $i/$wiki" );
                                unset( $nonErrorLoads[$i] );
                                unset( $currentLoads[$i] );
                                continue;
@@ -280,7 +280,7 @@ class LoadBalancer {
 
                # If all servers were down, quit now
                if ( !count( $nonErrorLoads ) ) {
-                       wfDebugLog( 'connect', "All servers down\n" );
+                       wfDebugLog( 'connect', "All servers down" );
                }
 
                if ( $i !== false ) {
index d0f8916..d105bd0 100644 (file)
@@ -134,7 +134,7 @@ class MWDebug {
         * @since 1.19
         * @param $msg string
         * @param $callerOffset int
-        * @param $level int A PHP error level. See sendWarning()
+        * @param $level int A PHP error level. See sendMessage()
         * @param $log string: 'production' will always trigger a php error, 'auto'
         *        will trigger an error if $wgDevelopmentWarnings is true, and 'debug'
         *        will only write to the debug log(s).
@@ -154,7 +154,7 @@ class MWDebug {
 
                $callerDescription = self::getCallerDescription( $callerOffset );
 
-               self::sendWarning( $msg, $callerDescription, $level );
+               self::sendMessage( $msg, $callerDescription, 'warning', $level );
 
                if ( self::$enabled ) {
                        self::$log[] = array(
@@ -228,9 +228,10 @@ class MWDebug {
 
                if ( $sendToLog ) {
                        global $wgDevelopmentWarnings; // we could have a more specific $wgDeprecationWarnings setting.
-                       self::sendWarning(
+                       self::sendMessage(
                                $msg,
                                $callerDescription,
+                               'deprecated',
                                $wgDevelopmentWarnings ? E_USER_DEPRECATED : false
                        );
                }
@@ -287,21 +288,22 @@ class MWDebug {
        }
 
        /**
-        * Send a warning to the debug log and optionally also trigger a PHP
+        * Send a message to the debug log and optionally also trigger a PHP
         * error, depending on the $level argument.
         *
         * @param $msg string Message to send
         * @param $caller array caller description get from getCallerDescription()
+        * @param $group string log group on which to send the message
         * @param $level int|bool error level to use; set to false to not trigger an error
         */
-       private static function sendWarning( $msg, $caller, $level ) {
+       private static function sendMessage( $msg, $caller, $group, $level ) {
                $msg .= ' [Called from ' . $caller['func'] . ' in ' . $caller['file'] . ']';
 
                if ( $level !== false ) {
                        trigger_error( $msg, $level );
                }
 
-               wfDebug( "$msg\n" );
+               wfDebugLog( $group, $msg, 'log' );
        }
 
        /**
index 85592e8..0dcff44 100644 (file)
@@ -139,7 +139,7 @@ class SquidUpdate {
                        return;
                }
 
-               wfDebugLog( 'squid', __METHOD__ . ': ' . implode( ' ', $urlArr ) . "\n" );
+               wfDebugLog( 'squid', __METHOD__ . ': ' . implode( ' ', $urlArr ) );
 
                if ( $wgHTCPRouting ) {
                        self::HTCPPurge( $urlArr );
@@ -200,7 +200,7 @@ class SquidUpdate {
                if ( !$conn ) {
                        $errstr = socket_strerror( socket_last_error() );
                        wfDebugLog( 'squid', __METHOD__ .
-                               ": Error opening UDP socket: $errstr\n" );
+                               ": Error opening UDP socket: $errstr" );
                        wfProfileOut( __METHOD__ );
 
                        return;
@@ -230,7 +230,7 @@ class SquidUpdate {
                        $conf = self::getRuleForURL( $url, $wgHTCPRouting );
                        if ( !$conf ) {
                                wfDebugLog( 'squid', __METHOD__ .
-                                       "No HTCP rule configured for URL {$url} , skipping\n" );
+                                       "No HTCP rule configured for URL {$url} , skipping" );
                                continue;
                        }
 
@@ -266,7 +266,7 @@ class SquidUpdate {
                                $htcpTransID, $htcpSpecifier, 2 );
 
                        wfDebugLog( 'squid', __METHOD__ .
-                               "Purging URL $url via HTCP\n" );
+                               "Purging URL $url via HTCP" );
                        foreach ( $conf as $subconf ) {
                                socket_sendto( $conn, $htcpPacket, $htcpLen, 0,
                                        $subconf['host'], $subconf['port'] );
index b7e5469..5e7b323 100644 (file)
@@ -187,27 +187,27 @@ class ExternalStoreDB extends ExternalStoreMedium {
                $cacheID = ( $itemID === false ) ? "$cluster/$id" : "$cluster/$id/";
                if ( isset( $externalBlobCache[$cacheID] ) ) {
                        wfDebugLog( 'ExternalStoreDB-cache',
-                               "ExternalStoreDB::fetchBlob cache hit on $cacheID\n" );
+                               "ExternalStoreDB::fetchBlob cache hit on $cacheID" );
 
                        return $externalBlobCache[$cacheID];
                }
 
                wfDebugLog( 'ExternalStoreDB-cache',
-                       "ExternalStoreDB::fetchBlob cache miss on $cacheID\n" );
+                       "ExternalStoreDB::fetchBlob cache miss on $cacheID" );
 
                $dbr = $this->getSlave( $cluster );
                $ret = $dbr->selectField( $this->getTable( $dbr ),
                        'blob_text', array( 'blob_id' => $id ), __METHOD__ );
                if ( $ret === false ) {
                        wfDebugLog( 'ExternalStoreDB',
-                               "ExternalStoreDB::fetchBlob master fallback on $cacheID\n" );
+                               "ExternalStoreDB::fetchBlob master fallback on $cacheID" );
                        // Try the master
                        $dbw = $this->getMaster( $cluster );
                        $ret = $dbw->selectField( $this->getTable( $dbw ),
                                'blob_text', array( 'blob_id' => $id ), __METHOD__ );
                        if ( $ret === false ) {
                                wfDebugLog( 'ExternalStoreDB',
-                                       "ExternalStoreDB::fetchBlob master failed to find $cacheID\n" );
+                                       "ExternalStoreDB::fetchBlob master failed to find $cacheID" );
                        }
                }
                if ( $itemID !== false && $ret !== false ) {
@@ -239,7 +239,7 @@ class ExternalStoreDB extends ExternalStoreMedium {
                if ( $ids ) {
                        wfDebugLog( __CLASS__, __METHOD__ .
                                " master fallback on '$cluster' for: " .
-                               implode( ',', array_keys( $ids ) ) . "\n" );
+                               implode( ',', array_keys( $ids ) ) );
                        // Try the master
                        $dbw = $this->getMaster( $cluster );
                        $res = $dbw->select( $this->getTable( $dbr ),
@@ -247,7 +247,7 @@ class ExternalStoreDB extends ExternalStoreMedium {
                                array( 'blob_id' => array_keys( $ids ) ),
                                __METHOD__ );
                        if ( $res === false ) {
-                               wfDebugLog( __CLASS__, __METHOD__ . " master failed on '$cluster'\n" );
+                               wfDebugLog( __CLASS__, __METHOD__ . " master failed on '$cluster'" );
                        } else {
                                $this->mergeBatchResult( $ret, $ids, $res );
                        }
@@ -255,7 +255,7 @@ class ExternalStoreDB extends ExternalStoreMedium {
                if ( $ids ) {
                        wfDebugLog( __CLASS__, __METHOD__ .
                                " master on '$cluster' failed locating items: " .
-                               implode( ',', array_keys( $ids ) ) . "\n" );
+                               implode( ',', array_keys( $ids ) ) );
                }
 
                return $ret;
index f3b9664..c3d2de8 100644 (file)
@@ -1218,7 +1218,8 @@ class SwiftFileBackend extends FileBackendStore {
                }
 
                // Run all requests for the first stage, then the next, and so on
-               for ( $stage = 0; $stage < count( $httpReqsByStage ); ++$stage ) {
+               $reqCount = count( $httpReqsByStage );
+               for ( $stage = 0; $stage < $reqCount; ++$stage ) {
                        $httpReqs = $this->http->runMulti( $httpReqsByStage[$stage] );
                        foreach ( $httpReqs as $index => $httpReq ) {
                                // Run the callback for each request of this operation
index 1e8da45..f3b7ce7 100644 (file)
@@ -812,8 +812,14 @@ class HTMLForm extends ContextSource {
                        if ( $this->isVForm() ) {
                                // mw-ui-block is necessary because the buttons aren't necessarily in an
                                // immediate child div of the vform.
-                               // TODO Let client specify if the primary submit button is progressive or destructive
-                               array_push( $attribs['class'], 'mw-ui-button', 'mw-ui-big', 'mw-ui-constructive', 'mw-ui-block' );
+                               // @todo Let client specify if the primary submit button is progressive or destructive
+                               array_push(
+                                       $attribs['class'],
+                                       'mw-ui-button',
+                                       'mw-ui-big',
+                                       'mw-ui-constructive',
+                                       'mw-ui-block'
+                               );
                        }
 
                        $html .= Xml::submitButton( $this->getSubmitText(), $attribs ) . "\n";
index 9dfbff8..33ff65e 100644 (file)
@@ -28,7 +28,6 @@
  * @since 1.17
  */
 class MysqlUpdater extends DatabaseUpdater {
-
        protected function getCoreUpdateList() {
                return array(
                        array( 'disableContentHandlerUseDB' ),
@@ -246,8 +245,9 @@ class MysqlUpdater extends DatabaseUpdater {
 
                        // 1.23
                        array( 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ),
-                       array( 'addIndex', 'logging', 'log_user_text_type_time',  'patch-logging_user_text_type_time_index.sql' ),
-                       array( 'addIndex', 'logging', 'log_user_text_time',  'patch-logging_user_text_time_index.sql' ),
+                       array( 'addIndex', 'logging', 'log_user_text_type_time',
+                               'patch-logging_user_text_type_time_index.sql' ),
+                       array( 'addIndex', 'logging', 'log_user_text_time', 'patch-logging_user_text_time_index.sql' ),
                        array( 'addField', 'page', 'page_links_updated', 'patch-page_links_updated.sql' ),
                );
        }
index cd5a8ad..7841fca 100644 (file)
@@ -260,7 +260,8 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'addPgIndex', 'job', 'job_cmd_token', '(job_cmd, job_token, job_random)' ),
                        array( 'addPgIndex', 'job', 'job_cmd_token_id', '(job_cmd, job_token, job_id)' ),
                        array( 'addPgIndex', 'filearchive', 'fa_sha1', '(fa_sha1)' ),
-                       array( 'addPgIndex', 'logging', 'logging_user_text_type_time', '(log_user_text, log_type, log_timestamp)' ),
+                       array( 'addPgIndex', 'logging', 'logging_user_text_type_time',
+                               '(log_user_text, log_type, log_timestamp)' ),
                        array( 'addPgIndex', 'logging', 'logging_user_text_time', '(log_user_text, log_timestamp)' ),
 
                        array( 'checkIndex', 'pagelink_unique', array(
index 3db3758..5d4f6de 100644 (file)
@@ -123,7 +123,8 @@ class SqliteUpdater extends DatabaseUpdater {
 
                        // 1.23
                        array( 'addField', 'recentchanges', 'rc_source', 'patch-rc_source.sql' ),
-                       array( 'addIndex', 'logging', 'log_user_text_type_time',  'patch-logging_user_text_type_time_index.sql' ),
+                       array( 'addIndex', 'logging', 'log_user_text_type_time',
+                               'patch-logging_user_text_type_time_index.sql' ),
                        array( 'addIndex', 'logging', 'log_user_text_time',  'patch-logging_user_text_time_index.sql' ),
                        array( 'addField', 'page', 'page_links_updated', 'patch-page_links_updated.sql' ),
                );
index 844aef2..1cb5542 100644 (file)
@@ -829,13 +829,13 @@ class Exif {
                }
 
                if ( $action === true ) {
-                       wfDebugLog( $this->log, "$class::$fname: accepted: '$in' (type: $type)\n" );
+                       wfDebugLog( $this->log, "$class::$fname: accepted: '$in' (type: $type)" );
                } elseif ( $action === false ) {
-                       wfDebugLog( $this->log, "$class::$fname: rejected: '$in' (type: $type)\n" );
+                       wfDebugLog( $this->log, "$class::$fname: rejected: '$in' (type: $type)" );
                } elseif ( $action === null ) {
-                       wfDebugLog( $this->log, "$class::$fname: input was: '$in' (type: $type)\n" );
+                       wfDebugLog( $this->log, "$class::$fname: input was: '$in' (type: $type)" );
                } else {
-                       wfDebugLog( $this->log, "$class::$fname: $action (type: $type; content: '$in')\n" );
+                       wfDebugLog( $this->log, "$class::$fname: $action (type: $type; content: '$in')" );
                }
        }
 
@@ -851,9 +851,9 @@ class Exif {
                }
                $class = ucfirst( __CLASS__ );
                if ( $io ) {
-                       wfDebugLog( $this->log, "$class::$fname: begin processing: '{$this->basename}'\n" );
+                       wfDebugLog( $this->log, "$class::$fname: begin processing: '{$this->basename}'" );
                } else {
-                       wfDebugLog( $this->log, "$class::$fname: end processing: '{$this->basename}'\n" );
+                       wfDebugLog( $this->log, "$class::$fname: end processing: '{$this->basename}'" );
                }
        }
 }
index 3c97480..56f2128 100644 (file)
@@ -343,7 +343,7 @@ class RedisBagOStuff extends BagOStuff {
         * Log a fatal error
         */
        protected function logError( $msg ) {
-               wfDebugLog( 'redis', "Redis error: $msg\n" );
+               wfDebugLog( 'redis', "Redis error: $msg" );
        }
 
        /**
index 0c887e4..0509008 100644 (file)
@@ -279,6 +279,30 @@ class RevDel_RevisionItem extends RevDel_Item {
                }
                return "<li>$difflink $revlink $userlink $comment</li>";
        }
+
+       public function getApiData( ApiResult $result ) {
+               $rev = $this->revision;
+               $user = $this->list->getUser();
+               $ret = array(
+                       'id' => $rev->getId(),
+                       'timestamp' => wfTimestamp( TS_ISO_8601, $rev->getTimestamp() ),
+               );
+               $ret += $rev->isDeleted( Revision::DELETED_USER ) ? array( 'userhidden' => '' ) : array();
+               $ret += $rev->isDeleted( Revision::DELETED_COMMENT ) ? array( 'commenthidden' => '' ) : array();
+               $ret += $rev->isDeleted( Revision::DELETED_TEXT ) ? array( 'texthidden' => '' ) : array();
+               if ( $rev->userCan( Revision::DELETED_USER, $user ) ) {
+                       $ret += array(
+                               'userid' => $rev->getUser( Revision::FOR_THIS_USER ),
+                               'user' => $rev->getUserText( Revision::FOR_THIS_USER ),
+                       );
+               }
+               if ( $rev->userCan( Revision::DELETED_COMMENT, $user ) ) {
+                       $ret += array(
+                               'comment' => $rev->getComment( Revision::FOR_THIS_USER ),
+                       );
+               }
+               return $ret;
+       }
 }
 
 /**
@@ -708,6 +732,50 @@ class RevDel_FileItem extends RevDel_Item {
                return '<li>' . $this->getLink() . ' ' . $this->getUserTools() . ' ' .
                        $data . ' ' . $this->getComment() . '</li>';
        }
+
+       public function getApiData( ApiResult $result ) {
+               $file = $this->file;
+               $user = $this->list->getUser();
+               $ret = array(
+                       'title' => $this->list->title->getPrefixedText(),
+                       'archivename' => $file->getArchiveName(),
+                       'timestamp' => wfTimestamp( TS_ISO_8601, $file->getTimestamp() ),
+                       'width' => $file->getWidth(),
+                       'height' => $file->getHeight(),
+                       'size' => $file->getSize(),
+               );
+               $ret += $file->isDeleted( Revision::DELETED_USER ) ? array( 'userhidden' => '' ) : array();
+               $ret += $file->isDeleted( Revision::DELETED_COMMENT ) ? array( 'commenthidden' => '' ) : array();
+               $ret += $this->isDeleted() ? array( 'contenthidden' => '' ) : array();
+               if ( !$this->isDeleted() ) {
+                       $ret += array(
+                               'url' => $file->getUrl(),
+                       );
+               } elseif ( $this->canViewContent() ) {
+                       $ret += array(
+                               'url' => SpecialPage::getTitleFor( 'Revisiondelete' )->getLinkURL(
+                                       array(
+                                               'target' => $this->list->title->getPrefixedText(),
+                                               'file' => $file->getArchiveName(),
+                                               'token' => $user->getEditToken( $file->getArchiveName() )
+                                       ),
+                                       false, PROTO_RELATIVE
+                               ),
+                       );
+               }
+               if ( $file->userCan( Revision::DELETED_USER, $user ) ) {
+                       $ret += array(
+                               'userid' => $file->user,
+                               'user' => $file->user_text,
+                       );
+               }
+               if ( $file->userCan( Revision::DELETED_COMMENT, $user ) ) {
+                       $ret += array(
+                               'comment' => $file->description,
+                       );
+               }
+               return $ret;
+       }
 }
 
 /**
@@ -962,4 +1030,41 @@ class RevDel_LogItem extends RevDel_Item {
 
                return "<li>$loglink $date $action $comment</li>";
        }
+
+       public function getApiData( ApiResult $result ) {
+               $logEntry = DatabaseLogEntry::newFromRow( $this->row );
+               $user = $this->list->getUser();
+               $ret = array(
+                       'id' => $logEntry->getId(),
+                       'type' => $logEntry->getType(),
+                       'action' => $logEntry->getSubtype(),
+               );
+               $ret += $logEntry->isDeleted( LogPage::DELETED_USER ) ? array( 'userhidden' => '' ) : array();
+               $ret += $logEntry->isDeleted( LogPage::DELETED_COMMENT ) ? array( 'commenthidden' => '' ) : array();
+               $ret += $logEntry->isDeleted( LogPage::DELETED_ACTION ) ? array( 'actionhidden' => '' ) : array();
+
+               if ( LogEventsList::userCan( $this->row, LogPage::DELETED_ACTION, $user ) ) {
+                       ApiQueryLogEvents::addLogParams(
+                               $result,
+                               $ret,
+                               $logEntry->getParameters(),
+                               $logEntry->getType(),
+                               $logEntry->getSubtype(),
+                               $logEntry->getTimestamp(),
+                               $logEntry->isLegacy()
+                       );
+               }
+               if ( LogEventsList::userCan( $this->row, LogPage::DELETED_USER, $user ) ) {
+                       $ret += array(
+                               'userid' => $this->row->log_user,
+                               'user' => $this->row->log_user_text,
+                       );
+               }
+               if ( LogEventsList::userCan( $this->row, LogPage::DELETED_COMMENT, $user ) ) {
+                       $ret += array(
+                               'comment' => $this->row->log_comment,
+                       );
+               }
+               return $ret;
+       }
 }
index 803467e..3874602 100644 (file)
@@ -80,13 +80,16 @@ abstract class RevDel_List extends RevisionListBase {
         * transactions are done here.
         *
         * @param array $params Associative array of parameters. Members are:
-        *     value:       The integer value to set the visibility to
-        *     comment:     The log comment.
+        *     value:         The integer value to set the visibility to
+        *     comment:       The log comment.
+        *     perItemStatus: Set if you want per-item status reports
         * @return Status
+        * @since 1.23 Added 'perItemStatus' param
         */
        public function setVisibility( $params ) {
                $bitPars = $params['value'];
                $comment = $params['comment'];
+               $perItemStatus = isset( $params['perItemStatus'] ) ? $params['perItemStatus'] : false;
 
                $this->res = false;
                $dbw = wfGetDB( DB_MASTER );
@@ -98,16 +101,27 @@ abstract class RevDel_List extends RevisionListBase {
                $idsForLog = array();
                $authorIds = $authorIPs = array();
 
+               if ( $perItemStatus ) {
+                       $status->itemStatuses = array();
+               }
+
                for ( $this->reset(); $this->current(); $this->next() ) {
                        $item = $this->current();
                        unset( $missing[$item->getId()] );
 
+                       if ( $perItemStatus ) {
+                               $itemStatus = Status::newGood();
+                               $status->itemStatuses[$item->getId()] = $itemStatus;
+                       } else {
+                               $itemStatus = $status;
+                       }
+
                        $oldBits = $item->getBits();
                        // Build the actual new rev_deleted bitfield
                        $newBits = RevisionDeleter::extractBitfield( $bitPars, $oldBits );
 
                        if ( $oldBits == $newBits ) {
-                               $status->warning( 'revdelete-no-change', $item->formatDate(), $item->formatTime() );
+                               $itemStatus->warning( 'revdelete-no-change', $item->formatDate(), $item->formatTime() );
                                $status->failCount++;
                                continue;
                        } elseif ( $oldBits == 0 && $newBits != 0 ) {
@@ -120,7 +134,7 @@ abstract class RevDel_List extends RevisionListBase {
 
                        if ( $item->isHideCurrentOp( $newBits ) ) {
                                // Cannot hide current version text
-                               $status->error( 'revdelete-hide-current', $item->formatDate(), $item->formatTime() );
+                               $itemStatus->error( 'revdelete-hide-current', $item->formatDate(), $item->formatTime() );
                                $status->failCount++;
                                continue;
                        }
@@ -128,13 +142,13 @@ abstract class RevDel_List extends RevisionListBase {
                                // Cannot access this revision
                                $msg = ( $opType == 'show' ) ?
                                        'revdelete-show-no-access' : 'revdelete-modify-no-access';
-                               $status->error( $msg, $item->formatDate(), $item->formatTime() );
+                               $itemStatus->error( $msg, $item->formatDate(), $item->formatTime() );
                                $status->failCount++;
                                continue;
                        }
                        // Cannot just "hide from Sysops" without hiding any fields
                        if ( $newBits == Revision::DELETED_RESTRICTED ) {
-                               $status->warning( 'revdelete-only-restricted', $item->formatDate(), $item->formatTime() );
+                               $itemStatus->warning( 'revdelete-only-restricted', $item->formatDate(), $item->formatTime() );
                                $status->failCount++;
                                continue;
                        }
@@ -151,19 +165,22 @@ abstract class RevDel_List extends RevisionListBase {
                                        $authorIPs[] = $item->getAuthorName();
                                }
                        } else {
-                               $status->error( 'revdelete-concurrent-change', $item->formatDate(), $item->formatTime() );
+                               $itemStatus->error( 'revdelete-concurrent-change', $item->formatDate(), $item->formatTime() );
                                $status->failCount++;
                        }
                }
 
                // Handle missing revisions
                foreach ( $missing as $id => $unused ) {
-                       $status->error( 'revdelete-modify-missing', $id );
+                       if ( $perItemStatus ) {
+                               $status->itemStatuses[$id] = Status::newFatal( 'revdelete-modify-missing', $id );
+                       } else {
+                               $status->error( 'revdelete-modify-missing', $id );
+                       }
                        $status->failCount++;
                }
 
                if ( $status->successCount == 0 ) {
-                       $status->ok = false;
                        $dbw->rollback( __METHOD__ );
                        return $status;
                }
@@ -325,4 +342,12 @@ abstract class RevDel_Item extends RevisionItemBase {
         * @return boolean success
         */
        abstract public function setBits( $newBits );
+
+       /**
+        * Get the return information about the revision for the API
+        * @since 1.23
+        * @param ApiResult $result API result object
+        * @return array Data for the API result
+        */
+       abstract public function getApiData( ApiResult $result );
 }
index 792d0a6..dea65f3 100644 (file)
@@ -180,7 +180,6 @@ class SpecialPageFactory {
                global $wgSpecialPages;
                global $wgDisableCounters, $wgDisableInternalSearch, $wgEmailAuthentication;
                global $wgEnableEmail, $wgEnableJavaScriptTest;
-               global $wgMiserMode;
 
                if ( !is_object( self::$list ) ) {
                        wfProfileIn( __METHOD__ );
@@ -206,9 +205,7 @@ class SpecialPageFactory {
                                self::$list['JavaScriptTest'] = 'SpecialJavaScriptTest';
                        }
 
-                       if ( !$wgMiserMode ) {
-                               self::$list['Activeusers'] = 'SpecialActiveUsers';
-                       }
+                       self::$list['Activeusers'] = 'SpecialActiveUsers';
 
                        // Add extension special pages
                        self::$list = array_merge( self::$list, $wgSpecialPages );
index 705dab5..641c046 100644 (file)
@@ -87,39 +87,31 @@ class ActiveUsersPager extends UsersPager {
        }
 
        function getIndexField() {
-               return 'rc_user_text';
+               return 'qcc_title';
        }
 
        function getQueryInfo() {
                $dbr = $this->getDatabase();
 
-               $conds = array( 'rc_user > 0' ); // Users - no anons
-               $conds[] = 'rc_log_type IS NULL OR rc_log_type != ' . $dbr->addQuotes( 'newusers' );
-               $conds[] = 'rc_timestamp >= ' . $dbr->addQuotes(
-                       $dbr->timestamp( wfTimestamp( TS_UNIX ) - $this->RCMaxAge * 24 * 3600 ) );
-
+               $conds = array(
+                       'qcc_type' => 'activeusers',
+                       'qcc_namespace' => NS_USER,
+                       'user_name = qcc_title',
+                       'rc_user_text = qcc_title'
+               );
                if ( $this->requestedUser != '' ) {
-                       $conds[] = 'rc_user_text >= ' . $dbr->addQuotes( $this->requestedUser );
+                       $conds[] = 'qcc_title >= ' . $dbr->addQuotes( $this->requestedUser );
                }
-
                if ( !$this->getUser()->isAllowed( 'hideuser' ) ) {
                        $conds[] = 'NOT EXISTS (' . $dbr->selectSQLText(
-                               'ipblocks', '1', array( 'rc_user=ipb_user', 'ipb_deleted' => 1 )
+                               'ipblocks', '1', array( 'ipb_user=user_id', 'ipb_deleted' => 1 )
                        ) . ')';
                }
 
                return array(
-                       'tables' => array( 'recentchanges' ),
-                       'fields' => array(
-                               'user_name' => 'rc_user_text', // for Pager inheritance
-                               'rc_user_text', // for Pager
-                               'user_id' => 'MAX(rc_user)', // Postgres
-                               'recentedits' => 'COUNT(*)'
-                       ),
-                       'options' => array(
-                               'GROUP BY' => array( 'rc_user_text' ),
-                               'USE INDEX' => array( 'recentchanges' => 'rc_user_text' )
-                       ),
+                       'tables' => array( 'querycachetwo', 'user', 'recentchanges' ),
+                       'fields' => array( 'user_name', 'user_id', 'recentedits' => 'COUNT(*)', 'qcc_title' ),
+                       'options' => array( 'GROUP BY' => array( 'qcc_title' ) ),
                        'conds' => $conds
                );
        }
@@ -249,6 +241,12 @@ class SpecialActiveUsers extends SpecialPage {
                $out->wrapWikiMsg( "<div class='mw-activeusers-intro'>\n$1\n</div>",
                        array( 'activeusers-intro', $this->getLanguage()->formatNum( $wgActiveUserDays ) ) );
 
+               // Occasionally merge in new updates
+               $seconds = self::mergeActiveUsers( 600 );
+               // Mention the level of staleness
+               $out->addWikiMsg( 'cachedspecial-viewing-cached-ttl',
+                       $this->getLanguage()->formatDuration( $seconds ) );
+
                $up = new ActiveUsersPager( $this->getContext(), null, $par );
 
                # getBody() first to check, if empty
@@ -269,4 +267,141 @@ class SpecialActiveUsers extends SpecialPage {
        protected function getGroupName() {
                return 'users';
        }
+
+       /**
+        * @param integer $period Seconds (do updates no more often than this)
+        * @return integer How many seconds old the cache is
+        */
+       public static function mergeActiveUsers( $period ) {
+               global $wgActiveUserDays;
+
+               $dbr = wfGetDB( DB_SLAVE );
+               $cTime = $dbr->selectField( 'querycache_info',
+                       'qci_timestamp',
+                       array( 'qci_type' => 'activeusers' )
+               );
+               if ( !wfReadOnly() ) {
+                       if ( !$cTime || ( time() - wfTimestamp( TS_UNIX, $cTime ) ) > $period ) {
+                               $dbw = wfGetDB( DB_MASTER );
+                               self::doQueryCacheUpdate( $dbw, 2 * $period );
+                       }
+               }
+               return ( time() -
+                       ( $cTime ? wfTimestamp( TS_UNIX, $cTime ) : $wgActiveUserDays * 86400 ) );
+       }
+
+       /**
+        * @param DatabaseBase $dbw Passed in from updateSpecialPages.php
+        * @return void
+        */
+       public static function cacheUpdate( DatabaseBase $dbw ) {
+               global $wgActiveUserDays;
+
+               self::doQueryCacheUpdate( $dbw, $wgActiveUserDays * 86400 );
+       }
+
+       /**
+        * Update the query cache as needed
+        *
+        * @param DatabaseBase $dbw
+        * @param integer $window Maximum time range of new data to scan (in seconds)
+        * @return bool Success
+        */
+       protected static function doQueryCacheUpdate( DatabaseBase $dbw, $window ) {
+               global $wgActiveUserDays;
+
+               $lockKey = wfWikiID() . '-activeusers';
+               if ( !$dbw->lock( $lockKey, __METHOD__, 1 ) ) {
+                       return false; // exclusive update (avoids duplicate entries)
+               }
+
+               $now = time();
+               $cTime = $dbw->selectField( 'querycache_info',
+                       'qci_timestamp',
+                       array( 'qci_type' => 'activeusers' )
+               );
+               $cTimeUnix = $cTime ? wfTimestamp( TS_UNIX, $cTime ) : 1;
+
+               // Pick the date range to fetch from. This is normally from the last
+               // update to till the present time, but has a limited window for sanity.
+               // If the window is limited, multiple runs are need to fully populate it.
+               $sTimestamp = max( $cTimeUnix, $now - $wgActiveUserDays * 86400 );
+               $eTimestamp = min( $sTimestamp + $window, $now );
+
+               // Get all the users active since the last update
+               $res = $dbw->select(
+                       array( 'recentchanges' ),
+                       array( 'rc_user_text', 'lastedittime' => 'MAX(rc_timestamp)' ),
+                       array(
+                               'rc_user > 0', // actual accounts
+                               'rc_log_type IS NULL OR rc_log_type != ' . $dbw->addQuotes( 'newusers' ),
+                               'rc_timestamp >= ' . $dbw->addQuotes( $dbw->timestamp( $sTimestamp ) ),
+                               'rc_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $eTimestamp ) )
+                       ),
+                       __METHOD__,
+                       array(
+                               'GROUP BY' => array( 'rc_user_text' ),
+                               'ORDER BY' => 'NULL' // avoid filesort
+                       )
+               );
+               $names = array();
+               foreach ( $res as $row ) {
+                       $names[$row->rc_user_text] = $row->lastedittime;
+               }
+
+               // Rotate out users that have not edited in too long (according to old data set)
+               $dbw->delete( 'querycachetwo',
+                       array(
+                               'qcc_type' => 'activeusers',
+                               'qcc_value < ' . $dbw->addQuotes( $now - $wgActiveUserDays * 86400 ) // TS_UNIX
+                       ),
+                       __METHOD__
+               );
+
+               // Find which of the recently active users are already accounted for
+               if ( count( $names ) ) {
+                       $res = $dbw->select( 'querycachetwo',
+                               array( 'user_name' => 'qcc_title' ),
+                               array(
+                                       'qcc_type' => 'activeusers',
+                                       'qcc_namespace' => NS_USER,
+                                       'qcc_title' => array_keys( $names ) ),
+                               __METHOD__
+                       );
+                       foreach ( $res as $row ) {
+                               unset( $names[$row->user_name] );
+                       }
+               }
+
+               // Insert the users that need to be added to the list (which their last edit time
+               if ( count( $names ) ) {
+                       $newRows = array();
+                       foreach ( $names as $name => $lastEditTime ) {
+                               $newRows[] = array(
+                                       'qcc_type'  => 'activeusers',
+                                       'qcc_namespace' => NS_USER,
+                                       'qcc_title' => $name,
+                                       'qcc_value' => wfTimestamp( TS_UNIX, $lastEditTime ),
+                                       'qcc_namespacetwo' => 0, // unused
+                                       'qcc_titletwo' => '' // unused
+                               );
+                       }
+                       foreach ( array_chunk( $newRows, 500 ) as $rowBatch ) {
+                               $dbw->insert( 'querycachetwo', $rowBatch, __METHOD__ );
+                               wfWaitForSlaves();
+                       }
+               }
+
+               // Touch the data freshness timestamp
+               $dbw->replace( 'querycache_info',
+                       array( 'qci_type' ),
+                       array( 'qci_type' => 'activeusers',
+                               'qci_timestamp' => $dbw->timestamp( $eTimestamp ) ), // not always $now
+                       __METHOD__
+               );
+
+               $dbw->unlock( $lockKey, __METHOD__ );
+
+               return true;
+       }
 }
index 8e56574..082eed0 100644 (file)
@@ -277,7 +277,7 @@ class SpecialPasswordReset extends FormSpecialPage {
 
                $title = $this->msg( 'passwordreset-emailtitle' );
 
-               $this->result = $firstUser->sendMail( $title->escaped(), $this->email->text() );
+               $this->result = $firstUser->sendMail( $title->text(), $this->email->text() );
 
                if ( isset( $data['Capture'] ) && $data['Capture'] ) {
                        // Save the user, will be used if an error occurs when sending the email
index 7352de7..d266e3f 100644 (file)
@@ -506,7 +506,6 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
        public function getFeedQuery() {
                global $wgFeedLimit;
 
-               $this->getOptions()->validateIntBounds( 'limit', 0, $wgFeedLimit );
                $options = $this->getOptions()->getChangedValues();
 
                // wfArrayToCgi() omits options set to null or false
@@ -517,6 +516,10 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                }
                unset( $value );
 
+               if ( isset( $options['limit'] ) && $options['limit'] > $wgFeedLimit ) {
+                       $options['limit'] = $wgFeedLimit;
+               }
+
                return wfArrayToCgi( $options );
        }
 
index c06f85e..588a313 100644 (file)
@@ -199,7 +199,7 @@ class SpecialSearch extends SpecialPage {
                if ( !is_null( $t ) ) {
                        global $wgGoToEdit;
                        wfRunHooks( 'SpecialSearchNogomatch', array( &$t ) );
-                       wfDebugLog( 'nogomatch', $t->getText(), false );
+                       wfDebugLog( 'nogomatch', $t->getText(), 'private' );
 
                        # If the feature is enabled, go straight to the edit page
                        if ( $wgGoToEdit ) {
index b572499..bdcc9a1 100644 (file)
@@ -139,7 +139,7 @@ class UploadFromChunks extends UploadFromFile {
                if ( !$status->isOk() ) {
                        return $status;
                }
-               wfDebugLog( 'fileconcatenate', "Combined $i chunks in $tAmount seconds.\n" );
+               wfDebugLog( 'fileconcatenate', "Combined $i chunks in $tAmount seconds." );
 
                // File system path
                $this->mTempPath = $tmpPath;
@@ -158,7 +158,7 @@ class UploadFromChunks extends UploadFromFile {
                $this->mLocalFile = parent::stashFile( $this->user );
                $tAmount = microtime( true ) - $tStart;
                $this->mLocalFile->setLocalReference( $tmpFile ); // reuse (e.g. for getImageInfo())
-               wfDebugLog( 'fileconcatenate', "Stashed combined file ($i chunks) in $tAmount seconds.\n" );
+               wfDebugLog( 'fileconcatenate', "Stashed combined file ($i chunks) in $tAmount seconds." );
 
                return $status;
        }
index f220c5d..aa30182 100644 (file)
@@ -1790,6 +1790,8 @@ $1",
 Калі Вы жадаеце загрузіць Ваш файл, вярніцеся назад і загрузіце гэты файл з новай назвай. [[File:$1|thumb|center|$1]]',
 'file-exists-duplicate' => 'Гэты файл дублюе {{PLURAL:$1|1=наступны файл|наступныя файлы}}:',
 'file-deleted-duplicate' => 'Падобны файл ([[:$1]]) ужо выдаляўся. Калі ласка, паглядзіце гісторыю выдаленьняў гэтага файла перад яго паўторнай загрузкай.',
+'file-deleted-duplicate-notitle' => 'Файл, ідэнтычны гэтаму файлу, раней ужо быў выдалены, а назва файла была забароненая.
+Вам трэба зьвярнуцца да некага з правамі прагляду зьвестак забароненых файлаў, каб прааналізаваць сытуацыю перад тым, як загружаць файл ізноў.',
 'uploadwarning' => 'Папярэджаньне',
 'uploadwarning-text' => 'Калі ласка, зьмяніце апісаньне файла ніжэй і паспрабуйце ізноў.',
 'savefile' => 'Захаваць файл',
@@ -2276,8 +2278,8 @@ $1',
 'listgrouprights-rights' => 'Правы',
 'listgrouprights-helppage' => 'Help:Правы групаў удзельнікаў',
 'listgrouprights-members' => '(сьпіс удзельнікаў групы)',
-'listgrouprights-addgroup' => 'можа дадаваць {{PLURAL:$2|1=групу|групы}}: $1',
-'listgrouprights-removegroup' => 'можа выдаляць {{PLURAL:$2|1=групу|групы}}: $1',
+'listgrouprights-addgroup' => 'можа дадаваць у {{PLURAL:$2|1=групу|групы}}: $1',
+'listgrouprights-removegroup' => 'можа выдаляць з {{PLURAL:$2|1=групы|групаў}}: $1',
 'listgrouprights-addgroup-all' => 'можа дадаваць усе групы',
 'listgrouprights-removegroup-all' => 'можа выдаляць усе групы',
 'listgrouprights-addgroup-self' => 'Можа дадаць уласны рахунак да {{PLURAL:$2|1=групы|групаў}}: $1',
index 2a4ad4d..8251d18 100644 (file)
@@ -479,7 +479,7 @@ $messages = array(
 # Vector skin
 'vector-action-addsection' => 'ТӀетоха хьедар',
 'vector-action-delete' => 'ДӀаяккха',
-'vector-action-move' => 'Цlе хийца',
+'vector-action-move' => 'ЦӀе хийца',
 'vector-action-protect' => 'Гlаролла дé',
 'vector-action-undelete' => 'Меттахlоттадé',
 'vector-action-unprotect' => 'ГӀароллех къаста',
@@ -687,7 +687,7 @@ URL язъеш гӀалат даьлла хила мега.
 ХӀу бахьна ду билгал дина дац.',
 'no-null-revision' => '«$1» агӀона нисдар дан цаделира',
 'badtitle' => 'Цамегаш йолу цlе',
-'badtitletext' => 'Дехарца йолу агlонан цlе нийса яц, йаьсса ю, хила мега нийса ца хlоттийна меттаюкъар йа юкъарвики цlе. Хила мега, цlарца цамагош йолу саберг.',
+'badtitletext' => 'Дехарца йолу агӀонан цӀе нийса яц, йаьсса ю, хила мега нийса ца хӀоттийна меттаюкъар йа юкъарвики цӀе. Хила мега, цӀарца цамагош йолу символаш.',
 'perfcached' => 'Лахара хаам схьаэца кэша чура цундела тӀехьарлаьра хийцамаш гойтуш бац. Кэша чохь латтаё оцул $1  кӀезиг {{PLURAL:$1|1=дӀаяздар|дӀаяздарш}}.',
 'perfcachedts' => 'Лахара хаам схьаэца кэша чура иза тӀаьхьара карла ялла $1. Кэша чохь латта до оцул $4 кӀезиг {{PLURAL:$4|1=дӀаяздар|дӀаяздарш}}.',
 'querypage-no-updates' => 'ХӀинца хӀара агӀо карлаякхар дӀадайина ду.
@@ -1156,11 +1156,13 @@ $1",
 # Diffs
 'history-title' => '$1 — хийцаман истори',
 'difference-title' => '$1 — Версешан башхалла',
-'lineno' => 'Могlа $1:',
+'lineno' => 'МогӀа $1:',
 'compareselectedversions' => 'Хаьржина версеш муха ю хьажа',
 'showhideselectedversions' => 'Гайта/къайлаяха хаьржина башхонаш',
 'editundo' => 'цаоьшу',
 'diff-empty' => '(башхалла яц)',
+'diff-multi-sameuser' => '(ца {{PLURAL:$1|гайтина юккъера цхьа верси|гайтина юккъера цхьа версеш}} оьцу декъашхочун)',
+'diff-multi-otherusers' => '(ца {{PLURAL:$1|гайтина юккъера верси|гайтина юккъера версеш}} {{PLURAL:$2|кхин цхьан декъашхочун|$2 декъашхойн}})',
 
 # Search results
 'searchresults' => 'Лахарна хилам',
@@ -1694,7 +1696,7 @@ PICT # тайп тайпан
 'doubleredirects' => 'Шалха дIасахьажийнарш',
 'doubleredirectstext' => 'ХӀокху агӀонехь ю дӀасахьажорашан тӀе хьажийна йолу дӀасахьажораш.
 <del>ТӀехула сиз хаькхна </del>нисйина чарна.',
-'double-redirect-fixed-move' => 'Агlон [[$1]] цlе хийцна, хlинца иза дlахьажийна оцу [[$2]]',
+'double-redirect-fixed-move' => 'АгӀон [[$1]] цӀе хийцина, хӀинца иза дӀахьажийна оцу [[$2]]',
 
 'brokenredirects' => 'ДIахаьдна долу дIасахьажораш',
 'brokenredirectstext' => 'Лахара дӀасахьажийнарш ю йоцучу агӀонийн тӀе хьажийна:',
@@ -1756,8 +1758,8 @@ PICT # тайп тайпан
 'newpages' => 'Керла агlонаш',
 'newpages-username' => 'Декъашхо:',
 'ancientpages' => 'Яззамаш оцу терахьца тӀаьххьара тадар дина долу',
-'move' => 'Цlе хийца',
-'movethispage' => 'Хlокху агlон цlе хийца',
+'move' => 'ЦӀе хийца',
+'movethispage' => 'ХӀокху агӀон цӀе хийца',
 'unusedimagestext' => 'Дехар до, тидаме эца, кхин йолу дуьнана машан-меттигаш а лелош хила мега нийсса йогӀу хьажораг (URL) хӀокху хӀуман, хӀокху могӀаме йогӀуш ялахь яцахь а иза хила мега жигара лелош.',
 'unusedcategoriestext' => 'ХӀокху категорешан чохь агӀонаш я кхин категореш яц.',
 'notargettitle' => 'Ӏалашо билгал йина яц',
@@ -1794,7 +1796,7 @@ PICT # тайп тайпан
 'allinnamespace' => 'Массо агlонаш оцу цlери анахь «$1»',
 'allpagessubmit' => 'Кхочушдé',
 'allpagesprefix' => 'Лаха агlонаш, дlайуьлалуш йолу:',
-'allpagesbadtitle' => 'Цамагош йолу агlон цlе. Коьрта могlан юкъах ю юкъарвики меттанашан юкъе тlечlагlйина йолу хьаьрк йа магийна доцу оцу коьрта моlанца сабол элп йа кхин.',
+'allpagesbadtitle' => 'Цамагош йолу агӀон цӀе. Коьрта могӀан юкъах ю юкъарвики меттанашан юкъе тӀечӀагӀйина йолу хьаьрк йа магийна доцу оцу коьрта моӀанца символаш йа кхин.',
 'allpages-bad-ns' => '{{SITENAME}} кху чохь ана цlераш яц «$1».',
 'allpages-hide-redirects' => 'Къайлаяха дӀасахьажийнарш',
 
@@ -1882,6 +1884,7 @@ PICT # тайп тайпан
 'notanarticle' => 'Бац яззам',
 'watchlist-details' => 'Хьан тергаме могlамца $1 {{PLURAL:$1|агlо|агlонаш|агlонаш}} ю, дийцаре агlонаша йоцуш.',
 'wlheader-showupdated' => "Хийцам бина агӀонаш '''Ӏаьржа''' шрифтцан билгальяха ю.",
+'wlnote2' => 'Лахахьа гайтина {{PLURAL:$1|тӀеххьара сахьт}} чохь бина хийцамаш $2 $3.',
 'wlshowlast' => 'Гайта тlаьххьара $1 сахьташ $2 денош $3',
 'watchlist-options' => 'Тергаме могlаман гlирс нисбар',
 
@@ -1944,7 +1947,7 @@ PICT # тайп тайпан
 'unprotectedarticle' => 'ГӀоролла дӀадаьстина «[[$1]]»',
 'movedarticleprotection' => '«[[$2]]» агӀона тӀера гӀаролла «[[$1]]» агӀона тӀе даьккхина',
 'protect-title' => 'Оцунна «$1» гӀоралла дар',
-'prot_1movedto2' => '«[[$1]]» цlе хийцина оцу «[[$2]]»',
+'prot_1movedto2' => '«[[$1]]» цӀе хийцина оцу «[[$2]]»',
 'protect-legend' => 'Бакъде гӀоралла дар',
 'protectcomment' => 'Бахьан:',
 'protectexpiry' => 'Чекхйолу:',
@@ -2188,24 +2191,24 @@ PICT # тайп тайпан
 '''ДӀАХЬЕДАР!'''
 
 ЦӀе хийцар бахьнехь гӀаръялла агӀонашна дукха дагахь боцу хийцамаш хила тарло. Цундела цӀе хийцале шеш хила тарлучу тӀехьонашах кхета аьлла тешна хила.",
-'movepagetalktext' => "Тlе хlоьттина йолу дийцаре агlо ишта цlе хийцина хира ю, '''цхьа йолу ханчохь, маца:'''
+'movepagetalktext' => "ТӀе хӀоьттина йолу дийцаре агӀо ишта цӀе хийцина хира ю, '''цхьа йолу ханчохь, маца:'''
 
-*Йаьсса йоцу дийцаре агlо йолуш ю оцу цlарца йа
-*Ахьа къастаман харжам цабиняхь а къастам хlотточехь.
+*Йаьсса йоцу дийцаре агӀо йолуш ю оцу цӀарца йа
+*Ахьа къастаман харжам цабиняхь а къастам хӀотточехь.
 
-Ишта чу ханчохь, ахьа дехьа яккха йезар ю йа куьйга хlоттайар, нагахь иза хьашт йалахь.",
+Ишта чу ханчохь, ахьа дехьа яккха йезар ю йа куьйга хӀоттайар, нагахь иза хьашт йалахь.",
 'movearticle' => 'Цle хийца хlокху агlон',
 'moveuserpage-warning' => "'''Тергам бе.''' Хьо декъашхочун агӀона цӀе хийца гӀерта. Дехар до, тергам бе, декъашхочун агӀона цӀе бен хийца лур яц, декъашхочун дӀаяздаран цӀе хийца лур яц.",
 'newtitle' => 'Керла цlе',
 'move-watch' => 'Латайé хӀара агӀо тергаме могӀанан юкъахь',
-'movepagebtn' => 'Агlон цlе хийца',
-'pagemovedsub' => 'Агlон цlе хийцина',
-'movepage-moved' => "'''Агlон цlе «$1» хийцина хlокху «$2»'''",
+'movepagebtn' => 'АгӀон цӀе хийца',
+'pagemovedsub' => 'АгӀон цӀе хийцина',
+'movepage-moved' => "'''АгӀон цӀе «$1» хийцина хӀокху «$2»'''",
 'movepage-moved-redirect' => 'Кхоьллина дӀасахьажориг.',
 'movepage-moved-noredirect' => 'ДӀасхьажориг кхоллар дохина.',
-'articleexists' => 'Хlарасанна цlе йолу агlо йолуш ю йа ахьа гойтуш йолу цlе магош яц.
-Дехар до, харжа кхин цlе.',
-'movetalk' => 'Цуьнца йогlуш йолу дийцаре агlон цlе хийцар',
+'articleexists' => 'ХӀарасанна цӀе йолу агӀо йолуш ю йа ахьа гойтуш йолу цӀе магош яц.
+Дехар до, харжа кхин цӀе.',
+'movetalk' => 'Цуьнца йогӀуш йолу дийцаре агӀон цӀе хийцар',
 'move-subpages' => 'ЦӀeрш хийцае бухара агӀонаши ($1 кхаччалц)',
 'move-talk-subpages' => 'ЦӀе хийца бухара агӀонаши а агӀонашан дийцаре а ($1  кхаччалц)',
 'movepage-page-exists' => 'Агӏо $1 йолуш ю цундела и ша юху дӏаязъян йиш яц.',
@@ -2298,7 +2301,7 @@ PICT # тайп тайпан
 'tooltip-ca-protect' => 'Гlаролла дé хlокху агlон хийцам цабайта',
 'tooltip-ca-unprotect' => 'Дlадаккха хlокху агlонна долу гаролла',
 'tooltip-ca-delete' => 'ДӀаяккха хӀара агӀо',
-'tooltip-ca-move' => 'Агlон цlе хийца',
+'tooltip-ca-move' => 'АгӀон цӀе хийца',
 'tooltip-ca-watch' => 'ТӀетоха хӀара агӀо сан тергаме могӀанан юкъа',
 'tooltip-ca-unwatch' => 'ДӀаяккха хӀара агӀо хьай тергаме могӀанан юкъар',
 'tooltip-search' => 'Лаха иза дош',
@@ -2417,6 +2420,8 @@ PICT # тайп тайпан
 
 # Video information, used by Language::formatTimePeriod() to format lengths in the above messages
 'seconds-abbrev' => '$1оцу',
+'minutes-abbrev' => '$1 мин',
+'hours-abbrev' => '$1 сахь.',
 'hours' => '{{PLURAL:$1|1 сахьт}}',
 'days' => '{{PLURAL:$1|$1 де}}',
 'weeks' => '{{PLURAL:$1|$1 кӀира}}',
@@ -2631,6 +2636,7 @@ MediaWiki яржош ю и шуна пайдане хир яц те аьлла,
 Шоьга кхача езаш яра [{{SERVER}}{{SCRIPTPATH}}/COPYING копи GNU General Public License] хӀокху программица, кхаьчна яцахь язъе Free Software Foundation, Inc., адрес тӀе: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA я [//www.gnu.org/licenses/old-licenses/gpl-2.0.html еша и онлайнехь].',
 'version-software' => 'ДӀахӀоттийна программин латтор',
 'version-software-version' => 'Верси',
+'version-entrypoints' => 'ЧугӀо адресин тӀадамаш',
 
 # Special:Redirect
 'redirect' => 'Декъашхочун файлан тӀера дӀасхьажор',
@@ -2737,7 +2743,7 @@ MediaWiki яржош ю и шуна пайдане хир яц те аьлла,
 'logentry-newusers-newusers' => '{{GENDER:$2|ДӀавазвелла|ДӀаязелла}} керла декъашхо $1',
 'logentry-newusers-create' => '{{GENDER:$2|ДӀавазвелла|ДӀаязелла}} керла декъашхо $1',
 'logentry-newusers-autocreate' => 'Автоматически кхоьллина {{GENDER:$2|декъашхочун}} $1 дӀаяздар',
-'logentry-rights-rights' => '$1 {{GENDER:$2|хийцина}} хӀокхуна $3 бакъо $4 → $5',
+'logentry-rights-rights' => '$1 {{GENDER:$2|хийцина}} $3 бакъо $4 → $5',
 'logentry-rights-rights-legacy' => '$1 {{GENDER:$2|хийцина}} хӏокхуна $3 бакъо',
 'rightsnone' => '(яц)',
 
index 0dd50ba..2d208ee 100644 (file)
@@ -1290,6 +1290,7 @@ Sie darf nicht mehr als $2 {{PLURAL:$2|Aufruf|Aufrufe}} haben, es {{PLURAL:$1|is
 Bitte prüfe den Vergleich unten, um sicherzustellen, dass du dies tun möchtest, und speichere dann unten deine Änderungen, um die Bearbeitung rückgängig zu machen.',
 'undo-failure' => 'Die Änderung konnte nicht rückgängig gemacht werden, da der betroffene Abschnitt zwischenzeitlich verändert wurde.',
 'undo-norev' => 'Die Bearbeitung konnte nicht rückgängig gemacht werden, da sie nicht vorhanden ist oder gelöscht wurde.',
+'undo-nochange' => 'Anscheinend wurde diese Bearbeitung bereits rückgängig gemacht.',
 'undo-summary' => 'Änderung $1 von [[Special:Contributions/$2|$2]] ([[User talk:$2|Diskussion]]) rückgängig gemacht.',
 'undo-summary-username-hidden' => 'Änderung $1 eines versteckten Benutzers rückgängig gemacht.',
 
index 228cfb6..15a0c02 100644 (file)
@@ -1209,8 +1209,8 @@ Sebebo ke terefê $3 ra diyao ''$2''",
 'last' => 'peyên',
 'page_first' => 'verên',
 'page_last' => 'peyên',
-'histlegend' => "Ferqê weçinayışi: Qutiya versiyonan mor ke u  ''enter''i bıpıloxne ya zi makera cêrêne bıpıloxne.<br /> 
-Lecant: '''({{int:cur}})''' = ferqê versiyonê peyêni, '''({{int:last}})''' = ferqê versiyonê verêni, '''{{int:minoreditletter}}''' = vurnayışo werdi.",
+'histlegend' => "Ferqê weçinıtışi: Qutiya versiyonan seba têversanayış işaret ke û dest be ''enter''i ya zi gocega cêrêne ro ne.<br />
+Cedwel: <strong>({{int:ferq}})</strong> = ferqê verziyonê peyêni, <strong>({{int:peyên}})</strong> = ferqê versiyonê verêni, <strong>{{int:q}}</strong> = vurnayışo werdi.",
 'history-fieldset-title' => 'Tarixi bıvêne',
 'history-show-deleted' => 'Tenya esterıtey',
 'histfirst' => 'Verênêr',
index 5d42c83..fa0a354 100644 (file)
@@ -1616,6 +1616,7 @@ These arguments have been omitted.",
 Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.',
 'undo-failure'                 => 'The edit could not be undone due to conflicting intermediate edits.',
 'undo-norev'                   => 'The edit could not be undone because it does not exist or was deleted.',
+'undo-nochange'                => 'The edit appears to have already been undone.',
 'undo-summary'                 => 'Undo revision $1 by [[Special:Contributions/$2|$2]] ([[User talk:$2|talk]])',
 'undo-summary-username-hidden' => 'Undo revision $1 by a hidden user',
 
index b187169..5bd1ca0 100644 (file)
@@ -583,6 +583,8 @@ Am det uunmeldin uftuslütjen, skel dü en nei paaswurd iindu.',
 'retypenew' => 'Skriiw det paaswurd noch ans weder hen:',
 'resetpass_submit' => 'Paaswurd saat an uunmelde',
 'changepassword-success' => 'Din paaswurd as feranert wurden!',
+'changepassword-throttled' => 'Dü heest tufölsis fersoocht, di uuntumeldin.
+Wees so gud an teew $1, iar dü det noch ans ferschükst.',
 'resetpass_forbidden' => 'Det paaswurd koon ei feranert wurd.',
 'resetpass-no-info' => 'Dü skel di uunmelde, am üüb det sidj tutugripen.',
 'resetpass-submit-loggedin' => 'Paaswurd feranre',
@@ -634,6 +636,8 @@ Tidjwiis paaswurd: $2',
 'changeemail-password' => 'Din {{SITENAME}} paaswurd:',
 'changeemail-submit' => 'E-mail adres feranre',
 'changeemail-cancel' => 'Ufbreeg',
+'changeemail-throttled' => 'Dü heest tufölsis fersoocht, di uuntumeldin.
+Wees so gud an teew $1, iar dü det noch ans ferschükst.',
 
 # Special:ResetTokens
 'resettokens' => 'Tokens turagsaat',
@@ -848,7 +852,7 @@ Uun't strik- an fersküüw-logbuk oner stäänt muar diartu.",
 'invalid-content-data' => 'Diar stäänt wat uun, wat diar ei hen hiart',
 'content-not-allowed-here' => '„$1“ mut ei skrewen wurd üüb sidj [[$2]]',
 'editwarning-warning' => 'Wan dü detheer sidj slotst, kön feranrangen ferleesen gung.
-Üs uunmeldet brüker könst dü detheer wäärnang bi din iinstelangen oner „Bewerke“ wechknipse.',
+Üs uunmeldet brüker könst dü detheer wäärnang bi din iinstelangen oner „{{int:prefs-editing}}“ ufstel.',
 'editpage-notsupportedcontentformat-title' => 'Detdiar formoot gongt ei.',
 'editpage-notsupportedcontentformat-text' => 'Det formoot $1 gongt ei mä det model $2.',
 
@@ -1056,6 +1060,8 @@ A nawigatjuun-links saat ales weder turag üüb di ual stant.',
 'showhideselectedversions' => 'Werjuunen wise of fersteeg',
 'editundo' => 'turagsaat',
 'diff-empty' => '(nään ferskeel)',
+'diff-multi-sameuser' => '({{PLURAL:$1|Ian werjuun diartesken|$1 werjuunen diartesken}} faan disalew brüker {{PLURAL:$1|woort|wurd}} ei uunwiset)',
+'diff-multi-otherusers' => '({{PLURAL:$1|Ian werjuun diartesken|$1 werjuunen diartesken}} faan {{PLURAL:$2|ään öödern brüker|$2 ööder brükern}} {{PLURAL:$1|woort|wurd}} ei uunwiset)',
 'diff-multi-manyusers' => '({{PLURAL:$1|Ian werjuun diartesken|$1 werjuunen diartesken}} faan muar üs $2 {{PLURAL:$2|brüker|brükern}} wurd ei uunwiset)',
 'difference-missing-revision' => "{{PLURAL:$2|Ian werjuun|$2 werjuunen}} faan di ferskeel ($1) {{PLURAL:$2|as|san}} ei fünjen wurden.
 
@@ -1076,7 +1082,7 @@ Dü könst det uun't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}}
 'shown-title' => 'Wise $1 {{PLURAL:$1|resultaat|resultaaten}} per sidj',
 'viewprevnext' => 'Wise ($1 {{int:pipe-separator}} $2) ($3)',
 'searchmenu-exists' => "'''Deer as en sid nååmd \"[[:\$1]]\" önj jüdeer Wiki'''",
-'searchmenu-new' => "'''Maage det sidj „[[:$1]]“ uun detheer wiki.'''",
+'searchmenu-new' => '<strong>Maage det sidj „[[:$1]]“ uun detheer wiki!</strong> {{PLURAL:$2|0=|Luke uk bi det fünjen sidj.|Luke uk bi a fünjen sidjen.}}',
 'searchprofile-articles' => 'Artiikler',
 'searchprofile-project' => 'Halep- an Projektsidjen',
 'searchprofile-images' => 'Multimedia',
@@ -1092,6 +1098,7 @@ Dü könst det uun't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}}
 'search-result-score' => 'Relewans: $1 %',
 'search-redirect' => '(widjerfeerd faan „$1“)',
 'search-section' => '(kirew $1)',
+'search-file-match' => '(fünjen tekst)',
 'search-suggest' => 'Mendst dü „$1“?',
 'search-interwiki-caption' => 'Saster-projekten',
 'search-interwiki-default' => '$1 resultaaten:',
@@ -2082,6 +2089,7 @@ Feranrangen faan detdiar sidj wurd üüb detdiar list fäästhäälen.",
 'watchmethod-list' => "Sidjen, diar dü uun't uug heest, am a leetst feranrangen beluke",
 'watchlistcontains' => "Dü häälst $1 {{PLURAL:$1|sidj|sidjen}} uun't uug.",
 'iteminvalidname' => 'Mä di iindrach „$1“ stemet wat ei, di nööm as ferkiard.',
+'wlnote2' => 'Diar stun a feranrangen faan a leetst {{PLURAL:$1|stünj|<strong>$1</strong> stünjen}}, üüb a stant faan $2, $3.',
 'wlshowlast' => 'Wise a feranrangen faan leetst $1 stünjen, $2 daar of $3.',
 'watchlist-options' => "Iinstelangen för't uunwisin",
 
@@ -2161,7 +2169,7 @@ Halep an muar diartu: {{canonicalurl:{{MediaWiki:Helppage}}}}',
 'delete-edit-reasonlist' => "Grünjer för't striken bewerke",
 'delete-toobig' => 'Detdiar sidj hää muar üs $1 {{PLURAL:$1|werjuun|werjuunen}} . Sok sidjen kön ei so gau stregen wurd, ööders san a servers plaat.',
 'delete-warning-toobig' => "Detdiar sidj hää muar üs $1 {{PLURAL:$1|werjuun|werjuunen}} . Det striken koon komer maage bi't dootenbeenk.",
-'deleting-backlinks-warning' => "'''Paase üüb:''' Diar ferwise noch ööder sidjen üüb det sidj, diar dü strik wel.",
+'deleting-backlinks-warning' => "'''Paase üüb:''' Diar ferwise noch ööder sidjen üüb det sidj, diar dü strik wel. Of det sidj as noch huarööders iinbünjen.",
 
 # Rollback
 'rollback' => 'Feranrangen turagsaat',
@@ -2458,7 +2466,7 @@ Luke bi't [[Special:BlockList|sperlist]] för aal jo aktuel speren.",
 'range_block_disabled' => 'Det mögelkhaid, hialer adresrümer tu sperin, as ei aktiif.',
 'ipb_expiry_invalid' => 'Didiar tidjrüm gongt ei.',
 'ipb_expiry_temp' => 'Ferbürgen brükernööm-speren skel permanent wees.',
-'ipb_hide_invalid' => 'Detdiar brükerkonto koon ei ferbürgen wurd, auer diar tuföl feranrangen uun a ferluup stun.',
+'ipb_hide_invalid' => 'Detdiar brükerkonto koon ei ferbürgen wurd, auer diar muar üs {{PLURAL:$1|ian feranrang|$1 feranrangen}} uun a ferluup stun.',
 'ipb_already_blocked' => '„$1“ as al speret',
 'ipb-needreblock' => '$1 as al speret. Wel dü a speriinstelangen feranre?',
 'ipb-otherblocks-header' => 'Ööder {{PLURAL:$1|sper|speren}}',
@@ -2619,6 +2627,7 @@ Wees so gud an beschük a sidjen [https://www.mediawiki.org/wiki/Localisation Me
 'allmessages-prefix' => 'Filter mä prefix:',
 'allmessages-language' => 'Spriak:',
 'allmessages-filter-submit' => 'Widjer',
+'allmessages-filter-translate' => 'Auersaat',
 
 # Thumbnails
 'thumbnail-more' => 'Fergratre',
@@ -2668,7 +2677,7 @@ Transwiki-import-aktjuunen wurd uun't [[Special:Log/import|Import-logbuk]] fää
 'importuploaderrortemp' => "Bi't huuchschüüren faan det importdatei as wat skiaf gingen. Diar as nian tidjwiis fertiaknis.",
 'import-parse-failure' => "Bi't importiarin faan det XML-datei as wat skiaf gingen.",
 'import-noarticle' => 'Diar as nian sidj tu importiarin bestemet wurden.',
-'import-nonewrevisions' => 'Aal jodiar werjuunen san al ans importiaret wurden.',
+'import-nonewrevisions' => 'Diar wurd nian werjuunen importiaret, auer jo eder al diar wiar of auersprüngen wurden san.',
 'xml-error-string' => '$1 uun rä $2, türn $3 (byte $4): $5',
 'import-upload' => 'XML-datein importiare',
 'import-token-mismatch' => 'Session dooten san wech. Ferschük det noch ans weder.',
@@ -2679,6 +2688,7 @@ Transwiki-import-aktjuunen wurd uun't [[Special:Log/import|Import-logbuk]] fää
 'import-error-special' => 'Det sidj „$1“ as ei importiaret wurden, auer hat tu en nöömrüm hiart, huar nian sidjen mögelk san.',
 'import-error-invalid' => 'Det sidj „$1“ as ei importiaret wurden, auer di nööm ei stemet.',
 'import-error-unserialize' => 'Det werjuun $2 faan det sidj „$1“ küd ei deserialisiaret wurd. Det werjuun woort mä det münster $3 brükt, an det as mä $4 serialisiaret.',
+'import-error-bad-location' => 'Det feranrang $2 mä model $3 koon ei üs "$1" üüb detheer wiki seekert wurd, auer detdiar model heer ei brükt wurd koon.',
 'import-options-wrong' => 'Ferkiard {{PLURAL:$2|iinstelang|iinstelangen}}: <nowiki>$1</nowiki>',
 'import-rootpage-invalid' => 'Didiar sidjennööm as ferkiard.',
 'import-rootpage-nosubpage' => 'Uun di nöömrüm „$1“ jaft at nian onersidjen.',
@@ -3818,4 +3828,7 @@ Uk parser-funktjuunen liküs <code><nowiki>{{</nowiki>#language:…}}</code> an
 'expand_templates_generate_rawhtml' => 'Rä HTML uunwise',
 'expand_templates_preview' => 'Föörskau',
 
+# Unknown messages
+'createaccount-hook-aborted' => '$1',
+'uploadinvalidxml' => "Det XML uun det huuchschüürd datei küd ei ''parset'' wurd.",
 );
index 03b8683..a09ce51 100644 (file)
@@ -1138,7 +1138,7 @@ IP-мекенжайыңыз бұл беттің түзету тарихында
 'edit-gone-missing' => 'Бетті жаңарту мүмкін емес.
 Мүмкін, бұл бет жойылған.',
 'edit-conflict' => 'Өңдемелер қақтығысы.',
-'postedit-confirmation' => 'Сіздің өңдемеңіз сақталды.',
+'postedit-confirmation' => 'Өңдемеңіз сақталды.',
 'edit-already-exists' => 'Жаңа бет жасау мүмкін емес.
 Ол әлдеқашан бар.',
 'defaultmessagetext' => 'Әдепкі мәтіні',
index 21aafec..6bb6baf 100644 (file)
@@ -380,7 +380,7 @@ $messages = array(
 'undelete_short' => 'Traer atrás $1 {{PLURAL:$1|trocamientos|trocamientos}}',
 'viewdeleted_short' => 'Ver {{PLURAL:$1|un trocamiento efassado|$1 trocamientos efassados}}',
 'protect' => 'Guadrar',
-'protect_change' => 'trocar el guardadijo',
+'protect_change' => 'trocar',
 'protectthispage' => 'Guardar esta hoja',
 'unprotect' => 'Trocar guardadijo',
 'unprotectthispage' => 'Trocar el guardadijo desta hoja',
@@ -983,8 +983,8 @@ Leyenda: (act) = diferencias con la versión actual,
 'recentchangeslinked-feed' => 'Trocamientos conectados',
 'recentchangeslinked-toolbox' => 'Trocamientos atados',
 'recentchangeslinked-title' => 'Los trocamientos relacionados con "$1"',
-'recentchangeslinked-summary' => "Esto es la lista de los trocamientos dalcavo de las hojas que relatan a una hoja particòlar (o de los miembros de la kategoría particòlar).
-Las hojas en tu [[Special:Watchlist|lista de akavidamiento]] son '''reforçadas'''.",
+'recentchangeslinked-summary' => "Esto es una lista de trocamientos dalcavo en las hojas atadas de una hoja partikolara (u en los miembros de una kategoría partikolara).
+Las hojas en tu [[Special:Watchlist|lista de acavidamiento]] son '''reforçadas'''.",
 'recentchangeslinked-page' => 'Nombre de la hoja',
 'recentchangeslinked-to' => 'Amostra los trocamientos freskos en lugar de la hoja indicada',
 
@@ -1385,7 +1385,7 @@ Puedes ver su manadero',
 'tooltip-ca-protect' => 'Guardar esta hoja',
 'tooltip-ca-delete' => 'Efassar esta hoja',
 'tooltip-ca-move' => 'Taxirea (renombra) esta hoja',
-'tooltip-ca-watch' => 'Ajustar esta hoja a tu lista de akavidamientos',
+'tooltip-ca-watch' => 'Ajusta esta hoja a tu lista de acavidamientos',
 'tooltip-ca-unwatch' => 'Quita esta hoja de tu lista de escojidos',
 'tooltip-search' => 'Buxca en {{SITENAME}}',
 'tooltip-search-go' => 'Ir a la hoja con este nombre egzakto, si egziste.',
@@ -1399,9 +1399,9 @@ Puedes ver su manadero',
 'tooltip-n-randompage' => 'Carga una hoja por azardo',
 'tooltip-n-help' => 'Ambézate y topa ayudo',
 'tooltip-t-whatlinkshere' => 'Una lista de todas las hojas del viki que tienen atamientos a esta hoja',
-'tooltip-t-recentchangeslinked' => 'Los trocamientos dalcavo de las hojas atadas a la ésta',
+'tooltip-t-recentchangeslinked' => 'Los trocamientos dalcavo en las hojas atadas a la ésta',
 'tooltip-feed-rss' => 'Sindicación RSS de esta hoja',
-'tooltip-feed-atom' => "Fuente de Atom d'esta hoja",
+'tooltip-feed-atom' => 'Canal Atomo parâ esta hoja',
 'tooltip-t-contributions' => 'Ver la lista de ajustamientos de este usuario',
 'tooltip-t-emailuser' => 'A este usuario, mándale una letra electrόnica (ímey)',
 'tooltip-t-upload' => 'Suve dosyas',
@@ -1423,7 +1423,7 @@ Puedes ver su manadero',
 'tooltip-watch' => 'Ajusta esta hoja a tu lista de escojidas',
 'tooltip-rollback' => '«Hazer aboltar» haze aboltar todos los trocamientos del usador dalcavo, sólo en klikando una vez.',
 'tooltip-undo' => '«Des-hazer» abolta este trocamiento y lo avre en el modo de previsteo. Permete ajustar una razón en el somario.',
-'tooltip-summary' => 'Entrar un somaryo kurto',
+'tooltip-summary' => 'Entrar un somario curto',
 
 # Attribution
 'anonymous' => '{{PLURAL:$1|Uzuario anonimo|Uzuarios anonimos}} de {{SITENAME}}',
index 9077e54..51308ec 100644 (file)
@@ -1163,7 +1163,7 @@ $1 ആണ് ഈ തടയൽ നടത്തിയത്. ''$2'' എന്ന
 'invalid-content-data' => 'അസാധുവായ ഉള്ളടക്ക ഡേറ്റ',
 'content-not-allowed-here' => '"$1" ഉള്ളടക്കം [[$2]] താളിൽ അനുവദിക്കുന്നില്ല',
 'editwarning-warning' => 'ഈ താളിൽ നിന്നും പോകുന്നത് താങ്കൾ വരുത്തിയ മാറ്റങ്ങൾ നഷ്ടപ്പെടാൻ ഇടയാക്കും.
-താങ്കൾ ലോഗിൻ ചെയ്തിട്ടുണ്ടെങ്കിൽ, താങ്കളുടെ ക്രമീകരണങ്ങളിൽ "തിരുത്തൽ" എന്ന ഭാഗത്ത് ചെന്ന് ഈ അറിയിപ്പ് പ്രദർശിപ്പിക്കുന്നത് ഒഴിവാക്കാവുന്നതാണ്.',
+താങ്കൾ ലോഗിൻ ചെയ്തിട്ടുണ്ടെങ്കിൽ, താങ്കളുടെ ക്രമീകരണങ്ങളിൽ "{{int:prefs-editing}}"  എന്ന ഭാഗത്ത് ചെന്ന് ഈ അറിയിപ്പ് പ്രദർശിപ്പിക്കുന്നത് ഒഴിവാക്കാവുന്നതാണ്.',
 'editpage-notsupportedcontentformat-title' => 'ഉള്ളടക്ക ഫോർമാറ്റ് പിന്തുണയ്ക്കുന്നില്ല',
 'editpage-notsupportedcontentformat-text' => 'ഉള്ളടക്കത്തിന്റെ ഫോർമാറ്റ് ആയ $1 ഉള്ളടക്ക രീതിയായ $2 പിന്തുണയ്ക്കുന്നില്ല.',
 
@@ -1381,6 +1381,8 @@ $1",
 'showhideselectedversions' => 'തിരഞ്ഞെടുത്ത മാറ്റങ്ങൾ പ്രദർശിപ്പിക്കുക/മറയ്ക്കുക',
 'editundo' => 'മാറ്റം തിരസ്ക്കരിക്കുക',
 'diff-empty' => '(വ്യത്യാസം ഇല്ല)',
+'diff-multi-sameuser' => '(ഒരേ ഉപയോക്താവ് ചെയ്ത {{PLURAL:$1|ഇടയ്ക്കുള്ള ഒരു നാൾപ്പതിപ്പ്|ഇടയ്ക്കുള്ള $1 നാൾപ്പതിപ്പുകൾ}} പ്രദർശിപ്പിക്കുന്നില്ല)',
+'diff-multi-otherusers' => '({{PLURAL:$2|മറ്റൊരു ഉപയോക്താവ്|$2 ഉപയോക്താക്കൾ}} ചെയ്ത {{PLURAL:$1|ഇടയ്ക്കുള്ള ഒരു നാൾപ്പതിപ്പ്|ഇടയ്ക്കുള്ള $1 നാൾപ്പതിപ്പുകൾ}} പ്രദർശിപ്പിക്കുന്നില്ല)',
 'diff-multi-manyusers' => '(ഇടയ്ക്ക് {{PLURAL:$2|ഒന്നിലധികം|$2 എണ്ണത്തിലധികം}} ഉപയോക്താക്കൾ ചെയ്തിട്ടുള്ള {{PLURAL:$1|ഒരു പതിപ്പ്|$1 പതിപ്പുകൾ}} പ്രദർശിപ്പിക്കുന്നില്ല.)',
 'difference-missing-revision' => 'ഈ വ്യത്യാസത്തിൽ ($1) {{PLURAL:$2|ഒരു നാൾപ്പതിപ്പ്|$2 നാൾപ്പതിപ്പുകൾ}} കാണാനായില്ല.
 
@@ -1401,7 +1403,7 @@ $1",
 'shown-title' => '{{PLURAL:$1|ഒരു ഫലം|$1 ഫലങ്ങൾ}} വീതം താളിൽ കാണിക്കുക',
 'viewprevnext' => '$1 {{int:pipe-separator}} $2 എണ്ണം കാണുക ($3)',
 'searchmenu-exists' => "'''\"[[:\$1]]\" എന്ന തലക്കെട്ടിൽ ഒരു താൾ ഈ വിക്കിയിൽ നിലവിലുണ്ട്'''",
-'searchmenu-new' => "'''ഈ വിക്കിയിൽ \"[[:\$1]]\" താൾ നിർമ്മിക്കുക!'''",
+'searchmenu-new' => '<strong>ഈ വിക്കിയിൽ "[[:$1]]" എന്ന താൾ സൃഷ്ടിക്കുക!</strong> {{PLURAL:$2|0=|ഒപ്പം താങ്കളുടെ തിരയലിനു ലഭിച്ച ഫലമായ ഈ താൾ കാണുക.|ഒപ്പം താങ്കളുടെ തിരയലിനു ലഭിച്ച ഫലങ്ങൾ കാണുക.}}',
 'searchprofile-articles' => 'ലേഖനങ്ങളിൽ',
 'searchprofile-project' => 'സഹായം, പദ്ധതി താളുകളിൽ',
 'searchprofile-images' => 'പ്രമാണങ്ങളിൽ',
@@ -2511,7 +2513,7 @@ $UNWATCHURL
 'delete-edit-reasonlist' => 'മായ്ക്കലിന്റെ കാരണം തിരുത്തുക',
 'delete-toobig' => 'ഈ താളിനു വളരെ വിപുലമായ തിരുത്തൽ ചരിത്രമുണ്ട്. $1 മേൽ {{PLURAL:$1|പതിപ്പുണ്ട്|പതിപ്പുകളുണ്ട്}}. ഇത്തരം താളുകൾ മായ്ക്കുന്നതു {{SITENAME}} സം‌രംഭത്തിന്റെ നിലനില്പ്പിനെ തന്നെ ബാധിക്കുമെന്നതിനാൽ ഈ താൾ മായ്ക്കുന്നതിനുള്ള അവകാശം പരിമിതപ്പെടുത്തിയിരിക്കുന്നു.',
 'delete-warning-toobig' => 'ഈ താളിനു വളരെ വിപുലമായ തിരുത്തൽ ചരിത്രമുണ്ട്. അതായത്, ഇതിനു് $1 മേൽ {{PLURAL:$1|പതിപ്പുണ്ട്|പതിപ്പുകളുണ്ട്}}. ഇത്തരം താളുകൾ മായ്ക്കുന്നതു {{SITENAME}} സം‌രംഭത്തിന്റെ ഡാറ്റാബേസ് ഓപ്പറേഷനെ ബാധിച്ചേക്കാം. അതിനാൽ വളരെ ശ്രദ്ധാപൂർവ്വം തുടർനടപടികളിലേക്കു നീങ്ങുക.',
-'deleting-backlinks-warning' => "'''മുന്നറിയിപ്പ്:''' മറ്റു താളുകളിൽ നിന്നും താളിലേയ്ക്കുള്ള കണ്ണികൾ താങ്കൾ മായ്ക്കാൻ പോവുകയാണ്.",
+'deleting-backlinks-warning' => "'''à´®àµ\81à´¨àµ\8dനറിയിപàµ\8dà´ªàµ\8d:''' à´®à´±àµ\8dà´±àµ\81 à´¤à´¾à´³àµ\81à´\95ളിൽ à´¨à´¿à´¨àµ\8dà´¨àµ\81à´\82 à´¤à´¾à´³à´¿à´²àµ\87à´¯àµ\8dà´\95àµ\8dà´\95àµ\81à´³àµ\8dà´³ à´\95à´£àµ\8dണിà´\95ൾ à´\85à´²àµ\8dà´²àµ\86à´\99àµ\8dà´\95ിൽ à´\89ൾപàµ\8dà´ªàµ\86à´\9fàµ\81à´¤àµ\8dതിയിà´\9fàµ\8dà´\9fàµ\81à´³àµ\8dà´³ à´¤à´¾à´³àµ\81à´\95ൾ à´¤à´¾à´\99àµ\8dà´\95ൾ à´®à´¾à´¯àµ\8dà´\95àµ\8dà´\95ാൻ à´ªàµ\8bà´µàµ\81à´\95യാണàµ\8d.",
 
 # Rollback
 'rollback' => 'തിരുത്തലുകൾ റോൾബാക്ക് ചെയ്യുക',
@@ -3035,7 +3037,7 @@ $1',
 തത്കാലത്തേയ്ക്കു വേണ്ടിയിരുന്ന ഒരു ഫോൾഡർ ലഭ്യമല്ല.',
 'import-parse-failure' => 'എക്സ്.എം.എൽ. ഇറക്കുമതി പാഴ്സ് പരാജയം',
 'import-noarticle' => 'ഇറക്കുമതി ചെയ്യാൻ താൾ ഇല്ല!',
-'import-nonewrevisions' => 'à´\8eà´²àµ\8dലാ à´ªà´¤à´¿à´ªàµ\8dà´ªàµ\81à´\95à´³àµ\81à´\82 à´®àµ\81à´®àµ\8dà´ªàµ\87 à´\87à´±à´\95àµ\8dà´\95àµ\81മതി à´\9aàµ\86à´¯àµ\8dതിà´\9fàµ\8dà´\9fàµ\81à´³àµ\8dളതാണàµ\8dâ\80\8c.',
+'import-nonewrevisions' => 'à´\92à´°àµ\81 à´¨à´¾àµ¾à´ªàµ\8dപതിപàµ\8dà´ªàµ\81à´\82 à´\87à´±à´\95àµ\8dà´\95àµ\81മതി à´\9aàµ\86à´¯àµ\8dതിà´\9fàµ\8dà´\9fà´¿à´²àµ\8dà´² (à´\8eà´²àµ\8dലാà´\82 à´¨à´¿à´²à´µà´¿àµ½ à´\89à´£àµ\8dà´\9fàµ\8d, à´\85à´²àµ\8dà´²àµ\86à´\99àµ\8dà´\95ിൽ à´ªà´¿à´´à´µàµ\81à´\95à´³àµ\81à´³àµ\8dളതിനാൽ à´\92ഴിവാà´\95àµ\8dà´\95à´¿).',
 'xml-error-string' => '$2 വരിയിൽ $1, നിര $3 (ബൈറ്റ് $4): $5',
 'import-upload' => 'എക്സ്.എം.എൽ. ഡേറ്റ അപ്‌‌ലോഡ് ചെയ്യുക',
 'import-token-mismatch' => 'സെഷൻ ഡാറ്റ നഷ്ടപ്പെട്ടതിനാൽ ദയവായി വീണ്ടും ശ്രമിക്കൂക',
@@ -4220,4 +4222,6 @@ $5
 'expand_templates_generate_rawhtml' => 'അസംസ്കൃത എച്ച്.റ്റി.എം.എൽ. പ്രദർശിപ്പിക്കുക',
 'expand_templates_preview' => 'എങ്ങനെയുണ്ടെന്നു കാണുക',
 
+# Unknown messages
+'uploadinvalidxml' => 'അപ്‌ലോഡ് ചെയ്ത പ്രമാണത്തിലെ എക്സ്.എം.എൽ. പാഴ്സ് ചെയ്യാൻ കഴിയില്ല.',
 );
index d75c838..aa7d656 100644 (file)
@@ -2300,6 +2300,9 @@ See also:
 {{Identical|Undo}}',
 'undo-norev' => 'Message appears if an attempt to revert an edit by clicking the "undo" link on the page history fails.
 
+{{Identical|Undo}}',
+'undo-nochange' => 'Message appears if an attempt to revert an edit by clicking the "undo" link results in an edit making no change to the current version of the page.
+
 {{Identical|Undo}}',
 'undo-summary' => 'Edit summary for an undo action. Parameters:
 * $1 - revision ID
index b913289..fb19e55 100644 (file)
@@ -535,7 +535,7 @@ $1",
 'youhavenewmessagesfromusers' => '$1 {{PLURAL:$3|huk ruraqmanta|$3 ruraqkunamanta}} qhawanayki kachkan ($2).',
 'youhavenewmessagesmanyusers' => '$1 achka ruraqkunamanta qhawanayki kachkan ($2).',
 'newmessageslinkplural' => '{{PLURAL:$1|Musuq willaymi|Musuq willaykunam}}',
-'newmessagesdifflinkplural' => 'qayna {{PLURAL:$1|hukchasqapi|hukchasqakunapi}} wakin kaynin',
+'newmessagesdifflinkplural' => 'ñaqha {{PLURAL:$1|hukchasqa|hukchasqakuna}}',
 'youhavenewmessagesmulti' => 'Musuq willaykunam qhawanayki kachkan $1-pi',
 'editsection' => "llamk'apuy",
 'editold' => "llamk'apuy",
@@ -660,7 +660,7 @@ Amachaq kamachiqqa kayrayku amachani nispa nirqanmi: "$3".',
 'invalidtitle-knownnamespace' => '"$2" sutisuyu, "$3" qillqasqayuq mana allin kaq qillqa suti',
 'invalidtitle-unknownnamespace' => 'Mana riqsisqa $1 kaq sutisuyu yupay, "$2" qillqasqayuq mana allin kaq qillqa suti',
 'exception-nologin' => 'Manam yaykurqankichu',
-'exception-nologin-text' => 'Kay wikipiqa icha kay ruranataqa rakiqunaykiwan yaykuspalla ruraytam atinki.',
+'exception-nologin-text' => "Ama hina kaspa [[Special:Userlogin|yaykuy]] kay p'anqaman rinata icha kayta ruranata atinaykipaq.",
 
 # Virus scanner
 'virus-badscanner' => "Manam allintachu churapusqa: mana riqsisqa añaw maskaq: ''$1''",
@@ -707,7 +707,7 @@ Ama qunqaychu [[Special:Preferences|{{SITENAME}} allinkachinaykikunata]] hukchay
 'gotaccount' => "Rakiqunaykiñachu kachkan? '''$1'''.",
 'gotaccountlink' => 'Rakiqunaykita willaway',
 'userlogin-resetlink' => 'Yaykuna willayniykikunatari qunqarqankichu?',
-'userlogin-resetpassword-link' => 'Yaykuna rimaykita kutichiy',
+'userlogin-resetpassword-link' => 'Yaykuna rimaykita qunqarqankichu?',
 'helplogin-url' => 'Help:Yaykuy',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Yaykunapaq yanapa]]',
 'userlogin-createanother' => 'Huk rakiqunata kamariy',
@@ -754,7 +754,7 @@ Allin qillqasqaykita llanchiriy.',
 'passwordtooshort' => 'Yaykuna rimayqa {{PLURAL:$1|1 icha aswan sanampayuq|$1 icha aswan sanampayuq}} kananmi.',
 'password-name-match' => 'Yaykuna rimaykiqa ruraqpa sutiykiman mana kaqlla kananmi.',
 'password-login-forbidden' => 'Kay ruraqpa sutinmanqa yaykuna rimanmanpas ama nisqam.',
-'mailmypassword' => 'Musuq yaykuna rimata e-chaskiwan kachamuway',
+'mailmypassword' => 'Yaykuna rimata kutichiy',
 'passwordremindertitle' => "{{SITENAME}}paq musuq mit'alla yaykuna rima",
 'passwordremindertext' => 'Pipas (qamchiki, $1 IP huchhayuq tiyaymanta) mañakuwarqan {{SITENAME}}paq musuq yaykuna rimatam e-chaski imamaytaykiman kachayta ($4).
 "$2" sutiyuq ruraqpa mit\'alla yaykuna rimanqa kamarisqañam, "$3" sutichasqam kachkan. Munarqaspaykiqa, kunan yaykuspa musuq yaykuna rimaykitam akllay.
@@ -770,8 +770,8 @@ Ama hina kaspa, chaskispaykiqa ruraqpa sutiykita nispa musuqmanta yaykuy.',
 'throttled-mailpassword' => "Huk yaykuna rima kutichinapaq yuyachina qayna {{PLURAL:$1|huk ura|$1 ura}} mit'api kachamusqañam. {{PLURAL:$1|Huk ura|$1 ura}} mit'apiqa hukllam yaykuna rima yuyachina kachasqa kachun millay rurayta hark'anapaq.",
 'mailerror' => 'E-chaskita kachaspa pantasqa: $1',
 'acct_creation_throttle_hit' => "Qampa IP huchhaykiyuq kachkaq ruraqkunaqa kay wikita watukuspa ñaqha 24 urapi {{PLURAL:$1|rakiqunaykim|$1 rakiqunaykim}} kamarirqanña. Manam atinkichikchu astawan kichayta huklla p'unchawpi chay IP huchhallayuq kaspa.",
-'emailauthenticated' => "E-chaski imamaytaykiqa $2 p'unchawpi, $3 pachapi chiqapchasqañam.",
-'emailnotauthenticated' => 'E-chaski imamaytaykitaqa manaraqmi takyachirqunkichu. Mana takyachirquptiykiqa, kay qatiq rurachinakunataqa manam atinkichu.',
+'emailauthenticated' => "E-chaski imamaytaykiqa $2 p'unchawpi, $3 pachapi takyachisqañam.",
+'emailnotauthenticated' => 'E-chaski imamaytaykitaqa manaraqmi takyachirqunkichu. Manaraq takyachirquptiykiqa, kay ruranakunapaq manam ima e-chaskipas kachasqa kanchu.',
 'noemailprefs' => "E-chaski imamaytaykita willaway kay rurachinakunata llamk'achinapaq.",
 'emailconfirmlink' => 'E-chaski imamaytaykita takyachiy',
 'invalidemailaddress' => "E-chaski imamaytaykiqa manam allinchu, manachá allinta qillqasqa. Ama hina kaspa, musuq allin sananchayuq imamaytaykita qillqamuy icha k'itichata ch'usaqchay.",
@@ -1058,7 +1058,7 @@ Qullusqachá.",
 'edit-already-exists' => "Manam atinichu musuq p'anqata kamariyta.
 Kachkañam.",
 'defaultmessagetext' => 'Ñawpaq qillqa',
-'content-failed-to-parse' => "Manam atinichu $2 samiqta $1 kikinchapaq t'ikrayta: $3",
+'content-failed-to-parse' => 'Manam atinichu $2 samiqta $1 kikinchapaq kuskiyta: $3',
 'invalid-content-data' => 'Samiqmanta willaykunaqa manam allinchu',
 'content-not-allowed-here' => '"$1" nisqa samiqqa [[$2]] sutiyuq p\'anqapi manam saqillasqachu',
 'editwarning-warning' => "Kay p'anqata saqispaykiqa lliw rurarqusqayki hukchasqakunatachá chinkachiykiman.
@@ -1181,18 +1181,19 @@ Kay wakin kayta qhawayta atinkim; astawanchá rikunkiman [{{fullurl:{{#Special:L
 {{SITENAME}}pi huk kamachiqkunaqa p'anqap pakasqa samiqninta qhawaspa qullusqa kaymanta kutichiyta atinkuraqmi kay kaqlla uyapuratam llamk'achispa, kay wikip kamariqninkuna mana huk saywachanakunata tiyachiptinqa.",
 'revdelete-confirm' => 'Ama hina kaspa, takyachiy munayniykita, qatiqninkunata riqsiyniykita, [[{{MediaWiki:Policy-url}}|kawpaykama]] rurayniykitapas.',
 'revdelete-suppress-text' => "Pakay ruranata '''kaylla kaptin''' llamk'achiy:
+* K'amiqchá willakuna
 * Runamanta mana allin willakuna
-*: ''wasi tiyay imamaytakuna, karu rimay huchhakuna, allin kawsay tarikuq wasimanta huchhakuna, chay hinakunapas.''",
+*: ''wasi tiyay imamaytakuna, karu rimay huchhakuna, mamallaqta sut'inchay huchhakuna, chay hinakunapas.''",
 'revdelete-legend' => 'Rikunapaq saywachanakunata tiyachiy',
-'revdelete-hide-text' => 'Qhawana qillqata pakay',
+'revdelete-hide-text' => 'Musuqmanta qhawana qillqa',
 'revdelete-hide-image' => 'Willañiqip samiqninta pakay',
 'revdelete-hide-name' => 'Rurayta paqtaytapas pakay',
-'revdelete-hide-comment' => "Llamk'apuymanta willapuyta pakay",
-'revdelete-hide-user' => 'Ruraqpa sutinta/IP huchhanta pakay',
+'revdelete-hide-comment' => "Llamk'apuymanta willapuy",
+'revdelete-hide-user' => 'Ruraqpa sutin/IP huchhan',
 'revdelete-hide-restricted' => "Kamachiqkunamanta willakunata huk ruraqkunamanta hina hark'ay",
 'revdelete-radio-same' => '(ama hukchaychu)',
-'revdelete-radio-set' => 'Arí',
-'revdelete-radio-unset' => 'Ama kachunchu',
+'revdelete-radio-set' => 'Pakasqa',
+'revdelete-radio-unset' => 'Rikunalla',
 'revdelete-suppress' => 'Kamachiqkunamantapas willakunata huk ruraqkunamanta hina raqpay',
 'revdelete-unsuppress' => "Qullusqamanta paqarisqa llamk'apusqakunapaq saywachanakunata raqpay",
 'revdelete-log' => 'Kayrayku:',
@@ -1290,7 +1291,7 @@ Imaymanata [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} qulluy ha
 'shown-title' => "Huk p'anqapi $1 {{PLURAL:$1|taripasqata|taripasqakunata}} rikuchiy",
 'viewprevnext' => 'Qhaway ($1 {{int:pipe-separator}} $2) ($3).',
 'searchmenu-exists' => "'''Kay wikipiqa «[[$1]]» sutiyuq p'anqam kachkan'''",
-'searchmenu-new' => "'''Kay wikipi \"[[:\$1]]\" sutiyuq p'anqata kamariy!'''",
+'searchmenu-new' => '<strong>Kay wikipi "[[:$1]]" sutiyuq p\'anqata kamariy!</strong> {{PLURAL:$2|0=|Maskayniykiwan tarisqa p\'anqatapas qhaway.|Maskaywan taripasqakunatapas qhaway.}}\'',
 'searchprofile-articles' => "Samiq p'anqakuna",
 'searchprofile-project' => "Yanapanapaq ruraykamaypaqpas p'anqakuna",
 'searchprofile-images' => 'Multimidya',
@@ -1619,7 +1620,7 @@ $1 {{PLURAL:$1|sanampamanta|sanampakunamanta}} aswan pisi kananmi.',
 'recentchanges-label-minor' => "Kayqa aslla llamk'apuymi",
 'recentchanges-label-bot' => "Kayqa rurana antachap llamk'apusqanmi",
 'recentchanges-label-unpatrolled' => "Kay llamk'apusqaqa manaraqmi patrullasqachu",
-'recentchanges-legend-newpage' => "$1 - musuq p'anqa",
+'recentchanges-legend-newpage' => "([[Special:NewPages|musuq p'anqakunatapas]] qhaway)",
 'rcnotefrom' => "Kay qatiqpiqa '''$2'''-mantapacha ('''$1'''-kama) hukchasqakunatam rikunki.",
 'rclistfrom' => '$1-manta musuq hukchasqakunata rikuchiy',
 'rcshowhideminor' => "$1 uchuylla llamk'apusqakunata",
@@ -2309,7 +2310,7 @@ wiki: $PAGEEDITOR_WIKI
 
 Kay p\'anqata mana musuqmanta watukamuptiykiqa, manam huk ruraykunamanta willasqaykichu. Tukuy watiqasqayki p\'anqakunapaq musyachina sananchakunatapas kutichiytam atinkiman.
 
-             Tukuy sunquwan, {{SITENAME}}pa e-chaski musyachina llikan
+Tukuy sunquwan, {{SITENAME}}pa e-chaski musyachina llikan
 
 --
 E-chaski willaykuy allinkachinakunata hukchanaykipaqqa kay p\'anqatam qhaway:
@@ -2345,7 +2346,7 @@ $2 nisqa p\'anqata qhaway ñaqha qullusqakunata rikunaykipaq.',
 'dellogpage' => 'Qullusqakuna',
 'dellogpagetext' => 'Kay qatiqpiqa lliwmanta aswan ñaqha qullusqakunatam rikunki. Rikuchisqa pachankunaqa sirwiqpa pachanpim.',
 'deletionlog' => 'qullusqakuna',
-'reverted' => 'Ñawpaq hukchasqata kutichiy',
+'reverted' => 'Ñawpaq hukchasqaman kutichisqa',
 'deletecomment' => 'Kayrayku:',
 'deleteotherreason' => 'Huk rayku:',
 'deletereasonotherlist' => 'Huk rayku',
@@ -2643,7 +2644,7 @@ Willariy imaraykum hark'anki (ahinataq: sapaq wandaluchasqa p'anqakunamanta will
 'range_block_disabled' => "Kamachiqpa patayayku hark'ay hayñinman ama nisqam.",
 'ipb_expiry_invalid' => 'Puchukana pachaqa manam allinchu.',
 'ipb_expiry_temp' => "Pakasqa ruraqpa sutin hark'aykunaqa tiyaqllam kachun.",
-'ipb_hide_invalid' => "Manam atinichu kay rakiqunata ñit'ipayta; nisyu llamk'apusqayuqñachá.",
+'ipb_hide_invalid' => "Manam atinichu kay rakiqunata ñit'ipayta; nisyu {{PLURAL:$1|llamk'apusqayuqñachá}}.",
 'ipb_already_blocked' => '"$1" sutiyuqqa hark\'asqañam kachkan.',
 'ipb-needreblock' => "$1 sutiyuqqa hark'asqañam. Allinchanakunata hukchayta munankichu?",
 'ipb-otherblocks-header' => "Huk {{PLURAL:$1|hark'ay|hark'aykuna}}",
@@ -2848,7 +2849,7 @@ Tukuy hawa wikimanta chaskisqakunaqa [[Special:Log/import|hawamanta chaskiy hall
 'importuploaderrorsize' => 'Manam atinichu hawamanta chaskina willañiqita churkuyta, saqillasqamanta aswan hatun kaptinmi.',
 'importuploaderrorpartial' => 'Manam atinichu hawamanta chaskina willañiqita churkuyta, rakillam churkusqa.',
 'importuploaderrortemp' => "Manam atinichu hawamanta chaskina willañiqita churkuyta, mit'alla willañiqi churana mana kaptinmi.",
-'import-parse-failure' => "Manam atinichu XML qillqata t'ikraspa hawamanta chaskiyta",
+'import-parse-failure' => 'Manam atinichu XML qillqata kuskispa hawamanta chaskiyta',
 'import-noarticle' => "Manam hawamanta chaskina p'anqachu!",
 'import-nonewrevisions' => 'Tukuy musuqchasqakunaqa ñawpaqtañam hawamanta chaskisqa.',
 'xml-error-string' => "$1, $2 siq'ipi, $3 tunupi (byte $4): $5",
@@ -3080,7 +3081,7 @@ Payta rurachiyqa antañiqiqniykita llikaykitapas waqllinqachá.",
 'svg-long-desc' => 'SVG willañiqi, rimasqakama $1 × $2 iñuyuq, willañiqip chhikan kaynin: $3',
 'svg-long-desc-animated' => 'Kuyuchisqa SVG willañiqi, rimasqakama $1 × $2 iñuyuq, willañiqip chhikan kaynin: $3',
 'svg-long-error' => 'Mana allin SVG willañiqi: $1',
-'show-big-image' => 'Qallariy huyaku',
+'show-big-image' => 'Qallariy willañiqi',
 'show-big-image-preview' => 'Kay ñawpaq qhawariypa chhikan kaynin: $1.',
 'show-big-image-other' => 'Huk {{PLURAL:$2|huyaku|huyakukuna}}: $1.',
 'show-big-image-size' => '$1 × $2 iñu',
@@ -3958,4 +3959,6 @@ Mana chayqa, kay qatiqpi kaq hunt'ana p'anqatam llamk'achiyta atinki. Willapuyni
 'expand_templates_remove_comments' => 'Willapusqakunata qichuy',
 'expand_templates_preview' => 'Ñawpaqta qhawallay',
 
+# Unknown messages
+'uploadinvalidxml' => 'Manam atinichu churkusqa willañiqipi XML-ta kuskiyta.',
 );
index 54bcbdb..c699dde 100644 (file)
@@ -951,7 +951,7 @@ Ar trebui să faceți acest lucru numai dacă le-ați partajat accidental cu alt
 'summary' => 'Rezumat:',
 'subject' => 'Subiect / titlu:',
 'minoredit' => 'Aceasta este o modificare minoră',
-'watchthis' => 'Monitorizează această pagină',
+'watchthis' => 'Urmărește această pagină',
 'savearticle' => 'Salvare pagină',
 'preview' => 'Previzualizare',
 'showpreview' => 'Previzualizare',
@@ -1134,7 +1134,7 @@ Ea există deja.',
 'invalid-content-data' => 'Date de conținut invalide',
 'content-not-allowed-here' => 'Conținutul de tip „$1” nu este permis pe pagina [[$2]]',
 'editwarning-warning' => 'Părăsind această pagină, există riscul pierderii modificărilor efectuate.
-Dacă sunteți autentificat, puteți dezactiva această avertizare în secțiunea „Modificare” a preferințelor dumneavoastră.',
+Dacă sunteți autentificat, puteți dezactiva această avertizare în secțiunea „{{int:prefs-editing}}” a preferințelor dumneavoastră.',
 'editpage-notsupportedcontentformat-title' => 'Formatul conținutului nu este acceptat',
 'editpage-notsupportedcontentformat-text' => 'Formatul de conținut $1 nu este acceptat de modelul de conținut $2.',
 
@@ -1349,6 +1349,8 @@ Folosirea linkurilor de navigare va reseta această coloană.',
 'showhideselectedversions' => 'Șterge/recuperează versiunile marcate',
 'editundo' => 'anulare',
 'diff-empty' => '(Nicio diferență)',
+'diff-multi-sameuser' => '(Nu {{PLURAL:$1|s-a afișat o versiune intermediară efectuată|s-au afișat $1 versiuni intermediare efectuate|s-au afișat $1 de versiuni intermediare efectuate}} de același utilizator)',
+'diff-multi-otherusers' => '(Nu {{PLURAL:$1|s-a afișat o versiune intermediară efectuată|s-au afișat $1 versiuni intermediare efectuate|s-au afișat $1 de versiuni intermediare efectuate}} de {{PLURAL:$2|un alt utilizator|alți $2 utilizatori|alți $2 de utilizatori}})',
 'diff-multi-manyusers' => '({{PLURAL:$1|O versiune intermediară efectuată de|$1 (de) versiuni intermediare efectuate de peste}} $2 {{PLURAL:$2|utilizator|utilizatori}} {{PLURAL:$1|neafișată|neafișate}})',
 'difference-missing-revision' => '{{PLURAL:$2|O versiune a|$2 versiuni ale|$2 de versiuni ale}} acestei diferențe ($1) nu {{PLURAL:$2|a fost găsită|au fost găsite}}.
 
index dc31f48..45ad9f0 100644 (file)
@@ -1700,13 +1700,29 @@ $1',
 'upload-http-error' => 'ఒక HTTP పొరపాటు జరిగింది: $1',
 
 # File backend
+'backend-fail-stream' => '"$1" ఫైలును స్ట్రీమింగు చెయ్యలేకపోయాం.',
+'backend-fail-backup' => '"$1" ఫైలును బ్యాకప్పు చెయ్యలేకపోయాం.',
 'backend-fail-notexists' => '$1 ఫైలు అసలు లేనేలేదు.',
+'backend-fail-hashes' => 'పోలిక కోసం ఫైలు హాష్‍లను పొందలేకపోయాం.',
+'backend-fail-notsame' => 'సరిగ్గా సరిపోయే ఫైలు కాదు గానీ, ఒక ఫైలు ఈసరికే "$1" వద్ద ఉంది.',
+'backend-fail-invalidpath' => '"$1" సరైన స్టోరేజి పాత్ కాదు',
 'backend-fail-delete' => '$1 ఫైలును తొలగించలేకున్నాం.',
+'backend-fail-describe' => '"$1" ఫైలు మెటాడేటాను మార్చలేకపోయాం.',
 'backend-fail-alreadyexists' => '$1 అనే దస్త్రం ఇప్పటికే ఉంది.',
+'backend-fail-store' => '"$1" ఫైలును "$2" వద్ద భద్రపరచలేకపోయాం.',
+'backend-fail-copy' => '"$1" నుండి "$2" కి ఫైలును కాపీ చెయ్యలేకపోయాం.',
+'backend-fail-move' => '"$1" నుండి "$2" కి ఫైలును తరలించలేకపోయాం.',
 'backend-fail-opentemp' => 'తాత్కాలిక దస్త్రాన్ని తెరవలేకపోతున్నాం.',
+'backend-fail-writetemp' => 'తాత్కాలిక ఫైలులో రాయలేకపోయాం.',
 'backend-fail-closetemp' => 'తాత్కాలిక దస్త్రాన్ని మూసివేయలేకపోయాం.',
 'backend-fail-read' => '$1 దస్త్రము చదువలేకపోతిమి.',
 'backend-fail-create' => '$1 ఫైలులో రాయలేకున్నాం.',
+'backend-fail-maxsize' => '"$1" ఫైలు {{PLURAL:$2|ఒక బైట్|$2 బైట్ల}} కంటే పెద్దది కావడం చేత దాన్ని రాయలేకపోయాం.',
+'backend-fail-readonly' => 'స్టోరేజి బ్యాక్‍ఎండ్ "$1" ప్రస్తుతం రీడ్-ఓన్లీ స్థితిలో ఉంది. దానికి కారణం: "<em>$2</em>"',
+'backend-fail-connect' => 'స్టోరేజీ బ్యాక్‍ఎండ్ "$1" కి కనెక్టు కాలేక పోయాం.',
+'backend-fail-internal' => 'స్టోరేజీ బ్యాక్‍ఎండ్ "$1" లో ఏదో తెలియని లోపం దొర్లింది.',
+'backend-fail-contenttype' => '"$1" లో దాచాల్సిన ఫైలు యొక్క కంటెంటు రకమేంటో నిర్ధారించలేకపోయాం.',
+'backend-fail-batchsize' => 'స్టోరేజీ బ్యాక్‍ఎండ్ కు $1 ఫైలు {{PLURAL:$1|ఆపరేషన్|ఆపరేషన్ల}} తో కూడిన బ్యాచ్ ఒకటి ఇవ్వబడింది; పరిమితి: $2 {{PLURAL:$2|ఆపరేషన్|ఆపరేషన్లు}}.',
 
 # ZipDirectoryReader
 'zip-file-open-error' => 'ఈ ఫైలును ZIP పరీక్ష కోసం తెరవబోతే, ఏదో తెలియని లోపం ఎదురైంది.',
index 128b706..980645f 100644 (file)
@@ -1156,7 +1156,7 @@ Nó đã tồn tại.',
 'invalid-content-data' => 'Dữ liệu nội dung không hợp lệ',
 'content-not-allowed-here' => 'Không cho phép đưa nội dung “$1” vào trang [[$2]]',
 'editwarning-warning' => 'Rời khỏi trang này sẽ khiến bạn mất các sửa đổi đã thực hiện.
-Nếu đã đăng nhập, bạn có thể tắt cảnh báo này tại mục “Sửa đổi” trong tùy chọn cá nhân.',
+Nếu đã đăng nhập, bạn có thể tắt cảnh báo này tại mục “{{int:prefs-editing}}” trong tùy chọn cá nhân.',
 'editpage-notsupportedcontentformat-title' => 'Không hỗ trợ định dạng nội dung',
 'editpage-notsupportedcontentformat-text' => 'Mô hình nội dung $2 không hỗ trợ định dạng nội dung $1.',
 
diff --git a/maintenance/benchmarks/benchmarkParse.php b/maintenance/benchmarks/benchmarkParse.php
new file mode 100644 (file)
index 0000000..cec2beb
--- /dev/null
@@ -0,0 +1,163 @@
+<?php
+/**
+ * Benchmark script for parse operations
+ *
+ * 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
+ * @author Tim Starling <tstarling@wikimedia.org>
+ * @ingroup Benchmark
+ */
+
+require __DIR__ . '/../Maintenance.php';
+
+/**
+ * Maintenance script to benchmark how long it takes to parse a given title at an optionally
+ * specified timestamp
+ *
+ * @since 1.23
+ */
+class BenchmarkParse extends Maintenance {
+       /** @var string MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS) */
+       private $templateTimestamp = null;
+
+       /** @var array Cache that maps a Title DB key to revision ID for the requested timestamp */
+       private $idCache = array();
+
+       function __construct() {
+               parent::__construct();
+               $this->addDescription( 'Benchmark parse operation' );
+               $this->addArg( 'title', 'The name of the page to parse' );
+               $this->addOption( 'cold', 'Don\'t repeat the parse operation to warm the cache' );
+               $this->addOption( 'page-time',
+                       'Use the version of the page which was current at the given time',
+                       false, true );
+               $this->addOption( 'tpl-time',
+                       'Use templates which were current at the given time (except that moves and ' .
+                               'deletes are not handled properly)',
+                       false, true );
+       }
+
+       function execute() {
+               if ( $this->hasOption( 'tpl-time' ) ) {
+                       $this->templateTimestamp = wfTimestamp( TS_MW, strtotime( $this->getOption( 'tpl-time' ) ) );
+                       Hooks::register( 'BeforeParserFetchTemplateAndtitle', array( $this, 'onFetchTemplate' ) );
+               }
+
+               $title = Title::newFromText( $this->getArg() );
+               if ( !$title ) {
+                       $this->error( "Invalid title" );
+                       exit( 1 );
+               }
+
+               if ( $this->hasOption( 'page-time' ) ) {
+                       $pageTimestamp = wfTimestamp( TS_MW, strtotime( $this->getOption( 'page-time' ) ) );
+                       $id = $this->getRevIdForTime( $title, $pageTimestamp );
+                       if ( !$id ) {
+                               $this->error( "The page did not exist at that time" );
+                               exit( 1 );
+                       }
+
+                       $revision = Revision::newFromId( $id );
+               } else {
+                       $revision = Revision::newFromTitle( $title );
+               }
+
+               if ( !$revision ) {
+                       $this->error( "Unable to load revision, incorrect title?" );
+                       exit( 1 );
+               }
+
+               if ( !$this->hasOption( 'cold' ) ) {
+                       $this->runParser( $revision );
+               }
+
+               $startUsage = getrusage();
+               $startTime = microtime( true );
+               $this->runParser( $revision );
+               $endUsage = getrusage();
+               $endTime = microtime( true );
+
+               printf( "CPU time = %.3f s, wall clock time = %.3f s\n",
+                       // CPU time
+                       $endUsage['ru_utime.tv_sec'] + $endUsage['ru_utime.tv_usec'] * 1e-6
+                               - $startUsage['ru_utime.tv_sec'] - $startUsage['ru_utime.tv_usec'] * 1e-6,
+                       // Wall clock time
+                       $endTime - $startTime );
+       }
+
+       /**
+        * Fetch the ID of the revision of a Title that occurred
+        *
+        * @param Title $title
+        * @param string $timestamp
+        * @return bool|string Revision ID, or false if not found or error
+        */
+       function getRevIdForTime( Title $title, $timestamp ) {
+               $dbr = wfGetDB( DB_SLAVE );
+
+               $id = $dbr->selectField(
+                       array( 'revision', 'page' ),
+                       'rev_id',
+                       array(
+                               'page_namespace' => $title->getNamespace(),
+                               'page_title' => $title->getDBkey(),
+                               'rev_timestamp <= ' . $dbr->addQuotes( $timestamp )
+                       ),
+                       __METHOD__,
+                       array( 'ORDER BY' => 'rev_timestamp DESC', 'LIMIT' => 1 ),
+                       array( 'revision' => array( 'INNER JOIN', 'rev_page=page_id' ) )
+               );
+
+               return $id;
+       }
+
+       /**
+        * Parse the text from a given Revision
+        *
+        * @param Revision $revision
+        */
+       function runParser( Revision $revision ) {
+               $content = $revision->getContent();
+               $content->getParserOutput( $revision->getTitle(), $revision->getId() );
+       }
+
+       /**
+        * Hook into the parser's revision ID fetcher. Make sure that the parser only
+        * uses revisions around the specified timestamp.
+        *
+        * @param Parser $parser
+        * @param Title $title
+        * @param bool &$skip
+        * @param string|bool &$id
+        * @return bool
+        */
+       function onFetchTemplate( Parser $parser, Title $title, &$skip, &$id ) {
+               $pdbk = $title->getPrefixedDBkey();
+               if ( !isset( $this->idCache[$pdbk] ) ) {
+                       $proposedId = $this->getRevIdForTime( $title, $this->templateTimestamp );
+                       $this->idCache[$pdbk] = $proposedId;
+               }
+               if ( $this->idCache[$pdbk] !== false ) {
+                       $id = $this->idCache[$pdbk];
+               }
+
+               return true;
+       }
+}
+
+$maintClass = 'BenchmarkParse';
+require RUN_MAINTENANCE_IF_MAIN;
index fd23ea1..f9eb675 100644 (file)
@@ -796,6 +796,7 @@ $wgMessageStructure = array(
                'undo-success',
                'undo-failure',
                'undo-norev',
+               'undo-nochange',
                'undo-summary',
                'undo-summary-username-hidden',
        ),
index 40c6951..c5ade2d 100644 (file)
@@ -34,19 +34,18 @@ class UpdateSpecialPages extends Maintenance {
                parent::__construct();
                $this->addOption( 'list', 'List special page names' );
                $this->addOption( 'only', 'Only update "page"; case sensitive, ' .
-               'check correct case by calling this script with --list or on ' .
-               'includes/QueryPage.php. Ex: --only=BrokenRedirects', false, true );
+                       'check correct case by calling this script with --list or on ' .
+                       'includes/QueryPage.php. Ex: --only=BrokenRedirects', false, true );
                $this->addOption( 'override', 'Also update pages that have updates disabled' );
        }
 
        public function execute() {
                global $IP, $wgQueryPages, $wgQueryCacheLimit, $wgDisableQueryPageUpdate;
 
-               if ( !$this->hasOption( 'list' ) && !$this->hasOption( 'only' ) ) {
-                       $this->doSpecialPageCacheUpdates();
-               }
                $dbw = wfGetDB( DB_MASTER );
 
+               $this->doSpecialPageCacheUpdates( $dbw );
+
                // This is needed to initialise $wgQueryPages
                require_once "$IP/includes/QueryPage.php";
 
@@ -56,12 +55,14 @@ class UpdateSpecialPages extends Maintenance {
 
                        # --list : just show the name of pages
                        if ( $this->hasOption( 'list' ) ) {
-                               $this->output( "$special\n" );
+                               $this->output( "$special [QueryPage]\n" );
                                continue;
                        }
 
-                       if ( !$this->hasOption( 'override' ) && $wgDisableQueryPageUpdate && in_array( $special, $wgDisableQueryPageUpdate ) ) {
-                               $this->output( sprintf( "%-30s disabled\n", $special ) );
+                       if ( !$this->hasOption( 'override' )
+                               && $wgDisableQueryPageUpdate && in_array( $special, $wgDisableQueryPageUpdate ) )
+                       {
+                               $this->output( sprintf( "%-30s [QueryPage] disabled\n", $special ) );
                                continue;
                        }
 
@@ -81,7 +82,7 @@ class UpdateSpecialPages extends Maintenance {
                        }
 
                        if ( !$this->hasOption( 'only' ) || $this->getOption( 'only' ) == $queryPage->getName() ) {
-                               $this->output( sprintf( '%-30s ', $special ) );
+                               $this->output( sprintf( '%-30s [QueryPage] ', $special ) );
                                if ( $queryPage->isExpensive() ) {
                                        $t1 = explode( ' ', microtime() );
                                        # Do the query
@@ -125,32 +126,41 @@ class UpdateSpecialPages extends Maintenance {
                }
        }
 
-       public function doSpecialPageCacheUpdates() {
+       public function doSpecialPageCacheUpdates( $dbw ) {
                global $wgSpecialPageCacheUpdates;
-               $dbw = wfGetDB( DB_MASTER );
 
                foreach ( $wgSpecialPageCacheUpdates as $special => $call ) {
-                       if ( !is_callable( $call ) ) {
-                               $this->error( "Uncallable function $call!" );
+                       # --list : just show the name of pages
+                       if ( $this->hasOption( 'list' ) ) {
+                               $this->output( "$special [callback]\n" );
                                continue;
                        }
-                       $this->output( sprintf( '%-30s ', $special ) );
-                       $t1 = explode( ' ', microtime() );
-                       call_user_func( $call, $dbw );
-                       $t2 = explode( ' ', microtime() );
-                       $elapsed = ( $t2[0] - $t1[0] ) + ( $t2[1] - $t1[1] );
-                       $hours = intval( $elapsed / 3600 );
-                       $minutes = intval( $elapsed % 3600 / 60 );
-                       $seconds = $elapsed - $hours * 3600 - $minutes * 60;
-                       if ( $hours ) {
-                               $this->output( $hours . 'h ' );
-                       }
-                       if ( $minutes ) {
-                               $this->output( $minutes . 'm ' );
+
+                       if ( !$this->hasOption( 'only' ) || $this->getOption( 'only' ) == $special ) {
+                               if ( !is_callable( $call ) ) {
+                                       $this->error( "Uncallable function $call!" );
+                                       continue;
+                               }
+                               $this->output( sprintf( '%-30s [callback] ', $special ) );
+                               $t1 = explode( ' ', microtime() );
+                               call_user_func( $call, $dbw );
+                               $t2 = explode( ' ', microtime() );
+
+                               $this->output( "completed in " );
+                               $elapsed = ( $t2[0] - $t1[0] ) + ( $t2[1] - $t1[1] );
+                               $hours = intval( $elapsed / 3600 );
+                               $minutes = intval( $elapsed % 3600 / 60 );
+                               $seconds = $elapsed - $hours * 3600 - $minutes * 60;
+                               if ( $hours ) {
+                                       $this->output( $hours . 'h ' );
+                               }
+                               if ( $minutes ) {
+                                       $this->output( $minutes . 'm ' );
+                               }
+                               $this->output( sprintf( "%.2fs\n", $seconds ) );
+                               # Wait for the slave to catch up
+                               wfWaitForSlaves();
                        }
-                       $this->output( sprintf( "completed in %.2fs\n", $seconds ) );
-                       # Wait for the slave to catch up
-                       wfWaitForSlaves();
                }
        }
 }
index 553d077..73b5dc4 100644 (file)
@@ -44,7 +44,6 @@
        // Content styling
        text-align: center;
        font-weight: bold;
-       white-space: nowrap;
        text-shadow: 0 1px rgba(0, 0, 0, .1);
 
        // Interaction styling
index 07ea833..a0eb658 100644 (file)
@@ -129,6 +129,7 @@ div#simpleSearch button#searchButton {
        padding-right: 0.4em;
        margin: 0;
        border: none;
+       background-color: transparent;
        background-image: none;
        text-indent: 0;
 
index 184e0ee..c001b47 100644 (file)
@@ -1,41 +1,7 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   version="1.1"
-   width="12"
-   height="13"
-   id="svg2">
-  <defs
-     id="defs4" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     transform="translate(-894,-731.57648)"
-     id="layer1">
-    <path
-       d="m -0.75761414,8.9593897 a 5.177032,5.177032 0 1 1 -10.35406386,0 5.177032,5.177032 0 1 1 10.35406386,0 z"
-       transform="matrix(0.85040896,0,0,0.83575426,904.36465,729.3551)"
-       id="path2996"
-       style="fill:none;stroke:#6c6c6c;stroke-width:2.13510537;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-    <path
-       d="m 902.17653,740.39168 2.26569,2.6414"
-       id="path3766"
-       style="fill:none;stroke:#6c6c6c;stroke-width:2.20000005;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-  </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="13">
+       <g stroke-width="2" stroke="#6c6c6c" fill="none">
+               <path d="m11.29 11.71-4-4"/>
+               <circle cx="5" cy="5" r="4"/>
+       </g>
 </svg>
index 4fd9a47..20d945d 100644 (file)
@@ -1,41 +1,7 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   version="1.1"
-   width="12"
-   height="13"
-   id="svg2">
-  <defs
-     id="defs4" />
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     transform="translate(-894,-731.57648)"
-     id="layer1">
-    <path
-       d="m -0.75761414,8.9593897 a 5.177032,5.177032 0 1 1 -10.35406386,0 5.177032,5.177032 0 1 1 10.35406386,0 z"
-       transform="matrix(-0.85040896,0,0,0.83575426,895.63918,729.3551)"
-       id="path2996"
-       style="fill:none;stroke:#6c6c6c;stroke-width:2.13510537;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-    <path
-       d="m 897.8273,740.39168 -2.26569,2.6414"
-       id="path3766"
-       style="fill:none;stroke:#6c6c6c;stroke-width:2.20000005;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
-  </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="13">
+       <g stroke-width="2" stroke="#6c6c6c" fill="none">
+               <path d="m.71 11.71 4-4"/>
+               <circle cx="7" cy="5" r="4"/>
+       </g>
 </svg>
index 2683a21..5a1fc05 100644 (file)
@@ -2,7 +2,7 @@
 
 div#content {
        margin-left: 11em;
-       padding: 1.5em 1.5em 1.5em 1.75em;
+       padding: 1.25em 1.5em 1.5em 1.5em;
 }
 #p-logo {
        left: @menu-main-logo-left;
index c2d0b92..542ffe7 100644 (file)
@@ -9,7 +9,7 @@
 @content-font-color: black;
 @content-font-size: 0.8em;
 @content-line-height: 1.5em;
-@content-padding: 1.25em 1.5em 1.5em 1.5em;
+@content-padding: 1em;
 @content-heading-font-size: 1.6em;
 @content-heading-font-family: sans-serif;
 @body-background-color: #fff;
index 54e6199..078dfef 100644 (file)
@@ -40,6 +40,21 @@ class TitleTest extends MediaWikiTestCase {
         * @todo This method should be split into 2 separate tests each with a provider
         */
        public function testSecureAndSplit() {
+               $this->setMwGlobals( array(
+                       'wgLocalInterwiki' => 'localtestiw',
+                       'wgHooks' => array(
+                               'InterwikiLoadPrefix' => array(
+                                       function ( $prefix, &$data ) {
+                                               if ( $prefix === 'localtestiw' ) {
+                                                       $data = array( 'iw_url' => 'localtestiw' );
+                                               } elseif ( $prefix === 'remotetestiw' ) {
+                                                       $data = array( 'iw_url' => 'remotetestiw' );
+                                               }
+                                               return false;
+                                       }
+                               )
+                       )
+               ));
                // Valid
                foreach ( array(
                        'Sandbox',
@@ -58,7 +73,17 @@ class TitleTest extends MediaWikiTestCase {
                        'A~~',
                        // Length is 256 total, but only title part matters
                        'Category:' . str_repeat( 'x', 248 ),
-                       str_repeat( 'x', 252 )
+                       str_repeat( 'x', 252 ),
+                       // interwiki prefix
+                       'localtestiw: #anchor',
+                       'localtestiw:foo',
+                       'localtestiw: foo # anchor',
+                       'localtestiw: Talk: Sandbox # anchor',
+                       'remotetestiw:',
+                       'remotetestiw: Talk: # anchor',
+                       'remotetestiw: #bar',
+                       'remotetestiw: Talk:',
+                       'remotetestiw: Talk: Foo'
                ) as $text ) {
                        $this->assertInstanceOf( 'Title', Title::newFromText( $text ), "Valid: $text" );
                }
@@ -106,7 +131,11 @@ class TitleTest extends MediaWikiTestCase {
                        // Namespace prefix without actual title
                        'Talk:',
                        'Category: ',
-                       'Category: #bar'
+                       'Category: #bar',
+                       // interwiki prefix
+                       'localtestiw:',
+                       'localtestiw: Talk: # anchor',
+                       'localtestiw: Talk:'
                ) as $text ) {
                        $this->assertNull( Title::newFromText( $text ), "Invalid: $text" );
                }
index 6659414..15bd8bb 100644 (file)
@@ -116,6 +116,7 @@ class ApiOptionsTest extends MediaWikiLangTestCase {
                        'testmultiselect-opt2' => 'registered-multiselect',
                        'testmultiselect-opt3' => 'registered-multiselect',
                        'testmultiselect-opt4' => 'registered-multiselect',
+                       'special' => 'special',
                );
 
                if ( $options === null ) {
@@ -389,6 +390,29 @@ class ApiOptionsTest extends MediaWikiLangTestCase {
                $this->assertEquals( self::$Success, $response );
        }
 
+       public function testSpecialOption() {
+               $this->mUserMock->expects( $this->never() )
+                       ->method( 'resetOptions' );
+
+               $this->mUserMock->expects( $this->never() )
+                       ->method( 'saveSettings' );
+
+               $request = $this->getSampleRequest( array(
+                       'change' => 'special=1'
+               ) );
+
+               $response = $this->executeQuery( $request );
+
+               $this->assertEquals( array(
+                       'options' => 'success',
+                       'warnings' => array(
+                               'options' => array(
+                                       '*' => "Validation error for 'special': cannot be set by this module"
+                               )
+                       )
+               ), $response );
+       }
+
        public function testUnknownOption() {
                $this->mUserMock->expects( $this->never() )
                        ->method( 'resetOptions' );