Merge "Adjust margin of wpSummary elements per move of summary box."
authorTrevor Parscal <tparscal@wikimedia.org>
Fri, 31 Aug 2012 23:20:55 +0000 (23:20 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 31 Aug 2012 23:20:55 +0000 (23:20 +0000)
74 files changed:
.gitignore
RELEASE-NOTES-1.20
includes/AuthPlugin.php
includes/AutoLoader.php
includes/DefaultSettings.php
includes/GlobalFunctions.php
includes/Linker.php
includes/LinksUpdate.php
includes/Setup.php
includes/SqlDataUpdate.php
includes/Timestamp.php [new file with mode: 0644]
includes/Title.php
includes/User.php
includes/UserMailer.php
includes/WikiError.php
includes/WikiPage.php
includes/ZhConversion.php
includes/api/ApiEditPage.php
includes/api/ApiFormatBase.php
includes/cache/MessageCache.php
includes/filebackend/FSFileBackend.php
includes/filebackend/FileBackend.php
includes/filebackend/FileBackendMultiWrite.php
includes/filebackend/FileBackendStore.php
includes/filebackend/FileOp.php
includes/filebackend/SwiftFileBackend.php
includes/filebackend/TempFSFile.php
includes/filerepo/FileRepo.php
includes/filerepo/file/LocalFile.php
includes/job/DoubleRedirectJob.php
includes/objectcache/APCBagOStuff.php
includes/objectcache/BagOStuff.php
includes/objectcache/DBABagOStuff.php
includes/objectcache/XCacheBagOStuff.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/specials/SpecialBlock.php
includes/specials/SpecialUpload.php
includes/zhtable/Makefile.py
includes/zhtable/toHK.manual
includes/zhtable/toTW.manual
languages/data/plurals-mediawiki.xml
languages/messages/MessagesBr.php
languages/messages/MessagesBs.php
languages/messages/MessagesCkb.php
languages/messages/MessagesDa.php
languages/messages/MessagesDe.php
languages/messages/MessagesDiq.php
languages/messages/MessagesEs.php
languages/messages/MessagesEt.php
languages/messages/MessagesFa.php
languages/messages/MessagesKa.php
languages/messages/MessagesLa.php
languages/messages/MessagesNb.php
languages/messages/MessagesNl.php
languages/messages/MessagesOr.php
languages/messages/MessagesPms.php
languages/messages/MessagesSr_ec.php
languages/messages/MessagesSv.php
languages/messages/MessagesTe.php
languages/messages/MessagesUk.php
maintenance/migrateUserGroup.php
resources/jquery/jquery.badge.css
resources/jquery/jquery.badge.js
resources/mediawiki/mediawiki.util.js
skins/Modern.php
skins/MonoBook.php
skins/common/preview.js
tests/parser/parserTests.txt
tests/phpunit/includes/ExtraParserTest.php
tests/phpunit/includes/HtmlTest.php
tests/phpunit/includes/TimestampTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/GenericArrayObjectTest.php
tests/phpunit/includes/media/BitmapMetadataHandlerTest.php
tests/phpunit/includes/specials/SpecialSearchTest.php

index e057d36..ff3ced3 100644 (file)
@@ -13,7 +13,7 @@ nbproject*
 project.index
 
 # MediaWiki install & usage
-cache/*.cdb
+cache
 images/[0-9a-f]
 images/archive
 images/deleted
index cdd2707..92b4c16 100644 (file)
@@ -24,7 +24,7 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
   This only affects installations which have $wgAllowCopyUploads set to true.
 * Removed f-prot support from $wgAntivirusSetup.
 * New variable $wgDBerrorLogTZ to provide dates in the error log in a
-  different timezone than the wiki timezone set by $wgLocalTimezone.
+  different timezone than the wiki timezone set by $wgLocaltimezone.
 * New variables $wgDBssl and $wgDBcompress to enable SSL and compression for database
   connections, if either are available for the selected DB type.
 
@@ -41,7 +41,7 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
 * &useskin=default will now always display the default skin. Useful for users with a
   preference for the non-default skin to look at something using the default skin.
 * (bug 27619) Remove preference option to display broken links as link?
-* (bug 34896) Update jQuery JSON plugin to v2.3 (2011-09-17).
+* (bug 34896) jQuery JSON plugin upgraded to v2.3 (2011-09-17).
 * (bug 34302) Add CSS classes to email fields in user preferences.
 * Introduced $wgDebugDBTransactions to trace transaction status (currently PostgreSQL only).
 * (bug 23795) Add parser itself to ParserMakeImageParams hook.
@@ -66,8 +66,8 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
   Special:Version
 * Edit notices can now be translated.
 * jQuery upgraded to 1.8.
-* jQuery UI upgraded to 1.8.22.
-* (bug 35705) QUnit upgraded from v1.2.0 to v1.8.0.
+* jQuery UI upgraded to 1.8.23.
+* (bug 35705) QUnit upgraded from v1.2.0 to v1.9.0.
 * (bug 37604) jquery.cookie upgraded to 2011 version.
 * (bug 22887) Add warning and tracking category for preprocessor errors
 * (bug 31704) Allow selection of associated namespace on the watchlist
@@ -130,10 +130,14 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
 * Show change tags when transclude Special:Recentchanges(linked) or Special:Newpages.
 * (bug 23226) Add |class= parameter to image links in order to add class(es) to HTML img tag.
 * (bug 39431) SVG animated status is now shown in long description
-* (bug 39376) jquery.form upgraded to 3.14
+* (bug 39376) jquery.form upgraded to 3.14.
 * SVG files will now show the actual width in the SVG's specified units
   in the metadata box.
 * Added ResourceLoader module "jquery.jStorage".
+* (bug 39273) Added AJAX support for "Show changes" (diff) in LivePreview.
+* Added ResourceLoader module "jquery.badge".
+* mw.util.$content now points to the overall content area in the skin rather than just
+  page text content area. If you need the old behaviour please use $( '#mw-content-text').
 
 === Bug fixes in 1.20 ===
 * (bug 30245) Use the correct way to construct a log page title.
@@ -225,13 +229,15 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
 * (bug 27111) Cascading foreign file repos now fetch shared descriptions properly.
 * EXIF below sea level GPS altitude data is now shown correctly.
 * (bug 39284) jquery.tablesorter should not consider "."" or "?"" to be a currency.
+* (bug 39273) "Show changes" should not be incorrectly displayed in the Live Preview state.
 
 === API changes in 1.20 ===
 * (bug 34316) Add ability to retrieve maximum upload size from MediaWiki API.
 * (bug 34313) MediaWiki API intro message about "HTML format" should mention
   the format parameter.
 * (bug 32384) Allow descending order for list=watchlistraw.
-* (bug 31883) Limit of bkusers of list=blocks and titles of action=query is not documented in API help.
+* (bug 31883) Limit of bkusers of list=blocks and titles of action=query is
+  not documented in API help.
 * (bug 32492) API now allows editing using pageid.
 * (bug 32497) API now allows changing of protection level using pageid.
 * (bug 32498) API now allows comparing pages using pageids.
@@ -260,8 +266,9 @@ upgrade PHP if you have not done so prior to upgrading MediaWiki.
 * Watchlist notification timestamp may be queried by page and may be updated via the API.
 * (bug 38904) prop=revisions&rvstart=... no longer blows up when continuing.
 * (bug 39032) ApiQuery generates help in constructor.
-* (bug 11142) Improve file extension blacklist error reporting in API upload
-* (bug 39635) PostgreSQL LOCK IN SHARE MODE option is a syntax error
+* (bug 11142) Improve file extension blacklist error reporting in API upload.
+* (bug 39635) PostgreSQL LOCK IN SHARE MODE option is a syntax error.
+* (bug 36329) Accesskey tooltips for Firefox 14 on Mac should use "ctrl-option-" prefix.
 
 === Languages updated in 1.20 ===
 
index c7fcf93..2e42439 100644 (file)
@@ -176,6 +176,15 @@ class AuthPlugin {
                return true;
        }
 
+       /**
+        * Should MediaWiki store passwords in its local database?
+        *
+        * @return bool
+        */
+       public function allowSetLocalPassword() {
+               return true;
+       }
+
        /**
         * Set the given password in the authentication database.
         * As a special case, the password may be set to null to request
index 9d78dbd..706e1c8 100644 (file)
@@ -245,6 +245,7 @@ $wgAutoloadLocalClasses = array(
        'StubObject' => 'includes/StubObject.php',
        'StubUserLang' => 'includes/StubObject.php',
        'TablePager' => 'includes/Pager.php',
+       'MWTimestamp' => 'includes/Timestamp.php',
        'Title' => 'includes/Title.php',
        'TitleArray' => 'includes/TitleArray.php',
        'TitleArrayFromResult' => 'includes/TitleArray.php',
index e898edf..6aa15be 100644 (file)
@@ -523,16 +523,11 @@ $wgAllowAsyncCopyUploads = false;
 
 /**
  * A list of domains copy uploads can come from
+ *
+ * @since 1.20
  */
 $wgCopyUploadsDomains = array();
 
-/**
- * Enable copy uploads from Special:Upload. $wgAllowCopyUploads must also be
- * true. If $wgAllowCopyUploads is true, but this is false, you will only be
- * able to perform copy uploads from the API or extensions (e.g. UploadWizard).
- */
-$wgCopyUploadsFromSpecialUpload = false;
-
 /**
  * Max size for uploads, in bytes. If not set to an array, applies to all
  * uploads. If set to an array, per upload type maximums can be set, using the
@@ -1454,7 +1449,7 @@ $wgDBerrorLog = false;
 
 /**
  * Timezone to use in the error log.
- * Defaults to the wiki timezone ($wgLocalTimezone).
+ * Defaults to the wiki timezone ($wgLocaltimezone).
  *
  * A list of useable timezones can found at:
  * http://php.net/manual/en/timezones.php
@@ -1701,6 +1696,8 @@ $wgSessionCacheType = CACHE_ANYTHING;
  * which are used when parsing certain text and interface messages.
  *
  * For available types see $wgMainCacheType.
+ *
+ * @since 1.20
  */
 $wgLanguageConverterCacheType = CACHE_ANYTHING;
 
@@ -2051,6 +2048,8 @@ $wgMaxSquidPurgeTitles = 400;
  * );
  * @endcode
  *
+ * @since 1.20
+ *
  * @see $wgHTCPMulticastTTL
  */
 $wgHTCPMulticastRouting = array();
@@ -2635,6 +2634,18 @@ $wgBreakFrames = false;
  */
 $wgEditPageFrameOptions = 'DENY';
 
+/**
+ * Disallow framing of API pages directly, by setting the X-Frame-Options
+ * header. Since the API returns CSRF tokens, allowing the results to be
+ * framed can compromise your user's account security.
+ * Options are:
+ *   - 'DENY': Do not allow framing. This is recommended for most wikis.
+ *   - 'SAMEORIGIN': Allow framing by pages on the same domain.
+ *   - false: Allow all framing.
+ */
+
+$wgApiFrameOptions = 'DENY';
+
 /**
  * Disable output compression (enabled by default if zlib is available)
  */
@@ -2745,6 +2756,8 @@ $wgSend404Code = true;
  * The $wgShowRollbackEditCount variable is used to show how many edits will be
  * rollback. The numeric value of the varible are the limit up to are counted.
  * If the value is false or 0, the edits are not counted.
+ *
+ * @since 1.20
  */
 $wgShowRollbackEditCount = 10;
 
@@ -4382,6 +4395,8 @@ $wgDebugComments = false;
 
 /**
  * Extensive database transaction state debugging
+ *
+ * @since 1.20
  */
 $wgDebugDBTransactions = false;
 
@@ -4899,6 +4914,8 @@ $wgUpgradeKey = false;
  * The value is the replacement for the key (it can contain $1, etc.)
  * %h will be replaced by the short SHA-1 (7 first chars) and %H by the
  * full SHA-1 of the HEAD revision.
+ *
+ * @since 1.20
  */
 $wgGitRepositoryViewers = array(
     'https://gerrit.wikimedia.org/r/p/(.*)' => 'https://gerrit.wikimedia.org/r/gitweb?p=$1;h=%H',
@@ -5771,11 +5788,6 @@ $wgActions = array(
  */
 $wgDisabledActions = array();
 
-/**
- * Allow the "info" action, very inefficient at the moment
- */
-$wgAllowPageInfo = false;
-
 /** @} */ # end actions }
 
 /*************************************************************************//**
@@ -6203,6 +6215,8 @@ $wgDBtestpassword = '';
 
 /**
  * Whether the user must enter their password to change their e-mail address
+ *
+ * @since 1.20
  */
 $wgRequirePasswordforEmailChange = true;
 
index 84cdeea..0a60518 100644 (file)
@@ -2401,6 +2401,7 @@ define( 'TS_ISO_8601_BASIC', 9 );
 /**
  * Get a timestamp string in one of various formats
  *
+ * @deprecated
  * @param $outputtype Mixed: A timestamp in one of the supported formats, the
  *                    function will autodetect which format is supplied and act
  *                    accordingly.
@@ -2408,118 +2409,8 @@ define( 'TS_ISO_8601_BASIC', 9 );
  * @return Mixed: String / false The same date in the format specified in $outputtype or false
  */
 function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
-       $uts = 0;
-       $da = array();
-       $strtime = '';
-
-       if ( !$ts ) { // We want to catch 0, '', null... but not date strings starting with a letter.
-               $uts = time();
-               $strtime = "@$uts";
-       } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) {
-               # TS_DB
-       } elseif ( preg_match( '/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) {
-               # TS_EXIF
-       } elseif ( preg_match( '/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D', $ts, $da ) ) {
-               # TS_MW
-       } elseif ( preg_match( '/^-?\d{1,13}$/D', $ts ) ) {
-               # TS_UNIX
-               $uts = $ts;
-               $strtime = "@$ts"; // http://php.net/manual/en/datetime.formats.compound.php
-       } elseif ( preg_match( '/^\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}.\d{6}$/', $ts ) ) {
-               # TS_ORACLE // session altered to DD-MM-YYYY HH24:MI:SS.FF6
-               $strtime = preg_replace( '/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3",
-                               str_replace( '+00:00', 'UTC', $ts ) );
-       } elseif ( preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z$/', $ts, $da ) ) {
-               # TS_ISO_8601
-       } elseif ( preg_match( '/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(?:\.*\d*)?Z$/', $ts, $da ) ) {
-               #TS_ISO_8601_BASIC
-       } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d*[\+\- ](\d\d)$/', $ts, $da ) ) {
-               # TS_POSTGRES
-       } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d* GMT$/', $ts, $da ) ) {
-               # TS_POSTGRES
-       } elseif (preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.\d\d\d$/', $ts, $da ) ) {
-               # TS_DB2
-       } elseif ( preg_match( '/^[ \t\r\n]*([A-Z][a-z]{2},[ \t\r\n]*)?' . # Day of week
-                                                       '\d\d?[ \t\r\n]*[A-Z][a-z]{2}[ \t\r\n]*\d{2}(?:\d{2})?' .  # dd Mon yyyy
-                                                       '[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d/S', $ts ) ) { # hh:mm:ss
-               # TS_RFC2822, accepting a trailing comment. See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html / r77171
-               # The regex is a superset of rfc2822 for readability
-               $strtime = strtok( $ts, ';' );
-       } elseif ( preg_match( '/^[A-Z][a-z]{5,8}, \d\d-[A-Z][a-z]{2}-\d{2} \d\d:\d\d:\d\d/', $ts ) ) {
-               # TS_RFC850
-               $strtime = $ts;
-       } elseif ( preg_match( '/^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} \d\d:\d\d:\d\d \d{4}/', $ts ) ) {
-               # asctime
-               $strtime = $ts;
-       } else {
-               # Bogus value...
-               wfDebug("wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n");
-
-               return false;
-       }
-
-       static $formats = array(
-               TS_UNIX => 'U',
-               TS_MW => 'YmdHis',
-               TS_DB => 'Y-m-d H:i:s',
-               TS_ISO_8601 => 'Y-m-d\TH:i:s\Z',
-               TS_ISO_8601_BASIC => 'Ymd\THis\Z',
-               TS_EXIF => 'Y:m:d H:i:s', // This shouldn't ever be used, but is included for completeness
-               TS_RFC2822 => 'D, d M Y H:i:s',
-               TS_ORACLE => 'd-m-Y H:i:s.000000', // Was 'd-M-y h.i.s A' . ' +00:00' before r51500
-               TS_POSTGRES => 'Y-m-d H:i:s',
-               TS_DB2 => 'Y-m-d H:i:s',
-       );
-
-       if ( !isset( $formats[$outputtype] ) ) {
-               throw new MWException( 'wfTimestamp() called with illegal output type.' );
-       }
-
-       if ( function_exists( "date_create" ) ) {
-               if ( count( $da ) ) {
-                       $ds = sprintf("%04d-%02d-%02dT%02d:%02d:%02d.00+00:00",
-                               (int)$da[1], (int)$da[2], (int)$da[3],
-                               (int)$da[4], (int)$da[5], (int)$da[6]);
-
-                       $d = date_create( $ds, new DateTimeZone( 'GMT' ) );
-               } elseif ( $strtime ) {
-                       $d = date_create( $strtime, new DateTimeZone( 'GMT' ) );
-               } else {
-                       return false;
-               }
-
-               if ( !$d ) {
-                       wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n");
-                       return false;
-               }
-
-               $output = $d->format( $formats[$outputtype] );
-       } else {
-               if ( count( $da ) ) {
-                       // Warning! gmmktime() acts oddly if the month or day is set to 0
-                       // We may want to handle that explicitly at some point
-                       $uts = gmmktime( (int)$da[4], (int)$da[5], (int)$da[6],
-                               (int)$da[2], (int)$da[3], (int)$da[1] );
-               } elseif ( $strtime ) {
-                       $uts = strtotime( $strtime );
-               }
-
-               if ( $uts === false ) {
-                       wfDebug("wfTimestamp() can't parse the timestamp (non 32-bit time? Update php): $outputtype; $ts\n");
-                       return false;
-               }
-
-               if ( TS_UNIX == $outputtype ) {
-                       return $uts;
-               }
-               $output = gmdate( $formats[$outputtype], $uts );
-       }
-
-       if ( ( $outputtype == TS_RFC2822 ) || ( $outputtype == TS_POSTGRES ) ) {
-               $output .= ' GMT';
-       }
-
-       return $output;
+       $timestamp = new MWTimestamp( $ts );
+       return $timestamp->getTimestamp( $outputtype );
 }
 
 /**
index 7aba444..8e31a1c 100644 (file)
@@ -865,31 +865,31 @@ class Linker {
         * Make a "broken" link to an image
         *
         * @param $title Title object
-        * @param $html String: link label in htmlescaped text form
+        * @param $label String: link label (plain text)
         * @param $query String: query string
-        * @param $trail String: link trail (HTML fragment)
-        * @param $prefix String: link prefix (HTML fragment)
+        * @param $unused1 Unused parameter kept for b/c
+        * @param $unused2 Unused parameter kept for b/c
         * @param $time Boolean: a file of a certain timestamp was requested
         * @return String
         */
