Merge "Define $wgAlwaysUseTidy to false where needed in unit tests"
authorNikerabbit <niklas.laxstrom@gmail.com>
Fri, 9 Nov 2012 17:55:23 +0000 (17:55 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 9 Nov 2012 17:55:23 +0000 (17:55 +0000)
56 files changed:
RELEASE-NOTES-1.21
img_auth.php
includes/Category.php
includes/CategoryViewer.php
includes/Categoryfinder.php
includes/DefaultSettings.php
includes/RecentChange.php
includes/Revision.php
includes/api/ApiQueryRevisions.php
includes/filebackend/FSFileBackend.php
includes/filebackend/FileBackend.php
includes/filebackend/FileBackendMultiWrite.php
includes/filebackend/FileBackendStore.php
includes/filebackend/SwiftFileBackend.php
includes/filebackend/filejournal/DBFileJournal.php
includes/filerepo/FileRepo.php
includes/filerepo/file/File.php
includes/installer/Installer.i18n.php
includes/job/JobQueue.php
includes/job/JobQueueDB.php
includes/job/JobQueueGroup.php
includes/objectcache/MemcachedPeclBagOStuff.php
includes/specials/SpecialMovepage.php
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRecentchangeslinked.php
includes/specials/SpecialWatchlist.php
languages/messages/MessagesAz.php
languages/messages/MessagesBcl.php
languages/messages/MessagesCa.php
languages/messages/MessagesDiq.php
languages/messages/MessagesEn.php
languages/messages/MessagesFa.php
languages/messages/MessagesKo.php
languages/messages/MessagesKu_latn.php
languages/messages/MessagesNn.php
languages/messages/MessagesPms.php
languages/messages/MessagesPt.php
languages/messages/MessagesPt_br.php
languages/messages/MessagesQqq.php
languages/messages/MessagesRo.php
languages/messages/MessagesRu.php
languages/messages/MessagesSk.php
languages/messages/MessagesSv.php
languages/messages/MessagesTr.php
languages/messages/MessagesVi.php
languages/messages/MessagesYi.php
maintenance/nextJobDB.php
resources/jquery/jquery.badge.js
skins/Vector.php
tests/TestsAutoLoader.php
tests/parser/parserTests.txt
tests/phpunit/includes/TitleTest.php
tests/phpunit/includes/UserTest.php
tests/phpunit/includes/db/ORMTableTest.php [new file with mode: 0644]
tests/phpunit/includes/filebackend/FileBackendTest.php
thumb.php

index 4be260e..b9b026c 100644 (file)
@@ -60,6 +60,7 @@ production.
 * (bug 26995) Update filename field on Upload page after having sanitized it.
 * (bug 41793) Contribution links to users with 0 edits on Special:ListUsers didn't
   show up red.
+* (bug 41899) A PHP notice no longer occurs when using the "rvcontinue" API parameter.
 
 === API changes in 1.21 ===
 * prop=revisions can now report the contentmodel and contentformat, see docs/contenthandler.txt
index b3a3495..b04974b 100644 (file)
@@ -48,7 +48,7 @@ if ( isset( $_SERVER['MW_COMPILED'] ) ) {
 wfProfileIn( 'img_auth.php' );
 
 # Set action base paths so that WebRequest::getPathInfo()
-# recognizes the "X" as the 'title' in ../image_auth/X urls.
+# recognizes the "X" as the 'title' in ../img_auth.php/X urls.
 $wgArticlePath = false; # Don't let a "/*" article path clober our action path
 $wgActionPaths = array( "$wgUploadPath/" );
 
index ffd7bb8..6a5eac7 100644 (file)
@@ -58,6 +58,9 @@ class Category {
                        # Already initialized
                        return true;
                }
+
+               wfProfileIn( __METHOD__ );
+
                $dbr = wfGetDB( DB_SLAVE );
                $row = $dbr->selectRow(
                        'category',
@@ -66,6 +69,8 @@ class Category {
                        __METHOD__
                );
 
+               wfProfileOut( __METHOD__ );
+
                if ( !$row ) {
                        # Okay, there were no contents.  Nothing to initialize.
                        if ( $this->mTitle ) {
@@ -190,25 +195,37 @@ class Category {
        }
 
        /** @return mixed DB key name, or false on failure */
-       public function getName() { return $this->getX( 'mName' ); }
+       public function getName() {
+               return $this->getX( 'mName' );
+       }
 
        /** @return mixed Category ID, or false on failure */
-       public function getID() { return $this->getX( 'mID' ); }
+       public function getID() {
+               return $this->getX( 'mID' );
+       }
 
        /** @return mixed Total number of member pages, or false on failure */
-       public function getPageCount() { return $this->getX( 'mPages' ); }
+       public function getPageCount() {
+               return $this->getX( 'mPages' );
+       }
 
        /** @return mixed Number of subcategories, or false on failure */
-       public function getSubcatCount() { return $this->getX( 'mSubcats' ); }
+       public function getSubcatCount() {
+               return $this->getX( 'mSubcats' );
+       }
 
        /** @return mixed Number of member files, or false on failure */
-       public function getFileCount() { return $this->getX( 'mFiles' ); }
+       public function getFileCount() {
+               return $this->getX( 'mFiles' );
+       }
 
        /**
         * @return Title|bool Title for this category, or false on failure.
         */
        public function getTitle() {
-               if ( $this->mTitle ) return $this->mTitle;
+               if ( $this->mTitle ) {
+                       return $this->mTitle;
+               }
 
                if ( !$this->initialize() ) {
                        return false;
@@ -226,20 +243,22 @@ class Category {
         * @return TitleArray object for category members.
         */
        public function getMembers( $limit = false, $offset = '' ) {
+               wfProfileIn( __METHOD__ );
+
                $dbr = wfGetDB( DB_SLAVE );
 
                $conds = array( 'cl_to' => $this->getName(), 'cl_from = page_id' );
                $options = array( 'ORDER BY' => 'cl_sortkey' );
 
                if ( $limit ) {
-                       $options[ 'LIMIT' ] = $limit;
+                       $options['LIMIT'] = $limit;
                }
 
                if ( $offset !== '' ) {
                        $conds[] = 'cl_sortkey > ' . $dbr->addQuotes( $offset );
                }
 
-               return TitleArray::newFromResult(
+               $result = TitleArray::newFromResult(
                        $dbr->select(
                                array( 'page', 'categorylinks' ),
                                array( 'page_id', 'page_namespace', 'page_title', 'page_len',
@@ -249,6 +268,10 @@ class Category {
                                $options
                        )
                );
+
+               wfProfileOut( __METHOD__ );
+
+               return $result;
        }
 
        /**
@@ -259,7 +282,7 @@ class Category {
                if ( !$this->initialize() ) {
                        return false;
                }
-               return $this-> { $key } ;
+               return $this->{$key};
        }
 
        /**
@@ -279,8 +302,10 @@ class Category {
                        }
                }
 
+               wfProfileIn( __METHOD__ );
+
                $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__  );
+               $dbw->begin( __METHOD__ );
 
                # Insert the row if it doesn't exist yet (e.g., this is being run via
                # update.php from a pre-1.16 schema).  TODO: This will cause lots and
@@ -303,8 +328,8 @@ class Category {
                $result = $dbw->selectRow(
                        array( 'categorylinks', 'page' ),
                        array( 'pages' => 'COUNT(*)',
-                                  'subcats' => "COUNT($cond1)",
-                                  'files' => "COUNT($cond2)"
+                               'subcats' => "COUNT($cond1)",
+                               'files' => "COUNT($cond2)"
                        ),
                        array( 'cl_to' => $this->mName, 'page_id = cl_from' ),
                        __METHOD__,
@@ -322,6 +347,8 @@ class Category {
                );
                $dbw->commit( __METHOD__ );
 
+               wfProfileOut( __METHOD__ );
+
                # Now we should update our local counts.
                $this->mPages   = $result->pages;
                $this->mSubcats = $result->subcats;
index 3d66b74..b59b4e1 100644 (file)
@@ -173,7 +173,7 @@ class CategoryViewer extends ContextSource {
 
        /**
         * Add a subcategory to the internal lists, using a title object
-        * @deprecated since 1.17 kept for compatibility, please use addSubcategoryObject instead
+        * @deprecated since 1.17 kept for compatibility, use addSubcategoryObject instead
         */
        function addSubcategory( Title $title, $sortkey, $pageLength ) {
                wfDeprecated( __METHOD__, '1.17' );
@@ -181,14 +181,14 @@ class CategoryViewer extends ContextSource {
        }
 
        /**
-       * Get the character to be used for sorting subcategories.
-       * If there's a link from Category:A to Category:B, the sortkey of the resulting
-       * entry in the categorylinks table is Category:A, not A, which it SHOULD be.
-       * Workaround: If sortkey == "Category:".$title, than use $title for sorting,
-       * else use sortkey...
-       *
-       * @param Title $title
-       * @param string $sortkey The human-readable sortkey (before transforming to icu or whatever).
+        * Get the character to be used for sorting subcategories.
+        * If there's a link from Category:A to Category:B, the sortkey of the resulting
+        * entry in the categorylinks table is Category:A, not A, which it SHOULD be.
+        * Workaround: If sortkey == "Category:".$title, than use $title for sorting,
+        * else use sortkey...
+        *
+        * @param Title $title
+        * @param string $sortkey The human-readable sortkey (before transforming to icu or whatever).
         * @return string
         */
        function getSubcategorySortChar( $title, $sortkey ) {
@@ -259,15 +259,15 @@ class CategoryViewer extends ContextSource {
 
        function finaliseCategoryState() {
                if ( $this->flip['subcat'] ) {
-                       $this->children            = array_reverse( $this->children );
+                       $this->children = array_reverse( $this->children );
                        $this->children_start_char = array_reverse( $this->children_start_char );
                }
                if ( $this->flip['page'] ) {
-                       $this->articles            = array_reverse( $this->articles );
+                       $this->articles = array_reverse( $this->articles );
                        $this->articles_start_char = array_reverse( $this->articles_start_char );
                }
                if ( !$this->showGallery && $this->flip['file'] ) {
-                       $this->imgsNoGallery            = array_reverse( $this->imgsNoGallery );
+                       $this->imgsNoGallery = array_reverse( $this->imgsNoGallery );
                        $this->imgsNoGallery_start_char = array_reverse( $this->imgsNoGallery_start_char );
                }
        }
@@ -302,7 +302,7 @@ class CategoryViewer extends ContextSource {
                                        'page_is_redirect', 'cl_sortkey', 'cat_id', 'cat_title',
                                        'cat_subcats', 'cat_pages', 'cat_files',
                                        'cl_sortkey_prefix', 'cl_collation' ),
-                               array_merge( array( 'cl_to' => $this->title->getDBkey() ),  $extraConds ),
+                               array_merge( array( 'cl_to' => $this->title->getDBkey() ), $extraConds ),
                                __METHOD__,
                                array(
                                        'USE INDEX' => array( 'categorylinks' => 'cl_sortkey' ),
@@ -310,7 +310,7 @@ class CategoryViewer extends ContextSource {
                                        'ORDER BY' => $this->flip[$type] ? 'cl_sortkey DESC' : 'cl_sortkey',
                                ),
                                array(
-                                       'categorylinks'  => array( 'INNER JOIN', 'cl_from = page_id' ),
+                                       'categorylinks' => array( 'INNER JOIN', 'cl_from = page_id' ),
                                        'category' => array( 'LEFT JOIN', 'cat_title = page_title AND page_namespace = ' . NS_CATEGORY )
                                )
                        );
@@ -469,7 +469,7 @@ class CategoryViewer extends ContextSource {
         */
        function formatList( $articles, $articles_start_char, $cutoff = 6 ) {
                $list = '';
-               if ( count ( $articles ) > $cutoff ) {
+               if ( count( $articles ) > $cutoff ) {
                        $list = self::columnList( $articles, $articles_start_char );
                } elseif ( count( $articles ) > 0 ) {
                        // for short lists of articles in categories.
@@ -478,7 +478,7 @@ class CategoryViewer extends ContextSource {
 
                $pageLang = $this->title->getPageLanguage();
                $attribs = array( 'lang' => $pageLang->getCode(), 'dir' => $pageLang->getDir(),
-                       'class' => 'mw-content-'.$pageLang->getDir() );
+                       'class' => 'mw-content-' . $pageLang->getDir() );
                $list = Html::rawElement( 'div', $attribs, $list );
 
                return $list;
@@ -604,7 +604,7 @@ class CategoryViewer extends ContextSource {
                        );
                }
 
-               return $this->msg('categoryviewer-pagedlinks')->rawParams($prevLink, $nextLink)->escaped();
+               return $this->msg( 'categoryviewer-pagedlinks' )->rawParams( $prevLink, $nextLink )->escaped();
        }
 
        /**
@@ -635,6 +635,7 @@ class CategoryViewer extends ContextSource {
                return Title::makeTitle( $title->getNamespace(),
                        $title->getDBkey(), $fragment );
        }
+
        /**
         * What to do if the category table conflicts with the number of results
         * returned?  This function says what. Each type is considered independently
@@ -676,8 +677,9 @@ class CategoryViewer extends ContextSource {
                        $fromOrUntil = true;
                }
 
-               if ( $dbcnt == $rescnt || ( ( $rescnt == $this->limit || $fromOrUntil )
-                       && $dbcnt > $rescnt ) ) {
+               if ( $dbcnt == $rescnt ||
+                       ( ( $rescnt == $this->limit || $fromOrUntil ) && $dbcnt > $rescnt )
+               ) {
                        # Case 1: seems sane.
                        $totalcnt = $dbcnt;
                } elseif ( $rescnt < $this->limit && !$fromOrUntil ) {
index e2b6a0c..589950f 100644 (file)
  *
  * Example use :
  * <code>
- *     # Determines whether the article with the page_id 12345 is in both
- *     # "Category 1" and "Category 2" or their subcategories, respectively
+ *     # Determines whether the article with the page_id 12345 is in both
+ *     # "Category 1" and "Category 2" or their subcategories, respectively
  *
- *     $cf = new Categoryfinder;
- *     $cf->seed(
- *             array( 12345 ),
- *             array( 'Category 1', 'Category 2' ),
- *             'AND'
- *     );
- *     $a = $cf->run();
- *     print implode( ',' , $a );
+ *     $cf = new Categoryfinder;
+ *     $cf->seed(
+ *         array( 12345 ),
+ *         array( 'Category 1', 'Category 2' ),
+ *         'AND'
+ *     );
+ *     $a = $cf->run();
+ *     print implode( ',' , $a );
  * </code>
  *
  */
@@ -135,7 +135,7 @@ class Categoryfinder {
 
                # iterate through the parents
                foreach ( $this->parents[$id] as $p ) {
-                       $pname = $p->cl_to ;
+                       $pname = $p->cl_to;
 
                        # Is this a condition?
                        if ( isset( $conds[$pname] ) ) {
@@ -172,6 +172,8 @@ class Categoryfinder {
         * Scans a "parent layer" of the articles/categories in $this->next
         */
        function scan_next_layer() {
+               wfProfileIn( __METHOD__ );
+
                # Find all parents of the article currently in $this->next
                $layer = array();
                $res = $this->dbr->select(
@@ -225,6 +227,7 @@ class Categoryfinder {
                foreach ( $layer as $v ) {
                        $this->deadend[$v] = $v;
                }
-       }
 
+               wfProfileOut( __METHOD__ );
+       }
 }
index 16cae7d..494b2a3 100644 (file)
@@ -361,7 +361,9 @@ $wgImgAuthPublicTest = true;
  *                          container  : backend container name the zone is in
  *                          directory  : root path within container for the zone
  *                          url        : base URL to the root of the zone
- *                          handlerUrl : base script handled URL to the root of the zone
+ *                          urlsByExt  : map of file extension types to base URLs
+ *                                       (useful for using a different cache for videos)
+ *                          handlerUrl : base script-handled URL to the root of the zone
  *                                       (see FileRepo::getZoneHandlerUrl() function)
  *                      Zones default to using "<repo name>-<zone name>" as the container name
  *                      and default to using the container root as the zone's root directory.
@@ -2779,6 +2781,13 @@ $wgFooterIcons = array(
  */
 $wgUseCombinedLoginLink = false;
 
+/**
+ *  Appearance of user page and talk page labels in personal tools.
+ *  - true = combine links into a single label
+ *  - false = keep links in separate labels
+ */
+$wgVectorCombineUserTalk = false;
+
 /**
  * Search form look for Vector skin only.
  *  - true = use an icon search button
index 3cf6c72..2bdcab4 100644 (file)
@@ -125,7 +125,7 @@ class RecentChange {
         */
        public static function newFromConds( $conds, $fname = __METHOD__ ) {
                $dbr = wfGetDB( DB_SLAVE );
-               $row = $dbr->selectRow( 'recentchanges', '*', $conds, $fname );
+               $row = $dbr->selectRow( 'recentchanges', self::selectFields(), $conds, $fname );
                if ( $row !== false ) {
                        return self::newFromRow( $row );
                } else {
@@ -133,6 +133,40 @@ class RecentChange {
                }
        }
 
+       /**
+        * Return the list of recentchanges fields that should be selected to create
+        * a new recentchanges object.
+        * @return array
+        */
+       public static function selectFields() {
+               return array(
+                       'rc_id',
+                       'rc_timestamp',
+                       'rc_cur_time',
+                       'rc_user',
+                       'rc_user_text',
+                       'rc_namespace',
+                       'rc_title',
+                       'rc_comment',
+                       'rc_minor',
+                       'rc_bot',
+                       'rc_new',
+                       'rc_cur_id',
+                       'rc_this_oldid',
+                       'rc_last_oldid',
+                       'rc_type',
+                       'rc_patrolled',
+                       'rc_ip',
+                       'rc_old_len',
+                       'rc_new_len',
+                       'rc_deleted',
+                       'rc_logid',
+                       'rc_log_type',
+                       'rc_log_action',
+                       'rc_params',
+               );
+       }
+
        # Accessors
 
        /**
index d556edc..3de8303 100644 (file)
@@ -124,7 +124,7 @@ class Revision implements IDBAccessObject {
         * Returns null if no such revision can be found.
         *
         * $flags include:
-        *      Revision::READ_LATEST  : Select the data from the master
+        *      Revision::READ_LATEST  : Select the data from the master (since 1.20)
         *      Revision::READ_LOCKING : Select & lock the data from the master
         *
         * @param $revId Integer
index 5c3b33b..ad40a64 100644 (file)
@@ -261,7 +261,7 @@ class ApiQueryRevisions extends ApiQueryBase {
                        // rvstart and rvstartid when that is supplied.
                        if ( !is_null( $params['continue'] ) ) {
                                $params['startid'] = $params['continue'];
-                               unset( $params['start'] );
+                               $params['start'] = null;
                        }
 
                        // This code makes an assumption that sorting by rev_id and rev_timestamp produces
index 04cf29e..3a03d4d 100644 (file)
@@ -190,16 +190,9 @@ class FSFileBackend extends FileBackendStore {
                        return $status;
                }
 
-               if ( file_exists( $dest ) ) {
-                       $ok = unlink( $dest );
-                       if ( !$ok ) {
-                               $status->fatal( 'backend-fail-delete', $params['dst'] );
-                               return $status;
-                       }
-               }
-
                if ( !empty( $params['async'] ) ) { // deferred
-                       $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
+                       $cmd = implode( ' ', array(
+                               wfIsWindows() ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
                                wfEscapeShellArg( $this->cleanPathSlashes( $params['src'] ) ),
                                wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
                        ) );
@@ -257,16 +250,9 @@ class FSFileBackend extends FileBackendStore {
                        return $status; // do nothing; either OK or bad status
                }
 
-               if ( file_exists( $dest ) ) {
-                       $ok = unlink( $dest );
-                       if ( !$ok ) {
-                               $status->fatal( 'backend-fail-delete', $params['dst'] );
-                               return $status;
-                       }
-               }
-
                if ( !empty( $params['async'] ) ) { // deferred
-                       $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
+                       $cmd = implode( ' ', array(
+                               wfIsWindows() ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
                                wfEscapeShellArg( $this->cleanPathSlashes( $source ) ),
                                wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
                        ) );
@@ -324,19 +310,9 @@ class FSFileBackend extends FileBackendStore {
                        return $status; // do nothing; either OK or bad status
                }
 
-               if ( file_exists( $dest ) ) {
-                       // Windows does not support moving over existing files
-                       if ( wfIsWindows() ) {
-                               $ok = unlink( $dest );
-                               if ( !$ok ) {
-                                       $status->fatal( 'backend-fail-delete', $params['dst'] );
-                                       return $status;
-                               }
-                       }
-               }
-
                if ( !empty( $params['async'] ) ) { // deferred
-                       $cmd = implode( ' ', array( wfIsWindows() ? 'MOVE' : 'mv',
+                       $cmd = implode( ' ', array(
+                               wfIsWindows() ? 'MOVE /Y' : 'mv', // (overwrite)
                                wfEscapeShellArg( $this->cleanPathSlashes( $source ) ),
                                wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
                        ) );
@@ -384,7 +360,8 @@ class FSFileBackend extends FileBackendStore {
                }
 
                if ( !empty( $params['async'] ) ) { // deferred
-                       $cmd = implode( ' ', array( wfIsWindows() ? 'DEL' : 'unlink',
+                       $cmd = implode( ' ', array(
+                               wfIsWindows() ? 'DEL' : 'unlink',
                                wfEscapeShellArg( $this->cleanPathSlashes( $source ) )
                        ) );
                        $status->value = new FSFileOpHandle( $this, $params, 'Copy', $cmd );
@@ -422,14 +399,6 @@ class FSFileBackend extends FileBackendStore {
                        return $status;
                }
 
-               if ( file_exists( $dest ) ) {
-                       $ok = unlink( $dest );
-                       if ( !$ok ) {
-                               $status->fatal( 'backend-fail-delete', $params['dst'] );
-                               return $status;
-                       }
-               }
-
                if ( !empty( $params['async'] ) ) { // deferred
                        $tempFile = TempFSFile::factory( 'create_', 'tmp' );
                        if ( !$tempFile ) {
@@ -441,7 +410,8 @@ class FSFileBackend extends FileBackendStore {
                                $status->fatal( 'backend-fail-create', $params['dst'] );
                                return $status;
                        }
-                       $cmd = implode( ' ', array( wfIsWindows() ? 'COPY' : 'cp',
+                       $cmd = implode( ' ', array(
+                               wfIsWindows() ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
                                wfEscapeShellArg( $this->cleanPathSlashes( $tempFile->getPath() ) ),
                                wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
                        ) );
index e01bfc2..b5e2315 100644 (file)
@@ -904,6 +904,24 @@ abstract class FileBackend {
         */
        abstract public function getLocalCopyMulti( array $params );
 
+       /**
+        * Return an HTTP URL to a given file that requires no authentication to use.
+        * The URL may be pre-authenticated (via some token in the URL) and temporary.
+        * This will return null if the backend cannot make an HTTP URL for the file.
+        *
+        * This is useful for key/value stores when using scripts that seek around
+        * large files and those scripts (and the backend) support HTTP Range headers.
+        * Otherwise, one would need to use getLocalReference(), which involves loading
+        * the entire file on to local disk.
+        *
+        * @param $params Array
+        * $params include:
+        *   - src : source storage path
+        * @return string|null
+        * @since 1.21
+        */
+       abstract public function getFileHttpUrl( array $params );
+
        /**
         * Check if a directory exists at a given storage path.
         * Backends using key/value stores will check if the path is a
index 90292ee..a443a3a 100644 (file)
@@ -651,6 +651,15 @@ class FileBackendMultiWrite extends FileBackend {
                return $tempFiles;
        }
 
+       /**
+        * @see FileBackend::getFileHttpUrl()
+        * @return string|null
+        */
+       public function getFileHttpUrl( array $params ) {
+               $realParams = $this->substOpPaths( $params, $this->backends[$this->masterIndex] );
+               return $this->backends[$this->masterIndex]->getFileHttpUrl( $realParams );
+       }
+
        /**
         * @see FileBackend::directoryExists()
         * @param $params array
index 7e91949..97e49a5 100644 (file)
@@ -804,6 +804,14 @@ abstract class FileBackendStore extends FileBackend {
         */
        abstract protected function doGetLocalCopyMulti( array $params );
 
+       /**
+        * @see FileBackend::getFileHttpUrl()
+        * @return string|null
+        */
+       public function getFileHttpUrl( array $params ) {
+               return null; // not supported
+       }
+
        /**
         * @see FileBackend::streamFile()
         * @return Status
index 8441f8f..48db9d3 100644 (file)
@@ -40,6 +40,7 @@ class SwiftFileBackend extends FileBackendStore {
        /** @var CF_Authentication */
        protected $auth; // Swift authentication handler
        protected $authTTL; // integer seconds
+       protected $swiftTempUrlKey; // string; shared secret value for making temp urls
        protected $swiftAnonUser; // string; username to handle unauthenticated requests
        protected $swiftUseCDN; // boolean; whether CloudFiles CDN is enabled
        protected $swiftCDNExpiry; // integer; how long to cache things in the CDN
@@ -66,6 +67,8 @@ class SwiftFileBackend extends FileBackendStore {
         *   - swiftUser          : Swift user used by MediaWiki (account:username)
         *   - swiftKey           : Swift authentication key for the above user
         *   - swiftAuthTTL       : Swift authentication TTL (seconds)
+        *   - swiftTempUrlKey    : Swift "X-Account-Meta-Temp-URL-Key" value on the account.
+        *                          Do not set this until it has been set in the backend.
         *   - swiftAnonUser      : Swift user used for end-user requests (account:username).
         *                          If set, then views of public containers are assumed to go
         *                          through this user. If not set, then public containers are
@@ -104,6 +107,9 @@ class SwiftFileBackend extends FileBackendStore {
                $this->swiftAnonUser = isset( $config['swiftAnonUser'] )
                        ? $config['swiftAnonUser']
                        : '';
+               $this->swiftTempUrlKey = isset( $config['swiftTempUrlKey'] )
+                       ? $config['swiftTempUrlKey']
+                       : '';
                $this->shardViaHashLevels = isset( $config['shardViaHashLevels'] )
                        ? $config['shardViaHashLevels']
                        : '';
@@ -1119,6 +1125,28 @@ class SwiftFileBackend extends FileBackendStore {
                return $tmpFiles;
        }
 
+       /**
+        * @see FileBackendStore::getFileHttpUrl()
+        * @return string|null
+        */
+       public function getFileHttpUrl( array $params ) {
+               if ( $this->swiftTempUrlKey != '' ) { // temp urls enabled
+                       list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
+                       if ( $srcRel === null ) {
+                               return null; // invalid path
+                       }
+                       try {
+                               $sContObj = $this->getContainer( $srcCont );
+                               $obj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
+                               return $obj->get_temp_url( $this->swiftTempUrlKey, 86400, "GET" );
+                       } catch ( NoSuchContainerException $e ) {
+                       } catch ( CloudFilesException $e ) { // some other exception?
+                               $this->handleException( $e, null, __METHOD__, $params );
+                       }
+               }
+               return null;
+       }
+
        /**
         * @see FileBackendStore::directoriesAreVirtual()
         * @return bool
index 34f3e53..44c6567 100644 (file)
@@ -75,6 +75,9 @@ class DBFileJournal extends FileJournal {
 
                try {
                        $dbw->insert( 'filejournal', $data, __METHOD__ );
+                       if ( mt_rand( 0, 99 ) == 0 ) {
+                               $this->purgeOldLogs(); // occasionally delete old logs
+                       }
                } catch ( DBError $e ) {
                        $status->fatal( 'filejournal-fail-dbquery', $this->backend );
                        return $status;
index 6cd93cc..651ee27 100644 (file)
@@ -127,6 +127,9 @@ class FileRepo {
                        if ( !isset( $this->zones[$zone]['directory'] ) ) {
                                $this->zones[$zone]['directory'] = '';
                        }
+                       if ( !isset( $this->zones[$zone]['urlsByExt'] ) ) {
+                               $this->zones[$zone]['urlsByExt'] = array();
+                       }
                }
        }
 
@@ -196,14 +199,17 @@ class FileRepo {
        /**
         * Get the URL corresponding to one of the four basic zones
         *
-        * @param $zone String: one of: public, deleted, temp, thumb
+        * @param $zone String One of: public, deleted, temp, thumb
+        * @param $ext String|null Optional file extension
         * @return String or false
         */
-       public function getZoneUrl( $zone ) {
-               if ( isset( $this->zones[$zone]['url'] )
-                       && in_array( $zone, array( 'public', 'temp', 'thumb' ) ) )
-               {
-                       return $this->zones[$zone]['url']; // custom URL
+       public function getZoneUrl( $zone, $ext = null ) {
+               if ( in_array( $zone, array( 'public', 'temp', 'thumb' ) ) ) { // standard public zones
+                       if ( $ext !== null && isset( $this->zones[$zone]['urlsByExt'][$ext] ) ) {
+                               return $this->zones[$zone]['urlsByExt'][$ext]; // custom URL for extension/zone
+                       } elseif ( isset( $this->zones[$zone]['url'] ) ) {
+                               return $this->zones[$zone]['url']; // custom URL for zone
+                       }
                }
                switch ( $zone ) {
                        case 'public':
index 557609d..9a080ae 100644 (file)
@@ -316,7 +316,8 @@ abstract class File {
        public function getUrl() {
                if ( !isset( $this->url ) ) {
                        $this->assertRepoDefined();
-                       $this->url = $this->repo->getZoneUrl( 'public' ) . '/' . $this->getUrlRel();
+                       $ext = $this->getExtension();
+                       $this->url = $this->repo->getZoneUrl( 'public', $ext ) . '/' . $this->getUrlRel();
                }
                return $this->url;
        }
@@ -1253,7 +1254,8 @@ abstract class File {
         */
        function getArchiveUrl( $suffix = false ) {
                $this->assertRepoDefined();
-               $path = $this->repo->getZoneUrl( 'public' ) . '/archive/' . $this->getHashPath();
+               $ext = $this->getExtension();
+               $path = $this->repo->getZoneUrl( 'public', $ext ) . '/archive/' . $this->getHashPath();
                if ( $suffix === false ) {
                        $path = substr( $path, 0, -1 );
                } else {
@@ -1272,7 +1274,8 @@ abstract class File {
         */
        function getArchiveThumbUrl( $archiveName, $suffix = false ) {
                $this->assertRepoDefined();
-               $path = $this->repo->getZoneUrl( 'thumb' ) . '/archive/' .
+               $ext = $this->getExtension();
+               $path = $this->repo->getZoneUrl( 'thumb', $ext ) . '/archive/' .
                        $this->getHashPath() . rawurlencode( $archiveName ) . "/";
                if ( $suffix === false ) {
                        $path = substr( $path, 0, -1 );
@@ -1291,7 +1294,8 @@ abstract class File {
         */
        function getThumbUrl( $suffix = false ) {
                $this->assertRepoDefined();
-               $path = $this->repo->getZoneUrl( 'thumb' ) . '/' . $this->getUrlRel();
+               $ext = $this->getExtension();
+               $path = $this->repo->getZoneUrl( 'thumb', $ext ) . '/' . $this->getUrlRel();
                if ( $suffix !== false ) {
                        $path .= '/' . rawurlencode( $suffix );
                }
index 2c729ba..32056e3 100644 (file)
@@ -9471,6 +9471,7 @@ $messages['is'] = array(
  * @author Beta16
  * @author Darth Kule
  * @author F. Cosoleto
+ * @author Gianfranco
  * @author Karika
  */
 $messages['it'] = array(
@@ -9564,10 +9565,14 @@ Installazione interrotta.",
        'config-using-server' => 'Nome server in uso "<nowiki>$1</nowiki>".',
        'config-using-uri' => 'URL del server in uso "<nowiki>$1$2</nowiki>".',
        'config-db-type' => 'Tipo di database:',
+       'config-db-wiki-settings' => 'Identifica questo wiki',
        'config-db-name' => 'Nome del database:',
+       'config-db-name-oracle' => 'Schema del database:',
+       'config-db-username' => 'Nome utente del database:',
        'config-db-password-empty' => 'Inserire una password per il nuovo utente del database: $1.
 Anche se può essere possibile creare utenti senza password, questo non è sicuro.',
        'config-db-install-help' => "Inserire il nome utente e la password che verranno usate per la connessione al database durante il processo d'installazione.",
+       'config-db-prefix' => 'Prefisso tabella del database:',
        'config-db-charset' => 'Set di caratteri del database',
        'config-charset-mysql5' => 'MySQL 4.1/5.0 UTF-8',
        'config-charset-mysql4' => 'MySQL 4.0 con compatibilità UTF-8',
@@ -9585,15 +9590,18 @@ Da cambiare solamente se si è sicuri di averne bisogno.',
        'config-header-oracle' => 'Impostazioni Oracle',
        'config-header-ibm_db2' => 'Impostazioni IBM DB2',
        'config-invalid-db-type' => 'Tipo di database non valido',
+       'config-db-web-account' => "Account del database per l'accesso web",
        'config-db-web-create' => "Crea l'account se non esiste già",
        'config-mysql-engine' => 'Storage engine:',
        'config-mysql-innodb' => 'InnoDB',
        'config-mysql-myisam' => 'MyISAM',
        'config-mysql-charset' => 'Set di caratteri del database:',
+       'config-mysql-binary' => 'Binario',
        'config-mysql-utf8' => 'UTF-8',
        'config-ibm_db2-low-db-pagesize' => "Il database DB2 in uso ha una tablespace predefinita con un insufficiente pagesize, che dovrebbe essere '''32K''' o maggiore.",
        'config-ns-generic' => 'Progetto',
        'config-ns-site-name' => 'Stesso nome wiki: $1',
+       'config-ns-other-default' => 'MyWiki',
        'config-admin-box' => 'Account amministratore',
        'config-admin-name' => 'Tuo nome:',
        'config-admin-password' => 'Password:',
@@ -9614,6 +9622,7 @@ Specificare un nome utente diverso.',
 Inserire un indirizzo email se si desidera effettuare l'iscrizione alla mailing list.",
        'config-almost-done' => 'Hai quasi finito!
 Adesso puoi saltare la rimanente parte della configurazione e semplicemente installare la wiki.',
+       'config-optional-continue' => 'Fammi altre domande.',
        'config-profile-wiki' => 'Wiki tradizionale',
        'config-profile-no-anon' => 'Creazione utenza obbligatoria',
        'config-profile-fishbowl' => 'Solo editori autorizzati',
@@ -9633,20 +9642,27 @@ Se vuoi usare testi da Wikipedia, o desideri che Wikipedia possa essere in grado
 
 In precedenza Wikipedia ha utilizzato la GNU Free Documentation License. La GFDL è una licenza valida, ma è di difficile comprensione e complica il riutilizzo dei contenuti.",
        'config-email-settings' => 'Impostazioni email',
+       'config-email-user' => 'Abilita invio email fra utenti',
        'config-email-auth' => 'Abilita autenticazione via email',
+       'config-upload-enable' => 'Consentire il caricamento di file',
        'config-upload-deleted' => 'Directory per i file cancellati:',
        'config-logo' => 'URL del logo:',
+       'config-instantcommons' => 'Abilita Instant Commons',
        'config-cc-again' => 'Seleziona di nuovo...',
        'config-cc-not-chosen' => 'Scegliere quale licenza Creative Commons si desidera e cliccare su "procedi".',
        'config-advanced-settings' => 'Configurazione avanzata',
+       'config-memcache-needservers' => 'È stato selezionato il tipo di caching Memcached, ma non è stato impostato alcun server.',
        'config-memcache-badip' => 'È stato inserito un indirizzo IP non valido per Memcached: $1.',
        'config-extensions' => 'Estensioni',
        'config-install-step-done' => 'fatto',
        'config-install-step-failed' => 'non riuscito',
+       'config-install-schema' => 'Creazione dello schema',
+       'config-install-user' => 'Creazione di utente del database',
        'config-install-user-alreadyexists' => 'L\'utente "$1" è già presente',
        'config-install-user-create-failed' => 'Creazione dell\'utente "$1" non riuscita: $2',
        'config-install-user-missing' => 'L\'utente indicato "$1" non esiste.',
        'config-install-tables-failed' => "'''Errore''': La creazione della tabella non è riuscita: $1",
+       'config-install-interwiki' => 'Riempimento della tabella interwiki predefinita',
        'config-install-interwiki-list' => 'Impossibile leggere il file <code>interwiki.list</code>.',
        'config-install-stats' => 'Inizializzazione delle statistiche',
        'config-install-keys' => 'Generazione delle chiavi segrete',
index dc5bdbd..21ef6d3 100644 (file)
@@ -88,7 +88,10 @@ abstract class JobQueue {
        }
 
        /**
-        * @return bool Quickly check if the queue is empty
+        * Quickly check if the queue is empty.
+        * Queue classes should use caching if they are any slower without memcached.
+        *
+        * @return bool
         */
        final public function isEmpty() {
                wfProfileIn( __METHOD__ );
index a8a2e89..223ef41 100644 (file)
@@ -28,7 +28,7 @@
  * @since 1.21
  */
 class JobQueueDB extends JobQueue {
-       const CACHE_TTL      = 30; // integer; seconds
+       const CACHE_TTL      = 300; // integer; seconds
        const MAX_JOB_RANDOM = 2147483647; // 2^31 - 1; used for job_random
 
        /**
@@ -97,7 +97,7 @@ class JobQueueDB extends JobQueue {
                                        $dbw->setFlag( $autoTrx ? DBO_TRX : 0 ); // restore automatic begin()
                                }
 
-                               $wgMemc->set( $key, 'false', $ttl );
+                               $wgMemc->set( $key, 'false', $ttl ); // queue is not empty
                        } );
                }
 
index 44db516..48f2746 100644 (file)
@@ -153,4 +153,17 @@ class JobQueueGroup {
 
                return array_diff( $this->getQueueTypes(), $wgJobTypesExcludedFromDefaultQueue );
        }
+
+       /**
+        * @return Array List of job types that have non-empty queues
+        */
+       public function getQueuesWithJobs() {
+               $types = array();
+               foreach ( $this->getQueueTypes() as $type ) {
+                       if ( !$this->get( $type )->isEmpty() ) {
+                               $types[] = $type;
+                       }
+               }
+               return $types;
+       }
 }
index 76886eb..7793710 100644 (file)
@@ -107,8 +107,11 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
         * @return Mixed
         */
        public function get( $key ) {
+               wfProfileIn( __METHOD__ );
                $this->debugLog( "get($key)" );
-               return $this->checkResult( $key, parent::get( $key ) );
+               $value = $this->checkResult( $key, parent::get( $key ) );
+               wfProfileOut( __METHOD__ );
+               return $value;
        }
 
        /**
@@ -224,9 +227,11 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
         * @return Array
         */
        public function getMulti( array $keys ) {
+               wfProfileIn( __METHOD__ );
                $this->debugLog( 'getMulti(' . implode( ', ', $keys ) . ')' );
                $callback = array( $this, 'encodeKey' );
                $result = $this->client->getMulti( array_map( $callback, $keys ) );
+               wfProfileOut( __METHOD__ );
                return $this->checkResult( false, $result );
        }
 
index af3dbf3..ce2633f 100644 (file)
@@ -464,8 +464,6 @@ class MovePageForm extends UnlistedSpecialPage {
                        DoubleRedirectJob::fixRedirects( 'move', $ot, $nt );
                }
 
-               wfRunHooks( 'SpecialMovepageAfterMove', array( &$this, &$ot, &$nt ) );
-
                $out = $this->getOutput();
                $out->setPageTitle( $this->msg( 'pagemovedsub' ) );
 
@@ -484,6 +482,8 @@ class MovePageForm extends UnlistedSpecialPage {
                        $newLink )->params( $oldText, $newText )->parseAsBlock() );
                $out->addWikiMsg( $msgName );
 
+               wfRunHooks( 'SpecialMovepageAfterMove', array( &$this, &$ot, &$nt ) );
+
                # Now we move extra pages we've been asked to move: subpages and talk
                # pages.  First, if the old page or the new page is a talk page, we
                # can't move any talk pages: cancel that.
index 2bd8b0a..f542640 100644 (file)
@@ -382,7 +382,7 @@ class SpecialRecentChanges extends IncludableSpecialPage {
                $invert = $opts['invert'];
                $associated = $opts['associated'];
 
-               $fields = array( $dbr->tableName( 'recentchanges' ) . '.*' ); // all rc columns
+               $fields = RecentChange::selectFields();
                // JOIN on watchlist for users
                if ( $uid ) {
                        $tables[] = 'watchlist';
index 40ebc2f..4db8958 100644 (file)
@@ -93,7 +93,7 @@ class SpecialRecentchangeslinked extends SpecialRecentChanges {
                $dbkey = $title->getDBkey();
 
                $tables = array( 'recentchanges' );
-               $select = array( $dbr->tableName( 'recentchanges' ) . '.*' );
+               $select = RecentChange::selectFields();
                $join_conds = array();
                $query_options = array();
 
index 5dfc113..688e0a5 100644 (file)
@@ -272,7 +272,7 @@ class SpecialWatchlist extends SpecialPage {
                $form .= '<hr />';
 
                $tables = array( 'recentchanges', 'watchlist' );
-               $fields = array( $dbr->tableName( 'recentchanges' ) . '.*' );
+               $fields = RecentChange::selectFields();
                $join_conds = array(
                        'watchlist' => array(
                                'INNER JOIN',
index 4c4dff3..5460386 100644 (file)
@@ -9,6 +9,7 @@
  *
  * @author Cekli829
  * @author Don Alessandro
+ * @author E THP
  * @author Emperyan
  * @author Erdemaslancan
  * @author Gulmammad
@@ -956,7 +957,7 @@ $1",
 
 # Preferences page
 'preferences' => 'Nizamlamalar',
-'mypreferences' => 'Nizamlamalarım',
+'mypreferences' => 'Nizamlamalar',
 'prefs-edits' => 'Redaktələrin sayı:',
 'prefsnologin' => 'Daxil olmamısınız',
 'prefsnologintext' => 'Nizamlamaları dəyişmək üçün <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} daxil olmaq]</span> zəruridir.',
index 48cb369..c328974 100644 (file)
@@ -232,7 +232,7 @@ $messages = array(
 'cancel' => 'Kanselaron',
 'moredotdotdot' => 'Kadagdagan...',
 'mypage' => 'An sakóng pahina',
-'mytalk' => 'An sakóng olay',
+'mytalk' => 'Orolayan',
 'anontalk' => 'Olay para kaining IP address',
 'navigation' => 'Nabigasyon',
 'and' => '&#32;asin',
@@ -1173,7 +1173,7 @@ Prubaran na panigmitan an saimong kahaputan nin ''all:'' sa paghanap kan gabos n
 
 # Preferences page
 'preferences' => 'Mga kabòtan',
-'mypreferences' => 'Mga kabòtan ko',
+'mypreferences' => 'Mga Kamuyahan ko',
 'prefs-edits' => 'Bilang kan mga hirá:',
 'prefsnologin' => 'Dai nakalaog',
 'prefsnologintext' => 'Ika dapat na magin <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} nakalaog na]</span> tanganing tuytuyon an mga kabotan nin paragamit.',
@@ -2105,7 +2105,7 @@ An e-surat na adres na saimong ilalaog sa [[Special:Preferences|saimong paragami
 
 # Watchlist
 'watchlist' => 'Pigbabantayan ko',
-'mywatchlist' => 'Babantáyan ko',
+'mywatchlist' => 'Bantay-listahan',
 'watchlistfor2' => 'Para ki $1 $2',
 'nowatchlist' => 'Mayo ka man na mga bagay saimong lista nin pigbabantayan.',
 'watchlistanontext' => 'Mag $1 tabi para mahiling o maghira nin mga bagay saimong lista nin mga pigbabantayan.',
@@ -2367,7 +2367,7 @@ $1",
 # Contributions
 'contributions' => 'Mga kontribusyon kan parágamit',
 'contributions-title' => 'Mga kontribusyon kan paragamit para sa $1',
-'mycontris' => 'Mga ambág ko',
+'mycontris' => 'Mga Kaarambagan',
 'contribsub2' => 'Para sa $1 ($2)',
 'nocontribs' => 'Mayong mga pagbabago na nahanap na kapadis sa ining mga criteria.',
 'uctop' => '(alituktok)',
@@ -2408,7 +2408,7 @@ An pinakahuring entrada sa talaan nin pagbara nakahaya sa ibaba bilang reperensi
 'whatlinkshere-hideredirs' => '$1 mga panukdong otro',
 'whatlinkshere-hidetrans' => '$1 kabaling-binalyuhan',
 'whatlinkshere-hidelinks' => '$1 mga kasugpon',
-'whatlinkshere-hideimages' => '$1 mga kasugpon kan imahe',
+'whatlinkshere-hideimages' => '$1 mga kasugpon nin mga sagunson',
 'whatlinkshere-filters' => 'Mga pansarà',
 
 # Block/unblock
index e2a8d68..f19cb10 100644 (file)
@@ -2509,8 +2509,8 @@ quines pàgines en concret estan sent vandalitzades).",
 'ipb-confirm' => 'Confirma el blocatge',
 'badipaddress' => "L'adreça IP no té el format correcte.",
 'blockipsuccesssub' => "S'ha blocat amb èxit",
-'blockipsuccesstext' => "[[Special:Contributions/$1|$1]] ha estat {{GENDER:$1|bloquejat|bloquejada|bloquejat/da}}.<br />
-Vegeu la [[Special:BlockList|llista d'IP blocades]] per revisar els bloqueigs.",
+'blockipsuccesstext' => '[[Special:Contributions/$1|$1]] ha estat {{GENDER:$1|blocat|blocada}}.<br />
+Vegeu la [[Special:BlockList|llista de bloqueigs]] per revisar-los.',
 'ipb-blockingself' => 'Esteu a punt de blocar-vos a vós mateix! Esteu segurs de voler-ho fer?',
 'ipb-confirmhideuser' => "Esteu a punt de bloquejar un usuari que està marcat amb l'opció «amaga l'usuari». Això suprimirà el seu nom a totes les llistes i registres. Esteu segurs de voler-ho fer?",
 'ipb-edit-dropdown' => 'Edita les raons per a blocar',
index 781a6bb..5c7a395 100644 (file)
@@ -357,7 +357,7 @@ $messages = array(
 
 # Font style option in Special:Preferences
 'editfont-style' => 'Cayê vurnayışi de terzê nuştışi:',
-'editfont-default' => 'Cıgeyrayoğo hesıbyaye',
+'editfont-default' => 'Cıgeyrayoğo hesabiyaye',
 'editfont-monospace' => 'Terzê nusteyê sabıtcagırewtoği',
 'editfont-sansserif' => 'Babetê Sans-serifi',
 'editfont-serif' => 'Babetê serifi',
@@ -442,8 +442,8 @@ $messages = array(
 'newwindow' => '(pençereyê newey de beno a)',
 'cancel' => 'Bıtexelne',
 'moredotdotdot' => 'Vêşi...',
-'mypage' => 'Pela mı',
-'mytalk' => 'Werênayışê mı',
+'mypage' => 'Per',
+'mytalk' => 'Werênayış',
 'anontalk' => 'Pela werênayışê nê IPy',
 'navigation' => 'Geyrayış',
 'and' => '&#32;u',
@@ -461,7 +461,7 @@ $messages = array(
 # Vector skin
 'vector-action-addsection' => 'Mesel Vırazê',
 'vector-action-delete' => 'Besterne',
-'vector-action-move' => 'Berdış',
+'vector-action-move' => 'Berê',
 'vector-action-protect' => 'Bıpawe',
 'vector-action-undelete' => 'Esterıtışi peyser bıgê',
 'vector-action-unprotect' => 'Starkerdışi bıvurne',
@@ -1369,7 +1369,7 @@ Pe verbendi ''all:'', vaceyê xo bıvurni ki contenti hemi cıgeyro (pelanê mı
 
 # Preferences page
 'preferences' => 'Tercihi',
-'mypreferences' => 'Tercihê mı',
+'mypreferences' => 'Tercihi',
 'prefs-edits' => 'Amarê vurnayışan:',
 'prefsnologin' => 'Şıma cıkewtış nêvıraşto',
 'prefsnologintext' => 'Şıma gani be <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} cikewte]</span> ke tercihanê karberi xo eyar bıkerê.',
@@ -2315,7 +2315,7 @@ qey heqê şexsi de [[{{MediaWiki:Listgrouprights-helppage}}|hema malumato ziyed
 
 # Watchlist
 'watchlist' => 'Lista mına seyrkerdışi',
-'mywatchlist' => 'Lista mına seyrkerdışi',
+'mywatchlist' => 'Lista seyr kerdışi',
 'watchlistfor2' => 'Qandê $1 ($2)',
 'nowatchlist' => 'listeya temaşa kerdıişê şıma de yew madde zi çina.',
 'watchlistanontext' => 'qey vurnayişê maddeya listeya temaşakerdişi $1.',
@@ -2573,7 +2573,7 @@ $1',
 # Contributions
 'contributions' => 'İştiraqê karberi',
 'contributions-title' => 'Dekerdenê karber de $1',
-'mycontris' => 'Cıkerdışê mı',
+'mycontris' => 'Cıkerdışi',
 'contribsub2' => 'Qandê $1 ($2)',
 'nocontribs' => 'Ena kriteriya de vurnayîş çini yo.',
 'uctop' => '(top)',
index ab5e6a3..ec31872 100644 (file)
@@ -767,16 +767,17 @@ XHTML id names.
 
 'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD', # only translate this message to other languages if you have to change it
 
-'about'         => 'About',
-'article'       => 'Content page',
-'newwindow'     => '(opens in new window)',
-'cancel'        => 'Cancel',
-'moredotdotdot' => 'More...',
-'mypage'        => 'Page',
-'mytalk'        => 'Talk',
-'anontalk'      => 'Talk for this IP address',
-'navigation'    => 'Navigation',
-'and'           => '&#32;and',
+'about'                => 'About',
+'article'              => 'Content page',
+'newwindow'            => '(opens in new window)',
+'cancel'               => 'Cancel',
+'moredotdotdot'        => 'More...',
+'mypage'               => 'Page',
+'mytalk'               => 'Talk',
+'mytalk-parenthetical' => 'talk',
+'anontalk'             => 'Talk for this IP address',
+'navigation'           => 'Navigation',
+'and'                  => '&#32;and',
 
 # Cologne Blue skin
 'qbfind'         => 'Find',
index ef153d9..5047cbb 100644 (file)
@@ -1114,7 +1114,7 @@ $2
 'userpage-userdoesnotexist-view' => 'حساب کاربری «$1» ثبت نشده‌است.',
 'blocked-notice-logextract' => 'دسترسی این کاربر در حال حاضر بسته است.
 آخرین مورد سیاهه قطع دسترسی در زیر آمده‌است:',
-'clearyourcache' => "''نکته:''' پس از ذخیره‌کردن ممکن است برای دیدن تغییرات نیاز باشد که حافظهٔ نهانی مرورگر خود را پاک کنید.
+'clearyourcache' => "'''نکته:''' پس از ذخیره‌کردن ممکن است برای دیدن تغییرات نیاز باشد که حافظهٔ نهانی مرورگر خود را پاک کنید.
 *'''فایرفاکس / سافاری:'''  کلید ''Shift'' را نگه دارید و روی دکمهٔ ''Reload'' کلیک کنید، یا کلید‌های ''Ctrl-F5'' یا ''Ctrl-R'' را با هم فشار دهید (در رایانه‌های اپل مکینتاش کلید‌های ''⌘-R'')
 *'''گوگل کروم:'''کلیدهای ''Ctrl+Shift+R'' را با هم فشار دهید. (در رایانه‌های اپل مکینتاش کلید‌های ''⌘-Shift-R'')
 *'''اینترنت اکسپلورر:''' کلید ''Ctrl'' را نگه‌دارید و روی دکمهٔ ''Refresh'' کلیک کنید، یا کلید‌های ''Ctrl-F5'' را با هم فشار دهید
index e0c126a..caed256 100644 (file)
@@ -477,8 +477,8 @@ $messages = array(
 'newwindow' => '(새 창으로 열림)',
 'cancel' => '취소',
 'moredotdotdot' => '더 보기...',
-'mypage' => 'ë\82´ ì\82¬ì\9a©ì\9e\90 ë¬¸ì\84\9c',
-'mytalk' => '내 사용자 토론',
+'mypage' => '문서',
+'mytalk' => '토론',
 'anontalk' => '익명 사용자 토론',
 'navigation' => '둘러보기',
 'and' => ',',
@@ -1436,7 +1436,7 @@ $1",
 
 # Preferences page
 'preferences' => '사용자 환경 설정',
-'mypreferences' => '사용자 환경 설정',
+'mypreferences' => '환경 설정',
 'prefs-edits' => '편집 횟수:',
 'prefsnologin' => '로그인하지 않음',
 'prefsnologintext' => '사용자 환경 설정을 바꾸려면 먼저 <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} 로그인]</span>해야 합니다.',
@@ -2400,7 +2400,7 @@ URL이 맞고 해당 웹사이트가 작동하는지 확인해주세요.',
 
 # Watchlist
 'watchlist' => '주시문서 목록',
-'mywatchlist' => '내 주시문서 목록',
+'mywatchlist' => '주시문서 목록',
 'watchlistfor2' => '사용자:$1 $2',
 'nowatchlist' => '주시하는 문서가 아직 없습니다.',
 'watchlistanontext' => '주시문서 목록을 보거나 고치려면 $1 하세요.',
@@ -2665,7 +2665,7 @@ $1',
 # Contributions
 'contributions' => '사용자 기여',
 'contributions-title' => '$1 사용자의 기여 목록',
-'mycontris' => '내 기여 목록',
+'mycontris' => '기여 목록',
 'contribsub2' => '$1($2)의 기여',
 'nocontribs' => '이 사용자는 아무 것도 기여하지 않았습니다.',
 'uctop' => '(최신)',
index b86c095..a9f3b97 100644 (file)
@@ -733,8 +733,7 @@ Sedema qedexekirina $3 ev e: ''$2''",
 'last' => 'berê',
 'page_first' => 'ya pêşîn',
 'page_last' => 'ya paşîn',
-'histlegend' => 'Rênîşan: (cudahî) = cudahiya nav vê û versiyona niha,
-(berê) = cudahiya nav vê û ya berî vê, B = guhertina biçûk',
+'histlegend' => "Rênîşan: ({{int:cur}}) = cudahiya nav vê û versiyona niha, ({{int:last}}) = cudahiya nav vê û ya berî vê, '''{{int:minoreditletter}}''' = guhertina biçûk",
 'history-fieldset-title' => 'Li dîrokê bigere',
 'history-show-deleted' => 'Tenê yên jêbirî',
 'histfirst' => 'Kevintirîn',
index ee4d901..e63863e 100644 (file)
@@ -972,7 +972,8 @@ Det siste elementet i blokkeringsloggen er oppgjeve nedanfor:',
 'userinvalidcssjstitle' => "'''Åtvaring:''' Det finst ikkje noka sidedrakt som heiter «$1». Hugs på at vanlege .css- og .js-sider brukar titlar med små bokstavar, til dømes {{ns:user}}:Døme/vector.css, og ikkje {{ns:user}}:Døme/Vector.css.",
 'updated' => '(Oppdatert)',
 'note' => "'''Merk:'''",
-'previewnote' => "'''Hugs at dette berre er ei førehandsvising.''' Endringane dine er ikkje lagra enno!",
+'previewnote' => "'''Hugsa at dette berre er ei førehandsvising.'''
+Endringane dine er ikkje lagra enno!",
 'continue-editing' => 'Gå til endringsområdet',
 'previewconflict' => 'Dette er ei førehandsvising av teksten i endringsboksen over, slik han vil sjå ut om du lagrar han',
 'session_fail_preview' => "'''Orsak! Endringa di kunne ikkje lagrast. Ver venleg og prøv ein gong til. Dersom det framleis ikkje går, prøv å logge deg ut og inn att.'''",
@@ -2762,7 +2763,7 @@ I desse falla lyt du flytta eller fletta sida manuelt, om ynskeleg.",
 'delete_and_move' => 'Slett og flytt',
 'delete_and_move_text' => '== Sletting påkravd ==
 
-Målsida «[[:$1]]» finst alt. Vil du sletta henne for å gjeva rom for flytting?',
+Målsida «[[:$1]]» finst allereie. Vil du slette ho for å gje rom for flytting?',
 'delete_and_move_confirm' => 'Ja, slett sida',
 'delete_and_move_reason' => 'Sletta for å gje rom for flytting frå «[[$1]]»',
 'selfmove' => 'Kjelde- og måltitlane er like; kan ikkje flytte sida over seg sjølv.',
index 4f84446..01a4e52 100644 (file)
@@ -3395,22 +3395,22 @@ Che a controla l'adrëssa che a l'ha dane, mai che a-i fusso dij caràter nen v
 'confirmemail_needlogin' => 'A venta $1 për confermé soa adrëssa ëd pòsta eletrònica.',
 'confirmemail_success' => "Soa adrëssa a l'é stàita confermà, adess a peul [[Special:UserLogin|rintré ant ël sistema]] e i-j auguroma da fessla bin ant la wiki!",
 'confirmemail_loggedin' => "Motobin mersì. Soa adrëssa ëd pòsta eletrònica adess a l'é confermà.",
-'confirmemail_error' => "Cheich-còs a l'é andà mal ën salvand soa conferma.",
+'confirmemail_error' => "Cheicòs a l'é andà mal ën salvand soa conferma.",
 'confirmemail_subject' => "Conferma dl'adrëssa postal da 'nt la {{SITENAME}}",
-'confirmemail_body' => "Cheidun, a l'é belfé che a sia stait pròpe chiel (ò chila), da 'nt l'adrëssa IP \$1,
-a l'ha doertà un cont utent \"\$2\" ansima a {{SITENAME}}, lassand-ne st'adrëssa ëd pòsta eletrònica-sì.
+'confirmemail_body' => "Cheidun, a l'é belfé che a sia stàit pròpe chiel, da 'nt l'adrëssa IP $1,
+a l'ha duvertà un cont utent «$2» ansima a {{SITENAME}}, lassand-ne st'adrëssa ëd pòsta eletrònica-sì.
 
 Për confermé che ës cont a l'é da bon sò e për ativé
-le possibilità corelà a la pòsta eletrònica ansima a {{SITENAME}}, che a deurba st'adrëssa-sì andrinta a sò programa ëd navigassion (browser):
+le possibilità gropà a la pòsta eletrònica ansima a {{SITENAME}}, che a deurba st'adrëssa-sì andrinta a sò programa ëd navigassion:
 
-\$3
+$3
 
-Se a fussa *nen* stait chiel a deurbe ël cont, anlora che a vada daré a sto colegament-sì
-për scanselé la conferma ëd l'adrëssa e-mail:
+Se a fussa *nen* stàit chiel a deurbe ël cont, anlora che a vada dapress a la liura sì-sota
+për scancelé la conferma ëd l'adrëssa ëd pòsta eletrònica:
 
-\$5
+$5
 
-Cost còdes ëd conferma a l'é bon fin-a al \$4.",
+Cost còdes ëd conferma a l'é bon fin-a al $4.",
 'confirmemail_body_changed' => "Cheidun, a l'é belfé ch'a sia chiel, da l'adrëssa IP \$1,
 a l'ha cangià l'adrëssa ëd pòsta eletrònica dël cont \"\$2\" con st'adrëssa-sì dzora a {{SITENAME}}.
 
index 4ae5911..e85ff8e 100644 (file)
@@ -442,7 +442,7 @@ $messages = array(
 'newwindow' => '(abre numa janela nova)',
 'cancel' => 'Cancelar',
 'moredotdotdot' => 'Mais...',
-'mypage' => 'Utilizador',
+'mypage' => 'Página',
 'mytalk' => 'Discussão',
 'anontalk' => 'Discussão para este IP',
 'navigation' => 'Navegação',
index 48c68c1..0fbe91e 100644 (file)
@@ -446,8 +446,8 @@ $messages = array(
 'newwindow' => '(abre em uma nova janela)',
 'cancel' => 'Cancelar',
 'moredotdotdot' => 'Mais...',
-'mypage' => 'Minha página',
-'mytalk' => 'Minha discussão',
+'mypage' => 'Página',
+'mytalk' => 'Discussão',
 'anontalk' => 'Discussão para este IP',
 'navigation' => 'Navegação',
 'and' => '&#32;e',
@@ -1374,7 +1374,7 @@ Note que os índices do sistema de busca externo poderão conter referências de
 
 # Preferences page
 'preferences' => 'Preferências',
-'mypreferences' => 'Minhas preferências',
+'mypreferences' => 'Preferências',
 'prefs-edits' => 'Número de edições:',
 'prefsnologin' => 'Não autenticado',
 'prefsnologintext' => 'É necessário estar <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} autenticado]</span> para definir as suas preferências.',
@@ -2580,7 +2580,7 @@ $1',
 # Contributions
 'contributions' => 'Contribuições {{GENDER:{{BASEPAGENAME}}|do usuário|da usuária}}',
 'contributions-title' => 'Contribuições {{GENDER:$1|do usuário|da usuária}} $1',
-'mycontris' => 'Minhas contribuições',
+'mycontris' => 'Contribuições',
 'contribsub2' => 'Para $1 ($2)',
 'nocontribs' => 'Não foram encontradas mudanças com este critério.',
 'uctop' => '(atual)',
index 2b3c976..a601b93 100644 (file)
@@ -347,9 +347,8 @@ Possible alternatives to the word 'content' are 'subject matter' or 'wiki subjec
 {{Identical|Cancel}}',
 'moredotdotdot' => '{{Identical|More...}}',
 'mypage' => "A text for the link to the user's user page in the links at the top of the page.",
-'mytalk' => 'In the personal urls page section - right upper corner.
-
-Used as link title in "Personal tools" toolbar.',
+'mytalk' => 'Used as link title in "Personal tools" toolbar.',
+'mytalk-parenthetical' => 'When user page and talk combined into single label, link title for talk label',
 'anontalk' => 'Link to the talk page appearing in [[mw:Help:Navigation#User_Links|user links]] for each anonymous users when [[mw:Manual:$wgShowIPinHeader|$wgShowIPinHeader]] is true.',
 'navigation' => 'This is shown as a section header in the sidebar of most skins.
 
index e85a226..ed2c360 100644 (file)
@@ -434,7 +434,7 @@ pe titlul secțiunii (JavaScript)',
 'newwindow' => '(se deschide într-o fereastră nouă)',
 'cancel' => 'Revocare',
 'moredotdotdot' => 'Mai mult…',
-'mypage' => 'Pagina mea',
+'mypage' => 'Pagină',
 'mytalk' => 'Discuții',
 'anontalk' => 'Discuția pentru această adresă IP',
 'navigation' => 'Navigare',
index 7a4aaff..278855e 100644 (file)
@@ -516,11 +516,11 @@ $messages = array(
 'category-empty' => "''Эта категория в данный момент пуста.''",
 'hidden-categories' => '{{PLURAL:$1|Скрытая категория|Скрытые категории}}',
 'hidden-category-category' => 'Скрытые категории',
-'category-subcat-count' => '{{PLURAL:$2|Данная категория содержит только следующую подкатегорию.|{{PLURAL:$1|Показана $1 подкатегория|Показано $1 подкатегории|Показано $1 подкатегорий}} из $2.}}',
+'category-subcat-count' => '{{PLURAL:$2|Данная категория содержит только следующую подкатегорию.|{{PLURAL:$1|Показана $1 подкатегория|Показано $1 подкатегории|Показано $1 подкатегорий}} из $2, находящихся в этой категории.}}',
 'category-subcat-count-limited' => 'В этой категории {{PLURAL:$1|$1 подкатегория|$1 подкатегории|$1 подкатегорий}}.',
-'category-article-count' => '{{PLURAL:$2|Эта категория содержит только одну страницу.|{{PLURAL:$1|Показана $1 страница|Показано $1 страницы|Показано $1 страниц}} этой категории из $2.}}',
+'category-article-count' => '{{PLURAL:$2|Эта категория содержит только одну страницу.|{{PLURAL:$1|Показана $1 страница|Показано $1 страницы|Показано $1 страниц}} из $2, находящихся в этой категории.}}',
 'category-article-count-limited' => 'В этой категории {{PLURAL:$1|$1 страница|$1 страницы|$1 страниц}}.',
-'category-file-count' => '{{PLURAL:$2|Эта категория содержит только один файл.|{{PLURAL:$1|Показан $1 файл|Показано $1 файла|Показано $1 файлов}} этой категории  из $2.}}',
+'category-file-count' => '{{PLURAL:$2|Эта категория содержит только один файл.|{{PLURAL:$1|Показан $1 файл|Показано $1 файла|Показано $1 файлов}} из $2, находящихся в этой категории.}}',
 'category-file-count-limited' => 'В этой категории {{PLURAL:$1|$1 файл|$1 файла|$1 файлов}}.',
 'listingcontinuesabbrev' => '(продолжение)',
 'index-category' => 'Индексируемые страницы',
@@ -580,7 +580,7 @@ $messages = array(
 'permalink' => 'Постоянная ссылка',
 'print' => 'Печать',
 'view' => 'Просмотр',
-'edit' => 'РедакÑ\82иÑ\80овать',
+'edit' => 'Ð\9fÑ\80авить',
 'create' => 'Создать',
 'editthispage' => 'Править эту страницу',
 'create-this-page' => 'Создать эту страницу',
@@ -761,7 +761,7 @@ $1',
 'delete-hook-aborted' => 'Правка отменена процедурой-перехватчиком.
 Дополнительных пояснений не приведено.',
 'badtitle' => 'Недопустимое название',
-'badtitletext' => 'Ð\97апÑ\80аÑ\88иваемое Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b Ð½ÐµÐ¿Ñ\80авилÑ\8cно, Ð¿Ñ\83Ñ\81Ñ\82о, Ð»Ð¸Ð±Ð¾ Ð½ÐµÐ¿Ñ\80авилÑ\8cно указано межъязыковое или интервики название. Возможно, в названии используются недопустимые символы.',
+'badtitletext' => 'Ð\97апÑ\80аÑ\88иваемое Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b Ð½ÐµÐ¿Ñ\80авилÑ\8cно, Ð¿Ñ\83Ñ\81Ñ\82о, Ð»Ð¸Ð±Ð¾ Ð½ÐµÐ²ÐµÑ\80но указано межъязыковое или интервики название. Возможно, в названии используются недопустимые символы.',
 'perfcached' => 'Следующие данные взяты из кэша и могут не учитывать последних изменений. В кэше хранится не более $1 {{PLURAL:$1|записи|записей|записей}}.',
 'perfcachedts' => 'Следующие данные взяты из кэша, последний раз он обновлялся в $1. В кэше хранится не более $4 {{PLURAL:$4|записи|записей|записей}}.',
 'querypage-no-updates' => 'Обновление этой страницы сейчас отключено.
@@ -811,7 +811,7 @@ $2',
 'welcomecreation' => '== Добро пожаловать, $1! ==
 Ваша учётная запись создана.
 Не забудьте провести [[Special:Preferences|персональную настройку]] сайта.',
-'yourname' => 'Имя участника:',
+'yourname' => 'Имя учётной записи:',
 'yourpassword' => 'Пароль:',
 'yourpasswordagain' => 'Повторный набор пароля:',
 'remembermypassword' => 'Помнить мою учётную запись на этом компьютере (не более $1 {{PLURAL:$1|дня|дней|дней}})',
@@ -827,9 +827,9 @@ $2',
 'logout' => 'Завершение сеанса',
 'userlogout' => 'Завершение сеанса',
 'notloggedin' => 'Вы не представились системе',
-'nologin' => "Нет учётной записи? '''$1'''.",
+'nologin' => 'Нет учётной записи? $1.',
 'nologinlink' => 'Создать учётную запись',
-'createaccount' => 'Ð\97аÑ\80егиÑ\81Ñ\82Ñ\80иÑ\80оваÑ\82Ñ\8c Ð½Ð¾Ð²Ð¾Ð³Ð¾ Ñ\83Ñ\87аÑ\81Ñ\82ника',
+'createaccount' => 'СоздаÑ\82Ñ\8c Ñ\83Ñ\87Ñ\91Ñ\82нÑ\83Ñ\8e Ð·Ð°Ð¿Ð¸Ñ\81Ñ\8c',
 'gotaccount' => "Вы уже зарегистрированы? '''$1'''.",
 'gotaccountlink' => 'Представьтесь',
 'userlogin-resetlink' => 'Забыли данные для входа?',
@@ -1006,8 +1006,8 @@ $2
 'showpreview' => 'Предварительный просмотр',
 'showlivepreview' => 'Быстрый предпросмотр',
 'showdiff' => 'Внесённые изменения',
-'anoneditwarning' => "'''Внимание:''' Вы не представились системе.
аш IP-адрес будет записан в историю изменений этой страницы.",
+'anoneditwarning' => "'''Внимание!''' Вы не авторизовались на сайте.
 истории изменений этой страницы будет записан ваш IP-адрес.",
 'anonpreviewwarning' => "''Вы не представились системе. Сохранение приведёт к записи вашего IP-адреса в историю изменений страницы.''",
 'missingsummary' => "'''Напоминание.''' Вы не дали краткого описания изменений. При повторном нажатии на кнопку «{{int:savearticle}}», ваши изменения будут сохранены без комментария.",
 'missingcommenttext' => 'Пожалуйста, введите ниже ваше сообщение.',
@@ -1151,12 +1151,12 @@ $2
 'cascadeprotectedwarning' => "'''Предупреждение:''' Данную страницу могут редактировать только участники группы «Администраторы», поскольку она включена {{PLURAL:$1|в следующую страницу, для которой|в следующие страницы, для которых}} включена каскадная защита:",
 'titleprotectedwarning' => "'''Предупреждение.  Это название защищено. Создать эту страницу могут только участники с [[Special:ListGroupRights|соответствующими правами]].'''
 Ниже для справки приведена последняя запись журнала:",
-'templatesused' => '{{PLURAL:$1|Шаблон, Ð¸Ñ\81полÑ\8cзованнÑ\8bй|ШаблонÑ\8b, Ð¸Ñ\81полÑ\8cзованнÑ\8bе}} Ð½Ð° Ñ\82екÑ\83Ñ\89ей Ð²ÐµÑ\80Ñ\81ии Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b:',
+'templatesused' => '{{PLURAL:$1|Шаблон, Ð¸Ñ\81полÑ\8cзованнÑ\8bй|ШаблонÑ\8b, Ð¸Ñ\81полÑ\8cзованнÑ\8bе}} Ð½Ð° Ñ\8dÑ\82ой Ñ\81Ñ\82Ñ\80аниÑ\86е:',
 'templatesusedpreview' => '{{PLURAL:$1|Шаблон, используемый|Шаблоны, используемые}} в предпросматриваемой странице:',
 'templatesusedsection' => '{{PLURAL:$1|Шаблон, используемый|Шаблоны, использованные}} в этом разделе:',
 'template-protected' => '(защищено)',
 'template-semiprotected' => '(частично защищено)',
-'hiddencategories' => 'Эта страница относится к $1 {{PLURAL:$1|скрытой категории|скрытым категориям|скрытым категориям}}:',
+'hiddencategories' => 'Эта страница относится к $1 {{PLURAL:$1|скрытой категории|скрытым категориям}}:',
 'edittools' => '<!-- Расположенный здесь текст будет показываться под формой редактирования и формой загрузки. -->',
 'nocreatetitle' => 'Создание страниц ограничено',
 'nocreatetext' => 'На этом сайте ограничена возможность создания новых страниц.
@@ -1194,9 +1194,9 @@ $2
 'content-model-css' => 'CSS',
 
 # Parser/template warnings
-'expensive-parserfunction-warning' => 'Внимание. Эта страница содержит слишком много вызовов ресурсоёмких функций.
+'expensive-parserfunction-warning' => "'''Внимание!''' Эта страница содержит слишком много вызовов ресурсоёмких функций.
 
\9eгÑ\80аниÑ\87ение Ð½Ð° ÐºÐ¾Ð»Ð¸Ñ\87еÑ\81Ñ\82во Ð²Ñ\8bзовов Ñ\83Ñ\81Ñ\82ановлено Ð½Ð° Ñ\83Ñ\80овне $2 {{PLURAL:$2|вÑ\8bзова|вÑ\8bзовов|вÑ\8bзовов}}, Ð² Ð´Ð°Ð½Ð½Ð¾Ð¼ Ñ\81лÑ\83Ñ\87ае Ñ\82Ñ\80ебÑ\83еÑ\82Ñ\81Ñ\8f Ñ\81делаÑ\82Ñ\8c $1 {{PLURAL:$1|вÑ\8bзов|вÑ\8bзова|вÑ\8bзовов}}.',
\94олжно Ð±Ñ\8bÑ\82Ñ\8c Ð½Ðµ Ð±Ð¾Ð»ÐµÐµ $2 {{PLURAL:$2|вÑ\8bзова|вÑ\8bзовов}}, Ð² Ñ\82о Ð²Ñ\80емÑ\8f ÐºÐ°Ðº Ñ\81ейÑ\87аÑ\81 Ð·Ð´ÐµÑ\81Ñ\8c $1 {{PLURAL:$1|вÑ\8bзов|вÑ\8bзова|вÑ\8bзовов}}.",
 'expensive-parserfunction-category' => 'Страницы со слишком большим количеством вызовов ресурсоёмких функций',
 'post-expand-template-inclusion-warning' => 'Предупреждение: суммарный размер включаемых шаблонов слишком велик.
 Некоторые шаблоны не будут включены.',
@@ -1352,7 +1352,7 @@ $1",
 ** Потенциально клеветнические сведения',
 'revdelete-otherreason' => 'Другая/дополнительная причина:',
 'revdelete-reasonotherlist' => 'Другая причина',
-'revdelete-edit-reasonlist' => 'Ð\9fÑ\80авить список причин',
+'revdelete-edit-reasonlist' => 'РедакÑ\82иÑ\80овать список причин',
 'revdelete-offender' => 'Автор версии страницы:',
 
 # Suppression log
@@ -1402,7 +1402,7 @@ $1",
 'diff-multi-manyusers' => '(не {{PLURAL:$1|показана $1 промежуточная версия|показаны $1 промежуточные версии|показаны $1 промежуточных версий}}, сделанные более чем $2 {{PLURAL:$2|участником|участниками}})',
 'difference-missing-revision' => '{{PLURAL:$2|$2 версия|$2 версии|$2 версий}} для этого сравнения ($1) {{PLURAL:$2|не обнаружена|не обнаружены}}.
 
-ЭÑ\82о Ð¾Ð±Ñ\8bÑ\87но Ð±Ñ\8bваеÑ\82, ÐµÑ\81ли Ð¿Ð¾Ñ\81ледоваÑ\82Ñ\8c Ð¿Ð¾ Ñ\83Ñ\81Ñ\82аÑ\80евÑ\88ей Ñ\81Ñ\81Ñ\8bлке Ð½Ð° Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\83, которая была удалена.
+ЭÑ\82о Ð¾Ð±Ñ\8bÑ\87но Ð±Ñ\8bваеÑ\82, ÐµÑ\81ли Ð¿ÐµÑ\80ейÑ\82и Ð¿Ð¾ Ñ\83Ñ\81Ñ\82аÑ\80евÑ\88ей Ñ\81Ñ\81Ñ\8bлке Ñ\81Ñ\80авнениÑ\8f Ð²ÐµÑ\80Ñ\81ий Ð´Ð»Ñ\8f Ñ\81Ñ\82Ñ\80аниÑ\86Ñ\8b, которая была удалена.
 Подробности могут быть в [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журнале удалений].',
 
 # Search results
@@ -1411,7 +1411,7 @@ $1",
 'searchresulttext' => 'Для получения более подробной информации о поиске на страницах проекта, см. [[{{MediaWiki:Helppage}}|справочный раздел]].',
 'searchsubtitle' => 'По запросу «[[:$1]]» ([[Special:Prefixindex/$1|страницы, начинающиеся с этого названия]]{{int:pipe-separator}}[[Special:WhatLinksHere/$1|ссылающиеся на это название]])',
 'searchsubtitleinvalid' => 'По запросу «$1»',
-'toomanymatches' => 'Найдено слишком много соответствий, пожалуйста, попробуйте другой запрос',
+'toomanymatches' => 'Найдено слишком много соответствий; пожалуйста, попробуйте сформулировать запрос иначе',
 'titlematches' => 'Совпадения в названиях страниц',
 'notitlematches' => 'Нет совпадений в названиях страниц',
 'textmatches' => 'Совпадения в текстах страниц',
@@ -1423,7 +1423,7 @@ $1",
 'shown-title' => 'Показывать $1 {{PLURAL:$1|запись|записи|записей}} на странице',
 'viewprevnext' => 'Просмотреть ($1 {{int:pipe-separator}} $2) ($3)',
 'searchmenu-legend' => 'Настройки поиска',
-'searchmenu-exists' => "'''Ð\92 Ñ\8dÑ\82ом Ð²Ð¸ÐºÐ¸-пÑ\80оекÑ\82е есть страница «[[:$1]]»'''",
+'searchmenu-exists' => "'''Ð\92 Ñ\8dÑ\82ой Ð²Ð¸ÐºÐ¸ есть страница «[[:$1]]»'''",
 'searchmenu-new' => "'''Создать страницу «[[:$1]]» в этом вики-проекте!'''",
 'searchhelp-url' => 'Help:Содержание',
 'searchmenu-prefix' => '[[Special:PrefixIndex/$1|Показать страницы с этим префиксом]]',
@@ -1435,10 +1435,10 @@ $1",
 'searchprofile-articles-tooltip' => 'Поиск в $1',
 'searchprofile-project-tooltip' => 'Поиск в $1',
 'searchprofile-images-tooltip' => 'Поиск файлов',
-'searchprofile-everything-tooltip' => 'Поиск на всех страницах (включая страницы обсуждения)',
+'searchprofile-everything-tooltip' => 'Поиск на всех страницах (включая страницы обсуждений)',
 'searchprofile-advanced-tooltip' => 'Искать в заданных пространствах имён',
 'search-result-size' => '$1 ({{PLURAL:$2|$2 слово|$2 слова|$2 слов}})',
-'search-result-category-size' => '$1 {{PLURAL:$1|член|члена|членов}} ($2 {{PLURAL:$2|подкатегория|подкатегории|подкатегорий}}, $3 {{PLURAL:$3|файл|файла|файлов}}).',
+'search-result-category-size' => '$1 {{PLURAL:$1|вхождение|вхождения|вхождений}} ($2 {{PLURAL:$2|подкатегория|подкатегории|подкатегорий}}, $3 {{PLURAL:$3|файл|файла|файлов}}).',
 'search-result-score' => 'Релевантность: $1%.',
 'search-redirect' => '(перенаправление с $1)',
 'search-section' => '(раздел «$1»)',
@@ -1540,7 +1540,7 @@ $1",
 'timezoneregion-europe' => 'Европа',
 'timezoneregion-indian' => 'Индийский океан',
 'timezoneregion-pacific' => 'Тихий океан',
-'allowemail' => 'Разрешить приём электронной почты от других участников',
+'allowemail' => 'Разрешить получение электронной почты от других участников',
 'prefs-searchoptions' => 'Поиск',
 'prefs-namespaces' => 'Пространства имён',
 'defaultns' => 'Иначе искать в следующих пространствах имён:',
@@ -1554,7 +1554,7 @@ $1",
 'prefs-emailconfirm-label' => 'Подтверждение электронной почты:',
 'prefs-textboxsize' => 'Размер окна редактирования',
 'youremail' => 'Электронная почта:',
-'username' => 'РегиÑ\81Ñ\82Ñ\80аÑ\86ионное Ð¸Ð¼Ñ\8f:',
+'username' => 'Ð\98мÑ\8f Ñ\83Ñ\87Ñ\91Ñ\82ной Ð·Ð°Ð¿Ð¸Ñ\81и:',
 'uid' => 'Идентификатор участника:',
 'prefs-memberingroups' => 'Член {{PLURAL:$1|группы|групп}}:',
 'prefs-registration' => 'Время регистрации:',
@@ -1596,22 +1596,22 @@ $1",
 
 # User preference: e-mail validation using jQuery
 'email-address-validity-valid' => 'Выглядит корректно',
-'email-address-validity-invalid' => 'ТÑ\80ебÑ\83еÑ\82Ñ\81Ñ\8f ÐºÐ¾Ñ\80Ñ\80екÑ\82нÑ\8bй Ð°Ð´Ñ\80еÑ\81!',
+'email-address-validity-invalid' => 'Ð\92ведиÑ\82е ÐºÐ¾Ñ\80Ñ\80екÑ\82нÑ\8bй Ð°Ð´Ñ\80еÑ\81 Ñ\8dлекÑ\82Ñ\80онной Ð¿Ð¾Ñ\87Ñ\82Ñ\8b!',
 
 # User rights
 'userrights' => 'Управление правами участника',
 'userrights-lookup-user' => 'Управление группами участников',
-'userrights-user-editname' => 'Введите имя участника:',
+'userrights-user-editname' => 'Введите имя учётной записи:',
 'editusergroup' => 'Изменить членство в группах',
 'editinguser' => "Изменение прав {{GENDER:$1|участника|участницы}} '''[[User:$1|$1]]''' $2",
 'userrights-editusergroup' => 'Изменение членства в группах',
 'saveusergroups' => 'Сохранить группы участника',
-'userrights-groupsmember' => 'Член Ð³Ñ\80Ñ\83пп:',
-'userrights-groupsmember-auto' => 'Неявный член:',
+'userrights-groupsmember' => 'СоÑ\81Ñ\82оиÑ\82 Ð² Ð³Ñ\80Ñ\83ппаÑ\85:',
+'userrights-groupsmember-auto' => 'Неявно состоит в группах:',
 'userrights-groups-help' => 'Вы можете изменить группы, в которые входит этот участник.
 * Если около названия группы стоит отметка, значит участник входит в эту группу.
 * Если отметка не стоит — участник не относится к соответствующей группе.
-* Знак * отмечает, что вы не можете удалить из группы участника, если добавите его в неё или наоборот.',
+* Знак * отмечает, что вы не сможете удалить участника из группы, если добавите его в неё, или наоборот.',
 'userrights-reason' => 'Причина:',
 'userrights-no-interwiki' => 'У вас нет разрешения изменять права участников на других вики.',
 'userrights-nodatabase' => 'База данных $1 не существует или не является локальной.',
@@ -1677,7 +1677,7 @@ $1",
 'right-undelete' => 'восстановление страниц',
 'right-suppressrevision' => 'просмотр и восстановление скрытых от администраторов версий страниц',
 'right-suppressionlog' => 'просмотр частных журналов',
-'right-block' => 'Ñ\83Ñ\81Ñ\82ановка Ð·Ð°Ð¿Ñ\80еÑ\82а Ð½Ð° Ñ\80едакÑ\82иÑ\80ование Ð´Ñ\80Ñ\83гим Ñ\83Ñ\87аÑ\81Ñ\82никам',
+'right-block' => 'Ñ\83Ñ\81Ñ\82ановка Ð¾Ð³Ñ\80аниÑ\87ений Ð½Ð° Ñ\80едакÑ\82иÑ\80ование Ð´Ð»Ñ\8f Ð´Ñ\80Ñ\83гиÑ\85 Ñ\83Ñ\87аÑ\81Ñ\82ников',
 'right-blockemail' => 'установка запрета на отправку электронной почты',
 'right-hideuser' => 'запрет имени участника и его сокрытие',
 'right-ipblock-exempt' => 'обход блокировок по IP, автоблокировок и блокировок диапазонов',
@@ -1739,9 +1739,9 @@ $1",
 'action-undelete' => 'восстановление этой страницы',
 'action-suppressrevision' => 'просмотр и восстановление этой скрытой версии страницы',
 'action-suppressionlog' => 'просмотр этого частного журнала',
-'action-block' => 'блокиÑ\80овка участника',
+'action-block' => 'огÑ\80аниÑ\87иваÑ\82Ñ\8c Ð²Ð¾Ð·Ð¼Ð¾Ð¶Ð½Ð¾Ñ\81Ñ\82Ñ\8c Ñ\80едакÑ\82иÑ\80ованиÑ\8f Ð´Ð»Ñ\8f Ñ\8dÑ\82ого участника',
 'action-protect' => 'изменение уровня защиты этой страницы',
-'action-rollback' => 'быстрый откат изменений последнего пользователя, который редактировал страницу',
+'action-rollback' => 'быстрый откат изменений участника, который последним редактировал страницу',
 'action-import' => 'импорт этой страницы из другой вики',
 'action-importupload' => 'импорт этой страницы из загруженного файла',
 'action-patrol' => 'отметка чужих правок как отпатрулированных',
@@ -1758,13 +1758,13 @@ $1",
 'recentchanges' => 'Свежие правки',
 'recentchanges-legend' => 'Настройки свежих правок',
 'recentchanges-summary' => 'Ниже в хронологическом порядке перечислены последние изменения на страницах {{grammar:genitive|{{SITENAME}}}}.',
-'recentchanges-feed-description' => 'Ð\9eÑ\82Ñ\81леживаÑ\82Ñ\8c Ð¿Ð¾Ñ\81ледние Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f Ð² Ð²Ð¸ÐºÐ¸ Ð² Ñ\8dÑ\82ом Ð¿Ð¾Ñ\82оке.',
+'recentchanges-feed-description' => 'Ð\9eÑ\82Ñ\81леживаÑ\82Ñ\8c Ð² Ñ\8dÑ\82ом Ð¿Ð¾Ñ\82оке Ð¿Ð¾Ñ\81ледние Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ\8f Ð² Ð²Ð¸ÐºÐ¸.',
 'recentchanges-label-newpage' => 'Этой правкой была создана новая страница.',
 'recentchanges-label-minor' => 'Это незначительное изменение',
 'recentchanges-label-bot' => 'Эта правка сделана ботом',
-'recentchanges-label-unpatrolled' => 'Эту правку ещё не отпатрулировали',
+'recentchanges-label-unpatrolled' => 'Эта правку ещё никем не патрулировалась',
 'rcnote' => "{{PLURAL:$1|Последнее '''$1''' изменение|Последние '''$1''' изменения|Последние '''$1''' изменений}} за '''$2''' {{PLURAL:$2|день|дня|дней}}, на момент времени $5 $4.",
-'rcnotefrom' => 'Ниже перечислены изменения с <strong>$2</strong> (по <strong>$1</strong>).',
+'rcnotefrom' => "Ниже перечислены изменения с '''$2''' (не более '''$1''').",
 'rclistfrom' => 'Показать изменения с $1.',
 'rcshowhideminor' => '$1 малые правки',
 'rcshowhidebots' => '$1 ботов',
@@ -2684,7 +2684,7 @@ $1',
 'mycontris' => 'Вклад',
 'contribsub2' => 'Вклад $1 ($2)',
 'nocontribs' => 'Изменений, соответствующих заданным условиям, найдено не было.',
-'uctop' => ' (последняя)',
+'uctop' => '(последняя)',
 'month' => 'С месяца (и ранее):',
 'year' => 'С года (и ранее):',
 
@@ -2715,8 +2715,8 @@ $1',
 'isredirect' => 'страница-перенаправление',
 'istemplate' => 'включение',
 'isimage' => 'файловая ссылка',
-'whatlinkshere-prev' => '{{PLURAL:$1|предыдущая|предыдущие|предыдущие}} $1',
-'whatlinkshere-next' => '{{PLURAL:$1|следующая|следующие|следующие}} $1',
+'whatlinkshere-prev' => '{{PLURAL:$1|предыдущая|предыдущие}} $1',
+'whatlinkshere-next' => '{{PLURAL:$1|следующая|следующие}} $1',
 'whatlinkshere-links' => '← ссылки',
 'whatlinkshere-hideredirs' => '$1 перенаправления',
 'whatlinkshere-hidetrans' => '$1 включения',
index 4a26e3a..c7d4211 100644 (file)
@@ -1648,7 +1648,7 @@ Musí obsahovať menej ako $1 {{PLURAL:$1|znak|znaky|znakov}}.',
 'hist' => 'história',
 'hide' => 'skryť',
 'show' => 'zobraziť',
-'minoreditletter' => 'D',
+'minoreditletter' => 'd',
 'newpageletter' => 'N',
 'boteditletter' => 'b',
 'number_of_watching_users_pageview' => '[$1 {{PLURAL:$1|sledujúci používateľ|sledujúci používatelia|sledujúcich používateľov}}]',
index 69f6912..29c46a0 100644 (file)
@@ -3848,6 +3848,7 @@ Du kan också [[Special:EditWatchlist|använda standardeditorn]].',
 'version-license' => 'Licens',
 'version-poweredby-credits' => "Den här wikin drivs av '''[//www.mediawiki.org/ MediaWiki]''', copyright © 2001-$1 $2.",
 'version-poweredby-others' => 'andra',
+'version-credits-summary' => 'Vi skulle vilja tacka följande personer för deras bidrag till [[Special:Version|MediaWiki]].',
 'version-license-info' => 'MediaWiki är fri programvara; du kan distribuera det och/eller modifiera det under villkoren i GNU General Public License, publicerad av Free Software Foundation; antingen version 2 av licensen, eller (om du önskar) någon senare version. 
 
 MediaWiki distribueras i hopp om att det ska vara användbart, men UTAN NÅGON GARANTI, även utan underförstådd garanti om SÄLJBARHET eller LÄMPLIGHET FÖR ETT VISST SYFTE. Se GNU General Public License för fler detaljer. 
index 143affb..a2ca3c0 100644 (file)
@@ -481,8 +481,8 @@ $messages = array(
 'newwindow' => '(yeni bir pencerede açılır)',
 'cancel' => 'İptal',
 'moredotdotdot' => 'Daha...',
-'mypage' => 'sayfam',
-'mytalk' => 'Mesaj sayfam',
+'mypage' => 'Sayfa',
+'mytalk' => 'Tartışma',
 'anontalk' => "Bu IP'nin iletileri",
 'navigation' => 'Gezinti',
 'and' => '&#32;ve',
@@ -1391,7 +1391,7 @@ Aramanızın başına '''all:''' önekini ekleyerek tüm içeriği aramayı (tar
 
 # Preferences page
 'preferences' => 'Tercihler',
-'mypreferences' => 'Tercihlerim',
+'mypreferences' => 'Tercihler',
 'prefs-edits' => 'Değişiklik sayısı:',
 'prefsnologin' => 'Oturum açık değil',
 'prefsnologintext' => 'Kullanıcı tercihlerinizi ayarlamak için <span class="plainlinks">[{{fullurl:{{#Special:UserLogin}}|returnto=$1}} giriş yapmalısınız]</span>.',
@@ -2285,7 +2285,7 @@ Bireysel haklarla ilgili [[{{MediaWiki:Listgrouprights-helppage}}|daha fazla bil
 
 # Watchlist
 'watchlist' => 'İzleme listem',
-'mywatchlist' => 'İzleme listem',
+'mywatchlist' => 'İzleme listesi',
 'watchlistfor2' => '$1 için $2',
 'nowatchlist' => 'İzleme listesinde hiçbir madde bulunmuyor.',
 'watchlistanontext' => 'Lütfen izleme listenizdeki maddeleri görmek ya da değiştirmek için $1.',
@@ -2542,7 +2542,7 @@ $1',
 # Contributions
 'contributions' => 'Kullanıcının katkıları',
 'contributions-title' => '$1 için kullanıcı katkıları',
-'mycontris' => 'Katkılarım',
+'mycontris' => 'Katkılar',
 'contribsub2' => '$1 ($2)',
 'nocontribs' => 'Bu kriterlere uyan değişiklik bulunamadı',
 'uctop' => '(son)',
index 3e60870..a7654c3 100644 (file)
@@ -429,8 +429,8 @@ $messages = array(
 'newwindow' => '(mở cửa sổ mới)',
 'cancel' => 'Hủy bỏ',
 'moredotdotdot' => 'Thêm nữa…',
-'mypage' => 'Trang của tôi',
-'mytalk' => 'Thảo luận với tôi',
+'mypage' => 'Trang cá nhân',
+'mytalk' => 'Thảo luận',
 'anontalk' => 'Thảo luận với IP này',
 'navigation' => 'Xem nhanh',
 'and' => '&#32;và',
@@ -2294,7 +2294,7 @@ Có [[{{MediaWiki:Listgrouprights-helppage}}|thông tin thêm]] về từng nhó
 
 # Watchlist
 'watchlist' => 'Trang tôi theo dõi',
-'mywatchlist' => 'Trang tôi theo dõi',
+'mywatchlist' => 'Trang theo dõi',
 'watchlistfor2' => 'Của $1 $2',
 'nowatchlist' => 'Danh sách theo dõi của bạn không có gì.',
 'watchlistanontext' => 'Xin hãy $1 để xem hay sửa đổi các trang được theo dõi.',
@@ -2553,7 +2553,7 @@ $1',
 # Contributions
 'contributions' => 'Đóng góp của thành viên',
 'contributions-title' => 'Đóng góp của thành viên $1',
-'mycontris' => 'Đóng góp của tôi',
+'mycontris' => 'Đóng góp',
 'contribsub2' => 'Của $1 ($2)',
 'nocontribs' => 'Không tìm thấy thay đổi nào khớp với yêu cầu.',
 'uctop' => '(mới nhất)',
index 09beb54..e68ad03 100644 (file)
@@ -317,7 +317,7 @@ $messages = array(
 'cancel' => 'זיי מבטל',
 'moredotdotdot' => 'נאך…',
 'mypage' => 'מײַן בלאט',
-'mytalk' => '×\9eײַ×\9f ×©×\9e×\95עס',
+'mytalk' => 'שמועס',
 'anontalk' => 'דאס רעדן פון דעם IP',
 'navigation' => 'נאַוויגאַציע',
 'and' => '&#32;און',
@@ -2102,7 +2102,7 @@ $1",
 
 # Watchlist
 'watchlist' => 'מיין אויפפַּאסונג ליסטע',
-'mywatchlist' => '×\9e×\99×\99×\9f ×\90×\95×\99פפַּ×\90ס×\95× ×\92 ×\9c×\99ס×\98×¢',
+'mywatchlist' => 'אויפפַּאסונג ליסטע',
 'watchlistfor2' => 'פֿאַר $1 $2',
 'nowatchlist' => 'איר האט נישט קיין שום בלעטער אין אייער אויפפַּאסונג ליסטע.',
 'watchlistanontext' => 'ביטע $1 כדי צו זען אדער ענדערן בלעטער אין אייער אַכטגעבן ליסטע.',
@@ -2352,7 +2352,7 @@ $1',
 # Contributions
 'contributions' => "באניצער'ס בײַשטײַערונגען",
 'contributions-title' => 'בײַשטײַערונגען פֿון באַניצער $1',
-'mycontris' => '×\9eײַנע ×\91ײַש×\98ײַער×\95× ×\92×¢×\9f',
+'mycontris' => 'בײַשטײַערונגען',
 'contribsub2' => 'וועגן $1 ($2)',
 'nocontribs' => 'נישט געטראפן קיין ענדערונגען צוזאמעגעפאסט מיט די קריטעריעס.',
 'uctop' => '(לעצטע)',
@@ -2392,7 +2392,7 @@ $1',
 'whatlinkshere-hideredirs' => '$1 ווײַטערפֿירונגען',
 'whatlinkshere-hidetrans' => '$1 אַריבערשליסונגען',
 'whatlinkshere-hidelinks' => '$1 פֿאַרבינדונגען',
-'whatlinkshere-hideimages' => '$1 ×\91×\99×\9c×\93ער פֿאַרבינדונגען',
+'whatlinkshere-hideimages' => '$1 ×\98עקע פֿאַרבינדונגען',
 'whatlinkshere-filters' => 'פֿילטערס',
 
 # Block/unblock
@@ -2862,6 +2862,7 @@ $1',
 
 # Info page
 'pageinfo-title' => 'אינפֿאָרמאַציע פֿאַר "$1"',
+'pageinfo-not-current' => 'קען ווייזן אינפארמאציע נאר פאר דער לויפיקער רעוויזיע.',
 'pageinfo-header-basic' => 'גרונטלעכע אינפֿארמאַציע',
 'pageinfo-header-edits' => '!רעדאַקטירן היסטאריע',
 'pageinfo-header-restrictions' => 'בלאט באַשיצונג',
@@ -2871,6 +2872,8 @@ $1',
 'pageinfo-length' => 'בלאט לענג (אין בייטן)',
 'pageinfo-article-id' => 'בלאט נומער',
 'pageinfo-robot-policy' => 'זוכמאשין סטאטוס',
+'pageinfo-robot-index' => 'אינדעקסירבאר',
+'pageinfo-robot-noindex' => 'נישט אינדעקסירבאר',
 'pageinfo-views' => 'צאַל קוקן',
 'pageinfo-watchers' => '!צאָל בלאט אויפֿפאַסער',
 'pageinfo-redirects-name' => 'ווײַטערפירונגען צו דעם בלאט',
@@ -3015,6 +3018,11 @@ $1',
 'exif-orientation' => 'אריענטאַציע',
 'exif-samplesperpixel' => 'צאל קאמאפאנענטן',
 'exif-planarconfiguration' => 'דאטן איינארדנונג',
+'exif-xresolution' => 'האריזאנטאלע רעזאלוציע',
+'exif-yresolution' => 'ווערטיקאלע רעזאלוציע',
+'exif-stripoffsets' => 'בילדדאטן פלאציר',
+'exif-rowsperstrip' => 'צאל שורות אין א שטרייף',
+'exif-stripbytecounts' => 'בייטן אין א קאמפרימירטן שטרייף',
 'exif-jpeginterchangeformatlength' => 'בייטן פון JPEG דאטן',
 'exif-datetime' => 'טעקע ענדערונג דאטע און צײַט',
 'exif-imagedescription' => 'בילד טיטל',
index 75018de..875b93d 100644 (file)
@@ -38,21 +38,31 @@ class nextJobDB extends Maintenance {
 
        public function execute() {
                global $wgMemc;
+
                $type = $this->getOption( 'type', false );
 
-               $memcKey = 'jobqueue:dbs:v2';
-               $pendingDBs = $wgMemc->get( $memcKey );
+               $memcKey = 'jobqueue:dbs:v3';
+               $pendingDbInfo = $wgMemc->get( $memcKey );
 
                // If the cache entry wasn't present, or in 1% of cases otherwise,
-               // regenerate the cache.
-               if ( !$pendingDBs || mt_rand( 0, 100 ) == 0 ) {
-                       $pendingDBs = $this->getPendingDbs();
-                       $wgMemc->set( $memcKey, $pendingDBs, 300 );
+               // regenerate the cache. Use any available stale cache if another
+               // process is currently regenerating the pending DB information.
+               if ( !$pendingDbInfo || mt_rand( 0, 100 ) == 0 ) {
+                       $lock = $wgMemc->add( 'jobqueue:dbs:v3:lock', 1, 1800 ); // lock
+                       if ( $lock ) {
+                               $pendingDbInfo = array(
+                                       'pendingDBs' => $this->getPendingDbs(),
+                                       'timestamp'  => time()
+                               );
+                               $wgMemc->set( $memcKey, $pendingDbInfo );
+                               $wgMemc->delete( 'jobqueue:dbs:v3:lock' ); // unlock
+                       }
                }
 
-               if ( !$pendingDBs ) {
-                       return;
+               if ( !$pendingDbInfo || !$pendingDbInfo['pendingDBs'] ) {
+                       return; // no DBs with jobs or cache is both empty and locked
                }
+               $pendingDBs = $pendingDbInfo['pendingDBs'];
 
                do {
                        $again = false;
@@ -97,24 +107,17 @@ class nextJobDB extends Maintenance {
         * @return bool
         */
        function checkJob( $type, $dbName ) {
-               global $wgJobTypesExcludedFromDefaultQueue;
-
+               $group = JobQueueGroup::singleton( $dbName );
                if ( $type === false ) {
-                       $lb = wfGetLB( $dbName );
-                       $db = $lb->getConnection( DB_MASTER, array(), $dbName );
-                       $conds = array();
-                       if ( count( $wgJobTypesExcludedFromDefaultQueue ) > 0 ) {
-                               foreach ( $wgJobTypesExcludedFromDefaultQueue as $cmdType ) {
-                                       $conds[] = "job_cmd != " . $db->addQuotes( $cmdType );
+                       foreach ( $group->getDefaultQueueTypes() as $type ) {
+                               if ( !$group->get( $type )->isEmpty() ) {
+                                       return true;
                                }
                        }
-                       $exists = (bool)$db->selectField( 'job', '1', $conds, __METHOD__ );
-                       $lb->reuseConnection( $db );
+                       return false;
                } else {
-                       $exists = !JobQueueGroup::singleton( $dbName )->get( $type )->isEmpty();
+                       return !$group->get( $type )->isEmpty();
                }
-
-               return $exists;
        }
 
        /**
@@ -123,42 +126,15 @@ class nextJobDB extends Maintenance {
         */
        private function getPendingDbs() {
                global $wgLocalDatabases;
-               $pendingDBs = array();
-               # Cross-reference DBs by master DB server
-               $dbsByMaster = array();
-               foreach ( $wgLocalDatabases as $db ) {
-                       $lb = wfGetLB( $db );
-                       $dbsByMaster[$lb->getServerName( 0 )][] = $db;
-               }
-
-               foreach ( $dbsByMaster as $dbs ) {
-                       $dbConn = wfGetDB( DB_MASTER, array(), $dbs[0] );
 
-                       # Padding row for MySQL bug
-                       $pad = str_repeat( '-', 40 );
-                       $sql = "(SELECT '$pad' as db, '$pad' as job_cmd)";
-                       foreach ( $dbs as $wikiId ) {
-                               if ( $sql != '' ) {
-                                       $sql .= ' UNION ';
-                               }
-
-                               list( $dbName, $tablePrefix ) = wfSplitWikiID( $wikiId );
-                               $dbConn->tablePrefix( $tablePrefix );
-                               $jobTable = $dbConn->tableName( 'job' );
-
-                               $sql .= "(SELECT DISTINCT '$wikiId' as db, job_cmd FROM $dbName.$jobTable GROUP BY job_cmd)";
-                       }
-                       $res = $dbConn->query( $sql, __METHOD__ );
-                       $first = true;
-                       foreach ( $res as $row ) {
-                               if ( $first ) {
-                                       // discard padding row
-                                       $first = false;
-                                       continue;
-                               }
-                               $pendingDBs[$row->job_cmd][] = $row->db;
+               $pendingDBs = array(); // (job type => (db list))
+               foreach ( $wgLocalDatabases as $db ) {
+                       $types = JobQueueGroup::singleton( $db )->getQueuesWithJobs();
+                       foreach ( $types as $type ) {
+                               $pendingDBs[$type][] = $db;
                        }
                }
+
                return $pendingDBs;
        }
 }
index 04495b7..9829747 100644 (file)
@@ -1,8 +1,6 @@
 /**
  * jQuery Badge plugin
  *
- * Based on Badger plugin by Daniel Raftery (http://thrivingkings.com/badger).
- *
  * @license MIT
  */
 
  * This program is distributed WITHOUT ANY WARRANTY.
  */
 ( function ( $ ) {
-
        /**
-        * Allows you to put a numeric "badge" on an item on the page.
+        * Allows you to put a "badge" on an item on the page. The badge container
+        * will be appended to the selected element(s).
         * See mediawiki.org/wiki/ResourceLoader/Default_modules#jQuery.badge
         *
-        * @param {string|number} badgeCount An explicit number, or "+n"/ "-n"
-        *  to modify the existing value. If the new value is equal or lower than 0,
-        *  any existing badge will be removed. The badge container will be appended
-        *  to the selected element(s).
-        * @param {Object} options Optional parameters specified below
-        *   type: 'inline' or 'overlay' (default)
-        *   callback: will be called with the number now shown on the badge as a parameter
+        * @param text The value to display in the badge. If the value is falsey (0,
+        *   null, false, '', etc.), any existing badge will be removed.
+        * @param boolean inline True if the badge should be displayed inline, false
+        *   if the badge should overlay the parent element (default is inline)
         */
-       $.fn.badge = function ( badgeCount, options ) {
-               var $badge,
-                       oldBadgeCount,
-                       newBadgeCount,
-                       $existingBadge = this.find( '.mw-badge' );
-
-               options = $.extend( { type : 'overlay' }, options );
-
-               // If there is no existing badge, this will give an empty string
-               oldBadgeCount = Number( $existingBadge.text() );
-               if ( isNaN( oldBadgeCount ) ) {
-                       oldBadgeCount = 0;
-               }
+       $.fn.badge = function ( text, inline ) {
+               var div, $badge = this.find( '.mw-badge' );
 
-               // If badgeCount is a number, use that as the new badge
-               if ( typeof badgeCount === 'number' ) {
-                       newBadgeCount = badgeCount;
-               } else if ( typeof badgeCount === 'string' ) {
-                       // If badgeCount is "+x", add x to the old badge
-                       if ( badgeCount.charAt(0) === '+' ) {
-                               newBadgeCount = oldBadgeCount + Number( badgeCount.substr(1) );
-                       // If badgeCount is "-x", subtract x from the old badge
-                       } else if ( badgeCount.charAt(0) === '-' ) {
-                               newBadgeCount = oldBadgeCount - Number( badgeCount.substr(1) );
-                       // If badgeCount can be converted into a number, convert it
-                       } else if ( !isNaN( Number( badgeCount ) ) ) {
-                               newBadgeCount = Number( badgeCount );
+               if ( text ) {
+                       // If a badge already exists, reuse it
+                       if ( $badge.length ) {
+                               $badge.find( '.mw-badge-content' ).text( text );
                        } else {
-                               newBadgeCount = 0;
+                               // Otherwise, create a new badge with the specified text and style
+                               div = document.createElement( 'div' );
+                               div.className = 'mw-badge mw-badge-' + ( inline ? 'inline' : 'overlay' );
+                               div.innerHTML = '<span class="mw-badge-content">' + text + '</span>';
+                               $( div ).appendTo( this );
                        }
-               // Other types are not supported, fall back to 0.
-               } else {
-                       newBadgeCount = 0;
-               }
-
-               // Badge count must be a whole number
-               newBadgeCount = Math.round( newBadgeCount );
-
-               if ( newBadgeCount <= 0 ) {
-                       // Badges should only exist for values > 0.
-                       $existingBadge.remove();
                } else {
-                       // Don't add duplicates
-                       if ( $existingBadge.length ) {
-                               $badge = $existingBadge;
-                               // Insert the new count into the badge
-                               this.find( '.mw-badge-content' ).text( newBadgeCount );
-                       } else {
-                               // Contruct a new badge with the count
-                               $badge = $( '<div>' )
-                                       .addClass( 'mw-badge' )
-                                       .append(
-                                               $( '<span>' )
-                                                       .addClass( 'mw-badge-content' )
-                                                       .text( newBadgeCount )
-                                       );
-                               this.append( $badge );
-                       }
-
-                       if ( options.type === 'inline' ) {
-                               $badge
-                                       .removeClass( 'mw-badge-overlay' )
-                                       .addClass( 'mw-badge-inline' );
-                       // Default: overlay
-                       } else {
-                               $badge
-                                       .removeClass( 'mw-badge-inline' )
-                                       .addClass( 'mw-badge-overlay' );
-
-                       }
-
-                       // If a callback was specified, call it with the badge count
-                       if ( $.isFunction( options.callback ) ) {
-                               options.callback( newBadgeCount );
-                       }
+                       $badge.remove();
                }
+               return this;
        };
 }( jQuery ) );
index 7fbca41..227d18d 100644 (file)
@@ -361,7 +361,7 @@ class VectorTemplate extends BaseTemplate {
         * @param $elements array
         */
        protected function renderNavigation( $elements ) {
-               global $wgVectorUseSimpleSearch;
+               global $wgVectorUseSimpleSearch, $wgVectorCombineUserTalk;
 
                // If only one element was given, wrap it in an array, allowing more
                // flexible arguments
@@ -444,10 +444,26 @@ class VectorTemplate extends BaseTemplate {
 <div id="p-personal" class="<?php if ( count( $this->data['personal_urls'] ) == 0 ) echo ' emptyPortlet'; ?>">
        <h5><?php $this->msg( 'personaltools' ) ?></h5>
        <ul<?php $this->html( 'userlangattributes' ) ?>>
-<?php                  foreach( $this->getPersonalTools() as $key => $item ) { ?>
-               <?php echo $this->makeListItem( $key, $item ); ?>
-
-<?php                  } ?>
+<?php
+                                       $personalTools = $this->getPersonalTools();
+                                       if ( $wgVectorCombineUserTalk && isset( $personalTools['userpage'] ) ) {
+?>
+               <li>
+<?php
+                                               echo $this->makeListItem( 'userpage', $personalTools['userpage'], array( 'tag' => 'span' ) );
+?> <?php
+                                               $personalTools['mytalk']['links'][0]['text'] = $this->getMsg( 'mytalk-parenthetical' )->text();
+                                               $talkItem = $this->makeListItem( 'mytalk', $personalTools['mytalk'], array( 'tag' => 'span' ) );
+                                               echo $this->getMsg( 'parentheses' )->rawParams( $talkItem )->escaped();
+                                               unset( $personalTools['userpage'], $personalTools['mytalk'] );
+?>
+               </li>
+<?php
+                                       }
+                                       foreach ( $personalTools as $key => $item ) {
+                                               echo $this->makeListItem( $key, $item );
+                                       }
+?>
        </ul>
 </div>
 <?php
index 8ed24bb..7a835d6 100644 (file)
@@ -43,6 +43,9 @@ $wgAutoloadClasses += array(
        'RandomImageGenerator' => "$testFolder/phpunit/includes/api/RandomImageGenerator.php",
        'UserWrapper' => "$testFolder/phpunit/includes/api/ApiTestCase.php",
 
+       //db
+       'ORMTableTest' => "$testFolder/phpunit/includes/db/ORMTableTest.php",
+
        //Selenium
        'SeleniumTestConstants' => "$testFolder/selenium/SeleniumTestConstants.php",
 
index 2fa8a50..4690e1a 100644 (file)
@@ -95,6 +95,13 @@ Template:attr_str
 {{{1}}}="{{{2}}}"
 !! endarticle
 
+!! article
+Template:table_attribs
+!! text
+<noinclude>
+|</noinclude>style="color: red"| Foo
+!! endarticle
+
 ###
 ### Basic tests
 ###
@@ -914,6 +921,15 @@ Bug 6200: Preformatted in <blockquote>
 </p>
 !! end
 
+!! test
+</pre> inside nowiki
+!! input
+<nowiki></pre></nowiki>
+!! result
+<p>&lt;/pre&gt;
+</p>
+!! end
+
 !!test
 Templates: Pre: 1a. Templates that break a line should suppress <pre>
 !!input
@@ -1033,6 +1049,55 @@ Templates: Pre: 1f: Wrapping should be based on expanded content
 </pre>
 !!end
 
+!! test
+Templates: Single-line variant of parameter whitespace stripping test
+!! input
+{{echo| a}}
+
+{{echo|1= a}}
+
+{{echo|{{echo| a}}}}
+
+{{echo|1={{echo| a}}}}
+!! result
+<pre>a
+</pre>
+<p>a
+</p>
+<pre>a
+</pre>
+<p>a
+</p>
+!! end
+
+!! test
+Templates: Strip whitespace from named parameters, but not positional ones
+!! input
+{{echo|
+ foo}}
+
+{{echo|
+* foo}}
+
+{{echo| 1 =
+ foo}}
+
+{{echo| 1 =
+* foo}}
+!! result
+<pre>foo
+</pre>
+<p><br />
+</p>
+<ul><li> foo
+</li></ul>
+<p>foo
+</p>
+<ul><li> foo
+</li></ul>
+
+!! end
+
 ###
 ### Parsoid-centric tests for testing RT edge cases for pre
 ###
@@ -2204,6 +2269,15 @@ http:/example.com
 </p>
 !! end
 
+!! test
+Bracketed external links with template-generated invalid target
+!! input
+[{{echo|http:/example.com}} title]
+!! result
+<p>[http:/example.com title]
+</p>
+!! end
+
 !! test
 Bug 2702: Mismatched <i>, <b> and <a> tags are invalid
 !! input
@@ -2707,6 +2781,19 @@ Indented table markup mixed with indented pre content (proposed in bug 6200)
 
 !! end
 
+!! test
+Template-generated table cell attributes and cell content
+!! input
+{|
+|{{table_attribs}}
+|}
+!! result
+<table>
+<tr>
+<td style="color: red"> Foo
+</td></tr></table>
+
+!! end
 
 ###
 ### Internal links
@@ -3254,6 +3341,15 @@ language=ln
 </p>
 !! end
 
+!! test
+Broken br tag sanitization
+!! input
+</br>
+!! result
+<p>&lt;/br&gt;
+</p>
+!! end
+
 !! test
 Incorrecly removing closing slashes from correctly formed XHTML
 !! input
@@ -10331,6 +10427,29 @@ Bug 529: Uncovered bullet
 
 !! end
 
+# Plain MediaWiki does not remove empty lists, but tidy actually does.
+# Templates in Wikipedia rely on this behavior, as tidy has always been
+# enabled there. These tests are normally run *without* tidy, so specify the
+# full output here. 
+# To test realistic parsing behavior, apply a tidy-like transformation to both
+# the expected output and your parser's output.
+!! test
+Bug 529: Uncovered bullet leaving empty list, normally removed by tidy
+!! input
+******* Foo {{bullet}}
+!! result
+<ul><li><ul><li><ul><li><ul><li><ul><li><ul><li><ul><li> Foo 
+</li></ul>
+</li></ul>
+</li></ul>
+</li></ul>
+</li></ul>
+</li></ul>
+</li><li> Bar
+</li></ul>
+
+!! end
+
 !! test
 Bug 529: Uncovered table already at line-start
 !! input
index 0a3f6f6..8647954 100644 (file)
@@ -126,11 +126,11 @@ class TitleTest extends MediaWikiTestCase {
                # - wgDefaultLanguageVariant
                # - Optional message
                return array(
-                       array( 'fr', 'Main_page', 'fr', 'fr', false ),
-                       array( 'es', 'Main_page', 'es', 'zh-tw', false ),
-                       array( 'zh', 'Main_page', 'zh', 'zh-tw', false ),
+                       array( 'fr', 'Help:I_need_somebody', 'fr', 'fr', false ),
+                       array( 'es', 'Help:I_need_somebody', 'es', 'zh-tw', false ),
+                       array( 'zh', 'Help:I_need_somebody', 'zh', 'zh-tw', false ),
 
-                       array( 'es',    'Main_page',                 'es', 'zh-tw', 'zh-cn' ),
+                       array( 'es',    'Help:I_need_somebody',      'es', 'zh-tw', 'zh-cn' ),
                        array( 'es',    'MediaWiki:About',           'es', 'zh-tw', 'zh-cn' ),
                        array( 'es',    'MediaWiki:About/',          'es', 'zh-tw', 'zh-cn' ),
                        array( 'de',    'MediaWiki:About/de',        'es', 'zh-tw', 'zh-cn' ),
@@ -139,7 +139,7 @@ class TitleTest extends MediaWikiTestCase {
                        array( 'en',    'User:JohnDoe/Common.js',    'es', 'zh-tw', 'zh-cn' ),
                        array( 'en',    'User:JohnDoe/Monobook.css', 'es', 'zh-tw', 'zh-cn' ),
 
-                       array( 'zh-cn', 'Main_page',                 'zh', 'zh-tw', 'zh-cn' ),
+                       array( 'zh-cn', 'Help:I_need_somebody',      'zh', 'zh-tw', 'zh-cn' ),
                        array( 'zh',    'MediaWiki:About',           'zh', 'zh-tw', 'zh-cn' ),
                        array( 'zh',    'MediaWiki:About/',          'zh', 'zh-tw', 'zh-cn' ),
                        array( 'de',    'MediaWiki:About/de',        'zh', 'zh-tw', 'zh-cn' ),
index bf9468a..e5a014d 100644 (file)
@@ -173,7 +173,7 @@ class UserTest extends MediaWikiTestCase {
                $user->addToDatabase();
 
                // let the user have a few (3) edits
-               $page = WikiPage::factory( Title::newFromText( 'UserTest_EditCount' ) );
+               $page = WikiPage::factory( Title::newFromText( 'Help:UserTest_EditCount' ) );
                for( $i = 0; $i < 3; $i++ ) {
                        $page->doEdit( (string) $i, 'test', 0, false, $user );
                }
diff --git a/tests/phpunit/includes/db/ORMTableTest.php b/tests/phpunit/includes/db/ORMTableTest.php
new file mode 100644 (file)
index 0000000..2ed3dd3
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Abstract class to construct tests for ORMTable deriving classes.
+ *
+ * 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.21
+ *
+ * @ingroup Test
+ *
+ * @group ORM
+ *
+ * @licence GNU GPL v2+
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ */
+abstract class ORMTableTest extends MediaWikiTestCase {
+
+       /**
+        * @since 1.21
+        * @return string
+        */
+       protected abstract function getTableClass();
+
+       /**
+        * @since 1.21
+        * @return IORMTable
+        */
+       public function getTable() {
+               $class = $this->getTableClass();
+               return $class::singleton();
+       }
+
+       /**
+        * @since 1.21
+        * @return string
+        */
+       public function getRowClass() {
+               return $this->getTable()->getRowClass();
+       }
+
+       /**
+        * @since 1.21
+        */
+       public function testSingleton() {
+               $class = $this->getTableClass();
+
+               $this->assertInstanceOf( $class, $class::singleton() );
+               $this->assertTrue( $class::singleton() === $class::singleton() );
+       }
+
+}
index da36e90..b6d44fd 100644 (file)
@@ -1181,6 +1181,50 @@ class FileBackendTest extends MediaWikiTestCase {
                $this->assertEquals( null, $tmpFile, "Local ref of not existing file is null ($backendName)." );
        }
 
+       /**
+        * @dataProvider provider_testGetFileHttpUrl
+        */
+       public function testGetFileHttpUrl( $source, $content ) {
+               $this->backend = $this->singleBackend;
+               $this->tearDownFiles();
+               $this->doTestGetFileHttpUrl( $source, $content );
+               $this->tearDownFiles();
+
+               $this->backend = $this->multiBackend;
+               $this->tearDownFiles();
+               $this->doTestGetFileHttpUrl( $source, $content );
+               $this->tearDownFiles();
+       }
+
+       private function doTestGetFileHttpUrl( $source, $content ) {
+               $backendName = $this->backendClass();
+
+               $this->prepare( array( 'dir' => dirname( $source ) ) );
+               $status = $this->backend->doOperation(
+                       array( 'op' => 'create', 'content' => $content, 'dst' => $source ) );
+               $this->assertGoodStatus( $status,
+                       "Creation of file at $source succeeded ($backendName)." );
+
+               $url = $this->backend->getFileHttpUrl( array( 'src' => $source ) );
+
+               if ( $url !== null ) { // supported
+                       $data = Http::request( "GET", $url );
+                       $this->assertEquals( $content, $data,
+                               "HTTP GET of URL has right contents ($backendName)." );
+               }
+       }
+
+       function provider_testGetFileHttpUrl() {
+               $cases = array();
+
+               $base = self::baseStorePath();
+               $cases[] = array( "$base/unittest-cont1/e/a/z/some_file.txt", "some file contents" );
+               $cases[] = array( "$base/unittest-cont1/e/a/some-other_file.txt", "more file contents" );
+               $cases[] = array( "$base/unittest-cont1/e/a/\$odd&.txt", "test file contents" );
+
+               return $cases;
+       }
+
        /**
         * @dataProvider provider_testPrepareAndClean
         */
index b0d72f3..15fbf07 100644 (file)
--- a/thumb.php
+++ b/thumb.php
@@ -38,6 +38,7 @@ if ( defined( 'THUMB_HANDLER' ) ) {
        // Called directly, use $_REQUEST params
        wfThumbHandleRequest();
 }
+
 wfLogProfilingData();
 
 //--------------------------------------------------------------------------
@@ -61,28 +62,21 @@ function wfThumbHandleRequest() {
  * @return void
  */
 function wfThumbHandle404() {
-       # lighttpd puts the original request in REQUEST_URI, while sjs sets
-       # that to the 404 handler, and puts the original request in REDIRECT_URL.
-       if ( isset( $_SERVER['REDIRECT_URL'] ) ) {
-               # The URL is un-encoded, so put it back how it was
-               $uriPath = str_replace( "%2F", "/", urlencode( $_SERVER['REDIRECT_URL'] ) );
-       } else {
-               $uriPath = $_SERVER['REQUEST_URI'];
-       }
-       # Just get the URI path (REDIRECT_URL/REQUEST_URI is either a full URL or a path)
-       if ( substr( $uriPath, 0, 1 ) !== '/' ) {
-               $bits = wfParseUrl( $uriPath );
-               if ( $bits && isset( $bits['path'] ) ) {
-                       $uriPath = $bits['path'];
-               } else {
-                       wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' );
-                       return;
-               }
+       global $wgArticlePath;
+
+       # Set action base paths so that WebRequest::getPathInfo()
+       # recognizes the "X" as the 'title' in ../thumb_handler.php/X urls.
+       $wgArticlePath = false; # Don't let a "/*" article path clober our action path
+
+       $matches = WebRequest::getPathInfo();
+       if ( !isset( $matches['title'] ) ) {
+               wfThumbError( 404, 'Could not determine the name of the requested thumbnail.' );
+               return;
        }
 
-       $params = wfExtractThumbParams( $uriPath ); // basic wiki URL param extracting
+       $params = wfExtractThumbParams( $matches['title'] ); // basic wiki URL param extracting
        if ( $params == null ) {
-               wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' );
+               wfThumbError( 400, 'The specified thumbnail parameters are not recognized.' );
                return;
        }
 
@@ -299,43 +293,28 @@ function wfStreamThumb( array $params ) {
  * Extract the required params for thumb.php from the thumbnail request URI.
  * At least 'width' and 'f' should be set if the result is an array.
  *
- * @param $uriPath String Thumbnail request URI path
+ * @param $thumbRel String Thumbnail path relative to the thumb zone
  * @return Array|null associative params array or null
  */
-function wfExtractThumbParams( $uriPath ) {
+function wfExtractThumbParams( $thumbRel ) {
        $repo = RepoGroup::singleton()->getLocalRepo();
 
-       // Zone URL might be relative ("/images") or protocol-relative ("//lang.site/image")
-       $zoneUriPath = $repo->getZoneHandlerUrl( 'thumb' )
-               ? $repo->getZoneHandlerUrl( 'thumb' ) // custom URL
-               : $repo->getZoneUrl( 'thumb' ); // default to main URL
-       $bits = wfParseUrl( wfExpandUrl( $zoneUriPath, PROTO_INTERNAL ) );
-       if ( $bits && isset( $bits['path'] ) ) {
-               $zoneUriPath = $bits['path'];
-       } else {
-               return null; // not a valid thumbnail URL
-       }
-
        $hashDirReg = $subdirReg = '';
        for ( $i = 0; $i < $repo->getHashLevels(); $i++ ) {
                $subdirReg .= '[0-9a-f]';
                $hashDirReg .= "$subdirReg/";
        }
-       $zoneReg = preg_quote( $zoneUriPath ); // regex for thumb zone URI
 
        // Check if this is a thumbnail of an original in the local file repo
-       if ( preg_match( "!^$zoneReg/((archive/)?$hashDirReg([^/]*)/([^/]*))$!", $uriPath, $m ) ) {
+       if ( preg_match( "!^((archive/)?$hashDirReg([^/]*)/([^/]*))$!", $thumbRel, $m ) ) {
                list( /*all*/, $rel, $archOrTemp, $filename, $thumbname ) = $m;
        // Check if this is a thumbnail of an temp file in the local file repo
-       } elseif ( preg_match( "!^$zoneReg/(temp/)($hashDirReg([^/]*)/([^/]*))$!", $uriPath, $m ) ) {
+       } elseif ( preg_match( "!^(temp/)($hashDirReg([^/]*)/([^/]*))$!", $thumbRel, $m ) ) {
                list( /*all*/, $archOrTemp, $rel, $filename, $thumbname ) = $m;
        } else {
                return null; // not a valid looking thumbnail request
        }
 
-       $filename = urldecode( $filename );
-       $thumbname = urldecode( $thumbname );
-
        $params = array( 'f' => $filename, 'rel404' => $rel );
        if ( $archOrTemp === 'archive/' ) {
                $params['archived'] = 1;