Merge "Move post-edit font-family rule to Vector-specific selector"
authorOri.livneh <ori@wikimedia.org>
Mon, 10 Jun 2013 03:26:46 +0000 (03:26 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Mon, 10 Jun 2013 03:26:46 +0000 (03:26 +0000)
109 files changed:
RELEASE-NOTES-1.22
docs/hooks.txt
includes/ChangesList.php
includes/DefaultSettings.php
includes/GitInfo.php
includes/Html.php
includes/HttpFunctions.php
includes/ImagePage.php
includes/LinksUpdate.php
includes/OutputPage.php
includes/RecentChange.php
includes/Sanitizer.php
includes/Title.php
includes/User.php
includes/WebStart.php
includes/Wiki.php
includes/WikiPage.php
includes/XmlTypeCheck.php
includes/actions/InfoAction.php
includes/api/ApiParse.php
includes/content/AbstractContent.php
includes/content/CssContent.php
includes/content/JavaScriptContent.php
includes/content/TextContent.php
includes/content/WikitextContent.php
includes/db/Database.php
includes/db/DatabaseMysql.php
includes/filebackend/SwiftFileBackend.php
includes/filebackend/lockmanager/LockManager.php
includes/filebackend/lockmanager/ScopedLock.php
includes/filerepo/file/LocalFile.php
includes/libs/CSSJanus.php
includes/media/MediaHandler.php
includes/media/MediaTransformOutput.php
includes/media/SVG.php
includes/normal/README
includes/parser/Parser.php
includes/templates/UsercreateVForm.php
includes/templates/UserloginVForm.php
languages/messages/MessagesAr.php
languages/messages/MessagesArc.php
languages/messages/MessagesAst.php
languages/messages/MessagesBe.php
languages/messages/MessagesBe_tarask.php
languages/messages/MessagesBs.php
languages/messages/MessagesCa.php
languages/messages/MessagesCe.php
languages/messages/MessagesCy.php
languages/messages/MessagesDa.php
languages/messages/MessagesEn.php
languages/messages/MessagesFrr.php
languages/messages/MessagesHe.php
languages/messages/MessagesIa.php
languages/messages/MessagesIs.php
languages/messages/MessagesIt.php
languages/messages/MessagesJa.php
languages/messages/MessagesLb.php
languages/messages/MessagesMin.php
languages/messages/MessagesMk.php
languages/messages/MessagesMl.php
languages/messages/MessagesNn.php
languages/messages/MessagesPms.php
languages/messages/MessagesPt.php
languages/messages/MessagesQqq.php
languages/messages/MessagesRo.php
languages/messages/MessagesRu.php
languages/messages/MessagesSah.php
languages/messages/MessagesSr_el.php
languages/messages/MessagesTh.php
languages/messages/MessagesVi.php
languages/messages/MessagesZh_hant.php
maintenance/dictionary/mediawiki.dic
maintenance/doMaintenance.php
maintenance/edit.php
maintenance/eraseArchivedFile.php [new file with mode: 0644]
maintenance/jsduck/categories.json
maintenance/jsduck/config.json
maintenance/language/messages.inc
maintenance/removeUnusedAccounts.php
maintenance/sql.php
resources/Resources.php
resources/jquery/jquery.client.js
resources/jquery/jquery.tablesorter.js
resources/mediawiki.special/images/arrow-collapsed-ltr.png [deleted file]
resources/mediawiki.special/images/arrow-collapsed-rtl.png [deleted file]
resources/mediawiki.special/images/arrow-expanded.png [deleted file]
resources/mediawiki.special/mediawiki.special.changeslist.css
resources/mediawiki/images/arrow-collapsed-ltr.png [new file with mode: 0644]
resources/mediawiki/images/arrow-collapsed-rtl.png [new file with mode: 0644]
resources/mediawiki/images/arrow-expanded.png [new file with mode: 0644]
resources/mediawiki/mediawiki.icon.css [new file with mode: 0644]
resources/mediawiki/mediawiki.user.js
skins/Vector.php
tests/TestsAutoLoader.php
tests/parser/parserTest.inc
tests/parser/parserTests.txt
tests/phpunit/includes/OutputPageTest.php
tests/phpunit/includes/XmlTypeCheckTest.php [new file with mode: 0644]
tests/phpunit/includes/content/ContentHandlerTest.php
tests/phpunit/includes/content/CssContentTest.php
tests/phpunit/includes/content/TextContentTest.php
tests/phpunit/includes/db/DatabaseSQLTest.php
tests/phpunit/includes/libs/CSSJanusTest.php
tests/phpunit/includes/parser/NewParserTest.php
tests/phpunit/mocks/media/MockBitmapHandler.php
tests/phpunit/phpunit.php
tests/qunit/suites/resources/jquery/jquery.client.test.js
tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
tests/selenium/installer/README.txt

index 42462dd..e32d484 100644 (file)
@@ -24,6 +24,7 @@ production.
 * $wgXhtmlDefaultNamespace is no longer used by core. Setting it will no longer change the
   xmlns used by MediaWiki. Reliance on this variable by extensions is deprecated.
 * $wgHandheldStyle was removed.
+* $wgHandheldForIPhone was removed.
 * $wgJsMimeType is no longer used by core. Most usage has been removed since
   HTML output is now exclusively HTML5.
 * $wgDBOracleDRCP added. True enables persistent connection with DRCP on Oracle.
@@ -99,6 +100,7 @@ production.
   handlers can take further action based on the status of the patrol footer
 * LinkCache singleton can now be altered or cleared, letting one to specify
   another instance that does not rely on a database backend.
+* MediaWiki's PHPUnit tests can now use PHPUnit installed using composer --dev.
 
 === Bug fixes in 1.22 ===
 * Disable Special:PasswordReset when $wgEnableEmail is false. Previously one
@@ -129,6 +131,8 @@ production.
   is disabled, and AJAX patrolling and watching are now disabled when use of
   the write API is not allowed.
 * (bug 48294) API: Fix chunk upload async mode.
+* (bug 46749) Broken files tracking category removed from pages if an image
+  with that name is uploaded.
 
 === API changes in 1.22 ===
 * (bug 46626) xmldoublequote parameter was removed. Because of a bug, the
@@ -155,6 +159,7 @@ production.
 * prop=info now adds the content model and page language of the title.
 * New upload log entries will now contain information on the relavent
   image (sha1 and timestamp).
+* (bug 49239) action=parse now can parse in preview mode.
 
 === Languages updated in 1.22===
 
index 66b5068..02c9fad 100644 (file)
@@ -801,6 +801,19 @@ content model name, but no entry for that model exists in $wgContentHandlers.
 $modeName: the requested content model name
 &$handler: set this to a ContentHandler object, if desired.
 
+'ContentGetParserOutput': Customize parser output for a given content object,
+called by AbstractContent::getParserOutput. May be used to override the normal
+model-specific rendering of page content.
+$content: The Content to render
+$title: Title of the page, as context
+$revId: The revision ID, as context
+$options: ParserOptions for rendering. To avoid confusing the parser cache,
+the output can only depend on parameters provided to this hook function, not on global state.
+$generateHtml: boolean, indicating whether full HTML should be generated. If false,
+generation of HTML may be skipped, but other information should still be present in the
+ParserOutput object.
+&$output: ParserOutput, to manipulate or replace
+
 'ConvertContent': Called by AbstractContent::convert when a conversion to another
 content model is requested.
 $content: The Content object to be converted.
@@ -1994,7 +2007,7 @@ $title : Current Title object being displayed in search results.
 $article: The article object corresponding to the page
 
 'ShowRawCssJs': Customise the output of raw CSS and JavaScript in page views.
-DEPRECATED, use the ContentHandler facility to handle CSS and JavaScript!
+DEPRECATED, use the ContentGetParserOutput hook instead!
 $text: Text being shown
 $title: Title of the custom script/stylesheet page
 $output: Current OutputPage object
index 796b92a..e0f522a 100644 (file)
@@ -194,7 +194,7 @@ class ChangesList extends ContextSource {
                $this->rcCacheIndex = 0;
                $this->lastdate = '';
                $this->rclistOpen = false;
-               $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
+               $this->getOutput()->addModules( 'mediawiki.special.changeslist' );
                return '';
        }
 
@@ -362,10 +362,18 @@ class ChangesList extends ContextSource {
         * @param $watched
         */
        public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
-               # If it's a new article, there is no diff link, but if it hasn't been
-               # patrolled yet, we need to give users a way to do so
+               global $wgUseRCPatrol;
+
                $params = array();
 
+               // In case we got a page creation which is yet unpatrolled and
+               // recent changes patrolling is enabled, the user probably rather
+               // wants to patrol the whole page (first revision) instead of seeing
+               // a patrollink for the current revision.
+               if ( $wgUseRCPatrol && $unpatrolled && $rc->getAttribute( 'rc_type' ) == RC_NEW ) {
+                       $params['patrolpage'] = 1;
+               }
+
                $articlelink = Linker::linkKnown(
                        $rc->getTitle(),
                        null,
@@ -524,7 +532,7 @@ class ChangesList extends ContextSource {
         * @param $rc RecentChange
         */
        public function insertRollback( &$s, &$rc ) {
-               if ( $rc->mAttribs['rc_type'] != RC_NEW && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
+               if ( $rc->mAttribs['rc_type'] == RC_EDIT && $rc->mAttribs['rc_this_oldid'] && $rc->mAttribs['rc_cur_id'] ) {
                        $page = $rc->getTitle();
                        /** Check for rollback and edit permissions, disallow special pages, and only
                          * show a link on the top-most revision */
@@ -703,7 +711,7 @@ class EnhancedChangesList extends ChangesList {
                $this->rcCacheIndex = 0;
                $this->lastdate = '';
                $this->rclistOpen = false;
-               $this->getOutput()->addModuleStyles( 'mediawiki.special.changeslist' );
+               $this->getOutput()->addModules( 'mediawiki.special.changeslist' );
                return '';
        }
        /**
@@ -940,7 +948,7 @@ class EnhancedChangesList extends ChangesList {
                                implode( $this->message['semicolon-separator'], $users )
                        )->escaped() . '</span>';
 
-               $tl = '<span class="mw-collapsible-toggle mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>';
+               $tl = '<span class="mw-collapsible-toggle mw-collapsible-arrow mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>';
                $r .= "<td>$tl</td>";
 
                # Main line
index b86de8c..06eba95 100644 (file)
@@ -2676,18 +2676,6 @@ $wgSkipSkin = '';
 /** Array for more like $wgSkipSkin. */
 $wgSkipSkins = array();
 
-/**
- * If set, 'screen' and 'handheld' media specifiers for stylesheets are
- * transformed such that they apply to the iPhone/iPod Touch Mobile Safari,
- * which doesn't recognize 'handheld' but does support media queries on its
- * screen size.
- *
- * Consider only using this if you have a *really good* handheld stylesheet,
- * as iPhone users won't have any way to disable it and use the "grown-up"
- * styles instead.
- */
-$wgHandheldForIPhone = false;
-
 /**
  * Allow user Javascript page?
  * This enables a lot of neat customizations, but may
@@ -5065,12 +5053,13 @@ $wgGitBin = '/usr/bin/git';
  * 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.
+ * %r will be replaced with a URL-encoded version of $1.
  *
  * @since 1.20
  */
 $wgGitRepositoryViewers = array(
-       'https://gerrit.wikimedia.org/r/p/(.*)' => 'https://gerrit.wikimedia.org/r/gitweb?p=$1;h=%H',
-       'ssh://(?:[a-z0-9_]+@)?gerrit.wikimedia.org:29418/(.*)' => 'https://gerrit.wikimedia.org/r/gitweb?p=$1;h=%H',
+       'https://gerrit.wikimedia.org/r/p/(.*)' => 'https://git.wikimedia.org/commit/%r/%H',
+       'ssh://(?:[a-z0-9_]+@)?gerrit.wikimedia.org:29418/(.*)' => 'https://git.wikimedia.org/commit/%r/%H',
 );
 
 /** @} */ # End of maintenance }
@@ -6179,6 +6168,12 @@ $wgAsyncHTTPTimeout = 25;
  */
 $wgHTTPProxy = false;
 
+/**
+ * Timeout for connections done internally (in seconds)
+ * Only works for curl
+ */
+$wgHTTPConnectTimeout = 5e0;
+
 /** @} */ # End HTTP client }
 
 /************************************************************************//**
index a54b807..407b3cb 100644 (file)
@@ -194,12 +194,13 @@ class GitInfo {
                }
                foreach ( self::getViewers() as $repo => $viewer ) {
                        $pattern = '#^' . $repo . '$#';
-                       if ( preg_match( $pattern, $url ) ) {
+                       if ( preg_match( $pattern, $url, $matches ) ) {
                                $viewerUrl = preg_replace( $pattern, $viewer, $url );
                                $headSHA1 = $this->getHeadSHA1();
                                $replacements = array(
                                        '%h' => substr( $headSHA1, 0, 7 ),
-                                       '%H' => $headSHA1
+                                       '%H' => $headSHA1,
+                                       '%r' => urlencode( $matches[1] ),
                                );
                                return strtr( $viewerUrl, $replacements );
                        }
index e7455b7..1d45e6a 100644 (file)
@@ -172,7 +172,7 @@ class Html {
 
                // In text/html, initial <html> and <head> tags can be omitted under
                // pretty much any sane circumstances, if they have no attributes.  See:
-               // <http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags>
+               // <http://www.whatwg.org/html/syntax.html#optional-tags>
                if ( !$wgWellFormedXml && !$attribs
                && in_array( $element, array( 'html', 'head' ) ) ) {
                        return '';
@@ -238,7 +238,7 @@ class Html {
                $element = strtolower( $element );
 
                // Reference:
-               // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags
+               // http://www.whatwg.org/html/syntax.html#optional-tags
                if ( !$wgWellFormedXml && in_array( $element, array(
                        'html',
                        'head',
@@ -842,7 +842,7 @@ class Html {
         * @return Boolean
         */
        public static function isXmlMimeType( $mimetype ) {
-               # http://www.whatwg.org/specs/web-apps/current-work/multipage/infrastructure.html#xml-mime-type
+               # http://www.whatwg.org/html/infrastructure.html#xml-mime-type
                # * text/xml
                # * application/xml
                # * Any mimetype with a subtype ending in +xml (this implicitly includes application/xhtml+xml)
@@ -902,7 +902,7 @@ class Html {
                $candidates = array();
                foreach ( $urls as $density => $url ) {
                        // Image candidate syntax per current whatwg live spec, 2012-09-23:
-                       // http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1.html#attr-img-srcset
+                       // http://www.whatwg.org/html/embedded-content-1.html#attr-img-srcset
                        $candidates[] = "{$url} {$density}x";
                }
                return implode( ", ", $candidates );
index 2da368c..cf36d2e 100644 (file)
@@ -40,6 +40,7 @@ class Http {
         * @param array $options options to pass to MWHttpRequest object.
         *      Possible keys for the array:
         *    - timeout             Timeout length in seconds
+        *    - connectTimeout      Timeout for connection, in seconds (curl only)
         *    - postData            An array of key-value pairs or a url-encoded form data
         *    - proxy               The proxy to use.
         *                          Otherwise it will use $wgHTTPProxy (if set)
@@ -65,6 +66,9 @@ class Http {
                if ( !isset( $options['timeout'] ) ) {
                        $options['timeout'] = 'default';
                }
+               if( !isset( $options['connectTimeout'] ) ) {
+                       $options['connectTimeout'] = 'default';
+               }
 
                $req = MWHttpRequest::factory( $url, $options );
                $status = $req->execute();
@@ -216,7 +220,7 @@ class MWHttpRequest {
         * @param array $options (optional) extra params to pass (see Http::request())
         */
        protected function __construct( $url, $options = array() ) {
-               global $wgHTTPTimeout;
+               global $wgHTTPTimeout, $wgHTTPConnectTimeout;
 
                $this->url = wfExpandUrl( $url, PROTO_HTTP );
                $this->parsedUrl = wfParseUrl( $this->url );
@@ -232,6 +236,11 @@ class MWHttpRequest {
                } else {
                        $this->timeout = $wgHTTPTimeout;
                }
+               if ( isset( $options['connectTimeout'] ) && $options['connectTimeout'] != 'default' ) {
+                       $this->connectTimeout = $options['connectTimeout'];
+               } else {
+                       $this->connectTimeout = $wgHTTPConnectTimeout;
+               }
                if ( isset( $options['userAgent'] ) ) {
                        $this->setUserAgent( $options['userAgent'] );
                }
@@ -721,6 +730,7 @@ class CurlHttpRequest extends MWHttpRequest {
 
                $this->curlOptions[CURLOPT_PROXY] = $this->proxy;
                $this->curlOptions[CURLOPT_TIMEOUT] = $this->timeout;
+               $this->curlOptions[CURLOPT_CONNECTTIMEOUT_MS] = $this->connectTimeout * 1000;
                $this->curlOptions[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_1_0;
                $this->curlOptions[CURLOPT_WRITEFUNCTION] = $this->callback;
                $this->curlOptions[CURLOPT_HEADERFUNCTION] = array( $this, "readHeader" );
index a5beb5d..8cfbc73 100644 (file)
@@ -311,6 +311,12 @@ class ImagePage extends Article {
                        } else {
                                $params = array( 'page' => $page );
                        }
+
+                       $renderLang = $request->getVal( 'lang' );
+                       if ( !is_null( $renderLang ) ) {
+                               $params['lang'] = $renderLang;
+                       }
+
                        $width_orig = $this->displayImg->getWidth( $page );
                        $width = $width_orig;
                        $height_orig = $this->displayImg->getHeight( $page );
index 4b1b5b8..0b7393a 100644 (file)
@@ -237,22 +237,36 @@ class LinksUpdate extends SqlDataUpdate {
                wfProfileOut( __METHOD__ );
        }
 
+       /**
+        * Queue recursive jobs for this page
+        *
+        * Which means do LinksUpdate on all templates
+        * that include the current page, using the job queue.
+        */
        function queueRecursiveJobs() {
-               wfProfileIn( __METHOD__ );
+               self::queueRecursiveJobsForTable( $this->mTitle, 'templatelinks' );
+       }
 
-               if ( $this->mTitle->getBacklinkCache()->hasLinks( 'templatelinks' ) ) {
+       /**
+        * Queue a RefreshLinks job for any table.
+        *
+        * @param Title $title Title to do job for
+        * @param String $table Table to use (e.g. 'templatelinks')
+        */
+       public static function queueRecursiveJobsForTable( Title $title, $table ) {
+               wfProfileIn( __METHOD__ );
+               if ( $title->getBacklinkCache()->hasLinks( $table ) ) {
                        $job = new RefreshLinksJob2(
-                               $this->mTitle,
+                               $title,
                                array(
-                                       'table' => 'templatelinks',
+                                       'table' => $table,
                                ) + Job::newRootJobParams( // "overall" refresh links job info
-                                       "refreshlinks:templatelinks:{$this->mTitle->getPrefixedText()}"
+                                       "refreshlinks:{$table}:{$title->getPrefixedText()}"
                                )
                        );
                        JobQueueGroup::singleton()->push( $job );
                        JobQueueGroup::singleton()->deduplicateRootJob( $job );
                }
-
                wfProfileOut( __METHOD__ );
        }
 
index 6bb29d2..978fddc 100644 (file)
@@ -2528,8 +2528,8 @@ $templates
                        // The spec recommends defining XHTML5's charset using the XML declaration
                        // instead of meta.
                        // Our XML declaration is output by Html::htmlHeader.
-                       // http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#attr-meta-http-equiv-content-type
-                       // http://www.whatwg.org/specs/web-apps/current-work/multipage/semantics.html#charset
+                       // http://www.whatwg.org/html/semantics.html#attr-meta-http-equiv-content-type
+                       // http://www.whatwg.org/html/semantics.html#charset
                        $ret .= Html::element( 'meta', array( 'charset' => 'UTF-8' ) );
                }
 
@@ -3566,7 +3566,7 @@ $templates
         * this stylesheet
         */
        public static function transformCssMedia( $media ) {
-               global $wgRequest, $wgHandheldForIPhone;
+               global $wgRequest;
 
                // http://www.w3.org/TR/css3-mediaqueries/#syntax
                $screenMediaQueryRegex = '/^(?:only\s+)?screen\b/i';
@@ -3598,18 +3598,6 @@ $templates
                        }
                }
 
-               // Expand longer media queries as iPhone doesn't grok 'handheld'
-               if ( $wgHandheldForIPhone ) {
-                       $mediaAliases = array(
-                               'screen' => 'screen and (min-device-width: 481px)',
-                               'handheld' => 'handheld, only screen and (max-device-width: 480px)',
-                       );
-
-                       if ( isset( $mediaAliases[$media] ) ) {
-                               $media = $mediaAliases[$media];
-                       }
-               }
-
                return $media;
        }
 
index b8ddf68..24db569 100644 (file)
@@ -96,10 +96,12 @@ class RecentChange {
        }
 
        /**
+        * @deprecated in 1.22
         * @param $row
         * @return RecentChange
         */
        public static function newFromCurRow( $row ) {
+               wfDeprecated( __METHOD__, '1.22' );
                $rc = new RecentChange;
                $rc->loadFromCurRow( $row );
                $rc->notificationtimestamp = false;
@@ -657,9 +659,11 @@ class RecentChange {
        /**
         * Makes a pseudo-RC entry from a cur row
         *
+        * @deprected in 1.22
         * @param $row
         */
        public function loadFromCurRow( $row ) {
+               wfDeprecated( __METHOD__, '1.22' );
                $this->mAttribs = array(
                        'rc_timestamp' => wfTimestamp( TS_MW, $row->rev_timestamp ),
                        'rc_cur_time' => $row->rev_timestamp,
index b4a1c62..7400a5a 100644 (file)
@@ -740,7 +740,7 @@ class Sanitizer {
 
                        # WAI-ARIA
                        # http://www.w3.org/TR/wai-aria/
-                       # http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#wai-aria
+                       # http://www.whatwg.org/html/elements.html#wai-aria
                        # For now we only support role="presentation" until we work out what roles should be
                        # usable by content and we ensure that our code explicitly rejects patterns that
                        # violate HTML5's ARIA restrictions.
@@ -1011,7 +1011,7 @@ class Sanitizer {
         *                                                          in the id and
         *                                                          name attributes
         * @see http://www.w3.org/TR/html401/struct/links.html#h-12.2.3 Anchors with the id attribute
-        * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#the-id-attribute
+        * @see http://www.whatwg.org/html/elements.html#the-id-attribute
         *   HTML5 definition of id attribute
         *
         * @param string $id id to escape
@@ -1467,7 +1467,7 @@ class Sanitizer {
                }
 
                if ( $wgAllowMicrodataAttributes ) {
-                       # add HTML5 microdata tags as specified by http://www.whatwg.org/specs/web-apps/current-work/multipage/microdata.html#the-microdata-model
+                       # add HTML5 microdata tags as specified by http://www.whatwg.org/html/microdata.html#the-microdata-model
                        $common = array_merge( $common, array(
                                'itemid', 'itemprop', 'itemref', 'itemscope', 'itemtype'
                        ) );
@@ -1606,7 +1606,7 @@ class Sanitizer {
                        'hr'         => array_merge( $common, array( 'noshade', 'size', 'width' ) ),
 
                        # HTML Ruby annotation text module, simple ruby only.
-                       # http://www.whatwg.org/specs/web-apps/current-work/multipage/text-level-semantics.html#the-ruby-element
+                       # http://www.whatwg.org/html/text-level-semantics.html#the-ruby-element
                        'ruby'       => $common,
                        # rbc
                        # rtc
@@ -1623,7 +1623,7 @@ class Sanitizer {
                        'bdi' => $common,
 
                        # HTML5 elements, defined by:
-                       # http://www.whatwg.org/specs/web-apps/current-work/multipage/
+                       # http://www.whatwg.org/html/
                        'data' => array_merge( $common, array( 'value' ) ),
                        'time' => array_merge( $common, array( 'datetime' ) ),
                        'mark' => $common,
@@ -1740,7 +1740,7 @@ class Sanitizer {
         * Does a string look like an e-mail address?
         *
         * This validates an email address using an HTML5 specification found at:
-        * http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
+        * http://www.whatwg.org/html/states-of-the-type-attribute.html#valid-e-mail-address
         * Which as of 2011-01-24 says:
         *
         *   A valid e-mail address is a string that matches the ABNF production
index 29e810e..a543126 100644 (file)
@@ -4492,8 +4492,6 @@ class Title {
         * @return Bool true if the update succeeded
         */
        public function invalidateCache() {
-               global $wgMemc;
-
                if ( wfReadOnly() ) {
                        return false;
                }
index c6d4336..ef3f9ac 100644 (file)
@@ -251,7 +251,7 @@ class User {
        }
 
        /**
-        * @return String
+        * @return string
         */
        function __toString() {
                return $this->getName();
@@ -266,7 +266,7 @@ class User {
                }
                wfProfileIn( __METHOD__ );
 
-               # Set it now to avoid infinite recursion in accessors
+               // Set it now to avoid infinite recursion in accessors
                $this->mLoadedItems = true;
 
                switch ( $this->mFrom ) {
@@ -276,7 +276,7 @@ class User {
                        case 'name':
                                $this->mId = self::idFromName( $this->mName );
                                if ( !$this->mId ) {
-                                       # Nonexistent user placeholder object
+                                       // Nonexistent user placeholder object
                                        $this->loadDefaults( $this->mName );
                                } else {
                                        $this->loadFromId();
@@ -301,7 +301,7 @@ class User {
 
        /**
         * Load user table data, given mId has already been set.
-        * @return Bool false if the ID does not exist, true otherwise
+        * @return bool false if the ID does not exist, true otherwise
         */
        public function loadFromId() {
                global $wgMemc;
@@ -310,25 +310,25 @@ class User {
                        return false;
                }
 
-               # Try cache
+               // Try cache
                $key = wfMemcKey( 'user', 'id', $this->mId );
                $data = $wgMemc->get( $key );
                if ( !is_array( $data ) || $data['mVersion'] < MW_USER_VERSION ) {
-                       # Object is expired, load from DB
+                       // Object is expired, load from DB
                        $data = false;
                }
 
                if ( !$data ) {
                        wfDebug( "User: cache miss for user {$this->mId}\n" );
-                       # Load from DB
+                       // Load from DB
                        if ( !$this->loadFromDatabase() ) {
-                               # Can't load from ID, user is anonymous
+                               // Can't load from ID, user is anonymous
                                return false;
                        }
                        $this->saveToCache();
                } else {
                        wfDebug( "User: got user {$this->mId} from cache\n" );
-                       # Restore from cache
+                       // Restore from cache
                        foreach ( self::$mCacheVars as $name ) {
                                $this->$name = $data[$name];
                        }
@@ -370,14 +370,14 @@ class User {
         * you have both an ID and a name handy.
         *
         * @param string $name Username, validated by Title::newFromText()
-        * @param string|Bool $validate Validate username. Takes the same parameters as
-        *    User::getCanonicalName(), except that true is accepted as an alias
-        *    for 'valid', for BC.
+        * @param string|bool $validate Validate username. Takes the same parameters as
+        *  User::getCanonicalName(), except that true is accepted as an alias
+        *  for 'valid', for BC.
         *
         * @return User|bool User object, or false if the username is invalid
-        *    (e.g. if it contains illegal characters or is an IP address). If the
-        *    username is not present in the database, the result will be a user object
-        *    with a name, zero user ID and default settings.
+        *  (e.g. if it contains illegal characters or is an IP address). If the
+        *  username is not present in the database, the result will be a user object
+        *  with a name, zero user ID and default settings.
         */
        public static function newFromName( $name, $validate = 'valid' ) {
                if ( $validate === true ) {
@@ -387,7 +387,7 @@ class User {
                if ( $name === false ) {
                        return false;
                } else {
-                       # Create unloaded user object
+                       // Create unloaded user object
                        $u = new User;
                        $u->mName = $name;
                        $u->mFrom = 'name';
@@ -418,7 +418,7 @@ class User {
         * If the code is invalid or has expired, returns NULL.
         *
         * @param string $code Confirmation code
-        * @return User object, or null
+        * @return User|null
         */
        public static function newFromConfirmationCode( $code ) {
                $dbr = wfGetDB( DB_SLAVE );
@@ -437,7 +437,7 @@ class User {
         * Create a new user object using data from session or cookies. If the
         * login credentials are invalid, the result is an anonymous user.
         *
-        * @param $request WebRequest object to use; $wgRequest will be used if omitted.
+        * @param WebRequest $request Object to use; $wgRequest will be used if omitted.
         * @return User object
         */
        public static function newFromSession( WebRequest $request = null ) {
@@ -472,7 +472,7 @@ class User {
        /**
         * Get the username corresponding to a given user ID
         * @param int $id User ID
-        * @return String|bool The corresponding username
+        * @return string|bool The corresponding username
         */
        public static function whoIs( $id ) {
                return UserCache::singleton()->getProp( $id, 'name' );
@@ -482,7 +482,7 @@ class User {
         * Get the real name of a user given their user ID
         *
         * @param int $id User ID
-        * @return String|bool The corresponding user's real name
+        * @return string|bool The corresponding user's real name
         */
        public static function whoIsReal( $id ) {
                return UserCache::singleton()->getProp( $id, 'real_name' );
@@ -491,12 +491,12 @@ class User {
        /**
         * Get database id given a user name
         * @param string $name Username
-        * @return Int|Null The corresponding user's ID, or null if user is nonexistent
+        * @return int|null The corresponding user's ID, or null if user is nonexistent
         */
        public static function idFromName( $name ) {
                $nt = Title::makeTitleSafe( NS_USER, $name );
                if ( is_null( $nt ) ) {
-                       # Illegal name
+                       // Illegal name
                        return null;
                }
 
@@ -542,8 +542,8 @@ class User {
         * addresses like this, if we allowed accounts like this to be created
         * new users could get the old edits of these anonymous users.
         *
-        * @param string $name to match
-        * @return Bool
+        * @param string $name Name to match
+        * @return bool
         */
        public static function isIP( $name ) {
                return preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/', $name ) || IP::isIPv6( $name );
@@ -557,8 +557,8 @@ class User {
         * is longer than the maximum allowed username size or doesn't begin with
         * a capital letter.
         *
-        * @param string $name to match
-        * @return Bool
+        * @param string $name Name to match
+        * @return bool
         */
        public static function isValidUserName( $name ) {
                global $wgContLang, $wgMaxNameChars;
@@ -611,8 +611,8 @@ class User {
         * If an account already exists in this form, login will be blocked
         * by a failure to pass this function.
         *
-        * @param string $name to match
-        * @return Bool
+        * @param string $name Name to match
+        * @return bool
         */
        public static function isUsableName( $name ) {
                global $wgReservedUsernames;
@@ -649,7 +649,7 @@ class User {
         * isValidUserName() to avoid disrupting existing accounts.
         *
         * @param string $name to match
-        * @return Bool
+        * @return bool
         */
        public static function isCreatableName( $name ) {
                global $wgInvalidUsernameCharacters;
@@ -679,7 +679,7 @@ class User {
         * Is the input a valid password for this user?
         *
         * @param string $password Desired password
-        * @return Bool
+        * @return bool
         */
        public function isValidPassword( $password ) {
                //simple boolean wrapper for getPasswordValidity
@@ -731,7 +731,7 @@ class User {
         * Does a string look like an e-mail address?
         *
         * This validates an email address using an HTML5 specification found at:
-        * http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
+        * http://www.whatwg.org/html/states-of-the-type-attribute.html#valid-e-mail-address
         * Which as of 2011-01-24 says:
         *
         *     A valid e-mail address is a string that matches the ABNF production
@@ -751,7 +751,7 @@ class User {
         * pass validation here.
         *
         * @param string $addr E-mail address
-        * @return Bool
+        * @return bool
         * @deprecated since 1.18 call Sanitizer::isValidEmail() directly
         */
        public static function isValidEmailAddr( $addr ) {
@@ -763,7 +763,7 @@ class User {
         * Given unvalidated user input, return a canonical username, or false if
         * the username is invalid.
         * @param string $name User input
-        * @param string|Bool $validate type of validation to use:
+        * @param string|bool $validate type of validation to use:
         *                - false        No validation
         *                - 'valid'      Valid for batch processes
         *                - 'usable'     Valid for batch processes and login
@@ -773,7 +773,7 @@ class User {
         * @return bool|string
         */
        public static function getCanonicalName( $name, $validate = 'valid' ) {
-               # Force usernames to capital
+               // Force usernames to capital
                global $wgContLang;
                $name = $wgContLang->ucfirst( $name );
 
@@ -784,15 +784,15 @@ class User {
                        return false;
                }
 
-               # Clean up name according to title rules
+               // Clean up name according to title rules
                $t = ( $validate === 'valid' ) ?
                        Title::newFromText( $name ) : Title::makeTitle( NS_USER, $name );
-               # Check for invalid titles
+               // Check for invalid titles
                if ( is_null( $t ) ) {
                        return false;
                }
 
-               # Reject various classes of invalid names
+               // Reject various classes of invalid names
                global $wgAuth;
                $name = $wgAuth->getCanonicalName( $t->getText() );
 
@@ -824,7 +824,7 @@ class User {
         * Count the number of edits of a user
         *
         * @param int $uid User ID to check
-        * @return Int the user's edit count
+        * @return int The user's edit count
         *
         * @deprecated since 1.21 in favour of User::getEditCount
         */
@@ -837,7 +837,7 @@ class User {
        /**
         * Return a random password.
         *
-        * @return String new random password
+        * @return string New random password
         */
        public static function randomPassword() {
                global $wgMinimalPasswordLength;
@@ -900,7 +900,7 @@ class User {
         * @param string $all 'all' to check if the whole object has been loaded
         *        or any other string to check if only the item is available (e.g.
         *        for optimisation)
-        * @return Boolean
+        * @return boolean
         */
        public function isItemLoaded( $item, $all = 'all' ) {
                return ( $this->mLoadedItems === true && $all === 'all' ) ||
@@ -910,7 +910,7 @@ class User {
        /**
         * Set that an item has been loaded
         *
-        * @param $item String
+        * @param string $item
         */
        protected function setItemLoaded( $item ) {
                if ( is_array( $this->mLoadedItems ) ) {
@@ -920,7 +920,7 @@ class User {
 
        /**
         * Load user data from the session or login cookie.
-        * @return Bool True if the user is logged in, false otherwise.
+        * @return bool True if the user is logged in, false otherwise.
         */
        private function loadFromSession() {
                $result = null;
@@ -959,13 +959,13 @@ class User {
 
                $proposedUser = User::newFromId( $sId );
                if ( !$proposedUser->isLoggedIn() ) {
-                       # Not a valid ID
+                       // Not a valid ID
                        return false;
                }
 
                global $wgBlockDisablesLogin;
                if ( $wgBlockDisablesLogin && $proposedUser->isBlocked() ) {
-                       # User blocked and we've disabled blocked user logins
+                       // User blocked and we've disabled blocked user logins
                        return false;
                }
 
@@ -979,7 +979,7 @@ class User {
                        $passwordCorrect = ( strlen( $token ) && $token === $request->getCookie( 'Token' ) );
                        $from = 'cookie';
                } else {
-                       # No session or persistent login cookie
+                       // No session or persistent login cookie
                        return false;
                }
 
@@ -989,7 +989,7 @@ class User {
                        wfDebug( "User: logged in from $from\n" );
                        return true;
                } else {
-                       # Invalid credentials
+                       // Invalid credentials
                        wfDebug( "User: can't log in from $from, invalid credentials\n" );
                        return false;
                }
@@ -999,13 +999,13 @@ class User {
         * Load user and user_group data from the database.
         * $this->mId must be set, this is how the user is identified.
         *
-        * @return Bool True if the user exists, false if the user is anonymous
+        * @return bool True if the user exists, false if the user is anonymous
         */
        public function loadFromDatabase() {
-               # Paranoia
+               // Paranoia
                $this->mId = intval( $this->mId );
 
-               /** Anonymous user */
+               // Anonymous user
                if ( !$this->mId ) {
                        $this->loadDefaults();
                        return false;
@@ -1017,13 +1017,13 @@ class User {
                wfRunHooks( 'UserLoadFromDatabase', array( $this, &$s ) );
 
                if ( $s !== false ) {
-                       # Initialise user table data
+                       // Initialise user table data
                        $this->loadFromRow( $s );
                        $this->mGroups = null; // deferred
                        $this->getEditCount(); // revalidation for nulls
                        return true;
                } else {
-                       # Invalid user_id
+                       // Invalid user_id
                        $this->mId = 0;
                        $this->loadDefaults();
                        return false;
@@ -1186,7 +1186,7 @@ class User {
         * Clear various cached data stored in this object. The cache of the user table
         * data (i.e. self::$mCacheVars) is not cleared unless $reloadFrom is given.
         *
-        * @param bool|String $reloadFrom Reload user and user_groups table data from a
+        * @param bool|string $reloadFrom Reload user and user_groups table data from a
         *   given source. May be "name", "id", "defaults", "session", or false for
         *   no reload.
         */
@@ -1227,7 +1227,7 @@ class User {
                }
 
                $defOpt = $wgDefaultUserOptions;
-               # default language setting
+               // Default language setting
                $defOpt['language'] = $defOpt['variant'] = $wgContLang->getCode();
                foreach ( SearchEngine::searchableNamespaces() as $nsnum => $nsname ) {
                        $defOpt['searchNs' . $nsnum] = !empty( $wgNamespacesToBeSearchedDefault[$nsnum] );
@@ -1243,7 +1243,7 @@ class User {
         * Get a given default option value.
         *
         * @param string $opt Name of option to retrieve
-        * @return String Default option value
+        * @return string Default option value
         */
        public static function getDefaultOption( $opt ) {
                $defOpts = self::getDefaultOptions();
@@ -1287,14 +1287,14 @@ class User {
                        $ip = null;
                }
 
-               # User/IP blocking
+               // User/IP blocking
                $block = Block::newFromTarget( $this, $ip, !$bFromSlave );
 
-               # Proxy blocking
+               // Proxy blocking
                if ( !$block instanceof Block && $ip !== null && !$this->isAllowed( 'proxyunbannable' )
                        && !in_array( $ip, $wgProxyWhitelist ) )
                {
-                       # Local list
+                       // Local list
                        if ( self::isLocallyBlockedProxy( $ip ) ) {
                                $block = new Block;
                                $block->setBlocker( wfMessage( 'proxyblocker' )->text() );
@@ -1308,7 +1308,7 @@ class User {
                        }
                }
 
-               # (bug 23343) Apply IP blocks to the contents of XFF headers, if enabled
+               // (bug 23343) Apply IP blocks to the contents of XFF headers, if enabled
                if ( !$block instanceof Block
                        && $wgApplyIpBlocksToXff
                        && $ip !== null
@@ -1351,7 +1351,7 @@ class User {
         *
         * @param string $ip IP to check
         * @param bool $checkWhitelist whether to check the whitelist first
-        * @return Bool True if blacklisted.
+        * @return bool True if blacklisted.
         */
        public function isDnsBlacklisted( $ip, $checkWhitelist = false ) {
                global $wgEnableSorbs, $wgEnableDnsBlacklist,
@@ -1374,7 +1374,7 @@ class User {
         *
         * @param string $ip IP to check
         * @param string|array $bases of Strings: URL of the DNS blacklist
-        * @return Bool True if blacklisted.
+        * @return bool True if blacklisted.
         */
        public function inDnsBlacklist( $ip, $bases ) {
                wfProfileIn( __METHOD__ );
@@ -1382,15 +1382,15 @@ class User {
                $found = false;
                // @todo FIXME: IPv6 ???  (http://bugs.php.net/bug.php?id=33170)
                if ( IP::isIPv4( $ip ) ) {
-                       # Reverse IP, bug 21255
+                       // Reverse IP, bug 21255
                        $ipReversed = implode( '.', array_reverse( explode( '.', $ip ) ) );
 
                        foreach ( (array)$bases as $base ) {
-                               # Make hostname
-                               # If we have an access key, use that too (ProjectHoneypot, etc.)
+                               // Make hostname
+                               // If we have an access key, use that too (ProjectHoneypot, etc.)
                                if ( is_array( $base ) ) {
                                        if ( count( $base ) >= 2 ) {
-                                               # Access key is 1, base URL is 0
+                                               // Access key is 1, base URL is 0
                                                $host = "{$base[1]}.$ipReversed.{$base[0]}";
                                        } else {
                                                $host = "$ipReversed.{$base[0]}";
@@ -1399,7 +1399,7 @@ class User {
                                        $host = "$ipReversed.$base";
                                }
 
-                               # Send query
+                               // Send query
                                $ipList = gethostbynamel( $host );
 
                                if ( $ipList ) {
@@ -1432,7 +1432,7 @@ class User {
                wfProfileIn( __METHOD__ );
 
                if ( !is_array( $wgProxyList ) ) {
-                       # Load from the specified file
+                       // Load from the specified file
                        $wgProxyList = array_map( 'trim', file( $wgProxyList ) );
                }
 
@@ -1441,7 +1441,7 @@ class User {
                } elseif ( array_search( $ip, $wgProxyList ) !== false ) {
                        $ret = true;
                } elseif ( array_key_exists( $ip, $wgProxyList ) ) {
-                       # Old-style flipped proxy list
+                       // Old-style flipped proxy list
                        $ret = true;
                } else {
                        $ret = false;
@@ -1453,7 +1453,7 @@ class User {
        /**
         * Is this user subject to rate limiting?
         *
-        * @return Bool True if rate limited
+        * @return bool True if rate limited
         */
        public function isPingLimitable() {
                global $wgRateLimitsExcludedIPs;
@@ -1474,10 +1474,10 @@ class User {
         * last-hit counters will be shared across wikis.
         *
         * @param string $action Action to enforce; 'edit' if unspecified
-        * @return Bool True if a rate limiter was tripped
+        * @return bool True if a rate limiter was tripped
         */
        public function pingLimiter( $action = 'edit' ) {
-               # Call the 'PingLimiter' hook
+               // Call the 'PingLimiter' hook
                $result = false;
                if ( !wfRunHooks( 'PingLimiter', array( &$this, $action, &$result ) ) ) {
                        return $result;
@@ -1488,7 +1488,7 @@ class User {
                        return false;
                }
 
-               # Some groups shouldn't trigger the ping limiter, ever
+               // Some groups shouldn't trigger the ping limiter, ever
                if ( !$this->isPingLimitable() ) {
                        return false;
                }
@@ -1581,7 +1581,7 @@ class User {
         * Check if user is blocked
         *
         * @param bool $bFromSlave Whether to check the slave database instead of the master
-        * @return Bool True if blocked, false otherwise
+        * @return bool True if blocked, false otherwise
         */
        public function isBlocked( $bFromSlave = true ) { // hacked from false due to horrible probs on site
                return $this->getBlock( $bFromSlave ) instanceof Block && $this->getBlock()->prevents( 'edit' );
@@ -1601,9 +1601,9 @@ class User {
        /**
         * Check if user is blocked from editing a particular article
         *
-        * @param $title Title to check
+        * @param Title $title Title to check
         * @param bool $bFromSlave whether to check the slave database instead of the master
-        * @return Bool
+        * @return bool
         */
        function isBlockedFrom( $title, $bFromSlave = false ) {
                global $wgBlockAllowsUTEdit;
@@ -1611,7 +1611,7 @@ class User {
 
                $blocked = $this->isBlocked( $bFromSlave );
                $allowUsertalk = ( $wgBlockAllowsUTEdit ? $this->mAllowUsertalk : false );
-               # If a user's name is suppressed, they cannot make edits anywhere
+               // If a user's name is suppressed, they cannot make edits anywhere
                if ( !$this->mHideName && $allowUsertalk && $title->getText() === $this->getName() &&
                  $title->getNamespace() == NS_USER_TALK ) {
                        $blocked = false;
@@ -1626,7 +1626,7 @@ class User {
 
        /**
         * If user is blocked, return the name of the user who placed the block
-        * @return String name of blocker
+        * @return string Name of blocker
         */
        public function blockedBy() {
                $this->getBlockedStatus();
@@ -1635,7 +1635,7 @@ class User {
 
        /**
         * If user is blocked, return the specified reason for the block
-        * @return String Blocking reason
+        * @return string Blocking reason
         */
        public function blockedFor() {
                $this->getBlockedStatus();
@@ -1644,7 +1644,7 @@ class User {
 
        /**
         * If user is blocked, return the ID for the block
-        * @return Int Block ID
+        * @return int Block ID
         */
        public function getBlockId() {
                $this->getBlockedStatus();
@@ -1657,7 +1657,7 @@ class User {
         * This is intended for quick UI checks.
         *
         * @param string $ip IP address, uses current client if none given
-        * @return Bool True if blocked, false otherwise
+        * @return bool True if blocked, false otherwise
         */
        public function isBlockedGlobally( $ip = '' ) {
                if ( $this->mBlockedGlobally !== null ) {
@@ -1678,7 +1678,7 @@ class User {
        /**
         * Check if user account is locked
         *
-        * @return Bool True if locked, false otherwise
+        * @return bool True if locked, false otherwise
         */
        public function isLocked() {
                if ( $this->mLocked !== null ) {
@@ -1693,7 +1693,7 @@ class User {
        /**
         * Check if user account is hidden
         *
-        * @return Bool True if hidden, false otherwise
+        * @return bool True if hidden, false otherwise
         */
        public function isHidden() {
                if ( $this->mHideName !== null ) {
@@ -1710,7 +1710,7 @@ class User {
 
        /**
         * Get the user's ID.
-        * @return Int The user's ID; 0 if the user is anonymous or nonexistent
+        * @return int The user's ID; 0 if the user is anonymous or nonexistent
         */
        public function getId() {
                if ( $this->mId === null && $this->mName !== null && User::isIP( $this->mName ) ) {
@@ -1734,16 +1734,16 @@ class User {
 
        /**
         * Get the user name, or the IP of an anonymous user
-        * @return String User's name or IP address
+        * @return string User's name or IP address
         */
        public function getName() {
                if ( $this->isItemLoaded( 'name', 'only' ) ) {
-                       # Special case optimisation
+                       // Special case optimisation
                        return $this->mName;
                } else {
                        $this->load();
                        if ( $this->mName === false ) {
-                               # Clean up IPs
+                               // Clean up IPs
                                $this->mName = IP::sanitizeIP( $this->getRequest()->getIP() );
                        }
                        return $this->mName;
@@ -1770,7 +1770,7 @@ class User {
 
        /**
         * Get the user's name escaped by underscores.
-        * @return String Username escaped by underscores.
+        * @return string Username escaped by underscores.
         */
        public function getTitleKey() {
                return str_replace( ' ', '_', $this->getName() );
@@ -1778,17 +1778,17 @@ class User {
 
        /**
         * Check if the user has new messages.
-        * @return Bool True if the user has new messages
+        * @return bool True if the user has new messages
         */
        public function getNewtalk() {
                $this->load();
 
-               # Load the newtalk status if it is unloaded (mNewtalk=-1)
+               // Load the newtalk status if it is unloaded (mNewtalk=-1)
                if ( $this->mNewtalk === -1 ) {
                        $this->mNewtalk = false; # reset talk page status
 
-                       # Check memcached separately for anons, who have no
-                       # entire User object stored in there.
+                       // Check memcached separately for anons, who have no
+                       // entire User object stored in there.
                        if ( !$this->mId ) {
                                global $wgDisableAnonTalk;
                                if ( $wgDisableAnonTalk ) {
@@ -1818,7 +1818,7 @@ class User {
        /**
         * Return the revision and link for the oldest new talk page message for
         * this user.
-        * Note: This function was designed to accomodate multiple talk pages, but
+        * @note This function was designed to accomodate multiple talk pages, but
         * currently only returns a single link and revision.
         * @return Array
         */
@@ -1842,7 +1842,7 @@ class User {
 
        /**
         * Get the revision ID for the oldest new talk page message for this user
-        * @return Integer or null if there are no new messages
+        * @return int|null Revision id or null if there are no new messages
         */
        public function getNewMessageRevisionId() {
                $newMessageRevisionId = null;
@@ -1867,9 +1867,9 @@ class User {
         *
         * @see getNewtalk()
         * @param string $field 'user_ip' for anonymous users, 'user_id' otherwise
-        * @param string|Int $id User's IP address for anonymous users, User ID otherwise
+        * @param string|int $id User's IP address for anonymous users, User ID otherwise
         * @param bool $fromMaster true to fetch from the master, false for a slave
-        * @return Bool True if the user has new messages
+        * @return bool True if the user has new messages
         */
        protected function checkNewtalk( $field, $id, $fromMaster = false ) {
                if ( $fromMaster ) {
@@ -1885,9 +1885,9 @@ class User {
        /**
         * Add or update the new messages flag
         * @param string $field 'user_ip' for anonymous users, 'user_id' otherwise
-        * @param string|Int $id User's IP address for anonymous users, User ID otherwise
+        * @param string|int $id User's IP address for anonymous users, User ID otherwise
         * @param $curRev Revision new, as yet unseen revision of the user talk page. Ignored if null.
-        * @return Bool True if successful, false otherwise
+        * @return bool True if successful, false otherwise
         */
        protected function updateNewtalk( $field, $id, $curRev = null ) {
                // Get timestamp of the talk page revision prior to the current one
@@ -1911,8 +1911,8 @@ class User {
        /**
         * Clear the new messages flag for the given user
         * @param string $field 'user_ip' for anonymous users, 'user_id' otherwise
-        * @param string|Int $id User's IP address for anonymous users, User ID otherwise
-        * @return Bool True if successful, false otherwise
+        * @param string|int $id User's IP address for anonymous users, User ID otherwise
+        * @return bool True if successful, false otherwise
         */
        protected function deleteNewtalk( $field, $id ) {
                $dbw = wfGetDB( DB_MASTER );
@@ -1970,7 +1970,7 @@ class User {
        /**
         * Generate a current or new-future timestamp to be stored in the
         * user_touched field when we update things.
-        * @return String Timestamp in TS_MW format
+        * @return string Timestamp in TS_MW format
         */
        private static function newTouchedTimestamp() {
                global $wgClockSkewFudge;
@@ -2029,7 +2029,6 @@ class User {
        /**
         * Validate the cache for this account.
         * @param string $timestamp A timestamp in TS_MW format
-        *
         * @return bool
         */
        public function validateCache( $timestamp ) {
@@ -2039,7 +2038,7 @@ class User {
 
        /**
         * Get the user touched timestamp
-        * @return String timestamp
+        * @return string timestamp
         */
        public function getTouched() {
                $this->load();
@@ -2097,8 +2096,8 @@ class User {
         * Set the password and reset the random token unconditionally.
         *
         * @param string|null $str New password to set or null to set an invalid
-        *        password hash meaning that the user will not be able to log in
-        *        through the web interface.
+        *  password hash meaning that the user will not be able to log in
+        *  through the web interface.
         */
        public function setInternalPassword( $str ) {
                $this->load();
@@ -2117,7 +2116,7 @@ class User {
        /**
         * Get the user's current token.
         * @param bool $forceCreation Force the generation of a new token if the user doesn't have one (default=true for backwards compatibility)
-        * @return String Token
+        * @return string Token
         */
        public function getToken( $forceCreation = true ) {
                $this->load();
@@ -2159,7 +2158,7 @@ class User {
        /**
         * Has password reminder email been sent within the last
         * $wgPasswordReminderResendTime hours?
-        * @return Bool
+        * @return bool
         */
        public function isPasswordReminderThrottled() {
                global $wgPasswordReminderResendTime;
@@ -2173,7 +2172,7 @@ class User {
 
        /**
         * Get the user's e-mail address
-        * @return String User's email address
+        * @return string User's email address
         */
        public function getEmail() {
                $this->load();
@@ -2183,7 +2182,7 @@ class User {
 
        /**
         * Get the timestamp of the user's e-mail authentication
-        * @return String TS_MW timestamp
+        * @return string TS_MW timestamp
         */
        public function getEmailAuthenticationTimestamp() {
                $this->load();
@@ -2227,11 +2226,11 @@ class User {
                $this->setEmail( $str );
 
                if ( $str !== '' && $wgEmailAuthentication ) {
-                       # Send a confirmation request to the new address if needed
+                       // Send a confirmation request to the new address if needed
                        $type = $oldaddr != '' ? 'changed' : 'set';
                        $result = $this->sendConfirmationMail( $type );
                        if ( $result->isGood() ) {
-                               # Say the the caller that a confirmation mail has been sent
+                               // Say the the caller that a confirmation mail has been sent
                                $result->value = 'eauth';
                        }
                } else {
@@ -2243,7 +2242,7 @@ class User {
 
        /**
         * Get the user's real name
-        * @return String User's real name
+        * @return string User's real name
         */
        public function getRealName() {
                if ( !$this->isItemLoaded( 'realname' ) ) {
@@ -2267,8 +2266,8 @@ class User {
         *
         * @param string $oname The option to check
         * @param string $defaultOverride A default value returned if the option does not exist
-        * @param bool $ignoreHidden = whether to ignore the effects of $wgHiddenPrefs
-        * @return String User's current value for the option
+        * @param bool $ignoreHidden Whether to ignore the effects of $wgHiddenPrefs
+        * @return string User's current value for the option
         * @see getBoolOption()
         * @see getIntOption()
         */
@@ -2321,7 +2320,7 @@ class User {
         * Get the user's current setting for a given option, as a boolean value.
         *
         * @param string $oname The option to check
-        * @return Bool User's current value for the option
+        * @return bool User's current value for the option
         * @see getOption()
         */
        public function getBoolOption( $oname ) {
@@ -2333,7 +2332,7 @@ class User {
         *
         * @param string $oname The option to check
         * @param int $defaultOverride A default value returned if the option does not exist
-        * @return Int User's current value for the option
+        * @return int User's current value for the option
         * @see getOption()
         */
        public function getIntOption( $oname, $defaultOverride = 0 ) {
@@ -2348,7 +2347,7 @@ class User {
         * Set the given option for a user.
         *
         * @param string $oname The option to set
-        * @param $val mixed New value to set
+        * @param mixed $val New value to set
         */
        public function setOption( $oname, $val ) {
                $this->loadOptions();
@@ -2472,11 +2471,11 @@ class User {
         * and 'all', which forces a reset of *all* preferences and overrides everything else.
         *
         * @param array|string $resetKinds which kinds of preferences to reset. Defaults to
-        *             array( 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' )
-        *             for backwards-compatibility.
+        *  array( 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' )
+        *  for backwards-compatibility.
         * @param $context IContextSource|null context source used when $resetKinds
-        *             does not contain 'all', passed to getOptionKinds().
-        *             Defaults to RequestContext::getMain() when null.
+        *  does not contain 'all', passed to getOptionKinds().
+        *  Defaults to RequestContext::getMain() when null.
         */
        public function resetOptions(
                $resetKinds = array( 'registered', 'registered-multiselect', 'registered-checkmatrix', 'unused' ),
@@ -2519,7 +2518,7 @@ class User {
 
        /**
         * Get the user's preferred date format.
-        * @return String User's preferred date format
+        * @return string User's preferred date format
         */
        public function getDatePreference() {
                // Important migration for old data rows
@@ -2544,8 +2543,8 @@ class User {
                global $wgMaxArticleSize; # Maximum article size, in Kb
                $threshold = $this->getIntOption( 'stubthreshold' );
                if ( $threshold > $wgMaxArticleSize * 1024 ) {
-                       # If they have set an impossible value, disable the preference
-                       # so we can use the parser cache again.
+                       // If they have set an impossible value, disable the preference
+                       // so we can use the parser cache again.
                        $threshold = 0;
                }
                return $threshold;
@@ -2590,7 +2589,7 @@ class User {
                                $this->getGroups(), // explicit groups
                                $this->getAutomaticGroups( $recache ) // implicit groups
                        ) );
-                       # Hook for additional groups
+                       // Hook for additional groups
                        wfRunHooks( 'UserEffectiveGroups', array( &$this, &$this->mEffectiveGroups ) );
                        // Force reindexation of groups when a hook has unset one of them
                        $this->mEffectiveGroups = array_values( array_unique( $this->mEffectiveGroups ) );
@@ -2619,8 +2618,8 @@ class User {
                                ) );
                        }
                        if ( $recache ) {
-                               # Assure data consistency with rights/groups,
-                               # as getEffectiveGroups() depends on this function
+                               // Assure data consistency with rights/groups,
+                               // as getEffectiveGroups() depends on this function
                                $this->mEffectiveGroups = null;
                        }
                        wfProfileOut( __METHOD__ );
@@ -2654,7 +2653,7 @@ class User {
 
        /**
         * Get the user's edit count.
-        * @return Int
+        * @return int
         */
        public function getEditCount() {
                if ( !$this->getId() ) {
@@ -2742,7 +2741,7 @@ class User {
 
        /**
         * Get whether the user is logged in
-        * @return Bool
+        * @return bool
         */
        public function isLoggedIn() {
                return $this->getID() != 0;
@@ -2750,7 +2749,7 @@ class User {
 
        /**
         * Get whether the user is anonymous
-        * @return Bool
+        * @return bool
         */
        public function isAnon() {
                return !$this->isLoggedIn();
@@ -2760,7 +2759,7 @@ class User {
         * Check if user is allowed to access a feature / make an action
         *
         * @internal param \String $varargs permissions to test
-        * @return Boolean: True if user is allowed to perform *any* of the given actions
+        * @return boolean: True if user is allowed to perform *any* of the given actions
         *
         * @return bool
         */
@@ -2791,28 +2790,28 @@ class User {
 
        /**
         * Internal mechanics of testing a permission
-        * @param $action String
+        * @param string $action
         * @return bool
         */
        public function isAllowed( $action = '' ) {
                if ( $action === '' ) {
                        return true; // In the spirit of DWIM
                }
-               # Patrolling may not be enabled
+               // Patrolling may not be enabled
                if ( $action === 'patrol' || $action === 'autopatrol' ) {
                        global $wgUseRCPatrol, $wgUseNPPatrol;
                        if ( !$wgUseRCPatrol && !$wgUseNPPatrol ) {
                                return false;
                        }
                }
-               # Use strict parameter to avoid matching numeric 0 accidentally inserted
-               # by misconfiguration: 0 == 'foo'
+               // Use strict parameter to avoid matching numeric 0 accidentally inserted
+               // by misconfiguration: 0 == 'foo'
                return in_array( $action, $this->getRights(), true );
        }
 
        /**
         * Check whether to enable recent changes patrol features for this user
-        * @return Boolean: True or false
+        * @return boolean: True or false
         */
        public function useRCPatrol() {
                global $wgUseRCPatrol;
@@ -2821,11 +2820,14 @@ class User {
 
        /**
         * Check whether to enable new pages patrol features for this user
-        * @return Bool True or false
+        * @return bool True or false
         */
        public function useNPPatrol() {
                global $wgUseRCPatrol, $wgUseNPPatrol;
-               return ( ( $wgUseRCPatrol || $wgUseNPPatrol ) && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) ) );
+               return (
+                       ( $wgUseRCPatrol || $wgUseNPPatrol )
+                               && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
+               );
        }
 
        /**
@@ -2877,7 +2879,7 @@ class User {
        /**
         * Check the watched status of an article.
         * @param $title Title of the article to look at
-        * @return Bool
+        * @return bool
         */
        public function isWatched( $title ) {
                return $this->getWatchedItem( $title )->isWatched();
@@ -2910,7 +2912,7 @@ class User {
        public function clearNotification( &$title ) {
                global $wgUseEnotif, $wgShowUpdatedMarker;
 
-               # Do nothing if the database is locked to writes
+               // Do nothing if the database is locked to writes
                if ( wfReadOnly() ) {
                        return;
                }
@@ -3011,7 +3013,7 @@ class User {
         * @param string $value Value to set
         * @param int $exp Expiration time, as a UNIX time value;
         *                   if 0 or not specified, use the default $wgCookieExpiration
-        * @param $secure Bool
+        * @param bool $secure
         *  true: Force setting the secure attribute when setting the cookie
         *  false: Force NOT setting the secure attribute when setting the cookie
         *  null (default): Use the default ($wgCookieSecure) to set the secure attribute
@@ -3112,7 +3114,7 @@ class User {
                $this->clearCookie( 'Token' );
                $this->clearCookie( 'forceHTTPS' );
 
-               # Remember when user logged out, to prevent seeing cached pages
+               // Remember when user logged out, to prevent seeing cached pages
                $this->setCookie( 'LoggedOut', wfTimestampNow(), time() + 86400 );
        }
 
@@ -3164,7 +3166,7 @@ class User {
 
        /**
         * If only this user's username is known, and it exists, return the user ID.
-        * @return Int
+        * @return int
         */
        public function idForName() {
                $s = trim( $this->getName() );
@@ -3354,7 +3356,7 @@ class User {
         * settings.
         *
         * @deprecated since 1.17 use the ParserOptions object to get the relevant options
-        * @return String Page rendering hash
+        * @return string Page rendering hash
         */
        public function getPageRenderingHash() {
                wfDeprecated( __METHOD__, '1.17' );
@@ -3394,7 +3396,7 @@ class User {
 
        /**
         * Get whether the user is explicitly blocked from account creation.
-        * @return Bool|Block
+        * @return bool|Block
         */
        public function isBlockedFromCreateAccount() {
                $this->getBlockedStatus();
@@ -3415,7 +3417,7 @@ class User {
 
        /**
         * Get whether the user is blocked from using Special:Emailuser.
-        * @return Bool
+        * @return bool
         */
        public function isBlockedFromEmailuser() {
                $this->getBlockedStatus();
@@ -3424,7 +3426,7 @@ class User {
 
        /**
         * Get whether the user is allowed to create an account.
-        * @return Bool
+        * @return bool
         */
        function isAllowedToCreateAccount() {
                return $this->isAllowed( 'createaccount' ) && !$this->isBlockedFromCreateAccount();
@@ -3452,7 +3454,7 @@ class User {
        /**
         * Determine whether the user is a newbie. Newbies are either
         * anonymous IPs, or the most recently created accounts.
-        * @return Bool
+        * @return bool
         */
        public function isNewbie() {
                return !$this->isAllowed( 'autoconfirmed' );
@@ -3461,7 +3463,7 @@ class User {
        /**
         * Check to see if the given clear-text password is one of the accepted passwords
         * @param string $password user password.
-        * @return Boolean: True if the given password is correct, otherwise False.
+        * @return boolean: True if the given password is correct, otherwise False.
         */
        public function checkPassword( $password ) {
                global $wgAuth, $wgLegacyEncoding;
@@ -3479,17 +3481,17 @@ class User {
                if ( $wgAuth->authenticate( $this->getName(), $password ) ) {
                        return true;
                } elseif ( $wgAuth->strict() ) {
-                       /* Auth plugin doesn't allow local authentication */
+                       // Auth plugin doesn't allow local authentication
                        return false;
                } elseif ( $wgAuth->strictUserAuth( $this->getName() ) ) {
-                       /* Auth plugin doesn't allow local authentication for this user name */
+                       // Auth plugin doesn't allow local authentication for this user name
                        return false;
                }
                if ( self::comparePasswords( $this->mPassword, $password, $this->mId ) ) {
                        return true;
                } elseif ( $wgLegacyEncoding ) {
-                       # Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
-                       # Check for this with iconv
+                       // Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
+                       // Check for this with iconv
                        $cp1252Password = iconv( 'UTF-8', 'WINDOWS-1252//TRANSLIT', $password );
                        if ( $cp1252Password != $password &&
                                self::comparePasswords( $this->mPassword, $cp1252Password, $this->mId ) )
@@ -3506,7 +3508,7 @@ class User {
         *
         * @param $plaintext string
         *
-        * @return Boolean: True if matches, false otherwise
+        * @return boolean: True if matches, false otherwise
         */
        public function checkTemporaryPassword( $plaintext ) {
                global $wgNewPasswordExpiry;
@@ -3529,7 +3531,7 @@ class User {
         *
         * @param string|array $salt of Strings Optional function-specific data for hashing
         * @param $request WebRequest object to use or null to use $wgRequest
-        * @return String The new edit token
+        * @return string The new edit token
         */
        public function editToken( $salt = '', $request = null ) {
                wfDeprecated( __METHOD__, '1.19' );
@@ -3546,7 +3548,7 @@ class User {
         *
         * @param string|array $salt of Strings Optional function-specific data for hashing
         * @param $request WebRequest object to use or null to use $wgRequest
-        * @return String The new edit token
+        * @return string The new edit token
         */
        public function getEditToken( $salt = '', $request = null ) {
                if ( $request == null ) {
@@ -3571,8 +3573,8 @@ class User {
        /**
         * Generate a looking random token for various uses.
         *
-        * @return String The new random token
-        * @deprecated since 1.20; Use MWCryptRand for secure purposes or wfRandomString for pseudo-randomness
+        * @return string The new random token
+        * @deprecated since 1.20: Use MWCryptRand for secure purposes or wfRandomString for pseudo-randomness
         */
        public static function generateToken() {
                return MWCryptRand::generateHex( 32 );
@@ -3586,8 +3588,8 @@ class User {
         *
         * @param string $val Input value to compare
         * @param string $salt Optional function-specific data for hashing
-        * @param $request WebRequest object to use or null to use $wgRequest
-        * @return Boolean: Whether the token matches
+        * @param WebRequest $request Object to use or null to use $wgRequest
+        * @return boolean: Whether the token matches
         */
        public function matchEditToken( $val, $salt = '', $request = null ) {
                $sessionToken = $this->getEditToken( $salt, $request );
@@ -3603,8 +3605,8 @@ class User {
         *
         * @param string $val Input value to compare
         * @param string $salt Optional function-specific data for hashing
-        * @param $request WebRequest object to use or null to use $wgRequest
-        * @return Boolean: Whether the token matches
+        * @param WebRequest $request object to use or null to use $wgRequest
+        * @return boolean: Whether the token matches
         */
        public function matchEditTokenNoSuffix( $val, $salt = '', $request = null ) {
                $sessionToken = $this->getEditToken( $salt, $request );
@@ -3675,7 +3677,7 @@ class User {
         * this change to the database.
         *
         * @param &$expiration \mixed Accepts the expiration time
-        * @return String New token
+        * @return string New token
         */
        protected function confirmationToken( &$expiration ) {
                global $wgUserEmailConfirmationTokenExpiry;
@@ -3693,7 +3695,7 @@ class User {
        /**
         * Return a URL the user can use to confirm their email address.
         * @param string $token Accepts the email confirmation token
-        * @return String New token URL
+        * @return string New token URL
         */
        protected function confirmationTokenUrl( $token ) {
                return $this->getTokenUrl( 'ConfirmEmail', $token );
@@ -3702,7 +3704,7 @@ class User {
        /**
         * Return a URL the user can use to invalidate their email address.
         * @param string $token Accepts the email confirmation token
-        * @return String New token URL
+        * @return string New token URL
         */
        protected function invalidationTokenUrl( $token ) {
                return $this->getTokenUrl( 'InvalidateEmail', $token );
@@ -3720,7 +3722,7 @@ class User {
         *
         * @param string $page Special page
         * @param string $token Token
-        * @return String Formatted URL
+        * @return string Formatted URL
         */
        protected function getTokenUrl( $page, $token ) {
                // Hack to bypass localization of 'Special:'
@@ -3774,7 +3776,7 @@ class User {
        /**
         * Is this user allowed to send e-mails within limits of current
         * site configuration?
-        * @return Bool
+        * @return bool
         */
        public function canSendEmail() {
                global $wgEnableEmail, $wgEnableUserEmail;
@@ -3789,7 +3791,7 @@ class User {
        /**
         * Is this user allowed to receive e-mails within limits of current
         * site configuration?
-        * @return Bool
+        * @return bool
         */
        public function canReceiveEmail() {
                return $this->isEmailConfirmed() && !$this->getOption( 'disablemail' );
@@ -3803,7 +3805,7 @@ class User {
         * confirmed their address by returning a code or using a password
         * sent to the address from the wiki.
         *
-        * @return Bool
+        * @return bool
         */
        public function isEmailConfirmed() {
                global $wgEmailAuthentication;
@@ -3827,7 +3829,7 @@ class User {
 
        /**
         * Check whether there is an outstanding request for e-mail confirmation.
-        * @return Bool
+        * @return bool
         */
        public function isEmailConfirmationPending() {
                global $wgEmailAuthentication;
@@ -3840,9 +3842,9 @@ class User {
        /**
         * Get the timestamp of account creation.
         *
-        * @return String|Bool|Null Timestamp of account creation, false for
-        *     non-existent/anonymous user accounts, or null if existing account
-        *     but information is not in database.
+        * @return string|bool|null Timestamp of account creation, false for
+        *  non-existent/anonymous user accounts, or null if existing account
+        *  but information is not in database.
         */
        public function getRegistration() {
                if ( $this->isAnon() ) {
@@ -3855,8 +3857,8 @@ class User {
        /**
         * Get the timestamp of the first edit
         *
-        * @return String|Bool Timestamp of first edit, or false for
-        *     non-existent/anonymous user accounts.
+        * @return string|bool Timestamp of first edit, or false for
+        *  non-existent/anonymous user accounts.
         */
        public function getFirstEditTimestamp() {
                if ( $this->getId() == 0 ) {
@@ -3936,7 +3938,7 @@ class User {
         * Get the localized descriptive name for a group, if it exists
         *
         * @param string $group Internal group name
-        * @return String Localized descriptive group name
+        * @return string Localized descriptive group name
         */
        public static function getGroupName( $group ) {
                $msg = wfMessage( "group-$group" );
@@ -3948,7 +3950,7 @@ class User {
         *
         * @param string $group Internal group name
         * @param string $username Username for gender (since 1.19)
-        * @return String Localized name for group member
+        * @return string Localized name for group member
         */
        public static function getGroupMember( $group, $username = '#' ) {
                $msg = wfMessage( "group-$group-member", $username );
@@ -4001,7 +4003,7 @@ class User {
         * Get the title of a page describing a particular group
         *
         * @param string $group Internal group name
-        * @return Title|Bool Title of the page if it exists, false otherwise
+        * @return Title|bool Title of the page if it exists, false otherwise
         */
        public static function getGroupPage( $group ) {
                $msg = wfMessage( 'grouppage-' . $group )->inContentLanguage();
@@ -4020,7 +4022,7 @@ class User {
         *
         * @param string $group Internal name of the group
         * @param string $text The text of the link
-        * @return String HTML link to the group
+        * @return string HTML link to the group
         */
        public static function makeGroupLinkHTML( $group, $text = '' ) {
                if ( $text == '' ) {
@@ -4040,7 +4042,7 @@ class User {
         *
         * @param string $group Internal name of the group
         * @param string $text The text of the link
-        * @return String Wikilink to the group
+        * @return string Wikilink to the group
         */
        public static function makeGroupLinkWiki( $group, $text = '' ) {
                if ( $text == '' ) {
@@ -4203,7 +4205,7 @@ class User {
         * Initialize user_editcount from data out of the revision table
         *
         * @param $add Integer Edits to add to the count from the revision table
-        * @return Integer Number of edits
+        * @return integer Number of edits
         */
        protected function initEditCount( $add = 0 ) {
                // Pull from a slave to be less cruel to servers
@@ -4232,7 +4234,7 @@ class User {
         * Get the description of a given right
         *
         * @param string $right Right to query
-        * @return String Localized description of the right
+        * @return string Localized description of the right
         */
        public static function getRightDescription( $right ) {
                $key = "right-$right";
@@ -4245,7 +4247,7 @@ class User {
         *
         * @param string $password Plain-text password
         * @param string $userId User ID
-        * @return String Password hash
+        * @return string Password hash
         */
        public static function oldCrypt( $password, $userId ) {
                global $wgPasswordSalt;
@@ -4261,9 +4263,8 @@ class User {
         *
         * @param string $password Plain-text password
         * @param bool|string $salt Optional salt, may be random or the user ID.
-
-        *                     If unspecified or false, will generate one automatically
-        * @return String Password hash
+        *  If unspecified or false, will generate one automatically
+        * @return string Password hash
         */
        public static function crypt( $password, $salt = false ) {
                global $wgPasswordSalt;
@@ -4291,7 +4292,7 @@ class User {
         * @param string $password Plain-text password to compare
         * @param string|bool $userId User ID for old-style password salt
         *
-        * @return Boolean
+        * @return boolean
         */
        public static function comparePasswords( $hash, $password, $userId = false ) {
                $type = substr( $hash, 0, 3 );
@@ -4302,14 +4303,14 @@ class User {
                }
 
                if ( $type == ':A:' ) {
-                       # Unsalted
+                       // Unsalted
                        return md5( $password ) === substr( $hash, 3 );
                } elseif ( $type == ':B:' ) {
-                       # Salted
+                       // Salted
                        list( $salt, $realHash ) = explode( ':', substr( $hash, 3 ), 2 );
                        return md5( $salt . '-' . md5( $password ) ) === $realHash;
                } else {
-                       # Old-style
+                       // Old-style
                        return self::oldCrypt( $password, $userId ) === $hash;
                }
        }
@@ -4468,7 +4469,7 @@ class User {
                $userId = $this->getId();
                $insert_rows = array();
                foreach ( $saveOptions as $key => $value ) {
-                       # Don't bother storing default values
+                       // Don't bother storing default values
                        $defaultOption = self::getDefaultOption( $key );
                        if ( ( is_null( $defaultOption ) &&
                                        !( $value === false || is_null( $value ) ) ) ||
index ead6d77..16658d3 100644 (file)
@@ -91,6 +91,11 @@ if ( $IP === false ) {
        }
 }
 
+# Load composer's autoloader if present
+if ( is_readable( "$IP/vendor/autoload.php" ) ) {
+       require_once "$IP/vendor/autoload.php";
+}
+
 # Get MWInit class
 require_once "$IP/includes/Init.php";
 
index 3f7e19c..cb0f60a 100644 (file)
@@ -624,27 +624,34 @@ class MediaWiki {
                        wfShellExec( "$cmd &", $retVal );
                        wfProfileOut( __METHOD__ . '-exec' );
                } else {
-                       // Fallback to running the jobs here while the user waits
-                       $group = JobQueueGroup::singleton();
-                       do {
-                               $job = $group->pop( JobQueueGroup::USE_CACHE ); // job from any queue
-                               if ( $job ) {
-                                       $output = $job->toString() . "\n";
-                                       $t = - microtime( true );
-                                       wfProfileIn( __METHOD__ . '-' . get_class( $job ) );
-                                       $success = $job->run();
-                                       wfProfileOut( __METHOD__ . '-' . get_class( $job ) );
-                                       $group->ack( $job ); // done
-                                       $t += microtime( true );
-                                       $t = round( $t * 1000 );
-                                       if ( $success === false ) {
-                                               $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n";
-                                       } else {
-                                               $output .= "Success, Time: $t ms\n";
+                       try {
+                               // Fallback to running the jobs here while the user waits
+                               $group = JobQueueGroup::singleton();
+                               do {
+                                       $job = $group->pop( JobQueueGroup::USE_CACHE ); // job from any queue
+                                       if ( $job ) {
+                                               $output = $job->toString() . "\n";
+                                               $t = - microtime( true );
+                                               wfProfileIn( __METHOD__ . '-' . get_class( $job ) );
+                                               $success = $job->run();
+                                               wfProfileOut( __METHOD__ . '-' . get_class( $job ) );
+                                               $group->ack( $job ); // done
+                                               $t += microtime( true );
+                                               $t = round( $t * 1000 );
+                                               if ( $success === false ) {
+                                                       $output .= "Error: " . $job->getLastError() . ", Time: $t ms\n";
+                                               } else {
+                                                       $output .= "Success, Time: $t ms\n";
+                                               }
+                                               wfDebugLog( 'jobqueue', $output );
                                        }
-                                       wfDebugLog( 'jobqueue', $output );
-                               }
-                       } while ( --$n && $job );
+                               } while ( --$n && $job );
+                       } catch ( MWException $e ) {
+                               // We don't want exceptions thrown during job execution to
+                               // be reported to the user since the output is already sent.
+                               // Instead we just log them.
+                               wfDebugLog( 'exception', $e->getLogMessage() );
+                       }
                }
        }
 }
index a3cb4b5..5ed6c8d 100644 (file)
@@ -3041,30 +3041,8 @@ class WikiPage implements Page, IDBAccessObject {
                        function() use ( $dbw, $that, $method, $added, $deleted ) {
                                $ns = $that->getTitle()->getNamespace();
 
-                               // First make sure the rows exist.  If one of the "deleted" ones didn't
-                               // exist, we might legitimately not create it, but it's simpler to just
-                               // create it and then give it a negative value, since the value is bogus
-                               // anyway.
-                               //
-                               // Sometimes I wish we had INSERT ... ON DUPLICATE KEY UPDATE.
-                               $insertCats = array_merge( $added, $deleted );
-                               if ( !$insertCats ) {
-                                       // Okay, nothing to do
-                                       return;
-                               }
-
-                               $insertRows = array();
-                               foreach ( $insertCats as $cat ) {
-                                       $insertRows[] = array(
-                                               'cat_id' => $dbw->nextSequenceValue( 'category_cat_id_seq' ),
-                                               'cat_title' => $cat
-                                       );
-                               }
-                               $dbw->insert( 'category', $insertRows, $method, 'IGNORE' );
-
                                $addFields = array( 'cat_pages = cat_pages + 1' );
                                $removeFields = array( 'cat_pages = cat_pages - 1' );
-
                                if ( $ns == NS_CATEGORY ) {
                                        $addFields[] = 'cat_subcats = cat_subcats + 1';
                                        $removeFields[] = 'cat_subcats = cat_subcats - 1';
@@ -3073,16 +3051,26 @@ class WikiPage implements Page, IDBAccessObject {
                                        $removeFields[] = 'cat_files = cat_files - 1';
                                }
 
-                               if ( $added ) {
-                                       $dbw->update(
+                               if ( count( $added ) ) {
+                                       $insertRows = array();
+                                       foreach ( $added as $cat ) {
+                                               $insertRows[] = array(
+                                                       'cat_title'   => $cat,
+                                                       'cat_pages'   => 1,
+                                                       'cat_subcats' => ( $ns == NS_CATEGORY ) ? 1 : 0,
+                                                       'cat_files'   => ( $ns == NS_FILE ) ? 1 : 0,
+                                               );
+                                       }
+                                       $dbw->upsert(
                                                'category',
+                                               $insertRows,
+                                               array( 'cat_title' ),
                                                $addFields,
-                                               array( 'cat_title' => $added ),
                                                $method
                                        );
                                }
 
-                               if ( $deleted ) {
+                               if ( count( $deleted ) ) {
                                        $dbw->update(
                                                'category',
                                                $removeFields,
@@ -3095,6 +3083,7 @@ class WikiPage implements Page, IDBAccessObject {
                                        $cat = Category::newFromName( $catName );
                                        wfRunHooks( 'CategoryAfterPageAdded', array( $cat, $that ) );
                                }
+
                                foreach ( $deleted as $catName ) {
                                        $cat = Category::newFromName( $catName );
                                        wfRunHooks( 'CategoryAfterPageRemoved', array( $cat, $that ) );
index 25dde7d..92ca7d8 100644 (file)
@@ -40,16 +40,52 @@ class XmlTypeCheck {
        public $rootElement = '';
 
        /**
-        * @param string $file filename
-        * @param $filterCallback callable (optional)
+        * @param string $input a filename or string containing the XML element
+        * @param callable $filterCallback (optional)
         *        Function to call to do additional custom validity checks from the
         *        SAX element handler event. This gives you access to the element
         *        namespace, name, and attributes, but not to text contents.
         *        Filter should return 'true' to toggle on $this->filterMatch
+        * @param boolean $isFile (optional) indicates if the first parameter is a
+        *        filename (default, true) or if it is a string (false)
         */
-       function __construct( $file, $filterCallback = null ) {
+       function __construct( $input, $filterCallback = null, $isFile = true ) {
                $this->filterCallback = $filterCallback;
-               $this->run( $file );
+               if ( $isFile ) {
+                       $this->validateFromFile( $input );
+               } else {
+                       $this->validateFromString( $input );
+               }
+       }
+
+       /**
+        * Alternative constructor: from filename
+        *
+        * @param string $fname the filename of an XML document
+        * @param callable $filterCallback (optional)
+        *        Function to call to do additional custom validity checks from the
+        *        SAX element handler event. This gives you access to the element
+        *        namespace, name, and attributes, but not to text contents.
+        *        Filter should return 'true' to toggle on $this->filterMatch
+        * @return XmlTypeCheck
+        */
+       public static function newFromFilename( $fname, $filterCallback = null ) {
+               return new self( $fname, $filterCallback, true );
+       }
+
+       /**
+        * Alternative constructor: from string
+        *
+        * @param string $string a string containing an XML element
+        * @param callable $filterCallback (optional)
+        *        Function to call to do additional custom validity checks from the
+        *        SAX element handler event. This gives you access to the element
+        *        namespace, name, and attributes, but not to text contents.
+        *        Filter should return 'true' to toggle on $this->filterMatch
+        * @return XmlTypeCheck
+        */
+       public static function newFromString( $string, $filterCallback = null ) {
+               return new self( $string, $filterCallback, false );
        }
 
        /**
@@ -62,15 +98,23 @@ class XmlTypeCheck {
        }
 
        /**
-        * @param $fname
+        * Get an XML parser with the root element handler.
+        * @see XmlTypeCheck::rootElementOpen()
+        * @return resource a resource handle for the XML parser
         */
-       private function run( $fname ) {
+       private function getParser() {
                $parser = xml_parser_create_ns( 'UTF-8' );
-
                // case folding violates XML standard, turn it off
                xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
-
                xml_set_element_handler( $parser, array( $this, 'rootElementOpen' ), false );
+               return $parser;
+       }
+
+       /**
+        * @param string $fname the filename
+        */
+       private function validateFromFile( $fname ) {
+               $parser = $this->getParser();
 
                if ( file_exists( $fname ) ) {
                        $file = fopen( $fname, "rb" );
@@ -79,7 +123,7 @@ class XmlTypeCheck {
                                        $chunk = fread( $file, 32768 );
                                        $ret = xml_parse( $parser, $chunk, feof( $file ) );
                                        if ( $ret == 0 ) {
-                                               // XML isn't well-formed!
+                                               $this->wellFormed = false;
                                                fclose( $file );
                                                xml_parser_free( $parser );
                                                return;
@@ -89,12 +133,26 @@ class XmlTypeCheck {
                                fclose( $file );
                        }
                }
-
                $this->wellFormed = true;
 
                xml_parser_free( $parser );
        }
 
+       /**
+        *
+        * @param string $string the XML-input-string to be checked.
+        */
+       private function validateFromString( $string ) {
+               $parser = $this->getParser();
+               $ret = xml_parse( $parser, $string, true );
+               xml_parser_free( $parser );
+               if ( $ret == 0 ) {
+                       $this->wellFormed = false;
+                       return;
+               }
+               $this->wellFormed = true;
+       }
+
        /**
         * @param $parser
         * @param $name
index 7cd1f72..e3ea6fc 100644 (file)
@@ -66,8 +66,8 @@ class InfoAction extends FormlessAction {
                // Clear page info.
                $revision = WikiPage::factory( $title )->getRevision();
                if ( $revision !== null ) {
-                       $memcKey = wfMemcKey( 'infoaction', $title->getPrefixedText(), $revision->getId() );
-                       $wgMemc->delete( $memcKey );
+                       $key = wfMemcKey( 'infoaction', sha1( $title->getPrefixedText() ), $revision->getId() );
+                       $wgMemc->delete( $key );
                }
        }
 
@@ -185,14 +185,16 @@ class InfoAction extends FormlessAction {
         * @return array
         */
        protected function pageInfo() {
-               global $wgContLang, $wgRCMaxAge, $wgMemc, $wgUnwatchedPageThreshold, $wgPageInfoTransclusionLimit;
+               global $wgContLang, $wgRCMaxAge, $wgMemc,
+                       $wgUnwatchedPageThreshold, $wgPageInfoTransclusionLimit;
 
                $user = $this->getUser();
                $lang = $this->getLanguage();
                $title = $this->getTitle();
                $id = $title->getArticleID();
 
-               $memcKey = wfMemcKey( 'infoaction', sha1( $title->getPrefixedText() ), $this->page->getLatest() );
+               $memcKey = wfMemcKey( 'infoaction',
+                       sha1( $title->getPrefixedText() ), $this->page->getLatest() );
                $pageCounts = $wgMemc->get( $memcKey );
                if ( $pageCounts === false ) {
                        // Get page information that would be too "expensive" to retrieve by normal means
index bde1d99..800af25 100644 (file)
@@ -94,8 +94,7 @@ class ApiParse extends ApiBase {
                                $titleObj = $rev->getTitle();
                                $wgTitle = $titleObj;
                                $pageObj = WikiPage::factory( $titleObj );
-                               $popts = $pageObj->makeParserOptions( $this->getContext() );
-                               $popts->enableLimitReport( !$params['disablepp'] );
+                               $popts = $this->makeParserOptions( $pageObj, $params );
 
                                // If for some reason the "oldid" is actually the current revision, it may be cached
                                if ( $rev->isCurrent() ) {
@@ -152,8 +151,7 @@ class ApiParse extends ApiBase {
                                        $oldid = $pageObj->getLatest();
                                }
 
-                               $popts = $pageObj->makeParserOptions( $this->getContext() );
-                               $popts->enableLimitReport( !$params['disablepp'] );
+                               $popts = $this->makeParserOptions( $pageObj, $params );
 
                                // Potentially cached
                                $p_result = $this->getParsedContent( $pageObj, $popts, $pageid,
@@ -170,8 +168,7 @@ class ApiParse extends ApiBase {
                        $wgTitle = $titleObj;
                        $pageObj = WikiPage::factory( $titleObj );
 
-                       $popts = $pageObj->makeParserOptions( $this->getContext() );
-                       $popts->enableLimitReport( !$params['disablepp'] );
+                       $popts = $this->makeParserOptions( $pageObj, $params );
 
                        if ( is_null( $text ) ) {
                                $this->dieUsage( 'The text parameter should be passed with the title parameter. Should you be using the "page" parameter instead?', 'params' );
@@ -359,6 +356,26 @@ class ApiParse extends ApiBase {
                }
        }
 
+       /**
+        * Constructs a ParserOptions object
+        *
+        * @param WikiPage $pageObj
+        * @param array $params
+        *
+        * @return ParserOptions
+        */
+       protected function makeParserOptions( WikiPage $pageObj, array $params ) {
+               wfProfileIn( __METHOD__ );
+
+               $popts = $pageObj->makeParserOptions( $this->getContext() );
+               $popts->enableLimitReport( !$params['disablepp'] );
+               $popts->setIsPreview( $params['preview'] );
+               $popts->setIsSectionPreview( $params['preview'] && $this->section !== false );
+
+               wfProfileOut( __METHOD__ );
+               return $popts;
+       }
+
        /**
         * @param $page WikiPage
         * @param $popts ParserOptions
@@ -594,6 +611,7 @@ class ApiParse extends ApiBase {
                        'section' => null,
                        'disablepp' => false,
                        'generatexml' => false,
+                       'preview' => false,
                        'contentformat' => array(
                                ApiBase::PARAM_TYPE => ContentHandler::getAllContentFormats(),
                        ),
@@ -649,6 +667,7 @@ class ApiParse extends ApiBase {
                        'section' => 'Only retrieve the content of this section number',
                        'disablepp' => 'Disable the PP Report from the parser output',
                        'generatexml' => 'Generate XML parse tree (requires prop=wikitext)',
+                       'preview' => 'Parse in preview mode',
                        'contentformat' => 'Content serialization format used for the input text',
                        'contentmodel' => 'Content model of the new content',
                );
index 137efb8..8c6e24a 100644 (file)
@@ -441,4 +441,57 @@ abstract class AbstractContent implements Content {
                wfRunHooks( 'ConvertContent', array( $this, $toModel, $lossy, &$result ) );
                return $result;
        }
+
+       /**
+        * Returns a ParserOutput object, filled by a call to fillParserOutput().
+        * Subclasses that want to control the parser output may override this, or
+        * they can override fillParserOutput(). If they override getParserOutput(),
+        * itself, they should take care to call the ContentGetParserOutput hook.
+        *
+        * @param $title Title Context title for parsing
+        * @param $revId int|null Revision ID (for {{REVISIONID}})
+        * @param $options ParserOptions|null Parser options
+        * @param $generateHtml bool Whether or not to generate HTML
+        *
+        * @return ParserOutput representing the HTML form of the text
+        */
+       public function getParserOutput( Title $title, $revId = null,
+               ParserOptions $options = null, $generateHtml = true
+       ) {
+               # Generic implementation, relying on $this->getHtml()
+
+               if ( $options === null ) {
+                       $options = $this->getContentHandler()->makeParserOptions( 'canonical' );
+               }
+
+               $po = new ParserOutput();
+
+               if ( wfRunHooks( 'ContentGetParserOutput',
+                       array( $this, $title, $revId, $options, $generateHtml, &$po ) ) ) {
+
+                       $this->fillParserOutput( $title, $revId, $options, $generateHtml, $po );
+               }
+
+               return $po;
+       }
+
+       /**
+        * Fills the provided ParserOutput object with the HTML returned by getHtml().
+        * Subclasses should override this (or getParserOutput) appropriately.
+        * This placeholder implementation always throws an exception.
+        *
+        * @param $title        Title Context title for parsing
+        * @param $revId        int|null Revision ID (for {{REVISIONID}})
+        * @param $options      ParserOptions|null Parser options
+        * @param $generateHtml bool Whether or not to generate HTML
+        * @param &$output      ParserOutput The output object to fill (reference).
+        *
+        * @throws MWException
+        */
+       protected function fillParserOutput( Title $title, $revId,
+               ParserOptions $options, $generateHtml, ParserOutput &$output
+       ) {
+               // Don't make abstract, so subclasses that override getParserOutput() directly don't fail.
+               throw new MWException( 'Subclasses of AbstractContent must override fillParserOutput!' );
+       }
 }
index 03cc2d0..a25bd76 100644 (file)
@@ -57,7 +57,7 @@ class CssContent extends TextContent {
        protected function getHtml() {
                $html = "";
                $html .= "<pre class=\"mw-code mw-css\" dir=\"ltr\">\n";
-               $html .= $this->getHighlightHtml();
+               $html .= htmlspecialchars( $this->getNativeData() );
                $html .= "\n</pre>\n";
 
                return $html;
index 2ae572b..247de6d 100644 (file)
@@ -58,7 +58,7 @@ class JavaScriptContent extends TextContent {
        protected function getHtml() {
                $html = "";
                $html .= "<pre class=\"mw-code mw-js\" dir=\"ltr\">\n";
-               $html .= $this->getHighlightHtml();
+               $html .= htmlspecialchars( $this->getNativeData() );
                $html .= "\n</pre>\n";
 
                return $html;
index f66dacd..a6ee7b0 100644 (file)
@@ -189,32 +189,27 @@ class TextContent extends AbstractContent {
        }
 
        /**
-        * Returns a generic ParserOutput object, wrapping the HTML returned by
-        * getHtml().
+        * Fills the provided ParserOutput object with the HTML returned by getHtml().
+        *
+        * Content models in $wgTextModelsToParse will be parsed as wikitext to process links,
+        * magic words, etc.
+        *
+        * Subclasses may override this to provide custom rendering.
         *
         * @param $title Title Context title for parsing
         * @param int|null $revId Revision ID (for {{REVISIONID}})
         * @param $options ParserOptions|null Parser options
         * @param bool $generateHtml Whether or not to generate HTML
-        *
-        * @return ParserOutput representing the HTML form of the text
+        * @param $output ParserOutput The output object to fill (reference).
         */
-       public function getParserOutput( Title $title,
-               $revId = null,
-               ParserOptions $options = null, $generateHtml = true
+       protected function fillParserOutput( Title $title, $revId,
+               ParserOptions $options, $generateHtml, ParserOutput &$output
        ) {
                global $wgParser, $wgTextModelsToParse;
 
-               if ( !$options ) {
-                       //NOTE: use canonical options per default to produce cacheable output
-                       $options = $this->getContentHandler()->makeParserOptions( 'canonical' );
-               }
-
                if ( in_array( $this->getModel(), $wgTextModelsToParse ) ) {
-                       // parse just to get links etc into the database
-                       $po = $wgParser->parse( $this->getNativeData(), $title, $options, true, true, $revId );
-               } else {
-                       $po = new ParserOutput();
+                       // parse just to get links etc into the database, HTML is replaced below.
+                       $output = $wgParser->parse( $this->getNativeData(), $title, $options, true, true, $revId );
                }
 
                if ( $generateHtml ) {
@@ -223,34 +218,25 @@ class TextContent extends AbstractContent {
                        $html = '';
                }
 
-               $po->setText( $html );
-               return $po;
+               $output->setText( $html );
        }
 
        /**
         * Generates an HTML version of the content, for display. Used by
         * getParserOutput() to construct a ParserOutput object.
         *
-        * This default implementation just calls getHighlightHtml(). Content
-        * models that have another mapping to HTML (as is the case for markup
-        * languages like wikitext) should override this method to generate the
-        * appropriate HTML.
+        * This default implementation runs the text returned by $this->getNativeData()
+        * through htmlspecialchars and tried to convert line breaks and indentation to HTML..
         *
         * @return string An HTML representation of the content
         */
-       protected function getHtml() {
-               return $this->getHighlightHtml();
-       }
-
-       /**
-        * Generates a syntax-highlighted version of the content, as HTML.
-        * Used by the default implementation of getHtml().
-        *
-        * @return string an HTML representation of the content's markup
-        */
-       protected function getHighlightHtml() {
-               # TODO: make Highlighter interface, use highlighter here, if available
-               return htmlspecialchars( $this->getNativeData() );
+       public static function convertWhiteSpaceToHTML( $msg ) {
+               $msg = htmlspecialchars( $msg );
+               $msg = preg_replace( '/^ /m', '&#160;', $msg );
+               $msg = preg_replace( '/ $/m', '&#160;', $msg );
+               $msg = preg_replace( '/  /', '&#160; ', $msg );
+               $msg = str_replace( "\n", '<br />', $msg );
+               return $msg;
        }
 
        /**
index 26337db..d821d9d 100644 (file)
@@ -277,28 +277,21 @@ class WikitextContent extends TextContent {
         * using $wgParser.
         *
         * @since    1.21
+        * @see AbstractContent::fillParserOutput().
         *
         * @param $title Title
         * @param int $revId Revision to pass to the parser (default: null)
         * @param $options ParserOptions (default: null)
         * @param bool $generateHtml (default: false)
-        *
-        * @internal param \IContextSource|null $context
-        * @return ParserOutput representing the HTML form of the text
+        * @param &$output ParserOutput representing the HTML form of the text,
+        *           may be manipulated or replaced.
         */
-       public function getParserOutput( Title $title,
-               $revId = null,
-               ParserOptions $options = null, $generateHtml = true
+       protected function fillParserOutput( Title $title, $revId,
+                       ParserOptions $options, $generateHtml, ParserOutput &$output
        ) {
                global $wgParser;
 
-               if ( !$options ) {
-                       //NOTE: use canonical options per default to produce cacheable output
-                       $options = $this->getContentHandler()->makeParserOptions( 'canonical' );
-               }
-
-               $po = $wgParser->parse( $this->getNativeData(), $title, $options, true, true, $revId );
-               return $po;
+               $output = $wgParser->parse( $this->getNativeData(), $title, $options, true, true, $revId );
        }
 
        protected function getHtml() {
index 799e168..4b2eae7 100644 (file)
@@ -2529,6 +2529,92 @@ abstract class DatabaseBase implements DatabaseType {
                return $this->query( $sql, $fname );
        }
 
+       /**
+        * INSERT ON DUPLICATE KEY UPDATE wrapper, upserts an array into a table.
+        *
+        * This updates any conflicting rows (according to the unique indexes) using
+        * the provided SET clause and inserts any remaining (non-conflicted) rows.
+        *
+        * $rows may be either:
+        *   - A single associative array. The array keys are the field names, and
+        *     the values are the values to insert. The values are treated as data
+        *     and will be quoted appropriately. If NULL is inserted, this will be
+        *     converted to a database NULL.
+        *   - An array with numeric keys, holding a list of associative arrays.
+        *     This causes a multi-row INSERT on DBMSs that support it. The keys in
+        *     each subarray must be identical to each other, and in the same order.
+        *
+        * It may be more efficient to leave off unique indexes which are unlikely
+        * to collide. However if you do this, you run the risk of encountering
+        * errors which wouldn't have occurred in MySQL.
+        *
+        * Usually throws a DBQueryError on failure. If errors are explicitly ignored,
+        * returns success.
+        *
+        * @param string $table Table name. This will be passed through DatabaseBase::tableName().
+        * @param array $rows A single row or list of rows to insert
+        * @param array $uniqueIndexes List of single field names or field name tuples
+        * @param array $set An array of values to SET. For each array element,
+        *                the key gives the field name, and the value gives the data
+        *                to set that field to. The data will be quoted by
+        *                DatabaseBase::addQuotes().
+        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
+        * @param array $options of options
+        *
+        * @return bool
+        * @since 1.22
+        */
+       public function upsert(
+               $table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__
+       ) {
+               if ( !count( $rows ) ) {
+                       return true; // nothing to do
+               }
+               $rows = is_array( reset( $rows ) ) ? $rows : array( $rows );
+
+               if ( count( $uniqueIndexes ) ) {
+                       $clauses = array(); // list WHERE clauses that each identify a single row
+                       foreach ( $rows as $row ) {
+                               foreach ( $uniqueIndexes as $index ) {
+                                       $index = is_array( $index ) ? $index : array( $index ); // columns
+                                       $rowKey = array(); // unique key to this row
+                                       foreach ( $index as $column ) {
+                                               $rowKey[$column] = $row[$column];
+                                       }
+                                       $clauses[] = $this->makeList( $rowKey, LIST_AND );
+                               }
+                       }
+                       $where = array( $this->makeList( $clauses, LIST_OR ) );
+               } else {
+                       $where = false;
+               }
+
+               $useTrx = !$this->mTrxLevel;
+               if ( $useTrx ) {
+                       $this->begin( $fname );
+               }
+               try {
+                       # Update any existing conflicting row(s)
+                       if ( $where !== false ) {
+                               $ok = $this->update( $table, $set, $where, $fname );
+                       } else {
+                               $ok = true;
+                       }
+                       # Now insert any non-conflicting row(s)
+                       $ok = $this->insert( $table, $rows, $fname, array( 'IGNORE' ) ) && $ok;
+               } catch ( Exception $e ) {
+                       if ( $useTrx ) {
+                               $this->rollback( $fname );
+                       }
+                       throw $e;
+               }
+               if ( $useTrx ) {
+                       $this->commit( $fname );
+               }
+
+               return $ok;
+       }
+
        /**
         * DELETE where the condition is a join.
         *
index ca5a2b4..3eb8c44 100644 (file)
@@ -352,6 +352,37 @@ class DatabaseMysql extends DatabaseBase {
                return $this->nativeReplace( $table, $rows, $fname );
        }
 
+       /**
+        * @param string $table
+        * @param array $rows
+        * @param array $uniqueIndexes
+        * @param array $set
+        * @param string $fname
+        * @param array $options
+        * @return bool
+        */
+       public function upsert(
+               $table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__
+       ) {
+               if ( !count( $rows ) ) {
+                       return true; // nothing to do
+               }
+               $rows = is_array( reset( $rows ) ) ? $rows : array( $rows );
+
+               $table = $this->tableName( $table );
+               $columns = array_keys( $rows[0] );
+
+               $sql = "INSERT INTO $table (" . implode( ',', $columns ) . ') VALUES ';
+               $rowTuples = array();
+               foreach ( $rows as $row ) {
+                       $rowTuples[] = '(' . $this->makeList( $row ) . ')';
+               }
+               $sql .= implode( ',', $rowTuples );
+               $sql .= " ON DUPLICATE KEY UPDATE " . $this->makeList( $set, LIST_SET );
+
+               return (bool)$this->query( $sql, $fname );
+       }
+
        /**
         * Estimate rows in dataset
         * Returns estimated count, based on EXPLAIN output
index f9e2ce4..54a90d3 100644 (file)
@@ -1111,7 +1111,11 @@ class SwiftFileBackend extends FileBackendStore {
                $names = array();
                $storageDir = rtrim( $params['dir'], '/' );
                $suffixStart = ( $dir === '' ) ? 0 : strlen( $dir ) + 1; // size of "path/to/dir/"
-               foreach ( $cfObjects as $object ) {
+               // Iterate over the list *backwards* as this primes the stat cache, which is LRU.
+               // If this fills the cache and the caller stats an uncached file before stating
+               // the ones on the listing, there would be zero cache hits if this went forwards.
+               for ( end( $cfObjects ); key( $cfObjects ) !== null; prev( $cfObjects ) ) {
+                       $object = current( $cfObjects );
                        $path = "{$storageDir}/" . substr( $object->name, $suffixStart );
                        $val = array(
                                // Convert dates like "Tue, 03 Jan 2012 22:01:04 GMT" to TS_MW
@@ -1122,7 +1126,7 @@ class SwiftFileBackend extends FileBackendStore {
                        $this->cheapCache->set( $path, 'stat', $val );
                        $names[] = $object->name;
                }
-               return $names;
+               return array_reverse( $names ); // keep the paths in original order
        }
 
        /**
index 9c778c7..f0e54ec 100644 (file)
@@ -56,7 +56,7 @@ abstract class LockManager {
        protected $domain; // string; domain (usually wiki ID)
        protected $lockTTL; // integer; maximum time locks can be held
 
-       /* Lock types; stronger locks have higher values */
+       /** Lock types; stronger locks have higher values */
        const LOCK_SH = 1; // shared lock (for reads)
        const LOCK_UW = 2; // shared lock (for reads used to write elsewhere)
        const LOCK_EX = 3; // exclusive lock (for writes)
@@ -92,11 +92,25 @@ abstract class LockManager {
         * @return Status
         */
        final public function lock( array $paths, $type = self::LOCK_EX, $timeout = 0 ) {
+               return $this->lockByType( array( $type => $paths ), $timeout );
+       }
+
+       /**
+        * Lock the resources at the given abstract paths
+        *
+        * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+        * @param integer $timeout Timeout in seconds (0 means non-blocking) (since 1.21)
+        * @return Status
+        * @since 1.22
+        */
+       final public function lockByType( array $pathsByType, $timeout = 0 ) {
                wfProfileIn( __METHOD__ );
+               $status = Status::newGood();
+               $pathsByType = $this->normalizePathsByType( $pathsByType );
                $msleep = array( 0, 50, 100, 300, 500 ); // retry backoff times
                $start = microtime( true );
                do {
-                       $status = $this->doLock( array_unique( $paths ), $this->lockTypeMap[$type] );
+                       $status = $this->doLockByType( $pathsByType );
                        $elapsed = microtime( true ) - $start;
                        if ( $status->isOK() || $elapsed >= $timeout || $elapsed < 0 ) {
                                break; // success, timeout, or clock set back
@@ -116,8 +130,20 @@ abstract class LockManager {
         * @return Status
         */
        final public function unlock( array $paths, $type = self::LOCK_EX ) {
+               return $this->unlockByType( array( $type => $paths ) );
+       }
+
+       /**
+        * Unlock the resources at the given abstract paths
+        *
+        * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+        * @return Status
+        * @since 1.22
+        */
+       final public function unlockByType( array $pathsByType ) {
                wfProfileIn( __METHOD__ );
-               $status = $this->doUnlock( array_unique( $paths ), $this->lockTypeMap[$type] );
+               $pathsByType = $this->normalizePathsByType( $pathsByType );
+               $status = $this->doUnlockByType( $pathsByType );
                wfProfileOut( __METHOD__ );
                return $status;
        }
@@ -146,6 +172,46 @@ abstract class LockManager {
                return sha1( "{$this->domain}:{$path}" );
        }
 
+       /**
+        * Normalize the $paths array by converting LOCK_UW locks into the
+        * appropriate type and removing any duplicated paths for each lock type.
+        *
+        * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+        * @return Array
+        * @since 1.22
+        */
+       final protected function normalizePathsByType( array $pathsByType ) {
+               $res = array();
+               foreach ( $pathsByType as $type => $paths ) {
+                       $res[$this->lockTypeMap[$type]] = array_unique( $paths );
+               }
+               return $res;
+       }
+
+       /**
+        * @see LockManager::lockByType()
+        * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+        * @return Status
+        * @since 1.22
+        */
+       protected function doLockByType( array $pathsByType ) {
+               $status = Status::newGood();
+               $lockedByType = array(); // map of (type => paths)
+               foreach ( $pathsByType as $type => $paths ) {
+                       $status->merge( $this->doLock( $paths, $type ) );
+                       if ( $status->isOK() ) {
+                               $lockedByType[$type] = $paths;
+                       } else {
+                               // Release the subset of locks that were acquired
+                               foreach ( $lockedByType as $type => $paths ) {
+                                       $status->merge( $this->doUnlock( $paths, $type ) );
+                               }
+                               break;
+                       }
+               }
+               return $status;
+       }
+
        /**
         * Lock resources with the given keys and lock type
         *
@@ -155,6 +221,20 @@ abstract class LockManager {
         */
        abstract protected function doLock( array $paths, $type );
 
+       /**
+        * @see LockManager::unlockByType()
+        * @param array $paths Map of LockManager::LOCK_* constants to lists of storage paths
+        * @return Status
+        * @since 1.22
+        */
+       protected function doUnlockByType( array $pathsByType ) {
+               $status = Status::newGood();
+               foreach ( $pathsByType as $type => $paths ) {
+                       $status->merge( $this->doUnlock( $paths, $type ) );
+               }
+               return $status;
+       }
+
        /**
         * Unlock resources with the given keys and lock type
         *
index edcb1d6..5faad4a 100644 (file)
@@ -36,24 +36,18 @@ class ScopedLock {
        protected $manager;
        /** @var Status */
        protected $status;
-       /** @var Array List of resource paths*/
-       protected $paths;
-
-       protected $type; // integer lock type
+       /** @var Array Map of lock types to resource paths */
+       protected $pathsByType;
 
        /**
-        * @param $manager LockManager
-        * @param array $paths List of storage paths
-        * @param $type integer LockManager::LOCK_* constant
-        * @param $status Status
+        * @param LockManager $manager
+        * @param array $pathsByType Map of lock types to path lists
+        * @param Status $status
         */
-       protected function __construct(
-               LockManager $manager, array $paths, $type, Status $status
-       ) {
+       protected function __construct( LockManager $manager, array $pathsByType, Status $status ) {
                $this->manager = $manager;
-               $this->paths = $paths;
+               $this->pathsByType = $pathsByType;
                $this->status = $status;
-               $this->type = $type;
        }
 
        /**
@@ -61,19 +55,24 @@ class ScopedLock {
         * Any locks are released once this object goes out of scope.
         * The status object is updated with any errors or warnings.
         *
-        * @param $manager LockManager
-        * @param array $paths List of storage paths
-        * @param $type integer LockManager::LOCK_* constant
-        * @param $status Status
+        * $type can be "mixed" and $paths can be a map of types to paths (since 1.22).
+        * Otherwise $type should be an integer and $paths should be a list of paths.
+        *
+        * @param LockManager $manager
+        * @param array $paths List of storage paths or map of lock types to path lists
+        * @param integer|string $type LockManager::LOCK_* constant or "mixed"
+        * @param Status $status
+        * @param integer $timeout Timeout in seconds (0 means non-blocking) (since 1.22)
         * @return ScopedLock|null Returns null on failure
         */
        public static function factory(
-               LockManager $manager, array $paths, $type, Status $status
+               LockManager $manager, array $paths, $type, Status $status, $timeout = 0
        ) {
-               $lockStatus = $manager->lock( $paths, $type );
+               $pathsByType = is_integer( $type ) ? array( $type => $paths ) : $paths;
+               $lockStatus = $manager->lockByType( $pathsByType, $timeout );
                $status->merge( $lockStatus );
                if ( $lockStatus->isOK() ) {
-                       return new self( $manager, $paths, $type, $status );
+                       return new self( $manager, $pathsByType, $status );
                }
                return null;
        }
@@ -91,9 +90,12 @@ class ScopedLock {
                $lock = null;
        }
 
+       /**
+        * Release the locks when this goes out of scope
+        */
        function __destruct() {
                $wasOk = $this->status->isOK();
-               $this->status->merge( $this->manager->unlock( $this->paths, $this->type ) );
+               $this->status->merge( $this->manager->unlockByType( $this->pathsByType ) );
                if ( $wasOk ) {
                        // Make sure status is OK, despite any unlockFiles() fatals
                        $this->status->setResult( true, $this->status->value );
index 3cb1f41..b21b87a 100644 (file)
@@ -1372,11 +1372,17 @@ class LocalFile extends File {
                # Invalidate cache for all pages using this file
                $update = new HTMLCacheUpdate( $this->getTitle(), 'imagelinks' );
                $update->doUpdate();
+               if ( !$reupload ) {
+                       LinksUpdate::queueRecursiveJobsForTable( $this->getTitle(), 'imagelinks' );
+               }
 
                # Invalidate cache for all pages that redirects on this page
                $redirs = $this->getTitle()->getRedirectsHere();
 
                foreach ( $redirs as $redir ) {
+                       if ( !$reupload && $redir->getNamespace() === NS_FILE ) {
+                               LinksUpdate::queueRecursiveJobsForTable( $redir, 'imagelinks' );
+                       }
                        $update = new HTMLCacheUpdate( $redir, 'imagelinks' );
                        $update->doUpdate();
                }
index fb1e9a4..ac33448 100644 (file)
@@ -113,8 +113,8 @@ class CSSJanus {
                $patterns['rtl_in_url'] = "/{$patterns['lookbehind_not_letter']}(rtl){$patterns['lookahead_for_closing_paren']}/i";
                $patterns['cursor_east'] = "/{$patterns['lookbehind_not_letter']}([ns]?)e-resize/";
                $patterns['cursor_west'] = "/{$patterns['lookbehind_not_letter']}([ns]?)w-resize/";
-               $patterns['four_notation_quantity'] = "/{$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}/i";
-               $patterns['four_notation_color'] = "/(-color\s*:\s*){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}/i";
+               $patterns['four_notation_quantity'] = "/(:\s*){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s+){$patterns['possibly_negative_quantity']}(\s*[;}])/i";
+               $patterns['four_notation_color'] = "/(-color\s*:\s*){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}(\s+){$patterns['color']}(\s*[;}])/i";
                // The two regexes below are parenthesized differently then in the original implementation to make the
                // callback's job more straightforward
                $patterns['bg_horizontal_percentage'] = "/(background(?:-position)?\s*:\s*[^%]*?)(-?{$patterns['num']})(%\s*(?:{$patterns['quantity']}|{$patterns['ident']}))/";
@@ -257,8 +257,8 @@ class CSSJanus {
         * @return string
         */
        private static function fixFourPartNotation( $css ) {
-               $css = preg_replace( self::$patterns['four_notation_quantity'], '$1$2$7$4$5$6$3', $css );
-               $css = preg_replace( self::$patterns['four_notation_color'], '$1$2$3$8$5$6$7$4', $css );
+               $css = preg_replace( self::$patterns['four_notation_quantity'], '$1$2$3$8$5$6$7$4$9', $css );
+               $css = preg_replace( self::$patterns['four_notation_color'], '$1$2$3$8$5$6$7$4$9', $css );
 
                return $css;
        }
index b368906..fd576e0 100644 (file)
@@ -228,6 +228,10 @@ abstract class MediaHandler {
 
        /**
         * Get the thumbnail extension and MIME type for a given source MIME type
+        *
+        * @param String $ext Extension of original file
+        * @param String $mime Mime type of original file
+        * @param Array $params Handler specific rendering parameters
         * @return array thumbnail extension and MIME type
         */
        function getThumbType( $ext, $mime, $params = null ) {
index 1d197b1..8632399 100644 (file)
@@ -32,7 +32,7 @@ abstract class MediaTransformOutput {
         */
        var $file;
 
-       var $width, $height, $url, $page, $path;
+       var $width, $height, $url, $page, $path, $lang;
 
        /**
         * @var array Associative array mapping optional supplementary image files
@@ -197,17 +197,26 @@ abstract class MediaTransformOutput {
 
        /**
         * @param $title string
-        * @param $params array
+        * @param $params string|array Query parameters to add
         * @return array
         */
-       public function getDescLinkAttribs( $title = null, $params = '' ) {
-               $query = '';
+       public function getDescLinkAttribs( $title = null, $params = array() ) {
+               if ( is_array( $params ) ) {
+                       $query = $params;
+               } else {
+                       $query = array();
+               }
                if ( $this->page && $this->page !== 1 ) {
-                       $query = 'page=' . urlencode( $this->page );
+                       $query['page'] = $this->page;
                }
-               if ( $params ) {
-                       $query .= $query ? '&' . $params : $params;
+               if( $this->lang ) {
+                       $query['lang'] = $this->lang;
                }
+
+               if ( is_string( $params ) && $params !== '' ) {
+                       $query = $params . '&' . wfArrayToCgi( $query );
+               }
+
                $attribs = array(
                        'href' => $this->file->getTitle()->getLocalURL( $query ),
                        'class' => 'image',
@@ -242,10 +251,12 @@ class ThumbnailImage extends MediaTransformOutput {
                # Previous parameters:
                #   $file, $url, $width, $height, $path = false, $page = false
 
+               $defaults = array(
+                       'page' => false,
+                       'lang' => false
+               );
+
                if ( is_array( $parameters ) ) {
-                       $defaults = array(
-                               'page' => false
-                       );
                        $actualParams = $parameters + $defaults;
                } else {
                        # Using old format, should convert. Later a warning could be added here.
@@ -254,7 +265,7 @@ class ThumbnailImage extends MediaTransformOutput {
                                'width' => $path,
                                'height' => $parameters,
                                'page' => ( $numArgs > 5 ) ? func_get_arg( 5 ) : false
-                       );
+                       ) + $defaults;
                        $path = ( $numArgs > 4 ) ? func_get_arg( 4 ) : false;
                }
 
@@ -269,6 +280,7 @@ class ThumbnailImage extends MediaTransformOutput {
                $this->height = round( $actualParams['height'] );
 
                $this->page = $actualParams['page'];
+               $this->lang = $actualParams['lang'];
        }
 
        /**
index a133f6f..f0356f5 100644 (file)
@@ -115,6 +115,7 @@ class SvgHandler extends ImageHandler {
                $clientHeight = $params['height'];
                $physicalWidth = $params['physicalWidth'];
                $physicalHeight = $params['physicalHeight'];
+               $lang = isset( $params['lang'] ) ? $params['lang'] : 'en';
 
                if ( $flags & self::TRANSFORM_LATER ) {
                        return new ThumbnailImage( $image, $dstUrl, $dstPath, $params );
@@ -132,7 +133,7 @@ class SvgHandler extends ImageHandler {
                }
 
                $srcPath = $image->getLocalRefPath();
-               $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight );
+               $status = $this->rasterize( $srcPath, $dstPath, $physicalWidth, $physicalHeight, $lang );
                if ( $status === true ) {
                        return new ThumbnailImage( $image, $dstUrl, $dstPath, $params );
                } else {
@@ -147,10 +148,11 @@ class SvgHandler extends ImageHandler {
         * @param string $dstPath
         * @param string $width
         * @param string $height
+        * @param string $lang Language code of the language to render the SVG in
         * @throws MWException
         * @return bool|MediaTransformError
         */
-       public function rasterize( $srcPath, $dstPath, $width, $height ) {
+       public function rasterize( $srcPath, $dstPath, $width, $height, $lang = false ) {
                global $wgSVGConverters, $wgSVGConverter, $wgSVGConverterPath;
                $err = false;
                $retval = '';
@@ -158,7 +160,7 @@ class SvgHandler extends ImageHandler {
                        if ( is_array( $wgSVGConverters[$wgSVGConverter] ) ) {
                                // This is a PHP callable
                                $func = $wgSVGConverters[$wgSVGConverter][0];
-                               $args = array_merge( array( $srcPath, $dstPath, $width, $height ),
+                               $args = array_merge( array( $srcPath, $dstPath, $width, $height, $lang ),
                                        array_slice( $wgSVGConverters[$wgSVGConverter], 1 ) );
                                if ( !is_callable( $func ) ) {
                                        throw new MWException( "$func is not callable" );
@@ -176,9 +178,15 @@ class SvgHandler extends ImageHandler {
                                                wfEscapeShellArg( $dstPath ) ),
                                        $wgSVGConverters[$wgSVGConverter]
                                ) . " 2>&1";
+
+                               $env = array();
+                               if( $lang !== false ) {
+                                       $env['LANG'] = $lang;
+                               }
+
                                wfProfileIn( 'rsvg' );
                                wfDebug( __METHOD__ . ": $cmd\n" );
-                               $err = wfShellExec( $cmd, $retval );
+                               $err = wfShellExec( $cmd, $retval, $env );
                                wfProfileOut( 'rsvg' );
                        }
                }
@@ -357,4 +365,68 @@ class SvgHandler extends ImageHandler {
                }
                return $result;
        }
+
+
+       /**
+        * @param string $name Parameter name
+        * @param $string $value Parameter value
+        * @return bool Validity
+        */
+       function validateParam( $name, $value ) {
+               if ( in_array( $name, array( 'width', 'height' ) ) ) {
+                       // Reject negative heights, widths
+                       return ( $value > 0 );
+               } elseif( $name == 'lang' ) {
+                       // Validate $code
+                       if( !Language::isValidBuiltinCode( $value ) ) {
+                               wfDebug( "Invalid user language code\n" );
+                               return false;
+                       }
+                       return true;
+               }
+               // Only lang, width and height are acceptable keys
+               return false;
+       }
+
+       /**
+        * @param array $params name=>value pairs of parameters
+        * @return string Filename to use
+        */
+       function makeParamString( $params ) {
+               $lang = '';
+               if( isset( $params['lang'] ) && $params['lang'] !== 'en' ) {
+                       $params['lang'] = mb_strtolower( $params['lang'] );
+                       $lang = "lang{$params['lang']}-";
+               }
+               if ( !isset( $params['width'] ) ) {
+                       return false;
+               }
+               return "$lang{$params['width']}px";
+       }
+
+       function parseParamString( $str ) {
+               $m = false;
+               if ( preg_match( '/^lang([a-z]+(?:-[a-z]+)*)-(\d+)px$/', $str, $m ) ) {
+                       return array( 'width' => array_pop( $m ), 'lang' => $m[1] );
+               } elseif( preg_match( '/^(\d+)px$/', $str, $m ) ) {
+                       return array( 'width' => $m[1], 'lang' => 'en' );
+               } else {
+                       return false;
+               }
+       }
+
+       function getParamMap() {
+               return array( 'img_lang' => 'lang', 'img_width' => 'width' );
+       }
+
+       /**
+        * @param $params
+        * @return array
+        */
+       function getScriptParams( $params ) {
+               return array(
+                       'width' => $params['width'],
+                       'lang' => $params['lang'],
+               );
+       }
 }
index 993642e..0f718d2 100644 (file)
@@ -48,7 +48,7 @@ grains of salt.
 
 There's an experimental PHP extension module which wraps the ICU library's
 normalization functions. This is *MUCH* faster than doing this work in pure
-PHP code. This is at https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/extensions/normal.git;a=summary.
+PHP code. This is at https://git.wikimedia.org/summary/mediawiki%2Fextensions%2Fnormal.git.
 It is used by the WMF, which currently runs PHP 5.3.10 on Linux.  It hasn't been
 thoroughly tested on other configurations, but may work.
 
index 840e174..28cde7f 100644 (file)
@@ -3323,7 +3323,7 @@ class Parser {
                if ( !$found && $title ) {
                        if ( !Profiler::instance()->isPersistent() ) {
                                # Too many unique items can kill profiling DBs/collectors
-                               $titleProfileIn = __METHOD__ . "-title-" . $title->getDBkey();
+                               $titleProfileIn = __METHOD__ . "-title-" . $title->getPrefixedDBkey();
                                wfProfileIn( $titleProfileIn ); // template in
                        }
                        wfProfileIn( __METHOD__ . '-loadtpl' );
index 2f90d3c..9258881 100644 (file)
@@ -121,8 +121,8 @@ class UsercreateTemplateVForm extends BaseTemplate {
                        $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
                }
        ?>
-               <div>
-                       <label><?php $this->msg( 'yourdomainname' ); ?></label>
+               <div id="mw-user-domain-section">
+                       <label for="wpDomain"><?php $this->msg( 'yourdomainname' ); ?></label>
                        <div class="mw-input">
                                <select name="wpDomain" value="<?php $this->text( 'domain' ); ?>"
                                        tabindex="4">
index 4f44209..5b2504e 100644 (file)
@@ -130,7 +130,7 @@ class UserloginTemplateVForm extends BaseTemplate {
                }
        ?>
                <div id="mw-user-domain-section">
-                       <label for='wpDomain' class="pos-above"><?php $this->msg( 'yourdomain' ); ?></label>
+                       <label for='wpDomain'><?php $this->msg( 'yourdomainname' ); ?></label>
                                <select name="wpDomain" value="<?php $this->text( 'domain' ); ?>"
                                        tabindex="3">
                                        <?php echo $doms ?>
index 840f1f8..14e12f3 100644 (file)
@@ -752,7 +752,7 @@ $1',
 'youhavenewmessagesmanyusers' => 'لك $1 من مستخدمين كثُر ($2).',
 'newmessageslinkplural' => '{{PLURAL:$1|رسالة جديدة|رسائل جديدة}}',
 'newmessagesdifflinkplural' => 'أحدث {{PLURAL:$1|تغيير|تغييرات}}',
-'youhavenewmessagesmulti' => 'لك رسائل جديدة في $1',
+'youhavenewmessagesmulti' => 'لديك رسائل جديدة في $1',
 'editsection' => 'عدّل',
 'editold' => 'تعديل',
 'viewsourceold' => 'استعرض المصدر',
index 98f5810..28619a6 100644 (file)
@@ -492,7 +492,6 @@ $1',
 # Special:PasswordReset
 'passwordreset' => 'ܣܘܡ ܡܠܬܐ ܕܥܠܠܐ ܙܒܢ ܐܚܪܝܢ',
 'passwordreset-legend' => 'ܣܘܡ ܡܠܬܐ ܕܥܠܠܐ ܙܒܢ ܐܚܪܝܢ',
-'passwordreset-pretext' => '{{PLURAL:$1||ܐܥܠ ܚܕ ܡܢ ܡܢܘܬ̈ܐ ܕܝܕ̈ܥܬܐ ܠܬܬܚܬ}}',
 'passwordreset-username' => 'ܫܡܐ ܕܡܦܠܚܢܐ:',
 'passwordreset-domain' => 'ܪܘܚܬܐ:',
 'passwordreset-email' => 'ܡܘܢܥܐ ܕܒܝܠܕܪܐ ܐܠܩܛܪܘܢܝܐ:',
@@ -536,8 +535,8 @@ $1',
 'accmailtitle' => 'ܡܠܬܐ ܕܥܠܠܐ ܫܕܪܬ',
 'newarticle' => '(ܚܕܬܐ)',
 'newarticletext' => "ܐܬܬ ܒܬܪ ܐܣܪܐ ܕܦܐܬܐ ܕܠܐ ܐܬܬܣܝܡܬ ܥܕܡܫ.
-ܠܣܘܝܡܐ ܕܦܐܬܐ ܗܕܐ, ܫܪܝ ܠܟܬܒܬܐ ܒܣܢܕܘܩܐ ܠܬܚܬ (ܚܙܝ [[{{MediaWiki:Helppage}}|ܦܐܬܐ ܕܥܘܕܪܢܐ]] ܠܐܚܪܢܐ ܝܕ̈ܥܬܐ).
-ܐܢ ܐܬܬ ܠܗܪܟܐ ܦܘܕܐܝܬ, ܕܘܫ ܠܦܪܡܝܬܐ ܕ '''ܠܒܣܬܪ back''' ܒܡܦܐܬܢܐ ܕܝܠܟ.",
+ܠܣܘܝܡܐ ܕܦܐܬܐ ܗܕܐ، ܫܪܝ ܠܟܬܒܬܐ ܒܣܢܕܘܩܐ ܠܬܚܬ (ܚܙܝ [[{{MediaWiki:Helppage}}|ܦܐܬܐ ܕܥܘܕܪܢܐ]] ܠܐܚܪܢܐ ܝܕ̈ܥܬܐ).
+ܐܢ ܐܬܬ ܠܗܪܟܐ ܦܘܕܐܝܬ، ܟܒܘܫ ܥܠ ܦܪܡܝܬܐ ܕ '''ܠܒܣܬܪ back''' ܒܡܦܐܬܢܐ ܕܝܠܟ.",
 'updated' => '(ܐܬܚܕܬ)',
 'note' => "'''ܡܥܝܪܢܘܬܐ:'''",
 'previewnote' => "'''ܕܟܪ ܕܗܢܘ ܚܝܪܐ ܩܕܡܝܐ ܒܠܚܘܕ'''.
index 35b7d3f..d540b12 100644 (file)
@@ -645,11 +645,11 @@ Seique yá camudaras correutamente la contraseña o que pidieras una nueva contr
 
 # Special:PasswordReset
 'passwordreset' => 'Reaniciar contraseña',
-'passwordreset-text' => 'Complete esti formulariu pa reaniciar la contraseña.',
+'passwordreset-text-one' => 'Complete esti formulariu pa reaniciar la contraseña.',
+'passwordreset-text-many' => '{{PLURAL:$1|Escriba una de les partes de los datos pa reaniciar la contraseña.}}',
 'passwordreset-legend' => 'Reaniciar contraseña',
 'passwordreset-disabled' => 'Los reanicios de contraseña tán desactivaos nesta wiki.',
 'passwordreset-emaildisabled' => 'Les funciones de corréu electrónicu tan desactivaes nesta wiki.',
-'passwordreset-pretext' => "{{PLURAL:$1||Escribi unu de los elementos de los datos d'abaxo}}",
 'passwordreset-username' => "Nome d'usuariu:",
 'passwordreset-domain' => 'Dominiu:',
 'passwordreset-capture' => '¿Ver el corréu electrónicu resultante?',
@@ -895,6 +895,7 @@ Nun dio esplicación.',
 Paez que se desanició.',
 'edit-conflict' => "Conflictu d'edición.",
 'edit-no-change' => "S'inoró la to edición, porque nun se fizo nengún cambéu nel testu.",
+'postedit-confirmation' => 'Guardose la edición.',
 'edit-already-exists' => 'Nun pudo crease una páxina nueva.
 Esta yá esiste.',
 'defaultmessagetext' => 'Testu predetermináu',
index a11c16e..1ba82be 100644 (file)
@@ -2214,7 +2214,7 @@ $1',
 'sp-contributions-newbies' => 'Паказваць толькі ўклады з новых рахункаў',
 'sp-contributions-newbies-sub' => 'З новых рахункаў',
 'sp-contributions-newbies-title' => 'Уклады ўдзельнікаў з новых рахункаў',
-'sp-contributions-blocklog' => 'Ð\96Ñ\83Ñ\80нал Ð·Ð°Ð±Ð°Ñ\80онаÑ\9e',
+'sp-contributions-blocklog' => 'блакÑ\96Ñ\80оÑ\9eкÑ\96',
 'sp-contributions-deleted' => 'сцёрты ўклад удзельніка',
 'sp-contributions-uploads' => 'Загрузкі',
 'sp-contributions-logs' => 'журналы',
index 1a836ac..ad817a5 100644 (file)
@@ -368,6 +368,18 @@ $messages = array(
 'oct' => 'кас',
 'nov' => 'ліс',
 'dec' => 'сьн',
+'january-date' => '$1 студзеня',
+'february-date' => '$1 лютага',
+'march-date' => '$1 сакавіка',
+'april-date' => '$1 красавіка',
+'may-date' => '$1 траўня',
+'june-date' => '$1 чэрвеня',
+'july-date' => '$1 ліпеня',
+'august-date' => '$1 жніўня',
+'september-date' => '$1 верасьня',
+'october-date' => '$1 кастрычніка',
+'november-date' => '$1 лістапада',
+'december-date' => '$1 сьнежня',
 
 # Categories related messages
 'pagecategories' => '{{PLURAL:$1|Катэгорыя|Катэгорыі|Катэгорыі}}',
@@ -819,6 +831,8 @@ $2',
 
 # Special:PasswordReset
 'passwordreset' => 'Ачыстка паролю',
+'passwordreset-text-one' => 'Запоўніце гэтую форму, каб скінуць пароль.',
+'passwordreset-text-many' => '{{PLURAL:$1|Увядзіце гэтыя зьвесткі, каб скінуць пароль.}}',
 'passwordreset-legend' => 'Ачысьціць пароль',
 'passwordreset-disabled' => 'Магчымасьць ачысткі паролю была забароненая ў {{GRAMMAR:месны|{{SITENAME}}}}.',
 'passwordreset-emaildisabled' => 'Функцыі e-mail у гэтай вікі былі адключаныя.',
@@ -1063,8 +1077,8 @@ $2
 'content-failed-to-parse' => 'Зьмест «$2» не адпавядае тыпу $1: $3.',
 'invalid-content-data' => 'Няслушныя зьвесткі',
 'content-not-allowed-here' => 'Зьмест тыпу «$1» на старонцы [[$2]] не дазволены',
-'editwarning-warning' => 'УÑ\81е Ð·Ñ\80обленÑ\8bÑ\8f Ð\92амÑ\96 Ð·Ñ\8cменÑ\8b Ð¼Ð¾Ð³Ñ\83Ñ\86Ñ\8c Ð±Ñ\8bÑ\86Ñ\8c Ñ\81Ñ\82Ñ\80аÑ\87анÑ\8bÑ\8f, ÐºÐ°Ð»Ñ\96 Ð\92Ñ\8b Ð¿Ð°ÐºÑ\96неÑ\86е Ð³Ñ\8dÑ\82Ñ\83Ñ\8e Ñ\81Ñ\82аÑ\80онкÑ\83.
\9aалÑ\96 Ð\92Ñ\8b Ñ\9eвайÑ\88лÑ\96 Ñ\9e Ñ\81Ñ\8bÑ\81Ñ\82Ñ\8dмÑ\83, Ð\92Ñ\8b Ð¼Ð¾Ð¶Ð°Ñ\86е Ð°Ð´ÐºÐ»Ñ\8eÑ\87Ñ\8bÑ\86Ñ\8c Ð³Ñ\8dÑ\82ае Ð¿Ð°Ð¿Ñ\8fÑ\80Ñ\8dджанÑ\8cне Ñ\83 Ñ\81Ñ\8dкÑ\86Ñ\8bÑ\96 Â«{{int:prefs-editing}}» Ð\92ашых наладаў.',
+'editwarning-warning' => 'Ð\9fакÑ\96нÑ\83Ñ\9eÑ\88Ñ\8b Ð³Ñ\8dÑ\82Ñ\83Ñ\8e Ñ\81Ñ\82аÑ\80онкÑ\83, Ð²Ñ\8b Ð¼Ð¾Ð¶Ð°Ñ\86е Ñ\81Ñ\82Ñ\80аÑ\86Ñ\96Ñ\86Ñ\8c Ñ\83Ñ\81е Ñ\9eнеÑ\81енÑ\8bÑ\8f Ð·Ñ\8cменÑ\8b.
\9aалÑ\96 Ð²Ñ\8b Ñ\9eвайÑ\88лÑ\96 Ñ\9e Ñ\81Ñ\8bÑ\81Ñ\82Ñ\8dмÑ\83, Ð\92Ñ\8b Ð¼Ð¾Ð¶Ð°Ñ\86е Ð°Ð´ÐºÐ»Ñ\8eÑ\87Ñ\8bÑ\86Ñ\8c Ð³Ñ\8dÑ\82ае Ð¿Ð°Ð¿Ñ\8fÑ\80Ñ\8dджанÑ\8cне Ñ\9e Ñ\81Ñ\8dкÑ\86Ñ\8bÑ\96 Â«Ð Ñ\8dдагаванÑ\8cне» Ð²ашых наладаў.',
 
 # Content models
 'content-model-wikitext' => 'вікі-тэкст',
@@ -2305,8 +2319,8 @@ $1',
 'notvisiblerev' => 'Вэрсія была выдаленая',
 'watchnochange' => 'Нічога з Вашага сьпісу назіраньня не зьмянілася за паказаны пэрыяд.',
 'watchlist-details' => 'У Вашым сьпісе назіраньня $1 {{PLURAL:$1|старонка|старонкі|старонак}} за выключэньнем старонак абмеркаваньня.',
-'wlheader-enotif' => 'Ð\94аÑ\81Ñ\8bлка Ð¿Ð°Ð²ÐµÐ´Ð°Ð¼Ð»ÐµÐ½Ñ\8cнÑ\8fÑ\9e Ð¿Ð° Ñ\8dлекÑ\82Ñ\80оннай Ð¿Ð¾Ñ\88Ñ\86е Ñ\9eклÑ\8eÑ\87анаÑ\8f.',
-'wlheader-showupdated' => "Старонкі, якія былі зьмененыя пасьля Вашага апошняга візыту, вылучаныя '''тоўстым''' шрыфтам",
+'wlheader-enotif' => 'Ð\90павÑ\8fÑ\88Ñ\87Ñ\8dнÑ\8cне Ð¿Ð° e-mail Ñ\83клÑ\8eÑ\87анае.',
+'wlheader-showupdated' => "Старонкі, зьмененыя з часу вашага апошняга візыту, вылучаныя '''тоўстым''' шрыфтам.",
 'watchmethod-recent' => 'прагляд апошніх зьменаў у старонках са сьпісу назіраньня',
 'watchmethod-list' => 'прагляд старонак са сьпісу назіраньня дзеля апошніх зьменах',
 'watchlistcontains' => 'Ваш сьпіс назіраньня зьмяшчае $1 {{PLURAL:$1|старонка|старонкі|старонак}}.',
index 3f15e3b..00f2f32 100644 (file)
@@ -695,7 +695,9 @@ Ne zaboravite da prilagodite sebi svoja [[Special:Preferences|{{SITENAME}} pode
 'yourpassword' => 'Šifra:',
 'userlogin-yourpassword' => 'Šifra',
 'userlogin-yourpassword-ph' => 'Unesite Vašu šifru/lozinku',
+'createacct-yourpassword-ph' => 'Unesite šifru/lozinku',
 'yourpasswordagain' => 'Ponovite šifru:',
+'createacct-yourpasswordagain-ph' => 'Unesite šifru/lozinku opet',
 'remembermypassword' => 'Zapamti moju šifru na ovom računaru (najviše $1 {{PLURAL:$1|dan|dana|dana}})',
 'userlogin-signwithsecure' => 'Koristite sigurnu konekciju',
 'securelogin-stick-https' => 'Ostani povezan na HTTPS nakon prijave',
@@ -719,8 +721,16 @@ Ne zaboravite da prilagodite sebi svoja [[Special:Preferences|{{SITENAME}} pode
 'gotaccountlink' => 'Prijavi se',
 'userlogin-resetlink' => 'Zaboravili ste detalje vaše prijave?',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Pomoć pri prijavljivanju]]',
-'createaccountmail' => 'e-poštom',
+'createacct-join' => 'Unesite svoje podatke ispod.',
+'createacct-emailrequired' => 'Adresa e-pošte',
+'createacct-emailoptional' => 'Adresa e-pošte (opcionalno)',
+'createaccountmail' => 'Koristite privremenu, slučajno stvorenu šifru/lozinku i pošaljite na dole navedenu adrеsu e-pošte',
+'createacct-realname' => 'Pravo ime (opcionalno)',
 'createaccountreason' => 'Razlog:',
+'createacct-reason' => 'Razlog',
+'createacct-captcha' => 'Sigurnosna provjera',
+'createacct-imgcaptcha-ph' => 'Unesite tekst koji vidite iznad',
+'createacct-submit' => 'Napravite svoj korisnički račun',
 'badretype' => 'Šifre koje ste unijeli se ne poklapaju.',
 'userexists' => 'Korisničko ime koje ste unijeli je već u upotrebi.
 Molimo Vas da izaberete drugo ime.',
@@ -821,16 +831,12 @@ Možda ste već uspješno promijenili Vašu šifru ili ste tražili novu privrem
 'passwordreset-capture-help' => 'Ako označite ovu kućicu, e-poruka (s privremenom lozinkom) će biti prikazana i poslata korisniku.',
 'passwordreset-email' => 'E-mail adresa:',
 'passwordreset-emailtitle' => 'Detalji računa na {{SITENAME}}',
-'passwordreset-emailtext-ip' => 'Neto (vjerovatno Vi, s IP adrese $1) je zatražio podsjetnik Vaših detalja računa
-za {{SITENAME}} ($4). Sljedeći {{PLURAL:$3|račun korisnika je|računi korisnika su}}
-povezani s ovom e-mail adresom:
+'passwordreset-emailtext-ip' => 'Neko (vjerovatno Vi, s IP adrese $1) je zatražio podsjetnik Vaših detalja računa za {{SITENAME}} ($4). Sljedeći {{PLURAL:$3|račun korisnika je|računi korisnika su}} povezani s ovom e-mail adresom:
 
 $2
 
 {{PLURAL:$3|Ova privremena šifra|Ove privremene šifre}} će isteći za {{PLURAL:$5|jedan dan|$5 dana}}.
-Trebate se prijaviti i odabrati novu šifru. Ako je neko drugi napravio ovaj
-zahtjev, ili ako ste se sjetili Vaše početne šifre, a ne želite je promijeniti, 
-možete zanemariti ovu poruku i nastaviti koristiti staru šifru.',
+Trebate se prijaviti i odabrati novu šifru. Ako je neko drugi napravio ovaj zahtjev, ili ako ste se sjetili Vaše početne šifre, a ne želite je promijeniti, možete zanemariti ovu poruku i nastaviti koristiti staru šifru.',
 'passwordreset-emailtext-user' => 'Korisnik $1 na {{SITENAME}} je zatražio podsjetnik o detaljima Vašeg računa za {{SITENAME}}
 ($4). Sljedeći {{PLURAL:$3|korisnički račun je|korisnički računi su}} povezani s ovom e-mail adresom:
 
index fe5bdf8..d6431ac 100644 (file)
@@ -444,7 +444,7 @@ $1",
 'disclaimerpage' => 'Project:Avís general',
 'edithelp' => 'Ajuda',
 'edithelppage' => "Help:Com s'edita una pàgina",
-'helppage' => 'Viquipèdia:Ajuda',
+'helppage' => 'Help:Índex',
 'mainpage' => 'Pàgina principal',
 'mainpage-description' => 'Pàgina principal',
 'policy-url' => 'Project:Polítiques',
@@ -622,7 +622,7 @@ No oblideu de canviar les vostres [[Special:Preferences|preferències de {{SITEN
 'createacct-yourpasswordagain' => 'Confirmeu la contrasenya',
 'createacct-yourpasswordagain-ph' => 'Introduïu de nou la contrasenya',
 'remembermypassword' => 'Recorda la contrasenya entre sessions (per un màxim de $1 {{PLURAL:$1|dia|dies}})',
-'userlogin-remembermypassword' => "Recorda'm",
+'userlogin-remembermypassword' => 'Mantén-me connectat',
 'userlogin-signwithsecure' => 'Connexió segura',
 'securelogin-stick-https' => "Roman connectat via HTTPS desprès d'autenticar-se",
 'yourdomainname' => 'El vostre domini',
@@ -660,9 +660,9 @@ No oblideu de canviar les vostres [[Special:Preferences|preferències de {{SITEN
 'createacct-imgcaptcha-ph' => 'Introduïu el text que apareix a dalt',
 'createacct-submit' => 'Crea el meu compte',
 'createacct-benefit-heading' => '{{SITENAME}} és feta per gent com tu.',
-'createacct-benefit-body1' => 'edicions',
-'createacct-benefit-body2' => 'pàgines',
-'createacct-benefit-body3' => 'col·laboradors aquest mes',
+'createacct-benefit-body1' => '{{PLURAL:$1|edició|edicions}}',
+'createacct-benefit-body2' => '{{PLURAL:$1|pàgina|pàgines}}',
+'createacct-benefit-body3' => '{{PLURAL:$1|col·laborador recent|col·laboradors recents}}',
 'badretype' => 'Les contrasenyes que heu introduït no coincideixen.',
 'userexists' => 'El nom que heu entrat ja és en ús.
 Escolliu-ne un de diferent.',
@@ -994,7 +994,7 @@ Ja existeix.",
 'invalid-content-data' => 'Dades de contingut no vàlides',
 'content-not-allowed-here' => 'No és permés el contingut "$1" a la pàgina [[$2]]',
 'editwarning-warning' => "Si sortiu d'aquesta pàgina perdreu tots els canvis que hàgiu fet.
-Si teniu un compte d'usuari, podeu eliminar aquest avís a la secció «{{int:prefs-editing}}» de les vostres preferències.",
+Si teniu un compte d'usuari, podeu eliminar aquest avís a la secció «Caixa d'edició» de les vostres preferències.",
 
 # Content models
 'content-model-wikitext' => 'wikitext',
@@ -2219,8 +2219,8 @@ S\'hi mostraran els canvis futurs que tinguin lloc en aquesta pàgina i la corre
 'notvisiblerev' => 'La versió ha estat esborrada',
 'watchnochange' => "No s'ha editat cap dels elements que vigileu en el període de temps que es mostra.",
 'watchlist-details' => 'Teniu $1 {{PLURAL:$1|pàgina vigilada|pàgines vigilades}}, sense comptar les pàgines de discussió.',
-'wlheader-enotif' => "S'ha habilitat la notificació per correu electrònic.",
-'wlheader-showupdated' => "Les pàgines que s'han canviat des de la vostra darrera visita es mostren '''en negreta'''",
+'wlheader-enotif' => 'La notificació per correu electrònic està habilitada.',
+'wlheader-showupdated' => "Les pàgines que s'han canviat des de la vostra darrera visita es mostren en '''negreta'''.",
 'watchmethod-recent' => "s'està comprovant si hi ha pàgines vigilades en les edicions recents",
 'watchmethod-list' => "s'està comprovant si hi ha edicions recents en les pàgines vigilades",
 'watchlistcontains' => 'La vostra llista de seguiment conté {{PLURAL:$1|una única pàgina|$1 pàgines}}.',
@@ -3858,7 +3858,7 @@ Amb aquest programa heu d'haver rebut [{{SERVER}}{{SCRIPTPATH}}/COPYING una còp
 'logentry-move-move-noredirect' => '$1 ha mogut $3 a $4 sense deixar una redirecció',
 'logentry-move-move_redir' => '$1 ha mogut $3 a $4 sobre una redirecció',
 'logentry-move-move_redir-noredirect' => '$1 {{GENDER:$2|ha desplaçat}} la pàgina $3 a $4 on hi havia una redirecció i sense crear una nova redirecció',
-'logentry-patrol-patrol' => '1$ {{GENDER:$2|ha marcat}} la revisió $4 de la pàgina «$3» com a patrullada',
+'logentry-patrol-patrol' => '$1 {{GENDER:$2|ha marcat}} la revisió $4 de la pàgina «$3» com a patrullada',
 'logentry-patrol-patrol-auto' => '$1 {{GENDER:$2|ha marcat}} automàticament la versió $4 de la pàgina $3 com a patrullada',
 'logentry-newusers-newusers' => "El compte d'usuari $1 {{GENDER:$2|ha estat creat}}",
 'logentry-newusers-create' => "El compte d'usuari $1 {{GENDER:$2|ha estat creat}}",
index 967f2f5..4fc6e46 100644 (file)
@@ -576,18 +576,18 @@ $1',
 'userlogin-remembermypassword' => 'Дагахь латт ве со',
 'yourdomainname' => 'Хьан машан меттиг:',
 'login' => 'Чу валар/ялар',
-'nav-login-createaccount' => 'Вовзийта хьой / дlавазло',
+'nav-login-createaccount' => 'Вовзийта/Йовзийта хьой / дӀавазло/дӀаязло',
 'loginprompt' => 'Ахьа бакъо йала йеза оцу «cookies» хьайна вовзийта лаахь гlирсан.',
 'userlogin' => 'Чу валар/ялар я декъашхон дӀаяздар кхоллар',
-'userloginnocreate' => 'Ð\92овзийÑ\82а Ñ\85Ñ\8cой',
+'userloginnocreate' => 'ЧÑ\83валаÑ\80\8fлаÑ\80',
 'logout' => 'Аравалар/ялар',
-'userlogout' => 'Ара валар',
+'userlogout' => 'Аравалар/ялар',
 'notloggedin' => 'Хьо вовзийта/йовзийта гlирсан',
 'nologin' => "Хlинца дlа вазвина/язйина вац/яц? '''$1'''.",
 'nologinlink' => 'Кхолла декъашхон дӀаяздар',
 'createaccount' => 'Кхолла декъашхон дӀаяздар',
 'gotaccount' => "Дlавазвелла вуй хьо? '''$1'''.",
-'gotaccountlink' => 'Ð\92овзийÑ\82а Ñ\85Ñ\8cой',
+'gotaccountlink' => 'ЧÑ\83валаÑ\80\8fлаÑ\80',
 'createaccountmail' => 'хааман зlене хула',
 'createaccountreason' => 'Бахьан:',
 'createacct-reason' => 'Бахьна',
@@ -665,10 +665,10 @@ $1',
 'newarticletext' => "Хьо веана хьажоригци хlокху агlон тlе, хlара агlо хlинца йоцаш ю.
 Нагахь иза кхолла лаахь, хlотта де лахо гуш долу корехь йоза (мадарра хьажа. [[{{MediaWiki:Helppage}}|гlон агlон чу]]).
 Нагахь гlалат даьлла нисвелляхь кхузе, атта тlе тlаlа йе '''юха йоккхуриг''' хьай гlирса тlяхь.",
-'anontalkpagetext' => "----''Хlара дийцаре агIо къайлаха волу декъашхочуьна  ю, хlинца дlавазвина воцуш, йа лелош воцуш.
-Цундела иза вовзийта лелош ду терахьца IP-долу метаг.
+'anontalkpagetext' => "----''Хlара дийцаре агIо къайлаха волу декъашхочуьна  ю, хlинца дӀавазвина/дӀаязйина воцуш/йоцуш, я лелош воцуш/йоцуш.
+Цундела иза вовзийта/йовзийта лелош ду терахьца IP-долу метаг.
 Иза терахь долу меттиг хила мега кхечу декъашхойчух терра.
-Нагахь хьо къайлах волу декъашхо валахь хьайна хаам кхаьчна аьлла хеташн, хьуна хьажийна доцуш, дехар до, кхолла хьай меттиг кху чохь[[Special:UserLogin/signup|дlавазло]] йа [[Special:UserLogin|хьой вовзийта]],",
+Нагахь хьо къайлах волу декъашхо валахь хьайна хаам кхаьчна аьлла хеташн, хьуна хьажийна доцуш, дехар до, кхолла хьай меттиг кху чохь[[Special:UserLogin/signup|дlавазло]] йа [[Special:UserLogin|хьой вовзийта/йовзийта]],",
 'noarticletext' => "Хlокх хан чохь кху агlонца йоза дац.
 Шуьга далундерг [[Special:Search/{{PAGENAME}}|лахар ишта хьехош йолу цlе]] кхечу яззамашкахь,
 <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} лахар кхечу тептаршкахь],
@@ -677,6 +677,10 @@ $1',
 Шуьга далундерг [[Special:Search/{{PAGENAME}}|лахар ишта хьехош йолу цӀе]] кхечу яззамашкахь,
 йа <span class="plainlinks">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} лаха оцуьнах терадерг кхечу тептаршкахь].</span>',
 'userpage-userdoesnotexist' => 'Ишта дlайазвар «<nowiki>$1</nowiki>» хlинца дац. Хьажа билгал, хьуна бакъалла лаьи кхолла йа хийцам ба хlокху агlон.',
+'sitecsspreview' => "'''ХӀара хьалх хьажар ду, хӀара CSS.'''
+'''хӀинца Ӏалашдина дац!'''",
+'sitejspreview' => "'''ХӀара хьалх хьажар ду, хӀара JavaScript-ишар.'''
+'''хӀинца Ӏалашдина дац!'''",
 'updated' => '(Карла йаькхина)',
 'note' => "'''Билгалдаккхар:'''",
 'previewnote' => "'''Хlара хьалх хьажар ду, йоза хlинца язданза ду!'''",
@@ -1742,7 +1746,7 @@ PICT # тайп тайпан
 * <strong class="mw-specialpagerestricted">Кlеззиг таронаш йолу леррина агlонаш.</strong>',
 'specialpages-group-maintenance' => 'Жамlаш гlирса хьашташ кхочушдар',
 'specialpages-group-other' => 'Кхин гlуллакхан агlонаш',
-'specialpages-group-login' => 'Хьой вовзийта / Дlавазло',
+'specialpages-group-login' => 'Хьой вовзийта/йовзийта / дӀавазло/дӀаязло',
 'specialpages-group-changes' => 'Керла нисдарш а тéптарш',
 'specialpages-group-media' => 'Жамlаш оцу медиа-гlирсашан а чуяхарш',
 'specialpages-group-users' => 'Декъашхой а бакъонаш',
index 082f59c..a7e2343 100644 (file)
@@ -677,6 +677,8 @@ Gall fod eich bod wedi llwyddo newid eich cyfrinair eisoes neu eich bod wedi gof
 
 # Special:PasswordReset
 'passwordreset' => 'Ailosod cyfrinair',
+'passwordreset-text-one' => 'Cwblhewch y ffurflen hon er mwyn ailosod eich cyfrinair.',
+'passwordreset-text-many' => "{{PLURAL:$1|Rhowch un o'r dewis o fanylion derbyniol er mwyn ailosod eich cyfrinair.}}",
 'passwordreset-legend' => 'Ailosod y cyfrinair',
 'passwordreset-disabled' => 'Analluogwyd ailosod cyfrineiriau ar y wici hwn.',
 'passwordreset-emaildisabled' => 'Analluogwyd offer e-bost ar y wici hwn.',
@@ -908,6 +910,7 @@ Ni roddodd eglurhad.',
 Ymddengys iddi gael ei dileu.",
 'edit-conflict' => 'Cyd-ddigwyddiad golygu.',
 'edit-no-change' => 'Anwybyddwyd eich golygiad, gan na newidiwyd y testun.',
+'postedit-confirmation' => 'Rhoddwyd eich golygiad ar gadw.',
 'edit-already-exists' => 'Ni ellid creu tudalen newydd.
 Mae ar gael yn barod.',
 'defaultmessagetext' => 'Y testun rhagosodedig',
index 5ccd8e8..eadb9ac 100644 (file)
@@ -759,11 +759,9 @@ Du har muligvis allerede skiftet din adgangskode eller anmodet om en ny midlerti
 
 # Special:PasswordReset
 'passwordreset' => 'Nulstil adgangskode',
-'passwordreset-text' => 'Udfyld denne formular for at nulstille din adgangskode.',
 'passwordreset-legend' => 'Nulstil adgangskode',
 'passwordreset-disabled' => 'Nulstilling af kodeord er slået fra på denne wiki.',
 'passwordreset-emaildisabled' => 'E-mailfunktioner er slået fra på denne wiki.',
-'passwordreset-pretext' => '{{PLURAL:$1||Indtast en af de nedenstående oplysninger}}',
 'passwordreset-username' => 'Brugernavn:',
 'passwordreset-domain' => 'Domæne:',
 'passwordreset-capture' => 'Se den resulterende email?',
index 6e0ecb5..c539dba 100644 (file)
@@ -274,6 +274,7 @@ $magicWords = array(
        'img_center'              => array( 1,    'center', 'centre' ),
        'img_framed'              => array( 1,    'framed', 'enframed', 'frame' ),
        'img_frameless'           => array( 1,    'frameless' ),
+       'img_lang'                => array( 1,    'lang=$1' ),
        'img_page'                => array( 1,    'page=$1', 'page $1' ),
        'img_upright'             => array( 1,    'upright', 'upright=$1', 'upright $1' ),
        'img_border'              => array( 1,    'border' ),
@@ -851,6 +852,7 @@ future releases. Also note that since each list value is wrapped in a unique
 'create-this-page'   => 'Create this page',
 'delete'             => 'Delete',
 'deletethispage'     => 'Delete this page',
+'undeletethispage'   => 'Undelete this page',
 'undelete_short'     => 'Undelete {{PLURAL:$1|one edit|$1 edits}}',
 'viewdeleted_short'  => 'View {{PLURAL:$1|one deleted edit|$1 deleted edits}}',
 'protect'            => 'Protect',
index b41d24a..ef80edf 100644 (file)
@@ -237,7 +237,7 @@ $messages = array(
 'views' => 'Uunsichten',
 'toolbox' => 'Werktjüügen',
 'userpage' => 'Brükersidj uunwise',
-'projectpage' => 'Prujäktsid wise',
+'projectpage' => 'Projektsidj wise',
 'imagepage' => 'Dateisidj uunwise',
 'mediawikipage' => 'Mädialangssidj uunwise',
 'templatepage' => 'Föörlaagensidj uunwise',
@@ -272,7 +272,7 @@ $1',
 'disclaimers' => 'Disclaimers',
 'disclaimerpage' => 'Project:Disclaimers',
 'edithelp' => "Halep bi't bewerkin",
-'edithelppage' => 'Help:Beårbe',
+'edithelppage' => 'Help:Bewerke',
 'helppage' => 'Help:Auersicht',
 'mainpage' => 'Hoodsidj',
 'mainpage-description' => 'Hoodsidj',
@@ -303,13 +303,13 @@ Luke efter bi't [[Special:Version|werjuunssidj]]",
 'newmessageslinkplural' => '{{PLURAL:$1|ian nei nooracht|nei noorachten}}',
 'newmessagesdifflinkplural' => 'leetst {{PLURAL:$1|feranrang|feranrangen}}',
 'youhavenewmessagesmulti' => 'Dü heest nei bööd üüb $1',
-'editsection' => 'Beårbe',
+'editsection' => 'Bewerke',
 'editsection-brackets' => '[$1]',
-'editold' => 'Beårbe',
+'editold' => 'Bewerke',
 'viewsourceold' => 'kweltekst uunwise',
 'editlink' => 'bewerke',
 'viewsourcelink' => 'kweltekst uunwise',
-'editsectionhint' => 'Säksjoon beårbe: $1',
+'editsectionhint' => 'Kirew bewerke: $1',
 'toc' => 'Inhåltsfertiiknis',
 'showtoc' => 'Wise',
 'hidetoc' => 'Ferbärje',
@@ -384,17 +384,17 @@ Wan dåt ai di fål as, hääst dü eewäntuäl en fäägel önj e software fün
 'internalerror_info' => 'Süsteemfeeler: $1',
 'fileappenderrorread' => '"$1" köö wilert dåt baitufäigen ai leesen wårde.',
 'fileappenderror' => 'Köö ai "$1" tu "$2" önjhinge.',
-'filecopyerror' => 'Jü dootäi "$1" köö ai eefter "$2" kopiiird wårde.',
-'filerenameerror' => 'Jü dootäi "$1" köö ai eefter "$2" amnååmd wårde.',
-'filedeleteerror' => 'Jü dootäi "$1" köö ai straagen wårde.',
+'filecopyerror' => 'Det datei $1 küd ei efter $2 kopiaret wurd.',
+'filerenameerror' => 'Det datei $1 küd ei efter $2 amnäämd wurd.',
+'filedeleteerror' => 'Det datei $1 küd ei stregen wurd.',
 'directorycreateerror' => 'Dåt fertiiknis "$1" köö ai mååged wårde.',
-'filenotfound' => 'Köö dootäi "$1" ai fine.',
-'fileexistserror' => 'Ai möölik, eefter dootäi "$1" tu schriwen: dootäi bestoont ål',
+'filenotfound' => 'Det datei $1 küd ei fünjen wurd.',
+'fileexistserror' => 'Uun det datei "$1" küd ei skrewen wurd: Hat as al diar.',
 'unexpected' => 'Ünfermousene wjart: "$1"="$2".',
 'formerror' => 'Fäägel: köö jü form ai lääwere',
 'badarticleerror' => 'Jüdeer aksjoon koon ai aw jüdeer sid mååged wårde.',
-'cannotdelete' => 'Jü sid unti dootäi "$1" köö ai straagen wårde.
-Dåt as flicht ål straagen foon huum ouders.',
+'cannotdelete' => 'Det sidj of datei "$1" küd ei stregen wurd.
+Det as ferlicht al faan hoker ööders stregen wurden.',
 'cannotdelete-title' => 'Sidj „$1“ koon ei stregen wurd.',
 'delete-hook-aborted' => 'Det striken as faan en software-feranerang faan MediaWiki ferhanert wurden. Di grünj as ei bekäänd.',
 'badtitle' => 'Ferkiard tiitel',
@@ -565,7 +565,7 @@ Fåls dåt benjüterkonto üt fersiinj önjläid wörd, koost dü jüdeer tising
 'login-throttled' => 'Dü heest tu oofding fersoocht, di önjtumälden.
 Wees sü gödj än täif, bit dü wider ferseechst.',
 'login-abort-generic' => 'Din önjmälding wus ei erfolchrik - Oufbräägen',
-'loginlanguagelabel' => 'Spräke: $1',
+'loginlanguagelabel' => 'Spriak: $1',
 'suspicious-userlogout' => 'Dan Oufmäldönjfrååge wörd ferwaigred, deer ja fermouslik foon en defäkte browser unti en cache-proxy sånd wörd.',
 
 # Email sending
@@ -710,7 +710,7 @@ För arke uunfraag wurd aal jo informatsjuunen boowen brükt.",
 Dåt koon ferschääwen unti wächhååld weese, wilt dü jü sid bekiikedest.',
 'loginreqtitle' => 'Dü skel di uunmelde.',
 'loginreqlink' => 'Uunmelde',
-'loginreqpagetext' => 'Dü möist $1 am oudere side tu bekiiken.',
+'loginreqpagetext' => 'Dü skel di $1, am ööder sidjen uuntulukin.',
 'accmailtitle' => 'Pååsuurd sånd.',
 'accmailtext' => "En tufäli generiird pååsuurd for [[User talk:$1|$1]] wörd tu $2 fersånd.
 
@@ -750,7 +750,9 @@ For informasjoon füliet di leeste üttooch üt dåt benjüterspär-logbök:',
 '''Dåt as nuch ai spiikerd!'''",
 'sitejspreview' => "'''Påås aw dåt dü jüdeer JavaScript code bloot forbekiikest.'''
 '''Dåt as nuch ai spiikerd!'''",
-'userinvalidcssjstitle' => "''Woorschauing:''' Skin \"\$1\"jeeft dåt ai. Betånk, dåt brükerspetsiifische .css- än .js-side ma en latj bökstääw önjfånge mönje, ålsü biispelswise ''{{ns:user}}:Münsterkjarl/vector.css'' önj stää foon ''{{ns:user}}:Münsterkjarl/Vector.css''.",
+'userinvalidcssjstitle' => "''Paase üüb:''' Skak \"\$1\" jaft at ei.
+Seenk diaram, dat faan en brüker iinracht .css- an .js-sidjen mä en letjen buksteew began skel. Bispal:
+''{{ns:user}}:Münsterkjarl/vector.css'' uunsteed faan ''{{ns:user}}:Münsterkjarl/Vector.css''.",
 'updated' => '(Änred)',
 'note' => "'''Påås aw:'''",
 'previewnote' => "'''Heer könst dü sä, hü det sidj wurd skal.'''
@@ -771,7 +773,7 @@ Wan't do imer noch ei loket, [[Special:UserLogout|melde di uf]] an weder uun.",
 Det komt flooksis föör, wan Dan anonym Proxy-siinst ei rocht werket.",
 'edit_form_incomplete' => "'''Enkelt dialen faan det formulaar san ei rocht uunkimen.'''
 Wees so gud an kontroliare ales noch ans.",
-'editing' => 'Beårbe foon $1',
+'editing' => 'Bewerkin faan $1',
 'creating' => 'Maage $1',
 'editingsection' => 'Beårben foon $1 (oufsnaas)',
 'editingcomment' => 'Beårben foon $1 (naien oufsnaas)',
@@ -781,8 +783,8 @@ Boowen könst dü di aktuel stant sä. Oner stun din fernanrangen.
 Bluas wat '''boowen''' stäänt, woort seekert. Diaram kopiare din feranrangen boowen iin.
 An do trak „{{int:savearticle}}“.",
 'yourtext' => 'Dan tekst',
-'storedversion' => 'Spiikerd färsjoon',
-'nonunicodebrowser' => "'''Påås aw:''' Dan browser koon unicode-tiikne ai rucht ferårbe. Brük hål en oudern browser am side tu ferårben.",
+'storedversion' => 'Seekert werjuun',
+'nonunicodebrowser' => "'''Paase üüb:''' Dan browser komt ei mä unicode-tiakens turocht. Wees so gud an brük en öödern browser.",
 'editingold' => "'''Paase üüb: Dü bewerkest en ual werjuun faan detdiar sidj.
 Wan dü det seekerst, wurd aal a nei werjuunen auerskrewen.'''",
 'yourdiff' => 'Ferskeeler',
@@ -808,14 +810,13 @@ Uun't logbuk stäänt muar diartu:",
 'titleprotectedwarning' => "'''Paase üüb: \"Detdiar sidj mä didiar nööm koon ei faan arken bewerket wurd. Bluas enkelt brükern mä [[Special:ListGroupRights|was brükerrochten]] kön detdiar sidj nei maage of bewerke.'''
 Uun't logbuk stäänt muar diartu:",
 'templatesused' => '{{PLURAL:$1|Jü füliend forlååg wårt|Da füliende forlååge wårde}} foon jüdeer sid ferwånd:',
-'templatesusedpreview' => '{{PLURAL:$1|Jü füliend forlååg wårt|Da füliende forlååge wårde}} foon diheere sideforlök ferwånd:',
+'templatesusedpreview' => '{{PLURAL:$1|Detdiar föörlaag woort|Jodiar föörlaagen wurd}} uun detdiar sidjenföörskau brükt:',
 'templatesusedsection' => '{{PLURAL:$1|Jü füliend forlååg wårt|Da füliende forlååge wårde}} foon dideer oufsnaas ferwånd:',
 'template-protected' => '(seekert)',
 'template-semiprotected' => '(hualew-seekert)',
 'hiddencategories' => 'Jüdeer sid as lasmoot foon {{PLURAL:$1|1 ferstäägen kategorii|$1 ferstäägene kategoriie}}:',
-'nocreatetext' => 'Aw {{SITENAME}} wörd dåt måågen foon naie side begränsed.
-Dü koost bestönjene side änre unti de [[Special:UserLogin|önjmälde unti mååg en account]].',
-'nocreate-loggedin' => 'Dü heest niinj beruchtiging, naie side tu måågen.',
+'nocreatetext' => 'Üüb {{SITENAME}} könst dü nian nei sidjen maage, oober dü könst sidjen feranre. [[Special:UserLogin|Melde di uun of racht en brükerkonto iin]].',
+'nocreate-loggedin' => 'Dü mutst nian nei sidjen maage.',
 'sectioneditnotsupported-title' => 'Jü beårbing foon oufsnaase wårt ai unerstüted',
 'sectioneditnotsupported-text' => 'Jü beårbing foon oufsnaase wårt aw jüdeer beårbingssid ai stiped.',
 'permissionserrors' => 'Beruchtigingsfäägel',
@@ -852,14 +853,14 @@ Dåt bestöö ål.',
 'expensive-parserfunction-warning' => 'Woorschauing: Jüdeer sid önjthålt tu fool apteele foon widluftie parserfunksjoone.
 
 Deer {{PLURAL:$2|mötj ai mör ås 1 apteel|mönje ai mör ås $1 apteele}} weese.',
-'expensive-parserfunction-category' => 'Side, da widluftie parserfunksjoone tu oofding apteele',
+'expensive-parserfunction-category' => 'Sidjen mä tuföl parser-funktjuunen.',
 'post-expand-template-inclusion-warning' => "'''Woorschauing:''' Jü grutelse foon da önjbünene forlååge as tu grut, hu forlååge koone ai önjbünen wårde.",
-'post-expand-template-inclusion-category' => 'Side, önj da jü maksimoole grutelse foon önjbünene forlååge ouerschran as',
+'post-expand-template-inclusion-category' => 'Sidjen mä muar üs det maksimaal taal faan iinbünjen föörlaagen.',
 'post-expand-template-argument-warning' => 'Påås aw: Jüdeer sid enthålt tumanst en argumänt önj en forlååge, dåt äkspandiird tu grut as. Dadeere argumänte wårde ignoriird.',
-'post-expand-template-argument-category' => 'Side, da ignoriirde forlååge-argumänte önjthüülje',
+'post-expand-template-argument-category' => 'Sidjen mä föörlaagen, huar ei arke iindrach brükt wurd koon.',
 'parser-template-loop-warning' => 'Forlåågesloif önjtdäkt: [[$1]]',
 'parser-template-recursion-depth-warning' => 'Forloagerekursjoonsdiipgränse ouerschran ($1)',
-'language-converter-depth-warning' => 'Spräkekonwärsjoonsdiipdegränse ouerschren ($1)',
+'language-converter-depth-warning' => 'Spriakenkonwerter auerläästet ($1)',
 'node-count-exceeded-category' => 'Jodiar sidjen haa tuföl ferbinjangen (nodes)',
 'node-count-exceeded-warning' => 'Detdiar sidj hää tuföl ferbinjangen (nodes)',
 'expansion-depth-exceeded-category' => 'Jodiar sidjen haa tuföl ütjwidjangen (expansion)',
@@ -884,13 +885,13 @@ Grün foon jü späre: ''$2''",
 # History pages
 'viewpagelogs' => 'Logbuken faan detdiar sidj uunwise',
 'nohistory' => 'Deer as niinj beårbingshistoori for jüdeer sid.',
-'currentrev' => 'Aktäle färsjoon.',
-'currentrev-asof' => 'Aktuäl färsjoon foon $2 am e klook $3',
-'revisionasof' => 'Färsjoon foon e klook $2, $3',
-'revision-info' => 'Färsjoon foon di $4 am e klook $5 foon $2',
-'previousrevision' => '← Näistålere färsjoon',
-'nextrevision' => 'Näistjunger färsjoon →',
-'currentrevisionlink' => 'Aktuäle färsjoon',
+'currentrev' => 'Leetst werjuun',
+'currentrev-asof' => 'Leetst werjuun faan di $2, am e klook $3',
+'revisionasof' => 'Werjuun faan $2, klook $3',
+'revision-info' => 'Werjuun faan di $4 am a klook $5 faan $2',
+'previousrevision' => '← Naistääler werjuun',
+'nextrevision' => 'Naistjonger werjuun →',
+'currentrevisionlink' => 'Leetst werjuun',
 'cur' => 'aktuäl',
 'next' => 'näist',
 'last' => 'leest',
@@ -909,25 +910,25 @@ Grün foon jü späre: ''$2''",
 'history-feed-title' => 'Färsjoonshistoori',
 'history-feed-description' => 'Färsjoonshistoori for jüdeer sid önj {{SITENAME}}',
 'history-feed-item-nocomment' => '$1 bit $2',
-'history-feed-empty' => 'Jü önjfordied sid bestoont ai. Flicht wörd jü sleeked unti ferschääwen.  [[Special:Search|Döörsäk]] {{SITENAME}} aw pååsende naie side.',
+'history-feed-empty' => 'Son sidj jaft at ei. Ferlicht as det stregen of fersköwen wurden. [[Special:Search|Schük]] üüb {{SITENAME}} efter nei sidjen, diar diartu paase.',
 
 # Revision deletion
 'rev-deleted-comment' => '(Tuhuupefooting wächnümen)',
 'rev-deleted-user' => '(Brükernoome wächhååld)',
 'rev-deleted-event' => '(Logbökaksjoon wächhååld)',
 'rev-deleted-user-contribs' => '[Benjüternoome unti IP-adräs wächhååld - beårbing üt baidråge ferstäägen]',
-'rev-deleted-text-permission' => "Judeer Färsjoon wörd '''straagen'''.
-Näre önjgoowen tu di strikforgung ås uk en begrüning fant huum önj dåt [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbök].",
-'rev-deleted-text-unhide' => "Jüdeer färsjoon as '''straagen''' wörden.
-Ainkelthäide fant huum önj dåt [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbök].
-Dü koost nuch [$1 jüdeer färsjoon bekiike], wan dü wider gunge mååst.",
-'rev-suppressed-text-unhide' => "Jüdeer färsjoon as '''unerdrükt''' wörden.
-Ainkelthäide fant huum önj dåt [{{fullurl:{{#special:Log}}/suppress|page={{FULLPAGENAMEE}}}} strik-logbök].
-Dü koost nuch [$1 jüdeer färsjoon bekiike], wan dü wider gunge mååst.",
-'rev-deleted-text-view' => "Jüdeer Färsjoon as '''straagen''' wörden.
-Dü koost da wider önjkiike. Näre önjgoowen tu di sleekforgung ås uk en begrüning fant huum önj dåt [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbök].",
-'rev-suppressed-text-view' => "Jüdeer färsjoon as '''unerdrükd''' wörden.
-Dü koost da önjkiike. Ainkelthäide stönje önj dåt [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} unerdrukings-logbök].",
+'rev-deleted-text-permission' => "Detdiar werjuun as '''stregen''' wurden.
+Uun't [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbuk] stäänt muar diartu.",
+'rev-deleted-text-unhide' => "Detdiar werjuun as '''stregen''' wurden.
+Uun't [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbuk] stäänt muar diartu.
+Dü könst [$1 detdiar werjuun uunluke], wan dü wel.",
+'rev-suppressed-text-unhide' => "Detdiar werjuun as '''ferbürgen''' wurden.
+Uun't [{{fullurl:{{#special:Log}}/suppress|page={{FULLPAGENAMEE}}}} fersteeg-logbuk] stäänt muar diartu.
+Dü könst [$1 detdiar werjuun uunluke], wan dü wel.",
+'rev-deleted-text-view' => "Detdiar werjuun as '''stregen''' wurden.
+Dü könst det noch uunluke. Uun't [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbuk] stäänt muar diartu.",
+'rev-suppressed-text-view' => "Detdiar werjuun as '''ferbürgen''' wurden.
+Dü könst det noch uunluke. Uun't [{{fullurl:{{#special:Log}}/suppress|page={{FULLPAGENAMEE}}}} fersteeg-logbuk] stäänt muar diartu.",
 'rev-deleted-no-diff' => "Dü könst di ferskeel ei uunluke, auer ian werjuun '''stregen''' wurden as.
 Wan dü muar wed wel, luke iin uun't [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbuk].",
 'rev-suppressed-no-diff' => "Dü könst di ferskeel ei uunluke, auer ian werjuun '''stregen''' wurden as.",
@@ -935,21 +936,20 @@ Wan dü muar wed wel, luke iin uun't [{{fullurl:{{#special:Log}}/delete|page={{F
 Dü könst [$1 di ferskeel uunluke], wan dü wel.",
 'rev-suppressed-unhide-diff' => "Ian faan jodiar werjuunen as '''ferbürgen''' wurden. Wan dü muar wed wel, luke iin uun't [{{fullurl:{{#special:Log}}/delete|page={{FULLPAGENAMEE}}}} fersteeg-logbuk].
 Dü könst [$1 di ferskeel uunluke], wan dü wel.",
-'rev-deleted-diff-view' => "En Färsjoon foon dåtdeer färsjoonsferschääl wörd '''straagen'''.
-Dü koost dåt färsjoonsferschääl siinj.
-Näre önjgoowen fant huum önj dåt [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbök].",
+'rev-deleted-diff-view' => "Ian faan jodiar werjuunen as '''stregen''' wurden.
+Dü könst di ferskeel uunluke. Wan dü muar wed wel, luke iin uun't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} strik-logbuk].",
 'rev-suppressed-diff-view' => "Ian faan jodiar werjuunen as '''ferbürgen''' wurden. Dü könst di ferskeel uunluke. Wan dü muar wed wel, luke iin uun't [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} fersteeg-logbuk].",
 'rev-delundel' => 'wis/fersteeg',
 'rev-showdeleted' => 'wise',
 'revisiondelete' => 'Werjuunen strik of weder iinstel',
-'revdelete-nooldid-title' => 'Niinj färsjoon önjjääwen',
+'revdelete-nooldid-title' => 'Nian werjuun uunden',
 'revdelete-nooldid-text' => 'Dü heest nian werjuun för detheer aktjuun uunden, of det werjuun jaft at ei, of dü ferschükst, en aktuel werjuun tu striken.',
 'revdelete-nologtype-title' => 'Niinj logtüüp önjjääwen',
 'revdelete-nologtype-text' => 'Deer wörd niinj logtüüp for jüheer aksjoon önjjääwen.',
 'revdelete-nologid-title' => 'Üngülti logönjdråch',
 'revdelete-nologid-text' => 'Deer wör niinj logtüüp ütkiisd unti di kiisde logtüüp bestoont ai.',
-'revdelete-no-file' => 'Jü önjjääwen dootäi bestoont ai.',
-'revdelete-show-file-confirm' => 'Bast dü sääker, dåt de jü sleeked färsjoon foon e dootäi „<nowiki>$1</nowiki>“ foon e $2 am e klook $3 önjsiinj wäät?',
+'revdelete-no-file' => 'Son dateinööm as ei diar.',
+'revdelete-show-file-confirm' => 'Wel dü würelk det stregen werjuun faan det datei „<nowiki>$1</nowiki>“ faan di $2, am a klook $3 uunluke?',
 'revdelete-show-file-submit' => 'Jåå',
 'revdelete-selected' => "'''{{PLURAL:$2|Ütjsoocht werjuun|Ütjsoocht werjuunen}} faan [[:$1]]:'''",
 'logdelete-selected' => "'''{{PLURAL:$1|Ütjsoocht logbukiindrach|Ütjsoocht logbukiindracher}}:'''",
@@ -961,8 +961,8 @@ Näre önjgoowen fant huum önj dåt [{{fullurl:{{#Special:Log}}/delete|page={{F
 * Persöönelk informatsjuunen, diar näämen wat uungung
 *: ''Adresen, Tilefoonnumern, Ferseekerangsnumern an sowat''",
 'revdelete-legend' => 'Seeten foon da sachtboorhäids-gränse',
-'revdelete-hide-text' => 'Täkst foon e färsjoon fersteege',
-'revdelete-hide-image' => 'Fersteege wat önj e Dootäi stoont',
+'revdelete-hide-text' => 'Tekst faan det werjuun fersteeg',
+'revdelete-hide-image' => 'Fersteeg, wat uun det datei stäänt',
 'revdelete-hide-name' => 'Logbök-aksjoon fersteege',
 'revdelete-hide-comment' => 'Beårbingskomäntoor fersteege',
 'revdelete-hide-user' => 'Benjüternoome//IP-adräse foon e beårber fersteege',
@@ -1001,7 +1001,7 @@ Dü heest deeraw nåån tugraawe.',
 ** Fülk informatsjuunen",
 'revdelete-otherreason' => 'Ouderen/tubaikaamenden grün:',
 'revdelete-reasonotherlist' => 'Ouderen grün',
-'revdelete-edit-reasonlist' => 'Strikgrüne beårbe',
+'revdelete-edit-reasonlist' => "Grünjer för't striken bewerke",
 'revdelete-offender' => 'Autoor foon jüdeer färsjoon:',
 
 # Suppression log
@@ -1013,7 +1013,7 @@ Luke bi [[Special:BlockList|List faan speret IP-adresen an brükernöömer]] fö
 'mergehistory' => 'Fersjoonshistoorie feriine',
 'mergehistory-header' => 'Ma jüdeer spetsjåålsid koost dü jü färsjoonshistoori foon en jurtkamstsid ma jü färsjoonshistoori foon en müüljsid feriine.
 Stal sääker, dåt jü färsjoonshistoori foon en sid histoorisch koräkt as.',
-'mergehistory-box' => 'Rewisjoone foon tou side feriine:',
+'mergehistory-box' => 'Ferluup faan tau sidjen tuupfeer:',
 'mergehistory-from' => 'Jurtkamstsid:',
 'mergehistory-into' => 'Müüljsid:',
 'mergehistory-list' => 'Werjuunen, diar tuupfeerd wurd kön.',
@@ -1060,11 +1060,11 @@ Dü könst det uun't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}}
 'searchresults' => 'Fünjen sidjen',
 'searchresults-title' => 'Fünjen sidjen för „$1“',
 'searchresulttext' => "Wan dü muar auer't schüken uun {{SITENAME}} wed wel, luke efter bi [[{{MediaWiki:Helppage}}|{{int:help}}]].",
-'searchsubtitle' => 'Din säkönjfrååg: „[[:$1|$1]]“ ([[Special:Prefixindex/$1|åle ma „$1“ beganende side]]{{int:pipe-separator}}[[Special:WhatLinksHere/$1|åle side, da eefter „$1“ ferlinke]])',
+'searchsubtitle' => 'Din uunfraag: „[[:$1|$1]]“ ([[Special:Prefixindex/$1|sidjen, diar mä „$1“ began]]{{int:pipe-separator}}[[Special:WhatLinksHere/$1|sidjen, diar efter „$1“ ferwise]])',
 'searchsubtitleinvalid' => 'Din säkönjfrååg: "$1".',
 'toomanymatches' => 'Diar kaam tuföl resultooten üüb din uunfraag. Fersjük det ööders.',
-'titlematches' => 'Oueriinjstiminge ma sidetiitle',
-'notitlematches' => 'Niinj oueriinjstiming ma sidetiitle',
+'titlematches' => 'Auerianstemangen mä sidjennöömer',
+'notitlematches' => 'Nian auerianstemangen mä sidjennöömer',
 'textmatches' => 'Oueriinjstiminge ma inhålte',
 'notextmatches' => 'Niinj oueriinjstiming ma inhålte',
 'prevn' => '{{PLURAL:$1|leesten|leeste $1}}',
@@ -1105,7 +1105,7 @@ Dü könst det uun't [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}}
 'showingresults' => "Heer {{PLURAL:$1|as '''1''' resultoot|san '''$1''' resultoote}}, beganend ma numer '''$2.'''",
 'showingresultsnum' => "Heer {{PLURAL:$3|as '''1''' resultoot|san '''$3''' resultoote}}, beganend ma numer '''$2.'''",
 'showingresultsheader' => "{{PLURAL:$5|resultoot '''$1''' foon '''$3'''|resultoote '''$1-$2''' foon '''$3'''}}, for '''$4.'''",
-'nonefound' => "'''Haanewising:''' Deer wårde ståndardmääsi man ainkelde noomerüme döörsoocht. Seet ''all:'' for din Säkbegrip, am åle side (inkl. diskusjoonside, forlååge, äsw.) tu döörsäken unti gesiilt di noome foon di tu döörsäkende noomerüm.",
+'nonefound' => "'''Paase üüb:''' Diar wurd man enkelt nöömrümer trochsoocht. Wan dü ''all:'' föör din wurd skraft, do woort uk uun aal a nöömrümer (datein, kategoriin, föörlaagen asw.) soocht. Dü könst uk en wasen nöömrüm föörwechstel.",
 'search-nonefound' => 'For din säkanfrååg würden niinj resultoote fünen.',
 'powersearch' => 'Ütjwidjet schüken',
 'powersearch-legend' => 'Ütjwidjet schüken',
@@ -1200,7 +1200,7 @@ Do san jo ual iinstelangen wech.',
 'prefs-memberingroups' => '{{GENDER:$2|Lasmoot}} faan {{PLURAL:$1|brükerskööl|brükersköölen}}:',
 'prefs-registration' => 'Uunmelde-tidj',
 'yourrealname' => 'Rocht nööm:',
-'yourlanguage' => 'Spräke:',
+'yourlanguage' => 'Spriak:',
 'yourvariant' => 'Spriak:',
 'prefs-help-variant' => 'Uun hün skriiwwiis skel a sidjen uunwiset wurd:',
 'yournick' => 'Nai signatuur:',
@@ -1355,8 +1355,8 @@ Do san jo ual iinstelangen wech.',
 
 # Associated actions - in the sentence "You do not have permission to X"
 'action-read' => 'jüdeer sid leese',
-'action-edit' => 'jüdeer sid beårbe',
-'action-createpage' => 'side mååge',
+'action-edit' => 'detdiar sidj tu bewerkin',
+'action-createpage' => 'nei sidjen tu maagin',
 'action-createtalk' => 'diskusjuunssidjen maage',
 'action-createaccount' => 'jüdeer brükerkonto mååge',
 'action-minoredit' => 'detdiar feranrang üs letj kääntiakne',
@@ -1407,7 +1407,7 @@ Do san jo ual iinstelangen wech.',
 'rcshowhidebots' => 'Bots $1',
 'rcshowhideliu' => '$1 uunmeldet brükern',
 'rcshowhideanons' => 'Anonymen brüker $1',
-'rcshowhidepatr' => '$1 efterluket feranrangen',
+'rcshowhidepatr' => 'Efterluket feranrangen $1',
 'rcshowhidemine' => 'Min bidracher $1',
 'rclinks' => 'Wis da leeste $1 änringe foon da leeste $2 deege.<br />$3',
 'diff' => 'ferskeel',
@@ -1434,7 +1434,7 @@ Do san jo ual iinstelangen wech.',
 'recentchangeslinked-noresult' => 'Uun detdiar tidj san jo ferlinket sidjen ei feranert wurden.',
 'recentchangeslinked-summary' => "Detdiar spezial-sidj wiset a leetst feranrangen faan ferwiset sidjen (of faan sidjen uun en was kategorii). Sidjen, diar dü [[Special:Watchlist|uun't uug behual]] wel, san '''fäät''' skrewen.",
 'recentchangeslinked-page' => 'Sid:',
-'recentchangeslinked-to' => 'Wis änringe aw side, da heerjurt ferlinke',
+'recentchangeslinked-to' => 'Wise feranrangen üüb sidjen, diar heerhen ferwise.',
 
 # Upload
 'upload' => 'Datei huuchschüür',
@@ -1442,7 +1442,7 @@ Do san jo ual iinstelangen wech.',
 'reuploaddesc' => "Ufbreeg an turag tu't sidj för't huuchsjüüren",
 'upload-tryagain' => 'Feranert dateibeskriiwang ufsjüür',
 'uploadnologin' => 'Ei uunmeldet',
-'uploadnologintext' => 'Dü möist [[Special:UserLogin|önjmälded weese]], am dat dü dootäie huuchsiinje koost.',
+'uploadnologintext' => 'Dü skel [[Special:UserLogin|uunmeldet]] wees, amdat dü datein huuchschüür könst.',
 'upload_directory_missing' => 'Dåt aplees-fertiiknis ($1) breecht än köö ai foon di wäbsärwer mååged wårde.',
 'upload_directory_read_only' => 'Dåt aplees-fertiiknis ($1) koon ai foon e wäbsärver beschraawen wårde.',
 'uploaderror' => 'Aplees-fäägel',
@@ -1678,14 +1678,14 @@ För a seekerhaid as img_auth.php ei aktiwiaret.',
 # File description page
 'file-anchor-link' => 'Datei',
 'filehist' => 'Dååtäifärsjoone',
-'filehist-help' => 'Klik aw en tidpunkt, am jüdeer färsjoon önjiinjtunaamen.',
+'filehist-help' => 'Klike üüb en tidjponkt, am detdiar werjuun uuntulukin.',
 'filehist-deleteall' => 'aal a werjuunen strik',
 'filehist-deleteone' => 'jüdeer färsjoon strike',
 'filehist-revert' => 'tubääg seete',
 'filehist-current' => 'aktuäl',
-'filehist-datetime' => 'Färsjoon foon e',
+'filehist-datetime' => 'Werjuun faan di',
 'filehist-thumb' => 'Forlökbil',
-'filehist-thumbtext' => 'Forlökbil for Färsjoon foon $2, am e klook $3',
+'filehist-thumbtext' => 'Föörskau för det werjuun faan $2, klook $3',
 'filehist-nothumb' => 'Niinj forlökbil deer',
 'filehist-user' => 'brüker',
 'filehist-dimensions' => 'Mätje',
@@ -1693,7 +1693,7 @@ För a seekerhaid as img_auth.php ei aktiwiaret.',
 'filehist-comment' => 'Komentoor',
 'filehist-missing' => 'Datei ei diar',
 'imagelinks' => 'Hü det datei brükt woort',
-'linkstoimage' => '{{PLURAL:$1|Jü füliend sid ferwånt|Da füliende $1 side ferwiinje}} jüdeer dååtäi:',
+'linkstoimage' => '{{PLURAL:$1|Detdiar sidj brükt|Jodiar $1 sidjen brük}} detdiar datei:',
 'linkstoimage-more' => 'Muar üs $1 {{PLURAL:$1|sidj ferwiset|sidjen ferwise}} üüb detdiar datei.
 Det list wiset {{PLURAL:$1|at iarst ferwisang|a iarst $1 ferwisangen}} üüb detdiar datei.
 Dü könst uk det [[Special:WhatLinksHere/$2|hial list]] uunluke.',
@@ -1712,7 +1712,7 @@ Ferlicht wel dü det [$2 beskriiwangssidj] feranre.',
 Ferlicht wel dü det [$2 beskriiwangssidj] feranre.',
 'filepage-nofile' => 'En datei mä didiar nööm jaft at ei.',
 'filepage-nofile-link' => 'En datei mä didiar nööm jaft at ei, man dü könst det [$1 huuchsjüür].',
-'uploadnewversion-linktext' => 'En nai färsjoon foon jüdeer dåtäi huuchsiinje',
+'uploadnewversion-linktext' => 'En nei werjuun faan detdiar datei huuchschüür',
 'shared-repo-from' => 'foon $1',
 'shared-repo' => 'en gemiansoom archiif',
 'upload-disallowed-here' => 'Dü könst detdiar datei ei auerskriiw.',
@@ -1879,7 +1879,7 @@ Uun arke rä stun ferwisangen tu't iarst an ööder widjerfeerang an uk tu det s
 'listusers-creationsort' => 'Sortiare efter dootem',
 'usereditcount' => '{{PLURAL:$1|feranrang|$1 feranrangen}}',
 'usercreated' => '{{GENDER:$3|Maaget}} di $1 am a klook $2',
-'newpages' => 'Naie side',
+'newpages' => 'Nei sidjen',
 'newpages-username' => 'Brükernoome:',
 'ancientpages' => 'Al loong ei muar bewerket sidjen',
 'move' => 'Ferschüwe',
@@ -1918,8 +1918,8 @@ Det woort efter logbukslach, brüker of sidjennööm uunwiset. Grat- an letjskri
 'alphaindexline' => '$1 bit $2',
 'nextpage' => 'Näist sid ($1)',
 'prevpage' => 'Leest sid ($1)',
-'allpagesfrom' => 'Side wise sunt:',
-'allpagesto' => 'Side wise bit:',
+'allpagesfrom' => 'Sidjen wise sant:',
+'allpagesto' => 'Sidjen wise bit:',
 'allarticles' => 'Aal a sidjen',
 'allinnamespace' => 'Aal a sidjen (nöömrüm: $1)',
 'allnotinnamespace' => 'Aal a sidjen (saner nöömrüm $1)',
@@ -2044,13 +2044,13 @@ Feranrangen faan detdiar sidj wurd üüb detdiar list fäästhäälen.",
 'removewatch' => "Ei muar uun't uug behual",
 'removedwatchtext' => 'Jü sid „[[:$1]]“ wörd foon din [[Special:Watchlist|eefterkiiklist]] wächhååld.',
 'watch' => 'Kiike eefter',
-'watchthispage' => 'Side eefterkiike',
+'watchthispage' => "Detdiar sidj uun't uug behual",
 'unwatch' => 'ai mör eefter kiike',
 'unwatchthispage' => "Ei muar uun't uug behual",
 'notanarticle' => 'Nään artiikel',
 'notvisiblerev' => 'Det werjuun faan en öödern brüker as stregen wurden.',
 'watchnochange' => "A sidjen, diar dü uun't uug heest, san uun di uunwiset tidjrüm ei bewerket wurden.",
-'watchlist-details' => 'Dü kiikst eefter {{PLURAL:$1|1 sid|$1 side}}.',
+'watchlist-details' => "Dü heest {{PLURAL:$1|1 sidj|$1 sidjen}} uun't uug.",
 'wlheader-enotif' => 'Di e-mail siinst as aktiif.',
 'wlheader-showupdated' => "Nei feranert sidjen wurd '''fäät''' uunwiset.",
 'watchmethod-recent' => "Leetst feranrangen faan sidjen, diar dü uun't uug heest",
@@ -2120,7 +2120,7 @@ Halep an muar diartu: {{canonicalurl:{{MediaWiki:Helppage}}}}',
 'confirmdeletetext' => 'Dü wel en sidj mä aal sin werjuunen strik. Dü skel gudkään, dat dü witjst, wat dü dääst an dat din dun mä a [[{{MediaWiki:Policy-url}}|brükerreegeln]] auerian stemet.',
 'actioncomplete' => 'Aksjoon beånd',
 'actionfailed' => 'Diar ging wat skiaf',
-'deletedtext' => '„$1“ wörd tunintemååged. In e $2 fanst dü en list foon da tuleest tunintemåågede side.',
+'deletedtext' => "„$1“ as stregen wurden. Uun't $2 fanjst dü a sidjen, diar tuleetst stregen wurden san.",
 'dellogpage' => 'Tunintemååg-Logbök',
 'dellogpagetext' => 'Diar stun a leetst stregen sidjen an datein.',
 'deletionlog' => "logbuk faan't striken",
@@ -2159,7 +2159,7 @@ Am dat diar ei noch muar skiaf gongt, as det aktjuun ufbreegen wurden.
 Gung turag, an began faan föören.",
 
 # Protect
-'protectlogpage' => 'Sideschütse-logbök',
+'protectlogpage' => 'Sidjenseekerangs-logbuk',
 'protectlogtext' => 'Detheer as det logbuk mä seekert sidjen.
 Üüb [[Special:ProtectedPages|detdiar list]] stun a seekert sidjen.',
 'protectedarticle' => 'schütsed „[[$1]]“',
@@ -2182,8 +2182,10 @@ Gung turag, an began faan föören.",
 'protect-text' => 'Heer koost dü e schütsstatus for jü sid "$1" önjkiike än änre.',
 'protect-locked-blocked' => "Dü könst det sidjenseekerang ei feranre, auer din brükerkonto speret as. So as det sidj '''„$1“:''' seekert wurden.",
 'protect-locked-dblock' => "Det dootenbeenk as speret, det sidjenseekerang koon ei feranert wurd. So as det sidj '''„$1“:''' seekert wurden.",
-'protect-locked-access' => "Din brükerkonto ferfäiget ai ouer da nüsie ruchte tu jü änring foon e sideschüts. Heer san da aktuäle sideschütsönjstalinge fon jü sid '''„$1“:'''",
-'protect-cascadeon' => 'Jüdeer sid as nütutids diilj foon e kaskaadenspäre. Jü as önj {{PLURAL:$1|jü füliende sid|da füliende side}} önjbünen, huk döör jü kaskaadenspäropsjoon schütsed {{PLURAL:$1|as|san}}. Di sideschütsstatus koon for jüdeer sid änred wårde, dåtdeer heet ouers nån influs aw jü kaskaadenspäre:',
+'protect-locked-access' => "Dü heest ei det brükerrocht, am det sidjenseekerhaid tu feranrin.
+Det sidj '''„$1“:''' as sodenang seekert wurden:",
+'protect-cascadeon' => 'Detdiar sidj as auer en kaskaadensper seekert wurden. Hat as uun {{PLURAL:$1|detdiar seekert sidj|jodiar seekert sidjen}} iinbünjen.
+Dü könst det seekerhaid feranre, det feranert oober ei det seekerhaid faan jo ööder sidjen.',
 'protect-default' => 'Arke brüker',
 'protect-fallback' => 'Ferloof bluas för brükern mä "$1"-rochten.',
 'protect-level-autoconfirmed' => 'Ferloof bluas för registriaret brükern.',
@@ -2281,7 +2283,7 @@ $1",
 'tooltip-invert' => 'Saat diar en tiaken, am feranrangen faan sidjen uun didiar nöömrüm ei uuntuwisin.',
 'namespace_association' => 'Ferbünjen nöömrüm',
 'tooltip-namespace_association' => 'Saat diar en tiaken, am di ferbünjen nöömrüm of diskusjuunsnöömrüm mä iintubetjin.',
-'blanknamespace' => '(Side)',
+'blanknamespace' => '(Sidjen)',
 
 # Contributions
 'contributions' => '{{GENDER:$1|Brüker}} bidracher',
@@ -2311,9 +2313,9 @@ $1",
 
 # What links here
 'whatlinkshere' => 'Ferwisangen üüb detdiar sidj',
-'whatlinkshere-title' => 'Side, da aw "$1" ferlinke',
+'whatlinkshere-title' => 'Sidjen, diar üüb "$1" ferwise',
 'whatlinkshere-page' => 'sid:',
-'linkshere' => "Da füliende side ferlinke aw '''„[[:$1]]“''':",
+'linkshere' => "Jodiar sidjen ferwise üüb '''„[[:$1]]“''':",
 'nolinkshere' => 'Nian sidj ferwiset üüb [[:$1]]',
 'nolinkshere-ns' => "Nian sidj ferwiset üüb '''„[[:$1]]“''' uun di ütjsoocht nöömrüm.",
 'isredirect' => 'widerliidjingssid',
@@ -2565,7 +2567,7 @@ Uun't logbuk stäänt muar diartu:",
 'file-exists-sharedrepo' => 'Didiar dateinööm woort al uun en gemiansoom archiif brükt. Wees so gud, an nem en öödern nööm.',
 
 # Export
-'export' => 'Side äksportiire',
+'export' => 'Sidjen eksportiare',
 'exporttext' => 'Mä detdiar spezial-sidj könst dü di tekst mä aal sin werjuunen tu en XML-datei eksportiare. Det nei datei koon do faan en ööder MediaWiki-Wiki [[Special:Import|importiaret]] wurd.
 
 Skriiw a sidjennöömer iin uun det tekstfial (man bluas ään noom uun arke rä).
@@ -2619,7 +2621,7 @@ $2',
 'thumbnail_image-missing' => 'Det datei as wel ei diar: $1',
 
 # Special:Import
-'import' => 'Side importiire',
+'import' => 'Sidjen importiare',
 'importinterwiki' => 'Transwiki import',
 'import-interwiki-text' => "Schük en Wiki an en sidj tu importiarin ütj. A werjuunen an brükernöömer bliiw erhäälen.
 Transwiki-import-aktjuunen wurd uun't [[Special:Log/import|Import-logbuk]] fäästhäälen.",
@@ -2689,13 +2691,13 @@ Transwiki-import-aktjuunen wurd uun't [[Special:Log/import|Import-logbuk]] fää
 'tooltip-pt-mytalk' => 'Din diskusjuunssidj',
 'tooltip-pt-anontalk' => 'Diskusjuun auer feranrangen faan detdiar IP-adres',
 'tooltip-pt-preferences' => 'Äine önjstalinge',
-'tooltip-pt-watchlist' => 'List foon eefterkiikede side',
+'tooltip-pt-watchlist' => "Sidjen, diar dü uun't uug behual wel",
 'tooltip-pt-mycontris' => 'List mä aanj bidracher',
 'tooltip-pt-login' => 'Wan dü di uunmeldest, heest dü muar mögelkhaiden. Dü säärst det oober ei.',
 'tooltip-pt-anonlogin' => 'Wan dü di uunmeldest, heest dü muar mögelkhaiden. Dü säärst det oober ei.',
 'tooltip-pt-logout' => 'Ufmelde',
 'tooltip-ca-talk' => 'Diskusjuun auer di artiikel',
-'tooltip-ca-edit' => 'Sid beårbe. Hål for dåt spikern jü forlökfunksjoon brüke.',
+'tooltip-ca-edit' => 'Sidj bewerke. Luke di det iarst ans uun, iar dü det seekerst.',
 'tooltip-ca-addsection' => 'Nai oufsnaas begane',
 'tooltip-ca-viewsource' => 'Jüdeer sid wårt uner ferbading hülen. Di kwältäkst koon önjkiiked wårde.',
 'tooltip-ca-history' => 'Ääler werjuunen faan detdiar sidj',
@@ -2717,8 +2719,8 @@ Transwiki-import-aktjuunen wurd uun't [[Special:Log/import|Import-logbuk]] fää
 'tooltip-n-recentchanges' => 'Leetst feranrangen faan {{SITENAME}}',
 'tooltip-n-randompage' => 'Tufali sid',
 'tooltip-n-help' => 'Heelpsid wise',
-'tooltip-t-whatlinkshere' => 'List foon ål da side, da heer jurt wise',
-'tooltip-t-recentchangeslinked' => 'Leest änringen bai side, da foon heer ferlinkd san',
+'tooltip-t-whatlinkshere' => 'Aal a sidjen, diar heerhen ferwise',
+'tooltip-t-recentchangeslinked' => 'Leetst feranrangen faan sidjen, huar faan heer üüb ferwiset woort',
 'tooltip-feed-rss' => 'RSS-feed for jüdeer sid',
 'tooltip-feed-atom' => 'Atom-feed for jüdeer sid',
 'tooltip-t-contributions' => 'List mä bidracher faan didiar brüker uunluke',
@@ -2916,10 +2918,11 @@ $1",
 'yesterday-at' => 'Jister am a klook $1',
 
 # Bad image list
-'bad_image_list' => 'Formååt:
+'bad_image_list' => 'Formaat:
 
-Bloot rae, da ma en * begane, wårde ütwjarted. As jarste eefter dåt * mötj en link aw en ai wansched dååtäi stönje.
-Deeraw föliende sidelinke önj dåtseelwi ra definiire ütnååme, önj di kontäkst weerfoon jü dååtäi duch tu schüns kaame mötj.',
+Bluas räen, diar mä en * began, wurd mätääld.
+Det skal bääft di * began mä en ferwisang üüb en ferkiard datei. 
+Ferwisangen uun det salew rä wurd üs ütjnoomen uunsen, huar det datei dach uunwiset wurd mut.',
 
 # Metadata
 'metadata' => 'Metadååte',
@@ -3331,7 +3334,7 @@ Deeraw föliende sidelinke önj dåtseelwi ra definiire ütnååme, önj di kont
 'exif-urgency-other' => 'Faan a brüker fäästlaanj prioriteet ($1)',
 
 # External editor support
-'edit-externally' => 'Jüdeer dåtäi ma en äkstärn prugram beårbe',
+'edit-externally' => 'Detdiar datei mä en ekstern program bewerke',
 'edit-externally-help' => '(Sii da [//www.mediawiki.org/wiki/Manual:External_editors Installationsanweisungen] for widere Informasjoon)',
 
 # 'all' in various places, this might be different for inflected languages
@@ -3481,8 +3484,8 @@ Dü könst uk det [[Special:EditWatchlist|normool sidj]] tu bewerkin nem.",
 
 # Watchlist editing tools
 'watchlisttools-view' => 'Eefterkiiklist: änringe',
-'watchlisttools-edit' => 'normåål beårbe',
-'watchlisttools-raw' => 'Listeformoot beårbe (import/äksport)',
+'watchlisttools-edit' => 'Normool bewerke',
+'watchlisttools-raw' => "Uun't listenformoot bewerke",
 
 # Signatures
 'signature' => '[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|Diskusjuun]])',
index 51d83f5..c425168 100644 (file)
@@ -936,6 +936,8 @@ $2',
 
 # Special:PasswordReset
 'passwordreset' => 'איפוס סיסמה',
+'passwordreset-text-one' => 'מלאו טופס זה כדי לאפס את הסיסמה.',
+'passwordreset-text-many' => '{{PLURAL:$1||הקלידו אחד מפריטי המידע כדי לאפס את הסיסמה.}}',
 'passwordreset-legend' => 'איפוס סיסמה',
 'passwordreset-disabled' => 'איפוסי סיסמה בוטלו באתר ויקי זה.',
 'passwordreset-emaildisabled' => 'שירותי הדוא"ל בוטלו באתר ויקי זה.',
index cbdabaf..321561f 100644 (file)
@@ -726,6 +726,7 @@ Es possibile que tu ha ja cambiate tu contrasigno o requestate un nove contrasig
 
 # Special:PasswordReset
 'passwordreset' => 'Reinitialisar contrasigno',
+'passwordreset-text-one' => 'Completa iste formulario pro reinitialisar tu contrasigno.',
 'passwordreset-legend' => 'Reinitialisar contrasigno',
 'passwordreset-disabled' => 'Le reinitialisation de contrasignos ha essite disactivate in iste wiki.',
 'passwordreset-emaildisabled' => 'Le functionalitate de e-mail ha essite disactivate in iste wiki.',
index 27d7950..36df20f 100644 (file)
@@ -693,6 +693,7 @@ Ekki gleyma að breyta [[Special:Preferences|{{SITENAME}} stillingunum]] þínum
 'userlogin-resetpassword-link' => 'Endursetja lykilorð',
 'helplogin-url' => 'Help:Innskráning',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Hjálp við innskráningu]]',
+'createacct-join' => 'Sláðu inn þínar upplýsingar fyrir neðan.',
 'createacct-emailrequired' => 'Netfang',
 'createacct-emailoptional' => 'Netfang (valfrjálst)',
 'createacct-email-ph' => 'Skrifaðu niður netfangið þitt',
@@ -700,6 +701,7 @@ Ekki gleyma að breyta [[Special:Preferences|{{SITENAME}} stillingunum]] þínum
 'createacct-realname' => 'Raunverulegt nafn (valfrjálst)',
 'createaccountreason' => 'Ástæða:',
 'createacct-reason' => 'Ástæða',
+'createacct-reason-ph' => 'Afhverju ertu að búa til annan aðgang',
 'createacct-captcha' => 'Öryggis athugun',
 'createacct-imgcaptcha-ph' => 'Sláðu inn textann að ofan',
 'createacct-submit' => 'Búa til aðganginn',
@@ -1038,6 +1040,7 @@ Engin skýring gefin.',
 Svo virðist sem henni hafi verið eytt.',
 'edit-conflict' => 'Breytingaárekstur.',
 'edit-no-change' => 'Breyting þín var hunsuð, því engin breyting var á textanum.',
+'postedit-confirmation' => 'Breytingin þín var vistuð.',
 'edit-already-exists' => 'Gat ekki skapað nýja síðu.
 Hún er nú þegar til.',
 'defaultmessagetext' => 'Sjálfgefinn skilaboða texti',
index 30f3c9c..78cd685 100644 (file)
@@ -852,6 +852,7 @@ La password potrebbe essere stata già cambiata, oppure potrebbe essere stata ri
 
 # Special:PasswordReset
 'passwordreset' => 'Reimposta password',
+'passwordreset-text-one' => 'Compila questo modulo per reimpostare la tua password.',
 'passwordreset-legend' => 'Reimposta password',
 'passwordreset-disabled' => 'La reimpostazione delle password è stata disabilitata su questa wiki',
 'passwordreset-emaildisabled' => 'Le funzionalità di posta elettronica sono state disabilitate su questa wiki.',
index 62d3f0d..c0dd43b 100644 (file)
@@ -810,7 +810,7 @@ $2',
 'createacct-yourpasswordagain-ph' => 'パスワードを再入力',
 'remembermypassword' => 'このブラウザーにログイン情報を保存 (最長 $1 {{PLURAL:$1|日|日間}})',
 'userlogin-remembermypassword' => 'ログイン状態を保持',
-'userlogin-signwithsecure' => 'セキュリティで保護された接続を使用',
+'userlogin-signwithsecure' => 'SSL (https) 接続を使用',
 'securelogin-stick-https' => 'ログイン後にHTTPS接続を維持',
 'yourdomainname' => 'ドメイン:',
 'password-change-forbidden' => 'このウィキではパスワードを変更できません。',
index ba16be5..f8fdad2 100644 (file)
@@ -759,6 +759,8 @@ Vläicht hutt Dir Äert Passwuert scho geännert oder en neit temporäert Passwu
 
 # Special:PasswordReset
 'passwordreset' => 'Passwuert zrécksetzen',
+'passwordreset-text-one' => 'Fëllt dëse Formulaire aus fir Äert Passwuert zréckzesetzen.',
+'passwordreset-text-many' => '{{PLURAL:$1|Gitt eng vun dësen Donnéeën a fir Ärt Passwuert zréckzesetzen.}}',
 'passwordreset-legend' => 'Passwuert zrécksetzen',
 'passwordreset-disabled' => "D'Zerécksetze vum Passwuert ass op dëser Wiki ausgeschalt.",
 'passwordreset-emaildisabled' => "D'E-Mail-Fonctioune goufen op dëser Wiki ausgeschalt.",
@@ -1000,6 +1002,7 @@ Si gouf anscheinend geläscht.",
 'postedit-confirmation' => 'Är Ännerung gouf gespäichert.',
 'edit-already-exists' => 'Déi nei Säit konnt net ugeluecht ginn, well et se scho gëtt.',
 'defaultmessagetext' => 'Standardtext',
+'content-failed-to-parse' => 'Den $2-Inhalt fir den $1-Modell konnt net geparst ginn: $1',
 'invalid-content-data' => 'Donnéeë vum Inhalt sinn net valabel',
 'content-not-allowed-here' => '"$1"-Inhalt ass op der Säit [[$2]] net erlaabt',
 'editwarning-warning' => 'Wann Dir dës Säit verloosst kann dat dozou féieren datt Dir all Ännerungen, déi Dir gemaach hutt, verléiert.
index 4ae8d18..a7f4f40 100644 (file)
@@ -1647,7 +1647,7 @@ Judul laman nan <del>dicoret</del> bararti alah dipelokan.',
 'listusers' => 'Daftar pangguno',
 'listusers-editsonly' => 'Tunjuakan hanyo pangguno nan ado jariah',
 'listusers-creationsort' => 'Uruikan manuruik tanggal pandaftaran',
-'usereditcount' => '$1 {{PLURAL:$1|suntiangan}}',
+'usereditcount' => '$1 {{PLURAL:$1|}} suntiangan',
 'usercreated' => '{{GENDER:$3|Dibuek}} pado $1 pukua $2',
 'newpages' => 'Laman baru',
 'newpages-username' => 'Namo pangguno:',
@@ -1863,7 +1863,7 @@ Awak dapek maubah tingkek perlindungannyo, walaupun indak pangaruah pado perlind
 ** Baulang kali dikosongan
 ** Pamintaan pangguno',
 'protect-edit-reasonlist' => 'Suntiang alasan palinduangan',
-'protect-expiry-options' => '1 jam:1 hour,1 ari:1 day,1 minggu:1 week,2 minggu:2 weeks,1 bulan:1 month,3 bulan:3 months,6 bulan:6 months,1 taun:1 year,salamonyo:infinite',
+'protect-expiry-options' => '1 jam:1 hour,1 ari:1 day,1 pakan:1 week,2 pakan:2 weeks,1 bulan:1 month,3 bulan:3 months,6 bulan:6 months,1 taun:1 year,salamonyo:infinite',
 'restriction-type' => 'Palinduangan:',
 'restriction-level' => 'Tingkek:',
 'minimum-size' => 'Ukuran min',
@@ -1978,7 +1978,7 @@ Masuakan alasan sakek di bawah (contoh, mambuek karusakan atau vandal).',
 'ipbenableautoblock' => 'Otomatih sakek alamaik IP tarakhia nan digunoan pangguno ko, jo sado alamaik IP takaik nan mancubo manyuntiang.',
 'ipbsubmit' => 'Sakek pangguno ko',
 'ipbother' => 'Salamo:',
-'ipboptions' => '2 jam:2 hours,1 ari:1 day,3 ari:3 days,1 minggu:1 week,2 minggu:2 weeks,1 bulan:1 month,3 bulan:3 months,6 bulan:6 months,1 taun:1 year,salamonyo:infinite',
+'ipboptions' => '2 jam:2 hours,1 ari:1 day,3 ari:3 days,1 pakan:1 week,2 pakan:2 weeks,1 bulan:1 month,3 bulan:3 months,6 bulan:6 months,1 taun:1 year,salamonyo:infinite',
 'ipbotheroption' => 'lainnyo',
 'ipbotherreason' => 'Alasan lain/tambahan:',
 'ipbhidename' => 'Suruakan namo pangguno dari daftar jo suntiangan',
@@ -2358,7 +2358,7 @@ Sanak hanyo buliah mancaliak sumbernyo sajo',
 'minutes' => '{{PLURAL:$1|1 minik|$1 minik}}',
 'hours' => '{{PLURAL:$1|1 jam|$1 jam}}',
 'days' => '{{PLURAL:$1|1 ari|$1 ari}}',
-'weeks' => '{{PLURAL:$1|1 minggu|$1 minggu}}',
+'weeks' => '{{PLURAL:$1|1 pakan|$1 pakan}}',
 'months' => '{{PLURAL:$1|1 bulan|$1 bulan}}',
 'years' => '{{PLURAL:$1|1 taun|$1 taun}}',
 'ago' => '$1 nan lalu',
index b629fba..6c7f0a6 100644 (file)
@@ -936,6 +936,8 @@ $2',
 
 # Special:PasswordReset
 'passwordreset' => 'Менување на лозинка',
+'passwordreset-text-one' => 'Пополнете го образецов за да ја измените лозинката.',
+'passwordreset-text-many' => '{{PLURAL:$1|Внесете еден од податоците за да ја смените лозинката.}}',
 'passwordreset-legend' => 'Нова лозинка',
 'passwordreset-disabled' => 'На ова вики е оневозможено задавање на нова лозинка.',
 'passwordreset-emaildisabled' => 'Можностите за е-пошта се исклучени на ова вики',
index 7547019..17c41e4 100644 (file)
@@ -901,6 +901,8 @@ $2',
 
 # Special:PasswordReset
 'passwordreset' => 'രഹസ്യവാക്ക് പുനഃക്രമീകരിക്കുക',
+'passwordreset-text-one' => 'രഹസ്യവാക്ക് പുനർസജ്ജീകരിക്കാനായി ഈ ഫോം പൂരിപ്പിക്കുക.',
+'passwordreset-text-many' => '{{PLURAL:$1|താങ്കളുടെ രഹസ്യവാക്ക് പുനസജ്ജീകരിക്കാൻ വിവരശകലം നൽകുക.}}',
 'passwordreset-legend' => 'രഹസ്യവാക്ക് പുനഃക്രമീകരിക്കുക',
 'passwordreset-disabled' => 'ഈ വിക്കിയിൽ രഹസ്യവാക്ക് പുനഃക്രമീകരിക്കലുകൾ പ്രവർത്തരഹിതമാക്കിയിരിക്കുകയാണ്.',
 'passwordreset-emaildisabled' => 'ഈ വിക്കിയിൽ ഇമെയിൽ സൗകര്യങ്ങൾ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു.',
@@ -1132,8 +1134,8 @@ $1 ആണ് ഈ തടയൽ നടത്തിയത്. ''$2'' എന്ന
 'content-failed-to-parse' => '$2 ഉള്ളടക്കം $1 മാതൃകയിൽ പാഴ്സ് ചെയ്യൽ പരാജയപ്പെട്ടു: $3',
 'invalid-content-data' => 'അസാധുവായ ഉള്ളടക്ക ഡേറ്റ',
 'content-not-allowed-here' => '"$1" ഉള്ളടക്കം [[$2]] താളിൽ അനുവദിക്കുന്നില്ല',
-'editwarning-warning' => 'ഈ താളിൽ നിന്നു പോകുന്നത് താങ്കൾ വരുത്തിയ മാറ്റങ്ങൾ നഷ്ടപ്പെടാൻ ഇടയാക്കുന്നതാണ്.
-താങ്കൾ ലോഗിൻ ചെയ്തിട്ടുണ്ടെങ്കിൽ, താങ്കളുടെ ക്രമീകരണങ്ങളിൽ "{{int:prefs-editing}}" ഭാഗത്ത് ചെന്ന് ഈ അറിയിപ്പ് ഒഴിവാക്കാവുന്നതാണ്.',
+'editwarning-warning' => 'ഈ താളിൽ നിന്നും പോകുന്നത് താങ്കൾ വരുത്തിയ മാറ്റങ്ങൾ നഷ്ടപ്പെടാൻ ഇടയാക്കും.
+താങ്കൾ ലോഗിൻ ചെയ്തിട്ടുണ്ടെങ്കിൽ, താങ്കളുടെ ക്രമീകരണങ്ങളിൽ "തിരുത്തൽ" എന്ന ഭാഗത്ത് ചെന്ന് ഈ അറിയിപ്പ് പ്രദർശിപ്പിക്കുന്നത് ഒഴിവാക്കാവുന്നതാണ്.',
 
 # Content models
 'content-model-wikitext' => 'വിക്കിഎഴുത്ത്',
index b3e2c98..c9c2cce 100644 (file)
@@ -425,6 +425,18 @@ $messages = array(
 'oct' => 'okt',
 'nov' => 'nov',
 'dec' => 'des',
+'january-date' => '$1. januar',
+'february-date' => '$1. februar',
+'march-date' => '$1. mars',
+'april-date' => '$1. april',
+'may-date' => '$1. mai',
+'june-date' => '$1. juni',
+'july-date' => '$1. juli',
+'august-date' => '$1. august',
+'september-date' => '$1. september',
+'october-date' => '$1. oktober',
+'november-date' => '$1. november',
+'december-date' => '$1. desember',
 
 # Categories related messages
 'pagecategories' => '{{PLURAL:$1|Kategori|Kategoriar}}',
@@ -1063,6 +1075,7 @@ Sletteloggen for sida finn du her:",
 Det ser ut til at ho er sletta.',
 'edit-conflict' => 'Endringskonflikt.',
 'edit-no-change' => 'Redigeringa di vart ignorert fordi det ikkje vart gjort endringar i teksten.',
+'postedit-confirmation' => 'Endringa di vart lagra.',
 'edit-already-exists' => 'Kunne ikkje opprette ny side fordi ho alt eksisterer.',
 'defaultmessagetext' => 'Standard meldingstekst',
 'content-failed-to-parse' => 'Klarte ikkje å tolke innhaldet «$2» for innhaldsmodellen «$1»: $3',
index 9aaf4b6..2cec50d 100644 (file)
@@ -224,11 +224,11 @@ $messages = array(
 'permalink' => 'Anliura fissa',
 'print' => 'Stampé',
 'view' => 'Vardé',
-'edit' => 'Modìfica',
+'edit' => 'Modifiché',
 'create' => 'Creé',
-'editthispage' => "Modìfica st'artìcol-sì",
+'editthispage' => 'Modifiché costa pàgina',
 'create-this-page' => 'Creé sta pàgina',
-'delete' => 'Scancela',
+'delete' => 'Scancelé',
 'deletethispage' => 'Scancela pàgina',
 'undelete_short' => 'Disdëscancela {{PLURAL:$1|na modìfica|$1 modìfiche}}',
 'viewdeleted_short' => 'Vardé {{PLURAL:$1|na modìfica scancelà|$1 modìfiche scancelà}}',
index 1836ec5..89b727f 100644 (file)
@@ -423,6 +423,18 @@ $messages = array(
 'oct' => 'Out.',
 'nov' => 'Nov.',
 'dec' => 'Dez.',
+'january-date' => '$1 de Janeiro',
+'february-date' => '$1 de Fevereiro',
+'march-date' => '$1 de Março',
+'april-date' => '$1 de Abril',
+'may-date' => '$1 de Maio',
+'june-date' => '$1 de Junho',
+'july-date' => '$1 de Julho',
+'august-date' => '$1 de Agosto',
+'september-date' => '$1 de Setembro',
+'october-date' => '$1 de Outubro',
+'november-date' => '$1 de Novembro',
+'december-date' => '$1 de Dezembro',
 
 # Categories related messages
 'pagecategories' => '{{PLURAL:$1|Categoria|Categorias}}',
@@ -739,8 +751,8 @@ Não se esqueça de personalizar as suas [[Special:Preferences|preferências]].'
 'createacct-yourpasswordagain' => 'Confirme a palavra-chave',
 'createacct-yourpasswordagain-ph' => 'Digite a palavra-chave novamente',
 'remembermypassword' => 'Recordar os meus dados neste computador (no máximo, por $1 {{PLURAL:$1|dia|dias}})',
-'userlogin-remembermypassword' => 'Memorizar neste computador',
-'userlogin-signwithsecure' => 'Inicie sessão com servidor seguro',
+'userlogin-remembermypassword' => 'Manter-me autenticado',
+'userlogin-signwithsecure' => 'Use uma ligação segura',
 'securelogin-stick-https' => 'Manter a ligação HTTPS após a autenticação',
 'yourdomainname' => 'O seu domínio:',
 'password-change-forbidden' => 'Não podes alterar senhas nesta wiki.',
@@ -761,6 +773,7 @@ Não se esqueça de personalizar as suas [[Special:Preferences|preferências]].'
 'gotaccount' => "Já possui uma conta? '''$1'''.",
 'gotaccountlink' => 'Autentique-se',
 'userlogin-resetlink' => 'Esqueceu-se do seu nome de utilizador ou da palavra-chave?',
+'userlogin-resetpassword-link' => 'Recuperar palavra-chave',
 'helplogin-url' => 'Ajuda:login',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Ajuda a fazer login]]',
 'createacct-join' => 'Insira a sua informação abaixo.',
@@ -771,11 +784,19 @@ Não se esqueça de personalizar as suas [[Special:Preferences|preferências]].'
 'createacct-realname' => 'Nome verdadeiro (opcional)',
 'createaccountreason' => 'Motivo:',
 'createacct-reason' => 'Razão',
+'createacct-reason-ph' => 'Porque está a criar outra conta',
 'createacct-captcha' => 'Verificar segurança',
+'createacct-imgcaptcha-ph' => 'Digite o texto que vê acima',
+'createacct-submit' => 'Crie a sua conta',
+'createacct-benefit-heading' => '{{SITENAME}} é feito por pessoas como você.',
+'createacct-benefit-body1' => '{{PLURAL:$1|edição|edições}}',
+'createacct-benefit-body2' => '{{PLURAL:$1|página|páginas}}',
+'createacct-benefit-body3' => '{{PLURAL:$1|contribuidor|contribuidores}} recentes',
 'badretype' => 'As palavras-chave que introduziu não são iguais.',
 'userexists' => 'O nome de utilizador introduzido já existe.
 Por favor escolha um nome diferente.',
 'loginerror' => 'Erro de autenticação',
+'createacct-error' => 'Erro na criação da conta',
 'createaccounterror' => 'Não foi possível criar a conta: $1',
 'nocookiesnew' => "A conta de utilizador foi criada, mas neste momento não está autenticado.
 A {{SITENAME}} utiliza ''cookies'' para autenticar os utilizadores.
@@ -846,6 +867,7 @@ Aguarde antes de tentar novamente, por favor.',
 # Email sending
 'php-mail-error-unknown' => 'Erro desconhecido na função mail() do PHP',
 'user-mail-no-addy' => 'Tentou enviar uma mensagem sem um endereço de correio electrónico',
+'user-mail-no-body' => 'Tentou mandar email sem conteúdo ou com conteúdo demasiado pequeno.',
 
 # Change password dialog
 'resetpass' => 'Alterar palavra-chave',
@@ -865,11 +887,15 @@ Para prosseguir, será necessário definir uma nova palavra-chave.',
 'resetpass-wrong-oldpass' => 'Palavra-chave temporária ou actual inválida.
 Pode ter já alterado com sucesso a sua palavra-chave ou solicitado uma nova palavra-chave temporária.',
 'resetpass-temp-password' => 'Palavra-chave temporária:',
+'resetpass-abort-generic' => 'Alteração de senha foi cancelada por uma extensão.',
 
 # Special:PasswordReset
 'passwordreset' => 'Repor palavra-chave',
+'passwordreset-text-one' => 'Preencha este formulário para repor a sua palavra-passe.',
+'passwordreset-text-many' => '{{PLURAL:$1|Digite uma parte dos dados para redefinir sua senha.}}',
 'passwordreset-legend' => 'Reiniciar a palavra-chave',
 'passwordreset-disabled' => 'O reinício da palavra-chave foi impossibilitado nesta wiki.',
+'passwordreset-emaildisabled' => 'Recursos de e-mail foram desactivados neste wiki.',
 'passwordreset-username' => 'Nome de utilizador:',
 'passwordreset-domain' => 'Domínio:',
 'passwordreset-capture' => 'Ver o email resultante?',
@@ -900,6 +926,7 @@ Palavra-chave temporária: $2',
 'changeemail-oldemail' => 'Correio electrónico actual:',
 'changeemail-newemail' => 'Correio electrónico novo:',
 'changeemail-none' => '(nenhum)',
+'changeemail-password' => 'A sua senha {{SITENAME}}:',
 'changeemail-submit' => 'Alterar correio electrónico',
 'changeemail-cancel' => 'Cancelar',
 
@@ -1115,6 +1142,8 @@ Ela parece ter sido eliminada.',
 'edit-already-exists' => 'Não foi possível criar uma página nova.
 Ela já existia.',
 'defaultmessagetext' => 'Texto da mensagem padrão',
+'content-failed-to-parse' => 'Falha ao analisar conteúdo $2 para modelo $1:$3',
+'invalid-content-data' => 'Dados de conteúdo inválidos',
 'content-not-allowed-here' => 'Conteúdo do tipo "$1" não é permitido na página [[$2]]',
 'editwarning-warning' => 'Sair desta página fará com que você perca todas as alterações feitas por você.
 Se você fez login, pode desabilitar este aviso na seção "{{int:prefs-editing}}" das suas preferências.',
@@ -1408,6 +1437,7 @@ Tente usar o prefixo ''all:'' para pesquisar todo o conteúdo (incluindo página
 'searchdisabled' => 'Foi impossibilitada a realização de pesquisas na {{SITENAME}}.
 Entretanto, pode realizar pesquisas através do Google.
 Note, no entanto, que a indexação da {{SITENAME}} neste motor de busca pode estar desactualizada.',
+'search-error' => 'Um erro ocorreu enquanto se efectuava a busca: $1',
 
 # Preferences page
 'preferences' => 'Preferências',
@@ -1552,6 +1582,7 @@ Esta informação será pública.',
 'userrights-notallowed' => 'A sua conta não tem permissão para adicionar ou remover privilégios a utilizadores.',
 'userrights-changeable-col' => 'Grupos que pode alterar',
 'userrights-unchangeable-col' => 'Grupos que não pode alterar',
+'userrights-conflict' => 'Conflito com os privilégios dos utilizadores! Por favor, aplique as suas mudanças novamente.',
 
 # Groups
 'group' => 'Grupo:',
@@ -1894,6 +1925,7 @@ Caso o problema persista, contacte um [[Special:ListUsers/sysop|administrador]].
 'backend-fail-notsame' => 'Já existe um ficheiro não idêntico em $1 .',
 'backend-fail-invalidpath' => '$1 não é um caminho de armazenamento válido.',
 'backend-fail-delete' => 'Não foi possível excluir o ficheiro $1.',
+'backend-fail-describe' => 'Não foi possível mudar metadados para o ficheiro "$1".',
 'backend-fail-alreadyexists' => 'O ficheiro $1 já existe.',
 'backend-fail-store' => 'Não foi possível armazenar o ficheiro $1 em $2.',
 'backend-fail-copy' => 'Não foi possível copiar o ficheiro $1 para $2.',
@@ -2126,6 +2158,12 @@ Talvez queira editar a descrição na [$2 página original de descrição do fic
 Estes links deviam ser desambiguados, apontando-os para uma página mais apropriada.<br />
 Considera-se que uma página é de desambiguação se nela for utilizada uma predefinição que esteja definida em [[MediaWiki:Disambiguationspage]].",
 
+'pageswithprop' => 'Páginas com uma propriedade',
+'pageswithprop-legend' => 'Páginas com uma propriedade',
+'pageswithprop-text' => 'Esta página lista páginas que usam uma propriedade em particular.',
+'pageswithprop-prop' => 'Nome da propriedade:',
+'pageswithprop-submit' => 'Avançar',
+
 'doubleredirects' => 'Redireccionamentos duplos',
 'doubleredirectstext' => 'Esta página lista todas as páginas que redireccionam para outras páginas de redireccionamento.
 Cada linha contém links para o primeiro e segundo redireccionamentos, bem como o destino do segundo redireccionamento, geralmente contendo a verdadeira página de destino, que devia ser o destino do primeiro redireccionamento.
@@ -2323,6 +2361,7 @@ Encontram-se disponíveis [[{{MediaWiki:Listgrouprights-helppage}}|informações
 'mailnologintext' => 'Precisa de estar [[Special:UserLogin|autenticado]] e possuir um endereço de correio válido nas suas [[Special:Preferences|preferências]], para poder enviar correio electrónico a outros utilizadores.',
 'emailuser' => 'Enviar correio electrónico a este utilizador',
 'emailuser-title-target' => 'Enviar correio eletrónico a {{GENDER:$1|este utilizador|esta utilizadora}}',
+'emailuser-title-notarget' => 'Enviar correio electrónico ao utilizador',
 'emailpage' => 'Enviar correio electrónico ao utilizador',
 'emailpagetext' => 'Pode usar o formulário abaixo para enviar uma mensagem por correio eletrónico para {{GENDER:$1|este utilizador|esta utilizadora}}.
 O endereço de correio que introduziu nas [[Special:Preferences|suas preferências]] irá aparecer no campo do remetente da mensagem "De:", para que o destinatário lhe possa responder diretamente.',
@@ -2394,6 +2433,16 @@ O nome desta página passará a aparecer a '''negrito''' na lista de [[Special:R
 'enotif_mailer' => 'Gerador de Notificações da {{SITENAME}}',
 'enotif_reset' => 'Marcar todas as páginas como visitadas',
 'enotif_impersonal_salutation' => 'Utilizador da "{{SITENAME}}"',
+'enotif_subject_deleted' => 'A página  $1 de {{SITENAME}} foi {{GENDER:$2|eliminada}} por $2',
+'enotif_subject_created' => 'A página $1 de {{SITENAME}} foi {{GENDER:$2|criada}} por $2',
+'enotif_subject_moved' => 'A página $1 de {{SITENAME}} foi {{GENDER:$2|movida}} por $2',
+'enotif_subject_restored' => 'A página $1 de {{SITENAME}} foi {{GENDER:$2|restaurada}} por $2',
+'enotif_subject_changed' => 'A página $1 de {{SITENAME}} foi {{GENDER:$2|alterada}} por $2',
+'enotif_body_intro_deleted' => 'A página $1 de {{SITENAME}} foi {{GENDER:$2|eliminada}} em $PAGEEDITDATE por $2, ver $3.',
+'enotif_body_intro_created' => 'A página $1 em {{SITENAME}} foi {{GENDER:$2| criada}} em $PAGEEDITDATE por $2, ver $3 para a versão actual.',
+'enotif_body_intro_moved' => 'A páginas $1 em {{SITENAME}} foi {{GENDER:$2|movida}} em $PAGEEDITDATE por $2, ver $3 para a versão actual.',
+'enotif_body_intro_restored' => 'A páginas $1 em {{SITENAME}} foi {{GENDER:$2|restaurada}} em $PAGEEDITDATE por $2, ver $3 para a versão actual.',
+'enotif_body_intro_changed' => 'A página $1 em {{SITENAME}} foi {{GENDER:$2|alterada}} em $PAGEEDITDATE por $2, ver $3 para a versão actual.',
 'enotif_lastvisited' => 'Consulte $1 para todas as alterações efectuadas desde a sua última visita.',
 'enotif_lastdiff' => 'Consulte $1 para ver esta alteração.',
 'enotif_anon_editor' => 'utilizador anónimo $1',
@@ -2497,6 +2546,8 @@ Consulte a [[Special:ProtectedPages|lista de páginas protegidas]] para ver as p
 'prot_1movedto2' => 'moveu [[$1]] para [[$2]]',
 'protect-badnamespace-title' => 'Espaço nominal não passível de protecção',
 'protect-badnamespace-text' => 'Páginas neste espaço nominal não podem ser protegidas.',
+'protect-norestrictiontypes-text' => 'Esta página não pode ser protegida porque não há nenhum tipo de restrição disponível.',
+'protect-norestrictiontypes-title' => 'Página não passível de protecção',
 'protect-legend' => 'Confirmar protecção',
 'protectcomment' => 'Motivo:',
 'protectexpiry' => 'Expiração:',
@@ -2778,6 +2829,7 @@ Contacte o seu fornecedor de internet ou o serviço de apoio técnico e informe-
 'proxyblocksuccess' => 'Concluído.',
 'sorbsreason' => "O seu endereço IP encontra-se listado como ''proxy'' aberto na DNSBL utilizada pela {{SITENAME}}.",
 'sorbs_create_account_reason' => "O seu endereço IP encontra-se listado como ''proxy'' aberto na DNSBL utilizada pela {{SITENAME}}. Não pode criar uma conta",
+'xffblockreason' => 'Um endereço IP presente no cabeçalho X-Forwardd-For, seja seu ou de um servidor de proxy que estiver a usar, foi bloqueado. A razão do bloqueio original foi: $1',
 'cant-block-while-blocked' => 'Não pode bloquear outros utilizadores enquanto estiver bloqueado.',
 'cant-see-hidden-user' => "O utilizador que está tentando bloquear já está bloqueado e oculto.
 Como não tem o privilégio para ocultar utilizadores ''(hideuser)'', não pode ver ou editar o bloqueio deste utilizador.",
@@ -2941,6 +2993,8 @@ Se deseja colaborar na localização genérica do MediaWiki, visite [//www.media
 'thumbnail-more' => 'Ampliar',
 'filemissing' => 'Ficheiro não encontrado',
 'thumbnail_error' => 'Erro ao criar miniatura: $1',
+'thumbnail_error_remote' => 'Mensagem de erro de $1 :
+$2',
 'djvu_page_error' => 'página DjVu inacessível',
 'djvu_no_xml' => 'Não foi possível aceder ao XML para o ficheiro DjVU',
 'thumbnail-temp-create' => 'Não foi possível criar o ficheiro temporário da miniatura',
@@ -2962,6 +3016,7 @@ Todas as acções de importação transwikis são registadas no [[Special:Log/im
 'import-interwiki-templates' => 'Incluir todas as predefinições',
 'import-interwiki-submit' => 'Importar',
 'import-interwiki-namespace' => 'Espaço nominal de destino:',
+'import-interwiki-rootpage' => 'Raiz da página de destino (opcional):',
 'import-upload-filename' => 'Nome do ficheiro:',
 'import-comment' => 'Comentário:',
 'importtext' => 'Exporte o ficheiro da wiki de origem utilizando a página especial [[Special:Export|exportação de páginas]].
@@ -2997,7 +3052,9 @@ Não há um directório temporário.',
 'import-error-interwiki' => 'A página "$1" não pode ser importada pois seu nome está reservado para um link externo (interwiki).',
 'import-error-special' => 'A página "$1" não pode ser importada porque ela pertence a um espaço nominal especial que não permite páginas.',
 'import-error-invalid' => 'A página "$1" não pode ser importada porque seu nome é inválido.',
+'import-error-unserialize' => 'Revisão $2 da página "$1" não pode ser desserializada. Foi relatado que a revisão usava o modelo de conteúdo $3 serializado como $4.',
 'import-options-wrong' => '{{PLURAL:$2|Opção errada|Opções erradas}}: <nowiki>$1</nowiki>',
+'import-rootpage-invalid' => 'A raiz da página dada é um título inválido.',
 
 # Import log
 'importlogpage' => 'Registo de importações',
index ffaa0cb..bec6bad 100644 (file)
@@ -533,6 +533,7 @@ See also:
 * {{msg-mw|Accesskey-ca-delete}}
 * {{msg-mw|Tooltip-ca-delete}}
 {{Identical|Delete}}',
+'undeletethispage' => 'In the Cologne Blue skin this is the text for link to undelete the page in admin view, at the bottom of the page.',
 'deletethispage' => 'In the Cologne Blue skin this is the text for link to delete the page in admin view, at the bottom of the page.
 {{Identical|Delete this page}}',
 'undelete_short' => "It is tab label. It's really can be named ''nstab-undelete''.",
@@ -651,7 +652,8 @@ See also:
 * {{msg-mw|Tooltip-n-currentevents}}',
 'currentevents-url' => "Target page of ''CurrentEvents'' in the sidebar. See also {{msg-mw|currentevents}}.
 {{doc-important|Do not translate the \"<tt>Project:</tt>\" part.}}",
-'disclaimers' => 'Used as display name for the link to [[{{MediaWiki:Disclaimerpage}}]] shown at the bottom of every page on the wiki. Example [[{{MediaWiki:Disclaimerpage}}|{{MediaWiki:Disclaimers}}]].',
+'disclaimers' => 'Used as display name for the link to [[{{MediaWiki:Disclaimerpage}}]] shown at the bottom of every page on the wiki. Example [[{{MediaWiki:Disclaimerpage}}|{{MediaWiki:Disclaimers}}]].
+{{Identical|Disclaimer}}',
 'disclaimerpage' => 'Used as page for that contains the site disclaimer. Used at the bottom of every page on the wiki. Example: [[{{MediaWiki:Disclaimerpage}}|{{MediaWiki:Disclaimers}}]].
 {{doc-important|Do not change the "<tt>Project:</tt>" part.}}',
 'edithelp' => 'This is the text that appears on the editing help link that is near the bottom of the editing page',
@@ -7699,7 +7701,8 @@ This property can come from xmp:Label in XMP ( http://www.adobe.com/content/dam/
 'exif-pngfilecomment' => 'See also:
 * {{msg-mw|Exif-pngfilecomment}}
 * {{msg-mw|Exif-giffilecomment}}',
-'exif-disclaimer' => 'Disclaimer for the image.',
+'exif-disclaimer' => 'Disclaimer for the image.
+{{Identical|Disclaimer}}',
 'exif-contentwarning' => 'Content warning for the image. For example if the image/media contains violent, sexual or otherwise offensive content.
 
 This comes from the png warning textual chunk. See http://www.w3.org/TR/PNG/#11keywords',
index a81f8b7..7fa1a6c 100644 (file)
@@ -870,11 +870,11 @@ Este posibil să fi reușit deja schimbarea parolei sau să fi cerut o parolă t
 
 # Special:PasswordReset
 'passwordreset' => 'Resetare parolă',
-'passwordreset-text' => 'Completați acest formular pentru a vă reseta parola.',
+'passwordreset-text-one' => 'Completați acest formular pentru a vă reseta parola.',
+'passwordreset-text-many' => '{{PLURAL:$1|Introduceți una din aceste informații pentru a vă reseta parola.}}',
 'passwordreset-legend' => 'Resetare parolă',
 'passwordreset-disabled' => 'Resetarea parolei a fost dezactivată pe acest wiki.',
 'passwordreset-emaildisabled' => 'Funcțiile de e-mail au fost dezactivate de pe acest wiki.',
-'passwordreset-pretext' => '{{PLURAL:$1| | Introduceți mai jos o parte din informații}}',
 'passwordreset-username' => 'Nume de utilizator:',
 'passwordreset-domain' => 'Domeniu:',
 'passwordreset-capture' => 'Vizualizați e-mailul rezultat?',
@@ -1116,6 +1116,7 @@ Nicio explicație furnizată.',
 Se pare că a fost ștearsă.',
 'edit-conflict' => 'Conflict de modificare.',
 'edit-no-change' => 'Modificarea dvs. a fost ignorată deoarece nu s-a efectuat nicio schimbare.',
+'postedit-confirmation' => 'Modificarea dumneavoastră a fost salvată.',
 'edit-already-exists' => 'Pagina nouă nu a putut fi creată.
 Ea există deja.',
 'defaultmessagetext' => 'Textul implicit',
index 199fca3..3a778fd 100644 (file)
@@ -535,6 +535,18 @@ $messages = array(
 'oct' => 'окт',
 'nov' => 'ноя',
 'dec' => 'дек',
+'january-date' => 'Январь $1',
+'february-date' => 'Февраль $1',
+'march-date' => 'Март $1',
+'april-date' => 'Апрель $1',
+'may-date' => 'Май $1',
+'june-date' => 'Июнь $1',
+'july-date' => 'Июль $1',
+'august-date' => 'Август $1',
+'september-date' => 'Сентябрь $1',
+'october-date' => 'Октябрь $1',
+'november-date' => 'Ноябрь $1',
+'december-date' => 'Декабрь $1',
 
 # Categories related messages
 'pagecategories' => '{{PLURAL:$1|Категория|Категории}}',
@@ -692,7 +704,7 @@ $1',
 'youhavenewmessages' => 'Вы получили $1 ($2).',
 'newmessageslink' => 'новые сообщения',
 'newmessagesdifflink' => 'последнее изменение',
-'youhavenewmessagesfromusers' => 'Вы получили $1 от {{PLURAL:$3|$3 участника|$3 участников|$3 участников}} ($2).',
+'youhavenewmessagesfromusers' => 'Вы получили $1 от {{PLURAL:$3|$3 участника|$3 участников}} ($2).',
 'youhavenewmessagesmanyusers' => 'Вы получили $1 от множества пользователей ($2).',
 'newmessageslinkplural' => '{{PLURAL:$1|новое сообщение|новые сообщения}}',
 'newmessagesdifflinkplural' => '{{PLURAL:$1|последнее изменение|последние изменения}}',
@@ -852,8 +864,8 @@ $2',
 'createacct-yourpasswordagain' => 'Подтвердите пароль',
 'createacct-yourpasswordagain-ph' => 'Введите пароль еще раз',
 'remembermypassword' => 'Помнить мою учётную запись на этом компьютере (не более $1 {{PLURAL:$1|дня|дней|дней}})',
-'userlogin-remembermypassword' => 'Ð\97апомниÑ\82Ñ\8c Ð¼ÐµÐ½Ñ\8f',
-'userlogin-signwithsecure' => 'Ð\92Ñ\8bполниÑ\82е Ð²Ñ\85од, Ð¸Ñ\81полÑ\8cзÑ\83Ñ\8f Ð±ÐµÐ·Ð¾Ð¿Ð°Ñ\81нÑ\8bй Ñ\81еÑ\80веÑ\80',
+'userlogin-remembermypassword' => 'Ð\9eÑ\81Ñ\82аваÑ\82Ñ\8cÑ\81Ñ\8f Ð² Ñ\81иÑ\81Ñ\82еме',
+'userlogin-signwithsecure' => 'Ð\97аÑ\89иÑ\89Ñ\91нное Ñ\81оединение',
 'securelogin-stick-https' => 'Продолжить подключение по HTTPS после входа',
 'yourdomainname' => 'Ваш домен:',
 'password-change-forbidden' => 'Вы не можете изменить пароль в этой вики.',
@@ -875,6 +887,7 @@ $2',
 'gotaccountlink' => 'Представьтесь',
 'userlogin-resetlink' => 'Забыли данные для входа?',
 'userlogin-resetpassword-link' => 'Сброс пароля',
+'helplogin-url' => 'Help:Вход в систему',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Помощь со входом в систему]]',
 'createacct-join' => 'Введите свои данные ниже.',
 'createacct-emailrequired' => 'Адрес электронной почты',
@@ -884,13 +897,14 @@ $2',
 'createacct-realname' => 'Настоящее имя (необязательно)',
 'createaccountreason' => 'Причина:',
 'createacct-reason' => 'Причина',
+'createacct-reason-ph' => 'Зачем вы создаёте другую учетную запись',
 'createacct-captcha' => 'Проверка безопасности',
 'createacct-imgcaptcha-ph' => 'Введите текст, который вы видите выше',
 'createacct-submit' => 'Зарегистрировать учётную запись',
 'createacct-benefit-heading' => '{{SITENAME}} сделана такими же людьми, как вы.',
 'createacct-benefit-body1' => '{{PLURAL:$1|правка|правки|правок}}',
-'createacct-benefit-body2' => '{{PLURAL:$1|страница|страницы|страниц}}',
-'createacct-benefit-body3' => '{{PLURAL:$1|Ñ\83Ñ\87аÑ\81Ñ\82ник|Ñ\83Ñ\87аÑ\81Ñ\82ников|Ñ\83Ñ\87аÑ\81Ñ\82ника}} Ð² последнее время',
+'createacct-benefit-body2' => '{{PLURAL:$1|статья|статьи|статей}}',
+'createacct-benefit-body3' => '{{PLURAL:$1|Ñ\83Ñ\87аÑ\81Ñ\82ник|Ñ\83Ñ\87аÑ\81Ñ\82ника|Ñ\83Ñ\87аÑ\81Ñ\82ников}} Ð·Ð° последнее время',
 'badretype' => 'Введённые вами пароли не совпадают.',
 'userexists' => 'Введённое имя участника уже используется.
 Пожалуйста, выберите другое имя.',
@@ -984,9 +998,12 @@ $2',
 'resetpass-wrong-oldpass' => 'Неправильный временный или текущий пароль.
 Возможно, вы уже успешно изменили пароль, или запросили новый временный пароль.',
 'resetpass-temp-password' => 'Временный пароль:',
+'resetpass-abort-generic' => 'Изменение пароля было прервано расширением.',
 
 # Special:PasswordReset
 'passwordreset' => 'Сброс пароля',
+'passwordreset-text-one' => 'Заполните эту форму, чтобы сбросить свой пароль.',
+'passwordreset-text-many' => '{{PLURAL:$1|Введите одну из частей данных для сброса пароля.}}',
 'passwordreset-legend' => 'Сбросить пароль',
 'passwordreset-disabled' => 'Сбросы пароля отключены на этой вики.',
 'passwordreset-emaildisabled' => 'Функции электронной почты отключены в этой вики.',
@@ -1018,7 +1035,7 @@ $2
 Временный пароль: $2',
 'passwordreset-emailsent' => 'Отправлено электронное письмо с информацией о сбросе пароля.',
 'passwordreset-emailsent-capture' => 'Отправлено электронное письмо с информацией о сбросе пароля, текст которого можно увидеть ниже.',
-'passwordreset-emailerror-capture' => 'Было создано электронное письмо с информацией о сбросе пароля, текст которого можно увидеть ниже. Однако его не удалось отправить по следующей причине: $1',
+'passwordreset-emailerror-capture' => 'Было создано электронное письмо с информацией о сбросе пароля, текст которого можно увидеть ниже, однако его не удалось отправить {{GENDER:$2|участнику|участнице}} по следующей причине: $1',
 
 # Special:ChangeEmail
 'changeemail' => 'Изменить адрес электронной почты',
@@ -1233,6 +1250,7 @@ $2
 Вероятно, она была удалена.',
 'edit-conflict' => 'Конфликт редактирования.',
 'edit-no-change' => 'Ваша правка была отклонена, так как в тексте не было сделано изменений.',
+'postedit-confirmation' => 'Ваша правка сохранена.',
 'edit-already-exists' => 'Невозможно создать новую страницу.
 Она уже существует.',
 'defaultmessagetext' => 'Текст по умолчанию',
@@ -1240,7 +1258,7 @@ $2
 'invalid-content-data' => 'Недопустимые данные',
 'content-not-allowed-here' => 'Содержимое "$1" недопустимо на странице [[$2]]',
 'editwarning-warning' => 'Переход на другую страницу может привести к потере сделанных вами изменений.
-Если вы зарегистрированы в системе, то вы можете отключить это предупреждение в разделе «{{int:prefs-editing}}» ваших настроек.',
+Если вы зарегистрированы в системе, то вы можете отключить это предупреждение в разделе «Редактирование» ваших настроек.',
 
 # Content models
 'content-model-wikitext' => 'вики-текст',
@@ -1666,6 +1684,7 @@ $1",
 'userrights-notallowed' => 'С вашей учётной записи не разрешено добавлять и удалять права участников.',
 'userrights-changeable-col' => 'Группы, которые вы можете изменять',
 'userrights-unchangeable-col' => 'Группы, которые вы не можете изменять',
+'userrights-conflict' => 'Конфликт прав участника! Пожалуйста, примените изменения заново.',
 
 # Groups
 'group' => 'Группа:',
@@ -2490,7 +2509,7 @@ $1',
 'notvisiblerev' => 'Версия была удалена',
 'watchnochange' => 'За этот период ни одна страница из списка наблюдения не менялась.',
 'watchlist-details' => 'В вашем списке наблюдения $1 {{PLURAL:$1|страница|страницы|страниц}}, не считая страниц обсуждения.',
-'wlheader-enotif' => 'Уведомление по эл. почте включено.',
+'wlheader-enotif' => 'Уведомления по эл. почте включены.',
 'wlheader-showupdated' => "Страницы, изменившиеся с вашего последнего их посещения, выделены '''жирным''' шрифтом.",
 'watchmethod-recent' => 'просмотр последних изменений для наблюдаемых страниц',
 'watchmethod-list' => 'просмотр наблюдаемых страниц для последних изменений',
@@ -3065,6 +3084,8 @@ $1',
 'thumbnail-more' => 'Увеличить',
 'filemissing' => 'Файл не найден',
 'thumbnail_error' => 'Ошибка создания миниатюры: $1',
+'thumbnail_error_remote' => 'Сообщение об ошибке от $1:
+$2',
 'djvu_page_error' => 'Номер страницы DjVu вне досягаемости',
 'djvu_no_xml' => 'Невозможно получить XML для DjVu',
 'thumbnail-temp-create' => 'Не удаётся создать временный файл эскиза',
@@ -3393,11 +3414,25 @@ $1',
 'minutes' => '{{PLURAL:$1|$1 минута|$1 минуты|$1 минут}}',
 'hours' => '{{PLURAL:$1|$1 час|$1 часа|$1 часов}}',
 'days' => '{{PLURAL:$1|$1 день|$1 дня|$1 дней}}',
+'weeks' => '{{PLURAL:$1|$1 неделя|$1 недели|$1 недель}}',
 'months' => '{{PLURAL:$1|$1 месяц|$1 месяца|$1 месяцев}}',
 'years' => '{{PLURAL:$1|$1 год|$1 года|$1 лет}}',
 'ago' => '$1 назад',
 'just-now' => 'только что',
 
+# Human-readable timestamps
+'hours-ago' => '$1 {{PLURAL:$1|час|часа|часов}} назад',
+'minutes-ago' => '$1 {{PLURAL:$1|минуту|минуты|минут}} назад',
+'seconds-ago' => '$1 {{PLURAL:$1|секунду|секунды|секунд}} назад',
+'monday-at' => 'в понедельник в $1',
+'tuesday-at' => 'во вторник в $1',
+'wednesday-at' => 'в среду в $1',
+'thursday-at' => 'в четверг в $1',
+'friday-at' => 'в пятницу в $1',
+'saturday-at' => 'в субботу в $1',
+'sunday-at' => 'в воскресенье в $1',
+'yesterday-at' => 'Вчера в $1',
+
 # Bad image list
 'bad_image_list' => 'Формат должен быть следующим:
 
@@ -4102,6 +4137,18 @@ MediaWiki распространяется в надежде, что она бу
 'version-entrypoints-articlepath' => '[https://www.mediawiki.org/wiki/Manual:$wgArticlePath Путь к статье]',
 'version-entrypoints-scriptpath' => '[https://www.mediawiki.org/wiki/Manual:$wgScriptPath Путь к скрипту]',
 
+# Special:Redirect
+'redirect' => 'Перенаправление с файла, участника или идентификатора версии',
+'redirect-legend' => 'Перенаправление на файл или страницу',
+'redirect-summary' => 'Эта специальная страница перенаправляет на файл (с имени файла), страницу (с идентификатора версии) или страницу участника (с числового идентификатора участника).',
+'redirect-submit' => 'Перейти',
+'redirect-lookup' => 'Поиск:',
+'redirect-value' => 'Значение:',
+'redirect-user' => 'Идентификатор участника',
+'redirect-revision' => 'Версия страницы',
+'redirect-file' => 'Название файла',
+'redirect-not-exists' => 'Значение не найдено',
+
 # Special:FileDuplicateSearch
 'fileduplicatesearch' => 'Поиск одинаковых файлов',
 'fileduplicatesearch-summary' => 'Поиск одинаковых файлов по хэш-коду.',
@@ -4193,6 +4240,7 @@ MediaWiki распространяется в надежде, что она бу
 'htmlform-selectorother-other' => 'Иное',
 'htmlform-no' => 'Нет',
 'htmlform-yes' => 'Да',
+'htmlform-chosen-placeholder' => 'Выберите вариант',
 
 # SQLite database support
 'sqlite-has-fts' => '$1 с поддержкой полнотекстового поиска',
index 332e052..a5df584 100644 (file)
@@ -151,6 +151,18 @@ $messages = array(
 'oct' => 'Алт',
 'nov' => 'Сэт',
 'dec' => 'Ахс',
+'january-date' => 'Тохсунньу $1',
+'february-date' => 'Олунньу $1',
+'march-date' => 'Кулун тутар $1',
+'april-date' => 'Муус устар $1',
+'may-date' => 'Ыам ыйын $1',
+'june-date' => 'Бэс ыйын $1',
+'july-date' => 'От ыйын $1',
+'august-date' => 'Атырдьах ыйын $1',
+'september-date' => 'Балаҕан ыйын $1',
+'october-date' => 'Алтынньы $1',
+'november-date' => 'Сэтинньи $1',
+'december-date' => 'Ахсынньы $1',
 
 # Categories related messages
 'pagecategories' => '{{PLURAL:$1|Категория|Категориялар}}',
@@ -460,10 +472,13 @@ $2',
 'yourpassword' => 'Киирии тыла:',
 'userlogin-yourpassword' => 'Киирии тыл',
 'userlogin-yourpassword-ph' => 'Киирии тылгын суруй',
+'createacct-yourpassword-ph' => 'Киирии тылгын суруй',
 'yourpasswordagain' => 'Киирии тылгын хатылаа:',
+'createacct-yourpasswordagain' => 'Киирии тылгын бигэргэт',
+'createacct-yourpasswordagain-ph' => 'Киирии тылгын хатылаа',
 'remembermypassword' => 'Миигин бу көмпүүтэргэ сигээ ($1 {{PLURAL:$1|күн|күнтэн ордуга суох}})',
-'userlogin-remembermypassword' => 'Ð\94олоÒ\95ойгоÑ\80 Ñ\85аÑ\82аа',
-'userlogin-signwithsecure' => 'Ð\91игÑ\8d Ñ\81иÑ\8dÑ\80бÑ\8dÑ\80и Ñ\82Ñ\83һанан ÐºÐ¸Ð¸Ñ\80ии',
+'userlogin-remembermypassword' => 'ТиһиликÑ\82Ñ\8dн Ñ\82аÑ\85Ñ\81Ñ\8bма',
+'userlogin-signwithsecure' => 'Ð\91игÑ\8d Ñ\85олбонÑ\83Ñ\83',
 'securelogin-stick-https' => 'Киирэн баран HTTPS нөҥүө холбонууну салгыырга',
 'yourdomainname' => 'Эн дөмүөнүҥ:',
 'password-change-forbidden' => 'Бу биикигэ киирии тылы уоарытар табыллыбат.',
@@ -484,14 +499,30 @@ $2',
 'gotaccount' => "Бэлиэтэммитиҥ дуо? '''$1'''.",
 'gotaccountlink' => 'Аатыҥ',
 'userlogin-resetlink' => 'Киирэр тылгын умнубуккун дуо?',
+'userlogin-resetpassword-link' => 'Киирии тылы уларытыы',
 'helplogin-url' => 'Help:Бэлиэ-ааты киллэрии',
 'userlogin-helplink' => '[[{{MediaWiki:helplogin-url}}|Бэлиэтэниигэ көмө]]',
+'createacct-join' => 'Аллара суруй.',
+'createacct-emailrequired' => 'Email аадырыс',
+'createacct-emailoptional' => 'Email аадырыс (булгуччута суох)',
+'createacct-email-ph' => 'Эл аадырыскын суруй',
 'createaccountmail' => 'Быстах киирии тылы туһаныы уонна ону email-ынан ыытыы',
+'createacct-realname' => 'Дьиҥнээх аатыҥ (булгуччута суох)',
 'createaccountreason' => 'Төрүөтэ:',
+'createacct-reason' => 'Төрүөтэ',
+'createacct-reason-ph' => 'Саҥа аатынан тоҕо киирэҕиний',
+'createacct-captcha' => 'Куттал суох буолуутун тургутуу',
+'createacct-imgcaptcha-ph' => 'Үөһэ көстөрү хатылаа',
+'createacct-submit' => 'Бэлиэтэнии',
+'createacct-benefit-heading' => '{{SITENAME}} ситим-сири эн курдук дьон оҥороллор.',
+'createacct-benefit-body1' => '{{PLURAL:$1|уларытыы|уларытыы}}',
+'createacct-benefit-body2' => '{{PLURAL:$1|сирэй|сирэй}}',
+'createacct-benefit-body3' => 'кэнники {{PLURAL:$1|кыттааччы|кыттааччы}}',
 'badretype' => 'Киирии тылларыҥ сөп түбэспэтилэр.',
 'userexists' => 'Суруйбут аатыҥ бэлиэр баар.
 Бука диэн, атын аатта тал.',
 'loginerror' => 'Ааккын система билбэтэ',
+'createacct-error' => 'Бэлиэтэнии кэмигэр алҕас таҕыста',
 'createaccounterror' => 'Саҥа аат бэлиэтиир кыах суох: $1',
 'nocookiesnew' => 'Маннык ааттаах кыттааччы баар буолла гынан баран, систиэмэҕэ киирэ илик. {{SITENAME}} «cookies» туттар, оттон эн көмпүүтэргэр ону туһанар бобуллубут. Бука диэн «cookies» холбоо, онтон өссө киирэн көр.',
 'nocookieslogin' => '{{SITENAME}} дьону билэргэ «cookies» туттар. Эн көмпүүтэргэр «cookies» бобуллубут. Ону холбоон баран өссө киирэн көр.',
index ce6edc4..fdbf933 100644 (file)
@@ -3166,6 +3166,7 @@ Ovo je verovatno izazvano vezom do spoljašnjeg sajta koji se nalazi na crnoj li
 'pageinfo-robot-noindex' => 'Ne može da se popiše',
 'pageinfo-views' => 'Broj pregleda',
 'pageinfo-watchers' => 'Broj nadgledača stranica',
+'pageinfo-few-watchers' => 'Manje od $1 {{PLURAL:$1|pratioca|pratilaca}}',
 'pageinfo-redirects-name' => 'Preusmeravanja na stranicu',
 'pageinfo-subpages-name' => 'Podstranice ove stranice',
 'pageinfo-subpages-value' => '$1 ($2 {{PLURAL:$2|preusmerenje|preusmerenja|preusmerenja}}; $3 {{PLURAL:$3|nepreusmerenje|nepreusmerenja|nepreusmerenja}})',
index e07655d..d5d7679 100644 (file)
@@ -194,7 +194,7 @@ $dateFormats = array(
        'ISO 8601 both' => 'xnY-xnm-xnd"T"xnH:xni:xns',
 );
 
-$linkTrail = '/^([a-z]+)(.*)\$/sD';
+$linkTrail = '/^([a-z]+)(.*)$/sD';
 
 $messages = array(
 # User preference toggles
index 07171fb..62bf24f 100644 (file)
@@ -880,6 +880,8 @@ Có thể bạn đã thay đổi thành công mật khẩu của mình hoặc đ
 
 # Special:PasswordReset
 'passwordreset' => 'Tái tạo mật khẩu',
+'passwordreset-text-one' => 'Hãy điền mẫu đơn này để tái tạo mật khẩu.',
+'passwordreset-text-many' => '{{PLURAL:$1|Nhập một trong những chi tiết sau để táo tạo mật khẩu.}}',
 'passwordreset-legend' => 'Tái tạo mật khẩu',
 'passwordreset-disabled' => 'Chức năng tái tạo mật khẩu đã bị tắt trên wiki này.',
 'passwordreset-emaildisabled' => 'Tính năng gửi thư điện tử không được kích hoạt trên wiki này.',
index 967887c..f69f53f 100644 (file)
@@ -826,6 +826,8 @@ $2',
 
 # Special:PasswordReset
 'passwordreset' => '重新設定密碼',
+'passwordreset-text-one' => '完成此表格以重新設定您的密碼。',
+'passwordreset-text-many' => '{{PLURAL:$1|輸入其中一項資料以重新設定您的密碼。}}',
 'passwordreset-legend' => '重設密碼',
 'passwordreset-disabled' => '此維基上已禁止了重設密碼。',
 'passwordreset-emaildisabled' => '電子郵件功能在此 wiki 上已禁用。',
index 59ea18f..22452fd 100644 (file)
@@ -1663,9 +1663,9 @@ ggp
 ghostscript
 gimpbaseenums
 git
+gitblit
 gitdir
 github
-gitweb
 global
 globalauth
 globalblock
index 10984b0..866ec5a 100644 (file)
@@ -53,9 +53,13 @@ $maintenance->setup();
 // to $maintenance->mSelf. Keep that here for b/c
 $self = $maintenance->getName();
 
-// Detect compiled mode
+# Load composer's autoloader if present
+if ( is_readable( "$IP/vendor/autoload.php" ) ) {
+       require_once "$IP/vendor/autoload.php";
+}
 # Get the MWInit class
 require_once "$IP/includes/Init.php";
+# Start the autoloader, so that extensions can derive classes from core files
 require_once "$IP/includes/AutoLoader.php";
 # Stub the profiler
 require_once "$IP/includes/profiler/Profiler.php";
index 7fca651..7c24f0f 100644 (file)
@@ -52,6 +52,8 @@ class EditCLI extends Maintenance {
                $noRC = $this->hasOption( 'no-rc' );
 
                $wgUser = User::newFromName( $userName );
+               $context = RequestContext::getMain();
+               $context->setUser( $wgUser );
                if ( !$wgUser ) {
                        $this->error( "Invalid username", true );
                }
@@ -63,6 +65,7 @@ class EditCLI extends Maintenance {
                if ( !$wgTitle ) {
                        $this->error( "Invalid title", true );
                }
+               $context->setTitle( $wgTitle );
 
                $page = WikiPage::factory( $wgTitle );
 
diff --git a/maintenance/eraseArchivedFile.php b/maintenance/eraseArchivedFile.php
new file mode 100644 (file)
index 0000000..4b06513
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+/**
+ * Delete archived (non-current) files from storage
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Maintenance
+ * @author Aaron Schulz
+ */
+
+require_once( __DIR__ . '/Maintenance.php' );
+
+/**
+ * Maintenance script to delete archived (non-current) files from storage.
+ *
+ * @TODO: Maybe add some simple logging
+ *
+ * @ingroup Maintenance
+ * @since 1.22
+ */
+class EraseArchivedFile extends Maintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->mDescription = "Erases traces of deleted files.";
+               $this->addOption( 'delete', 'Perform the deletion' );
+               $this->addOption( 'filename', 'File name', false, true );
+               $this->addOption( 'filekey', 'File storage key (with extension) or "*"', true, true );
+       }
+
+       public function execute() {
+               if ( !$this->hasOption( 'delete' ) ) {
+                       $this->output( "Use --delete to actually confirm this script\n" );
+               }
+
+               $filekey = $this->getOption( 'filekey' );
+               $filename = $this->getOption( 'filename' );
+
+               if ( $filekey === '*' ) { // all versions by name
+                       if ( !strlen( $filename ) ) {
+                               $this->error( "Missing --filename parameter.", 1 );
+                       }
+                       $afile = false;
+               } else { // specified version
+                       $dbw = wfGetDB( DB_MASTER );
+                       $row = $dbw->selectRow( 'filearchive', '*',
+                               array( 'fa_storage_group' => 'deleted', 'fa_storage_key' => $filekey ),
+                               __METHOD__ );
+                       if ( !$row ) {
+                               $this->error( "No deleted file exists with key '$filekey'.", 1 );
+                       }
+                       $filename = $row->fa_name;
+                       $afile = ArchivedFile::newFromRow( $row );
+               }
+
+               $file = wfLocalFile( $filename );
+               if ( $file->exists() ) {
+                       $this->error( "File '$filename' is still a public file, use the delete form.\n", 1 );
+               }
+
+               $this->output( "Purging all thumbnails for file '$filename'..." );
+               $file->purgeCache();
+               $file->purgeHistory();
+               $this->output( "done.\n" );
+
+               if ( $afile instanceof ArchivedFile ) {
+                       $this->scrubVersion( $afile );
+               } else {
+                       $this->output( "Finding deleted versions of file '$filename'...\n" );
+                       $this->scrubAllVersions( $filename );
+                       $this->output( "Done\n" );
+               }
+       }
+
+       protected function scrubAllVersions( $name ) {
+               $dbw = wfGetDB( DB_MASTER );
+               $res = $dbw->select( 'filearchive', '*',
+                       array( 'fa_name' => $name, 'fa_storage_group' => 'deleted' ),
+                       __METHOD__ );
+               foreach ( $res as $row ) {
+                       $this->scrubVersion( ArchivedFile::newFromRow( $row ) );
+               }
+       }
+
+       protected function scrubVersion( ArchivedFile $archivedFile ) {
+               $key = $archivedFile->getStorageKey();
+               $name = $archivedFile->getName();
+               $ts = $archivedFile->getTimestamp();
+               $repo = RepoGroup::singleton()->getLocalRepo();
+               $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key;
+               if ( $this->hasOption( 'delete' ) ) {
+                       $status = $repo->getBackend()->delete( array( 'src' => $path ) );
+                       if ( $status->isOK() ) {
+                               $this->output( "Deleted version '$key' ($ts) of file '$name'\n" );
+                       } else {
+                               $this->output( "Failed to delete version '$key' ($ts) of file '$name'\n" );
+                               $this->output( print_r( $status->getErrorsArray(), true ) );
+                       }
+               } else {
+                       $this->output( "Would delete version '{$key}' ({$ts}) of file '$name'\n" );
+               }
+       }
+}
+
+$maintClass = "EraseArchivedFile";
+require_once( RUN_MAINTENANCE_IF_MAIN );
index 68e1fb2..df927b3 100644 (file)
@@ -21,6 +21,7 @@
                                "classes": [
                                        "mw.Title",
                                        "mw.notification",
+                                       "mw.user",
                                        "mw.util",
                                        "mw.plugin.*"
                                ]
index 4ad75f0..46033fc 100644 (file)
@@ -14,6 +14,7 @@
                "../../resources/mediawiki/mediawiki.Title.js",
                "../../resources/mediawiki/mediawiki.notify.js",
                "../../resources/mediawiki/mediawiki.notification.js",
+               "../../resources/mediawiki/mediawiki.user.js",
                "../../resources/mediawiki.api",
                "../../resources/jquery/jquery.localize.js"
        ]
index bc7a7d4..3a07553 100644 (file)
@@ -237,6 +237,7 @@ $wgMessageStructure = array(
                'create-this-page',
                'delete',
                'deletethispage',
+               'undeletethispage',
                'undelete_short',
                'viewdeleted_short',
                'protect',
index 2d75722..24c66b1 100644 (file)
@@ -77,6 +77,9 @@ class RemoveUnusedAccounts extends Maintenance {
                        $this->output( "\nDeleting inactive accounts..." );
                        $dbw = wfGetDB( DB_MASTER );
                        $dbw->delete( 'user', array( 'user_id' => $del ), __METHOD__ );
+                       $dbw->delete( 'user_groups', array( 'ug_user' => $del ), __METHOD__ );
+                       $dbw->delete( 'user_former_groups', array( 'ufg_user' => $del ), __METHOD__ );
+                       $dbw->delete( 'user_properties', array( 'up_user' => $del ), __METHOD__ );
                        $dbw->delete( 'logging', array( 'log_user' => $del ), __METHOD__ );
                        $dbw->delete( 'recentchanges', array( 'rc_user' => $del ), __METHOD__ );
                        $this->output( "done.\n" );
index 216db93..a628b0b 100644 (file)
@@ -34,16 +34,42 @@ class MwSql extends Maintenance {
                parent::__construct();
                $this->mDescription = "Send SQL queries to a MediaWiki database";
                $this->addOption( 'cluster', 'Use an external cluster by name', false, true );
+               $this->addOption( 'slave', 'Use a slave server (either "any" or by name)', false, true );
        }
 
        public function execute() {
-               // Get a DB handle (with this wiki's DB select) from the appropriate load balancer
+               // Get the appropriate load balancer (for this wiki)
                if ( $this->hasOption( 'cluster' ) ) {
                        $lb = wfGetLBFactory()->getExternalLB( $this->getOption( 'cluster' ) );
-                       $dbw = $lb->getConnection( DB_MASTER ); // master for external LB
                } else {
-                       $dbw = wfGetDB( DB_MASTER ); // master for primary LB for this wiki
+                       $lb = wfGetLB();
                }
+               // Figure out which server to use
+               if ( $this->hasOption( 'slave' ) ) {
+                       $server = $this->getOption( 'slave' );
+                       if ( $server === 'any' ) {
+                               $index = DB_SLAVE;
+                       } else {
+                               $index = null;
+                               for ( $i = 0; $i < $lb->getServerCount(); ++$i ) {
+                                       if ( $lb->getServerName( $i ) === $server ) {
+                                               $index = $i;
+                                               break;
+                                       }
+                               }
+                               if ( $index === null ) {
+                                       $this->error( "No slave server configured with the name '$server'.", 1 );
+                               }
+                       }
+               } else {
+                       $index = DB_MASTER;
+               }
+               // Get a DB handle (with this wiki's DB selected) from the appropriate load balancer
+               $dbw = $lb->getConnection( $index );
+               if ( $this->hasOption( 'slave' ) && $dbw->getLBInfo( 'master' ) !== null ) {
+                       $this->error( "The server selected ({$dbw->getServer()}) is not a slave.", 1 );
+               }
+
                if ( $this->hasArg( 0 ) ) {
                        $file = fopen( $this->getArg( 0 ), 'r' );
                        if ( !$file ) {
index 90f613e..90c6c95 100644 (file)
@@ -596,6 +596,9 @@ return array(
                        'user.tokens',
                ),
        ),
+       'mediawiki.icon' => array(
+               'styles' => 'resources/mediawiki/mediawiki.icon.css',
+       ),
        'mediawiki.debug' => array(
                'scripts' => 'resources/mediawiki/mediawiki.debug.js',
                'styles' => 'resources/mediawiki/mediawiki.debug.css',
@@ -910,7 +913,7 @@ return array(
        ),
        'mediawiki.special.changeslist' => array(
                'styles' => 'resources/mediawiki.special/mediawiki.special.changeslist.css',
-               'dependencies' => array( 'jquery.makeCollapsible' ),
+               'dependencies' => array( 'jquery.makeCollapsible', 'mediawiki.icon' ),
        ),
        'mediawiki.special.movePage' => array(
                'scripts' => 'resources/mediawiki.special/mediawiki.special.movePage.js',
index b0bd685..2da022c 100644 (file)
@@ -6,7 +6,7 @@
        /* Private Members */
 
        /**
-        * @var profileCache {Object} Keyed by userAgent string,
+        * @var {Object} profileCache Keyed by userAgent string,
         * value is the parsed $.client.profile object for that user agent.
         */
        var profileCache = {};
@@ -18,9 +18,9 @@
                /**
                 * Get an object containing information about the client.
                 *
-                * @param nav {Object} An object with atleast a 'userAgent' and 'platform' key.
+                * @param {Object} nav An object with atleast a 'userAgent' and 'platform' key.
                 * Defaults to the global Navigator object.
-                * @return {Object} The resulting client object will be in the following format:
+                * @returns {Object} The resulting client object will be in the following format:
                 *  {
                 *   'name': 'firefox',
                 *   'layout': 'gecko',
                                        // Generic version digit
                                        x = 'x',
                                        // Strings found in user agent strings that need to be conformed
-                                       wildUserAgents = ['Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3'],
+                                       wildUserAgents = ['Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3', 'Iceweasel'],
                                        // Translations for conforming user agent strings
                                        userAgentTranslations = [
                                                // Tons of browsers lie about being something they are not
-                                               [/(Firefox|MSIE|KHTML,\slike\sGecko|Konqueror)/, ''],
+                                               [/(Firefox|MSIE|KHTML,?\slike\sGecko|Konqueror)/, ''],
                                                // Chrome lives in the shadow of Safari still
                                                ['Chrome Safari', 'Chrome'],
                                                // KHTML is the layout engine not the browser - LIES!
                                        // version detectection
                                        versionPrefixes = [
                                                'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'netscape6', 'opera', 'version', 'konqueror',
-                                               'lynx', 'msie', 'safari', 'ps3'
+                                               'lynx', 'msie', 'safari', 'ps3', 'android'
                                        ],
                                        // Used as matches 2, 3 and 4 in version extraction - 3 is used as actual version number
                                        versionSuffix = '(\\/|\\;?\\s|)([a-z0-9\\.\\+]*?)(\\;|dev|rel|\\)|\\s|$)',
                                        // Names of known browsers
                                        names = [
                                                'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'konqueror', 'lynx', 'msie', 'opera',
-                                               'safari', 'ipod', 'iphone', 'blackberry', 'ps3', 'rekonq'
+                                               'safari', 'ipod', 'iphone', 'blackberry', 'ps3', 'rekonq', 'android'
                                        ],
                                        // Tanslations for conforming browser names
                                        nameTranslations = [],
                },
 
                /**
-                * Checks the current browser against a support map object to determine if the browser has been black-listed or
-                * not. If the browser was not configured specifically it is assumed to work. It is assumed that the body
-                * element is classified as either "ltr" or "rtl". If neither is set, "ltr" is assumed.
+                * Checks the current browser against a support map object.
                 *
                 * A browser map is in the following format:
                 * {
+                *   // Multiple rules with configurable operators
+                *   'msie': [['>=', 7], ['!=', 9]],
+                *    // Match no versions
+                *   'iphone': false,
+                *    // Match any version
+                *   'android': null
+                * }
+                *
+                * It can optionally be split into ltr/rtl sections:
+                * {
                 *   'ltr': {
-                *     // Multiple rules with configurable operators
-                *     'msie': [['>=', 7], ['!=', 9]],
-                *      // Blocked entirely
+                *     'android': null,
                 *     'iphone': false
                 *   },
                 *   'rtl': {
-                *     // Test against a string
-                *     'msie': [['!==', '8.1.2.3']],
-                *     // RTL rules do not fall through to LTR rules, you must explicity set each of them
+                *     'android': false,
+                *     // rules are not inherited from ltr
                 *     'iphone': false
                 *   }
                 * }
                 *
-                * @param map {Object} Browser support map
-                * @param profile {Object} (optional) a client-profile object.
+                * @param {Object} map Browser support map
+                * @param {Object} [profile] A client-profile object
+                * @param {boolean} [exactMatchOnly=false] Only return true if the browser is matched, otherwise
+                * returns true if the browser is not found.
                 *
-                * @return Boolean true if browser known or assumed to be supported, false if blacklisted
+                * @returns {boolean} The current browser is in the support map
                 */
-               test: function ( map, profile ) {
+               test: function ( map, profile, exactMatchOnly ) {
                        /*jshint evil: true */
 
                        var conditions, dir, i, op, val;
                        profile = $.isPlainObject( profile ) ? profile : $.client.profile();
-                       dir = $( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr';
+                       if ( map.ltr && map.rtl ) {
+                               dir = $( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr';
+                               map = map[dir];
+                       }
                        // Check over each browser condition to determine if we are running in a compatible client
-                       if ( typeof map[dir] !== 'object' || map[dir][profile.name] === undefined ) {
-                               // Unknown, so we assume it's working
-                               return true;
+                       if ( typeof map !== 'object' || map[profile.name] === undefined ) {
+                               // Not found, return true if exactMatchOnly not set, false otherwise
+                               return !exactMatchOnly;
                        }
-                       conditions = map[dir][profile.name];
+                       conditions = map[profile.name];
                        if ( conditions === false ) {
+                               // Match no versions
                                return false;
                        }
+                       if ( conditions === null ) {
+                               // Match all versions
+                               return true;
+                       }
                        for ( i = 0; i < conditions.length; i++ ) {
                                op = conditions[i][0];
                                val = conditions[i][1];
index a552237..405c600 100644 (file)
 
        }
 
+       /**
+        * Sets the sort count of the columns that are not affected by the sorting to have them sorted
+        * in default (ascending) order when their header cell is clicked the next time.
+        *
+        * @param {jQuery} $headers
+        * @param {Number[][]} sortList
+        * @param {Number[][]} headerToColumns
+        */
+       function setHeadersOrder( $headers, sortList, headerToColumns ) {
+               // Loop through all headers to retrieve the indices of the columns the header spans across:
+               $.each( headerToColumns, function( headerIndex, columns ) {
+
+                       $.each( columns, function( i, columnIndex ) {
+                               var header = $headers[headerIndex];
+
+                               if ( !isValueInArray( columnIndex, sortList ) ) {
+                                       // Column shall not be sorted: Reset header count and order.
+                                       header.order = 0;
+                                       header.count = 0;
+                               } else {
+                                       // Column shall be sorted: Apply designated count and order.
+                                       $.each( sortList, function( j, sortColumn ) {
+                                               if ( sortColumn[0] === i ) {
+                                                       header.order = sortColumn[1];
+                                                       header.count = sortColumn[1] + 1;
+                                                       return false;
+                                               }
+                                       } );
+                               }
+                       } );
+
+               } );
+       }
+
        function isValueInArray( v, a ) {
                var l = a.length;
                for ( var i = 0; i < l; i++ ) {
                $.each( sortObjects, function( i, sortObject ) {
                        $.each ( sortObject, function( columnIndex, order ) {
                                var orderIndex = ( order === 'desc' ) ? 1 : 0;
-                               sortList.push( [columnIndex, orderIndex] );
+                               sortList.push( [parseInt( columnIndex, 10 ), orderIndex] );
                        } );
                } );
                return sortList;
                                                                }
                                                        }
 
+                                                       // Reset order/counts of cells not affected by sorting
+                                                       setHeadersOrder( $headers, config.sortList, headerToColumns );
+
                                                        // Set CSS for headers
                                                        setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, columnToHeader );
                                                        appendToTable(
                                                        sortList = convertSortList( sortList );
                                                }
 
+                                               // Set each column's sort count to be able to determine the correct sort
+                                               // order when clicking on a header cell the next time
+                                               setHeadersOrder( $headers, sortList, headerToColumns );
+
                                                // re-build the cache for the tbody cells
                                                cache = buildCache( table );
 
diff --git a/resources/mediawiki.special/images/arrow-collapsed-ltr.png b/resources/mediawiki.special/images/arrow-collapsed-ltr.png
deleted file mode 100644 (file)
index ea9c67a..0000000
Binary files a/resources/mediawiki.special/images/arrow-collapsed-ltr.png and /dev/null differ
diff --git a/resources/mediawiki.special/images/arrow-collapsed-rtl.png b/resources/mediawiki.special/images/arrow-collapsed-rtl.png
deleted file mode 100644 (file)
index 081d3a8..0000000
Binary files a/resources/mediawiki.special/images/arrow-collapsed-rtl.png and /dev/null differ
diff --git a/resources/mediawiki.special/images/arrow-expanded.png b/resources/mediawiki.special/images/arrow-expanded.png
deleted file mode 100644 (file)
index fa4bf26..0000000
Binary files a/resources/mediawiki.special/images/arrow-expanded.png and /dev/null differ
index 3c841e5..7b3740e 100644 (file)
@@ -49,16 +49,6 @@ table.mw-enhanced-rc td.mw-enhanced-rc-nested {
        cursor: pointer;
 }
 
-.mw-enhancedchanges-arrow.mw-collapsible-toggle-collapsed {
-       /* @embed */
-       background: url(images/arrow-collapsed-ltr.png) no-repeat left bottom;
-}
-
-.mw-enhancedchanges-arrow.mw-collapsible-toggle-expanded {
-       /* @embed */
-       background: url(images/arrow-expanded.png) no-repeat left bottom;
-}
-
 .mw-changeslist-line-watched .mw-title,
 .mw-enhanced-watched .mw-enhanced-rc-time {
        font-weight: bold;
diff --git a/resources/mediawiki/images/arrow-collapsed-ltr.png b/resources/mediawiki/images/arrow-collapsed-ltr.png
new file mode 100644 (file)
index 0000000..ea9c67a
Binary files /dev/null and b/resources/mediawiki/images/arrow-collapsed-ltr.png differ
diff --git a/resources/mediawiki/images/arrow-collapsed-rtl.png b/resources/mediawiki/images/arrow-collapsed-rtl.png
new file mode 100644 (file)
index 0000000..081d3a8
Binary files /dev/null and b/resources/mediawiki/images/arrow-collapsed-rtl.png differ
diff --git a/resources/mediawiki/images/arrow-expanded.png b/resources/mediawiki/images/arrow-expanded.png
new file mode 100644 (file)
index 0000000..fa4bf26
Binary files /dev/null and b/resources/mediawiki/images/arrow-expanded.png differ
diff --git a/resources/mediawiki/mediawiki.icon.css b/resources/mediawiki/mediawiki.icon.css
new file mode 100644 (file)
index 0000000..f61b725
--- /dev/null
@@ -0,0 +1,15 @@
+/* General-purpose icons via CSS. Classes here should be named "mw-icon-*". */
+
+/* For the collapsed and expanded arrows, we also provide selectors to make it
+ * easy to use them with jquery.makeCollapsible. */
+.mw-icon-arrow-collapsed,
+.mw-collapsible-arrow.mw-collapsible-toggle-collapsed {
+       /* @embed */
+       background: url(images/arrow-collapsed-ltr.png) no-repeat left bottom;
+}
+
+.mw-icon-arrow-expanded,
+.mw-collapsible-arrow.mw-collapsible-toggle-expanded {
+       /* @embed */
+       background: url(images/arrow-expanded.png) no-repeat left bottom;
+}
index 9fd2e5e..e8366a5 100644 (file)
@@ -1,57 +1,52 @@
-/*
- * Implementation for mediaWiki.user
+/**
+ * @class mw.user
+ * @singleton
  */
-
 ( function ( mw, $ ) {
+       var callbacks, options, tokens, user;
 
        /**
-        * User object
+        * Gets the current user's groups or rights.
+        *
+        * @private
+        * @param {string} info One of 'groups' or 'rights'
+        * @param {Function} callback
         */
-       function User( options, tokens ) {
-               var user, callbacks;
-
-               /* Private Members */
-
-               user = this;
-               callbacks = {};
-
-               /**
-                * Gets the current user's groups or rights.
-                * @param {String} info: One of 'groups' or 'rights'.
-                * @param {Function} callback
-                */
-               function getUserInfo( info, callback ) {
-                       var api;
-                       if ( callbacks[info] ) {
-                               callbacks[info].add( callback );
-                               return;
-                       }
-                       callbacks.rights = $.Callbacks('once memory');
-                       callbacks.groups = $.Callbacks('once memory');
+       function getUserInfo( info, callback ) {
+               var api;
+               if ( callbacks[info] ) {
                        callbacks[info].add( callback );
-                       api = new mw.Api();
-                       api.get( {
-                               action: 'query',
-                               meta: 'userinfo',
-                               uiprop: 'rights|groups'
-                       } ).always( function ( data ) {
-                               var rights, groups;
-                               if ( data.query && data.query.userinfo ) {
-                                       rights = data.query.userinfo.rights;
-                                       groups = data.query.userinfo.groups;
-                               }
-                               callbacks.rights.fire( rights || [] );
-                               callbacks.groups.fire( groups || [] );
-                       } );
+                       return;
                }
+               callbacks.rights = $.Callbacks('once memory');
+               callbacks.groups = $.Callbacks('once memory');
+               callbacks[info].add( callback );
+               api = new mw.Api();
+               api.get( {
+                       action: 'query',
+                       meta: 'userinfo',
+                       uiprop: 'rights|groups'
+               } ).always( function ( data ) {
+                       var rights, groups;
+                       if ( data.query && data.query.userinfo ) {
+                               rights = data.query.userinfo.rights;
+                               groups = data.query.userinfo.groups;
+                       }
+                       callbacks.rights.fire( rights || [] );
+                       callbacks.groups.fire( groups || [] );
+               } );
+       }
 
-               /* Public Members */
-
-               this.options = options || new mw.Map();
+       callbacks = {};
 
-               this.tokens = tokens || new mw.Map();
+       // Extend the skeleton mw.user from mediawiki.js
+       // This is kind of ugly but we're stuck with this for b/c reasons
+       options = mw.user.options || new mw.Map();
+       tokens = mw.user.tokens || new mw.Map();
 
-               /* Public Methods */
+       mw.user = user = {
+               options: options,
+               tokens: tokens,
 
                /**
                 * Generates a random user session ID (32 alpha-numeric characters).
@@ -59,9 +54,9 @@
                 * This information would potentially be stored in a cookie to identify a user during a
                 * session or series of sessions. Its uniqueness should not be depended on.
                 *
-                * @return String: Random set of 32 alpha-numeric characters
+                * @return {string} Random set of 32 alpha-numeric characters
                 */
-               this.generateRandomSessionId = function () {
+               generateRandomSessionId: function () {
                        var i, r,
                                id = '',
                                seed = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
                                id += seed.substring( r, r + 1 );
                        }
                        return id;
-               };
+               },
 
                /**
                 * Gets the current user's name.
                 *
-                * @return Mixed: User name string or null if users is anonymous
+                * @return {string|null} User name string or null if users is anonymous
                 */
-               this.getName = function () {
+               getName: function () {
                        return mw.config.get( 'wgUserName' );
-               };
+               },
 
                /**
-                * @deprecated since 1.20 use mw.user.getName() instead
+                * @inheritdoc #getName
+                * @deprecated since 1.20 use #getName instead
                 */
-               this.name = function () {
-                       return this.getName();
-               };
+               name: function () {
+                       return user.getName();
+               },
 
                /**
                 * Get date user registered, if available.
                 *
-                * @return {Date|false|null} date user registered, or false for anonymous users, or
+                * @return {Date|boolean|null} Date user registered, or false for anonymous users, or
                 *  null when data is not available
                 */
-               this.getRegistration = function () {
+               getRegistration: function () {
                        var registration = mw.config.get( 'wgUserRegistration' );
-                       if ( this.isAnon() ) {
+                       if ( user.isAnon() ) {
                                return false;
                        } else if ( registration === null ) {
                                // Information may not be available if they signed up before
                        } else {
                                return new Date( registration );
                        }
-               };
+               },
 
                /**
                 * Checks if the current user is anonymous.
                 *
-                * @return Boolean
+                * @return {boolean}
                 */
-               this.isAnon = function () {
+               isAnon: function () {
                        return user.getName() === null;
-               };
+               },
 
                /**
-                * @deprecated since 1.20 use mw.user.isAnon() instead
+                * @inheritdoc #isAnon
+                * @deprecated since 1.20 use #isAnon instead
                 */
-               this.anonymous = function () {
+               anonymous: function () {
                        return user.isAnon();
-               };
+               },
 
                /**
-                * Gets a random session ID automatically generated and kept in a cookie.
+                * Gets a random ID automatically generated and stored in a session cookie.
                 *
                 * This ID is ephemeral for everyone, staying in their browser only until they close
                 * their browser.
                 *
-                * @return String: User name or random session ID
+                * @return {string} Random session ID
                 */
-               this.sessionId = function () {
+               sessionId: function () {
                        var sessionId = $.cookie( 'mediaWiki.user.sessionId' );
                        if ( typeof sessionId === 'undefined' || sessionId === null ) {
                                sessionId = user.generateRandomSessionId();
                                $.cookie( 'mediaWiki.user.sessionId', sessionId, { 'expires': null, 'path': '/' } );
                        }
                        return sessionId;
-               };
+               },
 
                /**
                 * Gets the current user's name or the session ID
                 *
                 * @return {string} User name or random session ID
                 */
-               this.id = function () {
+               id: function () {
                        var name = user.getName();
                        if ( name ) {
                                return name;
                        }
                        return user.sessionId();
-               };
+               },
 
                /**
                 * Gets the user's bucket, placing them in one at random based on set odds if needed.
                 *
-                * @param key String: Name of bucket
-                * @param options Object: Bucket configuration options
-                * @param options.buckets Object: List of bucket-name/relative-probability pairs (required,
-                * must have at least one pair)
-                * @param options.version Number: Version of bucket test, changing this forces rebucketing
-                * (optional, default: 0)
-                * @param options.expires Number: Length of time (in days) until the user gets rebucketed
-                * (optional, default: 30)
-                * @return String: Bucket name - the randomly chosen key of the options.buckets object
-                *
-                * @example
                 *     mw.user.bucket( 'test', {
                 *         'buckets': { 'ignored': 50, 'control': 25, 'test': 25 },
                 *         'version': 1,
                 *         'expires': 7
                 *     } );
+                *
+                * @param {string} key Name of bucket
+                * @param {Object} options Bucket configuration options
+                * @param {Object} options.buckets List of bucket-name/relative-probability pairs (required,
+                *  must have at least one pair)
+                * @param {number} options.version Version of bucket test, changing this forces rebucketing
+                *  (optional, default: 0)
+                * @param {number} options.expires Length of time (in days) until the user gets rebucketed
+                *  (optional, default: 30)
+                * @return {string} Bucket name - the randomly chosen key of the options.buckets object
                 */
-               this.bucket = function ( key, options ) {
+               bucket: function ( key, options ) {
                        var cookie, parts, version, bucket,
                                range, k, rand, total;
 
                                );
                        }
                        return bucket;
-               };
+               },
 
                /**
                 * Gets the current user's groups.
+                *
+                * @param {Function} callback
                 */
-               this.getGroups = function ( callback ) {
+               getGroups: function ( callback ) {
                        getUserInfo( 'groups', callback );
-               };
+               },
 
                /**
                 * Gets the current user's rights.
+                *
+                * @param {Function} callback
                 */
-               this.getRights = function ( callback ) {
+               getRights: function ( callback ) {
                        getUserInfo( 'rights', callback );
-               };
-       }
-
-       // Extend the skeleton mw.user from mediawiki.js
-       // This is kind of ugly but we're stuck with this for b/c reasons
-       mw.user = new User( mw.user.options, mw.user.tokens );
+               }
+       };
 
 }( mediaWiki, jQuery ) );
index ad309ba..d0305f0 100644 (file)
@@ -61,8 +61,7 @@ class SkinVector extends SkinTemplate {
        }
 
        /**
-        * Load skin and user CSS files in the correct order
-        * fixes bug 22916
+        * Loads skin and user CSS files.
         * @param $out OutputPage object
         */
        function setupSkinUserCss( OutputPage $out ) {
index 9f8887b..1e9c2af 100644 (file)
@@ -85,6 +85,8 @@ $wgAutoloadClasses += array(
        'MockFSFile' => "$testDir/phpunit/mocks/filebackend/MockFSFile.php",
        'MockFileBackend' => "$testDir/phpunit/mocks/filebackend/MockFileBackend.php",
        'MockBitmapHandler' => "$testDir/phpunit/mocks/media/MockBitmapHandler.php",
+       'MockImageHandler' => "$testDir/phpunit/mocks/media/MockBitmapHandler.php",
+       'MockSvgHandler' => "$testDir/phpunit/mocks/media/MockBitmapHandler.php",
 
        # tests/phpunit/languages
        'LanguageClassesTestCase' => "$testDir/phpunit/languages/LanguageClassesTestCase.php",
index 972ad8f..1c3ba1d 100644 (file)
@@ -1,6 +1,8 @@
 <?php
 /**
- * Helper code for the MediaWiki parser test suite.
+ * Helper code for the MediaWiki parser test suite. Some code is duplicated
+ * in PHPUnit's NewParserTests.php, so you'll probably want to update both
+ * at the same time.
  *
  * Copyright © 2004, 2010 Brion Vibber <brion@pobox.com>
  * http://www.mediawiki.org/
@@ -681,6 +683,8 @@ class ParserTest {
                        'wgNoFollowDomainExceptions' => array(),
                        'wgThumbnailScriptPath' => false,
                        'wgUseImageResize' => true,
+                       'wgSVGConverter' => 'null',
+                       'wgSVGConverters' => array( 'null' => 'echo "1">$output' ),
                        'wgLocaltimezone' => 'UTC',
                        'wgAllowExternalImages' => true,
                        'wgUseTidy' => false,
@@ -869,6 +873,8 @@ class ParserTest {
                # Clear the message cache
                MessageCache::singleton()->clear();
 
+               // Remember to update newParserTests.php after changing the below
+               // (and it uses a slightly different syntax just for teh lulz)
                $this->uploadDir = $this->setupUploadDir();
                $user = User::createNew( 'WikiSysop' );
                $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.jpg' ) );
@@ -902,6 +908,19 @@ class ParserTest {
                        'fileExists' => true
                ), $this->db->timestamp( '20130225203040' ), $user );
 
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
+               $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', array(
+                               'size'        => 12345,
+                               'width'       => 240,
+                               'height'      => 180,
+                               'bits'        => 24,
+                               'media_type'  => MEDIATYPE_DRAWING,
+                               'mime'        => 'image/svg+xml',
+                               'metadata'    => serialize( array() ),
+                               'sha1'        => wfBaseConvert( '', 16, 36, 31 ),
+                               'fileExists'  => true
+               ), $this->db->timestamp( '20010115123500' ), $user );
+
                # This image will be blacklisted in [[MediaWiki:Bad image list]]
                $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Bad.jpg' ) );
                $image->recordUpload2( '', 'zomgnotcensored', 'Borderline image', array(
@@ -985,7 +1004,11 @@ class ParserTest {
                copy( "$IP/skins/monobook/wiki.png", "$dir/e/ea/Thumb.png" );
                wfMkdirParents( $dir . '/0/09', null, __METHOD__ );
                copy( "$IP/skins/monobook/headbg.jpg", "$dir/0/09/Bad.jpg" );
-
+               wfMkdirParents( $dir . '/f/ff', null, __METHOD__ );
+               copy( "$IP/skins/monobook/headbg.jpg", "$dir/f/ff/Foobar.svg" );
+               file_put_contents( "$dir/f/ff/Foobar.svg",
+                       '<?xml version="1.0" encoding="utf-8"?>' .
+                       '<svg xmlns="http://www.w3.org/2000/svg" />' );
                return $dir;
        }
 
@@ -1035,6 +1058,14 @@ class ParserTest {
 
                                "$dir/0/09/Bad.jpg",
 
+                               "$dir/f/ff/Foobar.svg",
+                               "$dir/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png",
+                               "$dir/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png",
+                               "$dir/thumb/f/ff/Foobar.svg/langde-270px-Foobar.svg.png",
+                               "$dir/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png",
+                               "$dir/thumb/f/ff/Foobar.svg/langde-180px-Foobar.svg.png",
+                               "$dir/thumb/f/ff/Foobar.svg/langde-360px-Foobar.svg.png",
+
                                "$dir/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
                        )
                );
@@ -1048,10 +1079,13 @@ class ParserTest {
                                "$dir/thumb/3/3a/Foobar.jpg",
                                "$dir/thumb/3/3a",
                                "$dir/thumb/3",
-
                                "$dir/e/ea",
                                "$dir/e",
-
+                               "$dir/f/ff/",
+                               "$dir/f/",
+                               "$dir/thumb/f/ff/Foobar.svg",
+                               "$dir/thumb/f/ff/",
+                               "$dir/thumb/f/",
                                "$dir/0/09/",
                                "$dir/0/",
                                "$dir/thumb",
index f7296bd..4aadd7a 100644 (file)
@@ -5216,7 +5216,7 @@ Magic Word: {{NUMBEROFFILES}}
 !! input
 {{NUMBEROFFILES}}
 !! result
-<p>3
+<p>4
 </p>
 !! end
 
@@ -7757,6 +7757,37 @@ Thumbnail image caption with a free URL and explicit alt
 
 !! end
 
+
+!! test
+SVG thumbnails with no language set
+!! options
+!! input
+[[File:Foobar.svg|thumb|width=200]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.svg" class="image"><img alt="" src="http://example.com/images/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png" width="180" height="180" class="thumbimage" srcset="http://example.com/images/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png 1.5x, http://example.com/images/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png 2x" /></a>  <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.svg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>width=200</div></div></div>
+
+!! end
+
+!! test
+SVG thumbnails with language de
+!! options
+!! input
+[[File:Foobar.svg|thumb|width=200|lang=de]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/index.php?title=File:Foobar.svg&amp;lang=de" class="image"><img alt="" src="http://example.com/images/thumb/f/ff/Foobar.svg/langde-180px-Foobar.svg.png" width="180" height="180" class="thumbimage" srcset="http://example.com/images/thumb/f/ff/Foobar.svg/langde-270px-Foobar.svg.png 1.5x, http://example.com/images/thumb/f/ff/Foobar.svg/langde-360px-Foobar.svg.png 2x" /></a>  <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.svg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>width=200</div></div></div>
+
+!! end
+
+!! test
+SVG thumbnails with invalid language code
+!! options
+!! input
+[[File:Foobar.svg|thumb|width=200|lang=invalid.language.code]]
+!! result
+<div class="thumb tright"><div class="thumbinner" style="width:182px;"><a href="/wiki/File:Foobar.svg" class="image"><img alt="" src="http://example.com/images/thumb/f/ff/Foobar.svg/180px-Foobar.svg.png" width="180" height="180" class="thumbimage" srcset="http://example.com/images/thumb/f/ff/Foobar.svg/270px-Foobar.svg.png 1.5x, http://example.com/images/thumb/f/ff/Foobar.svg/360px-Foobar.svg.png 2x" /></a>  <div class="thumbcaption"><div class="magnify"><a href="/wiki/File:Foobar.svg" class="internal" title="Enlarge"><img src="/skins/common/images/magnify-clip.png" width="15" height="11" alt="" /></a></div>lang=invalid.language.code</div></div></div>
+
+!! end
+
 !! test
 BUG 1887: A ISBN with a thumbnail
 !! input
@@ -13965,6 +13996,7 @@ HttP://MediaWiki.Org/
 </p>
 !! end
 
+
 ###
 ### Parsoids-specific tests
 ### Parsoid-PHP parser incompatibilities
index 4084fb1..56bb0fc 100644 (file)
@@ -19,7 +19,6 @@ class OutputPageTest extends MediaWikiTestCase {
         *
         * options['printableQuery'] - value of query string for printable, or omitted for none
         * options['handheldQuery'] - value of query string for handheld, or omitted for none
-        * options['handheldForIPhone'] - value of the $wgHandheldForIPhone global
         * options['media'] - passed into the method under the same name
         * options['expectedReturn'] - expected return value
         * options['message'] - PHPUnit message for assertion
@@ -39,58 +38,38 @@ class OutputPageTest extends MediaWikiTestCase {
                $fauxRequest = new FauxRequest( $queryData, false );
                $this->setMWGlobals( array(
                        'wgRequest' => $fauxRequest,
-                       'wgHandheldForIPhone' => $args['handheldForIPhone']
                ) );
 
                $actualReturn = OutputPage::transformCssMedia( $args['media'] );
                $this->assertSame( $args['expectedReturn'], $actualReturn, $args['message'] );
        }
 
-       /**
-        * Tests a case of transformCssMedia with both values of wgHandheldForIPhone.
-        * Used to verify that behavior is orthogonal to that option.
-        *
-        * If the value of wgHandheldForIPhone should matter, use assertTransformCssMediaCase.
-        *
-        * @param array $args key-value array of arguments as shown in assertTransformCssMediaCase.
-        * Will be mutated.
-        */
-       protected function assertTransformCssMediaCaseWithBothHandheldForIPhone( $args ) {
-               $message = $args['message'];
-               foreach ( array( true, false ) as $handheldForIPhone ) {
-                       $args['handheldForIPhone'] = $handheldForIPhone;
-                       $stringHandheldForIPhone = var_export( $handheldForIPhone, true );
-                       $args['message'] = "$message. \$wgHandheldForIPhone was $stringHandheldForIPhone";
-                       $this->assertTransformCssMediaCase( $args );
-               }
-       }
-
        /**
         * Tests print requests
         */
        public function testPrintRequests() {
-               $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+               $this->assertTransformCssMediaCase( array(
                        'printableQuery' => '1',
                        'media' => 'screen',
                        'expectedReturn' => null,
                        'message' => 'On printable request, screen returns null'
                ) );
 
-               $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+               $this->assertTransformCssMediaCase( array(
                        'printableQuery' => '1',
                        'media' => self::SCREEN_MEDIA_QUERY,
                        'expectedReturn' => null,
                        'message' => 'On printable request, screen media query returns null'
                ) );
 
-               $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+               $this->assertTransformCssMediaCase( array(
                        'printableQuery' => '1',
                        'media' => self::SCREEN_ONLY_MEDIA_QUERY,
                        'expectedReturn' => null,
                        'message' => 'On printable request, screen media query with only returns null'
                ) );
 
-               $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+               $this->assertTransformCssMediaCase( array(
                        'printableQuery' => '1',
                        'media' => 'print',
                        'expectedReturn' => '',
@@ -103,25 +82,30 @@ class OutputPageTest extends MediaWikiTestCase {
         */
        public function testScreenRequests() {
                $this->assertTransformCssMediaCase( array(
-                       'handheldForIPhone' => false,
                        'media' => 'screen',
                        'expectedReturn' => 'screen',
-                       'message' => 'On screen request, with handheldForIPhone false, screen media type is preserved'
+                       'message' => 'On screen request, screen media type is preserved'
+               ) );
+
+               $this->assertTransformCssMediaCase( array(
+                       'media' => 'handheld',
+                       'expectedReturn' => 'handheld',
+                       'message' => 'On screen request, handheld media type is preserved'
                ) );
 
-               $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+               $this->assertTransformCssMediaCase( array(
                        'media' => self::SCREEN_MEDIA_QUERY,
                        'expectedReturn' => self::SCREEN_MEDIA_QUERY,
                        'message' => 'On screen request, screen media query is preserved.'
                ) );
 
-               $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+               $this->assertTransformCssMediaCase( array(
                        'media' => self::SCREEN_ONLY_MEDIA_QUERY,
                        'expectedReturn' => self::SCREEN_ONLY_MEDIA_QUERY,
                        'message' => 'On screen request, screen media query with only is preserved.'
                ) );
 
-               $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+               $this->assertTransformCssMediaCase( array(
                        'media' => 'print',
                        'expectedReturn' => 'print',
                        'message' => 'On screen request, print media type is preserved'
@@ -129,44 +113,21 @@ class OutputPageTest extends MediaWikiTestCase {
        }
 
        /**
-        * Tests handheld and wgHandheldForIPhone behavior
+        * Tests handheld behavior
         */
        public function testHandheld() {
-               $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+               $this->assertTransformCssMediaCase( array(
                        'handheldQuery' => '1',
                        'media' => 'handheld',
                        'expectedReturn' => '',
                        'message' => 'On request with handheld querystring and media is handheld, returns empty string'
                ) );
 
-               $this->assertTransformCssMediaCaseWithBothHandheldForIPhone( array(
+               $this->assertTransformCssMediaCase( array(
                        'handheldQuery' => '1',
                        'media' => 'screen',
                        'expectedReturn' => null,
                        'message' => 'On request with handheld querystring and media is screen, returns null'
                ) );
-
-               // A bit counter-intuitively, $wgHandheldForIPhone should only matter if the query handheld is false or omitted
-               $this->assertTransformCssMediaCase( array(
-                       'handheldQuery' => '0',
-                       'media' => 'screen',
-                       'handheldForIPhone' => true,
-                       'expectedReturn' => 'screen and (min-device-width: 481px)',
-                       'message' => 'With $wgHandheldForIPhone true, screen media type is transformed'
-               ) );
-
-               $this->assertTransformCssMediaCase( array(
-                       'media' => 'handheld',
-                       'handheldForIPhone' => true,
-                       'expectedReturn' => 'handheld, only screen and (max-device-width: 480px)',
-                       'message' => 'With $wgHandheldForIPhone true, handheld media type is transformed'
-               ) );
-
-               $this->assertTransformCssMediaCase( array(
-                       'media' => 'handheld',
-                       'handheldForIPhone' => false,
-                       'expectedReturn' => 'handheld',
-                       'message' => 'With $wgHandheldForIPhone false, handheld media type is preserved'
-               ) );
        }
 }
diff --git a/tests/phpunit/includes/XmlTypeCheckTest.php b/tests/phpunit/includes/XmlTypeCheckTest.php
new file mode 100644 (file)
index 0000000..18f363f
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/**
+ * PHPUnit tests for XMLTypeCheck.
+ * @author physikerwelt
+ * @group ?
+ * @covers XMLTypeCheck
+ */
+class XmlTypeCheckTest extends MediaWikiTestCase {
+       const WELL_FORMED_XML = "<root><child /></root>";
+       const MAL_FORMED_XML = "<root><child /></error>";
+
+       /**
+        * @covers XMLTypeCheck::newFromString
+        * @covers XMLTypeCheck::getRootElement
+        */
+       public function testWellFormedXML() {
+               $testXML = XmlTypeCheck::newFromString( self::WELL_FORMED_XML );
+               $this->assertTrue( $testXML->wellFormed );
+               $this->assertEquals( 'root', $testXML->getRootElement() );
+       }
+
+       /**
+        * @covers XMLTypeCheck::newFromString
+        */
+       public function testMalFormedXML() {
+               $testXML = XmlTypeCheck::newFromString( self::MAL_FORMED_XML );
+               $this->assertFalse( $testXML->wellFormed );
+       }
+
+}
\ No newline at end of file
index c345513..2904472 100644 (file)
@@ -431,4 +431,18 @@ class DummyContentForTesting extends AbstractContent {
        public function getParserOutput( Title $title, $revId = null, ParserOptions $options = null, $generateHtml = true ) {
                return new ParserOutput( $this->getNativeData() );
        }
+
+       /**
+        * @see AbstractContent::fillParserOutput()
+        *
+        * @param $title        Title Context title for parsing
+        * @param $revId        int|null Revision ID (for {{REVISIONID}})
+        * @param $options      ParserOptions|null Parser options
+        * @param $generateHtml bool Whether or not to generate HTML
+        * @param $output       ParserOutput The output object to fill (reference).
+        */
+       protected function fillParserOutput( Title $title, $revId,
+                       ParserOptions $options, $generateHtml, ParserOutput &$output ) {
+               $output = new ParserOutput( $this->getNativeData() );
+       }
 }
index 1c45820..61716f9 100644 (file)
@@ -5,7 +5,7 @@
  * @group Database
  *        ^--- needed, because we do need the database to test link updates
  */
-class CssContentTest extends MediaWikiTestCase {
+class CssContentTest extends JavaScriptContentTest {
 
        protected function setUp() {
                parent::setUp();
index c7138b7..d7dde37 100644 (file)
@@ -7,14 +7,21 @@
  */
 class TextContentTest extends MediaWikiLangTestCase {
        protected $context;
+       protected $savedContentGetParserOutput;
 
        protected function setUp() {
+               global $wgHooks;
+
                parent::setUp();
 
                // Anon user
                $user = new User();
                $user->setName( '127.0.0.1' );
 
+               $this->context = new RequestContext( new FauxRequest() );
+               $this->context->setTitle( Title::newFromText( 'Test' ) );
+               $this->context->setUser( $user );
+
                $this->setMwGlobals( array(
                        'wgUser' => $user,
                        'wgTextModelsToParse' => array(
@@ -26,9 +33,22 @@ class TextContentTest extends MediaWikiLangTestCase {
                        'wgAlwaysUseTidy' => false,
                ) );
 
-               $this->context = new RequestContext( new FauxRequest() );
-               $this->context->setTitle( Title::newFromText( 'Test' ) );
-               $this->context->setUser( $user );
+               // bypass hooks that force custom rendering
+               if ( isset( $wgHooks['ContentGetParserOutput'] )  ) {
+                       $this->savedContentGetParserOutput = $wgHooks['ContentGetParserOutput'];
+                       unset( $wgHooks['ContentGetParserOutput'] );
+               }
+       }
+
+       public function teardown() {
+               global $wgHooks;
+
+               // restore hooks that force custom rendering
+               if ( $this->savedContentGetParserOutput !== null ) {
+                       $wgHooks['ContentGetParserOutput'] = $this->savedContentGetParserOutput;
+               }
+
+               parent::teardown();
        }
 
        public function newContent( $text ) {
index 965a5f3..46ccfe0 100644 (file)
@@ -204,6 +204,41 @@ class DatabaseSQLTest extends MediaWikiTestCase {
                );
        }
 
+       /**
+        * @dataProvider provideUpsert
+        */
+       function testUpsert( $sql, $sqlText ) {
+               $this->database->upsert(
+                       $sql['table'],
+                       $sql['rows'],
+                       $sql['uniqueIndexes'],
+                       $sql['set'],
+                       __METHOD__
+               );
+               $this->assertLastSql( $sqlText );
+       }
+
+       public static function provideUpsert() {
+               return array(
+                       array(
+                               array(
+                                       'table' => 'upsert_table',
+                                       'rows' => array( 'field' => 'text', 'field2' => 'text2' ),
+                                       'uniqueIndexes' => array( 'field' ),
+                                       'set' => array( 'field' => 'set' ),
+                               ),
+                               "BEGIN; " .
+                                       "UPDATE upsert_table " .
+                                       "SET field = 'set' " .
+                                       "WHERE ((field = 'text')); " .
+                                       "INSERT IGNORE INTO upsert_table " .
+                                       "(field,field2) " .
+                                       "VALUES ('text','text2'); " .
+                                       "COMMIT"
+                       ),
+               );
+       }
+
        /**
         * @dataProvider provideDeleteJoin
         */
index 632eb52..a1b87f6 100644 (file)
@@ -151,14 +151,11 @@ class CSSJanusTest extends MediaWikiTestCase {
                                '#settings td p strong'
                        ),
                        array(
-                               # Not sure how 4+ values should behave,
-                               # testing to make sure changes are detected
-                               '.foo { x-unknown: 1 2 3 4 5; }',
-                               '.foo { x-unknown: 1 4 3 2 5; }',
+                               // Do not mangle 5 or more values
+                               '.foo { -x-unknown: 1 2 3 4 5; }'
                        ),
                        array(
-                               '.foo { x-unknown: 1 2 3 4 5 6; }',
-                               '.foo { x-unknown: 1 4 3 2 5 6; }',
+                               '.foo { -x-unknown: 1 2 3 4 5 6; }'
                        ),
 
                        // Shorthand / Three notation
index 8cee07b..6975939 100644 (file)
@@ -102,6 +102,10 @@ class NewParserTest extends MediaWikiTestCase {
 
                $tmpGlobals['wgParser'] = new StubObject( 'wgParser', $GLOBALS['wgParserConf']['class'], array( $GLOBALS['wgParserConf'] ) );
 
+               $tmpGlobals['wgFileExtensions'][] = 'svg';
+               $tmpGlobals['wgSVGConverter'] = 'rsvg';
+               $tmpGlobals['wgSVGConverters']['rsvg'] = '$path/rsvg-convert -w $width -h $height $input -o $output';
+
                if ( $GLOBALS['wgStyleDirectory'] === false ) {
                        $tmpGlobals['wgStyleDirectory'] = "$IP/skins";
                }
@@ -113,6 +117,8 @@ class NewParserTest extends MediaWikiTestCase {
                foreach ( $wgMediaHandlers as $type => $handler ) {
                        $tmpGlobals['wgMediaHandlers'][$type] = 'MockBitmapHandler';
                }
+               // Vector images have to be handled slightly differently
+               $tmpGlobals['wgMediaHandlers']['image/svg+xml'] = 'MockSvgHandler';
 
                $tmpHooks = $wgHooks;
                $tmpHooks['ParserTestParser'][] = 'ParserTestParserHook::setup';
@@ -270,6 +276,20 @@ class NewParserTest extends MediaWikiTestCase {
                                $this->db->timestamp( '20010115123500' ), $user
                        );
                }
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Foobar.svg' ) );
+               if ( !$this->db->selectField( 'image', '1', array( 'img_name' => $image->getName() ) ) ) {
+                       $image->recordUpload2( '', 'Upload of some lame SVG', 'Some lame SVG', array(
+                                       'size'        => 12345,
+                                       'width'       => 200,
+                                       'height'      => 200,
+                                       'bits'        => 24,
+                                       'media_type'  => MEDIATYPE_DRAWING,
+                                       'mime'        => 'image/svg+xml',
+                                       'metadata'    => serialize( array() ),
+                                       'sha1'        => wfBaseConvert( '', 16, 36, 31 ),
+                                       'fileExists'  => true
+                       ), $this->db->timestamp( '20010115123500' ), $user );
+               }
        }
 
        //ParserTest setup/teardown functions
@@ -440,6 +460,19 @@ class NewParserTest extends MediaWikiTestCase {
                $backend->store( array(
                        'src' => "$IP/skins/monobook/headbg.jpg", 'dst' => "$base/local-public/0/09/Bad.jpg"
                ) );
+
+               // No helpful SVG file to copy, so make one ourselves
+               $tmpDir = wfTempDir();
+               $tempFsFile = new TempFSFile( "$tmpDir/Foobar.svg" );
+               $tempFsFile->autocollect(); // destroy file when $tempFsFile leaves scope
+               file_put_contents( "$tmpDir/Foobar.svg",
+                       '<?xml version="1.0" encoding="utf-8"?>' .
+                       '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"><text>Foo</text></svg>' );
+
+               $backend->prepare( array( 'dir' => "$base/local-public/f/ff" ) );
+               $backend->quickStore( array(
+                       'src' => "$tmpDir/Foobar.svg", 'dst' => "$base/local-public/f/ff/Foobar.svg"
+               ) );
        }
 
        /**
@@ -492,6 +525,14 @@ class NewParserTest extends MediaWikiTestCase {
 
                                "$base/local-public/0/09/Bad.jpg",
 
+                               "$base/local-public/f/ff/Foobar.svg",
+                               "$base/local-thumb/f/ff/Foobar.svg/180px-Foobar.svg.jpg",
+                               "$base/local-thumb/f/ff/Foobar.svg/270px-Foobar.svg.jpg",
+                               "$base/local-thumb/f/ff/Foobar.svg/360px-Foobar.svg.jpg",
+                               "$base/local-thumb/f/ff/Foobar.svg/langde-180px-Foobar.svg.jpg",
+                               "$base/local-thumb/f/ff/Foobar.svg/langde-270px-Foobar.svg.jpg",
+                               "$base/local-thumb/f/ff/Foobar.svg/langde-360px-Foobar.svg.jpg",
+
                                "$base/local-public/math/f/a/5/fa50b8b616463173474302ca3e63586b.png",
                        )
                );
index 2ae30a3..742b41e 100644 (file)
  * @ingroup Media
  */
 
+class MockBitmapHandler extends BitmapHandler {
+       function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+               return MockImageHandler::doFakeTransform( $this, $image, $dstPath, $dstUrl, $params, $flags );
+       }
+       function doClientImage( $image, $scalerParams ) {
+                       return $this->getClientScalingThumbnailImage( $image, $scalerParams );
+       }
+}
+class MockSvgHandler extends SvgHandler {
+       function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+               return MockImageHandler::doFakeTransform( $this, $image, $dstPath, $dstUrl, $params, $flags );
+       }
+}
 /**
  * Mock handler for images.
  *
@@ -28,7 +41,7 @@
  *
  * @ingroup Media
  */
-class MockBitmapHandler extends BitmapHandler {
+class MockImageHandler {
 
        /**
         * Override BitmapHandler::doTransform() making sure we do not generate
@@ -36,14 +49,14 @@ class MockBitmapHandler extends BitmapHandler {
         * will be consumed by the unit test.  There is no need to create a real
         * thumbnail on the filesystem.
         */
-       function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
+       static function doFakeTransform( $that, $image, $dstPath, $dstUrl, $params, $flags = 0 ) {
                # Example of what we receive:
                # $image: LocalFile
                # $dstPath: /tmp/transform_7d0a7a2f1a09-1.jpg
                # $dstUrl : http://example.com/images/thumb/0/09/Bad.jpg/320px-Bad.jpg
                # $params:  width: 320,  descriptionUrl http://trunk.dev/wiki/File:Bad.jpg
 
-               $this->normaliseParams( $image, $params );
+               $that->normaliseParams( $image, $params );
 
                $scalerParams = array(
                        # The size to which the image will be resized
@@ -67,9 +80,11 @@ class MockBitmapHandler extends BitmapHandler {
                # In some cases, we do not bother generating a thumbnail.
                if ( !$image->mustRender() &&
                        $scalerParams['physicalWidth'] == $scalerParams['srcWidth']
-                       && $scalerParams['physicalHeight'] == $scalerParams['srcHeight'] ) {
+                       && $scalerParams['physicalHeight'] == $scalerParams['srcHeight']
+               ) {
                        wfDebug( __METHOD__ . ": returning unscaled image\n" );
-                       return $this->getClientScalingThumbnailImage( $image, $scalerParams );
+                       // getClientScalingThumbnailImage is protected
+                       return $that->doClientImage( $image, $scalerParams );
                }
 
                return new ThumbnailImage( $image, $dstUrl, false, $params );
index deef745..e4cb6b6 100755 (executable)
@@ -103,12 +103,17 @@ class PHPUnitMaintClass extends Maintenance {
 $maintClass = 'PHPUnitMaintClass';
 require RUN_MAINTENANCE_IF_MAIN;
 
-require_once 'PHPUnit/Runner/Version.php';
+if ( !class_exists( 'PHPUnit_Runner_Version') ) {
+       require_once 'PHPUnit/Runner/Version.php';
+}
 
 if ( PHPUnit_Runner_Version::id() !== '@package_version@'
        && version_compare( PHPUnit_Runner_Version::id(), '3.6.7', '<' )
 ) {
        die( 'PHPUnit 3.6.7 or later required, you have ' . PHPUnit_Runner_Version::id() . ".\n" );
 }
-require_once 'PHPUnit/Autoload.php';
+
+if ( !class_exists( 'PHPUnit_TextUI_Command' ) ) {
+       require_once 'PHPUnit/Autoload.php';
+}
 MediaWikiPHPUnitCommand::main();
index 88bbf5c..b2a6fd5 100644 (file)
@@ -1,16 +1,11 @@
 ( function ( $ ) {
-       var uacount, uas, testMap;
 
        QUnit.module( 'jquery.client', QUnit.newMwEnvironment() );
 
-       /** Number of user-agent defined */
-       uacount = 0;
-
-       uas = ( function () {
-
+       var uacount = 0,
                // Object keyed by userAgent. Value is an array (human-readable name, client-profile object, navigator.platform value)
                // Info based on results from http://toolserver.org/~krinkle/testswarm/job/174/
-               var uas = {
+               uas = {
                        // Internet Explorer 6
                        // Internet Explorer 7
                        'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)': {
                                        rtl: true
                                }
                        },
+                       // Iceweasel 15.0.1
+                       'Mozilla/5.0 (X11; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1 Iceweasel/15.0.1': {
+                               title: 'Iceweasel 15.0.1',
+                               platform: 'Linux',
+                               profile: {
+                                       name: 'iceweasel',
+                                       layout: 'gecko',
+                                       layoutVersion: 20100101,
+                                       platform: 'linux',
+                                       version: '15.0.1',
+                                       versionBase: '15',
+                                       versionNumber: 15
+                               },
+                               wikiEditor: {
+                                       ltr: true,
+                                       rtl: true
+                               }
+                       },
                        // Firefox 5
                        // Safari 3
                        // Safari 4
                                }
                        },
                        // Safari 5
+                       // Safari 6
+                       'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/536.29.13 (KHTML, like Gecko) Version/6.0.4 Safari/536.29.13': {
+                               title: 'Safari 6',
+                               platform: 'MacIntel',
+                               profile: {
+                                       name: 'safari',
+                                       layout: 'webkit',
+                                       layoutVersion: 536,
+                                       platform: 'mac',
+                                       version: '6.0.4',
+                                       versionBase: '6',
+                                       versionNumber: 6
+                               },
+                               wikiEditor: {
+                                       ltr: true,
+                                       rtl: true
+                               }
+                       },
+                       // Safari 6.0.5+ (doesn't have the comma in "KHTML, like Gecko")
+                       'Mozilla/5.0 (Macintosh; Intel Mac OS X 1084) AppleWebKit/536.30.1 (KHTML like Gecko) Version/6.0.5 Safari/536.30.1': {
+                               title: 'Safari 6',
+                               platform: 'MacIntel',
+                               profile: {
+                                       name: 'safari',
+                                       layout: 'webkit',
+                                       layoutVersion: 536,
+                                       platform: 'mac',
+                                       version: '6.0.5',
+                                       versionBase: '6',
+                                       versionNumber: 6
+                               },
+                               wikiEditor: {
+                                       ltr: true,
+                                       rtl: true
+                               }
+                       },
                        // Opera 10+
                        'Opera/9.80 (Windows NT 5.1)': {
                                title: 'Opera 10+ (exact version unspecified)',
                                        rtl: true
                                }
                        },
+                       // Android WebKit Browser 2.3
+                       'Mozilla/5.0 (Linux; U; Android 2.3.5; en-us; HTC Vision Build/GRI40) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1': {
+                               title: 'Android WebKit Browser 2.3',
+                               platform: 'Linux armv7l',
+                               profile: {
+                                       name: 'android',
+                                       layout: 'webkit',
+                                       layoutVersion: 533,
+                                       platform: 'linux',
+                                       version: '2.3.5',
+                                       versionBase: '2',
+                                       versionNumber: 2.3
+                               },
+                               wikiEditor: {
+                                       ltr: true,
+                                       rtl: true
+                               }
+                       },
                        // Bug #34924
                        'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.34 (KHTML, like Gecko) rekonq Safari/534.34': {
                                title: 'Rekonq',
                                        rtl: true
                                }
                        }
-               };
-               $.each( uas, function () {
-                       uacount++;
-               } );
-               return uas;
-       }() );
-
-       QUnit.test( 'profile userAgent support', uacount, function ( assert ) {
-               // Generate a client profile object and compare recursively
-               var uaTest = function ( rawUserAgent, data ) {
-                       var ret = $.client.profile( {
-                               userAgent: rawUserAgent,
-                               platform: data.platform
-                       } );
-                       assert.deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent );
-               };
+               },
+               testMap = {
+                       // Example from WikiEditor
+                       // Make sure to use raw numbers, a string like "7.0" would fail on a
+                       // version 10 browser since in string comparaison "10" is before "7.0" :)
+                       'ltr': {
+                               'msie': [['>=', 7.0]],
+                               'firefox': [['>=', 2]],
+                               'opera': [['>=', 9.6]],
+                               'safari': [['>=', 3]],
+                               'chrome': [['>=', 3]],
+                               'netscape': [['>=', 9]],
+                               'blackberry': false,
+                               'ipod': false,
+                               'iphone': false
+                       },
+                       'rtl': {
+                               'msie': [['>=', 8]],
+                               'firefox': [['>=', 2]],
+                               'opera': [['>=', 9.6]],
+                               'safari': [['>=', 3]],
+                               'chrome': [['>=', 3]],
+                               'netscape': [['>=', 9]],
+                               'blackberry': false,
+                               'ipod': false,
+                               'iphone': false
+                       }
+               }
+       ;
 
-               // Loop through and run tests
-               $.each( uas, uaTest );
+       // Count test cases
+       $.each( uas, function () {
+               uacount++;
        } );
 
-       QUnit.test( 'profile return validation for current user agent', 7, function ( assert ) {
+       QUnit.test( 'profile( navObject )', 7, function ( assert ) {
                var p = $.client.profile();
 
                function unknownOrType( val, type, summary ) {
                assert.equal( typeof p.versionNumber, 'number', 'p.versionNumber is a number' );
        } );
 
-       // Example from WikiEditor
-       // Make sure to use raw numbers, a string like "7.0" would fail on a
-       // version 10 browser since in string comparaison "10" is before "7.0" :)
-       testMap = {
-               'ltr': {
-                       'msie': [['>=', 7.0]],
-                       'firefox': [['>=', 2]],
-                       'opera': [['>=', 9.6]],
-                       'safari': [['>=', 3]],
-                       'chrome': [['>=', 3]],
-                       'netscape': [['>=', 9]],
-                       'blackberry': false,
-                       'ipod': false,
-                       'iphone': false
-               },
-               'rtl': {
-                       'msie': [['>=', 8]],
-                       'firefox': [['>=', 2]],
-                       'opera': [['>=', 9.6]],
-                       'safari': [['>=', 3]],
-                       'chrome': [['>=', 3]],
-                       'netscape': [['>=', 9]],
-                       'blackberry': false,
-                       'ipod': false,
-                       'iphone': false
-               }
-       };
+       QUnit.test( 'profile( navObject ) - samples', uacount, function ( assert ) {
+               // Loop through and run tests
+               $.each( uas, function ( rawUserAgent, data ) {
+                       // Generate a client profile object and compare recursively
+                       var ret = $.client.profile( {
+                               userAgent: rawUserAgent,
+                               platform: data.platform
+                       } );
+                       assert.deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent );
+               } );
+       } );
 
-       QUnit.test( 'test', 1, function ( assert ) {
+       QUnit.test( 'test( testMap )', 4, function ( assert ) {
                // .test() uses eval, make sure no exceptions are thrown
                // then do a basic return value type check
-               var testMatch = $.client.test( testMap );
+               var testMatch = $.client.test( testMap ),
+                       ie7Profile = $.client.profile( {
+                               'userAgent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)',
+                               'platform': ''
+                       } );
+
+               assert.equal( typeof testMatch, 'boolean', 'map with ltr/rtl split returns a boolean value' );
+
+               testMatch = $.client.test( testMap.ltr );
+
+               assert.equal( typeof testMatch, 'boolean', 'simple map (without ltr/rtl split) returns a boolean value' );
+
+               assert.equal( $.client.test( {
+                       'msie': null
+               }, ie7Profile ), true, 'returns true if any version of a browser are allowed (null)' );
+
+               assert.equal( $.client.test( {
+                       'msie': false
+               }, ie7Profile ), false, 'returns false if all versions of a browser are not allowed (false)' );
+       } );
+
+       QUnit.test( 'test( testMap, exactMatchOnly )', 2, function ( assert ) {
+               var ie7Profile = $.client.profile( {
+                       'userAgent': 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)',
+                       'platform': ''
+               } );
 
-               assert.equal( typeof testMatch, 'boolean', 'test returns a boolean value' );
+               assert.equal( $.client.test( {
+                       'firefox': [['>=', 2]]
+               }, ie7Profile, false ), true, 'returns true if browser not found and exactMatchOnly not set' );
 
+               assert.equal( $.client.test( {
+                       'firefox': [['>=', 2]]
+               }, ie7Profile, true ), false, 'returns false if browser not found and exactMatchOnly is set' );
        } );
 
-       QUnit.test( 'User-agent matches against WikiEditor\'s compatibility map', uacount * 2, function ( assert ) {
+       QUnit.test( 'test( testMap) - WikiEditor sample', uacount * 2, function ( assert ) {
                var $body = $( 'body' ),
                        bodyClasses = $body.attr( 'class' );
 
index 7c8503b..4353464 100644 (file)
                        $table.find( '.headerSort:eq(0)' ).click();
                }
        );
+       tableTest(
+               'Basic planet table: ascending by name (multiple clicks)',
+               header,
+               planets,
+               ascendingName,
+               function ( $table ) {
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(1)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
        tableTest(
                'Basic planet table: descending by name',
                header,
                        $table.data( 'tablesorter' ).sort();
                }
        );
+       tableTest(
+               'Sort via click event after having initialized the tablesorter with initial sorting',
+               header,
+               initial,
+               descasc,
+               function ( $table ) {
+                       $table.tablesorter(
+                               { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
+                       );
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
+       tableTest(
+               'Multi-sort via click event after having initialized the tablesorter with initial sorting',
+               header,
+               initial,
+               asc,
+               function ( $table ) {
+                       $table.tablesorter(
+                               { sortList: [ { 0: 'desc' }, { 1: 'desc' } ] }
+                       );
+                       $table.find( '.headerSort:eq(0)' ).click();
+
+                       // Pretend to click while pressing the multi-sort key
+                       var event = $.Event( 'click' );
+                       event[$table.data( 'tablesorter' ).config.sortMultiSortKey] = true;
+                       $table.find( '.headerSort:eq(1)' ).trigger( event );
+               }
+       );
        QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
                var $table = tableCreate( header, initial );
                $table.tablesorter(
                        $table.find( '.headerSort:eq(0)' ).click();
                }
        );
+       tableTest( 'Sorting with colspanned headers: sort spanned column twice',
+               header,
+               initial,
+               [ caa4, bbc2, abc3, aab5, aaa1 ],
+               function ( $table ) {
+                       // Make colspanned header for test
+                       $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
+                       $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(0)' ).click();
+                       $table.find( '.headerSort:eq(0)' ).click();
+               }
+       );
        tableTest( 'Sorting with colspanned headers: subsequent column',
                header,
                initial,
                        $table.find( '.headerSort:eq(1)' ).click();
                }
        );
+       tableTest( 'Sorting with colspanned headers: sort subsequent column twice',
+               header,
+               initial,
+               [ aab5, caa4, abc3, bbc2, aaa1 ],
+               function ( $table ) {
+                       // Make colspanned header for test
+                       $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
+                       $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
+
+                       $table.tablesorter();
+                       $table.find( '.headerSort:eq(1)' ).click();
+                       $table.find( '.headerSort:eq(1)' ).click();
+               }
+       );
+
 
        // Regression tests!
        tableTest(
index bc880a8..4296f79 100644 (file)
@@ -1,6 +1,6 @@
 == Details==
 
-Automated Selenium test scripts written for MediaWiki Installer is available at https://gerrit.wikimedia.org/r/gitweb?p=mediawiki/core.git;a=tree;f=tests/selenium/installer;hb=HEAD.
+Automated Selenium test scripts written for MediaWiki Installer is available at https://git.wikimedia.org/tree/mediawiki%2Fcore.git/HEAD/tests%2Fselenium%2Finstaller.
 Detailed test cases available at http://www.mediawiki.org/wiki/New_installer/Test_plan.
 
 Version : MediaWiki 1.18alpha