-       public static function makeBrokenImageLinkObj( $title, $html = '', $query = '', $trail = '', $prefix = '', $time = false ) {
+       public static function makeBrokenImageLinkObj( $title, $label = '', $query = '', $unused1 = '', $unused2 = '', $time = false ) {
                global $wgEnableUploads, $wgUploadMissingFileUrl, $wgUploadNavigationUrl;
                if ( ! $title instanceof Title ) {
-                       return "<!-- ERROR -->{$prefix}{$html}{$trail}";
+                       return "<!-- ERROR -->" . htmlspecialchars( $label );
                }
                wfProfileIn( __METHOD__ );
+               if ( $label == '' ) {
+                       $label = $title->getPrefixedText();
+               }
+               $encLabel = htmlspecialchars( $label );
                $currentExists = $time ? ( wfFindFile( $title ) != false ) : false;
 
-               list( $inside, $trail ) = self::splitTrail( $trail );
-               if ( $html == '' )
-                       $html = htmlspecialchars( $title->getPrefixedText() );
-
                if ( ( $wgUploadMissingFileUrl || $wgUploadNavigationUrl || $wgEnableUploads ) && !$currentExists ) {
                        $redir = RepoGroup::singleton()->getLocalRepo()->checkRedirect( $title );
 
                        if ( $redir ) {
                                wfProfileOut( __METHOD__ );
-                               return self::linkKnown( $title, "$prefix$html$inside", array(), wfCgiToArray( $query ) ) . $trail;
+                               return self::linkKnown( $title, $encLabel, array(), wfCgiToArray( $query ) );
                        }
 
                        $href = self::getUploadUrl( $title, $query );
@@ -897,10 +897,10 @@ class Linker {
                        wfProfileOut( __METHOD__ );
                        return '<a href="' . htmlspecialchars( $href ) . '" class="new" title="' .
                                htmlspecialchars( $title->getPrefixedText(), ENT_QUOTES ) . '">' .
-                               "$prefix$html$inside</a>$trail";
+                               $encLabel . '</a>';
                } else {
                        wfProfileOut( __METHOD__ );
-                       return self::linkKnown( $title, "$prefix$html$inside", array(), wfCgiToArray( $query ) ) . $trail;
+                       return self::linkKnown( $title, $encLabel, array(), wfCgiToArray( $query ) );
                }
        }
 
index 1d0bcf7..87db4d6 100644 (file)
@@ -51,7 +51,7 @@ class LinksUpdate extends SqlDataUpdate {
         * @param $recursive Boolean: queue jobs for recursive updates?
         */
        function __construct( $title, $parserOutput, $recursive = true ) {
-               parent::__construct( );
+               parent::__construct( false ); // no implicit transaction
 
                if ( !( $title instanceof Title ) ) {
                        throw new MWException( "The calling convention to LinksUpdate::LinksUpdate() has changed. " .
@@ -825,7 +825,7 @@ class LinksDeletionUpdate extends SqlDataUpdate {
         * @param $page WikiPage Page we are updating
         */
        function __construct( WikiPage $page ) {
-               parent::__construct( );
+               parent::__construct( false ); // no implicit transaction
 
                $this->mPage = $page;
        }
index baf7b35..924c3c0 100644 (file)
@@ -332,9 +332,6 @@ if ( !$wgEnotifMinorEdits ) {
 foreach( $wgDisabledActions as $action ){
        $wgActions[$action] = false;
 }
-if( !$wgAllowPageInfo ){
-       $wgActions['info'] = false;
-}
 
 if ( !$wgHtml5Version && $wgHtml5 && $wgAllowRdfaAttributes ) {
        # see http://www.w3.org/TR/rdfa-in-html/#document-conformance
index aeb9ba4..52c9be0 100644 (file)
@@ -36,11 +36,16 @@ abstract class SqlDataUpdate extends DataUpdate {
        protected $mOptions;       //!< SELECT options to be used (array)
 
        private   $mHasTransaction; //!< bool whether a transaction is open on this object (internal use only!)
+       protected $mUseTransaction; //!< bool whether this update should be wrapped in a transaction
 
        /**
         * Constructor
-       **/
-       public function __construct( ) {
+        *
+        * @param bool $withTransaction whether this update should be wrapped in a transaction (default: true).
+        *             A transaction is only started if no transaction is already in progress,
+        *             see beginTransaction() for details.
+        **/
+       public function __construct( $withTransaction = true ) {
                global $wgAntiLockFlags;
 
                parent::__construct( );
@@ -53,16 +58,22 @@ abstract class SqlDataUpdate extends DataUpdate {
 
                // @todo: get connection only when it's needed? make sure that doesn't break anything, especially transactions!
                $this->mDb = wfGetDB( DB_MASTER );
+
+               $this->mWithTransaction = $withTransaction;
                $this->mHasTransaction = false;
        }
 
        /**
-        * Begin a database transaction.
+        * Begin a database transaction, if $withTransaction was given as true in the constructor for this SqlDataUpdate.
         *
-        * Because nested transactions are not supportred by the Database class, this implementation
-        * checkes Database::trxLevel() and only opens a transaction if none is yet active.
+        * Because nested transactions are not supported by the Database class, this implementation
+        * checks Database::trxLevel() and only opens a transaction if none is already active.
         */
        public function beginTransaction() {
+               if ( !$this->mWithTransaction ) {
+                       return;
+               }
+
                // NOTE: nested transactions are not supported, only start a transaction if none is open
                if ( $this->mDb->trxLevel() === 0 ) {
                        $this->mDb->begin( get_class( $this ) . '::beginTransaction'  );
@@ -76,6 +87,7 @@ abstract class SqlDataUpdate extends DataUpdate {
        public function commitTransaction() {
                if ( $this->mHasTransaction ) {
                        $this->mDb->commit( get_class( $this ) . '::commitTransaction' );
+                       $this->mHasTransaction = false;
                }
        }
 
@@ -85,6 +97,7 @@ abstract class SqlDataUpdate extends DataUpdate {
        public function abortTransaction() {
                if ( $this->mHasTransaction ) {
                        $this->mDb->rollback( get_class( $this ) . '::abortTransaction' );
+                       $this->mHasTransaction = false;
                }
        }
 
diff --git a/includes/Timestamp.php b/includes/Timestamp.php
new file mode 100644 (file)
index 0000000..b5ea775
--- /dev/null
@@ -0,0 +1,204 @@
+<?php
+
+/**
+ * Library for creating and parsing MW-style timestamps. Based on the JS
+ * library that does the same thing.
+ *
+ * @author Tyler Romeo, 2012
+ * @since 1.20
+ */
+class MWTimestamp {
+       /**
+        * Standard gmdate() formats for the different timestamp types.
+        */
+       private static $formats = array(
+               TS_UNIX => 'U',
+               TS_MW => 'YmdHis',
+               TS_DB => 'Y-m-d H:i:s',
+               TS_ISO_8601 => 'Y-m-d\TH:i:s\Z',
+               TS_ISO_8601_BASIC => 'Ymd\THis\Z',
+               TS_EXIF => 'Y:m:d H:i:s', // This shouldn't ever be used, but is included for completeness
+               TS_RFC2822 => 'D, d M Y H:i:s',
+               TS_ORACLE => 'd-m-Y H:i:s.000000', // Was 'd-M-y h.i.s A' . ' +00:00' before r51500
+               TS_POSTGRES => 'Y-m-d H:i:s',
+               TS_DB2 => 'Y-m-d H:i:s',
+       );
+
+       /**
+        * Different units for human readable timestamps.
+        * @see MWTimestamp::getHumanTimestamp
+        */
+       private static $units = array(
+               "milliseconds" => 1,
+               "seconds" => 1000, // 1000 milliseconds per second
+               "minutes" => 60, // 60 seconds per minute
+               "hours" => 60, // 60 minutes per hour
+               "days" => 24 // 24 hours per day
+       );
+
+       /**
+        * The actual timestamp being wrapped. Either a DateTime
+        * object or a string with a Unix timestamp depending on
+        * PHP.
+        */
+       private $timestamp;
+
+       /**
+        * Make a new timestamp and set it to the specified time,
+        * or the current time if unspecified.
+        *
+        * @param $timestamp Timestamp to set, or false for current time
+        */
+       public function __construct( $timestamp = false ) {
+               $this->setTimestamp( $timestamp );
+       }
+
+       /**
+        * Set the timestamp to the specified time, or the current time if unspecified.
+        *
+        * Parse the given timestamp into either a DateTime object or a Unix teimstamp,
+        * and then store it.
+        *
+        * @param $ts Timestamp to store, or false for now
+        */
+       public function setTimestamp( $ts = false ) {
+               $uts = 0;
+               $da = array();
+               $strtime = '';
+
+               if ( !$ts ) { // We want to catch 0, '', null... but not date strings starting with a letter.
+                       $uts = time();
+                       $strtime = "@$uts";
+               } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) {
+                       # TS_DB
+               } elseif ( preg_match( '/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) {
+                       # TS_EXIF
+               } elseif ( preg_match( '/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D', $ts, $da ) ) {
+                       # TS_MW
+               } elseif ( preg_match( '/^-?\d{1,13}$/D', $ts ) ) {
+                       # TS_UNIX
+                       $uts = $ts;
+                       $strtime = "@$ts"; // http://php.net/manual/en/datetime.formats.compound.php
+               } elseif ( preg_match( '/^\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}.\d{6}$/', $ts ) ) {
+                       # TS_ORACLE // session altered to DD-MM-YYYY HH24:MI:SS.FF6
+                       $strtime = preg_replace( '/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3",
+                                       str_replace( '+00:00', 'UTC', $ts ) );
+               } elseif ( preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z$/', $ts, $da ) ) {
+                       # TS_ISO_8601
+               } elseif ( preg_match( '/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(?:\.*\d*)?Z$/', $ts, $da ) ) {
+                       #TS_ISO_8601_BASIC
+               } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d*[\+\- ](\d\d)$/', $ts, $da ) ) {
+                       # TS_POSTGRES
+               } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d* GMT$/', $ts, $da ) ) {
+                       # TS_POSTGRES
+               } elseif (preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.\d\d\d$/', $ts, $da ) ) {
+                       # TS_DB2
+               } elseif ( preg_match( '/^[ \t\r\n]*([A-Z][a-z]{2},[ \t\r\n]*)?' . # Day of week
+                                                               '\d\d?[ \t\r\n]*[A-Z][a-z]{2}[ \t\r\n]*\d{2}(?:\d{2})?' .  # dd Mon yyyy
+                                                               '[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d/S', $ts ) ) { # hh:mm:ss
+                       # TS_RFC2822, accepting a trailing comment. See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html / r77171
+                       # The regex is a superset of rfc2822 for readability
+                       $strtime = strtok( $ts, ';' );
+               } elseif ( preg_match( '/^[A-Z][a-z]{5,8}, \d\d-[A-Z][a-z]{2}-\d{2} \d\d:\d\d:\d\d/', $ts ) ) {
+                       # TS_RFC850
+                       $strtime = $ts;
+               } elseif ( preg_match( '/^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} \d\d:\d\d:\d\d \d{4}/', $ts ) ) {
+                       # asctime
+                       $strtime = $ts;
+               } else {
+                       throw new TimestampException( __METHOD__ . " : Invalid timestamp - $ts" );
+               }
+
+               if( !$strtime ) {
+                       $da = array_map( 'intval', $da );
+                       $da[0] = "%04d-%02d-%02dT%02d:%02d:%02d.00+00:00";
+                       $strtime = call_user_func_array( "sprintf", $da );
+               }
+
+               if( function_exists( "date_create" ) ) {
+                       try {
+                               $final = new DateTime( $strtime, new DateTimeZone( 'GMT' ) );
+                       } catch(Exception $e) {
+                               throw new TimestampException( __METHOD__ . 'Invalid timestamp format.' );
+                       }
+               } else {
+                       $final = strtotime( $strtime );
+               }
+
+               if( $final === false ) {
+                       throw new TimestampException( __METHOD__ . 'Invalid timestamp format.' );
+               }
+               $this->timestamp = $final;
+       }
+
+       /**
+        * Get the timestamp represented by this object in a certain form.
+        *
+        * Convert the internal timestamp to the specified format and then
+        * return it.
+        *
+        * @param $style Output format for timestamp
+        * @return string The formatted timestamp
+        */
+       public function getTimestamp( $style = TS_UNIX ) {
+               if( !isset( self::$formats[$style] ) ) {
+                       throw new TimestampException( __METHOD__ . ' : Illegal timestamp output type.' );
+               }
+
+               if( is_object( $this->timestamp ) ) {
+                       // DateTime object was used, call DateTime::format.
+                       $output = $this->timestamp->format( self::$formats[$style] );
+               } elseif( TS_UNIX == $style ) {
+                       // Unix timestamp was used and is wanted, just return it.
+                       $output = $this->timestamp;
+               } else {
+                       // Unix timestamp was used, use gmdate().
+                       $output = gmdate( self::$formats[$style], $this->timestamp );
+               }
+
+               if ( ( $style == TS_RFC2822 ) || ( $style == TS_POSTGRES ) ) {
+                       $output .= ' GMT';
+               }
+
+               return $output;
+       }
+
+       /**
+        * Get the timestamp in a human-friendly relative format, e.g., "3 days ago".
+        *
+        * Determine the difference between the timestamp and the current time, and
+        * generate a readable timestamp by returning "<N> <units> ago", where the
+        * largest possible unit is used.
+        *
+        * @return string Formatted timestamp
+        */
+       public function getHumanTimestamp() {
+               $then = $this->getTimestamp( TS_UNIX );
+               $now = time();
+               $timeago = ($now - $then) * 1000;
+               $message = false;
+
+               foreach( self::$units as $unit => $factor ) {
+                       $next = $timeago / $factor;
+                       if( $next < 1 ) {
+                               break;
+                       } else {
+                               $timeago = $next;
+                               $message = array( $unit, floor( $timeago ) );
+                       }
+               }
+
+               if( $message ) {
+                       $initial = call_user_func_array( 'wfMessage', $message );
+                       return wfMessage( 'ago', $initial );
+               } else {
+                       return wfMessage( 'just-now' );
+               }
+       }
+
+       public function __toString() {
+               return $this->getTimestamp();
+       }
+}
+
+class TimestampException extends MWException {}
index b01c00b..eccae70 100644 (file)
@@ -347,7 +347,7 @@ class Title {
         * @return Title the new object
         */
        public static function newMainPage() {
-               $title = Title::newFromText( wfMsgForContent( 'mainpage' ) );
+               $title = Title::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
                // Don't give fatal errors if the message is broken
                if ( !$title ) {
                        $title = Title::newFromText( 'Main Page' );
@@ -1594,7 +1594,7 @@ class Title {
         *   queries by skipping checks for cascading protections and user blocks.
         * @param $ignoreErrors Array of Strings Set this to a list of message keys
         *   whose corresponding errors may be ignored.
-        * @return Array of arguments to wfMsg to explain permissions problems.
+        * @return Array of arguments to wfMessage to explain permissions problems.
         */
        public function getUserPermissionsErrors( $action, $user, $doExpensiveQueries = true, $ignoreErrors = array() ) {
                $errors = $this->getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries );
@@ -1751,7 +1751,7 @@ class Title {
                # Check $wgNamespaceProtection for restricted namespaces
                if ( $this->isNamespaceProtected( $user ) ) {
                        $ns = $this->mNamespace == NS_MAIN ?
-                               wfMsg( 'nstab-main' ) : $this->getNsText();
+                               wfMessage( 'nstab-main' )->text() : $this->getNsText();
                        $errors[] = $this->mNamespace == NS_MEDIAWIKI ?
                                array( 'protectedinterface' ) : array( 'namespaceprotected',  $ns );
                }
@@ -1950,7 +1950,7 @@ class Title {
                        $id = $user->blockedBy();
                        $reason = $user->blockedFor();
                        if ( $reason == '' ) {
-                               $reason = wfMsg( 'blockednoreason' );
+                               $reason = wfMessage( 'blockednoreason' )->text();
                        }
                        $ip = $user->getRequest()->getIP();
 
@@ -2108,7 +2108,7 @@ class Title {
         * @param $user User to check
         * @param $doExpensiveQueries Bool Set this to false to avoid doing unnecessary queries.
         * @param $short Bool Set this to true to stop after the first permission error.
-        * @return Array of arrays of the arguments to wfMsg to explain permissions problems.
+        * @return Array of arrays of the arguments to wfMessage to explain permissions problems.
         */
        protected function getUserPermissionsErrorsInternal( $action, $user, $doExpensiveQueries = true, $short = false ) {
                wfProfileIn( __METHOD__ );
@@ -3546,9 +3546,13 @@ class Title {
                        );
                        # Update the protection log
                        $log = new LogPage( 'protect' );
-                       $comment = wfMsgForContent( 'prot_1movedto2', $this->getPrefixedText(), $nt->getPrefixedText() );
+                       $comment = wfMessage(
+                               'prot_1movedto2',
+                               $this->getPrefixedText(),
+                               $nt->getPrefixedText()
+                       )->inContentLanguage()->text();
                        if ( $reason ) {
-                               $comment .= wfMsgForContent( 'colon-separator' ) . $reason;
+                               $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
                        }
                        // @todo FIXME: $params?
                        $log->addEntry( 'move_prot', $nt, $comment, array( $this->getPrefixedText() ) );
@@ -3606,7 +3610,7 @@ class Title {
                $formatter->setContext( RequestContext::newExtraneousContext( $this ) );
                $comment = $formatter->getPlainActionText();
                if ( $reason ) {
-                       $comment .= wfMsgForContent( 'colon-separator' ) . $reason;
+                       $comment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $reason;
                }
                # Truncate for whole multibyte characters.
                $comment = $wgContLang->truncate( $comment, 255 );
@@ -3820,7 +3824,7 @@ class Title {
                        return false;
                }
                # Get the article text
-               $rev = Revision::newFromTitle( $nt );
+               $rev = Revision::newFromTitle( $nt, false, Revision::READ_LATEST );
                if( !is_object( $rev ) ){
                        return false;
                }
@@ -4113,11 +4117,11 @@ class Title {
                if ( in_array( 'include_old', $options ) ) {
                        $old_cmp = '>=';
                }
-               if ( in_array( 'include_new', $options ) ) {\r
-                       $new_cmp = '<=';\r
-               }\r
-               if ( in_array( 'include_both', $options ) ) {\r
-                       $old_cmp = '>=';\r
+               if ( in_array( 'include_new', $options ) ) {
+                       $new_cmp = '<=';
+               }
+               if ( in_array( 'include_both', $options ) ) {
+                       $old_cmp = '>=';
                        $new_cmp = '<=';
                }
                // No DB query needed if $old and $new are the same or successive revisions:
@@ -4128,7 +4132,7 @@ class Title {
                                return ( $old_cmp === '>' && $new_cmp === '<' ) ? 0 : 1;
                        }
                        return ( $old->getRawUserText() === $new->getRawUserText() ) ? 1 : 2;
-               }\r
+               }
                $dbr = wfGetDB( DB_SLAVE );
                $res = $dbr->select( 'revision', 'DISTINCT rev_user_text',
                        array(
index f43844e..0a3db4c 100644 (file)
@@ -624,7 +624,7 @@ class User {
                // Certain names may be reserved for batch processes.
                foreach ( $reservedUsernames as $reserved ) {
                        if ( substr( $reserved, 0, 4 ) == 'msg:' ) {
-                               $reserved = wfMsgForContent( substr( $reserved, 4 ) );
+                               $reserved = wfMessage( substr( $reserved, 4 ) )->inContentLanguage()->text();
                        }
                        if ( $reserved == $name ) {
                                return false;
@@ -1305,13 +1305,13 @@ class User {
                        # Local list
                        if ( self::isLocallyBlockedProxy( $ip ) ) {
                                $block = new Block;
-                               $block->setBlocker( wfMsg( 'proxyblocker' ) );
-                               $block->mReason = wfMsg( 'proxyblockreason' );
+                               $block->setBlocker( wfMessage( 'proxyblocker' )->text() );
+                               $block->mReason = wfMessage( 'proxyblockreason' )->text();
                                $block->setTarget( $ip );
                        } elseif ( $this->isAnon() && $this->isDnsBlacklisted( $ip ) ) {
                                $block = new Block;
-                               $block->setBlocker( wfMsg( 'sorbs' ) );
-                               $block->mReason = wfMsg( 'sorbsreason' );
+                               $block->setBlocker( wfMessage( 'sorbs' )->text() );
+                               $block->mReason = wfMessage( 'sorbsreason' )->text();
                                $block->setTarget( $ip );
                        }
                }
@@ -2016,7 +2016,7 @@ class User {
 
                if( $str !== null ) {
                        if( !$wgAuth->allowPasswordChange() ) {
-                               throw new PasswordError( wfMsg( 'password-change-forbidden' ) );
+                               throw new PasswordError( wfMessage( 'password-change-forbidden' )->text() );
                        }
 
                        if( !$this->isValidPassword( $str ) ) {
@@ -2029,12 +2029,12 @@ class User {
                                        $message = $valid;
                                        $params = array( $wgMinimalPasswordLength );
                                }
-                               throw new PasswordError( wfMsgExt( $message, array( 'parsemag' ), $params ) );
+                               throw new PasswordError( wfMessage( $message, $params )->text() );
                        }
                }
 
                if( !$wgAuth->setPassword( $this, $str ) ) {
-                       throw new PasswordError( wfMsg( 'externaldberror' ) );
+                       throw new PasswordError( wfMessage( 'externaldberror' )->text() );
                }
 
                $this->setInternalPassword( $str );
@@ -2891,11 +2891,16 @@ class User {
         * @todo Only rarely do all these fields need to be set!
         */
        public function saveSettings() {
+               global $wgAuth;
+
                $this->load();
                if ( wfReadOnly() ) { return; }
                if ( 0 == $this->mId ) { return; }
 
                $this->mTouched = self::newTouchedTimestamp();
+               if ( !$wgAuth->allowSetLocalPassword() ) {
+                       $this->mPassword = '';
+               }
 
                $dbw = wfGetDB( DB_MASTER );
                $dbw->update( 'user',
@@ -3353,15 +3358,15 @@ class User {
                        $message = 'confirmemail_body_' . $type;
                }
 
-               return $this->sendMail( wfMsg( 'confirmemail_subject' ),
-                       wfMsg( $message,
+               return $this->sendMail( wfMessage( 'confirmemail_subject' )->text(),
+                       wfMessage( $message,
                                $this->getRequest()->getIP(),
                                $this->getName(),
                                $url,
                                $wgLang->timeanddate( $expiration, false ),
                                $invalidateURL,
                                $wgLang->date( $expiration, false ),
-                               $wgLang->time( $expiration, false ) ) );
+                               $wgLang->time( $expiration, false ) )->text() );
        }
 
        /**
@@ -4013,10 +4018,10 @@ class User {
                        $action = 'create2';
                        if ( $byEmail ) {
                                if ( $reason === '' ) {
-                                       $reason = wfMsgForContent( 'newuserlog-byemail' );
+                                       $reason = wfMessage( 'newuserlog-byemail' )->inContentLanguage()->text();
                                } else {
                                        $reason = $wgContLang->commaList( array(
-                                               $reason, wfMsgForContent( 'newuserlog-byemail' ) ) );
+                                               $reason, wfMessage( 'newuserlog-byemail' )->inContentLanguage()->text() ) );
                                }
                        }
                }
@@ -4185,8 +4190,8 @@ class User {
                /*
                if ( $wgMinimalPasswordLength > 1 ) {
                        $ret['pattern'] = '.{' . intval( $wgMinimalPasswordLength ) . ',}';
-                       $ret['title'] = wfMsgExt( 'passwordtooshort', 'parsemag',
-                               $wgMinimalPasswordLength );
+                       $ret['title'] = wfMessage( 'passwordtooshort' )
+                               ->numParams( $wgMinimalPasswordLength )->text();
                }
                */
 
index 36da6fb..01e7132 100644 (file)
@@ -623,32 +623,36 @@ class EmailNotification {
 
                if ( $this->oldid ) {
                        // Always show a link to the diff which triggered the mail. See bug 32210.
-                       $keys['$NEWPAGE'] = wfMsgForContent( 'enotif_lastdiff',
-                               $this->title->getCanonicalUrl( 'diff=next&oldid=' . $this->oldid ) );
+                       $keys['$NEWPAGE'] = wfMessage( 'enotif_lastdiff',
+                               $this->title->getCanonicalUrl( 'diff=next&oldid=' . $this->oldid ) )
+                               ->inContentLanguage()->text();
                        if ( !$wgEnotifImpersonal ) {
                                // For personal mail, also show a link to the diff of all changes
                                // since last visited.
-                               $keys['$NEWPAGE'] .= " \n" . wfMsgForContent( 'enotif_lastvisited',
-                                       $this->title->getCanonicalUrl( 'diff=0&oldid=' . $this->oldid ) );
+                               $keys['$NEWPAGE'] .= " \n" . wfMessage( 'enotif_lastvisited',
+                                       $this->title->getCanonicalUrl( 'diff=0&oldid=' . $this->oldid ) )
+                                       ->inContentLanguage()->text();
                        }
                        $keys['$OLDID']   = $this->oldid;
-                       $keys['$CHANGEDORCREATED'] = wfMsgForContent( 'changed' );
+                       $keys['$CHANGEDORCREATED'] = wfMessage( 'changed' )->inContentLanguage()->text();
                } else {
-                       $keys['$NEWPAGE'] = wfMsgForContent( 'enotif_newpagetext' );
+                       $keys['$NEWPAGE'] = wfMessage( 'enotif_newpagetext' )->inContentLanguage()->text();
                        # clear $OLDID placeholder in the message template
                        $keys['$OLDID']   = '';
-                       $keys['$CHANGEDORCREATED'] = wfMsgForContent( 'created' );
+                       $keys['$CHANGEDORCREATED'] = wfMessage( 'created' )->inContentLanguage()->text();
                }
 
                $keys['$PAGETITLE'] = $this->title->getPrefixedText();
                $keys['$PAGETITLE_URL'] = $this->title->getCanonicalUrl();
-               $keys['$PAGEMINOREDIT'] = $this->minorEdit ? wfMsgForContent( 'minoredit' ) : '';
+               $keys['$PAGEMINOREDIT'] = $this->minorEdit ?
+                       wfMessage( 'minoredit' )->inContentLanguage()->text() : '';
                $keys['$UNWATCHURL'] = $this->title->getCanonicalUrl( 'action=unwatch' );
 
                if ( $this->editor->isAnon() ) {
                        # real anon (user:xxx.xxx.xxx.xxx)
-                       $keys['$PAGEEDITOR'] = wfMsgForContent( 'enotif_anon_editor', $this->editor->getName() );
-                       $keys['$PAGEEDITOR_EMAIL'] = wfMsgForContent( 'noemailtitle' );
+                       $keys['$PAGEEDITOR'] = wfMessage( 'enotif_anon_editor', $this->editor->getName() )
+                               ->inContentLanguage()->text();
+                       $keys['$PAGEEDITOR_EMAIL'] = wfMessage( 'noemailtitle' )->inContentLanguage()->text();
                } else {
                        $keys['$PAGEEDITOR'] = $wgEnotifUseRealName ? $this->editor->getRealName() : $this->editor->getName();
                        $emailPage = SpecialPage::getSafeTitleFor( 'Emailuser', $this->editor->getName() );
@@ -662,12 +666,12 @@ class EmailNotification {
 
                # Now build message's subject and body
 
-               $subject = wfMsgExt( 'enotif_subject', 'content' );
+               $subject = wfMessage( 'enotif_subject' )->inContentLanguage()->plain();
                $subject = strtr( $subject, $keys );
                $subject = MessageCache::singleton()->transform( $subject, false, null, $this->title );
                $this->subject = strtr( $subject, $postTransformKeys );
 
-               $body = wfMsgExt( 'enotif_body', 'content' );
+               $body = wfMessage( 'enotif_body' )->inContentLanguage()->plain();
                $body = strtr( $body, $keys );
                $body = MessageCache::singleton()->transform( $body, false, null, $this->title );
                $this->body = wordwrap( strtr( $body, $postTransformKeys ), 72 );
@@ -769,7 +773,7 @@ class EmailNotification {
                                array( '$WATCHINGUSERNAME',
                                        '$PAGEEDITDATE',
                                        '$PAGEEDITTIME' ),
-                               array( wfMsgForContent( 'enotif_impersonal_salutation' ),
+                               array( wfMessage( 'enotif_impersonal_salutation' )->inContentLanguage()->text(),
                                        $wgContLang->date( $this->timestamp, false, false ),
                                        $wgContLang->time( $this->timestamp, false, false ) ),
                                $this->body );
index 7c167f6..4b0e68c 100644 (file)
@@ -134,12 +134,12 @@ class WikiXmlError extends WikiError {
        /** @return string */
        function getMessage() {
                // '$1 at line $2, col $3 (byte $4): $5',
-               return wfMsgHtml( 'xml-error-string',
+               return wfMessage( 'xml-error-string',
                        $this->mMessage,
                        $this->mLine,
                        $this->mColumn,
                        $this->mByte . $this->mContext,
-                       xml_error_string( $this->mXmlError ) );
+                       xml_error_string( $this->mXmlError ) )->escaped();
        }
 
        function _extractContext( $context, $offset ) {
index a4e1d8d..1021767 100644 (file)
@@ -92,6 +92,7 @@ class WikiPage extends Page implements IDBAccessObject {
         * Create a WikiPage object of the appropriate class for the given title.
         *
         * @param $title Title
+        * @throws MWException
         * @return WikiPage object of the appropriate type
         */
        public static function factory( Title $title ) {
@@ -711,7 +712,7 @@ class WikiPage extends Page implements IDBAccessObject {
         * Determine whether a page would be suitable for being counted as an
         * article in the site_stats table based on the title & its content
         *
-        * @param $editInfo Object or false: object returned by prepareTextForEdit(),
+        * @param $editInfo Object|bool (false): object returned by prepareTextForEdit(),
         *        if false, the current database state will be used
         * @return Boolean
         */
@@ -1326,7 +1327,8 @@ class WikiPage extends Page implements IDBAccessObject {
 
                        if ( $section == 'new' ) {
                                # Inserting a new section
-                               $subject = $sectionTitle ? wfMsgForContent( 'newsectionheaderdefaultlevel', $sectionTitle ) . "\n\n" : '';
+                               $subject = $sectionTitle ? wfMessage( 'newsectionheaderdefaultlevel' )
+                                       ->rawParams( $sectionTitle )->inContentLanguage()->text() . "\n\n" : '';
                                if ( wfRunHooks( 'PlaceNewSection', array( $this, $oldtext, $subject, &$text ) ) ) {
                                        $text = strlen( trim( $oldtext ) ) > 0
                                                ? "{$oldtext}\n\n{$subject}{$text}"
@@ -1389,9 +1391,10 @@ class WikiPage extends Page implements IDBAccessObject {
         * edit-already-exists error will be returned. These two conditions are also possible with
         * auto-detection due to MediaWiki's performance-optimised locking strategy.
         *
-        * @param $baseRevId int the revision ID this edit was based off, if any
+        * @param bool|int $baseRevId int the revision ID this edit was based off, if any
         * @param $user User the user doing the edit
         *
+        * @throws MWException
         * @return Status object. Possible errors:
         *     edit-hook-aborted:       The ArticleSave hook aborted the edit but didn't set the fatal flag of $status
         *     edit-gone-missing:       In update mode, but the article didn't exist
@@ -1930,12 +1933,15 @@ class WikiPage extends Page implements IDBAccessObject {
                        if ( $restrictions != '' ) {
                                $protectDescription .= $wgContLang->getDirMark() . "[$action=$restrictions] (";
                                if ( $encodedExpiry[$action] != 'infinity' ) {
-                                       $protectDescription .= wfMsgForContent( 'protect-expiring',
+                                       $protectDescription .= wfMessage(
+                                               'protect-expiring',
                                                $wgContLang->timeanddate( $expiry[$action], false, false ) ,
                                                $wgContLang->date( $expiry[$action], false, false ) ,
-                                               $wgContLang->time( $expiry[$action], false, false ) );
+                                               $wgContLang->time( $expiry[$action], false, false )
+                                       )->inContentLanguage()->text();
                                } else {
-                                       $protectDescription .= wfMsgForContent( 'protect-expiry-indefinite' );
+                                       $protectDescription .= wfMessage( 'protect-expiry-indefinite' )
+                                               ->inContentLanguage()->text();
                                }
 
                                $protectDescription .= ') ';
@@ -1976,7 +1982,12 @@ class WikiPage extends Page implements IDBAccessObject {
                        }
 
                        # Prepare a null revision to be added to the history
-                       $editComment = $wgContLang->ucfirst( wfMsgForContent( $revCommentMsg, $this->mTitle->getPrefixedText() ) );
+                       $editComment = $wgContLang->ucfirst(
+                               wfMessage(
+                                       $revCommentMsg,
+                                       $this->mTitle->getPrefixedText()
+                               )->inContentLanguage()->text()
+                       );
                        if ( $reason ) {
                                $editComment .= ": $reason";
                        }
@@ -1984,7 +1995,9 @@ class WikiPage extends Page implements IDBAccessObject {
                                $editComment .= " ($protectDescription)";
                        }
                        if ( $cascade ) {
-                               $editComment .= ' [' . wfMsgForContent( 'protect-summary-cascade' ) . ']';
+                               // FIXME: Should use 'brackets' message.
+                               $editComment .= ' [' . wfMessage( 'protect-summary-cascade' )
+                                       ->inContentLanguage()->text() . ']';
                        }
 
                        # Insert a null revision
@@ -2051,6 +2064,7 @@ class WikiPage extends Page implements IDBAccessObject {
         * Take an array of page restrictions and flatten it to a string
         * suitable for insertion into the page_restrictions field.
         * @param $limit Array
+        * @throws MWException
         * @return String
         */
        protected static function flattenRestrictions( $limit ) {
@@ -2392,9 +2406,9 @@ class WikiPage extends Page implements IDBAccessObject {
                $target = Revision::newFromId( $s->rev_id );
                if ( empty( $summary ) ) {
                        if ( $from == '' ) { // no public user name
-                               $summary = wfMsgForContent( 'revertpage-nouser' );
+                               $summary = wfMessage( 'revertpage-nouser' )->inContentLanguage()->text();
                        } else {
-                               $summary = wfMsgForContent( 'revertpage' );
+                               $summary = wfMessage( 'revertpage' )->inContentLanguage()->text();
                        }
                }
 
@@ -2585,10 +2599,11 @@ class WikiPage extends Page implements IDBAccessObject {
                        $truncatedtext = $wgContLang->truncate(
                                str_replace( "\n", ' ', $newtext ),
                                max( 0, 255
-                                       - strlen( wfMsgForContent( 'autoredircomment' ) )
+                                       - strlen( wfMessage( 'autoredircomment' )->inContentLanguage()->text() )
                                        - strlen( $rt->getFullText() )
                                ) );
-                       return wfMsgForContent( 'autoredircomment', $rt->getFullText(), $truncatedtext );
+                       return wfMessage( 'autoredircomment', $rt->getFullText() )
+                               ->rawParams( $truncatedtext )->inContentLanguage()->text();
                }
 
                # New page autosummaries
@@ -2597,22 +2612,24 @@ class WikiPage extends Page implements IDBAccessObject {
 
                        $truncatedtext = $wgContLang->truncate(
                                str_replace( "\n", ' ', $newtext ),
-                               max( 0, 200 - strlen( wfMsgForContent( 'autosumm-new' ) ) ) );
+                               max( 0, 200 - strlen( wfMessage( 'autosumm-new' )->inContentLanguage()->text() ) ) );
 
-                       return wfMsgForContent( 'autosumm-new', $truncatedtext );
+                       return wfMessage( 'autosumm-new' )->rawParams( $truncatedtext )
+                               ->inContentLanguage()->text();
                }
 
                # Blanking autosummaries
                if ( $oldtext != '' && $newtext == '' ) {
-                       return wfMsgForContent( 'autosumm-blank' );
+                       return wfMessage( 'autosumm-blank' )->inContentLanguage()->text();
                } elseif ( strlen( $oldtext ) > 10 * strlen( $newtext ) && strlen( $newtext ) < 500 ) {
                        # Removing more than 90% of the article
 
                        $truncatedtext = $wgContLang->truncate(
                                $newtext,
-                               max( 0, 200 - strlen( wfMsgForContent( 'autosumm-replace' ) ) ) );
+                               max( 0, 200 - strlen( wfMessage( 'autosumm-replace' )->inContentLanguage()->text() ) ) );
 
-                       return wfMsgForContent( 'autosumm-replace', $truncatedtext );
+                       return wfMessage( 'autosumm-replace' )->rawParams( $truncatedtext )
+                               ->inContentLanguage()->text();
                }
 
                # If we reach this point, there's no applicable autosummary for our case, so our
@@ -2687,12 +2704,16 @@ class WikiPage extends Page implements IDBAccessObject {
                if ( $blank ) {
                        // The current revision is blank and the one before is also
                        // blank. It's just not our lucky day
-                       $reason = wfMsgForContent( 'exbeforeblank', '$1' );
+                       $reason = wfMessage( 'exbeforeblank', '$1' )->inContentLanguage()->text();
                } else {
                        if ( $onlyAuthor ) {
-                               $reason = wfMsgForContent( 'excontentauthor', '$1', $onlyAuthor );
+                               $reason = wfMessage(
+                                       'excontentauthor',
+                                       '$1',
+                                       $onlyAuthor
+                               )->inContentLanguage()->text();
                        } else {
-                               $reason = wfMsgForContent( 'excontent', '$1' );
+                               $reason = wfMessage( 'excontent', '$1' )->inContentLanguage()->text();
                        }
                }
 
@@ -2946,6 +2967,7 @@ class WikiPage extends Page implements IDBAccessObject {
 
        /**
         * @deprecated since 1.18
+        * @param $oldid int
         * @return bool
         */
        public function useParserCache( $oldid ) {
index b98f521..247b193 100644 (file)
@@ -15802,6 +15802,7 @@ $zh2TW = array(
 '彩线' => '綵線',
 '彩船' => '綵船',
 '彩衣' => '綵衣',
+'綫' => '線',
 '缉凶' => '緝凶',
 '緝兇' => '緝凶',
 '緝凶' => '緝凶',
@@ -15937,6 +15938,30 @@ $zh2TW = array(
 );
 
 $zh2HK = array(
+'505線' => '505綫',
+'505线' => '505綫',
+'507線' => '507綫',
+'507线' => '507綫',
+'610線' => '610綫',
+'610线' => '610綫',
+'614P線' => '614P綫',
+'614P线' => '614P綫',
+'614线' => '614綫',
+'614線' => '614綫',
+'615P線' => '615P綫',
+'615P线' => '615P綫',
+'615线' => '615綫',
+'615線' => '615綫',
+'705线' => '705綫',
+'705線' => '705綫',
+'706线' => '706綫',
+'706線' => '706綫',
+'751P線' => '751P綫',
+'751P线' => '751P綫',
+'751線' => '751綫',
+'751线' => '751綫',
+'761P线' => '761P綫',
+'761P線' => '761P綫',
 '“' => '「',
 '”' => '」',
 '‘' => '『',
@@ -16173,6 +16198,8 @@ $zh2HK = array(
 '動著者' => '動著者',
 '動著述' => '動著述',
 '動著錄' => '動著錄',
+'北环线' => '北環綫',
+'北環線' => '北環綫',
 '医院里' => '医院裏',
 '波札那' => '博茨瓦納',
 '珍妮弗·卡普里亚蒂' => '卡佩雅蒂',
@@ -16420,6 +16447,8 @@ $zh2HK = array(
 '寫著者' => '寫著者',
 '寫著述' => '寫著述',
 '寫著錄' => '寫著錄',
+'将军澳线' => '將軍澳綫',
+'將軍澳線' => '將軍澳綫',
 '专辑里' => '專輯裏',
 '專輯裡' => '專輯裏',
 '尋著' => '尋着',
@@ -16950,6 +16979,10 @@ $zh2HK = array(
 '本著錄' => '本著錄',
 '村子里' => '村子裏',
 '村子裡' => '村子裏',
+'东涌线' => '東涌綫',
+'東涌線' => '東涌綫',
+'東鐵線' => '東鐵綫',
+'东铁线' => '東鐵綫',
 '枕著' => '枕着',
 '枕著作' => '枕著作',
 '枕著名' => '枕著名',
@@ -16983,6 +17016,8 @@ $zh2HK = array(
 '樂著錄' => '樂著錄',
 '寶獅' => '標致',
 '標誌著' => '標誌着',
+'機場快線' => '機場快綫',
+'机场快线' => '機場快綫',
 '機器人' => '機械人',
 '机器人' => '機械人',
 '历史里' => '歷史裏',
@@ -17015,8 +17050,12 @@ $zh2HK = array(
 '沉著者' => '沉著者',
 '沉著述' => '沉著述',
 '沉著錄' => '沉著錄',
+'沙中线' => '沙中綫',
+'沙中線' => '沙中綫',
 '沙地阿拉伯' => '沙特阿拉伯',
 '沙烏地阿拉伯' => '沙特阿拉伯',
+'沙田至中環線' => '沙田至中環綫',
+'沙田至中环线' => '沙田至中環綫',
 '马拉特·萨芬' => '沙芬',
 '沿著' => '沿着',
 '沿著作' => '沿著作',
@@ -17074,6 +17113,8 @@ $zh2HK = array(
 '涼著錄' => '涼著錄',
 '深淵裡' => '深淵裏',
 '深渊里' => '深渊裏',
+'港岛线' => '港島綫',
+'港島線' => '港島綫',
 '渴著' => '渴着',
 '渴著作' => '渴著作',
 '渴著名' => '渴著名',
@@ -17114,6 +17155,14 @@ $zh2HK = array(
 '潤著者' => '潤著者',
 '潤著述' => '潤著述',
 '潤著錄' => '潤著錄',
+'無線劇集' => '無綫劇集',
+'无线剧集' => '無綫劇集',
+'無線收費' => '無綫收費',
+'无线收费' => '無綫收費',
+'无线节目' => '無綫節目',
+'無線節目' => '無綫節目',
+'无线电视' => '無綫電視',
+'無線電視' => '無綫電視',
 '菸' => '煙',
 '照著' => '照着',
 '照著作' => '照著作',
@@ -17547,6 +17596,8 @@ $zh2HK = array(
 '苦著錄' => '苦著錄',
 '苦里' => '苦裏',
 '苦裡' => '苦裏',
+'荃湾线' => '荃灣綫',
+'荃灣線' => '荃灣綫',
 '莫三比克' => '莫桑比克',
 '賴索托' => '萊索托',
 '馬自達' => '萬事得',
@@ -17630,6 +17681,8 @@ $zh2HK = array(
 '裹著者' => '裹著者',
 '裹著述' => '裹著述',
 '裹著錄' => '裹著錄',
+'西铁线' => '西鐵綫',
+'西鐵線' => '西鐵綫',
 '見著' => '見着',
 '見著作' => '見著作',
 '見著名' => '見著名',
@@ -17638,6 +17691,8 @@ $zh2HK = array(
 '見著者' => '見著者',
 '見著述' => '見著述',
 '見著錄' => '見著錄',
+'觀塘線' => '觀塘綫',
+'观塘线' => '觀塘綫',
 '記著' => '記着',
 '記著作' => '記著作',
 '記著名' => '記著名',
@@ -17821,6 +17876,8 @@ $zh2HK = array(
 '辦著錄' => '辦著錄',
 '近角聪信' => '近角聰信',
 '近角聰信' => '近角聰信',
+'迪士尼线' => '迪士尼綫',
+'迪士尼線' => '迪士尼綫',
 '迫著' => '迫着',
 '追著' => '追着',
 '追著作' => '追著作',
@@ -18080,6 +18137,8 @@ $zh2HK = array(
 '馬爾地夫' => '馬爾代夫',
 '馬利共和國' => '馬里共和國',
 '土豆' => '馬鈴薯',
+'馬鞍山線' => '馬鞍山綫',
+'马鞍山线' => '馬鞍山綫',
 '駕著' => '駕着',
 '駕著作' => '駕著作',
 '駕著名' => '駕著名',
@@ -18456,4 +18515,4 @@ $zh2SG = array(
 '笨豬跳' => '绑紧跳',
 '蹦极跳' => '绑紧跳',
 '笑星' => '谐星',
-);
+);
\ No newline at end of file
index 3be4b00..0963fe7 100644 (file)
@@ -60,7 +60,11 @@ class ApiEditPage extends ApiBase {
                        if ( $titleObj->isRedirect() ) {
                                $oldTitle = $titleObj;
 
-                               $titles = Title::newFromRedirectArray( Revision::newFromTitle( $oldTitle )->getText( Revision::FOR_THIS_USER ) );
+                               $titles = Title::newFromRedirectArray(
+                                       Revision::newFromTitle(
+                                               $oldTitle, false, Revision::READ_LATEST
+                                       )->getText( Revision::FOR_THIS_USER )
+                               );
                                // array_shift( $titles );
 
                                $redirValues = array();
@@ -182,7 +186,7 @@ class ApiEditPage extends ApiBase {
                if ( !is_null( $params['summary'] ) ) {
                        $requestArray['wpSummary'] = $params['summary'];
                }
-               
+
                if ( !is_null( $params['sectiontitle'] ) ) {
                        $requestArray['wpSectionTitle'] = $params['sectiontitle'];
                }
index a8d69f5..54c90a6 100644 (file)
@@ -143,6 +143,12 @@ abstract class ApiFormatBase extends ApiBase {
 
                $this->getMain()->getRequest()->response()->header( "Content-Type: $mime; charset=utf-8" );
 
+               //Set X-Frame-Options API results (bug 39180)
+               global $wgApiFrameOptions;
+               if ( $wgApiFrameOptions ) {
+                       $this->getMain()->getRequest()->response()->header( "X-Frame-Options: $wgApiFrameOptions" );
+               }
+
                if ( $isHtml ) {
 ?>
 <!DOCTYPE HTML>
index 24f32d6..b854a2e 100644 (file)
@@ -766,7 +766,9 @@ class MessageCache {
                }
 
                # Try loading it from the database
-               $revision = Revision::newFromTitle( Title::makeTitle( NS_MEDIAWIKI, $title ) );
+               $revision = Revision::newFromTitle(
+                       Title::makeTitle( NS_MEDIAWIKI, $title ), false, Revision::READ_LATEST
+               );
                if ( $revision ) {
                        $message = $revision->getText();
                        if ($message === false) {
index a0befd6..9349534 100644 (file)
@@ -685,7 +685,7 @@ class FSFileBackend extends FileBackendStore {
 
                // Create a new temporary file with the same extension...
                $ext = FileBackend::extensionFromPath( $params['src'] );
-               $tmpFile = TempFSFile::factory( wfBaseName( $source ) . '_', $ext );
+               $tmpFile = TempFSFile::factory( 'localcopy_', $ext );
                if ( !$tmpFile ) {
                        return null;
                }
index cb2433a..2ab7655 100644 (file)
@@ -39,8 +39,7 @@
  * All "storage paths" are of the format "mwstore://<backend>/<container>/<path>".
  * The "<path>" portion is a relative path that uses UNIX file system (FS)
  * notation, though any particular backend may not actually be using a local
- * filesystem.
- * Therefore, the relative paths are only virtual.
+ * filesystem. Therefore, the relative paths are only virtual.
  *
  * Backend contents are stored under wiki-specific container names by default.
  * For legacy reasons, this has no effect for the FS backend class, and per-wiki
@@ -171,7 +170,8 @@ abstract class FileBackend {
         *         'dst'                 => <storage path>,
         *         'content'             => <string of new file contents>,
         *         'overwrite'           => <boolean>,
-        *         'overwriteSame'       => <boolean>
+        *         'overwriteSame'       => <boolean>,
+        *         'disposition'         => <Content-Disposition header value>
         *     );
         * @endcode
         *
@@ -182,7 +182,8 @@ abstract class FileBackend {
         *         'src'                 => <file system path>,
         *         'dst'                 => <storage path>,
         *         'overwrite'           => <boolean>,
-        *         'overwriteSame'       => <boolean>
+        *         'overwriteSame'       => <boolean>,
+        *         'disposition'         => <Content-Disposition header value>
         *     )
         * @endcode
         *
@@ -193,7 +194,8 @@ abstract class FileBackend {
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>,
         *         'overwrite'           => <boolean>,
-        *         'overwriteSame'       => <boolean>
+        *         'overwriteSame'       => <boolean>,
+        *         'disposition'         => <Content-Disposition header value>
         *     )
         * @endcode
         *
@@ -204,7 +206,8 @@ abstract class FileBackend {
         *         'src'                 => <storage path>,
         *         'dst'                 => <storage path>,
         *         'overwrite'           => <boolean>,
-        *         'overwriteSame'       => <boolean>
+        *         'overwriteSame'       => <boolean>,
+        *         'disposition'         => <Content-Disposition header value>
         *     )
         * @endcode
         *
@@ -231,6 +234,10 @@ abstract class FileBackend {
         *   - overwriteSame       : An error will not be given if a file already
         *                           exists at the destination that has the same
         *                           contents as the new contents to be written there.
+        *   - disposition         : When supplied, the backend will add a Content-Disposition
+        *                           header when GETs/HEADs of the destination file are made.
+        *                           Backends that don't support file metadata will ignore this.
+        *                           See http://tools.ietf.org/html/rfc6266 (since 1.20).
         *
         * $opts is an associative of boolean flags, including:
         *   - force               : Operation precondition errors no longer trigger an abort.
@@ -400,7 +407,8 @@ abstract class FileBackend {
         *     array(
         *         'op'                  => 'create',
         *         'dst'                 => <storage path>,
-        *         'content'             => <string of new file contents>
+        *         'content'             => <string of new file contents>,
+        *         'disposition'         => <Content-Disposition header value>
         *     )
         * @endcode
         * b) Copy a file system file into storage
@@ -408,7 +416,8 @@ abstract class FileBackend {
         *     array(
         *         'op'                  => 'store',
         *         'src'                 => <file system path>,
-        *         'dst'                 => <storage path>
+        *         'dst'                 => <storage path>,
+        *         'disposition'         => <Content-Disposition header value>
         *     )
         * @endcode
         * c) Copy a file within storage
@@ -416,7 +425,8 @@ abstract class FileBackend {
         *     array(
         *         'op'                  => 'copy',
         *         'src'                 => <storage path>,
-        *         'dst'                 => <storage path>
+        *         'dst'                 => <storage path>,
+        *         'disposition'         => <Content-Disposition header value>
         *     )
         * @endcode
         * d) Move a file within storage
@@ -424,7 +434,8 @@ abstract class FileBackend {
         *     array(
         *         'op'                  => 'move',
         *         'src'                 => <storage path>,
-        *         'dst'                 => <storage path>
+        *         'dst'                 => <storage path>,
+        *         'disposition'         => <Content-Disposition header value>
         *     )
         * @endcode
         * e) Delete a file within storage
@@ -445,6 +456,10 @@ abstract class FileBackend {
         * @par Boolean flags for operations (operation-specific):
         *   - ignoreMissingSource : The operation will simply succeed and do
         *                           nothing if the source file does not exist.
+        *   - disposition         : When supplied, the backend will add a Content-Disposition
+        *                           header when GETs/HEADs of the destination file are made.
+        *                           Backends that don't support file metadata will ignore this.
+        *                           See http://tools.ietf.org/html/rfc6266 (since 1.20).
         *
         * $opts is an associative of boolean flags, including:
         *   - bypassReadOnly      : Allow writes in read-only mode (since 1.20)
@@ -1089,6 +1104,19 @@ abstract class FileBackend {
                return ( self::normalizeContainerPath( $path ) !== null );
        }
 
+       /**
+        * Build a Content-Disposition header value per RFC 6266
+        *
+        * @param $type string One of (attachment, inline)
+        * @param $filename string Suggested file name (should not contain slashes)
+        * @return string
+        */
+       final public static function makeContentDisposition( $type, $filename ) {
+               $type     = strtolower( $type );
+               $type     = in_array( $type, array( 'inline', 'attachment' ) ) ? $type : 'inline';
+               return "$type; filename*=UTF-8''" . rawurlencode( basename( $filename ) );
+       }
+
        /**
         * Validate and normalize a relative storage path.
         * Null is returned if the path involves directory traversal.
index 59392f6..9efa0db 100644 (file)
@@ -160,11 +160,14 @@ class FileBackendMultiWrite extends FileBackend {
                // Actually attempt the operation batch on the master backend...
                $masterStatus = $mbe->doOperations( $realOps, $opts );
                $status->merge( $masterStatus );
-               // Propagate the operations to the clone backends...
-               foreach ( $this->backends as $index => $backend ) {
-                       if ( $index !== $this->masterIndex ) { // not done already
-                               $realOps = $this->substOpBatchPaths( $ops, $backend );
-                               $status->merge( $backend->doOperations( $realOps, $opts ) );
+               // Propagate the operations to the clone backends if there were no fatal errors.
+               // If $ops only had one operation, this might avoid backend inconsistencies.
+               if ( !count( $masterStatus->getErrorsArray() ) ) {
+                       foreach ( $this->backends as $index => $backend ) {
+                               if ( $index !== $this->masterIndex ) { // not done already
+                                       $realOps = $this->substOpBatchPaths( $ops, $backend );
+                                       $status->merge( $backend->doOperations( $realOps, $opts ) );
+                               }
                        }
                }
                // Make 'success', 'successCount', and 'failCount' fields reflect
index 9bec145..5771560 100644 (file)
@@ -89,6 +89,7 @@ abstract class FileBackendStore extends FileBackend {
         *   - content       : the raw file contents
         *   - dst           : destination storage path
         *   - overwrite     : overwrite any file that exists at the destination
+        *   - disposition   : Content-Disposition header value for the destination
         *   - async         : Status will be returned immediately if supported.
         *                     If the status is OK, then its value field will be
         *                     set to a FileBackendStoreOpHandle object.
@@ -105,7 +106,9 @@ abstract class FileBackendStore extends FileBackend {
                } else {
                        $status = $this->doCreateInternal( $params );
                        $this->clearCache( array( $params['dst'] ) );
-                       $this->deleteFileCache( $params['dst'] ); // persistent cache
+                       if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                               $this->deleteFileCache( $params['dst'] ); // persistent cache
+                       }
                }
                wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
@@ -125,6 +128,7 @@ abstract class FileBackendStore extends FileBackend {
         *   - src           : source path on disk
         *   - dst           : destination storage path
         *   - overwrite     : overwrite any file that exists at the destination
+        *   - disposition   : Content-Disposition header value for the destination
         *   - async         : Status will be returned immediately if supported.
         *                     If the status is OK, then its value field will be
         *                     set to a FileBackendStoreOpHandle object.
@@ -141,7 +145,9 @@ abstract class FileBackendStore extends FileBackend {
                } else {
                        $status = $this->doStoreInternal( $params );
                        $this->clearCache( array( $params['dst'] ) );
-                       $this->deleteFileCache( $params['dst'] ); // persistent cache
+                       if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                               $this->deleteFileCache( $params['dst'] ); // persistent cache
+                       }
                }
                wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
@@ -161,6 +167,7 @@ abstract class FileBackendStore extends FileBackend {
         *   - src           : source storage path
         *   - dst           : destination storage path
         *   - overwrite     : overwrite any file that exists at the destination
+        *   - disposition   : Content-Disposition header value for the destination
         *   - async         : Status will be returned immediately if supported.
         *                     If the status is OK, then its value field will be
         *                     set to a FileBackendStoreOpHandle object.
@@ -173,7 +180,9 @@ abstract class FileBackendStore extends FileBackend {
                wfProfileIn( __METHOD__ . '-' . $this->name );
                $status = $this->doCopyInternal( $params );
                $this->clearCache( array( $params['dst'] ) );
-               $this->deleteFileCache( $params['dst'] ); // persistent cache
+               if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                       $this->deleteFileCache( $params['dst'] ); // persistent cache
+               }
                wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
@@ -222,6 +231,7 @@ abstract class FileBackendStore extends FileBackend {
         *   - src           : source storage path
         *   - dst           : destination storage path
         *   - overwrite     : overwrite any file that exists at the destination
+        *   - disposition   : Content-Disposition header value for the destination
         *   - async         : Status will be returned immediately if supported.
         *                     If the status is OK, then its value field will be
         *                     set to a FileBackendStoreOpHandle object.
@@ -235,7 +245,9 @@ abstract class FileBackendStore extends FileBackend {
                $status = $this->doMoveInternal( $params );
                $this->clearCache( array( $params['src'], $params['dst'] ) );
                $this->deleteFileCache( $params['src'] ); // persistent cache
-               $this->deleteFileCache( $params['dst'] ); // persistent cache
+               if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                       $this->deleteFileCache( $params['dst'] ); // persistent cache
+               }
                wfProfileOut( __METHOD__ . '-' . $this->name );
                wfProfileOut( __METHOD__ );
                return $status;
@@ -1460,7 +1472,9 @@ abstract class FileBackendStore extends FileBackend {
        }
 
        /**
-        * Set the cached stat info for a file path
+        * Set the cached stat info for a file path.
+        * Negatives (404s) are not cached. By not caching negatives, we can skip cache
+        * salting for the case when a file is created at a path were there was none before.
         *
         * @param $path string Storage path
         * @param $val mixed Information to cache
index fa87c3a..7c43c48 100644 (file)
@@ -431,18 +431,15 @@ abstract class FileOp {
 
 /**
  * Store a file into the backend from a file on the file system.
- * Parameters similar to FileBackendStore::storeInternal(), which include:
- *   - src           : source path on file system
- *   - dst           : destination storage path
- *   - overwrite     : do nothing and pass if an identical file exists at destination
- *   - overwriteSame : override any existing file at destination
+ * Parameters for this operation are outlined in FileBackend::doOperations().
  */
 class StoreFileOp extends FileOp {
        /**
         * @return array
         */
        protected function allowedParams() {
-               return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+               return array( array( 'src', 'dst' ),
+                       array( 'overwrite', 'overwriteSame', 'disposition' ) );
        }
 
        /**
@@ -508,15 +505,12 @@ class StoreFileOp extends FileOp {
 
 /**
  * Create a file in the backend with the given content.
- * Parameters similar to FileBackendStore::createInternal(), which include:
- *   - content       : the raw file contents
- *   - dst           : destination storage path
- *   - overwrite     : do nothing and pass if an identical file exists at destination
- *   - overwriteSame : override any existing file at destination
+ * Parameters for this operation are outlined in FileBackend::doOperations().
  */
 class CreateFileOp extends FileOp {
        protected function allowedParams() {
-               return array( array( 'content', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+               return array( array( 'content', 'dst' ),
+                       array( 'overwrite', 'overwriteSame', 'disposition' ) );
        }
 
        protected function doPrecheck( array &$predicates ) {
@@ -571,18 +565,15 @@ class CreateFileOp extends FileOp {
 
 /**
  * Copy a file from one storage path to another in the backend.
- * Parameters similar to FileBackendStore::copyInternal(), which include:
- *   - src           : source storage path
- *   - dst           : destination storage path
- *   - overwrite     : do nothing and pass if an identical file exists at destination
- *   - overwriteSame : override any existing file at destination
+ * Parameters for this operation are outlined in FileBackend::doOperations().
  */
 class CopyFileOp extends FileOp {
        /**
         * @return array
         */
        protected function allowedParams() {
-               return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+               return array( array( 'src', 'dst' ),
+                       array( 'overwrite', 'overwriteSame', 'disposition' ) );
        }
 
        /**
@@ -642,18 +633,15 @@ class CopyFileOp extends FileOp {
 
 /**
  * Move a file from one storage path to another in the backend.
- * Parameters similar to FileBackendStore::moveInternal(), which include:
- *   - src           : source storage path
- *   - dst           : destination storage path
- *   - overwrite     : do nothing and pass if an identical file exists at destination
- *   - overwriteSame : override any existing file at destination
+ * Parameters for this operation are outlined in FileBackend::doOperations().
  */
 class MoveFileOp extends FileOp {
        /**
         * @return array
         */
        protected function allowedParams() {
-               return array( array( 'src', 'dst' ), array( 'overwrite', 'overwriteSame' ) );
+               return array( array( 'src', 'dst' ),
+                       array( 'overwrite', 'overwriteSame', 'disposition' ) );
        }
 
        /**
@@ -719,9 +707,7 @@ class MoveFileOp extends FileOp {
 
 /**
  * Delete a file at the given storage path from the backend.
- * Parameters similar to FileBackendStore::deleteInternal(), which include:
- *   - src                 : source storage path
- *   - ignoreMissingSource : don't return an error if the file does not exist
+ * Parameters for this operation are outlined in FileBackend::doOperations().
  */
 class DeleteFileOp extends FileOp {
        /**
index 9c111c9..88727e4 100644 (file)
@@ -210,13 +210,21 @@ class SwiftFileBackend extends FileBackendStore {
                        if ( !strlen( $obj->content_type ) ) { // special case
                                $obj->content_type = 'unknown/unknown';
                        }
+                       // Set the Content-Disposition header if requested
+                       if ( isset( $params['disposition'] ) ) {
+                               $obj->headers['Content-Disposition'] = $params['disposition'];
+                       }
                        if ( !empty( $params['async'] ) ) { // deferred
-                               $handle = $obj->write_async( $params['content'] );
-                               $status->value = new SwiftFileOpHandle( $this, $params, 'Create', $handle );
-                               $status->value->affectedObjects[] = $obj;
+                               $op = $obj->write_async( $params['content'] );
+                               $status->value = new SwiftFileOpHandle( $this, $params, 'Create', $op );
+                               if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                                       $status->value->affectedObjects[] = $obj;
+                               }
                        } else { // actually write the object in Swift
                                $obj->write( $params['content'] );
-                               $this->purgeCDNCache( array( $obj ) );
+                               if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                                       $this->purgeCDNCache( array( $obj ) );
+                               }
                        }
                } catch ( CDNNotEnabledException $e ) {
                        // CDN not enabled; nothing to see here
@@ -292,6 +300,10 @@ class SwiftFileBackend extends FileBackendStore {
                        if ( !strlen( $obj->content_type ) ) { // special case
                                $obj->content_type = 'unknown/unknown';
                        }
+                       // Set the Content-Disposition header if requested
+                       if ( isset( $params['disposition'] ) ) {
+                               $obj->headers['Content-Disposition'] = $params['disposition'];
+                       }
                        if ( !empty( $params['async'] ) ) { // deferred
                                wfSuppressWarnings();
                                $fp = fopen( $params['src'], 'rb' );
@@ -299,14 +311,18 @@ class SwiftFileBackend extends FileBackendStore {
                                if ( !$fp ) {
                                        $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
                                } else {
-                                       $handle = $obj->write_async( $fp, filesize( $params['src'] ), true );
-                                       $status->value = new SwiftFileOpHandle( $this, $params, 'Store', $handle );
+                                       $op = $obj->write_async( $fp, filesize( $params['src'] ), true );
+                                       $status->value = new SwiftFileOpHandle( $this, $params, 'Store', $op );
                                        $status->value->resourcesToClose[] = $fp;
-                                       $status->value->affectedObjects[] = $obj;
+                                       if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                                               $status->value->affectedObjects[] = $obj;
+                                       }
                                }
                        } else { // actually write the object in Swift
                                $obj->load_from_filename( $params['src'], true ); // calls $obj->write()
-                               $this->purgeCDNCache( array( $obj ) );
+                               if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                                       $this->purgeCDNCache( array( $obj ) );
+                               }
                        }
                } catch ( CDNNotEnabledException $e ) {
                        // CDN not enabled; nothing to see here
@@ -374,13 +390,21 @@ class SwiftFileBackend extends FileBackendStore {
                // (b) Actually copy the file to the destination
                try {
                        $dstObj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
+                       $hdrs = array(); // source file headers to override with new values
+                       if ( isset( $params['disposition'] ) ) {
+                               $hdrs['Content-Disposition'] = $params['disposition'];
+                       }
                        if ( !empty( $params['async'] ) ) { // deferred
-                               $handle = $sContObj->copy_object_to_async( $srcRel, $dContObj, $dstRel );
-                               $status->value = new SwiftFileOpHandle( $this, $params, 'Copy', $handle );
-                               $status->value->affectedObjects[] = $dstObj;
+                               $op = $sContObj->copy_object_to_async( $srcRel, $dContObj, $dstRel, null, $hdrs );
+                               $status->value = new SwiftFileOpHandle( $this, $params, 'Copy', $op );
+                               if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                                       $status->value->affectedObjects[] = $dstObj;
+                               }
                        } else { // actually write the object in Swift
-                               $sContObj->copy_object_to( $srcRel, $dContObj, $dstRel );
-                               $this->purgeCDNCache( array( $dstObj ) );
+                               $sContObj->copy_object_to( $srcRel, $dContObj, $dstRel, null, $hdrs );
+                               if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                                       $this->purgeCDNCache( array( $dstObj ) );
+                               }
                        }
                } catch ( CDNNotEnabledException $e ) {
                        // CDN not enabled; nothing to see here
@@ -445,14 +469,23 @@ class SwiftFileBackend extends FileBackendStore {
                try {
                        $srcObj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
                        $dstObj = new CF_Object( $dContObj, $dstRel, false, false ); // skip HEAD
+                       $hdrs = array(); // source file headers to override with new values
+                       if ( isset( $params['disposition'] ) ) {
+                               $hdrs['Content-Disposition'] = $params['disposition'];
+                       }
                        if ( !empty( $params['async'] ) ) { // deferred
-                               $handle = $sContObj->move_object_to_async( $srcRel, $dContObj, $dstRel );
-                               $status->value = new SwiftFileOpHandle( $this, $params, 'Move', $handle );
+                               $op = $sContObj->move_object_to_async( $srcRel, $dContObj, $dstRel, null, $hdrs );
+                               $status->value = new SwiftFileOpHandle( $this, $params, 'Move', $op );
                                $status->value->affectedObjects[] = $srcObj;
-                               $status->value->affectedObjects[] = $dstObj;
+                               if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                                       $status->value->affectedObjects[] = $dstObj;
+                               }
                        } else { // actually write the object in Swift
-                               $sContObj->move_object_to( $srcRel, $dContObj, $dstRel );
-                               $this->purgeCDNCache( array( $srcObj, $dstObj ) );
+                               $sContObj->move_object_to( $srcRel, $dContObj, $dstRel, null, $hdrs );
+                               $this->purgeCDNCache( array( $srcObj ) );
+                               if ( !empty( $params['overwrite'] ) ) { // file possibly mutated
+                                       $this->purgeCDNCache( array( $dstObj ) );
+                               }
                        }
                } catch ( CDNNotEnabledException $e ) {
                        // CDN not enabled; nothing to see here
@@ -493,8 +526,8 @@ class SwiftFileBackend extends FileBackendStore {
                        $sContObj = $this->getContainer( $srcCont );
                        $srcObj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD
                        if ( !empty( $params['async'] ) ) { // deferred
-                               $handle = $sContObj->delete_object_async( $srcRel );
-                               $status->value = new SwiftFileOpHandle( $this, $params, 'Delete', $handle );
+                               $op = $sContObj->delete_object_async( $srcRel );
+                               $status->value = new SwiftFileOpHandle( $this, $params, 'Delete', $op );
                                $status->value->affectedObjects[] = $srcObj;
                        } else { // actually write the object in Swift
                                $sContObj->delete_object( $srcRel );
@@ -989,11 +1022,8 @@ class SwiftFileBackend extends FileBackendStore {
                        return null;
                }
 
-               # Check the recursion guard to avoid loops when filling metadata
-               if ( empty( $params['nostat'] ) && !$this->fileExists( $params ) ) {
-                       return null;
-               }
-
+               // Blindly create a tmp file and stream to it, catching any exception if the file does
+               // not exist. Also, doing a stat here will cause infinite loops when filling metadata.
                $tmpFile = null;
                try {
                        $sContObj = $this->getContainer( $srcCont );
@@ -1001,7 +1031,7 @@ class SwiftFileBackend extends FileBackendStore {
                        // Get source file extension
                        $ext = FileBackend::extensionFromPath( $srcRel );
                        // Create a new temporary file...
-                       $tmpFile = TempFSFile::factory( wfBaseName( $srcRel ) . '_', $ext );
+                       $tmpFile = TempFSFile::factory( 'localcopy_', $ext );
                        if ( $tmpFile ) {
                                $handle = fopen( $tmpFile->getPath(), 'wb' );
                                if ( $handle ) {
@@ -1013,6 +1043,8 @@ class SwiftFileBackend extends FileBackendStore {
                        }
                } catch ( NoSuchContainerException $e ) {
                        $tmpFile = null;
+               } catch ( NoSuchObjectException $e ) {
+                       $tmpFile = null;
                } catch ( CloudFilesException $e ) { // some other exception?
                        $tmpFile = null;
                        $this->handleException( $e, null, __METHOD__, $params );
index ed6bf2f..b738898 100644 (file)
@@ -43,7 +43,7 @@ class TempFSFile extends FSFile {
         */
        public static function factory( $prefix, $extension = '' ) {
                wfProfileIn( __METHOD__ );
-               $base = wfTempDir() . '/' . $prefix . dechex( mt_rand( 0, 99999999 ) );
+               $base = wfTempDir() . '/' . $prefix . wfRandomString( 12 );
                $ext = ( $extension != '' ) ? ".{$extension}" : "";
                for ( $attempt = 1; true; $attempt++ ) {
                        $path = "{$base}-{$attempt}{$ext}";
index 3159077..bef2642 100644 (file)
@@ -935,19 +935,38 @@ class FileRepo {
        public function storeTemp( $originalName, $srcPath ) {
                $this->assertWritableRepo(); // fail out if read-only
 
-               $date      = gmdate( "YmdHis" );
-               $hashPath  = $this->getHashPath( $originalName );
-               $dstRel    = "{$hashPath}{$date}!{$originalName}";
-               $dstUrlRel = $hashPath . $date . '!' . rawurlencode( $originalName );
+               $date       = gmdate( "YmdHis" );
+               $hashPath   = $this->getHashPath( $originalName );
+               $dstRel     = "{$hashPath}{$date}!{$originalName}";
+               $dstUrlRel  = $hashPath . $date . '!' . rawurlencode( $originalName );
+               $virtualUrl = $this->getVirtualUrl( 'temp' )  . '/' . $dstUrlRel;
 
-               $result = $this->store( $srcPath, 'temp', $dstRel, self::SKIP_LOCKING );
-               $result->value = $this->getVirtualUrl( 'temp' ) . '/' . $dstUrlRel;
+               $result = $this->quickImport( $srcPath, $virtualUrl );
+               $result->value = $virtualUrl;
 
                return $result;
        }
 
        /**
-        * Concatenate a list of files into a target file location.
+        * Remove a temporary file or mark it for garbage collection
+        *
+        * @param $virtualUrl String: the virtual URL returned by FileRepo::storeTemp()
+        * @return Boolean: true on success, false on failure
+        */
+       public function freeTemp( $virtualUrl ) {
+               $this->assertWritableRepo(); // fail out if read-only
+
+               $temp = $this->getVirtualUrl( 'temp' );
+               if ( substr( $virtualUrl, 0, strlen( $temp ) ) != $temp ) {
+                       wfDebug( __METHOD__.": Invalid temp virtual URL\n" );
+                       return false;
+               }
+
+               return $this->quickPurge( $virtualUrl )->isOK();
+       }
+
+       /**
+        * Concatenate a list of temporary files into a target file location.
         *
         * @param $srcPaths Array Ordered list of source virtual URLs/storage paths
         * @param $dstPath String Target file system path
@@ -961,14 +980,10 @@ class FileRepo {
                $status = $this->newGood();
 
                $sources = array();
-               $deleteOperations = array(); // post-concatenate ops
                foreach ( $srcPaths as $srcPath ) {
                        // Resolve source to a storage path if virtual
                        $source = $this->resolveToStoragePath( $srcPath );
                        $sources[] = $source; // chunk to merge
-                       if ( $flags & self::DELETE_SOURCE ) {
-                               $deleteOperations[] = array( 'op' => 'delete', 'src' => $source );
-                       }
                }
 
                // Concatenate the chunks into one FS file
@@ -979,36 +994,16 @@ class FileRepo {
                }
 
                // Delete the sources if required
-               if ( $deleteOperations ) {
-                       $opts = array( 'force' => true );
-                       $status->merge( $this->backend->doOperations( $deleteOperations, $opts ) );
+               if ( $flags & self::DELETE_SOURCE ) {
+                       $status->merge( $this->quickPurgeBatch( $srcPaths ) );
                }
 
-               // Make sure status is OK, despite any $deleteOperations fatals
+               // Make sure status is OK, despite any quickPurgeBatch() fatals
                $status->setResult( true );
 
                return $status;
        }
 
-       /**
-        * Remove a temporary file or mark it for garbage collection
-        *
-        * @param $virtualUrl String: the virtual URL returned by FileRepo::storeTemp()
-        * @return Boolean: true on success, false on failure
-        */
-       public function freeTemp( $virtualUrl ) {
-               $this->assertWritableRepo(); // fail out if read-only
-
-               $temp = "mwrepo://{$this->name}/temp";
-               if ( substr( $virtualUrl, 0, strlen( $temp ) ) != $temp ) {
-                       wfDebug( __METHOD__.": Invalid temp virtual URL\n" );
-                       return false;
-               }
-               $path = $this->resolveVirtualUrl( $virtualUrl );
-
-               return $this->cleanupBatch( array( $path ), self::SKIP_LOCKING )->isOK();
-       }
-
        /**
         * Copy or move a file either from a storage path, virtual URL,
         * or FS path, into this repository at the specified destination location.
index e849c8e..75b9d45 100644 (file)
@@ -1151,7 +1151,7 @@ class LocalFile extends File {
                # Add the log entry
                $log = new LogPage( 'upload' );
                $action = $reupload ? 'overwrite' : 'upload';
-               $log->addEntry( $action, $descTitle, $comment, array(), $user );
+               $logId = $log->addEntry( $action, $descTitle, $comment, array(), $user );
 
                wfProfileIn( __METHOD__ . '-edit' );
                if ( $descTitle->exists() ) {
@@ -1169,6 +1169,8 @@ class LocalFile extends File {
                                wfRunHooks( 'NewRevisionFromEditComplete', array( $wikiPage, $nullRevision, $latest, $user ) );
                                $wikiPage->updateRevisionOn( $dbw, $nullRevision );
                        }
+                       $dbw->update( 'logging', array( 'log_page' => $descTitle->getArticleID() ), array( 'log_id' => $logId ), __METHOD__ );
+
                        # Invalidate the cache for the description page
                        $descTitle->invalidateCache();
                        $descTitle->purgeSquid();
@@ -1176,7 +1178,11 @@ class LocalFile extends File {
                        # New file; create the description page.
                        # There's already a log entry, so don't make a second RC entry
                        # Squid and file cache for the description page are purged by doEdit.
-                       $wikiPage->doEdit( $pageText, $comment, EDIT_NEW | EDIT_SUPPRESS_RC, false, $user );
+                       $status = $wikiPage->doEdit( $pageText, $comment, EDIT_NEW | EDIT_SUPPRESS_RC, false, $user );
+
+                       if ( isset( $status->value['revision'] ) ) {
+                               $dbw->update( 'logging', array( 'log_page' => $status->value['revision']->getPage() ), array( 'log_id' => $logId ), __METHOD__ );
+                       }
                }
                wfProfileOut( __METHOD__ . '-edit' );
 
index 5b86a90..08af997 100644 (file)
@@ -89,7 +89,7 @@ class DoubleRedirectJob extends Job {
                        return false;
                }
 
-               $targetRev = Revision::newFromTitle( $this->title );
+               $targetRev = Revision::newFromTitle( $this->title, false, Revision::READ_LATEST );
                if ( !$targetRev ) {
                        wfDebug( __METHOD__.": target redirect already deleted, ignoring\n" );
                        return true;
index 5a7729b..1a0de21 100644 (file)
@@ -27,7 +27,6 @@
  * @ingroup Cache
  */
 class APCBagOStuff extends BagOStuff {
-
        /**
         * @param $key string
         * @return mixed
@@ -36,7 +35,11 @@ class APCBagOStuff extends BagOStuff {
                $val = apc_fetch( $key );
 
                if ( is_string( $val ) ) {
-                       $val = unserialize( $val );
+                       if ( $this->isInteger( $val ) ) {
+                               $val = intval( $val );
+                       } else {
+                               $val = unserialize( $val );
+                       }
                }
 
                return $val;
@@ -49,7 +52,11 @@ class APCBagOStuff extends BagOStuff {
         * @return bool
         */
        public function set( $key, $value, $exptime = 0 ) {
-               apc_store( $key, serialize( $value ), $exptime );
+               if ( !$this->isInteger( $value ) ) {
+                       $value = serialize( $value );
+               }
+
+               apc_store( $key, $value, $exptime );
 
                return true;
        }
@@ -65,6 +72,14 @@ class APCBagOStuff extends BagOStuff {
                return true;
        }
 
+       public function incr( $key, $value = 1 ) {
+               return apc_inc( $key, $value );
+       }
+
+       public function decr( $key, $value = 1 ) {
+               return apc_dec( $key, $value );
+       }
+
        /**
         * @return Array
         */
@@ -80,4 +95,3 @@ class APCBagOStuff extends BagOStuff {
                return $keys;
        }
 }
-
index fcc3aa9..57029a8 100644 (file)
@@ -164,10 +164,11 @@ abstract class BagOStuff {
        }
 
        /**
+        * Increase stored value of $key by $value while preserving its TTL
         * @param $key String: Key to increase
         * @param $value Integer: Value to add to $key (Default 1)
         * @return null if lock is not possible else $key value increased by $value
-        * @return bool success
+        * @return integer
         */
        public function incr( $key, $value = 1 ) {
                if ( !$this->lock( $key ) ) {
@@ -186,9 +187,10 @@ abstract class BagOStuff {
        }
 
        /**
+        * Decrease stored value of $key by $value while preserving its TTL
         * @param $key String
         * @param $value Integer
-        * @return bool success
+        * @return integer
         */
        public function decr( $key, $value = 1 ) {
                return $this->incr( $key, - $value );
@@ -235,4 +237,14 @@ abstract class BagOStuff {
                        return $exptime;
                }
        }
+
+       /**
+        * Check if a value is an integer
+        *
+        * @param $value mixed
+        * @return bool
+        */
+       protected function isInteger( $value ) {
+               return ( is_int( $value ) || ctype_digit( $value ) );
+       }
 }
index 8483d7e..264aed7 100644 (file)
@@ -45,8 +45,7 @@ class DBABagOStuff extends BagOStuff {
                        $params['dir'] = wfTempDir();
                }
 
-               $this->mFile = $params['dir']."/mw-cache-" . wfWikiID();
-               $this->mFile .= '.db';
+               $this->mFile = $params['dir'] . '/mw-cache-' . wfWikiID() . '.db';
                wfDebug( __CLASS__ . ": using cache file {$this->mFile}\n" );
                $this->mHandler = $wgDBAhandler;
        }
@@ -58,7 +57,7 @@ class DBABagOStuff extends BagOStuff {
         *
         * @return string
         */
-       function encode( $value, $expiry ) {
+       protected function encode( $value, $expiry ) {
                # Convert to absolute time
                $expiry = $this->convertExpiry( $expiry );
 
@@ -69,7 +68,7 @@ class DBABagOStuff extends BagOStuff {
         * @param $blob string
         * @return array list containing value first and expiry second
         */
-       function decode( $blob ) {
+       protected function decode( $blob ) {
                if ( !is_string( $blob ) ) {
                        return array( null, 0 );
                } else {
@@ -83,7 +82,7 @@ class DBABagOStuff extends BagOStuff {
        /**
         * @return resource
         */
-       function getReader() {
+       protected function getReader() {
                if ( file_exists( $this->mFile ) ) {
                        $handle = dba_open( $this->mFile, 'rl', $this->mHandler );
                } else {
@@ -100,7 +99,7 @@ class DBABagOStuff extends BagOStuff {
        /**
         * @return resource
         */
-       function getWriter() {
+       protected function getWriter() {
                $handle = dba_open( $this->mFile, 'cl', $this->mHandler );
 
                if ( !$handle ) {
@@ -114,7 +113,7 @@ class DBABagOStuff extends BagOStuff {
         * @param $key string
         * @return mixed|null|string
         */
-       function get( $key ) {
+       public function get( $key ) {
                wfProfileIn( __METHOD__ );
                wfDebug( __METHOD__ . "($key)\n" );
 
@@ -149,7 +148,7 @@ class DBABagOStuff extends BagOStuff {
         * @param $exptime int
         * @return bool
         */
-       function set( $key, $value, $exptime = 0 ) {
+       public function set( $key, $value, $exptime = 0 ) {
                wfProfileIn( __METHOD__ );
                wfDebug( __METHOD__ . "($key)\n" );
 
@@ -173,7 +172,7 @@ class DBABagOStuff extends BagOStuff {
         * @param $time int
         * @return bool
         */
-       function delete( $key, $time = 0 ) {
+       public function delete( $key, $time = 0 ) {
                wfProfileIn( __METHOD__ );
                wfDebug( __METHOD__ . "($key)\n" );
 
@@ -196,7 +195,7 @@ class DBABagOStuff extends BagOStuff {
         * @param $exptime int
         * @return bool
         */
-       function add( $key, $value, $exptime = 0 ) {
+       public function add( $key, $value, $exptime = 0 ) {
                wfProfileIn( __METHOD__ );
 
                $blob = $this->encode( $value, $exptime );
@@ -214,7 +213,7 @@ class DBABagOStuff extends BagOStuff {
                if ( !$ret ) {
                        list( $value, $expiry ) = $this->decode( dba_fetch( $key, $handle ) );
 
-                       if ( $expiry < time() ) {
+                       if ( $expiry && $expiry < time() ) {
                                # Yes expired, delete and try again
                                dba_delete( $key, $handle );
                                $ret = dba_insert( $key, $blob, $handle );
@@ -229,8 +228,43 @@ class DBABagOStuff extends BagOStuff {
        }
 
        /**
-        * @return Array
+        * @param $key string
+        * @param $step integer
+        * @return integer|bool
         */
+       public function incr( $key, $step = 1 ) {
+               wfProfileIn( __METHOD__ );
+
+               $handle = $this->getWriter();
+
+               if ( !$handle ) {
+                       wfProfileOut( __METHOD__ );
+                       return false;
+               }
+
+               list( $value, $expiry ) = $this->decode( dba_fetch( $key, $handle ) );
+               if ( !is_null( $value ) ) {
+                       if ( $expiry && $expiry < time() ) {
+                               # Key is expired, delete it
+                               dba_delete( $key, $handle );
+                               wfDebug( __METHOD__ . ": $key expired\n" );
+                               $value = null;
+                       } else {
+                               $value += $step;
+                               $blob = $this->encode( $value, $expiry );
+
+                               $ret = dba_replace( $key, $blob, $handle );
+                               $value = $ret ? $value : null;
+                       }
+               }
+
+               dba_close( $handle );
+
+               wfProfileOut( __METHOD__ );
+
+               return is_null( $value ) ? false : (int)$value;
+       }
+
        function keys() {
                $reader = $this->getReader();
                $k1 = dba_firstkey( $reader );
@@ -250,4 +284,3 @@ class DBABagOStuff extends BagOStuff {
                return $result;
        }
 }
-
index 08f52b7..6c88289 100644 (file)
@@ -38,7 +38,11 @@ class XCacheBagOStuff extends BagOStuff {
                $val = xcache_get( $key );
 
                if ( is_string( $val ) ) {
-                       $val = unserialize( $val );
+                       if ( $this->isInteger( $val ) ) {
+                               $val = intval( $val );
+                       } else {
+                               $val = unserialize( $val );
+                       }
                }
 
                return $val;
@@ -53,7 +57,11 @@ class XCacheBagOStuff extends BagOStuff {
         * @return bool
         */
        public function set( $key, $value, $expire = 0 ) {
-               xcache_set( $key, serialize( $value ), $expire );
+               if ( !$this->isInteger( $value ) ) {
+                       $value = serialize( $value );
+               }
+
+               xcache_set( $key, $value, $expire );
                return true;
        }
 
@@ -68,5 +76,12 @@ class XCacheBagOStuff extends BagOStuff {
                xcache_unset( $key );
                return true;
        }
-}
 
+       public function incr( $key, $value = 1 ) {
+               return xcache_inc( $key, $value );
+       }
+
+       public function decr( $key, $value = 1 ) {
+               return xcache_dec( $key, $value );
+       }
+}
index 9dd6939..623a269 100644 (file)
@@ -77,7 +77,7 @@ abstract class ResourceLoaderWikiModule extends ResourceLoaderModule {
                if ( !$title->isCssJsSubpage() && !$title->isCssOrJsPage() ) {
                        return null;
                }
-               $revision = Revision::newFromTitle( $title );
+               $revision = Revision::newFromTitle( $title, false, Revision::READ_NORMAL );
                if ( !$revision ) {
                        return null;
                }
@@ -182,7 +182,7 @@ abstract class ResourceLoaderWikiModule extends ResourceLoaderModule {
                        // We're dealing with a subclass that doesn't have a DB
                        return array();
                }
-               
+
                $hash = $context->getHash();
                if ( isset( $this->titleMtimes[$hash] ) ) {
                        return $this->titleMtimes[$hash];
index b6484ce..1d6656a 100644 (file)
@@ -106,9 +106,9 @@ class SpecialBlock extends FormSpecialPage {
                $form->setSubmitTextMsg( $msg );
 
                # Don't need to do anything if the form has been posted
-               if( !$this->getRequest()->wasPosted() && $this->preErrors ){
+               if ( !$this->getRequest()->wasPosted() && $this->preErrors ) {
                        $s = HTMLForm::formatErrors( $this->preErrors );
-                       if( $s ){
+                       if ( $s ) {
                                $form->addHeaderText( Html::rawElement(
                                                'div',
                                                array( 'class' => 'error' ),
@@ -122,7 +122,7 @@ class SpecialBlock extends FormSpecialPage {
         * Get the HTMLForm descriptor array for the block form
         * @return Array
         */
-       protected function getFormFields(){
+       protected function getFormFields() {
                global $wgBlockAllowsUTEdit;
 
                $user = $this->getUser();
@@ -158,14 +158,14 @@ class SpecialBlock extends FormSpecialPage {
                        ),
                );
 
-               if( self::canBlockEmail( $user ) ) {
+               if ( self::canBlockEmail( $user ) ) {
                        $a['DisableEmail'] = array(
                                'type' => 'check',
                                'label-message' => 'ipbemailban',
                        );
                }
 
-               if( $wgBlockAllowsUTEdit ){
+               if ( $wgBlockAllowsUTEdit ) {
                        $a['DisableUTEdit'] = array(
                                'type' => 'check',
                                'label-message' => 'ipb-disableusertalk',
@@ -180,7 +180,7 @@ class SpecialBlock extends FormSpecialPage {
                );
 
                # Allow some users to hide name from block log, blocklist and listusers
-               if( $user->isAllowed( 'hideuser' ) ) {
+               if ( $user->isAllowed( 'hideuser' ) ) {
                        $a['HideUser'] = array(
                                'type' => 'check',
                                'label-message' => 'ipbhidename',
@@ -189,7 +189,7 @@ class SpecialBlock extends FormSpecialPage {
                }
 
                # Watchlist their user page? (Only if user is logged in)
-               if( $user->isLoggedIn() ) {
+               if ( $user->isLoggedIn() ) {
                        $a['Watch'] = array(
                                'type' => 'check',
                                'label-message' => 'ipbwatchuser',
@@ -228,7 +228,7 @@ class SpecialBlock extends FormSpecialPage {
         * @return Bool whether fields were altered (that is, whether the target is
         *     already blocked)
         */
-       protected function maybeAlterFormDefaults( &$fields ){
+       protected function maybeAlterFormDefaults( &$fields ) {
                # This will be overwritten by request data
                $fields['Target']['default'] = (string)$this->target;
 
@@ -237,7 +237,7 @@ class SpecialBlock extends FormSpecialPage {
 
                $block = Block::newFromTarget( $this->target );
 
-               if( $block instanceof Block && !$block->mAuto # The block exists and isn't an autoblock
+               if ( $block instanceof Block && !$block->mAuto # The block exists and isn't an autoblock
                        && ( $this->type != Block::TYPE_RANGE # The block isn't a rangeblock
                          || $block->getTarget() == $this->target ) # or if it is, the range is what we're about to block
                        )
@@ -246,21 +246,27 @@ class SpecialBlock extends FormSpecialPage {
                        $fields['CreateAccount']['default'] = $block->prevents( 'createaccount' );
                        $fields['AutoBlock']['default'] = $block->isAutoblocking();
 
-                       if( isset( $fields['DisableEmail'] ) ){
+                       if ( isset( $fields['DisableEmail'] ) ) {
                                $fields['DisableEmail']['default'] = $block->prevents( 'sendemail' );
                        }
 
-                       if( isset( $fields['HideUser'] ) ){
+                       if ( isset( $fields['HideUser'] ) ) {
                                $fields['HideUser']['default'] = $block->mHideName;
                        }
 
-                       if( isset( $fields['DisableUTEdit'] ) ){
+                       if ( isset( $fields['DisableUTEdit'] ) ) {
                                $fields['DisableUTEdit']['default'] = $block->prevents( 'editownusertalk' );
                        }
 
-                       $fields['Reason']['default'] = $block->mReason;
+                       // If the username was hidden (ipb_deleted == 1), don't show the reason
+                       // unless this user also has rights to hideuser: Bug 35839
+                       if ( !$block->mHideName || $this->getUser()->isAllowed( 'hideuser' ) ) {
+                               $fields['Reason']['default'] = $block->mReason;
+                       } else {
+                               $fields['Reason']['default'] = '';
+                       }
 
-                       if( $this->getRequest()->wasPosted() ){
+                       if ( $this->getRequest()->wasPosted() ) {
                                # Ok, so we got a POST submission asking us to reblock a user.  So show the
                                # confirm checkbox; the user will only see it if they haven't previously
                                $fields['Confirm']['type'] = 'check';
@@ -271,7 +277,7 @@ class SpecialBlock extends FormSpecialPage {
                                $fields['Confirm']['default'] = 1;
                        }
 
-                       if( $block->mExpiry == 'infinity' ) {
+                       if ( $block->mExpiry == 'infinity' ) {
                                $fields['Expiry']['default'] = 'infinite';
                        } else {
                                $fields['Expiry']['default'] = wfTimestamp( TS_RFC2822, $block->mExpiry );
@@ -282,14 +288,14 @@ class SpecialBlock extends FormSpecialPage {
                }
 
                # We always need confirmation to do HideUser
-               if( $this->requestedHideUser ){
+               if ( $this->requestedHideUser ) {
                        $fields['Confirm']['type'] = 'check';
                        unset( $fields['Confirm']['default'] );
                        $this->preErrors[] = 'ipb-confirmhideuser';
                }
 
                # Or if the user is trying to block themselves
-               if( (string)$this->target === $this->getUser()->getName() ){
+               if ( (string)$this->target === $this->getUser()->getName() ) {
                        $fields['Confirm']['type'] = 'check';
                        unset( $fields['Confirm']['default'] );
                        $this->preErrors[] = 'ipb-blockingself';
@@ -300,17 +306,17 @@ class SpecialBlock extends FormSpecialPage {
         * Add header elements like block log entries, etc.
         * @return String
         */
-       protected function preText(){
+       protected function preText() {
                $this->getOutput()->addModules( 'mediawiki.special.block' );
 
                $text = $this->msg( 'blockiptext' )->parse();
 
                $otherBlockMessages = array();
-               if( $this->target !== null ) {
+               if ( $this->target !== null ) {
                        # Get other blocks, i.e. from GlobalBlocking or TorBlock extension
                        wfRunHooks( 'OtherBlockLogLink', array( &$otherBlockMessages, $this->target ) );
 
-                       if( count( $otherBlockMessages ) ) {
+                       if ( count( $otherBlockMessages ) ) {
                                $s = Html::rawElement(
                                        'h2',
                                        array(),
@@ -319,7 +325,7 @@ class SpecialBlock extends FormSpecialPage {
 
                                $list = '';
 
-                               foreach( $otherBlockMessages as $link ) {
+                               foreach ( $otherBlockMessages as $link ) {
                                        $list .= Html::rawElement( 'li', array(), $link ) . "\n";
                                }
 
@@ -340,11 +346,11 @@ class SpecialBlock extends FormSpecialPage {
         * Add footer elements to the form
         * @return string
         */
-       protected function postText(){
+       protected function postText() {
                $links = array();
 
                # Link to the user's contributions, if applicable
-               if( $this->target instanceof User ){
+               if ( $this->target instanceof User ) {
                        $contribsPage = SpecialPage::getTitleFor( 'Contributions', $this->target->getName() );
                        $links[] = Linker::link(
                                $contribsPage,
@@ -353,7 +359,7 @@ class SpecialBlock extends FormSpecialPage {
                }
 
                # Link to unblock the specified user, or to a blank unblock form
-               if( $this->target instanceof User ) {
+               if ( $this->target instanceof User ) {
                        $message = $this->msg( 'ipb-unblock-addr', wfEscapeWikiText( $this->target->getName() ) )->parse();
                        $list = SpecialPage::getTitleFor( 'Unblock', $this->target->getName() );
                } else {
@@ -387,7 +393,7 @@ class SpecialBlock extends FormSpecialPage {
                );
 
                $userTitle = self::getTargetUserTitle( $this->target );
-               if( $userTitle ){
+               if ( $userTitle ) {
                        # Get relevant extracts from the block and suppression logs, if possible
                        $out = '';
 
@@ -405,7 +411,7 @@ class SpecialBlock extends FormSpecialPage {
                        $text .= $out;
 
                        # Add suppression block entries if allowed
-                       if( $user->isAllowed( 'suppressionlog' ) ) {
+                       if ( $user->isAllowed( 'suppressionlog' ) ) {
                                LogEventsList::showLogExtract(
                                        $out,
                                        'suppress',
@@ -433,7 +439,7 @@ class SpecialBlock extends FormSpecialPage {
         * @return Title|null
         */
        protected static function getTargetUserTitle( $target ) {
-               if( $target instanceof User ) {
+               if ( $target instanceof User ) {
                        return $target->getUserPage();
                } elseif ( IP::isIPAddress( $target ) ) {
                        return Title::makeTitleSafe( NS_USER, $target );
@@ -449,18 +455,18 @@ class SpecialBlock extends FormSpecialPage {
         * @param $request WebRequest optionally try and get data from a request too
         * @return array( User|string|null, Block::TYPE_ constant|null )
         */
-       public static function getTargetAndType( $par, WebRequest $request = null ){
+       public static function getTargetAndType( $par, WebRequest $request = null ) {
                $i = 0;
                $target = null;
 
-               while( true ){
-                       switch( $i++ ){
+               while( true ) {
+                       switch( $i++ ) {
                                case 0:
                                        # The HTMLForm will check wpTarget first and only if it doesn't get
                                        # a value use the default, which will be generated from the options
                                        # below; so this has to have a higher precedence here than $par, or
                                        # we could end up with different values in $this->target and the HTMLForm!
-                                       if( $request instanceof WebRequest ){
+                                       if ( $request instanceof WebRequest ) {
                                                $target = $request->getText( 'wpTarget', null );
                                        }
                                        break;
@@ -468,13 +474,13 @@ class SpecialBlock extends FormSpecialPage {
                                        $target = $par;
                                        break;
                                case 2:
-                                       if( $request instanceof WebRequest ){
+                                       if ( $request instanceof WebRequest ) {
                                                $target = $request->getText( 'ip', null );
                                        }
                                        break;
                                case 3:
                                        # B/C @since 1.18
-                                       if( $request instanceof WebRequest ){
+                                       if ( $request instanceof WebRequest ) {
                                                $target = $request->getText( 'wpBlockAddress', null );
                                        }
                                        break;
@@ -484,7 +490,7 @@ class SpecialBlock extends FormSpecialPage {
 
                        list( $target, $type ) = Block::parseTarget( $target );
 
-                       if( $type !== null ){
+                       if ( $type !== null ) {
                                return array( $target, $type );
                        }
                }
@@ -505,9 +511,9 @@ class SpecialBlock extends FormSpecialPage {
 
                list( $target, $type ) = self::getTargetAndType( $value );
 
-               if( $type == Block::TYPE_USER ){
+               if ( $type == Block::TYPE_USER ) {
                        # TODO: why do we not have a User->exists() method?
-                       if( !$target->getId() ){
+                       if ( !$target->getId() ) {
                                return $form->msg( 'nosuchusershort',
                                        wfEscapeWikiText( $target->getName() ) );
                        }
@@ -517,31 +523,31 @@ class SpecialBlock extends FormSpecialPage {
                                return $form->msg( 'badaccess', $status );
                        }
 
-               } elseif( $type == Block::TYPE_RANGE ){
+               } elseif ( $type == Block::TYPE_RANGE ) {
                        list( $ip, $range ) = explode( '/', $target, 2 );
 
-                       if( ( IP::isIPv4( $ip ) && $wgBlockCIDRLimit['IPv4'] == 32 )
+                       if ( ( IP::isIPv4( $ip ) && $wgBlockCIDRLimit['IPv4'] == 32 )
                                || ( IP::isIPv6( $ip ) && $wgBlockCIDRLimit['IPv6'] == 128 ) )
                        {
                                # Range block effectively disabled
                                return $form->msg( 'range_block_disabled' );
                        }
 
-                       if( ( IP::isIPv4( $ip ) && $range > 32 )
+                       if ( ( IP::isIPv4( $ip ) && $range > 32 )
                                || ( IP::isIPv6( $ip ) && $range > 128 ) )
                        {
                                # Dodgy range
                                return $form->msg( 'ip_range_invalid' );
                        }
 
-                       if( IP::isIPv4( $ip ) && $range < $wgBlockCIDRLimit['IPv4'] ) {
+                       if ( IP::isIPv4( $ip ) && $range < $wgBlockCIDRLimit['IPv4'] ) {
                                return $form->msg( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv4'] );
                        }
 
-                       if( IP::isIPv6( $ip ) && $range < $wgBlockCIDRLimit['IPv6'] ) {
+                       if ( IP::isIPv6( $ip ) && $range < $wgBlockCIDRLimit['IPv6'] ) {
                                return $form->msg( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv6'] );
                        }
-               } elseif( $type == Block::TYPE_IP ){
+               } elseif ( $type == Block::TYPE_IP ) {
                        # All is well
                } else {
                        return $form->msg( 'badipaddress' );
@@ -566,7 +572,7 @@ class SpecialBlock extends FormSpecialPage {
         * @param  $context IContextSource
         * @return Bool|String
         */
-       public static function processForm( array $data, IContextSource $context ){
+       public static function processForm( array $data, IContextSource $context ) {
                global $wgBlockAllowsUTEdit;
 
                $performer = $context->getUser();
@@ -579,7 +585,7 @@ class SpecialBlock extends FormSpecialPage {
                $data['Confirm'] = !in_array( $data['Confirm'], array( '', '0', null, false ), true );
 
                list( $target, $type ) = self::getTargetAndType( $data['Target'] );
-               if( $type == Block::TYPE_USER ){
+               if ( $type == Block::TYPE_USER ) {
                        $user = $target;
                        $target = $user->getName();
                        $userId = $user->getId();
@@ -591,14 +597,14 @@ class SpecialBlock extends FormSpecialPage {
                        # since both $data['PreviousTarget'] and $target are normalized
                        # but $data['target'] gets overriden by (non-normalized) request variable
                        # from previous request.
-                       if( $target === $performer->getName() &&
+                       if ( $target === $performer->getName() &&
                                ( $data['PreviousTarget'] !== $target || !$data['Confirm'] ) )
                        {
                                return array( 'ipb-blockingself' );
                        }
-               } elseif( $type == Block::TYPE_RANGE ){
+               } elseif ( $type == Block::TYPE_RANGE ) {
                        $userId = 0;
-               } elseif( $type == Block::TYPE_IP ){
+               } elseif ( $type == Block::TYPE_IP ) {
                        $target = $target->getName();
                        $userId = 0;
                } else {
@@ -606,24 +612,24 @@ class SpecialBlock extends FormSpecialPage {
                        return array( 'badipaddress' );
                }
 
-               if( ( strlen( $data['Expiry'] ) == 0) || ( strlen( $data['Expiry'] ) > 50 )
+               if ( ( strlen( $data['Expiry'] ) == 0) || ( strlen( $data['Expiry'] ) > 50 )
                        || !self::parseExpiryInput( $data['Expiry'] ) )
                {
                        return array( 'ipb_expiry_invalid' );
                }
 
-               if( !isset( $data['DisableEmail'] ) ){
+               if ( !isset( $data['DisableEmail'] ) ) {
                        $data['DisableEmail'] = false;
                }
 
                # If the user has done the form 'properly', they won't even have been given the
                # option to suppress-block unless they have the 'hideuser' permission
-               if( !isset( $data['HideUser'] ) ){
+               if ( !isset( $data['HideUser'] ) ) {
                        $data['HideUser'] = false;
                }
 
-               if( $data['HideUser'] ) {
-                       if( !$performer->isAllowed('hideuser') ){
+               if ( $data['HideUser'] ) {
+                       if ( !$performer->isAllowed('hideuser') ) {
                                # this codepath is unreachable except by a malicious user spoofing forms,
                                # or by race conditions (user has oversight and sysop, loads block form,
                                # and is de-oversighted before submission); so need to fail completely
@@ -632,16 +638,16 @@ class SpecialBlock extends FormSpecialPage {
                        }
 
                        # Recheck params here...
-                       if( $type != Block::TYPE_USER ) {
+                       if ( $type != Block::TYPE_USER ) {
                                $data['HideUser'] = false; # IP users should not be hidden
-                       } elseif( !in_array( $data['Expiry'], array( 'infinite', 'infinity', 'indefinite' ) ) ) {
+                       } elseif ( !in_array( $data['Expiry'], array( 'infinite', 'infinity', 'indefinite' ) ) ) {
                                # Bad expiry.
                                return array( 'ipb_expiry_temp' );
-                       } elseif( $user->getEditCount() > self::HIDEUSER_CONTRIBLIMIT ) {
+                       } elseif ( $user->getEditCount() > self::HIDEUSER_CONTRIBLIMIT ) {
                                # Typically, the user should have a handful of edits.
                                # Disallow hiding users with many edits for performance.
                                return array( 'ipb_hide_invalid' );
-                       } elseif( !$data['Confirm'] ){
+                       } elseif ( !$data['Confirm'] ) {
                                return array( 'ipb-confirmhideuser' );
                        }
                }
@@ -659,15 +665,15 @@ class SpecialBlock extends FormSpecialPage {
                $block->isAutoblocking( $data['AutoBlock'] );
                $block->mHideName = $data['HideUser'];
 
-               if( !wfRunHooks( 'BlockIp', array( &$block, &$performer ) ) ) {
+               if ( !wfRunHooks( 'BlockIp', array( &$block, &$performer ) ) ) {
                        return array( 'hookaborted' );
                }
 
                # Try to insert block. Is there a conflicting block?
                $status = $block->insert();
-               if( !$status ) {
+               if ( !$status ) {
                        # Show form unless the user is already aware of this...
-                       if( !$data['Confirm'] || ( array_key_exists( 'PreviousTarget', $data )
+                       if ( !$data['Confirm'] || ( array_key_exists( 'PreviousTarget', $data )
                                && $data['PreviousTarget'] !== $target ) )
                        {
                                return array( array( 'ipb_already_blocked', $block->getTarget() ) );
@@ -677,13 +683,13 @@ class SpecialBlock extends FormSpecialPage {
                                # be sure the user is blocked by now it should work for our purposes
                                $currentBlock = Block::newFromTarget( $target );
 
-                               if( $block->equals( $currentBlock ) ) {
+                               if ( $block->equals( $currentBlock ) ) {
                                        return array( array( 'ipb_already_blocked', $block->getTarget() ) );
                                }
 
                                # If the name was hidden and the blocking user cannot hide
                                # names, then don't allow any block changes...
-                               if( $currentBlock->mHideName && !$performer->isAllowed( 'hideuser' ) ) {
+                               if ( $currentBlock->mHideName && !$performer->isAllowed( 'hideuser' ) ) {
                                        return array( 'cant-see-hidden-user' );
                                }
 
@@ -692,12 +698,12 @@ class SpecialBlock extends FormSpecialPage {
                                $logaction = 'reblock';
 
                                # Unset _deleted fields if requested
-                               if( $currentBlock->mHideName && !$data['HideUser'] ) {
+                               if ( $currentBlock->mHideName && !$data['HideUser'] ) {
                                        RevisionDeleteUser::unsuppressUserName( $target, $userId );
                                }
 
                                # If hiding/unhiding a name, this should go in the private logs
-                               if( (bool)$currentBlock->mHideName ){
+                               if ( (bool)$currentBlock->mHideName ) {
                                        $data['HideUser'] = true;
                                }
                        }
@@ -708,12 +714,12 @@ class SpecialBlock extends FormSpecialPage {
                wfRunHooks( 'BlockIpComplete', array( $block, $performer ) );
 
                # Set *_deleted fields if requested
-               if( $data['HideUser'] ) {
+               if ( $data['HideUser'] ) {
                        RevisionDeleteUser::suppressUserName( $target, $userId );
                }
 
                # Can't watch a rangeblock
-               if( $type != Block::TYPE_RANGE && $data['Watch'] ) {
+               if ( $type != Block::TYPE_RANGE && $data['Watch'] ) {
                        $performer->addWatch( Title::makeTitle( NS_USER, $target ) );
                }
 
@@ -751,18 +757,18 @@ class SpecialBlock extends FormSpecialPage {
         *     the wiki's content language
         * @return Array
         */
-       public static function getSuggestedDurations( $lang = null ){
+       public static function getSuggestedDurations( $lang = null ) {
                $a = array();
                $msg = $lang === null
                        ? wfMessage( 'ipboptions' )->inContentLanguage()->text()
                        : wfMessage( 'ipboptions' )->inLanguage( $lang )->text();
 
-               if( $msg == '-' ){
+               if ( $msg == '-' ) {
                        return array();
                }
 
-               foreach( explode( ',', $msg ) as $option ) {
-                       if( strpos( $option, ':' ) === false ){
+               foreach ( explode( ',', $msg ) as $option ) {
+                       if ( strpos( $option, ':' ) === false ) {
                                $option = "$option:$option";
                        }
 
@@ -781,7 +787,7 @@ class SpecialBlock extends FormSpecialPage {
         */
        public static function parseExpiryInput( $expiry ) {
                static $infinity;
-               if( $infinity == null ){
+               if ( $infinity == null ) {
                        $infinity = wfGetDB( DB_SLAVE )->getInfinity();
                }
 
@@ -826,8 +832,8 @@ class SpecialBlock extends FormSpecialPage {
                        $user = User::newFromName( $user );
                }
 
-               if( $performer->isBlocked() ){
-                       if( $user instanceof User && $user->getId() == $performer->getId() ) {
+               if ( $performer->isBlocked() ) {
+                       if ( $user instanceof User && $user->getId() == $performer->getId() ) {
                                # User is trying to unblock themselves
                                if ( $performer->isAllowed( 'unblockself' ) ) {
                                        return true;
@@ -851,40 +857,41 @@ class SpecialBlock extends FormSpecialPage {
         * reader for this block, to provide more information in the logs
         * @param $data Array from HTMLForm data
         * @param $type Block::TYPE_ constant (USER, RANGE, or IP)
-        * @return array
+        * @return string
         */
        protected static function blockLogFlags( array $data, $type ) {
                global $wgBlockAllowsUTEdit;
                $flags = array();
 
-               # when blocking a user the option 'anononly' is not available/has no effect -> do not write this into log
-               if( !$data['HardBlock'] && $type != Block::TYPE_USER ){
+               # when blocking a user the option 'anononly' is not available/has no effect
+               # -> do not write this into log
+               if ( !$data['HardBlock'] && $type != Block::TYPE_USER ) {
                        // For grepping: message block-log-flags-anononly
                        $flags[] = 'anononly';
                }
 
-               if( $data['CreateAccount'] ){
+               if ( $data['CreateAccount'] ) {
                        // For grepping: message block-log-flags-nocreate
                        $flags[] = 'nocreate';
                }
 
                # Same as anononly, this is not displayed when blocking an IP address
-               if( !$data['AutoBlock'] && $type == Block::TYPE_USER ){
+               if ( !$data['AutoBlock'] && $type == Block::TYPE_USER ) {
                        // For grepping: message block-log-flags-noautoblock
                        $flags[] = 'noautoblock';
                }
 
-               if( $data['DisableEmail'] ){
+               if ( $data['DisableEmail'] ) {
                        // For grepping: message block-log-flags-noemail
                        $flags[] = 'noemail';
                }
 
-               if( $wgBlockAllowsUTEdit && $data['DisableUTEdit'] ){
+               if ( $wgBlockAllowsUTEdit && $data['DisableUTEdit'] ) {
                        // For grepping: message block-log-flags-nousertalk
                        $flags[] = 'nousertalk';
                }
 
-               if( $data['HideUser'] ){
+               if ( $data['HideUser'] ) {
                        // For grepping: message block-log-flags-hiddenname
                        $flags[] = 'hiddenname';
                }
index ac54faa..43ea345 100644 (file)
@@ -789,8 +789,6 @@ class UploadForm extends HTMLForm {
         * @return Array: descriptor array
         */
        protected function getSourceSection() {
-               global $wgCopyUploadsFromSpecialUpload;
-
                if ( $this->mSessionKey ) {
                        return array(
                                'SessionKey' => array(
@@ -804,9 +802,7 @@ class UploadForm extends HTMLForm {
                        );
                }
 
-               $canUploadByUrl = UploadFromUrl::isEnabled()
-                       && UploadFromUrl::isAllowed( $this->getUser() )
-                       && $wgCopyUploadsFromSpecialUpload;
+               $canUploadByUrl = UploadFromUrl::isEnabled() && UploadFromUrl::isAllowed( $this->getUser() );
                $radio = $canUploadByUrl;
                $selectedSourceType = strtolower( $this->getRequest()->getText( 'wpSourceType', 'File' ) );
 
index 2bb53ab..fd603ce 100755 (executable)
@@ -31,7 +31,7 @@ def unichr3( *args ):
 
 # DEFINE
 UNIHAN_VER = '5.2.0'
-SF_MIRROR = 'cdnetworks-kr-2'
+SF_MIRROR = 'dfn'
 SCIM_TABLES_VER = '0.5.10'
 SCIM_PINYIN_VER = '0.5.91'
 LIBTABE_VER = '0.2.3'
@@ -370,7 +370,7 @@ $zh2Hant = array(\n'''
         +  PHPArray( toCN ) \
         +  '\n);\n\n$zh2SG = array(\n' \
         +  PHPArray( toSG ) \
-        +  '\n);'
+        +  '\n);\n'
     
     f = open( os.path.join( '..', 'ZhConversion.php' ), 'wb', encoding = 'utf8' )
     print ('Writing ZhConversion.php ... ')
index 2ebb750..1f7fe7d 100644 (file)
 分布于      分佈於
 分布於      分佈於
 想象 想像
+無線電視   無綫電視
+无线电视   無綫電視
+無線收費   無綫收費
+无线收费   無綫收費
+無線節目   無綫節目
+无线节目   無綫節目
+無線劇集   無綫劇集
+无线剧集   無綫劇集
+東鐵線      東鐵綫
+东铁线      東鐵綫
+觀塘線      觀塘綫
+观塘线      觀塘綫
+荃灣線      荃灣綫
+荃湾线      荃灣綫
+港島線      港島綫
+港岛线      港島綫
+東涌線      東涌綫
+东涌线      東涌綫
+將軍澳線   將軍澳綫
+将军澳线   將軍澳綫
+西鐵線      西鐵綫
+西铁线      西鐵綫
+馬鞍山線   馬鞍山綫
+马鞍山线   馬鞍山綫
+迪士尼線   迪士尼綫
+迪士尼线   迪士尼綫
+沙田至中環線     沙田至中環綫
+沙田至中环线     沙田至中環綫
+沙中線      沙中綫
+沙中线      沙中綫
+北環線      北環綫
+北环线      北環綫
+機場快線   機場快綫
+机场快线   機場快綫
+505線 505綫
+505线 505綫
+507線 507綫
+507线 507綫
+610線 610綫
+610线 610綫
+614線 614綫
+614线 614綫
+614P線        614P綫
+614P线        614P綫
+615線 615綫
+615线 615綫
+615P線        615P綫
+615P线        615P綫
+705線 705綫
+705线 705綫
+706線 706綫
+706线 706綫
+751線 751綫
+751线 751綫
+751P線        751P綫
+751P线        751P綫
+761P線        761P綫
+761P线        761P綫
index 35b6268..1a14e99 100644 (file)
 想象 想像
 锎    鉲
 信道 信道
+綫    線
index fe9e031..54f23dc 100644 (file)
@@ -6,30 +6,30 @@
                        <pluralRule count="one">n is 1</pluralRule>
                        <pluralRule count="two">n is 2</pluralRule>
                </pluralRules>
+               <pluralRules locales="dsb">
+                       <pluralRule count="one">n mod 100 is 1</pluralRule>
+                       <pluralRule count="two">n mod 100 is 2</pluralRule>
+                       <pluralRule count="few">n mod 100 in 3..4</pluralRule>
+               </pluralRules>
+               <pluralRules locales="cu">
+                       <pluralRule count="one">n mod 10 is 1</pluralRule>
+                       <pluralRule count="two">n mod 10 is 2</pluralRule>
+                       <pluralRule count="few">n mod 10 in 3..4</pluralRule>
+               </pluralRules>
+               <!-- Plural form transformations
+               Based on this discussion: http://translatewiki.net/wiki/Thread:Support/New_plural_rules_for_Scots_Gaelic_(gd)
+               $forms[0] - 1
+               $forms[1] - 2
+               $forms[2] - 11
+               $forms[3] - 12
+               $forms[4] - 3-10, 13-19
+               $forms[5] - 0, 20, rest -->
+               <pluralRules locales="gd">
+                       <pluralRule count="one">n is 1</pluralRule>
+                       <pluralRule count="two">n is 2</pluralRule>
+                       <pluralRule count="elevan">n is 11</pluralRule>
+                       <pluralRule count="twelve">n is 12</pluralRule>
+                       <pluralRule count="few">n in 3..10 or n in 13..19</pluralRule>
+               </pluralRules>
        </plurals>
-       <pluralRules locales="dsb">
-               <pluralRule count="one">n mod 100 is 1</pluralRule>
-               <pluralRule count="two">n mod 100 is 2</pluralRule>
-               <pluralRule count="few">n mod 100 in 3..4</pluralRule>
-       </pluralRules>
-       <pluralRules locales="cu">
-               <pluralRule count="one">n mod 10 is 1</pluralRule>
-               <pluralRule count="two">n mod 10 is 2</pluralRule>
-               <pluralRule count="few">n mod 10 in 3..4</pluralRule>
-       </pluralRules>
-       <!-- Plural form transformations
-       Based on this discussion: http://translatewiki.net/wiki/Thread:Support/New_plural_rules_for_Scots_Gaelic_(gd)
-       $forms[0] - 1
-       $forms[1] - 2
-       $forms[2] - 11
-       $forms[3] - 12
-       $forms[4] - 3-10, 13-19
-       $forms[5] - 0, 20, rest -->
-       <pluralRules locales="gd">
-               <pluralRule count="one">n is 1</pluralRule>
-               <pluralRule count="two">n is 2</pluralRule>
-               <pluralRule count="elevan">n is 11</pluralRule>
-               <pluralRule count="twelve">n is 12</pluralRule>
-               <pluralRule count="few">n in 3..10 or n in 13..19</pluralRule>
-       </pluralRules>
 </supplementalData>
index fabcfde..63f32bf 100644 (file)
@@ -495,9 +495,9 @@ Ur roll eus ar pajennoù dibar reizh a c'hallit kavour war [[Special:SpecialPage
 'databaseerror' => 'Fazi bank roadennoù',
 'dberrortext' => 'C\'hoarvezet ez eus ur fazi ereadur eus ar reked er bank roadennoù, ar pezh a c\'hall talvezout ez eus un draen er meziant.
 Setu ar goulenn bet pledet gantañ da ziwezhañ :
-<blockquote><tt>$1</tt></blockquote>
-adal an arc\'hwel "<tt>$2</tt>".
-Adkaset eo bet ar fazi "<tt>$3: $4</tt>" gant ar bank roadennoù.',
+<blockquote><code>$1</code></blockquote>
+adal an arc\'hwel "<code>$2</code>".
+Adkaset eo bet ar fazi "<samp>$3: $4</samp>" gant ar bank roadennoù.',
 'dberrortextcl' => 'Ur fazi ereadur zo en ur reked savet ouzh ar bank roadennoù.
 Setu ar goulenn bet pledet gantañ da ziwezhañ :
 "$1"
@@ -837,10 +837,10 @@ pe <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}}
 'blocked-notice-logextract' => "Stanket eo an implijer-mañ evit poent.
 Dindan emañ merket moned diwezhañ marilh ar stankadennoù, d'ho kelaouiñ :",
 'clearyourcache' => "Notenn :''' Goude bezañ enrollet ho pajenn e rankot freskaat krubuilh ho merdeer a-benn gwelet ar c'hemmoù.
-* '''Firefox / Safari: ''' Derc'hel da bouezañ war ''Pennlizherenn'' en ur glikañ war ''Adkargañ'', pe pouezañ war ''Ctrl-F5'' pe ''Ctrl-R'' (''⌘-R'' war ur Mac); 
-* '''Google Chrome:''' Pouezañ war ''Ctrl-Pennlizh-R'' (''⌘-Shift-R'' war ur Mac)
+* '''Firefox / Safari:''' Derc'hel da bouezañ war ''Pennlizherenn'' en ur glikañ war ''Adkargañ'', pe pouezañ war ''Ctrl-F5'' pe ''Ctrl-R'' (''⌘-R'' war ur Mac); 
+* ''''Google Chrome:''' Pouezañ war ''Ctrl-Pennlizh-R'' (''⌘-Shift-R'' war ur Mac)
 * '''Internet Explorer:''' Derc'hel da bouezañ war ''Ctrl'' en ur glikañ war ''Freskaat,'' pe pouezañ war ''Ctrl-F5''
-* '''Konqueror: '''Klikañ war ''Adkargañ'' pe pouezañ war ''F5;'' 
+* ''''Konqueror: '''Klikañ war ''Adkargañ'' pe pouezañ war ''F5;'' 
 * '''Opera:''' Riñsañ ar grubuilh e ''Ostilhoù → Penndibaboù''",
 'usercssyoucanpreview' => "'''Tun :''' Grit gant ar bouton \"{{int:showpreview}}\" evit testiñ ho follenn CSS nevez a-raok enrollañ anezhi.",
 'userjsyoucanpreview' => "'''Tun :''' Grit gant ar bouton \"{{int:showpreview}}\" evit testiñ ho follenn JS nevez a-raok enrollañ anezhi.",
@@ -2158,7 +2158,7 @@ a-benn gellout kemmañ ho roll evezhiañ.",
 'addedwatchtext' => 'Ouzh ho [[Special:Watchlist|rollad evezhiañ]] eo bet ouzhpennet ar bajenn "[[:$1]]".
 Kemmoù da zont ar bajenn-mañ ha re ar bajenn gaozeal stag outi a vo rollet amañ hag e teuio ar bajenn <b>e tev</b> er [[Special:RecentChanges|roll kemmoù diwezhañ]] evit bezañ gwelet aesoc\'h ganeoc\'h.
 
-Evit tennañ ar bajenn-mañ a-ziwar ho rollad evezhiañ. klikit war "Paouez da evezhiañ" er framm merdeiñ.',
+Evit tennañ ar bajenn-mañ a-ziwar ho rollad evezhiañ, klikit war "Paouez da evezhiañ" er framm merdeiñ.',
 'removewatch' => 'Lemel a-ziwar ar roll evezhiañ',
 'removedwatchtext' => 'Lamet eo bet ar bajenn "[[:$1]]" a-ziwar ho [[Special:Watchlist|roll evezhiañ]].',
 'watch' => 'Evezhiañ',
index d0d6682..263acb9 100644 (file)
@@ -523,7 +523,7 @@ $1',
 'disclaimerpage' => 'Project:Uslovi korištenja, pravne napomene i odricanje odgovornosti',
 'edithelp' => 'Pomoć pri uređivanju stranice',
 'edithelppage' => 'Help:Uređivanje',
-'helppage' => 'Pomoć:Sadržaj',
+'helppage' => 'Help:Sadržaj',
 'mainpage' => 'Početna strana',
 'mainpage-description' => 'Početna strana',
 'policy-url' => 'Project:Pravila',
@@ -666,6 +666,7 @@ $2',
 'ns-specialprotected' => 'Specijalne stranice se ne mogu uređivati.',
 'titleprotected' => 'Naslov stranice je zaštićen od postavljanja od strane korisnika [[User:$1|$1]].
 Iz razloga "\'\'$2\'\'".',
+'exception-nologin' => 'Niste prijavljeni',
 
 # Virus scanner
 'virus-badscanner' => "Loša konfiguracija: nepoznati anti-virus program: ''$1''",
@@ -3022,6 +3023,8 @@ Ovo je vjerovatno izazvao vezom ka vanjskoj nepoželjnoj stranici.',
 # Info page
 'pageinfo-title' => 'Informacije za "$1"',
 'pageinfo-header-edits' => 'Izmjene',
+'pageinfo-header-restrictions' => 'Zaštita stranice',
+'pageinfo-article-id' => 'ID stranice',
 'pageinfo-views' => 'Broj pogleda',
 'pageinfo-watchers' => 'Broj onih koji pregledaju',
 'pageinfo-edits' => 'Broj izmjena',
index 06aa3b9..03f45f7 100644 (file)
@@ -413,6 +413,10 @@ $1',
 'youhavenewmessages' => '$1ت ھەیە ($2).',
 'newmessageslink' => 'پەیامی نوێ',
 'newmessagesdifflink' => 'دوا گۆڕانکارییەکان',
+'youhavenewmessagesfromusers' => '$1ت لە {{PLURAL:$3|بەکارھێنەرێکی تر| $3 بەکارھێنەر}} ھەیە ( $2 ).',
+'youhavenewmessagesmanyusers' => '$1ت  لە ژمارەیەک بەکارھێنەر ھەیە ( $2 ).',
+'newmessageslinkplural' => '{{PLURAL:$1|پەیامێکی نوێ|پەیامی نوێ}}',
+'newmessagesdifflinkplural' => 'دوایین {{PLURAL:$1|گۆڕانکاری|گۆڕانکارییەکان}}',
 'youhavenewmessagesmulti' => 'لە $1 دا پەیامی نوێت ھەیە',
 'editsection' => 'دەستکاری',
 'editold' => 'دەستکاری',
@@ -1252,7 +1256,7 @@ $1",
 'badsig' => 'ئیمزاكه‌ هه‌ڵه‌یه‌، ته‌ماشای كۆدی HTML بكه‌‌',
 'badsiglength' => 'واژۆکەت زۆر درێژە.
 واژۆ نابێ لە $1 {{PLURAL:$1|نووسە|نووسە}} درێژتر بێت.',
-'yourgender' => 'ڕەگەز:',
+'yourgender' => 'زایەند:',
 'gender-unknown' => 'ئاشکرا نەکراو',
 'gender-male' => 'پیاو',
 'gender-female' => 'ژن',
@@ -3149,7 +3153,7 @@ $5
 
 # Signatures
 'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|لێدوان]])',
-'timezone-utc' => 'بەکاتی جیهانی',
+'timezone-utc' => 'UTC',
 
 # Core parser functions
 'unknown_extension_tag' => 'تاگی درێژکراوەی نەناسراو "$1"',
index e039978..3985e1e 100644 (file)
@@ -404,7 +404,7 @@ $messages = array(
 'redirectedfrom' => '(Omdirigeret fra $1)',
 'redirectpagesub' => 'Omdirigering',
 'lastmodifiedat' => 'Denne side blev senest ændret $1 kl. $2.',
-'viewcount' => 'Siden er vist {{PLURAL:$1|en gang|$1 gange}}.',
+'viewcount' => 'Siden er vist {{PLURAL:$1|en gang|$1 gange}}.',
 'protectedpage' => 'Beskyttet side',
 'jumpto' => 'Skift til:',
 'jumptonavigation' => 'Navigation',
@@ -858,7 +858,6 @@ Loggen over den seneste blokering ses nedenfor:',
 * '''Firefox / Safari:''' Hold ''shifttasten'' nede og klik på ''reload'', eller tryk enten ''Ctrl-F5'' eller ''Ctrl-Shift-r'' (''⌘-R'' på en Mac).
 * '''Google Chrome:''' Tryk ''Ctrl-Shift-R'' (''⌘-Shift-R'' på en Mac).
 * '''Internet Explorer:''' Hold ''controltasten'' nede og klik på ''refresh'' eller tryk på ''Ctrl-F5''.
-* '''Konqueror:''' Klik på ''reload'' eller tryk på ''F5''.
 * '''Opera:''' Tøm cachen i ''Tools → Preferences''.",
 'usercssyoucanpreview' => "'''Tip:''' Brug \"{{int:showpreview}}\"-knappen for at teste dit nye CSS inden du gemmer.",
 'userjsyoucanpreview' => "'''Tip:''' Brug \"{{int:showpreview}}\"-knappen for at teste dit nye JavaScript inden du gemmer.",
@@ -1836,6 +1835,7 @@ Måske vil du redigere beskrivelsen på dens [$2 filbeskrivelsesside] der.',
 'uploadnewversion-linktext' => 'Læg en ny version af denne fil op',
 'shared-repo-from' => 'fra $1',
 'shared-repo' => 'et delt filarkiv',
+'upload-disallowed-here' => 'Desværre kan du ikke overskrive dette billede.',
 
 # File reversion
 'filerevert' => 'Gendan $1',
@@ -1944,6 +1944,7 @@ Hver linje indeholder henvisninger til den første og den anden omdirigering, s
 # Miscellaneous special pages
 'nbytes' => '$1 {{PLURAL:$1|byte|bytes}}',
 'ncategories' => '$1 {{PLURAL:$1|kategori|kategorier}}',
+'ninterwikis' => '$1 {{PLURAL:$1|interwikilink|interwikilinks}}',
 'nlinks' => '{{PLURAL:$1|1 henvisning|$1 henvisninger}}',
 'nmembers' => '$1 {{PLURAL:$1|medlem|medlemmer}}',
 'nrevisions' => '{{PLURAL:$1|1 ændring|$1 ændringer}}',
@@ -1972,6 +1973,7 @@ Hver linje indeholder henvisninger til den første og den anden omdirigering, s
 'mostlinkedtemplates' => 'Hyppigst brugte skabeloner',
 'mostcategories' => 'Mest brugte sider',
 'mostimages' => 'Mest brugte filer',
+'mostinterwikis' => 'Sider med flest interwikilinks',
 'mostrevisions' => 'Sider med de fleste ændringer',
 'prefixindex' => 'Alle sider med præfiks',
 'prefixindex-namespace' => 'Alle sider med præfiks (navnerummet $1)',
@@ -2118,6 +2120,8 @@ Der findes muligvis [[{{MediaWiki:Listgrouprights-helppage}}|yderligere informat
 'mailnologin' => 'Du er ikke logget på',
 'mailnologintext' => 'Du skal være [[Special:UserLogin|logget på]] og have en gyldig e-mailadresse sat i dine [[Special:Preferences|indstillinger]] for at sende e-mail til andre brugere.',
 'emailuser' => 'E-mail til denne bruger',
+'emailuser-title-target' => 'Send email til denne {{GENDER:$1|bruger}}',
+'emailuser-title-notarget' => 'Send email til en bruger',
 'emailpage' => 'E-mail bruger',
 'emailpagetext' => 'Du kan bruge formularen nedenfor til at sende en e-mail til denne bruger.
 Den e-mail-adresse du har angivet i [[Special:Preferences|dine indstillinger]] vil dukke op i "fra"-feltet på e-mailen, så modtageren kan svare dig.',
@@ -2895,11 +2899,27 @@ Dette skyldes sandsynligvis en henvisning til et sortlistet eksternt websted.',
 
 # Info page
 'pageinfo-title' => 'Information om "$1"',
-'pageinfo-header-edits' => 'Redigeringer',
+'pageinfo-header-basic' => 'Grundlæggende oplysninger',
+'pageinfo-header-edits' => 'Redigeringshistorik',
+'pageinfo-header-restrictions' => 'Sidebeskyttelse',
+'pageinfo-header-properties' => 'Sideegenskaber',
+'pageinfo-default-sort' => 'Standardsorteringsnøgle',
+'pageinfo-length' => 'Sidelængde (i bytes)',
+'pageinfo-article-id' => 'Side-ID',
+'pageinfo-robot-index' => 'Indekserbar',
+'pageinfo-robot-noindex' => 'Ikke indekserbar',
 'pageinfo-views' => 'Antal visninger',
 'pageinfo-watchers' => 'Antal brugere, der overvåger siden',
-'pageinfo-edits' => 'Antal redigeringer',
-'pageinfo-authors' => 'Antal forskellige forfattere',
+'pageinfo-redirects-name' => 'Omdirigeringer til denne side',
+'pageinfo-subpages-name' => 'Undersider til denne side',
+'pageinfo-firsttime' => 'Dato for oprettelsen af siden',
+'pageinfo-lasttime' => 'Dato for seneste redigering',
+'pageinfo-edits' => 'Samlet antal redigeringer',
+'pageinfo-authors' => 'Det samlede antal forskellige forfattere',
+'pageinfo-restriction' => 'Sidebeskyttelse (<code>{{lcfirst:$1}}</code>)',
+'pageinfo-magic-words' => '{{PLURAL:$1|Magisk|Magiske}} ord ($1)',
+'pageinfo-hidden-categories' => '{{PLURAL:$1|Skjult kategori|Skjulte kategorier}} ($1)',
+'pageinfo-templates' => '{{PLURAL:$1|Transkluderet skabelon|Transkluderede skabeloner}} ($1)',
 
 # Skin names
 'skinname-standard' => 'Klassik',
@@ -2953,6 +2973,7 @@ Du kan beskadige dit system hvis du udfører den.",
 'file-info-size-pages' => '$1 × $2 punkter, filstørrelse: $3, MIME-type: $4, $5 {{PLURAL:$5|side|sider}}',
 'file-nohires' => 'Ingen højere opløsning fundet.',
 'svg-long-desc' => 'SVG fil, basisstørrelse $1 × $2 punkters, størrelse: $3',
+'svg-long-desc-animated' => 'Animeret SVG-fil, basisstørrelse $1 × $2 punkter, filstørrelse: $3',
 'show-big-image' => 'Version i større opløsning',
 'show-big-image-preview' => 'Størrelse af denne forhåndsvisning: $1.',
 'show-big-image-other' => '{{PLURAL:$2|Anden opløsning|Andre opløsninger}}: $1.',
@@ -2962,6 +2983,8 @@ Du kan beskadige dit system hvis du udfører den.",
 'file-info-png-looped' => 'gentaget',
 'file-info-png-repeat' => 'afspillede $1 {{PLURAL:$1|gang|gange}}',
 'file-info-png-frames' => '$1 {{PLURAL:$1|billede|billeder}}',
+'file-no-thumb-animation' => "'''Bemærk: På grund af tekniske begrænsninger vil miniaturebilleder af denne fil ikke blive animeret.'''",
+'file-no-thumb-animation-gif' => "'''Bemærk: På grund af tekniske begrænsninger vil miniaturebilleder af GIF-filer, der som denne er i høj opløsning, ikke blive animeret.'''",
 
 # Special:NewFiles
 'newimages' => 'Galleri med de nyeste billeder',
index 72f9a97..2dd650f 100644 (file)
@@ -2062,8 +2062,8 @@ Eine [[Special:WhatLinksHere/$2|vollständige Liste]] ist verfügbar.',
 Vielleicht möchtest du die Beschreibung auf der dortigen [$2 Dateibeschreibungsseite] bearbeiten.',
 'sharedupload-desc-create' => 'Diese Datei stammt aus $1 und kann von anderen Projekten verwendet werden.
 Vielleicht möchtest du die Beschreibung auf der dortigen [$2 Dateibeschreibungsseite] bearbeiten.',
-'filepage-nofile' => 'Es ist keine Datei dieses namens vorhanden.',
-'filepage-nofile-link' => 'Es ist keine Datei dieses namens vorhanden. Du kannst jedoch [$1 diese Datei hochladen].',
+'filepage-nofile' => 'Es ist keine Datei dieses Namens vorhanden.',
+'filepage-nofile-link' => 'Es ist keine Datei dieses Namens vorhanden. Du kannst jedoch [$1 diese Datei hochladen].',
 'uploadnewversion-linktext' => 'Eine neue Version dieser Datei hochladen',
 'shared-repo-from' => 'aus $1',
 'shared-repo' => 'einem gemeinsam genutzten Medienarchiv',
index ce239ae..af3f213 100644 (file)
@@ -2211,7 +2211,7 @@ hem zi bıewnê [[Special:WantedCategories|kategori yê ke waziyeni]].',
 # Special:DeletedContributions
 'deletedcontributions' => 'İştiraqê karberan de besternayına',
 'deletedcontributions-title' => 'Îştirakê karberî wederna',
-'sp-deletedcontributions-contribs' => 'iştıraqi',
+'sp-deletedcontributions-contribs' => 'pêşteni',
 
 # Special:LinkSearch
 'linksearch' => 'Gıreyê teberi cı geyrê',
@@ -2558,9 +2558,9 @@ $1',
 'blanknamespace' => '(Ser)',
 
 # Contributions
-'contributions' => 'İştirakê karberi',
+'contributions' => 'İştiraqê karberi',
 'contributions-title' => '$1 de iştırakê karberi',
-'mycontris' => 'İştıraqi',
+'mycontris' => 'Pêşteni',
 'contribsub2' => 'Qandê $1 ($2)',
 'nocontribs' => 'Ena kriteriya de vurnayîş çini yo.',
 'uctop' => '(ser)',
@@ -2683,7 +2683,7 @@ Cıkewtışo tewr peyêno ke bloke biyo, cêr seba referansi belikerdeyo:',
 'blocklink' => 'kılit ke',
 'unblocklink' => 'ake',
 'change-blocklink' => 'kılit-kerdışi bıvurne',
-'contribslink' => 'iştıraqi',
+'contribslink' => 'pêşteni',
 'emaillink' => 'e-poste bırışe',
 'autoblocker' => 'Şıma otomatikmen kılit biy, çıke adresa şımawa \'\'IP\'\'y terefê "[[User:$1|$1]]" gureniyena.
 Sebebê kılit-biyayışê $1\'i: "$2"o',
@@ -3664,7 +3664,7 @@ $8',
 'exif-ycbcrpositioning-1' => 'Wertekerdış',
 'exif-ycbcrpositioning-2' => 'Wayırê-site',
 
-'exif-dc-contributor' => 'İştırakdari',
+'exif-dc-contributor' => 'Pêşteni',
 'exif-dc-coverage' => 'Heruna yana wextin grotışa medya',
 'exif-dc-date' => 'Tarix(i)',
 'exif-dc-publisher' => 'Hesrekar',
@@ -3823,7 +3823,7 @@ Ma rica keno tesdiq bike ke ti raştî wazeno eno pel bivirazo.",
 # Table pager
 'ascending_abbrev' => 'berz',
 'descending_abbrev' => 'nızm',
-'table_pager_next' => 'Pela badê cû',
+'table_pager_next' => 'Pela peyên',
 'table_pager_prev' => 'Pela verêne',
 'table_pager_first' => 'Pela jûyıne',
 'table_pager_last' => 'Pela peyêne',
index 6094f7b..c36b38e 100644 (file)
@@ -1025,8 +1025,10 @@ o [{{fullurl:{{FULLPAGENAME}}|action=edit}} editar esta página]</span>.',
 'noarticletext-nopermission' => 'Actualmente no hay texto en esta página.
 Puedes [[Special:Search/{{PAGENAME}}|buscar este título de página]] en otras páginas,
 o <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar en los registros relacionados]</span>.',
-'missing-revision' => 'La revisión # $1  de la página denominada "{{PAGENAME}}" no existe.
-!¡ N!Esto es generalmente causado al seguir un enlace de historia obsoleto a una página que se ha borrado.!¡ N!Los detalles pueden encontrarse en el [{{fullurl: {{#Special:Log}} / delete|page = {{FULLPAGENAMEE}}}} registro de borrado].',
+'missing-revision' => 'La revisión #$1 de la página «{{PAGENAME}}» no existe.
+
+Esto suele deberse a seguir un enlace obsoleto hacia el historial de una página que ya ha sido borrada.
+Los detalles pueden encontrarse en el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de borrado].',
 'userpage-userdoesnotexist' => 'La cuenta de usuario «<nowiki>$1</nowiki>» no está registrada. Por favor comprueba si quieres crear o editar esta página.',
 'userpage-userdoesnotexist-view' => 'La cuenta de usuario «$1» no está registrada.',
 'blocked-notice-logextract' => 'Este usuario está actualmente bloqueado.
@@ -1340,8 +1342,10 @@ Nota que usar los enlaces de navegación borrará las selecciones de esta column
 'editundo' => 'deshacer',
 'diff-multi' => '(No se {{PLURAL:$1|muestra una edición intermedia realizada|muestran $1 ediciones intermedias realizadas}} por {{PLURAL:$2|un usuario|$2 usuarios}})',
 'diff-multi-manyusers' => '(No se {{PLURAL:$1|muestra una edición intermedia|muestran $1 ediciones intermedias}} de {{PLURAL:$2|un usuario|$2 usuarios}})',
-'difference-missing-revision' => '{{PLURAL:$2|Un revisión| $2  revisiones}} de esta diferencia ( $1 )  no {{PLURAL:$2| ha siado encontrada|han sido encontradas}}.
-!¡ N!Esto es generalmente causado por seguir un enlace de diffs obsoletas a una página que ha sido borrada.!¡ N!Los detalles pueden encontrarse en el [{{fullurl:{{#Special:log}} / delete|page = {{FULLPAGENAMEE}}}} registro de borrado].',
+'difference-missing-revision' => 'No {{PLURAL:$2|se ha encontrado|se han encontrado}} {{PLURAL:$2|una revisión|$2 revisiones}} de esta diferencia ($1).
+
+Esto suele deberse a seguir un enlace obsoleto hacia una página que ya ha sido borrada.
+Los detalles pueden encontrarse en el [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de borrado].',
 
 # Search results
 'searchresults' => 'Resultados de la búsqueda',
index 8444723..276121a 100644 (file)
@@ -494,14 +494,14 @@ $messages = array(
 'talk' => 'Arutelu',
 'views' => 'vaatamisi',
 'toolbox' => 'Tööriistad',
-'userpage' => 'Kasutajalehekülg',
+'userpage' => 'Vaata kasutajalehekülge',
 'projectpage' => 'Vaata projektilehekülge',
 'imagepage' => 'Vaata faililehekülge',
 'mediawikipage' => 'Vaata sõnumi lehekülge',
-'templatepage' => 'Mallilehekülg',
+'templatepage' => 'Vaata malli lehekülge',
 'viewhelppage' => 'Vaata abilehekülge',
-'categorypage' => 'Kategoorialehekülg',
-'viewtalkpage' => 'Arutelulehekülg',
+'categorypage' => 'Vaata kategooria lehekülge',
+'viewtalkpage' => 'Vaata arutelulehekülge',
 'otherlanguages' => 'Teistes keeltes',
 'redirectedfrom' => '(Ümber suunatud leheküljelt $1)',
 'redirectpagesub' => 'Ümbersuunamisleht',
index 9094df2..54d1c09 100644 (file)
@@ -697,6 +697,10 @@ $1',
 'youhavenewmessages' => '$1 دارید ($2).',
 'newmessageslink' => 'پیام‌های جدید',
 'newmessagesdifflink' => 'آخرین تغییر',
+'youhavenewmessagesfromusers' => 'شما  $1  از  {{PLURAL:$3| کاربر دیگر| $3  کاربر}} دارید ( $2 ).',
+'youhavenewmessagesmanyusers' => 'شما  $1  از تعدادی کاربر دارید ( $2 ).',
+'newmessageslinkplural' => '{{PLURAL:$1|پیام جدید |پیام جدید}}',
+'newmessagesdifflinkplural' => '$1 {{PLURAL:$1|تغییر|تغییرات}} اخیر',
 'youhavenewmessagesmulti' => 'پیام‌های جدیدی در $1 دارید.',
 'editsection' => 'ویرایش',
 'editold' => 'ویرایش',
@@ -753,10 +757,10 @@ $1',
 'dberrortext' => 'اشکال نحوی در درخواست فرستاده شده به پایگاه داده رخ داد.
 دلیل این مشکل می‌تواند ایرادی در نرم‌افزار باشد.
 آخرین درخواست که برای پایگاه داده فرستاد شد این بود:
-<blockquote style="direction:ltr;"><tt>$1</tt></blockquote>
-این درخواست از درون عملگر «<span class="ltr"><tt>$2</tt></span>» فرستاده شد.
+<blockquote style="direction:ltr;"><code>$1</code></blockquote>
+این درخواست از درون عملگر «<span class="ltr"><code>$2</code></span>» فرستاده شد.
 پایگاه داده این خطا را بازگرداند:
-<div class="ltr"><tt>$3: $4</tt></div>',
+<div class="ltr"><samp>$3: $4</samp></div>',
 'dberrortextcl' => 'اشکال نحوی در درخواست فرستاده شده به پایگاه داده رخ داد.
 آخرین درخواستی که برای پایگاه داده فرستاد شد این بود:
 <div class="ltr">$1</div>
@@ -1109,16 +1113,19 @@ $2
 'noarticletext-nopermission' => 'این صفحه هم‌اکنون متنی ندارد.
 شما می‌توانید در دیگر صفحه‌ها [[Special:Search/{{PAGENAME}}|این عنوان را جستجو کنید]]،
 یا <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} سیاهه‌های مرتبط را بگردید]</span>.',
+'missing-revision' => 'ویرایش #$1 از صفحهٔ "{{PAGENAME}}" موجود نیست.
+
+معمولاً در اثر پیوند به تاریخچهٔ به‌روز نشدهٔ صفحهٔ حذف شده است.
+می‌توانید جزئیات بیشتر را در [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سیاههٔ حذف] بیابید.',
 'userpage-userdoesnotexist' => 'حساب کاربر «<nowiki>$1</nowiki>» ثبت نشده‌است.
 لطفاً مطمئن شوید که می‌خواهید این صفحه را ایجاد یا ویرایش کنید.',
 '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'' را با هم فشار دهید
-*'''کانکوئرر:''' روی دکمهٔ ''Reload'' کلیک کنید و یا کلید ''F5'' را فشار دهید
 *'''اپرا:''' حافظهٔ نهانی مرورگر را از طریق منوی ''Tools &rarr; Preferences'' پاک کنید",
 'usercssyoucanpreview' => "'''نکته:''' پیش از ذخیه‌کردن فایل CSS یا JS خود، با دکمهٔ '''{{int:showpreview}}''' آن را آزمایش کنید.",
 'userjsyoucanpreview' => "'''نکته:''' پیش از ذخیره‌کردن فایل CSS یا JS خود، با دکمهٔ '''{{int:showpreview}}''' آن را آزمایش کنید.",
@@ -1242,6 +1249,7 @@ $2
 'expansion-depth-exceeded-warning' => 'صفحه حداکثر عمق بسط دادن تجاوز کرد',
 'parser-unstrip-loop-warning' => 'حلقه در دستور unstrip پیدا شد',
 'parser-unstrip-recursion-limit' => 'از حداکثر ارجاع در دستور unstrip تجاوز شد ($1)',
+'converter-manual-rule-error' => 'خطا در ساختار کتابچهٔ مبدل زبان',
 
 # "Undo" feature
 'undo-success' => 'این ویرایش را می‌توان خنثی کرد.
@@ -1428,6 +1436,10 @@ $1",
 'editundo' => 'خنثی‌سازی',
 'diff-multi' => '({{PLURAL:$1|یک|$1}} ویرایش میانی توسط {{PLURAL:$2|یک|$2}} کاربر نشان داده نشده‌است)',
 'diff-multi-manyusers' => '({{PLURAL:$1|یک|$1}} ویرایش میانی توسط بیش از {{PLURAL:$2|یک|$2}} کاربر نشان داده نشده‌است)',
+'difference-missing-revision' => '{{PLURAL:$2|یک ویرایش|$2 ویرایش}}  از تفاوت نسخه‌ها ($1) {{PLURAL:$2|یافت|یافت}}  نشد.
+
+معمولاً در اثر پیوند به تاریخچهٔ به‌روز نشدهٔ صفحهٔ حذف شده است.
+می‌توانید جزئیات بیشتر را در [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} سیاههٔ حذف] بیابید.',
 
 # Search results
 'searchresults' => 'نتایج جستجو',
@@ -2132,6 +2144,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 'shared-repo-from' => 'از $1',
 'shared-repo' => 'یک مخزن مشترک',
 'shared-repo-name-wikimediacommons' => 'ویکی‌انبار',
+'upload-disallowed-here' => 'متاسفانه شما نمی توانید این نگاره را بازنویس کنید.',
 
 # File reversion
 'filerevert' => 'واگردانی $1',
@@ -2213,8 +2226,8 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 
 'disambiguations' => 'صفحه‌های دارای پیوند به صفحه‌های ابهام‌زدایی',
 'disambiguationspage' => 'Template:ابهام‌زدایی',
-'disambiguations-text' => "صفحه‌های زیر پیوندی به یک '''صفحهٔ ابهام‌زدایی''' هستند.
-اÛ\8cÙ\86 ØµÙ\81Ø­Ù\87â\80\8cÙ\87ا Ø¨اید در عوض به موضوعات مرتبط پیوند داده شوند.<br />
+'disambiguations-text' => "صفحه‌های زیر حاوی حداقل یک پیوند به یک '''صفحهٔ ابهام‌زدایی''' هستند.
+اÛ\8cÙ\86 ØµÙ\81Ø­Ù\87â\80\8cÙ\87ا Ø´اید در عوض به موضوعات مرتبط پیوند داده شوند.<br />
 یک صفحه هنگامی صفحهٔ ابهام‌زدایی در نظر گرفته می‌شود که در آن از الگویی که به [[MediaWiki:Disambiguationspage]] پیوند دارد استفاده شده باشد.",
 
 'doubleredirects' => 'تغییرمسیرهای دوتایی',
@@ -2240,6 +2253,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 # Miscellaneous special pages
 'nbytes' => '$1 {{PLURAL:$1|بایت|بایت}}',
 'ncategories' => '$1 {{PLURAL:$1|رده|رده}}',
+'ninterwikis' => '$1 {{PLURAL:$1|میان‌ویکی|میان‌ویکی}}',
 'nlinks' => '$1 {{PLURAL:$1|پیوند|پیوند}}',
 'nmembers' => '$1 {{PLURAL:$1|عضو|عضو}}',
 'nrevisions' => '$1 {{PLURAL:$1|نسخه|نسخه}}',
@@ -2268,6 +2282,7 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 'mostlinkedtemplates' => 'الگوهایی که بیشتر از همه به آن‌ها پیوند داده شده‌است',
 'mostcategories' => 'صفحه‌های دارای بیشترین رده',
 'mostimages' => 'پرونده‌هایی که بیشتر از همه به آن‌ها پیوند داده شده‌است',
+'mostinterwikis' => 'صفحه‌های دارای بیشترین میان‌ویکی',
 'mostrevisions' => 'صفحه‌های دارای بیشترین نسخه',
 'prefixindex' => 'تمام صفحه‌ها با پیشوند',
 'prefixindex-namespace' => 'همهٔ صفحه‌های دارای پیشوند (فضای‌نام $1)',
@@ -2414,6 +2429,8 @@ https://www.mediawiki.org/wiki/Manual:Image_Authorization را ببینید.',
 'mailnologin' => 'نشانی‌ای از فرستنده موجود نیست',
 'mailnologintext' => 'برای فرستادن رایانامه به کاربران دیگر باید [[Special:UserLogin|به سامانه وارد شوید]] و نشانی رایانامهٔ معتبری در [[Special:Preferences|ترجیحات]] خود داشته باشید.',
 'emailuser' => 'فرستادن نامه به این کاربر',
+'emailuser-title-target' => 'ایمیل این {{GENDER:$1| کاربر}}',
+'emailuser-title-notarget' => 'رایانامه به کاربر',
 'emailpage' => 'رایانامه به کاربر',
 'emailpagetext' => 'شما می‌توانید از فرم زیر برای ارسال یک رایانامه به این کاربر استفاده کنید.
 نشانی رایانامه‌ای که در [[Special:Preferences|ترجیحات کاربریتان]] وارد کرده‌اید در نشانی فرستنده (From) نامه خواهد آمد، تا گیرنده بتواند پاسخ دهد.',
@@ -3066,6 +3083,7 @@ $1',
 'import-interwiki-templates' => 'تمام الگوها را شامل شود',
 'import-interwiki-submit' => 'درون‌ریزی شود',
 'import-interwiki-namespace' => 'فضای نام مقصد:',
+'import-interwiki-rootpage' => 'مقصد صفحه ٔ مبنا (اختیاری):',
 'import-upload-filename' => 'نام پرونده:',
 'import-comment' => 'توضیح:',
 'importtext' => 'لطفاً پرونده را از ویکی منبع با کمک [[Special:Export|ابزار برون‌بری]] دریافت کنید.
@@ -3100,6 +3118,9 @@ $1',
 'import-error-interwiki' => 'صفحه «$1» وارد نشد. چون نام آن برای پیوند خارجی (interwiki) رزرو شده‌است.',
 'import-error-special' => 'صفحه «$1» درون‌ریزی نشد، چرا که متعلق به فضای نام غیرمجاز است.',
 'import-error-invalid' => 'صفحه "$1" به دلیل نامعتبر بودن نامش وارد نمی‌شود.',
+'import-options-wrong' => '{{PLURAL:$2|جزئیات|جزئیات}} اشتباه: <nowiki>$1</nowiki>',
+'import-rootpage-invalid' => 'با توجه به ریشه صفحه عنوان نامعتبر است.',
+'import-rootpage-nosubpage' => 'فضای نام  "$1" صفحهٔ مبنا اجازهٔ زیرصفحه نمی‌دهد.',
 
 # Import log
 'importlogpage' => 'سیاههٔ درون‌ریزی‌ها',
@@ -3221,11 +3242,34 @@ $1',
 
 # Info page
 'pageinfo-title' => 'اطلاعات در مورد «$1»',
-'pageinfo-header-edits' => 'ویرایش',
+'pageinfo-header-basic' => 'اطلاعات اولیه',
+'pageinfo-header-edits' => 'ویرایش تاریخچه',
+'pageinfo-header-restrictions' => 'حفاظت از صفحه',
+'pageinfo-header-properties' => 'ويژگيهای صفحه',
+'pageinfo-display-title' => 'نمایش عنوان',
+'pageinfo-default-sort' => 'کلید مرتب‌سازی پیش‌فرض',
+'pageinfo-length' => 'حجم صفحه  (بایت)',
+'pageinfo-article-id' => 'شناسهٔ صفحه',
+'pageinfo-robot-policy' => 'وضعیت موتور جستجو',
+'pageinfo-robot-index' => 'فهرست‌پذیر',
+'pageinfo-robot-noindex' => 'عدم فهرست‌پذیری',
 'pageinfo-views' => 'شمار بازدیدها',
-'pageinfo-watchers' => 'شمار پی‌گیری‌کنندگان',
-'pageinfo-edits' => 'شمار ویرایش‌ها',
-'pageinfo-authors' => 'شمار نویسندگان یکتا',
+'pageinfo-watchers' => 'شمار پی‌گیری‌کنندگان صفحه',
+'pageinfo-redirects-name' => 'تغییرمسیرها به این صفحه',
+'pageinfo-subpages-name' => 'زیرصفحه‌های این صفحه',
+'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|تغییرمسیر|تغییرمسیر}}; $3 {{PLURAL:$3|غیرتغییرمسیر|غیرتغییرمسیر}})',
+'pageinfo-firstuser' => 'به‌وجود آورندهٔ صفحه',
+'pageinfo-firsttime' => 'زمان ایجاد صفحه',
+'pageinfo-lastuser' => 'آخرین ویرایشگر',
+'pageinfo-lasttime' => 'تاریخ آخرین ویرایش',
+'pageinfo-edits' => 'شمار کلی ویرایش‌ها',
+'pageinfo-authors' => 'تعداد کلی نویسندگان یکتا',
+'pageinfo-recent-edits' => 'شماره ویرایش‌های اخیر (در $1 گذشته)',
+'pageinfo-recent-authors' => 'تعداد نویسندگان یکتای اخیر',
+'pageinfo-restriction' => 'محافظت صفحه ( <code>{{lcfirst:$1}}</code> )',
+'pageinfo-magic-words' => '{{PLURAL:$1|حرف|حروف}} جادویی ($1)',
+'pageinfo-hidden-categories' => '{{PLURAL:$1| ردهٔ|ردهٔ}} پنهان ( $1 )',
+'pageinfo-templates' => '{{PLURAL:$1|الگو|الگو}} استفاده‌شده ($1)',
 
 # Skin names
 'skinname-standard' => 'کلاسیک',
@@ -3281,6 +3325,7 @@ $1',
 'file-info-size-pages' => '<span style="direction:ltr">$1 × $2</span> نقطه، حجم پرونده: $3، نوع MIME پرونده: $4، $5 صفحه',
 'file-nohires' => 'تفکیک‌پذیری بالاتری در دسترس نیست.',
 'svg-long-desc' => 'پروندهٔ اس‌وی‌جی، با ابعاد <span dir="ltr">$1 × $2</span> پیکسل، اندازهٔ پرونده: $3',
+'svg-long-desc-animated' => 'پروندهٔ اس‌وی‌جی متحرک، با ابعاد <span dir="ltr">$1 × $2</span> پیکسل، اندازهٔ پرونده: $3',
 'show-big-image' => 'تصویر با تفکیک‌پذیری بالاتر',
 'show-big-image-preview' => 'اندازهٔ این پیش‌نمایش: $1.',
 'show-big-image-other' => '{{PLURAL:$2|کیفیت|کیفیت‌های}} دیگر: $1.',
@@ -3290,6 +3335,8 @@ $1',
 'file-info-png-looped' => 'چرخش‌دار',
 'file-info-png-repeat' => '$1 {{PLURAL:$1|بار|بار}} پخش شد',
 'file-info-png-frames' => '$1 {{PLURAL:$1|قاب|قاب}}',
+'file-no-thumb-animation' => "'''توجه: به علت مسائل فنی پیش‌نمایش پرونده به صورت متحرک نمایش داده نمی‌شود.'''",
+'file-no-thumb-animation-gif' => "'''توجه:به علت مسائل فنی پیش‌نمایش پرونده‌های GIF مانند این پرونده، به صورت متحرک نمایش داده نمی‌شود.'''",
 
 # Special:NewFiles
 'newimages' => 'نگارخانهٔ پرونده‌های جدید',
index cc82658..2308b5c 100644 (file)
@@ -435,6 +435,8 @@ $1',
 'youhavenewmessages' => 'თქვენ გაქვთ $1 ($2).',
 'newmessageslink' => 'ახალი შეტყობინებები',
 'newmessagesdifflink' => 'განსხვავება წინა ვერსიასთან',
+'youhavenewmessagesfromusers' => 'თქვენ გაქვთ $1 {{PLURAL:$3|სხვა მომხმარებლისგან|$3 მომხმარებლებისგან}} ($2).',
+'youhavenewmessagesmanyusers' => 'თქვენ გაქვთ $1 ბევრი მომხმარებლისგან ($2).',
 'newmessageslinkplural' => '{{PLURAL:$1|ახალი შეტყობინება|ახალი შეტყობინება}}',
 'newmessagesdifflinkplural' => 'ბოლო {{PLURAL:$1|ცვლილება|ცვლილება}}',
 'youhavenewmessagesmulti' => 'თქვენ გაქვთ ახალი შეტყობინება $1-ზე',
@@ -2283,8 +2285,8 @@ $UNWATCHURL
 'rollback' => 'რცვლილებების გაუქმება',
 'rollback_short' => 'სწრაფი გაუქმება',
 'rollbacklink' => 'სწრაფი გაუქმება',
-'rollbacklinkcount' => '$1 {{PLURAL:$1|á\83ªá\83\95á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\98á\83¡|á\83ªá\83\95á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\98á\83¡}} á\83\93á\83\90á\83\91á\83 á\83£á\83\9cება',
-'rollbacklinkcount-morethan' => '$1-á\83\96á\83\94 á\83\9bá\83\94á\83¢á\83\98 {{PLURAL:$1|á\83ªá\83\95á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\98á\83¡|á\83ªá\83\95á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\98á\83¡}} á\83\93á\83\90á\83\91á\83 á\83£á\83\9cება',
+'rollbacklinkcount' => '$1 {{PLURAL:$1|á\83ªá\83\95á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\98á\83¡|á\83ªá\83\95á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\98á\83¡}} á\83\92á\83\90á\83£á\83¥á\83\9bება',
+'rollbacklinkcount-morethan' => '$1-á\83\96á\83\94 á\83\9bá\83\94á\83¢á\83\98 {{PLURAL:$1|á\83ªá\83\95á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\98á\83¡|á\83ªá\83\95á\83\9aá\83\98á\83\9aá\83\94á\83\91á\83\98á\83¡}} á\83\92á\83\90á\83£á\83¥á\83\9bება',
 'rollbackfailed' => 'შეცდომა გაუქმებისას',
 'cantrollback' => 'შეუძლებელია უწინდელი რედაქციის აღდგენა; ის, ვინც უკანასკნელი ცვლილებები შეიტანა, ამ სტატიის ერთადერთი ავტორია.',
 'alreadyrolled' => 'შეუძლებელია ბოლო ცვლილების გაუქმება [[:$1]], გაკეებული [[User:$2|$2]] ([[User talk:$2|განხილვა]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);
@@ -2331,7 +2333,7 @@ $UNWATCHURL
 თქვენ შეგიძლიათ ამ გვერდის დაცვის დონე შეცვალოთ, თუმცა ეს კასკადურ დაცვაზე გავლენას არ იქონიებს.',
 'protect-default' => 'ყველა მომხმარებელი',
 'protect-fallback' => 'საჭიროა „$1-ის“ უფლება',
-'protect-level-autoconfirmed' => 'ახალი და არარეგისტრირებული მომხმარებლების დაბლოკვა',
+'protect-level-autoconfirmed' => 'ახალი და არარეგისტრირებული მომხმარებლებისაგან დაცვა',
 'protect-level-sysop' => 'მხოლოდ ადმინისტრატორები',
 'protect-summary-cascade' => 'იერარქიული',
 'protect-expiring' => 'ვადა გასდის: $1 (UTC)',
@@ -2408,7 +2410,7 @@ $UNWATCHURL
 'undelete-no-results' => 'არ არის ნაპოვნი შესაბამისი გვერდები წაშლათა არქივში.',
 'undelete-filename-mismatch' => 'შეუძლებელია წაშლილი ფაილის აღდგენა $1-ში – განსხვავებები წაშლამდე.',
 'undelete-bad-store-key' => 'შეუძლებელია წაშლილი ფაილის აღდგენა $1-ში – იგი არ არსებობდა წაშლამდე.',
-'undelete-cleanup-error' => 'გამოუყენებელი სარქივო ფაილის «$1» წაშლის შეცდომა:',
+'undelete-cleanup-error' => 'გამოუყენებელი სარქივო ფაილის „$1“ წაშლის შეცდომა:',
 'undelete-missing-filearchive' => 'შეუძლებელია ფაილის აღდგენა არქივის იდენტიფიკატორით $1, რადგანაც ის არ არის მონაცემთა ბაზაში. შესაძლებელია ფაილი უკვე აღდგენილია.',
 'undelete-error' => 'შეცდომა გვერდის აღდგენისას',
 'undelete-error-short' => 'შეცდომა ფაილის წაშლის გაუქმებაში: $1',
@@ -2455,7 +2457,7 @@ $1',
 
 # What links here
 'whatlinkshere' => 'ბმული გვერდზე',
-'whatlinkshere-title' => 'გვერდები, რომლებიც შეიცავენ ბმულებს "$1"-ზე',
+'whatlinkshere-title' => 'გვერდები, რომლებიც შეიცავენ ბმულებს „$1“-ზე',
 'whatlinkshere-page' => 'გვერდი:',
 'linkshere' => "მომდევნო გვერდები შეიცავენ ბმულებს '''[[:$1]]'''-ზე:",
 'nolinkshere' => "'''[[:$1]]'''-ზე ბმული არ არის.",
@@ -2581,7 +2583,7 @@ $1',
 'ipb_expiry_invalid' => 'მოქმედების მიუღებელი პერიოდი',
 'ipb_expiry_temp' => 'ბლოკირება მომხმარებლის სახელის დამალვით უვადო უნდა იყოს',
 'ipb_hide_invalid' => 'შეუძლებელია მოხემული ანგარიშის დამალვა, სავარაუდოდ მის მიერ განხორციელებულია ძალიან ბევრი შესწორება',
-'ipb_already_blocked' => '"$1" უკვე ბლოკირებულია',
+'ipb_already_blocked' => '„$1“ უკვე ბლოკირებულია',
 'ipb-needreblock' => 'მომხმარებელი $1 უკვე დაიბლოკა. გსურთ დაბლოკვის პარამეტრების შესწორება?',
 'ipb-otherblocks-header' => 'სხვა {{PLURAL:$1|დაბლოკვა|დაბლოკვები}}',
 'unblock-hideuser' => 'თქვენ არ შეგიძლიათ მოხსნათ ბლოკი ამ მომხმარებელს, რადგან მისი მომხმარებლის სახელი დამალულია.',
@@ -2680,12 +2682,12 @@ $1',
 'delete_and_move' => 'წაშლა და გადატანა',
 'delete_and_move_text' => '==საჭიროა წაშლა==
 
-სტატია დასახელებით "[[:$1]]" უკვე არსებობს. გსურთ მისი წაშლა გადატანისთვის ადგილის დასათმობად?',
+სტატია დასახელებით „[[:$1]]“ უკვე არსებობს. გსურთ მისი წაშლა გადატანისთვის ადგილის დასათმობად?',
 'delete_and_move_confirm' => 'დიახ, წაშალეთ ეს გვერდი',
-'delete_and_move_reason' => 'წაშლილია "[[$1]]"-á\83\93á\83\90á\83\9c á\83\92á\83\90á\83\93á\83\90á\83¢á\83\90á\83\9cá\83\98á\83¡á\83\97á\83\95á\83\98á\83¡ á\83\90á\83\93á\83\92á\83\98á\83\9aá\83\98á\83¡ á\83\93á\83\90á\83¡á\83\90á\83\97á\83\9bá\83\9dá\83\91á\83\90á\83\93',
+'delete_and_move_reason' => 'წაშლილია â\80\9e[[$1]]-á\83\93á\83\90á\83\9câ\80\9c á\83\92á\83\90á\83\93á\83\90á\83¢á\83\90á\83\9cá\83\98á\83¡á\83\97á\83\95á\83\98á\83¡ á\83\90á\83\93á\83\92á\83\98á\83\9aá\83\98á\83¡ á\83\93á\83\90á\83¡á\83\90á\83\97á\83\9bá\83\9dá\83\91á\83\90á\83\93',
 'selfmove' => 'წყარო და დანიშნულების სათაურები მსგავსია; შეუძლებელია გვერდის საკუთარ თავზე გადატანა.',
-'immobile-source-namespace' => 'შეუძლებელია სახელის გადარქმევა «$1» სახელთა სივრცეში',
-'immobile-target-namespace' => 'შეუძლებელია გვერდის გადატანა «$1» სახელთა სივრცეში',
+'immobile-source-namespace' => 'შეუძლებელია სახელის გადარქმევა „$1“ სახელთა სივრცეში',
+'immobile-target-namespace' => 'შეუძლებელია გვერდის გადატანა „$1“ სახელთა სივრცეში',
 'immobile-target-namespace-iw' => 'ინტერვიკის ბმული შეუძლებელია გამოყენებული იქნას გადარქმევისთვის.',
 'immobile-source-page' => 'ამ გვეერდის გადატანა შეუძლებელია.',
 'immobile-target-page' => 'შეუძლებელია მოცემულ სახელზე გადატანა.',
@@ -2794,10 +2796,10 @@ $1',
 'import-upload' => 'XML მონაცემების ატვირთვა',
 'import-token-mismatch' => 'სეანსის მონაცემები დაიკარგა. კიდევ ერთხელ სცადეთ!',
 'import-invalid-interwiki' => 'შეუძლებელია იმპორტირება მოცემული ვიკიდან.',
-'import-error-edit' => 'გვერდი "$1" იმპორტირება არ მოხდა, რადგან თქვენ არ გაქვთ მისი რედაქტირების უფლება.',
-'import-error-create' => 'გვერდი "$1" იმპორტირება არ მოხდა, რადგან თქვენ არ გაქვთ მისი შექმნის უფლება.',
-'import-error-interwiki' => 'გვერდი "$1" არ იქნა იმპორტირებული, რადგანაც მისი სახელი დარეგისტრირებულია გარე ბმულებისათვის (interwiki).',
-'import-error-special' => 'გვერდი "$1" არ იქნა იმპორტირებული, რადგანაც ის განეკუთვნება განსაკუთრებულ სახელთა სივრცეს, რომელიც კრძალავს გვერდების შექმნას.',
+'import-error-edit' => 'გვერდი „$1“ იმპორტირება არ მოხდა, რადგან თქვენ არ გაქვთ მისი რედაქტირების უფლება.',
+'import-error-create' => 'გვერდი „$1“ იმპორტირება არ მოხდა, რადგან თქვენ არ გაქვთ მისი შექმნის უფლება.',
+'import-error-interwiki' => 'გვერდი „$1“ არ იქნა იმპორტირებული, რადგანაც მისი სახელი დარეგისტრირებულია გარე ბმულებისათვის (interwiki).',
+'import-error-special' => 'გვერდი „$1“ არ იქნა იმპორტირებული, რადგანაც ის განეკუთვნება განსაკუთრებულ სახელთა სივრცეს, რომელიც კრძალავს გვერდების შექმნას.',
 'import-error-invalid' => 'გვერდი "$1" იმპორტირება არ მოხდა მიუღებელი სახელის გამო.',
 'import-options-wrong' => 'არასწორი {{PLURAL:$2|პარამეტრი|პარამეტრი}}: <nowiki>$1</nowiki>',
 'import-rootpage-invalid' => 'ძირეული გვერდის მითითებული სახელი არასწორია.',
@@ -2805,9 +2807,9 @@ $1',
 # Import log
 'importlogpage' => 'იმპორტის ჟურნალი',
 'importlogpagetext' => 'ადმინისტრატორთა მიერ გვერდების იმპორტირება  ცვლილებების ჩათვლით სხვა ვიკიდან.',
-'import-logentry-upload' => '«[[$1]]» — ფაილის იმპორტი',
+'import-logentry-upload' => '„[[$1]]“ — ფაილის იმპორტი',
 'import-logentry-upload-detail' => '$1 ცვლილება',
-'import-logentry-interwiki' => '«$1» — ტრანსვიკი იმპორტი',
+'import-logentry-interwiki' => '„$1“ — ტრანსვიკი იმპორტი',
 'import-logentry-interwiki-detail' => '$1 ცვლილება $2-დან',
 
 # JavaScriptTest
@@ -2815,7 +2817,7 @@ $1',
 'javascripttest-disabled' => 'ეს ფუნქცია ამ ვიკიში არ ჩართულა.',
 'javascripttest-title' => 'მიმდინარეობს $1-ის ტესტირება',
 'javascripttest-pagetext-noframework' => 'ეს გვერდი დარეგისტრირებულია JavaScript-ის ტესტების გასაშვებად.',
-'javascripttest-pagetext-unknownframework' => '"$1"-ის ტესტირების უცნობი გარემო.',
+'javascripttest-pagetext-unknownframework' => '„$1-ის“ ტესტირების უცნობი გარემო.',
 'javascripttest-pagetext-frameworks' => 'გთხოვთ, აირჩიეთ ტესტირების ერთ-ერთი შემდეგი გარემო: $1',
 'javascripttest-pagetext-skins' => 'ტესტების გასაშვებად აირჩიეთ გაფორმების თემა:',
 'javascripttest-qunit-intro' => 'იხილეთ [$1 ტესტირების დოკუმენტები] mediawiki.org-ზე.',
@@ -3851,12 +3853,12 @@ MediaWiki ვრცელდება იმ იმედით, რომ ი
 'revdelete-uname-unhid' => 'მომხმარებლის სახელი გახსნილია',
 'revdelete-restricted' => 'შეზღუდვა ადმინისტრატორთათვის',
 'revdelete-unrestricted' => 'ადმინისტრატორთათვის შეზღუდვები მოხსნილია',
-'logentry-move-move' => '$1 á\83\92á\83\90á\83\93á\83\90á\83\98á\83¢á\83\90á\83\9cá\83\90 á\83\92á\83\95á\83\94á\83 á\83\93á\83\98 $3 â\86\92 $4-á\83\96á\83\94',
-'logentry-move-move-noredirect' => '$1 á\83\92á\83\90á\83\93á\83\90á\83\98á\83¢á\83\90á\83\9cá\83\90 á\83\92á\83\95á\83\94á\83 á\83\93á\83\98 $3 $4-á\83¨á\83\98 გადამისამართების დატოვების გარეშე',
-'logentry-move-move_redir' => '$1 á\83\92á\83\90á\83\93á\83\90á\83\98á\83¢á\83\90á\83\9cá\83\90 á\83\92á\83\95á\83\94á\83 á\83\93á\83\98 $3 $4-á\83¨á\83\98 გადამისამართებაზე',
+'logentry-move-move' => '$1 á\83\92á\83\95á\83\94á\83 á\83\93á\83\98 â\80\9e$3â\80\9c á\83\92á\83\90á\83\93á\83\90á\83\98á\83¢á\83\90á\83\9cá\83\90 á\83\92á\83\95á\83\94á\83 á\83\93á\83\96á\83\94 â\80\9e$4â\80\9c',
+'logentry-move-move-noredirect' => '$1 á\83\92á\83\95á\83\94á\83 á\83\93á\83\98 â\80\9e$3â\80\9c á\83\92á\83\90á\83\93á\83\90á\83\98á\83¢á\83\90á\83\9cá\83\90 á\83\92á\83\95á\83\94á\83 á\83\93á\83\96á\83\94 â\80\9e$4â\80\9c გადამისამართების დატოვების გარეშე',
+'logentry-move-move_redir' => '$1 á\83\92á\83\95á\83\94á\83 á\83\93á\83\98 â\80\9e$3â\80\9c á\83\92á\83\90á\83\93á\83\90á\83\98á\83¢á\83\90á\83\9cá\83\90 á\83\92á\83\95á\83\94á\83 á\83\93á\83\96á\83\94 â\80\9e$4â\80\9c გადამისამართებაზე',
 'logentry-move-move_redir-noredirect' => '$1 გადაიტანა გვერდი $3 $4-ში გადამისამართების დატოვების გარეშე',
-'logentry-patrol-patrol' => '$1 გააკეთა გვერდი $3-ის $4 ვერსიის პატრულირება',
-'logentry-patrol-patrol-auto' => '$1 ავტომატურად გააკეთა გვერდი $3-ის $4 ვერსიის პატრულირება',
+'logentry-patrol-patrol' => '$1 გააკეთა გვერდის „$3“ $4 ვერსიის პატრულირება',
+'logentry-patrol-patrol-auto' => '$1 ავტომატურად გააკეთა გვერდის „$3“ $4 ვერსიის პატრულირება',
 'logentry-newusers-newusers' => '$1 შექმნა მომხმარებლის ანგარიში',
 'logentry-newusers-create' => '$1 შექმნა მომხმარებლის ანგარიში',
 'logentry-newusers-create2' => '$1 შექმნა მომხმარებელ $3 ანგარიში',
index 5a877bc..961b81f 100644 (file)
@@ -400,6 +400,10 @@ Vide [[Special:Version|paginam versionis]].',
 'youhavenewmessages' => 'Habes $1 ($2).',
 'newmessageslink' => 'nuntia nova',
 'newmessagesdifflink' => 'dissimilia post mutationem ultimam',
+'youhavenewmessagesfromusers' => 'Habes $1 ab {{PLURAL:$3|uno usore alio|usoribus $3}} ($2).',
+'youhavenewmessagesmanyusers' => 'Habes $1 a multis usoribus ($2).',
+'newmessageslinkplural' => '{{PLURAL:$1|nuntium novum|nuntia nova}}',
+'newmessagesdifflinkplural' => 'dissimilitudo post mutationem ultimam',
 'youhavenewmessagesmulti' => 'Habes nuntia nova in $1',
 'editsection' => 'recensere',
 'editold' => 'recensere',
index ce5f61a..b1f60f1 100644 (file)
@@ -1994,6 +1994,7 @@ Kanskje du vil redigere beskrivelsen på dens [$2 filbeskrivelsesside].',
 'uploadnewversion-linktext' => 'Last opp en ny versjon av denne filen',
 'shared-repo-from' => 'fra $1',
 'shared-repo' => 'et delt fillager',
+'upload-disallowed-here' => 'Beklageligvis kan du ikke overskrive dette bildet.',
 
 # File reversion
 'filerevert' => 'Tilbakestill $1',
@@ -3104,7 +3105,7 @@ Dette er sannsynligvis forårsaket av en lenke til et svartelistet eksternt nett
 'pageinfo-authors' => 'Totalt antall forskjellige forfattere',
 'pageinfo-recent-edits' => 'Antall nylige redigeringer (innen siste $1)',
 'pageinfo-recent-authors' => 'Antall nylige forfattere',
-'pageinfo-restriction' => 'Sidebeskyttelse (<code>$1</code>)',
+'pageinfo-restriction' => 'Sidebeskyttelse (<code>{{lcfirst:$1}}</code>)',
 'pageinfo-magic-words' => '{{PLURAL:$1|Magisk|Magiske}} ord ($1)',
 'pageinfo-hidden-categories' => '{{PLURAL:$1|Skjult kategori|Skjulte kategorier}} ($1)',
 'pageinfo-templates' => 'Transkludert {{PLURAL:$1|mal|maler}} ($1)',
@@ -3159,7 +3160,8 @@ Ved å åpne den kan systemet ditt kompromitteres.",
 'file-info-size' => '$1 × $2 piksler, filstørrelse: $3, MIME-type: $4',
 'file-info-size-pages' => '$1 × $2 piksler, filstørrelse: $3, MIME-type: $4, $5 {{PLURAL:$5|side|sider}}',
 'file-nohires' => 'Ingen høyere oppløsning tilgjengelig.',
-'svg-long-desc' => 'SVG-fil, standardoppløsning $1 × $2 piksler, filstørrelse: $3',
+'svg-long-desc' => 'SVG-fil, standardstørrelse $1 × $2 piksler, filstørrelse: $3',
+'svg-long-desc-animated' => 'Animert SVG-fil, standardstørrelse $1 × $2 piksler, filstørrelse: $3',
 'show-big-image' => 'Full oppløsning',
 'show-big-image-preview' => 'Størrelse på denne forhåndsvisningen: $1.',
 'show-big-image-other' => '{{PLURAL:$2|Annen oppløsning|Andre oppløsninger}}: $1.',
@@ -3169,6 +3171,8 @@ Ved å åpne den kan systemet ditt kompromitteres.",
 'file-info-png-looped' => 'loopet',
 'file-info-png-repeat' => 'avspilt $1 {{PLURAL:$1|gang|ganger}}',
 'file-info-png-frames' => '$1 {{PLURAL:$1|bilde|bilder}}',
+'file-no-thumb-animation' => "'''Merk: På grunn av tekniske begrensninger vil ikke miniatyrbilder av denne filen bli animert.'''",
+'file-no-thumb-animation-gif' => "'''Merk: På grunn av tekniske begrensninger vil ikke miniatyrbilder av høyoppløselige GIF-bilder, slik som denne, bli animert.'''",
 
 # Special:NewFiles
 'newimages' => 'Galleri over nye filer',
index 0ae203d..b30c2fb 100644 (file)
@@ -2799,7 +2799,7 @@ Zie de [[Special:BlockList|blokkadelijst]] voor recente blokkades.',
 'ipblocklist-empty' => 'De blokkeerlijst is leeg.',
 'ipblocklist-no-results' => 'Dit IP-adres of deze gebruikersnaam is niet geblokkeerd.',
 'blocklink' => 'blokkeren',
-'unblocklink' => 'deblokkeren',
+'unblocklink' => 'blokkade opheffen',
 'change-blocklink' => 'blokkade wijzigen',
 'contribslink' => 'bijdragen',
 'emaillink' => 'e-mail verzenden',
index ed01145..0005c66 100644 (file)
@@ -1332,6 +1332,7 @@ $1",
 'prefs-beta' => 'ଆଗ ବିଶେଷତାମାନ',
 'prefs-datetime' => 'ତାରିଖ ଓ ସମୟ',
 'prefs-labs' => 'ପରଖଶାଳା ସୁବିଧାସବୁ',
+'prefs-user-pages' => 'ବ୍ୟବହାରକାରୀଙ୍କର ପୃଷ୍ଠାଗୁଡିକ',
 'prefs-personal' => 'ସଭ୍ୟ ପ୍ରଫାଇଲ',
 'prefs-rc' => 'ନଗଦ ବଦଳ',
 'prefs-watchlist' => 'ଦେଖଣା ତାଲିକା',
@@ -2128,6 +2129,7 @@ URLଟି ଠିକ ଅଚିକି କି ନାଁ ଓ ସାଇଟଟି ସ
 'allpagesbadtitle' => 'ଆପଣ ଅନୁରୋଧ କରିଥିବା ପୃଷ୍ଠାଟି ଭୁଲ, ଅଲଗା ଭାଷାର ବ୍ୟବହାର କରାଯାଇଛି ବା ଭୁଲ ଇଣ୍ଟର ଉଇକି ଉପସର୍ଗ ଦିଆଯାଇଛି ।
 ଏଥିରେ ଥିବା ଗୋଟିଏ ବା ଦୁଇଟି ଅକ୍ଷର ଶିରୋନାମା ଭାବରେ ବ୍ୟବହାର କରାଯାଇ ପାରିବ ନାହିଁ ।',
 'allpages-bad-ns' => '{{SITENAME}}ରେ "$1" ନେମସ୍ପେସଟିଏ ନାହିଁ ।',
+'allpages-hide-redirects' => 'ପୁନଃପ୍ରେରଣସମୂହକୁ ଲୁଚାଇବେ',
 
 # SpecialCachedPage
 'cachedspecial-refresh-now' => 'ନୂତନତମ ଦେଖନ୍ତୁ ।',
@@ -2199,6 +2201,7 @@ URLଟି ଠିକ ଅଚିକି କି ନାଁ ଓ ସାଇଟଟି ସ
 'mailnologin' => 'ଗୋଟିଏ ବି ପଠାଇବା ଠିକଣା ନାହିଁ',
 'mailnologintext' => 'ଆପଣ ନିଜ [[Special:Preferences|ପସନ୍ଦସବୁ]]ରେ [[Special:UserLogin|ଲଗ ଇନ]] କରିଥିଲେ ଓ ନିଜର ଏକ ସଚଳ ଇ-ମେଲ ଠିକଣା ଥିଲେ ଯାଇ ବାକି ସବୁ ସଭ୍ୟଙ୍କୁ ଇ-ମେଲ ପଠାଇପାରିବେ ।',
 'emailuser' => 'ଏହି ସଭ୍ୟଙ୍କୁ ଇମେଲ କରିବେ',
+'emailuser-title-notarget' => 'ବ୍ୟବହାରକାରୀ କୁ ଇ-ମେଲ',
 'emailpage' => 'ଇ-ମେଲ ବ୍ୟବହାରକାରୀ',
 'emailpagetext' => 'ତଳେ ଥିବା ଫର୍ମ ବ୍ୟବହାର କରି ଆପଣ ଏହି ସଭ୍ୟଙ୍କୁ ଇ-ମେଲ କରିପାରିବେ ।
 [[Special:Preferences|ଆପଣଙ୍କ ପସନ୍ଦ]]ରେ ଥିବା ଇ-ମେଲ ଠିକଣା ପ୍ରେରକ ଭାବରେ ଦେଖାଯିବ, ତେଣୁ ଚିଠି ପାଇଥିବା ସଭ୍ୟ ଆପଣଙ୍କୁ ସିଧା ସଳଖ ଉତ୍ତର ଦେଇପାରିବ ।',
@@ -2994,6 +2997,7 @@ MediaWiki ବ୍ୟବହାର କରି [[Special:Import|ପୃଷ୍ଠା 
 # Info page
 'pageinfo-title' => '"$1"ର ବିବରଣୀ',
 'pageinfo-header-edits' => 'ବଦଳସବୁ',
+'pageinfo-article-id' => 'ପୃଷ୍ଠା ଆଇଡ଼ି',
 'pageinfo-views' => 'ଦେଖଣା ସଂଖ୍ୟା',
 'pageinfo-watchers' => 'ଦେଖଣାହାରି ସଂଖ୍ୟା',
 'pageinfo-edits' => 'ସମ୍ପାଦନା ସଂଖ୍ୟା:',
@@ -3875,6 +3879,11 @@ MediaWiki ଉପଯୋଗୀ ହେବା ଲକ୍ଷରେ ବଣ୍ଟାଯ
 'api-error-verification-error' => 'ଏହି ଫାଇଲଟି ବୋଧ ହୁଏ ନଷ୍ଟ ହୋଇଯାଇଅଛି କିମ୍ବା ଭୁଲ ଏକ୍ସଟେନସନ ଦିଆଯାଇଅଛି ।',
 
 # Durations
+'duration-seconds' => '$1 {{PLURAL:$1|ସେକଣ୍ଡ|ସେକେଣ୍ଡ}}',
+'duration-minutes' => '$1 {{PLURAL:$1|ମିନିଟ|ମିନିଟ}}',
+'duration-hours' => '$1 {{PLURAL:$1|ଘଣ୍ଟା|ଘଣ୍ଟା}}',
+'duration-days' => '$1 {{PLURAL:$1|ଦିନ|ଦିନଗୁଡିକ}}',
+'duration-weeks' => '$1 {{PLURAL: $1|ସପ୍ତାହ|ସପ୍ତାହଗୁଡିକ}}',
 'duration-years' => '$1 {{PLURAL:$1|year|years}}',
 'duration-decades' => '$1 {{PLURAL:$1|decade|decades}',
 'duration-centuries' => '$1 {{PLURAL:$1|century|centuries}}',
index bb4ad3c..3b3bf84 100644 (file)
@@ -2757,12 +2757,12 @@ A peul visualisene la sorgiss",
 'tooltip-ca-nstab-media' => 'Vardé la pàgina dël mojen',
 'tooltip-ca-nstab-special' => "Costa a l'é na pàgina special, a peul nen modifichela.",
 'tooltip-ca-nstab-project' => 'Vardé la pàgina proteta.',
-'tooltip-ca-nstab-image' => 'Vardé la pàgina dl',
-'tooltip-ca-nstab-mediawiki' => 'Vardé ël messagi ëd sistema.',
+'tooltip-ca-nstab-image' => "Vardé la pàgina dl'archivi",
+'tooltip-ca-nstab-mediawiki' => 'Vardé ël mëssagi ëd sistema.',
 'tooltip-ca-nstab-template' => 'Vardé lë stamp.',
-'tooltip-ca-nstab-help' => 'Vardé la pàgina d',
+'tooltip-ca-nstab-help' => "Vardé la pàgina d'agiut",
 'tooltip-ca-nstab-category' => 'Vardé la pàgina dla categorìa.',
-'tooltip-minoredit' => 'Marca sossì coma modìfica cita',
+'tooltip-minoredit' => 'Marché sòn coma modìfica cita',
 'tooltip-save' => 'Salva le modìfiche',
 'tooltip-preview' => 'Preuva dle modìfiche (mej sempe fela, prima che fé che salvé!)',
 'tooltip-diff' => "Fame vëdde che modìfiche che i l'hai faje al test.",
index 6a55ec0..60e8121 100644 (file)
@@ -3270,10 +3270,26 @@ $1',
 'pageinfo-header-properties' => 'Својства странице',
 'pageinfo-display-title' => 'Наслов за приказ',
 'pageinfo-default-sort' => 'Подразумевани кључ сортирања',
+'pageinfo-length' => 'Дужина странице (у бајтовима)',
+'pageinfo-article-id' => 'ИД странице',
+'pageinfo-robot-policy' => 'Статус претраживача',
+'pageinfo-robot-index' => 'Може да се попише',
+'pageinfo-robot-noindex' => 'Не може да се попише',
 'pageinfo-views' => 'Број прегледа',
 'pageinfo-watchers' => 'Број надгледача страница',
+'pageinfo-redirects-name' => 'Преусмеравања на страницу',
+'pageinfo-subpages-name' => 'Подстранице ове странице',
+'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|преусмерење|преусмерења|преусмерења}}; $3 {{PLURAL:$3|непреусмерење|непреусмерења|непреусмерења}})',
+'pageinfo-firstuser' => 'Аутор странице',
+'pageinfo-firsttime' => 'Датум стварања странице',
+'pageinfo-lastuser' => 'Последњи уредник',
+'pageinfo-lasttime' => 'Датум последње измене',
 'pageinfo-edits' => 'Број измена',
 'pageinfo-authors' => 'Број засебних аутора',
+'pageinfo-recent-edits' => 'Број скорашњих измена (у последњих $1)',
+'pageinfo-recent-authors' => 'Број скорашњих засебних аутора',
+'pageinfo-restriction' => 'Заштита странице (<code>{{lcfirst:$1}}</code>)',
+'pageinfo-magic-words' => '{{PLURAL:$1|Магична реч|Магичне речи}} ($1)',
 
 # Skin names
 'skinname-standard' => 'Класично',
index 1f07bc6..3f4acd2 100644 (file)
@@ -1292,6 +1292,10 @@ Se till att sidhistorikens kontinuitet behålls när du sammanfogar historik.',
 'editundo' => 'gör ogjord',
 'diff-multi' => '({{PLURAL:$1|En mellanliggande version|$1 mellanliggande versioner}} av {{PLURAL:$2|en användare|$2 användare}} visas inte)',
 'diff-multi-manyusers' => '({{PLURAL:$1|En mellanliggande version|$1 mellanliggande versioner}} av mer än $2 användare visas inte)',
+'difference-missing-revision' => '{{PLURAL:$2|En revision|$2 revisioner}} av denna skillnad ($1) kunde inte hittas.
+
+Detta orsakas vanligtvis av att följa en utgången difflänk till en sida som har raderats.
+Detaljer kan hittas i [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} raderingsloggen].',
 
 # Search results
 'searchresults' => 'Sökresultat',
@@ -2073,8 +2077,8 @@ Innan mallarna raderas, kontrollera att det inte finns andra länkar till dem.',
 
 'disambiguations' => 'Sidor som länkar till förgreningssidor',
 'disambiguationspage' => 'Template:Förgrening',
-'disambiguations-text' => "Följande sidor länkar till ''förgreningssidor''.
-Länkarna bör troligtvis ändras så att de länkar till en artikel istället.<br />
+'disambiguations-text' => "Följande sidorna innehåller minst en länk till en '''förgreningssida'''.
+De bör troligtvis ändras så att de länkar till en mer passande sida istället.<br />
 En sida anses vara en förgreningssida om den inkluderar en mall som länkas till från [[MediaWiki:Disambiguationspage]].",
 
 'doubleredirects' => 'Dubbla omdirigeringar',
@@ -2912,6 +2916,7 @@ All överföring mellan wikier (transwiki) listas i  [[Special:Log/import|import
 'import-interwiki-templates' => 'Inkludera alla mallar',
 'import-interwiki-submit' => 'Importera',
 'import-interwiki-namespace' => 'Målnamnrymd:',
+'import-interwiki-rootpage' => 'Destinationens grundsida (valfri):',
 'import-upload-filename' => 'Filnamn:',
 'import-comment' => 'Kommentar:',
 'importtext' => 'Var god exportera filen från ursprungs-wikin med hjälp av [[Special:Export|exporteringsverktyget]].
@@ -2945,6 +2950,7 @@ Spara den på din dator och ladda upp den här.',
 'import-error-special' => 'Sidan "$1" är inte importerad eftersom den tillhör en särskild namnrymd som inte tillåter sidor.',
 'import-error-invalid' => 'Sidan "$1" är inte importerad eftersom dess namn är ogiltigt.',
 'import-options-wrong' => 'Fel {{PLURAL:$2|alternativ|alternativ}}: <nowiki>$1</nowiki>',
+'import-rootpage-invalid' => 'Angiven grundsida är en ogiltig titel.',
 'import-rootpage-nosubpage' => 'Namnrymden "$1" till grundsidan tillåter inte undersidor.',
 
 # Import log
@@ -3108,14 +3114,22 @@ Detta orsakades troligen av en länk till en svartlistad webbplats.',
 'pageinfo-length' => 'Sidlängd (i byte)',
 'pageinfo-article-id' => 'Sid-ID',
 'pageinfo-robot-policy' => 'Sökmotorns status',
+'pageinfo-robot-index' => 'Indexerbar',
+'pageinfo-robot-noindex' => 'Inte indexerbar',
 'pageinfo-views' => 'Antal visningar',
 'pageinfo-watchers' => 'Antal användare som bevakar sidan',
 'pageinfo-redirects-name' => 'Omdirigeringar till denna sida',
 'pageinfo-subpages-name' => 'Undersidor till denna sida',
 'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|omdirigering|omdirigeringar}}; $3 {{PLURAL:$3|icke-omdirigering|icke-omdirigeringar}})',
+'pageinfo-firstuser' => 'Sidskapare',
+'pageinfo-firsttime' => 'Datum när sidan skapades',
+'pageinfo-lastuser' => 'Senaste redigeraren',
 'pageinfo-lasttime' => 'Datum för senaste redigeringen',
 'pageinfo-edits' => 'Totalt antal redigeringar',
 'pageinfo-authors' => 'Totalt antal olika författare',
+'pageinfo-recent-edits' => 'Antal nyliga redigeringar (inom de senaste $1)',
+'pageinfo-recent-authors' => 'Antal nyliga olika författare',
+'pageinfo-restriction' => 'Sidskydd (<code>{{lcfirst:$1}}</code>)',
 'pageinfo-magic-words' => '{{PLURAL:$1|Magiskt|Magiska}} ord ($1)',
 'pageinfo-hidden-categories' => '{{PLURAL:$1|Dold kategori|Dolda kategorier}} ($1)',
 
@@ -3182,6 +3196,8 @@ Om du kör den kan din dator skadas.",
 'file-info-png-looped' => 'upprepad',
 'file-info-png-repeat' => 'spelad $1 {{PLURAL:$1|gång|gånger}}',
 'file-info-png-frames' => '$1 {{PLURAL:$1|bild|bilder}}',
+'file-no-thumb-animation' => "'''OBS: På grund av tekniska begränsningar kommer inte miniatyrer av denna fil animeras.'''",
+'file-no-thumb-animation-gif' => "'''OBS: På grund av tekniska begränsningar kommer inte miniatyrer av GIF-bilder med hög upplösning som denna animeras.'''",
 
 # Special:NewFiles
 'newimages' => 'Galleri över nya filer',
index 148206a..95d9f23 100644 (file)
@@ -693,7 +693,7 @@ $2
 # Special:ChangeEmail
 'changeemail' => 'ఈ-మెయిలు చిరునామా మార్పు',
 'changeemail-header' => 'ఖాతా ఈ-మెయిల్ చిరునామాని మార్చండి',
-'changeemail-text' => 'à°®à±\80 à°\88à°®à±\86యిలà±\8d à°\9aà°¿à°°à±\81నామా à°®à°¾à°°à±\8dà°\9aà±\81à°\9fà°\95à±\81 à°\88 à°«à°¾à°°à°®à±\81 à°¨à°¿à°\82à°ªà°\82à°¡à°¿. à°\88 à°®à°¾à°°à±\8dà°ªà±\81ని à°\96à°\9aà±\8dà°\9aితపరà°\9aà±\81à°\9fà°\95à±\81 à°®à±\80 à°¸à°\82à°\95à±\87తపదà°\82  à°ªà±\8dà°°à°µà±\87శపà±\86à°\9fà±\8dà°\9fాలి.',
+'changeemail-text' => 'à°®à±\80 à°\88à°®à±\86యిలà±\81 à°\9aà°¿à°°à±\81నామాని à°®à°¾à°°à±\8dà°\9aà±\81à°\95à±\8bడానిà°\95à°¿ à°\88 à°«à°¾à°°à°¾à°¨à±\8dని à°¨à°¿à°\82à°ªà°\82à°¡à°¿. à°\88 à°®à°¾à°°à±\8dà°ªà±\81ని à°¨à°¿à°°à±\8dధారిà°\82à°\9aడానిà°\95à°¿ à°®à±\80 à°¸à°\82à°\95à±\87తపదానà±\8dని à°\87à°µà±\8dవాలà±\8dసివసà±\8dà°¤à±\81à°\82à°¦ి.',
 'changeemail-no-info' => 'ఈ పేజీని నేరుగా చూడటానికి మీరు లోనికి ప్రవేశించివుండాలి.',
 'changeemail-oldemail' => 'ప్రస్తుత ఈ-మెయిలు చిరునామా:',
 'changeemail-newemail' => 'కొత్త ఈ-మెయిలు చిరునామా:',
index e71b8b4..973166f 100644 (file)
@@ -620,6 +620,8 @@ $1',
 'youhavenewmessages' => 'Ви отримали $1 ($2).',
 'newmessageslink' => 'нові повідомлення',
 'newmessagesdifflink' => 'остання зміна',
+'newmessageslinkplural' => '{{PLURAL:$1|нове повідомлення|нові повідомлення|нових повідомлень}}',
+'newmessagesdifflinkplural' => '{{PLURAL:$1|остання зміна|останні зміни|останніх змін}}',
 'youhavenewmessagesmulti' => 'Ви отримали нові повідомлення на $1',
 'editsection' => 'ред.',
 'editsection-brackets' => '[$1]',
@@ -1024,7 +1026,7 @@ $2
 'userpage-userdoesnotexist-view' => 'Обліковий запис користувача „$1“ не зареєстровано.',
 'blocked-notice-logextract' => 'Цей користувач наразі заблокований.
 Останній запис у журналі блокувань такий:',
-'clearyourcache' => "'''Увага:''' Ð\9fÑ\96Ñ\81лÑ\8f Ð·Ð±ÐµÑ\80еженнÑ\8f Ñ\81лÑ\96д Ð¾Ñ\87иÑ\81Ñ\82иÑ\82и ÐºÐµÑ\88 Ð±Ñ\80аÑ\83зеÑ\80а, щоб побачити зміни.
+'clearyourcache' => "'''Увага:''' Ð\9fÑ\96Ñ\81лÑ\8f Ð·Ð±ÐµÑ\80еженнÑ\8f Ñ\81лÑ\96д Ð¾Ñ\87иÑ\81Ñ\82иÑ\82и ÐºÐµÑ\88 Ð¾Ð³Ð»Ñ\8fдаÑ\87а, щоб побачити зміни.
 * '''Firefox / Safari:''' тримайте ''Shift'', коли натискаєте ''Оновити'', або натисніть ''Ctrl-F5'' чи ''Ctrl-Shift-R'' (''⌘-R'' на Apple Mac)
 * '''Google Chrome:''' натисніть ''Ctrl-Shift-R'' (''⌘-Shift-R'' на Apple Mac)
 * '''Internet Explorer:''' тримайте ''Ctrl'', коли натискаєте ''Оновити'', або натисніть ''Ctrl-F5''
index 497e1e0..496af72 100644 (file)
@@ -60,7 +60,15 @@ class MigrateUserGroup extends Maintenance {
                        $dbw->update( 'user_groups',
                                array( 'ug_group' => $newGroup ),
                                array( 'ug_group' => $oldGroup,
-                                       "ug_user BETWEEN $blockStart AND $blockEnd" )
+                                       "ug_user BETWEEN $blockStart AND $blockEnd" ),
+                               __METHOD__,
+                               array( 'IGNORE' )
+                       );
+                       $count += $dbw->affectedRows();
+                       $dbw->delete( 'user_groups',
+                               array( 'ug_group' => $oldGroup,
+                                       "ug_user BETWEEN $blockStart AND $blockEnd" ),
+                               __METHOD__
                        );
                        $count += $dbw->affectedRows();
                        $dbw->commit( __METHOD__ );
index 49063ba..92e7255 100644 (file)
@@ -2,19 +2,19 @@
        min-width: 8px;
        height: 14px;
        border: 1px solid white;
-       border-radius: 8px;
        -moz-border-radius: 8px;
        -webkit-border-radius: 8px;
-       box-shadow: 0px 1px 4px #ccc;
+       border-radius: 8px;
        -moz-box-shadow: 0px 1px 4px #ccc;
        -webkit-box-shadow: 0px 1px 4px #ccc;
+       box-shadow: 0px 1px 4px #ccc;
        background-color: #b60a00;
-       background-image: linear-gradient(bottom, #a70802 0%, #cf0e00 100%);
        background-image: -o-linear-gradient(bottom, #a70802 0%, #cf0e00 100%);
        background-image: -moz-linear-gradient(bottom, #a70802 0%, #cf0e00 100%);
+       background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #a70802), color-stop(1, #cf0e00));
        background-image: -webkit-linear-gradient(bottom, #a70802 0%, #cf0e00 100%);
        background-image: -ms-linear-gradient(bottom, #a70802 0%, #cf0e00 100%);
-       background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #a70802), color-stop(1, #cf0e00));
+       background-image: linear-gradient(bottom, #a70802 0%, #cf0e00 100%);
        padding: 0 3px;
        text-align: center;
 }
index d40acc6..04495b7 100644 (file)
@@ -1,9 +1,16 @@
-// Badger v1.0 by Daniel Raftery
-// http://thrivingkings.com/badger
-// http://twitter.com/ThrivingKings
-// Modified by Ryan Kaldari <rkaldari@wikimedia.org>
+/**
+ * jQuery Badge plugin
+ *
+ * Based on Badger plugin by Daniel Raftery (http://thrivingkings.com/badger).
+ *
+ * @license MIT
+ */
 
 /**
+ * @author Ryan Kaldari <rkaldari@wikimedia.org>, 2012
+ * @author Andrew Garrett <agarrett@wikimedia.org>, 2012
+ * @author Marius Hoch <hoo@online.de>, 2012
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
  *
  * This program is distributed WITHOUT ANY WARRANTY.
  */
+( function ( $ ) {
 
-(function( $ ) {
-       $.fn.badge = function( badge, options ) {
-               var existingBadge = this.find( '.mw-badge' );
-               options = $.extend( {}, options );
+       /**
+        * Allows you to put a numeric "badge" on an item on the page.
+        * 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
+        */
+       $.fn.badge = function ( badgeCount, options ) {
+               var $badge,
+                       oldBadgeCount,
+                       newBadgeCount,
+                       $existingBadge = this.find( '.mw-badge' );
 
-               badge = String(badge);
-               if ( badge.charAt(0) === '+' ) {
-                       if ( existingBadge.length > 0 ) {
-                               oldBadge = existingBadge.text();
-                               badge = Math.round( Number( oldBadge ) + Number( badge.substr(1) ) );
-                       } else {
-                               badge = badge.substr(1);
-                       }
-               } else if ( badge.charAt(0) === '-' ) {
-                       if ( existingBadge.length > 0 ) {
-                               oldBadge = existingBadge.text();
-                               badge = Math.round( Number( oldBadge ) - Number( badge.substr(1) ) );
+               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;
+               }
+
+               // 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 );
                        } else {
-                               badge = 0;
+                               newBadgeCount = 0;
                        }
+               // Other types are not supported, fall back to 0.
+               } else {
+                       newBadgeCount = 0;
                }
 
-               if ( Number(badge) <= 0 ) {
-                       // Clear any existing badge
-                       existingBadge.remove();
+               // 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
-                       var $badge = existingBadge;
-                       if ( existingBadge.length > 0 ) {
-                               this.find( '.mw-badge-content' ).text( badge );
+                       if ( $existingBadge.length ) {
+                               $badge = $existingBadge;
+                               // Insert the new count into the badge
+                               this.find( '.mw-badge-content' ).text( newBadgeCount );
                        } else {
-                               $badge = $('<div/>')
-                                       .addClass('mw-badge')
-                                       .addClass('mw-badge-overlay')
+                               // Contruct a new badge with the count
+                               $badge = $( '<div>' )
+                                       .addClass( 'mw-badge' )
                                        .append(
-                                               $('<span/>')
-                                                       .addClass('mw-badge-content')
-                                                       .text(badge)
+                                               $( '<span>' )
+                                                       .addClass( 'mw-badge-content' )
+                                                       .text( newBadgeCount )
                                        );
-                               this.append($badge);
+                               this.append( $badge );
                        }
 
-                       if ( options.type ) {
-                               if ( options.type == 'inline' ) {
-                                       $badge.removeClass('mw-badge-overlay')
-                                               .addClass('mw-badge-inline');
-                               } else if ( options.type == 'overlay' ) {
-                                       $badge.removeClass('mw-badge-inline')
-                                               .addClass('mw-badge-overlay');
-                               }
+                       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 number
-                       if ( options.callback ) {
-                               options.callback( badge );
+                       // If a callback was specified, call it with the badge count
+                       if ( $.isFunction( options.callback ) ) {
+                               options.callback( newBadgeCount );
                        }
                }
        };
-} ) ( jQuery );
+}( jQuery ) );
index 7e2bab8..e7e35be 100644 (file)
                                && profile.name === 'safari'
                                && profile.layoutVersion > 526 ) {
                                util.tooltipAccessKeyPrefix = 'ctrl-alt-';
-
+                       // Firefox 14+ on Mac
+                       } else if ( profile.platform === 'mac'
+                               && profile.name === 'firefox'
+                               && profile.versionNumber >= 14 ) {
+                               util.tooltipAccessKeyPrefix = 'ctrl-option-';
                        // Safari/Konqueror on any platform, or any browser on Mac
                        // (but not Safari on Windows)
                        } else if ( !( profile.platform === 'win' && profile.name === 'safari' )
                        }
 
                        /* Fill $content var */
-                       if ( $( '#bodyContent' ).length ) {
-                               // Vector, Monobook, Chick etc.
-                               util.$content = $( '#bodyContent' );
-
-                       } else if ( $( '#mw_contentholder' ).length ) {
-                               // Modern
-                               util.$content = $( '#mw_contentholder' );
-
-                       } else if ( $( '#article' ).length ) {
-                               // Standard, CologneBlue
-                               util.$content = $( '#article' );
+                       util.$content = ( function () {
+                               var $content, selectors = [
+                                       // The preferred standard for setting $content (class="mw-body")
+                                       // You may also use (class="mw-body mw-body-primary") if you use
+                                       // mw-body in multiple locations.
+                                       // Or class="mw-body-primary" if you want $content to be deeper
+                                       // in the dom than mw-body
+                                       '.mw-body-primary',
+                                       '.mw-body',
+
+                                       /* Legacy fallbacks for setting the content */
+                                       // Vector, Monobook, Chick, etc... based skins
+                                       '#bodyContent',
+
+                                       // Modern based skins
+                                       '#mw_contentholder',
+
+                                       // Standard, CologneBlue
+                                       '#article',
+
+                                       // #content is present on almost all if not all skins. Most skins (the above cases)
+                                       // have #content too, but as an outer wrapper instead of the article text container.
+                                       // The skins that don't have an outer wrapper do have #content for everything
+                                       // so it's a good fallback
+                                       '#content',
+
+                                       // If nothing better is found fall back to our bodytext div that is guaranteed to be here
+                                       '#mw-content-text',
+
+                                       // Should never happen... well, it could if someone is not finished writing a skin and has
+                                       // not inserted bodytext yet. But in any case <body> should always exist
+                                       'body'
+                               ];
+                               for ( var i = 0, l = selectors.length; i < l; i++ ) {
+                                       $content = $( selectors[i] ).first();
+                                       if ( $content.length ) {
+                                               return $content;
+                                       }
+                               }
 
-                       } else {
-                               // #content is present on almost all if not all skins. Most skins (the above cases)
-                               // have #content too, but as an outer wrapper instead of the article text container.
-                               // The skins that don't have an outer wrapper do have #content for everything
-                               // so it's a good fallback
-                               util.$content = $( '#content' );
-                       }
+                               // Make sure we don't unset util.$content if it was preset and we don't find anything
+                               return util.$content;
+                       } )();
 
                        // Table of contents toggle
                        $tocTitle = $( '#toctitle' );
 
                /*
                 * @var jQuery
-                * A jQuery object that refers to the page-content element
+                * A jQuery object that refers to the content area element
                 * Populated by init().
                 */
                $content: null,
index 6e87786..4211bb8 100644 (file)
@@ -77,7 +77,7 @@ class ModernTemplate extends MonoBookTemplate {
        <!-- contentholder does nothing by default, but it allows users to style the text inside
             the content area without affecting the meaning of 'em' in #mw_content, which is used
             for the margins -->
-       <div id="mw_contentholder">
+       <div id="mw_contentholder" class="mw-body">
                <div class='mw-topboxes'>
                        <div id="mw-js-message" style="display:none;"<?php $this->html('userlangattributes')?>></div>
                        <div class="mw-topbox" id="siteSub"><?php $this->msg('tagline') ?></div>
index 5c7ca14..0bd7134 100644 (file)
@@ -80,7 +80,7 @@ class MonoBookTemplate extends BaseTemplate {
 
                $this->html( 'headelement' );
 ?><div id="globalWrapper">
-<div id="column-content"><div id="content">
+<div id="column-content"><div id="content" class="mw-body-primary">
        <a id="top"></a>
        <?php if($this->data['sitenotice']) { ?><div id="siteNotice"><?php $this->html('sitenotice') ?></div><?php } ?>
 
index a332a8e..823d467 100644 (file)
@@ -8,10 +8,10 @@
                $( mw ).trigger( 'LivePreviewPrepare' );
 
                var postData = $('#editform').formToArray();
-               postData.push( { 'name' : 'wpPreview', 'value' : '1' } );
+               postData.push( { 'name' : e.target.name, 'value' : '1' } );
 
                // Hide active diff, used templates, old preview if shown
-               var copyElements = ['#wikiPreview', '.templatesUsed', '.hiddencats',
+               var copyElements = ['#wikiPreview', '#wikiDiff', '.templatesUsed', '.hiddencats',
                                                        '#catlinks', '#p-lang', '.mw-summary-preview'];
                var copySelector = copyElements.join(',');
 
                        $( '.editCheckboxes' ).before( $( '<div>' ).addClass( 'mw-summary-preview' ) );
                }
 
-               $( '#wpPreview' ).click( doLivePreview );
+               // construct space for diff if missing. also load diff styles.
+               if ( !document.getElementById( 'wikiDiff' ) && document.getElementById( 'wikiPreview' ) ) {
+                       $( '#wikiPreview' ).after( $( '<div>' ).attr( 'id', 'wikiDiff' ) );
+                       // diff styles are by default only loaded when needed
+                       // if there was no diff container, we can expect the styles not to be there either
+                       mw.loader.load( 'mediawiki.action.history.diff' );
+               }
+
+               $( '#wpPreview, #wpDiff' ).click( doLivePreview );
        } );
 }) ( jQuery );
index 5036268..453e88a 100644 (file)
@@ -2442,6 +2442,21 @@ Link with double quotes in title part (literal) and alternate part (interpreted)
 </p>
 !! end
 
+!! test
+Broken image links with HTML captions (bug 39700)
+!! input
+[[File:Nonexistent|<script></script>]]
+[[File:Nonexistent|100px|<script></script>]]
+[[File:Nonexistent|&lt;]]
+[[File:Nonexistent|a<i>b</i>c]]
+!! result
+<p><a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">&lt;script&gt;&lt;/script&gt;</a>
+<a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">&lt;script&gt;&lt;/script&gt;</a>
+<a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">&lt;</a>
+<a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">abc</a>
+</p>
+!! end
+
 !! test
 Plain link to URL
 !! input
index 0413b5a..903a6d2 100644 (file)
@@ -21,6 +21,8 @@ class ExtraParserTest extends MediaWikiTestCase {
                $this->options = new ParserOptions;
                $this->options->setTemplateCallback( array( __CLASS__, 'statelessFetchTemplate' ) );
                $this->parser = new Parser;
+
+               MagicWord::clearCache();
        }
 
        // Bug 8689 - Long numeric lines kill the parser
index 8b019d7..0dd85c1 100644 (file)
@@ -6,15 +6,18 @@ class HtmlTest extends MediaWikiTestCase {
        private static $oldContLang;
        private static $oldLanguageCode;
        private static $oldNamespaces;
+       private static $oldHTML5;
 
        public function setUp() {
-               global $wgLang, $wgContLang, $wgLanguageCode;
-               
+               global $wgLang, $wgContLang, $wgLanguageCode, $wgHTML5;
+
+               // Save globals
                self::$oldLang = $wgLang;
                self::$oldContLang = $wgContLang;
                self::$oldNamespaces = $wgContLang->getNamespaces();
                self::$oldLanguageCode = $wgLanguageCode;
-               
+               self::$oldHTML5 = $wgHTML5;
+
                $wgLanguageCode = 'en';
                $wgContLang = $wgLang = Language::factory( $wgLanguageCode );
 
@@ -42,14 +45,35 @@ class HtmlTest extends MediaWikiTestCase {
                        101  => 'Custom_talk',
                ) );
        }
-       
+
        public function tearDown() {
-               global $wgLang, $wgContLang, $wgLanguageCode;
+               global $wgLang, $wgContLang, $wgLanguageCode, $wgHTML5;
 
+               // Restore globals
                $wgContLang->setNamespaces( self::$oldNamespaces );
                $wgLang = self::$oldLang;
                $wgContLang = self::$oldContLang;
                $wgLanguageCode = self::$oldLanguageCode;
+               $wgHTML5 = self::$oldHTML5;
+       }
+
+       /**
+        * Wrapper to easily set $wgHTML5 = true.
+        * Original value will be restored after test completion.
+        * @todo Move to MediaWikiTestCase
+        */
+       public function enableHTML5() {
+               global $wgHTML5;
+               $wgHTML5 = true;
+       }
+       /**
+        * Wrapper to easily set $wgHTML5 = false
+        * Original value will be restored after test completion.
+        * @todo Move to MediaWikiTestCase
+        */
+       public function disableHTML5() {
+               global $wgHTML5;
+               $wgHTML5 = false;
        }
 
        public function testExpandAttributesSkipsNullAndFalse() {
diff --git a/tests/phpunit/includes/TimestampTest.php b/tests/phpunit/includes/TimestampTest.php
new file mode 100644 (file)
index 0000000..231228f
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * Tests timestamp parsing and output.
+ */
+class TimestampTest extends MediaWikiTestCase {
+       /**
+        * Test parsing of valid timestamps and outputing to MW format.
+        * @dataProvider provideValidTimestamps
+        */
+       function testValidParse( $format, $original, $expected ) {
+               $timestamp = new MWTimestamp( $original );
+               $this->assertEquals( $expected, $timestamp->getTimestamp( TS_MW ) );
+       }
+
+       /**
+        * Test outputting valid timestamps to different formats.
+        * @dataProvider provideValidTimestamps
+        */
+       function testValidOutput( $format, $expected, $original ) {
+               $timestamp = new MWTimestamp( $original );
+               $this->assertEquals( $expected, (string) $timestamp->getTimestamp( $format ) );
+       }
+
+       /**
+        * Test an invalid timestamp.
+        * @expectedException TimestampException
+        */
+       function testInvalidParse() {
+               $timestamp = new MWTimestamp( "This is not a timestamp." );
+       }
+
+       /**
+        * Test requesting an invalid output format.
+        * @expectedException TimestampException
+        */
+       function testInvalidOutput() {
+               $timestamp = new MWTimestamp( '1343761268' );
+               $timestamp->getTimestamp( 98 );
+       }
+
+       /**
+        * Test human readable timestamp format.
+        */
+       function testHumanOutput() {
+               $timestamp = new MWTimestamp( time() - 3600 );
+               $this->assertEquals( "1 hour ago", $timestamp->getHumanTimestamp()->toString() );
+       }
+
+       /**
+        * Returns a list of valid timestamps in the format:
+        * array( type, timestamp_of_type, timestamp_in_MW )
+        */
+       function provideValidTimestamps() {
+               return array(
+                       // Various formats
+                       array( TS_UNIX, '1343761268', '20120731190108' ),
+                       array( TS_MW, '20120731190108', '20120731190108' ),
+                       array( TS_DB, '2012-07-31 19:01:08', '20120731190108' ),
+                       array( TS_ISO_8601, '2012-07-31T19:01:08Z', '20120731190108' ),
+                       array( TS_ISO_8601_BASIC, '20120731T190108Z', '20120731190108' ),
+                       array( TS_EXIF, '2012:07:31 19:01:08', '20120731190108' ),
+                       array( TS_RFC2822, 'Tue, 31 Jul 2012 19:01:08 GMT', '20120731190108' ),
+                       array( TS_ORACLE, '31-07-2012 19:01:08.000000', '20120731190108' ),
+                       array( TS_POSTGRES, '2012-07-31 19:01:08 GMT', '20120731190108' ),
+                       array( TS_DB2, '2012-07-31 19:01:08', '20120731190108' ),
+                       // Some extremes and weird values
+                       array( TS_ISO_8601, '9999-12-31T23:59:59Z', '99991231235959' ),
+                       array( TS_UNIX, '-62135596801', '00001231235959' )
+               );
+       }
+}
index 8a2fb55..70fce11 100644 (file)
@@ -39,22 +39,30 @@ abstract class GenericArrayObjectTest extends MediaWikiTestCase {
        public abstract function elementInstancesProvider();
 
        /**
-        * Provides instances of the concrete class being tested.
+        * Returns the name of the concrete class being tested.
         *
         * @since 1.20
         *
-        * @return array
+        * @return string
         */
-       public abstract function instanceProvider();
+       public abstract function getInstanceClass();
 
        /**
-        * Returns the name of the concrete class being tested.
+        * Provides instances of the concrete class being tested.
         *
         * @since 1.20
         *
-        * @return string
+        * @return array
         */
-       public abstract function getInstanceClass();
+       public function instanceProvider() {
+               $instances = array();
+
+               foreach ( $this->elementInstancesProvider() as $elementInstances ) {
+                       $instances[] = $this->getNew( $elementInstances );
+               }
+
+               return $this->arrayWrap( $instances );
+       }
 
        /**
         * @since 1.20
index d6e2547..b604d59 100644 (file)
@@ -73,8 +73,10 @@ class BitmapMetadataHandlerTest extends MediaWikiTestCase {
                $this->assertEquals( '2020:07:14 01:36:05', $meta['DateTimeDigitized'] );
                $this->assertEquals( '1997:03:02 00:01:02', $meta['DateTimeOriginal'] );
        }
-       /* File has an invalid time (+ one valid but really weird time)
+       /**
+        * File has an invalid time (+ one valid but really weird time)
         * that shouldn't be included
+        * @expectedException TimestampException
         */
        public function testIPTCDatesInvalid() {
                $meta = BitmapMetadataHandler::Jpeg( $this->filePath .
index ea9d553..20e42a6 100644 (file)
@@ -87,6 +87,14 @@ class SpecialSearchTest extends MediaWikiTestCase {
                                'advanced', array( 2, 14 ),
                                'Bug 33583: search with no option should honor User search preferences'
                        ),
+                       array(
+                               $EMPTY_REQUEST, array_fill_keys( array_map( function( $ns ) {
+                                       return "searchNs$ns";
+                               }, $defaultNS ), 0 ) + array( 'searchNs2' => 1, 'searchNs14' => 1 ),
+                               'advanced', array( 2, 14 ),
+                               'Bug 33583: search with no option should honor User search preferences'
+                               . 'and have all other namespace disabled'
+                       ),
                );
        }