Merge "Use LoggerInterface::class in MediaWikiTestCaseTest"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 4 May 2016 17:58:12 +0000 (17:58 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 4 May 2016 17:58:12 +0000 (17:58 +0000)
104 files changed:
RELEASE-NOTES-1.27
composer.json
docs/extension.schema.json [changed mode: 0644->0755]
extensions/README
includes/DefaultSettings.php
includes/EditPage.php
includes/GlobalFunctions.php
includes/Message.php
includes/api/ApiHelp.php
includes/api/ApiOptions.php
includes/api/ApiQueryRecentChanges.php
includes/api/ApiUpload.php
includes/api/i18n/ku-latn.json
includes/api/i18n/pl.json
includes/changes/EnhancedChangesList.php
includes/db/Database.php
includes/db/DatabaseMssql.php
includes/deferred/LinksUpdate.php
includes/filebackend/FileBackendMultiWrite.php
includes/htmlform/HTMLForm.php
includes/htmlform/HTMLFormField.php
includes/htmlform/HTMLFormFieldCloner.php
includes/htmlform/OOUIHTMLForm.php
includes/installer/Installer.php
includes/installer/MssqlInstaller.php
includes/installer/MssqlUpdater.php
includes/jobqueue/jobs/RefreshLinksJob.php
includes/parser/ParserCache.php
includes/poolcounter/PoolWorkArticleView.php
includes/registration/ExtensionProcessor.php [changed mode: 0644->0755]
includes/resourceloader/ResourceLoaderWikiModule.php
includes/search/SearchEngine.php
includes/session/Session.php
includes/specials/SpecialBlock.php
languages/i18n/ast.json
languages/i18n/be-tarask.json
languages/i18n/bg.json
languages/i18n/bgn.json
languages/i18n/cs.json
languages/i18n/dty.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/frp.json
languages/i18n/gl.json
languages/i18n/he.json
languages/i18n/ht.json
languages/i18n/ku-latn.json
languages/i18n/lki.json
languages/i18n/nds.json
languages/i18n/qqq.json
languages/i18n/sa.json
languages/i18n/uk.json
maintenance/convertExtensionToRegistration.php [changed mode: 0644->0755]
maintenance/mssql/archives/named_constraints.sql [deleted file]
maintenance/mssql/archives/patch-add-cl_collation_ext_index.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-bot_passwords.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-categorylinks-constraints.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-drop-page_counter.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-drop-rc_cur_time.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-drop-ss_total_views.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-drop-user_options.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-filearchive-constraints.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-filearchive-schema.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-il_from_namespace.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-image-constraints.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-image-schema.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-kill-cl_collation_index.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-oldimage-constraints.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-oldimage-schema.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-pl_from_namespace.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-pp_sortkey.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-tl_from_namespace.sql [new file with mode: 0644]
maintenance/mssql/archives/patch-uploadstash-constraints.sql [new file with mode: 0644]
maintenance/mssql/tables.sql
maintenance/resources/update-oojs-ui.sh
resources/Resources.php
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-core-apex.css
resources/lib/oojs-ui/oojs-ui-core-mediawiki.css
resources/lib/oojs-ui/oojs-ui-core.js
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui-toolbars-apex.css
resources/lib/oojs-ui/oojs-ui-toolbars-mediawiki.css
resources/lib/oojs-ui/oojs-ui-toolbars.js
resources/lib/oojs-ui/oojs-ui-widgets-apex.css
resources/lib/oojs-ui/oojs-ui-widgets-mediawiki.css
resources/lib/oojs-ui/oojs-ui-widgets.js
resources/lib/oojs-ui/oojs-ui-windows-apex.css
resources/lib/oojs-ui/oojs-ui-windows-mediawiki.css
resources/lib/oojs-ui/oojs-ui-windows.js
resources/src/jquery/jquery.makeCollapsible.js
resources/src/mediawiki.legacy/shared.css
resources/src/mediawiki.skinning/content.parsoid.less
resources/src/mediawiki.widgets/mw.widgets.TitleWidget.js
resources/src/mediawiki/api.js
resources/src/mediawiki/mediawiki.apihelp.css
skins/README
tests/phpunit/includes/GlobalFunctions/wfAppendQueryTest.php
tests/phpunit/includes/MessageTest.php
tests/phpunit/includes/registration/ExtensionProcessorTest.php
tests/phpunit/includes/session/SessionTest.php

index 9b77cd1..3a0326e 100644 (file)
@@ -13,6 +13,8 @@ HHVM 3.1. Additionally, the following PHP extensions are required:
 * json
 * mbstring
 * xml
+The following PHP extensions are strongly recommended:
+* openssl
 
 === Configuration changes in 1.27 ===
 * $wgAllowMicrodataAttributes and $wgAllowRdfaAttributes were removed,
@@ -114,6 +116,9 @@ HHVM 3.1. Additionally, the following PHP extensions are required:
   module should express a dependency on it.
 * Removed configuration option $wgCopyrightIcon (deprecated since 1.18). Use
   $wgFooterIcons['copyright']['copyright'] instead.
+* If the openssl and mcrypt PHP extensions are both unavailable, secure
+  session storage (soon to be used for login) will raise an exception. This
+  exception may be bypassed by setting $wgSessionInsecureSecrets = true.
 
 === New features in 1.27 ===
 * $wgDataCenterUpdateStickTTL was also added. This decides how long a user
index 4fb107e..57531c1 100644 (file)
@@ -25,7 +25,7 @@
                "ext-xml": "*",
                "liuggio/statsd-php-client": "1.0.18",
                "mediawiki/at-ease": "1.1.0",
-               "oojs/oojs-ui": "0.17.0",
+               "oojs/oojs-ui": "0.17.1",
                "oyejorge/less.php": "1.7.0.10",
                "php": ">=5.5.9",
                "psr/log": "1.0.0",
old mode 100644 (file)
new mode 100755 (executable)
index 3c2c057..ed3eaa9
                        "type": "object"
                },
                "Hooks": {
-                       "type": "object",
+                       "type": [ "string", "object" ],
                        "description": "Hooks this extension uses (mapping of hook name to callback)"
                },
                "JobClasses": {
index bad230e..923a19b 100644 (file)
@@ -11,8 +11,7 @@ directory and make a symbolic link:
  mediawiki/extensions$ ln -s ../../extensions-trunk/FooBar
 
 Most extensions are available through Git:
-    https://gerrit.wikimedia.org/r/#/admin/projects/?filter=mediawiki%252Fextensions%252F
-    https://git.wikimedia.org/project/mediawiki
+    https://phabricator.wikimedia.org/diffusion/MEXT/
 
 
 Please note that under POSIX systems (Linux...), parent of a symbolic path
index 5b3684b..ab9a28d 100644 (file)
@@ -7990,6 +7990,23 @@ $wgPagePropsHaveSortkey = true;
  */
 $wgHttpsPort = 443;
 
+/**
+ * Secret for session storage.
+ * This should be set in LocalSettings.php, otherwise wgSecretKey will
+ * be used.
+ * @since 1.27
+ */
+$wgSessionSecret = false;
+
+/**
+ * If for some reason you can't install the PHP OpenSSL or mcrypt extensions,
+ * you can set this to true to make MediaWiki work again at the cost of storing
+ * sensitive session data insecurely. But it would be much more secure to just
+ * install the OpenSSL extension.
+ * @since 1.27
+ */
+$wgSessionInsecureSecrets = false;
+
 /**
  * Secret for hmac-based key derivation function (fast,
  * cryptographically secure random numbers).
index 8f6b202..9112112 100644 (file)
@@ -2838,7 +2838,7 @@ class EditPage {
                                                '{{fullurl:Special:UserLogin/signup|returnto={{FULLPAGENAMEE}}}}' ]
                                );
                        } else {
-                               $wgOut->wrapWikiMsg( "<div id=\"mw-anon-preview-warning\">\n$1</div>",
+                               $wgOut->wrapWikiMsg( "<div id=\"mw-anon-preview-warning\" class=\"warningbox\">\n$1</div>",
                                        'anonpreviewwarning'
                                );
                        }
index 5c42bc2..b5de66f 100644 (file)
@@ -501,12 +501,26 @@ function wfAppendQuery( $url, $query ) {
                $query = wfArrayToCgi( $query );
        }
        if ( $query != '' ) {
+               // Remove the fragment, if there is one
+               $fragment = false;
+               $hashPos = strpos( $url, '#' );
+               if ( $hashPos !== false ) {
+                       $fragment = substr( $url, $hashPos );
+                       $url = substr( $url, 0, $hashPos );
+               }
+
+               // Add parameter
                if ( false === strpos( $url, '?' ) ) {
                        $url .= '?';
                } else {
                        $url .= '&';
                }
                $url .= $query;
+
+               // Put the fragment back
+               if ( $fragment !== false ) {
+                       $url .= $fragment;
+               }
        }
        return $url;
 }
index fd016fc..c7752aa 100644 (file)
@@ -382,6 +382,32 @@ class Message implements MessageSpecifier, Serializable {
                return new self( $key, $params );
        }
 
+       /**
+        * Transform a MessageSpecifier or a primitive value used interchangeably with
+        * specifiers (a message key string, or a key + params array) into a proper Message
+        * @param string|array|MessageSpecifier $value
+        * @return Message
+        * @throws InvalidArgumentException
+        * @since 1.27
+        */
+       public static function newFromSpecifier( $value ) {
+               if ( $value instanceof RawMessage ) {
+                       $message = new RawMessage( $value->getKey(), $value->getParams() );
+               } elseif ( $value instanceof MessageSpecifier ) {
+                       $message = new Message( $value );
+               } elseif ( is_array( $value ) ) {
+                       $key = array_shift( $value );
+                       $message = new Message( $key, $value );
+               } elseif ( is_string( $value ) ) {
+                       $message = new Message( $value );
+               } else {
+                       throw new InvalidArgumentException( __METHOD__ . ': invalid argument type '
+                               . gettype( $value ) );
+               }
+
+               return $message;
+       }
+
        /**
         * Factory function accepting multiple message keys and returning a message instance
         * for the first message which is non-empty. If all messages are empty then an
index f7539ce..0f0fbdc 100644 (file)
@@ -255,28 +255,43 @@ class ApiHelp extends ApiBase {
                                }
 
                                if ( $module->isMain() ) {
-                                       $header = $context->msg( 'api-help-main-header' )->parse();
+                                       $headerContent = $context->msg( 'api-help-main-header' )->parse();
+                                       $headerAttr = [
+                                               'class' => 'apihelp-header',
+                                       ];
                                } else {
                                        $name = $module->getModuleName();
-                                       $header = $module->getParent()->getModuleManager()->getModuleGroup( $name ) .
+                                       $headerContent = $module->getParent()->getModuleManager()->getModuleGroup( $name ) .
                                                "=$name";
                                        if ( $module->getModulePrefix() !== '' ) {
-                                               $header .= ' ' .
+                                               $headerContent .= ' ' .
                                                        $context->msg( 'parentheses', $module->getModulePrefix() )->parse();
                                        }
+                                       // Module names are always in English and not localized,
+                                       // so English language and direction must be set explicitly,
+                                       // otherwise parentheses will get broken in RTL wikis
+                                       $headerAttr = [
+                                               'class' => 'apihelp-header apihelp-module-name',
+                                               'dir' => 'ltr',
+                                               'lang' => 'en',
+                                       ];
                                }
+
+                               $headerAttr['id'] = $anchor;
+
                                $haveModules[$anchor] = [
                                        'toclevel' => count( $tocnumber ),
                                        'level' => $level,
                                        'anchor' => $anchor,
-                                       'line' => $header,
+                                       'line' => $headerContent,
                                        'number' => implode( '.', $tocnumber ),
                                        'index' => false,
                                ];
                                if ( empty( $options['noheader'] ) ) {
-                                       $help['header'] .= Html::element( 'h' . min( 6, $level ),
-                                               [ 'id' => $anchor, 'class' => 'apihelp-header' ],
-                                               $header
+                                       $help['header'] .= Html::element(
+                                               'h' . min( 6, $level ),
+                                               $headerAttr,
+                                               $headerContent
                                        );
                                }
                        } else {
index e51d46d..8bfe447 100644 (file)
@@ -86,8 +86,7 @@ class ApiOptions extends ApiBase {
                                                // We need a dummy HTMLForm for the validate callback...
                                                $htmlForm = new HTMLForm( [], $this );
                                        }
-                                       $field = HTMLForm::loadInputFromParameters( $key, $prefs[$key] );
-                                       $field->mParent = $htmlForm;
+                                       $field = HTMLForm::loadInputFromParameters( $key, $prefs[$key], $htmlForm );
                                        $validation = $field->validate( $value, $user->getOptions() );
                                        break;
                                case 'registered-multiselect':
index 74c2214..f0fd2f4 100644 (file)
@@ -206,7 +206,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                        ) {
                                if ( !$user->useRCPatrol() && !$user->useNPPatrol() ) {
                                        $this->dieUsage(
-                                               'You need the patrol right to request the patrolled flag',
+                                               'You need patrol or patrolmarks permission to request the patrolled flag',
                                                'permissiondenied'
                                        );
                                }
@@ -277,7 +277,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
 
                        if ( $this->fld_patrolled && !$user->useRCPatrol() && !$user->useNPPatrol() ) {
                                $this->dieUsage(
-                                       'You need the patrol right to request the patrolled flag',
+                                       'You need patrol or patrolmarks permission to request the patrolled flag',
                                        'permissiondenied'
                                );
                        }
index 1571b27..0a79aa4 100644 (file)
@@ -488,6 +488,16 @@ class ApiUpload extends ApiBase {
 
                        $this->dieUsageMsg( 'badaccess-groups' );
                }
+
+               // Check blocks
+               if ( $user->isBlocked() ) {
+                       $this->dieBlocked( $user->getBlock() );
+               }
+
+               // Global blocks
+               if ( $user->isBlockedGlobally() ) {
+                       $this->dieBlocked( $user->getGlobalBlock() );
+               }
        }
 
        /**
index 9997caf..12ec506 100644 (file)
@@ -30,6 +30,9 @@
        "apihelp-login-example-login": "Têkeve.",
        "apihelp-move-param-reason": "Sedemê bo guherandina nav.",
        "apihelp-move-param-ignorewarnings": "Guh nede hişyariyan.",
+       "apihelp-opensearch-example-te": "Rûpelên ku bi <kbd>Te</kbd> dest pê dikin bibîne.",
+       "apihelp-parse-example-page": "Rûpelekê analîz bike.",
+       "apihelp-parse-example-summary": "Kurteyekê analîz bike",
        "apihelp-protect-description": "Asta parastinê ya rûpelekê biguherîne.",
        "apihelp-protect-example-protect": "Rûpelekê biparêze.",
        "apihelp-tag-param-reason": "Sedemê bo guherandinê."
index 418fec2..a9b9099 100644 (file)
@@ -27,7 +27,7 @@
        "apihelp-block-param-anononly": "Blokuj tylko anonimowych użytkowników (blokuje anonimowe edycje z tego adresu IP).",
        "apihelp-block-param-nocreate": "Zapobiegnij utworzeniu konta.",
        "apihelp-block-param-autoblock": "Zablokuj ostatni adres IP tego użytkownika i automatycznie wszystkie kolejne, z których będzie się logował.",
-       "apihelp-block-param-noemail": "Uniemożliwia użytkownikowi wysyłanie wiadomości e-mail za pośrednictwem interfejsu wiki. (Wymagane uprawnienia <code>blockemail</code>).",
+       "apihelp-block-param-noemail": "Uniemożliwia użytkownikowi wysyłanie wiadomości e-mail za pośrednictwem interfejsu wiki. (Wymagane uprawnienie <code>blockemail</code>).",
        "apihelp-block-param-hidename": "Ukryj nazwę użytkownika z rejestru blokad. (Wymagane uprawnienia <code>hideuser</code>)",
        "apihelp-block-param-allowusertalk": "Pozwala użytkownikowi edytować własną stronę dyskusji (zależy od <var>[[mw:Manual:$wgBlockAllowsUTEdit|$wgBlockAllowsUTEdit]]</var>).",
        "apihelp-block-param-reblock": "Jeżeli ten użytkownik jest już zablokowany, nadpisz blokadę.",
index a808516..1070877 100644 (file)
@@ -262,6 +262,7 @@ class EnhancedChangesList extends ChangesList {
                        if ( !$line ) {
                                // completely ignore this RC entry if we don't want to render it
                                unset( $block[$i] );
+                               continue;
                        }
 
                        // Roll up flags
@@ -286,6 +287,7 @@ class EnhancedChangesList extends ChangesList {
 
                        $lines[] = $line;
                }
+
                // Further down are some assumptions that $block is a 0-indexed array
                // with (count-1) as last key. Let's make sure it is.
                $block = array_values( $block );
index c36cfdb..8807e13 100644 (file)
@@ -1847,7 +1847,7 @@ abstract class DatabaseBase implements IDatabase {
                if ( !$alias || (string)$alias === (string)$name ) {
                        return $name;
                } else {
-                       return $name . ' AS ' . $alias; // PostgreSQL needs AS
+                       return $name . ' AS ' . $this->addIdentifierQuotes( $alias ); // PostgreSQL needs AS
                }
        }
 
index 5c46c1a..33f8162 100644 (file)
@@ -38,6 +38,7 @@ class DatabaseMssql extends Database {
        protected $mBinaryColumnCache = null;
        protected $mBitColumnCache = null;
        protected $mIgnoreDupKeyErrors = false;
+       protected $mIgnoreErrors = [];
 
        protected $mPort;
 
@@ -206,35 +207,31 @@ class DatabaseMssql extends Database {
                        $success = (bool)$stmt;
                }
 
+               // make a copy so that anything we add below does not get reflected in future queries
+               $ignoreErrors = $this->mIgnoreErrors;
+
                if ( $this->mIgnoreDupKeyErrors ) {
-                       // ignore duplicate key errors, but nothing else
+                       // ignore duplicate key errors
                        // this emulates INSERT IGNORE in MySQL
-                       if ( $success === false ) {
-                               $errors = sqlsrv_errors( SQLSRV_ERR_ERRORS );
-                               $success = true;
-
-                               foreach ( $errors as $err ) {
-                                       if ( $err['SQLSTATE'] == '23000' && $err['code'] == '2601' ) {
-                                               continue; // duplicate key error caused by unique index
-                                       } elseif ( $err['SQLSTATE'] == '23000' && $err['code'] == '2627' ) {
-                                               continue; // duplicate key error caused by primary key
-                                       } elseif ( $err['SQLSTATE'] == '01000' && $err['code'] == '3621' ) {
-                                               continue; // generic "the statement has been terminated" error
-                                       }
+                       $ignoreErrors[] = '2601'; // duplicate key error caused by unique index
+                       $ignoreErrors[] = '2627'; // duplicate key error caused by primary key
+                       $ignoreErrors[] = '3621'; // generic "the statement has been terminated" error
+               }
 
-                                       $success = false; // getting here means we got an error we weren't expecting
-                                       break;
-                               }
+               if ( $success === false ) {
+                       $errors = sqlsrv_errors();
+                       $success = true;
 
-                               if ( $success ) {
-                                       $this->mAffectedRows = 0;
-                                       return $stmt;
+                       foreach ( $errors as $err ) {
+                               if ( !in_array( $err['code'], $ignoreErrors ) ) {
+                                       $success = false;
+                                       break;
                                }
                        }
-               }
 
-               if ( $success === false ) {
-                       return false;
+                       if ( $success === false ) {
+                               return false;
+                       }
                }
                // remember number of rows affected
                $this->mAffectedRows = sqlsrv_rows_affected( $stmt );
@@ -276,7 +273,15 @@ class DatabaseMssql extends Database {
                        $res = $res->result;
                }
 
-               return sqlsrv_num_rows( $res );
+               $ret = sqlsrv_num_rows( $res );
+
+               if ( $ret === false ) {
+                       // we cannot get an amount of rows from this cursor type
+                       // has_rows returns bool true/false if the result has rows
+                       $ret = (int)sqlsrv_has_rows( $res );
+               }
+
+               return $ret;
        }
 
        /**
@@ -536,8 +541,9 @@ class DatabaseMssql extends Database {
                # This does not return the same info as MYSQL would, but that's OK
                # because MediaWiki never uses the returned value except to check for
                # the existance of indexes.
-               $sql = "sp_helpindex '" . $table . "'";
+               $sql = "sp_helpindex '" . $this->tableName( $table ) . "'";
                $res = $this->query( $sql, $fname );
+
                if ( !$res ) {
                        return null;
                }
@@ -696,6 +702,12 @@ class DatabaseMssql extends Database {
                                $row = $ret->fetchObject();
                                if ( is_object( $row ) ) {
                                        $this->mInsertId = $row->$identity;
+
+                                       // it seems that mAffectedRows is -1 sometimes when OUTPUT INSERTED.identity is used
+                                       // if we got an identity back, we know for sure a row was affected, so adjust that here
+                                       if ( $this->mAffectedRows == -1 ) {
+                                               $this->mAffectedRows = 1;
+                                       }
                                }
                        }
                }
@@ -1351,6 +1363,24 @@ class DatabaseMssql extends Database {
                return $table;
        }
 
+       /**
+        * Delete a table
+        * @param string $tableName
+        * @param string $fName
+        * @return bool|ResultWrapper
+        * @since 1.18
+        */
+       public function dropTable( $tableName, $fName = __METHOD__ ) {
+               if ( !$this->tableExists( $tableName, $fName ) ) {
+                       return false;
+               }
+
+               // parent function incorrectly appends CASCADE, which we don't want
+               $sql = "DROP TABLE " . $this->tableName( $tableName );
+
+               return $this->query( $sql, $fName );
+       }
+
        /**
         * Called in the installer and updater.
         * Probably doesn't need to be called anywhere else in the codebase.
@@ -1370,6 +1400,16 @@ class DatabaseMssql extends Database {
        public function scrollableCursor( $value = null ) {
                return wfSetVar( $this->mScrollableCursor, $value );
        }
+
+       /**
+        * Called in the installer and updater.
+        * Probably doesn't need to be called anywhere else in the codebase.
+        * @param array|null $value
+        * @return array|null
+        */
+       public function ignoreErrors( array $value = null ) {
+               return wfSetVar( $this->mIgnoreErrors, $value );
+       }
 } // end DatabaseMssql class
 
 /**
index 4215ed0..c0205be 100644 (file)
@@ -55,6 +55,9 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
        /** @var array Map of language codes to titles */
        public $mInterlangs;
 
+       /** @var array 2-D map of (prefix => DBK => 1) */
+       public $mInterwikis;
+
        /** @var array Map of arbitrary name to value */
        public $mProperties;
 
index c432025..3b20048 100644 (file)
@@ -385,6 +385,11 @@ class FileBackendMultiWrite extends FileBackend {
                        }
                }
 
+               if ( !$status->isOK() ) {
+                       wfDebugLog( 'FileOperation', get_class( $this ) .
+                               " failed to resync: " . FormatJson::encode( $paths ) );
+               }
+
                return $status;
        }
 
index d671029..0dab3bb 100644 (file)
@@ -569,7 +569,7 @@ class HTMLForm extends ContextSource {
 
                # Check for cancelled submission
                foreach ( $this->mFlatFields as $fieldname => $field ) {
-                       if ( !empty( $field->mParams['nodata'] ) ) {
+                       if ( !array_key_exists( $fieldname, $this->mFieldData ) ) {
                                continue;
                        }
                        if ( $field->cancelSubmit( $this->mFieldData[$fieldname], $this->mFieldData ) ) {
@@ -580,7 +580,7 @@ class HTMLForm extends ContextSource {
 
                # Check for validation
                foreach ( $this->mFlatFields as $fieldname => $field ) {
-                       if ( !empty( $field->mParams['nodata'] ) ) {
+                       if ( !array_key_exists( $fieldname, $this->mFieldData ) ) {
                                continue;
                        }
                        if ( $field->isHidden( $this->mFieldData ) ) {
@@ -1117,7 +1117,7 @@ class HTMLForm extends ContextSource {
                        ];
 
                        if ( isset( $button['label-message'] ) ) {
-                               $label = $this->msg( $button['label-message'] )->parse();
+                               $label = $this->getMessage( $button['label-message'] )->parse();
                        } elseif ( isset( $button['label'] ) ) {
                                $label = htmlspecialchars( $button['label'] );
                        } elseif ( isset( $button['label-raw'] ) ) {
@@ -1198,17 +1198,10 @@ class HTMLForm extends ContextSource {
                $errorstr = '';
 
                foreach ( $errors as $error ) {
-                       if ( is_array( $error ) ) {
-                               $msg = array_shift( $error );
-                       } else {
-                               $msg = $error;
-                               $error = [];
-                       }
-
                        $errorstr .= Html::rawElement(
                                'li',
                                [],
-                               $this->msg( $msg, $error )->parse()
+                               $this->getMessage( $error )->parse()
                        );
                }
 
@@ -1725,4 +1718,14 @@ class HTMLForm extends ContextSource {
 
                return $this;
        }
+
+       /**
+        * Turns a *-message parameter (which could be a MessageSpecifier, or a message name, or a
+        * name + parameters array) into a Message.
+        * @param mixed $value
+        * @return Message
+        */
+       protected function getMessage( $value ) {
+               return Message::newFromSpecifier( $value )->setContext( $this );
+       }
 }
index d14fa90..9f5e728 100644 (file)
@@ -28,7 +28,7 @@ abstract class HTMLFormField {
        protected $mShowEmptyLabels = true;
 
        /**
-        * @var HTMLForm
+        * @var HTMLForm|null
         */
        public $mParent;
 
@@ -1095,16 +1095,13 @@ abstract class HTMLFormField {
         * @return Message
         */
        protected function getMessage( $value ) {
-               if ( $value instanceof Message ) {
-                       return $value;
-               } elseif ( $value instanceof MessageSpecifier ) {
-                       return Message::newFromKey( $value );
-               } elseif ( is_array( $value ) ) {
-                       $msg = array_shift( $value );
-                       return $this->msg( $msg, $value );
-               } else {
-                       return $this->msg( $value, [] );
+               $message = Message::newFromSpecifier( $value );
+
+               if ( $this->mParent ) {
+                       $message->setContext( $this->mParent );
                }
+
+               return $message;
        }
 
        /**
index 3f80884..ec1bd84 100644 (file)
@@ -207,7 +207,7 @@ class HTMLFormFieldCloner extends HTMLFormField {
                foreach ( $values as $key => $value ) {
                        $fields = $this->createFieldsForKey( $key );
                        foreach ( $fields as $fieldname => $field ) {
-                               if ( !empty( $field->mParams['nodata'] ) ) {
+                               if ( !array_key_exists( $fieldname, $value ) ) {
                                        continue;
                                }
                                if ( $field->cancelSubmit( $value[$fieldname], $alldata ) ) {
@@ -237,7 +237,7 @@ class HTMLFormFieldCloner extends HTMLFormField {
                foreach ( $values as $key => $value ) {
                        $fields = $this->createFieldsForKey( $key );
                        foreach ( $fields as $fieldname => $field ) {
-                               if ( !empty( $field->mParams['nodata'] ) ) {
+                               if ( !array_key_exists( $fieldname, $value ) ) {
                                        continue;
                                }
                                $ok = $field->validate( $value[$fieldname], $alldata );
index 278d453..7a2ed50 100644 (file)
@@ -100,7 +100,7 @@ class OOUIHTMLForm extends HTMLForm {
                        if ( $isBadIE ) {
                                $label = $button['value'];
                        } elseif ( isset( $button['label-message'] ) ) {
-                               $label = new OOUI\HtmlSnippet( $this->msg( $button['label-message'] )->parse() );
+                               $label = new OOUI\HtmlSnippet( $this->getMessage( $button['label-message'] )->parse() );
                        } elseif ( isset( $button['label'] ) ) {
                                $label = $button['label'];
                        } elseif ( isset( $button['label-raw'] ) ) {
@@ -198,18 +198,7 @@ class OOUIHTMLForm extends HTMLForm {
                }
 
                foreach ( $errors as &$error ) {
-                       if ( is_array( $error ) ) {
-                               $msg = array_shift( $error );
-                       } else {
-                               $msg = $error;
-                               $error = [];
-                       }
-                       // if the error is already a message object, don't use it as a message key
-                       if ( !$msg instanceof Message ) {
-                               $error = $this->msg( $msg, $error )->parse();
-                       } else {
-                               $error = $msg->parse();
-                       }
+                       $error = $this->getMessage( $error )->parse();
                        $error = new OOUI\HtmlSnippet( $error );
                }
 
index 3d1c860..d747707 100644 (file)
@@ -1697,7 +1697,7 @@ abstract class Installer {
                                wfMessage( 'mainpagedocfooter' )->inContentLanguage()->text()
                        );
 
-                       $page->doEditContent( $content,
+                       $status = $page->doEditContent( $content,
                                '',
                                EDIT_NEW,
                                false,
index c6b8960..62cd883 100644 (file)
@@ -500,19 +500,19 @@ class MssqlInstaller extends DatabaseInstaller {
                                "CREATE DATABASE " . $conn->addIdentifierQuotes( $dbName ),
                                __METHOD__
                        );
-                       $conn->selectDB( $dbName );
-                       if ( !$this->schemaExists( $schemaName ) ) {
-                               $conn->query(
-                                       "CREATE SCHEMA " . $conn->addIdentifierQuotes( $schemaName ),
-                                       __METHOD__
-                               );
-                       }
-                       if ( !$this->catalogExists( $schemaName ) ) {
-                               $conn->query(
-                                       "CREATE FULLTEXT CATALOG " . $conn->addIdentifierQuotes( $schemaName ),
-                                       __METHOD__
-                               );
-                       }
+               }
+               $conn->selectDB( $dbName );
+               if ( !$this->schemaExists( $schemaName ) ) {
+                       $conn->query(
+                               "CREATE SCHEMA " . $conn->addIdentifierQuotes( $schemaName ),
+                               __METHOD__
+                       );
+               }
+               if ( !$this->catalogExists( $schemaName ) ) {
+                       $conn->query(
+                               "CREATE FULLTEXT CATALOG " . $conn->addIdentifierQuotes( $schemaName ),
+                               __METHOD__
+                       );
                }
                $this->setupSchemaVars();
 
index bdaf4c8..a6ab05c 100644 (file)
@@ -41,22 +41,24 @@ class MssqlUpdater extends DatabaseUpdater {
                        [ 'addField', 'mwuser', 'user_password_expires', 'patch-user_password_expires.sql' ],
 
                        // 1.24
-                       [ 'addField', 'page', 'page_lang', 'patch-page-page_lang.sql' ],
+                       [ 'addField', 'page', 'page_lang', 'patch-page_page_lang.sql' ],
 
                        // 1.25
                        [ 'dropTable', 'hitcounter' ],
                        [ 'dropField', 'site_stats', 'ss_total_views', 'patch-drop-ss_total_views.sql' ],
                        [ 'dropField', 'page', 'page_counter', 'patch-drop-page_counter.sql' ],
-                       // Constraint updates
-                       [ 'updateConstraints', 'category_types', 'categorylinks', 'cl_type' ],
-                       [ 'updateConstraints', 'major_mime', 'filearchive', 'fa_major_mime' ],
-                       [ 'updateConstraints', 'media_type', 'filearchive', 'fa_media_type' ],
-                       [ 'updateConstraints', 'major_mime', 'oldimage', 'oi_major_mime' ],
-                       [ 'updateConstraints', 'media_type', 'oldimage', 'oi_media_type' ],
-                       [ 'updateConstraints', 'major_mime', 'image', 'img_major_mime' ],
-                       [ 'updateConstraints', 'media_type', 'image', 'img_media_type' ],
-                       [ 'updateConstraints', 'media_type', 'uploadstash', 'us_media_type' ],
-                       // END: Constraint updates
+                       // scripts were updated in 1.27 due to SQL errors; retaining old updatekeys so that people
+                       // updating from 1.23->1.25->1.27 do not execute these scripts twice even though the
+                       // updatekeys no longer make sense as they are.
+                       [ 'updateSchema', 'categorylinks', 'cl_type-category_types-ck',
+                               'patch-categorylinks-constraints.sql' ],
+                       [ 'updateSchema', 'filearchive', 'fa_major_mime-major_mime-ck',
+                               'patch-filearchive-constraints.sql' ],
+                       [ 'updateSchema', 'oldimage', 'oi_major_mime-major_mime-ck',
+                               'patch-oldimage-constraints.sql' ],
+                       [ 'updateSchema', 'image', 'img_major_mime-major_mime-ck', 'patch-image-constraints.sql' ],
+                       [ 'updateSchema', 'uploadstash', 'us_media_type-media_type-ck',
+                               'patch-uploadstash-constraints.sql' ],
 
                        [ 'modifyField', 'image', 'img_major_mime',
                                'patch-img_major_mime-chemical.sql' ],
@@ -69,79 +71,49 @@ class MssqlUpdater extends DatabaseUpdater {
                        [ 'dropTable', 'msg_resource_links' ],
                        [ 'dropTable', 'msg_resource' ],
                        [ 'addField', 'watchlist', 'wl_id', 'patch-watchlist-wl_id.sql' ],
+                       [ 'dropField', 'mwuser', 'user_options', 'patch-drop-user_options.sql' ],
+                       [ 'addTable', 'bot_passwords', 'patch-bot_passwords.sql' ],
+                       [ 'addField', 'pagelinks', 'pl_from_namespace', 'patch-pl_from_namespace.sql' ],
+                       [ 'addField', 'templatelinks', 'tl_from_namespace', 'patch-tl_from_namespace.sql' ],
+                       [ 'addField', 'imagelinks', 'il_from_namespace', 'patch-il_from_namespace.sql' ],
+                       [ 'dropIndex', 'categorylinks', 'cl_collation', 'patch-kill-cl_collation_index.sql' ],
+                       [ 'addIndex', 'categorylinks', 'cl_collation_ext',
+                               'patch-add-cl_collation_ext_index.sql' ],
+                       [ 'dropField', 'recentchanges', 'rc_cur_time', 'patch-drop-rc_cur_time.sql' ],
+                       [ 'addField', 'page_props', 'pp_sortkey', 'patch-pp_sortkey.sql' ],
+                       [ 'updateSchema', 'oldimage', 'oldimage varchar', 'patch-oldimage-schema.sql' ],
+                       [ 'updateSchema', 'filearchive', 'filearchive varchar', 'patch-filearchive-schema.sql' ],
+                       [ 'updateSchema', 'image', 'image varchar', 'patch-image-schema.sql' ]
                ];
        }
 
+       protected function applyPatch( $path, $isFullPath = false, $msg = null ) {
+               $prevScroll = $this->db->scrollableCursor( false );
+               $prevPrep = $this->db->prepareStatements( false );
+               parent::applyPatch( $path, $isFullPath, $msg );
+               $this->db->scrollableCursor( $prevScroll );
+               $this->db->prepareStatements( $prevPrep );
+       }
+
        /**
-        * Drops unnamed and creates named constraints following the pattern
-        * <column>_ckc
+        * General schema update for a table that touches more than one field or requires
+        * destructive actions (such as dropping and recreating the table).
         *
-        * @param string $constraintType
-        * @param string $table Name of the table to which the field belongs
-        * @param string $field Name of the field to modify
-        * @return bool False if patch is skipped.
+        * @param string $table
+        * @param string $updatekey
+        * @param string $patch
+        * @param bool $fullpath
         */
-       protected function updateConstraints( $constraintType, $table, $field ) {
-               global $wgDBname, $wgDBmwschema;
-
-               if ( !$this->doTable( $table ) ) {
-                       return true;
-               }
-
-               $this->output( "...updating constraints on [$table].[$field] ..." );
-               $updateKey = "$field-$constraintType-ck";
+       protected function updateSchema( $table, $updatekey, $patch, $fullpath = false ) {
                if ( !$this->db->tableExists( $table, __METHOD__ ) ) {
-                       $this->output( "...$table table does not exist, skipping modify field patch.\n" );
-                       return true;
-               } elseif ( !$this->db->fieldExists( $table, $field, __METHOD__ ) ) {
-                       $this->output( "...$field field does not exist in $table table, " .
-                               "skipping modify field patch.\n" );
-                       return true;
-               } elseif ( $this->updateRowExists( $updateKey ) ) {
-                       $this->output( "...$field in table $table already patched.\n" );
-                       return true;
-               }
-
-               # After all checks passed, start the update
-               $this->insertUpdateRow( $updateKey );
-               $path = 'named_constraints.sql';
-               $constraintMap = [
-                       'category_types' =>
-                               "($field in('page', 'subcat', 'file'))",
-                       'major_mime'     =>
-                               "($field in('unknown', 'application', 'audio', 'image', 'text', 'video'," .
-                               " 'message', 'model', 'multipart'))",
-                       'media_type'     =>
-                               "($field in('UNKNOWN', 'BITMAP', 'DRAWING', 'AUDIO', 'VIDEO', 'MULTIMEDIA'," .
-                               "'OFFICE', 'TEXT', 'EXECUTABLE', 'ARCHIVE'))"
-               ];
-               $constraint = $constraintMap[$constraintType];
-
-               # and hack-in those variables that should be replaced
-               # in our template file right now
-               $this->db->setSchemaVars( [
-                       'tableName'       => $table,
-                       'fieldName'       => $field,
-                       'checkConstraint' => $constraint,
-                       'wgDBname'        => $wgDBname,
-                       'wgDBmwschema'    => $wgDBmwschema,
-               ] );
-
-               # Full path from file name
-               $path = $this->db->patchPath( $path );
-
-               # No need for a cursor allowing result-iteration; just apply a patch
-               # store old value for re-setting later
-               $wasScrollable = $this->db->scrollableCursor( false );
+                       $this->output( "...$table table does not exist, skipping schema update patch.\n" );
+               } elseif ( $this->updateRowExists( $updatekey ) ) {
+                       $this->output( "...$table already had schema updated by $patch.\n" );
+               } else {
+                       $this->insertUpdateRow( $updatekey );
 
-               # Apply patch
-               $this->db->sourceFile( $path );
-
-               # Reset DB instance to have original state
-               $this->db->setSchemaVars( false );
-               $this->db->scrollableCursor( $wasScrollable );
-
-               $this->output( "done.\n" );
+                       return $this->applyPatch( $patch, $fullpath, "Updating schema of table $table" );
+               }
 
                return true;
        }
index 15ee980..c297715 100644 (file)
@@ -154,6 +154,13 @@ class RefreshLinksJob extends Job {
                        return false; // just deleted?
                }
 
+               if ( !$revision->isCurrent() ) {
+                       // If the revision isn't current, there's no point in doing a bunch
+                       // of work just to fail at the lockAndGetLatest() check later.
+                       $this->setLastError( "Revision {$revision->getId()} is not current" );
+                       return false;
+               }
+
                $content = $revision->getContent( Revision::RAW );
                if ( !$content ) {
                        // If there is no content, pretend the content is empty
@@ -185,17 +192,21 @@ class RefreshLinksJob extends Job {
                        }
 
                        if ( $page->getTouched() >= $skewedTimestamp || $opportunistic ) {
-                               // Something bumped page_touched since this job was made
-                               // or the cache is otherwise suspected to be up-to-date
+                               // Something bumped page_touched since this job was made or the cache is
+                               // otherwise suspected to be up-to-date. As long as the cache rev ID matches
+                               // and it reflects the job's triggering change, then it is usable.
                                $parserOutput = ParserCache::singleton()->getDirty( $page, $parserOptions );
-                               if ( $parserOutput && $parserOutput->getCacheTime() < $skewedTimestamp ) {
+                               if ( !$parserOutput
+                                       || $parserOutput->getCacheRevisionId() != $revision->getId()
+                                       || $parserOutput->getCacheTime() < $skewedTimestamp
+                               ) {
                                        $parserOutput = false; // too stale
                                }
                        }
                }
 
                // Fetch the current revision and parse it if necessary...
-               if ( $parserOutput == false ) {
+               if ( !$parserOutput ) {
                        $start = microtime( true );
                        // Revision ID must be passed to the parser output to get revision variables correct
                        $parserOutput = $content->getParserOutput(
index 5876e0b..c6265a7 100644 (file)
@@ -150,7 +150,7 @@ class ParserCache {
                                        "Parser options key expired, touched " . $article->getTouched()
                                        . ", epoch $wgCacheEpoch, cached $cacheTime\n" );
                                return false;
-                       } elseif ( $optionsKey->isDifferentRevision( $article->getLatest() ) ) {
+                       } elseif ( !$useOutdated && $optionsKey->isDifferentRevision( $article->getLatest() ) ) {
                                wfIncrStats( "pcache.miss.revid" );
                                $revId = $article->getLatest();
                                $cachedRevId = $optionsKey->getCacheRevisionId();
@@ -230,7 +230,7 @@ class ParserCache {
                                "ParserOutput key expired, touched $touched, "
                                . "epoch $wgCacheEpoch, cached $cacheTime\n" );
                        $value = false;
-               } elseif ( $value->isDifferentRevision( $article->getLatest() ) ) {
+               } elseif ( !$useOutdated && $value->isDifferentRevision( $article->getLatest() ) ) {
                        wfIncrStats( "pcache.miss.revid" );
                        $revId = $article->getLatest();
                        $cachedRevId = $value->getCacheRevisionId();
@@ -262,7 +262,7 @@ class ParserCache {
         */
        public function save( $parserOutput, $page, $popts, $cacheTime = null, $revId = null ) {
                $expire = $parserOutput->getCacheExpiry();
-               if ( $expire > 0 ) {
+               if ( $expire > 0 && !$this->mMemc instanceof EmptyBagOStuff ) {
                        $cacheTime = $cacheTime ?: wfTimestampNow();
                        if ( !$revId ) {
                                $revision = $page->getRevision();
@@ -302,7 +302,7 @@ class ParserCache {
                                'ParserCacheSaveComplete',
                                [ $this, $parserOutput, $page->getTitle(), $popts, $revId ]
                        );
-               } else {
+               } elseif ( $expire <= 0 ) {
                        wfDebug( "Parser output was marked as uncacheable and has not been saved.\n" );
                }
        }
index 7ddedb9..29016a8 100644 (file)
@@ -19,7 +19,7 @@
  */
 
 class PoolWorkArticleView extends PoolCounterWork {
-       /** @var Page */
+       /** @var WikiPage */
        private $page;
 
        /** @var string */
@@ -44,7 +44,7 @@ class PoolWorkArticleView extends PoolCounterWork {
        private $error = false;
 
        /**
-        * @param Page $page
+        * @param WikiPage $page
         * @param ParserOptions $parserOptions ParserOptions to use for the parse
         * @param int $revid ID of the revision being parsed.
         * @param bool $useParserCache Whether to use the parser cache.
@@ -52,7 +52,7 @@ class PoolWorkArticleView extends PoolCounterWork {
         * @param Content|string $content Content to parse or null to load it; may
         *   also be given as a wikitext string, for BC.
         */
-       public function __construct( Page $page, ParserOptions $parserOptions,
+       public function __construct( WikiPage $page, ParserOptions $parserOptions,
                $revid, $useParserCache, $content = null
        ) {
                if ( is_string( $content ) ) { // BC: old style call
old mode 100644 (file)
new mode 100755 (executable)
index f977124..415e664
@@ -209,8 +209,12 @@ class ExtensionProcessor implements Processor {
        protected function extractHooks( array $info ) {
                if ( isset( $info['Hooks'] ) ) {
                        foreach ( $info['Hooks'] as $name => $value ) {
-                               foreach ( (array)$value as $callback ) {
-                                       $this->globals['wgHooks'][$name][] = $callback;
+                               if ( is_array( $value ) ) {
+                                       foreach ( $value as $callback ) {
+                                               $this->globals['wgHooks'][$name][] = $callback;
+                                       }
+                               } else {
+                                       $this->globals['wgHooks'][$name][] = $value;
                                }
                        }
                }
index 796dc20..a3f8825 100644 (file)
@@ -75,6 +75,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                                case 'styles':
                                case 'scripts':
                                case 'group':
+                               case 'targets':
                                        $this->{$member} = $option;
                                        break;
                        }
index 27b6dd4..dcef95c 100644 (file)
@@ -607,7 +607,7 @@ abstract class SearchEngine {
         * @return array
         */
        public static function namespacesAsText( $namespaces ) {
-               return MediaWikiServices::getInstance()->getSearchEngineConfig()->namespacesAsText();
+               return MediaWikiServices::getInstance()->getSearchEngineConfig()->namespacesAsText( $namespaces );
        }
 
        /**
index 4188f4f..29878d4 100644 (file)
@@ -379,6 +379,156 @@ final class Session implements \Countable, \Iterator, \ArrayAccess {
                $this->remove( 'wsTokenSecrets' );
        }
 
+       /**
+        * Fetch the secret keys for self::setSecret() and self::getSecret().
+        * @return string[] Encryption key, HMAC key
+        */
+       private function getSecretKeys() {
+               global $wgSessionSecret, $wgSecretKey;
+
+               $wikiSecret = $wgSessionSecret ?: $wgSecretKey;
+               $userSecret = $this->get( 'wsSessionSecret', null );
+               if ( $userSecret === null ) {
+                       $userSecret = \MWCryptRand::generateHex( 32 );
+                       $this->set( 'wsSessionSecret', $userSecret );
+               }
+
+               $keymats = hash_pbkdf2( 'sha256', $wikiSecret, $userSecret, 10001, 64, true );
+               return [
+                       substr( $keymats, 0, 32 ),
+                       substr( $keymats, 32, 32 ),
+               ];
+       }
+
+       /**
+        * Set a value in the session, encrypted
+        *
+        * This relies on the secrecy of $wgSecretKey (by default), or $wgSessionSecret.
+        *
+        * @param string|int $key
+        * @param mixed $value
+        */
+       public function setSecret( $key, $value ) {
+               global $wgSessionInsecureSecrets;
+
+               list( $encKey, $hmacKey ) = $this->getSecretKeys();
+               $serialized = serialize( $value );
+
+               // The code for encryption (with OpenSSL) and sealing is taken from
+               // Chris Steipp's OATHAuthUtils class in Extension::OATHAuth.
+
+               // Encrypt
+               // @todo: import a pure-PHP library for AES instead of doing $wgSessionInsecureSecrets
+               $iv = \MWCryptRand::generate( 16, true );
+               if ( function_exists( 'openssl_encrypt' ) ) {
+                       $ciphertext = openssl_encrypt( $serialized, 'aes-256-ctr', $encKey, OPENSSL_RAW_DATA, $iv );
+                       if ( $ciphertext === false ) {
+                               throw new UnexpectedValueException( 'Encryption failed: ' . openssl_error_string() );
+                       }
+               } elseif ( function_exists( 'mcrypt_encrypt' ) ) {
+                       $ciphertext = mcrypt_encrypt( 'rijndael-128', $encKey, $serialized, 'ctr', $iv );
+                       if ( $ciphertext === false ) {
+                               throw new UnexpectedValueException( 'Encryption failed' );
+                       }
+               } elseif ( $wgSessionInsecureSecrets ) {
+                       $ex = new \Exception( 'No encryption is available, storing data as plain text' );
+                       $this->logger->warning( $ex->getMessage(), [ 'exception' => $ex ] );
+                       $ciphertext = $serialized;
+               } else {
+                       throw new \BadMethodCallException(
+                               'Encryption is not available. You really should install the PHP OpenSSL extension, ' .
+                               'or failing that the mcrypt extension. But if you really can\'t and you\'re willing ' .
+                               'to accept insecure storage of sensitive session data, set ' .
+                               '$wgSessionInsecureSecrets = true in LocalSettings.php to make this exception go away.'
+                       );
+               }
+
+               // Seal
+               $sealed = base64_encode( $iv ) . '.' . base64_encode( $ciphertext );
+               $hmac = hash_hmac( 'sha256', $sealed, $hmacKey, true );
+               $encrypted = base64_encode( $hmac ) . '.' . $sealed;
+
+               // Store
+               $this->set( $key, $encrypted );
+       }
+
+       /**
+        * Fetch a value from the session that was set with self::setSecret()
+        * @param string|int $key
+        * @param mixed $default Returned if $this->exists( $key ) would be false or decryption fails
+        * @return mixed
+        */
+       public function getSecret( $key, $default = null ) {
+               global $wgSessionInsecureSecrets;
+
+               // Fetch
+               $encrypted = $this->get( $key, null );
+               if ( $encrypted === null ) {
+                       return $default;
+               }
+
+               // The code for unsealing, checking, and decrypting (with OpenSSL) is
+               // taken from Chris Steipp's OATHAuthUtils class in
+               // Extension::OATHAuth.
+
+               // Unseal and check
+               $pieces = explode( '.', $encrypted );
+               if ( count( $pieces ) !== 3 ) {
+                       $ex = new \Exception( 'Invalid sealed-secret format' );
+                       $this->logger->warning( $ex->getMessage(), [ 'exception' => $ex ] );
+                       return $default;
+               }
+               list( $hmac, $iv, $ciphertext ) = $pieces;
+               list( $encKey, $hmacKey ) = $this->getSecretKeys();
+               $integCalc = hash_hmac( 'sha256', $iv . '.' . $ciphertext, $hmacKey, true );
+               if ( !hash_equals( $integCalc, base64_decode( $hmac ) ) ) {
+                       $ex = new \Exception( 'Sealed secret has been tampered with, aborting.' );
+                       $this->logger->warning( $ex->getMessage(), [ 'exception' => $ex ] );
+                       return $default;
+               }
+
+               // Decrypt
+               // @todo: import a pure-PHP library for AES instead of doing $wgSessionInsecureSecrets
+               if ( function_exists( 'openssl_decrypt' ) ) {
+                       $serialized = openssl_decrypt(
+                               base64_decode( $ciphertext ), 'aes-256-ctr', $encKey, OPENSSL_RAW_DATA, base64_decode( $iv )
+                       );
+                       if ( $serialized === false ) {
+                               $ex = new \Exception( 'Decyption failed: ' . openssl_error_string() );
+                               $this->logger->debug( $ex->getMessage(), [ 'exception' => $ex ] );
+                               return $default;
+                       }
+               } elseif ( function_exists( 'mcrypt_decrypt' ) ) {
+                       $serialized = mcrypt_decrypt(
+                               'rijndael-128', $encKey, base64_decode( $ciphertext ), 'ctr', base64_decode( $iv )
+                       );
+                       if ( $serialized === false ) {
+                               $ex = new \Exception( 'Decyption failed' );
+                               $this->logger->debug( $ex->getMessage(), [ 'exception' => $ex ] );
+                               return $default;
+                       }
+               } elseif ( $wgSessionInsecureSecrets ) {
+                       $ex = new \Exception(
+                               'No encryption is available, retrieving data that was stored as plain text'
+                       );
+                       $this->logger->warning( $ex->getMessage(), [ 'exception' => $ex ] );
+                       $serialized = base64_decode( $ciphertext );
+               } else {
+                       throw new \BadMethodCallException(
+                               'Encryption is not available. You really should install the PHP OpenSSL extension, ' .
+                               'or failing that the mcrypt extension. But if you really can\'t and you\'re willing ' .
+                               'to accept insecure storage of sensitive session data, set ' .
+                               '$wgSessionInsecureSecrets = true in LocalSettings.php to make this exception go away.'
+                       );
+               }
+
+               $value = unserialize( $serialized );
+               if ( $value === false && $serialized !== serialize( false ) ) {
+                       $value = $default;
+               }
+               return $value;
+       }
+
        /**
         * Delay automatic saving while multiple updates are being made
         *
index 625e4aa..fcadede 100644 (file)
@@ -28,7 +28,7 @@
  * @ingroup SpecialPage
  */
 class SpecialBlock extends FormSpecialPage {
-       /** @var User User to be blocked, as passed either by parameter (url?wpTarget=Foo)
+       /** @var User|string|null User to be blocked, as passed either by parameter (url?wpTarget=Foo)
         * or as subpage (Special:Block/Foo) */
        protected $target;
 
@@ -330,8 +330,12 @@ class SpecialBlock extends FormSpecialPage {
 
                $otherBlockMessages = [];
                if ( $this->target !== null ) {
+                       $targetName = $this->target;
+                       if ( $this->target instanceof User ) {
+                               $targetName = $this->target->getName();
+                       }
                        # Get other blocks, i.e. from GlobalBlocking or TorBlock extension
-                       Hooks::run( 'OtherBlockLogLink', [ &$otherBlockMessages, $this->target ] );
+                       Hooks::run( 'OtherBlockLogLink', [ &$otherBlockMessages, $targetName ] );
 
                        if ( count( $otherBlockMessages ) ) {
                                $s = Html::rawElement(
index c5f50c6..40401e7 100644 (file)
        "userpage-userdoesnotexist": "La cuenta d'usuariu «$1» nun ta rexistrada.\nPor favor comprueba si quies crear/editar esta páxina.",
        "userpage-userdoesnotexist-view": "La cuenta d'usuariu «$1» nun ta rexistrada.",
        "blocked-notice-logextract": "Anguaño esti usuariu ta bloquiáu.\nMás abaxo ufrese la entrada del rexistru de bloqueos pa referencia:",
-       "clearyourcache": "'''Nota:''' Llueu de guardar, seique tengas que llimpiar la caché del restolador pa ver los cambeos.\n*'''Firefox / Safari:''' Caltén ''Mayús'' mentes calques en ''Recargar'', o calca ''Ctrl-F5'' o ''Ctrl-R'' (''⌘-R'' nun Mac)\n* '''Google Chrome:''' Calca ''Ctrl-Mayús-R'' (''⌘-Mayús-R'' nun Mac)\n* '''Internet Explorer:''' Caltén ''Ctrl'' mentes calques ''Refrescar'', o calca ''Ctrl-F5''\n* '''Opera:''' llimpia la caché en ''Ferramientes → Preferencies''",
+       "clearyourcache": "'''Nota:''' Llueu de guardar, seique tengas que llimpiar la caché del restolador pa ver los cambeos.\n*'''Firefox / Safari:''' Caltén ''Mayús'' mentes calques en ''Recargar'', o calca ''Ctrl-F5'' o ''Ctrl-R'' (''⌘-R'' nun Mac)\n* '''Google Chrome:''' Calca ''Ctrl-Mayús-R'' (''⌘-Mayús-R'' nun Mac)\n* '''Internet Explorer:''' Caltén ''Ctrl'' mentes calques ''Refrescar'', o calca ''Ctrl-F5''\n* '''Opera:''' Entra'n Menú → Preferencies'' (''Opera → Preferencies'' nun Mac) y d'ehí en ''Intimidá y seguridá → Llimpiar datos de navegación → Imáxenes y ficheros en caché''.",
        "usercssyoucanpreview": "'''Conseyu:''' Usa'l botón \"{{int:showpreview}}\" pa probar el CSS nuevu enantes de guardalu.",
        "userjsyoucanpreview": "'''Conseyu:''' Usa'l botón \"{{int:showpreview}}\" pa probar el JavaScript nuevu enantes de guardalu.",
        "usercsspreview": "'''Recuerda que namái ye la vista previa del CSS d'usuariu.'''\n'''¡Inda nun ta guardáu!'''",
        "watchlistedit-raw-done": "Anovóse la to llista de siguimientu.",
        "watchlistedit-raw-added": "{{PLURAL:$1|Añadióse un títulu|Añadiéronse $1 títulos}}:",
        "watchlistedit-raw-removed": "{{PLURAL:$1|Eliminóse ún títulu|Elimináronse $1 títulos}}:",
-       "watchlistedit-clear-title": "Llimpióse la llista de siguimientu",
+       "watchlistedit-clear-title": "Llimpiar la llista de siguimientu",
        "watchlistedit-clear-legend": "Llimpiar la llista de siguimientu",
        "watchlistedit-clear-explain": "Desaniciaránse tolos títulos de la to llista de siguimientu",
        "watchlistedit-clear-titles": "Títulos:",
index 8acc7a8..647cf94 100644 (file)
        "listgrouprights-namespaceprotection-header": "Абмежаваньні прасторы назваў",
        "listgrouprights-namespaceprotection-namespace": "Прастора назваў",
        "listgrouprights-namespaceprotection-restrictedto": "Правы, якія дазваляюць удзельніку рэдагаваць",
+       "listgrants": "Дазволы",
+       "listgrants-grant": "Дазвол",
        "trackingcategories": "Катэгорыі, якія патрабуюць увагі",
        "trackingcategories-summary": "На гэтай старонцы пералічаныя катэгорыя, які патрабуюць увагі і якія аўтаматычна запаўняюцца праграмным забесьяпчэньнем MediaWiki. Іх назвы могуць быць зьмененыя рэдагаваньнем сыстэмных паведамленьняў у прасторы назваў {{ns:8}}.",
        "trackingcategories-msg": "Катэгорыя, якая патрабуе ўвагі",
index 4020789..9f67d1f 100644 (file)
@@ -33,7 +33,8 @@
                        "Matma Rex",
                        "Xð",
                        "Miroslav35232",
-                       "Ket"
+                       "Ket",
+                       "Ricordo.tenerissimo"
                ]
        },
        "tog-underline": "Подчертаване на препратките:",
        "minoredit": "Това е малка промяна",
        "watchthis": "Наблюдаване на страницата",
        "savearticle": "Съхраняване",
+       "publishpage": "Публикуване на страницата",
        "preview": "Предварителен преглед",
        "showpreview": "Предварителен преглед",
        "showdiff": "Показване на промените",
        "suppress": "Премахване от публичния архив",
        "querypage-disabled": "Тази специална страница е изключена, защото затруднява производителността на уикито.",
        "apihelp-no-such-module": "Модул \"$1\" не беше намерен.",
+       "apisandbox-fullscreen": "Разшири полето",
        "apisandbox-reset": "Изчистване",
        "apisandbox-examples": "Пример",
-       "apisandbox-results": "Резултат",
+       "apisandbox-dynamic-parameters-add-placeholder": "Име на параметъра",
+       "apisandbox-results": "Резултати",
        "booksources": "Източници на книги",
        "booksources-search-legend": "Търсене на информация за книга",
        "booksources-search": "Търсене",
index 2863646..262fc53 100644 (file)
        "listgrouprights-rights": "حقوق",
        "listgrouprights-helppage": "Help:گروپانئ حقوق",
        "listgrouprights-members": "(اعضائانی لڑلیست)",
+       "listgrouprights-right-display": "<span class=\"listgrouprights-granted\">$1 <code>($2)</code></span>",
        "listgrouprights-addgroup": "توانیت ای {{PLURAL:$2|گروپ|گروپ ئان}} ئا اضافه بکینت: $1",
        "listgrouprights-removegroup": "توانیت ای {{PLURAL:$2|گروپ|گروپ ئان}} ئه پاک بکنیت: $1",
        "listgrouprights-addgroup-all": "توانیت موچین گروپانا اضافه بکنیت",
        "bydate": "شه تاریخی رُوگا",
        "sp-newimages-showfrom": "نشان‌داتین نۆکین اکسانی شه $2، $1 بئ بعد",
        "seconds": "{{PLURAL:$1|$1ثانیه| $1  ثانیه}}",
-       "minutes": "{{PLURAL:$1|دقیقه|دقیقه}}",
+       "minutes": "{{PLURAL:$1|$1 دقیقه|$1 دقیقه}}",
        "hours": "{{PLURAL:$1|ساعت|ساعت}}",
        "days": "{{PLURAL:$1|روچ|روچ}}",
        "weeks": "{{PLURAL:$1|$1 هپتگ|$1 هپتگ ئان}}",
index 89ce77e..8f91514 100644 (file)
        "api-error-nomodule": "Interní chyba: není nastaven načítací modul.",
        "api-error-ok-but-empty": "Interní chyba: ze serveru nepřišla odpověď.",
        "api-error-overwrite": "Není dovoleno přepsat existující soubor.",
+       "api-error-ratelimited": "Pokoušíte se načíst v krátkém časovém rozmezí načíst více souborů, než je na této wiki dovoleno.\nZkuste to znovu za několik minut.",
        "api-error-stashfailed": "Vnitřní chyba: Serveru se nepodařilo uložit dočasný soubor.",
        "api-error-publishfailed": "Vnitřní chyba: Serveru se nepodařilo zveřejnit dočasný soubor.",
        "api-error-stasherror": "Při načítání souboru do skrýše došlo k chybě.",
index 51104de..29c1cc1 100644 (file)
        "morenotlisted": "यो सूची पूरा नाइँ हो ।",
        "mypage": "पानो",
        "mytalk": "मेरी कुरडी",
-       "anontalk": "यà¥\88 à¤\86à¤\87. à¤ªà¥\80. à¤\95ा à¤¬à¤¾à¤°à¥\87मà¥\80 à¤\95à¥\81रडà¥\80 à¤\97र",
+       "anontalk": "à¤\95à¥\81रडà¥\80",
        "navigation": "खोज",
        "and": "&#32;र",
        "qbfind": "तम जाण",
index 4160dea..aacf623 100644 (file)
        "feedback-useragent": "User agent:",
        "searchsuggest-search": "Search",
        "searchsuggest-containing": "containing...",
+       "api-error-autoblocked": "Your IP address has been blocked automatically, because it was used by a blocked user.",
        "api-error-badaccess-groups": "You are not permitted to upload files to this wiki.",
        "api-error-badtoken": "Internal error: Bad token.",
+       "api-error-blocked": "You have been blocked from editing.",
        "api-error-copyuploaddisabled": "Uploading by URL is disabled on this server.",
        "api-error-duplicate": "There {{PLURAL:$1|is another file|are some other files}} already on the site with the same content.",
        "api-error-duplicate-archive": "There {{PLURAL:$1|was another file|were some other files}} already on the site with the same content, but {{PLURAL:$1|it was|they were}} deleted.",
index af614eb..200374b 100644 (file)
        "july-date": "$1‑a de julio",
        "august-date": "$1‑a de aŭgusto",
        "september-date": "$1‑a de septembro",
-       "october-date": "$1-a de oktobro",
-       "november-date": "$1-a de novembro",
-       "december-date": "$1-a de decembro",
+       "october-date": "$1a de oktobro",
+       "november-date": "$1a de novembro",
+       "december-date": "$1a de decembro",
        "period-am": "ATM",
        "period-pm": "PTM",
        "pagecategories": "{{PLURAL:$1|Kategorio|Kategorioj}}",
        "help": "Helpo",
        "search": "Serĉi",
        "searchbutton": "Serĉi",
-       "go": "Ek!",
+       "go": "Ek",
        "searcharticle": "Ek",
        "history": "Historio de versioj",
        "history_short": "Historio",
        "newpage": "Nova paĝo",
        "talkpage": "Diskuti la paĝon",
        "talkpagelinktext": "diskuto",
-       "specialpage": "Speciala Paĝo",
+       "specialpage": "Speciala paĝo",
        "personaltools": "Personaj iloj",
        "articlepage": "Vidi paĝenhavon",
        "talk": "Diskuto",
        "nologin": "Ĉu vi ne havas konton? $1.",
        "nologinlink": "Krei konton",
        "createaccount": "Krei konton",
-       "gotaccount": "Ĉu vi jam havas konton? '''$1'''.",
+       "gotaccount": "Ĉu vi jam havas konton? $1.",
        "gotaccountlink": "Ensaluti",
        "userlogin-resetlink": "Ĉu vi forgesis ensalutajn detalojn?",
        "userlogin-resetpassword-link": "Ĉu vi forgesis vian pasvorton?",
        "subject": "Temo:",
        "minoredit": "Ĉi tiu ŝanĝo estas redakteto",
        "watchthis": "Atenti ĉi tiun paĝon",
-       "savearticle": "Konservi ŝanĝojn",
+       "savearticle": "Konservi paĝon",
+       "publishpage": "Publikigi paĝon",
        "preview": "Antaŭrigardo",
        "showpreview": "Antaŭrigardo",
        "showdiff": "Montri ŝanĝojn",
        "sitecsspreview": "'''Konsciu ke vi nur antaŭrigardas tiun ĉi CSS.'''\n'''Ĝi ne jam estis savita!''",
        "sitejspreview": "'''Konsciu ke vi nur antaŭrigardas tiun ĉi Ĝavaskripta kodon''. ''Ĝi ne jam estis konservita''.",
        "userinvalidcssjstitle": "'''Averto:''' Ne ekzistas etoso \"$1\".\nRememoru ke individuaj .css-aj kaj .js-aj paĝoj uzas minusklan titolon, ekz. {{ns:user}}:Foo/vector.css kontraŭe al {{ns:user}}:Foo/Vector.css.",
-       "updated": "(Ŝanĝo registrita)",
-       "note": "'''Noto:'''",
+       "updated": "(Ĝisdatigita)",
+       "note": "<strong>Noto:</strong>",
        "previewnote": "'''Memoru, ke ĉi tio estas nur antaŭrigardo.''' \nViaj ŝanĝoj ne ankoraŭ estas konservitaj!",
        "continue-editing": "Iri al redakta spaco",
        "previewconflict": "La jena antaŭrigardo montras la tekston el la supra tekstujo,\nkiel ĝi aperos se vi elektos konservi la paĝon.",
        "uploaded-animate-svg": "Trovis markon je \"animate\", kiu povus ŝanĝi la atributon je \"href\", per uzi la atributon je \"from\" <code>&lt;$1 $2=\"$3\"&gt;</code> en la alŝuta SVGa dosiero.",
        "uploaded-setting-event-handler-svg": "Ensigni eventotraktilajn atributojn estas blokita, trovis <code>&lt;$1 $2=\"$3\"&gt;</code> en la alŝuta SVGa dosiero.",
        "uploaded-setting-href-svg": "Uzi la markon je \"set\" por aldoni atributon je \"href\" al ujero estas blokita.",
+       "uploaded-wrong-setting-svg": "Uzi la ''ensigni'' (''<span lang=\"en\">set</span>'') markon por aldoni foran/datenan/skriptan celon al ajn atributon estas blokita. Trovis <code>&lt;set to=\"$1\"&gt;</code> en la alŝutita SVGan dosieron.",
+       "uploaded-setting-handler-svg": "SVGa dosiero kiu ensignas la traktilan atributon (''<span lang=\"en\">handler</span>'') kun fora/datena/skripta estas blokita. Trovis <code>$1=\"$2\"</code> en la alsûtita SVGa dosiero.",
+       "uploaded-remote-url-svg": "SVGa dosiero kiu ensignas ajn stilan atributon kun fora URL estas blokita. Trovis <code>$1=\"$2\"</code> en la alsûtita SVGa dosiero.",
+       "uploaded-image-filter-svg": "Trovis bildan filtrilon kun URL:  <code>&lt;$1 $2=\"$3\"&gt;</code> en la alŝutita SVGa dosiero.",
        "uploadscriptednamespace": "Ĉi tiu SVG-dosiero enhavas nevalidan nomspacon \"$1\"",
        "uploadinvalidxml": "Ne eblas interpreti la XML-sintakson en la alŝutita dosiero",
        "uploadvirus": "Viruso troviĝas en la dosiero! Detaloj: $1",
        "upload-options": "Alŝutaj agordoj",
        "watchthisupload": "Atenti ĉi tiun dosieron",
        "filewasdeleted": "Dosiero de ĉi tiu nomo estis antaŭe alŝutita kaj poste forigita. Bonvolu kontroli en la $1 antaŭ alŝuti ĝin denove.",
+       "filename-thumb-name": "Ĉi tiu aspektas kiel titolo de etigita versio de plena bildo. Bonvolu ne alŝuti etigitajn versiojn de bildoj en la sama vikio. Alimaniere, bonvolu modifi la dosiernomon pli signife, kaj ne plu havas la eta-versian prefikson.",
        "filename-bad-prefix": "La nomo de la dosiero kiun vi alŝutas komencas kun '''\"$1\"''', kiu estas nepriskriba nomo ofte aŭtomate donata de ciferecaj fotiloj. Bonvolu elekti pli priskriban nomon por via bildo.",
        "upload-proto-error": "Malvalida protokolo",
        "upload-proto-error-text": "Fora alŝuto devas URL-on komence de <code>http://</code> aŭ <code>ftp://</code>.",
        "upload-too-many-redirects": "La URL-o enhavis tro multajn alidirektilojn",
        "upload-http-error": "HTTP-eraro okazis: $1",
        "upload-copy-upload-invalid-domain": "Kopio-alŝutoj ne disponiĝas el ĉi tiu domajno.",
+       "upload-foreign-cant-upload": "Tiu vikio ne estas agorita por alŝuti alŝutitan dosieron al la petita fora dosierdeponejo.",
        "upload-dialog-title": "Alŝuti dosieron",
        "upload-dialog-button-cancel": "Nuligi",
        "upload-dialog-button-done": "Farite",
        "upload-dialog-button-upload": "Alŝuti",
        "upload-form-label-infoform-title": "Detaloj",
        "upload-form-label-infoform-name": "Nomo",
+       "upload-form-label-infoform-name-tooltip": "Unika priskriba titolo por tiu ĉi dosiero, kiu servos kiel dosiernomo. Eblas uzi normalan lingvaĵon kun interspacoj. Ne aldonu la dosieran aldonaĵon.",
        "upload-form-label-infoform-description": "Priskribo",
+       "upload-form-label-infoform-description-tooltip": "Bonvolu koncize priskribi ĉion noteblan pri la verko.\nPor foto, menciu la esencaj aĵoj bildigitaj, la okazon, aŭ la lokon.",
        "upload-form-label-usage-title": "Uzo",
        "upload-form-label-usage-filename": "Dosiernomo",
        "foreign-structured-upload-form-label-own-work": "Tio estas mia propra laboro",
        "foreign-structured-upload-form-label-infoform-categories": "Kategorioj",
        "foreign-structured-upload-form-label-infoform-date": "Dato",
+       "foreign-structured-upload-form-label-own-work-message-local": "Mi konfirmas ke mi alŝutas tiun dosieron respektante pri la uzadokondiĉoj kaj permesopolitikoj de  {{SITENAME}}.",
+       "foreign-structured-upload-form-label-not-own-work-message-local": "Se vi ne eblas alŝuti tiun dosieron respektante de politikoj de {{SITENAME}}, bonvolu fermi tiun dialogon kaj provi denove kun alia metodo.",
+       "foreign-structured-upload-form-label-not-own-work-local-local": "Vi eble ŝatu egale pravi [[Special:Upload|la defaŭltan paĝon]].",
+       "foreign-structured-upload-form-label-own-work-message-default": "Mi komprenas ke mi alŝutas tiun dosieron al komunigita deponejo. Mi konfirmas ke mi faras tiun respektante de la uzadtermoj kaj de la permisilopolitikoj tie.",
        "backend-fail-stream": "Ne povis fluigi dosieron $1.",
        "backend-fail-backup": "Ne povis enarkivigi dosieron $1.",
        "backend-fail-notexists": "La dosiero $1 ne ekzistas.",
        "backend-fail-read": "Ne povas legi dosieron \"$1\".",
        "backend-fail-create": "Ne povas skribi dosieron $1.",
        "backend-fail-maxsize": "Ne povis skribi la dosieron \"$1,\" ĉar ĝi estas pli granda ol {{PLURAL:$2|bitoko|$2 bitokoj}}.",
-       "backend-fail-readonly": "La interna konservujo \"$1\" nune estas nurlega. La indikata kialo estas: \"''$2''\"",
+       "backend-fail-readonly": "La interna konservujo \"$1\" nune estas nurlega. La indikata kialo estas: <em>$2</em>",
        "backend-fail-synced": "La dosiero \"$1\" estas en nekohera stato kun la internaj konservujoj",
        "backend-fail-connect": "Ne eblis konekti la internan konservujon \"$1\".",
        "backend-fail-internal": "Nekonata eraro okazis en interna konservujo \"$1\".",
        "uploadstash-summary": "Tiu ĉi paĝo alirebligas la dosierojn alŝutitajn (aŭ alŝutatajn), kiuj ne jam estas publikigitaj per la vikio. Tiujn ĉi dosierojn ne povas vidi  iu ajn, krom la alŝutinto mem.",
        "uploadstash-clear": "Malplenigi la dosierkonversejon.",
        "uploadstash-nofiles": "Mankas dosieroj en la konservejo.",
-       "uploadstash-badtoken": "Malsukcesis tiu ago, eble pro tio ke viaj ensalutiloj senvalidiĝis. Reprovu.",
-       "uploadstash-errclear": "Sensukcesis la forigo de la dosieroj.",
+       "uploadstash-badtoken": "Malsukcesis tiu ago, eble pro tio ke viaj ensalutiloj senvalidiĝis. Bonvolu reprovi.",
+       "uploadstash-errclear": "Malsukcesis la forigo de la dosieroj.",
        "uploadstash-refresh": "Aktualigi la dosierliston.",
        "uploadstash-thumbnail": "Vidi bildetigon",
        "invalid-chunk-offset": "Malvalida deŝovo de dosierpeco",
        "apisandbox": "API testejo",
        "apisandbox-api-disabled": "API estas malŝalta en ĉi tiu retejo.",
        "apisandbox-intro": "Uzu tiun ĉi paĝon por eksperimenti kun '''MediaWiki API'''.\nVidu [//www.mediawiki.org/wiki/API:Main_page la API-dokumentadon] por pli da detaloj pri la uzo de API. Ekz-e: [//www.mediawiki.org/wiki/API#A_simple_example atingi la enhavon de la Ĉefpaĝo]. Elektu agon por vidi pliajn ekzemplojn.\n\nNotu ke, kvankam ĉi tiu estas provejo, agoj kiun vi faros en ĉi tiu paĝo povas modifi la vikion.",
+       "apisandbox-unfullscreen": "Montri paĝon",
        "apisandbox-submit": "Fari mendon",
        "apisandbox-reset": "Nuligi",
+       "apisandbox-retry": "Reprovi",
+       "apisandbox-helpurls": "Ligiloj pri helpo",
        "apisandbox-examples": "Ekzemploj",
        "apisandbox-dynamic-parameters": "Aldonaj parametroj",
        "apisandbox-dynamic-parameters-add-label": "Aldoni parametron:",
        "apisandbox-dynamic-parameters-add-placeholder": "Nomo de parametro",
        "apisandbox-dynamic-error-exists": "Parametro nomata \"$1\" jam ekzistas.",
+       "apisandbox-submit-invalid-fields-title": "Iuj kampoj estas malvalidaj.",
        "apisandbox-results": "Rezultoj",
        "apisandbox-request-url-label": "Mendi URL-on.",
-       "apisandbox-request-time": "Tempo de peto: $1",
+       "apisandbox-request-time": "Tempo de peto:{{PLURAL:$1|$1 ms}}",
        "booksources": "Librofontoj",
        "booksources-search-legend": "Serĉi librofontojn",
        "booksources-search": "Serĉi",
        "booksources-text": "Jen ligilaro al aliaj TTT-ejoj, kiuj vendas librojn,\nkaj/aŭ informumos pri la libro ligita.\nLa {{SITENAME}} ne estas komerce ligita al tiuj vendejoj, kaj la listo ne estu\nkomprenata kiel rekomendo aŭ reklamo.",
        "booksources-invalid-isbn": "La donata ISBN verŝajne estas nevalida; kontrolu pri erara kopiado el la originala fonto.",
        "specialloguserlabel": "Faranto:",
-       "speciallogtitlelabel": "Celo (titolo aŭ uzanto):",
+       "speciallogtitlelabel": "Celo (titolo aŭ  {{ns:user}}:salutnomo por uzanto):",
        "log": "Protokoloj",
        "logeventslist-submit": "Montri",
        "all-logs-page": "Ĉiuj publikaj protokoloj",
        "wlshowhideanons": "anonimaj uzantoj",
        "wlshowhidepatr": "patrolitaj redaktoj",
        "wlshowhidemine": "miaj redaktoj",
+       "wlshowhidecategorization": "paĝokategoriado",
        "watchlist-options": "Opcioj por atentaro",
        "watching": "Aldonata al la atentaro...",
        "unwatching": "Malatentante...",
        "deletepage": "Forigi paĝon",
        "confirm": "Konfirmi",
        "excontent": "enhavis: '$1'",
-       "excontentauthor": "la enteno estis : '$1' (kaj la sola kontribuinto estis '$2')",
+       "excontentauthor": "la enteno estis: '$1', kaj la sola kontribuinto estis \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|talk]])",
        "exbeforeblank": "antaŭ malplenigo enhavis: '$1'",
        "delete-confirm": "Forigi \"$1\"",
        "delete-legend": "Forigi",
        "delete-toobig": "Ĉi tiu paĝo havas grandan redakto-historion, pli ol $1 {{PLURAL:$1|version|versiojn}}. Forigo de ĉi tiaj paĝoj estis limigitaj por preventi akcidentan disrompigon de {{SITENAME}}.",
        "delete-warning-toobig": "Ĉi tiu paĝo havas grandan redakto-historion, pli ol $1 {{PLURAL:$1|version|versiojn}}. Forigo de ĝi povas disrompigi operacion de {{SITENAME}}; forigu singarde.",
        "deleteprotected": "Vi ne povas forigi ĉi tiun paĝon ĉar ĝi estis protektita.",
-       "deleting-backlinks-warning": "'''Atentigo:'''\n[[Special:WhatLinksHere/{{FULLPAGENAME}}|Aliaj paĝoj]] ligas al aŭ transkludas tiun ĉi forigotan paĝon.",
+       "deleting-backlinks-warning": "<strong>Atentigo:</strong>\n[[Special:WhatLinksHere/{{FULLPAGENAME}}|Aliaj paĝoj]] ligas al aŭ transkludas tiun ĉi forigotan paĝon.",
        "rollback": "Restarigi antaŭan redakton",
        "rollbacklink": "malvalidi",
        "rollbacklinkcount": "nuligi $1 {{PLURAL:$1|redakton|redaktojn}}",
        "ipb-unblock": "Malforbari salutnomon aŭ IP-adreson",
        "ipb-blocklist": "Vidi ekzistantajn forbarojn",
        "ipb-blocklist-contribs": "Kontribuoj de {{GENDER:$1|$1}}",
+       "ipb-blocklist-duration-left": "$1 restas",
        "unblockip": "Malforbari IP-adreson/nomon",
        "unblockiptext": "Per la jena formulo vi povas repovigi al iu\nforbarita IP-adreso/nomo la povon enskribi en la vikio.",
        "ipusubmit": "Forigi ĉi tiun forbaron",
        "hebrew-calendar-m11-gen": "abo",
        "hebrew-calendar-m12-gen": "elulo",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskuto]])",
+       "timezone-local": "Loka",
        "duplicate-defaultsort": "'''Averto:''' Defaŭlta ordiga ŝlosilo \"$2\" anstataŭigas pli fruan defaŭltan ordigan ŝlosilon \"$1\".",
        "duplicate-displaytitle": "<strong>Atentigo:</strong> La montrata titolo \"$2\" transpasas la antaŭan titolon \"$1\".",
        "invalid-indicator-name": "<strong>Eraro:</strong> Atributo de la paĝstata indikilo <code>name</code> maldevas esti malplena.",
        "htmlform-cloner-create": "Aldoni plian",
        "htmlform-cloner-delete": "Forigi",
        "htmlform-cloner-required": "Almenaŭ unu valoro estas nepra.",
+       "htmlform-title-not-exists": "$1 ne ekzistas.",
+       "htmlform-user-not-exists": "<strong>$1</strong> ne ekzistas.",
+       "htmlform-user-not-valid": "<strong>$1</strong> ne estas valida salutnomo.",
        "sqlite-has-fts": "$1 kun tut-teksta subteno",
        "sqlite-no-fts": "$1 sen tut-teksta subteno",
        "logentry-delete-delete": "$1 forigis paĝon $3",
        "special-characters-title-minus": "minus-signo",
        "mw-widgets-dateinput-placeholder-day": "JJJJ-MM-TT",
        "mw-widgets-dateinput-placeholder-month": "JJJJ-MM",
+       "mw-widgets-titleinput-description-new-page": "paĝo ankoraŭ ne ekzistas",
+       "mw-widgets-titleinput-description-redirect": "alidirektas al $1",
        "api-error-blacklisted": "Bonvolu elekti alian, priskriban titolon.",
+       "sessionprovider-generic": "$1 seancoj",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "kuketaj seancoj",
        "randomrootpage": "Hazarda radika paĝo",
        "log-action-filter-all": "Ĉia",
+       "log-action-filter-block-block": "Forbari",
        "log-action-filter-protect-unprotect": "Malprotektado",
        "log-action-filter-upload-upload": "Novalŝuta",
        "log-action-filter-upload-overwrite": "Realŝuta"
index eac16c4..5a40fc6 100644 (file)
        "exbeforeblank": "محتوای صفحه قبل از خالی‌کردن این بود: «$1»",
        "delete-confirm": "حذف «$1»",
        "delete-legend": "حذف",
-       "historywarning": "<strong>هشدار:</strong> صفحه‌ای که در حال پاک‌کردن آن هستید دارای یک تاریخچه همراه $1 {{PLURAL:$1|بازبینی|بازبینی‌ها}} است:",
+       "historywarning": "<strong>هشدار:</strong> صفحه‌ای که در حال پاک‌کردن آن هستید دارای یک تاریخچه همراه $1 {{PLURAL:$1|بازبینی|بازبینی}} است:",
        "historyaction-submit": "نمایش",
        "confirmdeletetext": "شما در حال حذف کردن یک صفحه یا تصویر از پایگاه داده‌ها همراه با تمام تاریخچهٔ آن هستید.\nلطفاً این عمل را تأیید کنید و اطمینان حاصل کنید که عواقب این کار را می‌دانید و این عمل را مطابق با [[{{MediaWiki:Policy-url}}|سیاست‌ها]] انجام می‌دهید.",
        "actioncomplete": "عمل انجام شد",
index db908f1..117e5db 100644 (file)
        "botpasswords-label-resetpassword": "Uudista salasana",
        "botpasswords-label-grants": "Valittavissa olevat toimintaoikeudet:",
        "botpasswords-label-restrictions": "Käyttörajoitukset:",
+       "botpasswords-label-grants-column": "Myönnetään",
        "botpasswords-bad-appid": "Botin nimi \"$1\" ei kelpaa.",
        "botpasswords-insert-failed": "Botin nimen \"$1\" lisääminen epäonnsitui. Onko se jo lisätty?",
        "botpasswords-update-failed": "Botin nimen \"$1\" päivittäminen epäonnistui. Onko se poistettu?",
        "minoredit": "Tämä on pieni muutos",
        "watchthis": "Tarkkaile tätä sivua",
        "savearticle": "Tallenna sivu",
+       "publishpage": "Julkaise sivu",
        "preview": "Esikatselu",
        "showpreview": "Esikatsele",
        "showdiff": "Näytä muutokset",
        "right-applychangetags": "Asettaa [[Special:Tags|merkkauksia]] omien muutosten yhteyteen",
        "right-changetags": "Lisätä ja poistaa satunnaisia [[Special:Tags|merkkauksia]] yksittäisissä sivuversioissa tai lokimerkinnöissä",
        "grant-generic": "\"$1\" oikeuksien joukko",
+       "grant-group-page-interaction": "Ole vuorovaikutuksessa sivujen kanssa",
+       "grant-group-file-interaction": "Ole vuorovaikutuksessa mediatiedostojen kanssa",
+       "grant-group-watchlist-interaction": "Ole vuorovaikutuksessa oman tarkkailulistasi kanssa",
        "grant-group-email": "Lähettää sähköpostia",
        "grant-group-high-volume": "Suorittaa suuri määrä toimintoja",
        "grant-group-customization": "Mukauttaminen ja asetukset",
        "apisandbox-results-error": "Tapahtui virhe ladattaessa API-kyselyn vastausta: $1",
        "apisandbox-request-url-label": "Pyynnön URL",
        "apisandbox-request-time": "Pyyntöön kulunut aika: {{PLURAL:$1|$1 ms}}",
+       "apisandbox-results-fixtoken": "Korjaa \"token\" ja lähetä uudelleen",
        "apisandbox-alert-page": "Tällä sivulla olevat kentät eivät ole kelvollisia.",
        "apisandbox-alert-field": "Tässä kentässä oleva arvo ei ole kelvollinen.",
        "booksources": "Kirjalähteet",
        "tooltip-ca-nstab-category": "Näytä luokkasivu",
        "tooltip-minoredit": "Merkitse tämä pieneksi muutokseksi",
        "tooltip-save": "Tallenna muokkaukset",
+       "tooltip-publish": "Julkaise tekemäsi muutokset",
        "tooltip-preview": "Esikatsele muokkausta ennen tallennusta",
        "tooltip-diff": "Näytä tehdyt muutokset",
        "tooltip-compareselectedversions": "Vertaile valittuja versioita",
index 32b8cad..d78c041 100644 (file)
        "yourpasswordagain": "Confirmez le mot de passe :",
        "createacct-yourpasswordagain": "Confirmez le mot de passe",
        "createacct-yourpasswordagain-ph": "Entrez à nouveau le mot de passe",
-       "remembermypassword": "Me reconnecter automatiquement lors des prochaines visites avec ce navigateur (durant au maximum $1 jour{{PLURAL:$1||s}})",
+       "remembermypassword": "Mémoriser mes données de connection avec ce navigateur (durant au maximum $1 jour{{PLURAL:$1||s}})",
        "userlogin-remembermypassword": "Garder ma session active",
        "userlogin-signwithsecure": "Utiliser une connexion sécurisée",
        "cannotloginnow-title": "Impossible de se connecter maintenant",
        "cannotloginnow-text": "La connexion n’est pas possible en utilisant $1.",
        "yourdomainname": "Votre domaine :",
        "password-change-forbidden": "Vous ne pouvez pas modifier les mots de passe sur ce wiki.",
-       "externaldberror": "Une erreur s’est produite sur la base de données d’authentification externe, ou bien vous n’êtes pas autorisé à mettre à jour votre compte externe.",
+       "externaldberror": "Soit une erreur s’est produite sur la base de données d’authentification, soit vous n’êtes pas autorisé à mettre à jour votre compte externe.",
        "login": "Connexion",
        "nav-login-createaccount": "Créer un compte ou se connecter",
        "userlogin": "Créer un compte ou se connecter",
        "api-error-nomodule": "Erreur interne : aucun module de versement défini.",
        "api-error-ok-but-empty": "Erreur interne : Le serveur n'a pas répondu.",
        "api-error-overwrite": "Écraser un fichier existant n'est pas autorisé.",
+       "api-error-ratelimited": "Vous essayez de télécharger plus de fichiers dans un court espace de temps que ce que ce wiki autorise.\nVeuillez réessayer dans quelques minutes.",
        "api-error-stashfailed": "Erreur interne : le serveur n'a pas pu enregistrer le fichier temporaire.",
        "api-error-publishfailed": "Erreur interne : Le serveur n'a pas pu publier le fichier temporaire.",
        "api-error-stasherror": "Une erreur s'est produite lors du téléchargement du fichier pour le dissimuler.",
index a370a3f..103e8eb 100644 (file)
        "category-empty": "<em>Ora cela catègoria contint gins de pâge de mèdiâ.</em>",
        "hidden-categories": "{{PLURAL:$1|Catègoria cachiêe|Catègories cachiêes}}",
        "hidden-category-category": "Catègories cachiêes",
-       "category-subcat-count": "Cela catègoria at {{PLURAL:$2|mas que cela sot-catègoria-que.|{{PLURAL:$1|cela sot-catègoria|celes $1 sot-catègories}}-que, sur na soma de $2.}}",
-       "category-subcat-count-limited": "Cela catègoria at {{PLURAL:$1|cela sot-catègoria|celes $1 sot-catègories}}-que.",
-       "category-article-count": "{{PLURAL:$2|Cela catègoria contint mas que cela pâge-que.|{{PLURAL:$1|Cela pâge-que est|Celes $1 pâges-que sont}} a cela catègoria, sur na soma de $2.}}",
-       "category-article-count-limited": "{{PLURAL:$1|Cela pâge-que figure|Celes $1 pâges-que figuront}} dedens la presenta catègoria.",
-       "category-file-count": "{{PLURAL:$2|Cela catègoria contint mas que cél fichiér-que.|{{PLURAL:$1|Cél fichiér-que est|Celos $1 fichiérs-que sont}} a cela catègoria, sur na soma de $2.}}",
-       "category-file-count-limited": "{{PLURAL:$1|Cél fichiér-que figure|Celos $1 fichiérs-que figuront}} dedens la presenta catègoria.",
+       "category-subcat-count": "Cela catègoria at {{PLURAL:$2|mas que ceta sot-catègoria.|{{PLURAL:$1|ceta sot-catègoria|cetes $1 sot-catègories}}, sur na soma de $2.}}",
+       "category-subcat-count-limited": "Cela catègoria at {{PLURAL:$1|ceta sot-catègoria|cetes $1 sot-catègories}}.",
+       "category-article-count": "{{PLURAL:$2|Cela catègoria contint mas que ceta pâge.|{{PLURAL:$1|Ceta pâge est|Cetes $1 pâges sont}} a cela catègoria, sur na soma de $2.}}",
+       "category-article-count-limited": "{{PLURAL:$1|Ceta pâge figure|Cetes $1 pâges figuront}} dedens la presenta catègoria.",
+       "category-file-count": "{{PLURAL:$2|Cela catègoria contint mas que ceti fichiér.|{{PLURAL:$1|Ceti fichiér est|Cetos $1 fichiérs sont}} a cela catègoria, sur na soma de $2.}}",
+       "category-file-count-limited": "{{PLURAL:$1|Ceti fichiér figure|Cetos $1 fichiérs figuront}} dedens la presenta catègoria.",
        "listingcontinuesabbrev": "(suita)",
        "index-category": "Pâges endèxâyes",
        "noindex-category": "Pâges pas endèxâyes",
        "title-invalid-magic-tilde": "Lo titro de la pâge demandâye contint na cobla de tildes magicos pas justa (<nowiki>~~~</nowiki>).",
        "title-invalid-too-long": "Lo titro de la pâge demandâye est trop long. Dêt pas dèpassar $1 octèt{{PLURAL:$1||s}} dens l’encodâjo UTF-8.",
        "title-invalid-leading-colon": "Lo titro de la pâge demandâye contint un doux-pouents pas justo u comencement.",
-       "perfcached": "Celes balyês-que sont en cacho et pôvont pas étre a jorn. Por lo més {{PLURAL:$1|un rèsultat est disponiblo|$1 rèsultats sont disponiblos}} dedens lo cacho.",
-       "perfcachedts": "Celes balyês-que sont en cacho et sont étâyes betâyes a jorn por lo dèrriér côp lo $1. Por lo més {{PLURAL:$4|un rèsultat est disponiblo|$4 rèsultats sont disponiblos}} dedens lo cacho.",
+       "perfcached": "Cetes balyês sont en cacho et pôvont pas étre a jorn. Por lo més {{PLURAL:$1|un rèsultat est disponiblo|$1 rèsultats sont disponiblos}} dedens lo cacho.",
+       "perfcachedts": "Cetes balyês sont en cacho et sont étâyes betâyes a jorn por lo dèrriér côp lo $1. Por lo més {{PLURAL:$4|un rèsultat est disponiblo|$4 rèsultats sont disponiblos}} dedens lo cacho.",
        "querypage-no-updates": "Ora les mêses a jorn por cela pâge sont dèsactivâyes.\nLes balyês ique seront pas betâyes a jorn.",
        "viewsource": "Vêre lo tèxto sôrsa",
        "viewsource-title": "Vêre lo tèxto sôrsa de $1",
        "protectedinterface": "Cela pâge balye de tèxto d’entredoux por la programeria sur cél vouiqui et est vêr protègiêe por èvitar los abus.\nPor apondre ou ben changiér de traduccions sur tôs los vouiquis, se vos plét empleyéd [//translatewiki.net/ translatewiki.net], lo projèt de localisacion de MediaWiki.",
        "editinginterface": "<strong>Atencion :</strong> vos éte aprés changiér na pâge empleyêe por fâre lo tèxto d’entredoux de la programeria.\nLos changements sè cognetront sur l’aparence de l’entredoux utilisator por los ôtros utilisators de cél vouiqui.",
        "translateinterface": "Por apondre changiér de traduccions sur tôs los vouiquis, se vos plét empleyéd [//translatewiki.net/ translatewiki.net], lo projèt de localisacion de MediaWiki.",
-       "cascadeprotected": "Cela pâge est protègiêe contre los changements, el est transcllua per {{PLURAL:$1|cela pâge-que qu’est étâye protègiêe|celes pâges-que que sont étâyes protègiêes}} avouéc lo chouèx « protèccion en cascâda » activâ :\n$2",
+       "cascadeprotected": "Cela pâge est protègiêe contre los changements, el est transcllua per {{PLURAL:$1|ceta pâge qu’est étâye protègiêe|cetes pâges que sont étâyes protègiêes}} avouéc lo chouèx « protèccion en cascâda » activâ :\n$2",
        "namespaceprotected": "Vos éd pas la pèrmission de changiér les pâges de l’èspâço de noms « <strong>$1</strong> ».",
        "customcssprotected": "Vos éd pas la pèrmission de changiér cela pâge CSS, contint la configuracion a sè d’un ôtr’utilisator.",
        "customjsprotected": "Vos éd pas la pèrmission de changiér cela pâge JavaScript, contint la configuracion a sè d’un ôtr’utilisator.",
        "mailerror": "Fôta pendent l’èxpèdicion du mèssâjo : $1",
        "acct_creation_throttle_hit": "Des vesitors de cél vouiqui qu’emplèyont voutron adrèce IP ant fêt $1 comptio{{PLURAL:$1||s}} pendent lo jorn passâ, cen qu’est lo més ôtorisâ dens ceti temps.\nDu côp los vesitors qu’emplèyont cel’adrèce IP pôvont fâre gins de comptio por lo moment.",
        "emailauthenticated": "Voutron adrèce èlèctronica est étâye confirmâye lo $2 a $3.",
-       "emailnotauthenticated": "Voutron adrèce èlèctronica est p’oncor confirmâye.\nNion mèssâjo serat mandâ por châcuna de celes fonccionalitâts-que.",
+       "emailnotauthenticated": "Voutron adrèce èlèctronica est p’oncor confirmâye.\nNion mèssâjo serat mandâ por châcuna de cetes fonccionalitâts.",
        "noemailprefs": "Spècifiâd un’adrèce èlèctronica dens voutres prèferences por empleyér celes fonccionalitâts.",
        "emailconfirmlink": "Confirmâd voutron adrèce èlèctronica",
        "invalidemailaddress": "Cel’adrèce èlèctronica pôt pas étre accèptâye, semble avêr un format pas justo.\nSe vos plét, buchiéd un’adrèce ben formatâye ou ben lèssiéd cél champ vouedo.",
        "passwordreset-capture-help": "Se vos pouentâd cela câsa, lo mèssâjo (avouéc lo contresegno temporèro) vos serat montrâ quand serat mandâ a l’utilisator.",
        "passwordreset-email": "Adrèce èlèctronica :",
        "passwordreset-emailtitle": "Dètalys du comptio dessus {{SITENAME}}",
-       "passwordreset-emailtext-ip": "Yon (de sûr vos, avouéc l’adrèce IP $1) at demandâ na remês’a zérô de voutron\ncontresegno por {{SITENAME}} ($4). {{PLURAL:$3|Cél comptio utilisator-que est associyê|Celos comptios utilisators-que sont associyês}}\na cel’adrèce èlèctronica :\n\n$2\n\n{{PLURAL:$3|Cél contresegno temporèro èxpirerat|Celos contresegnos temporèros èxpireront}} dens {{PLURAL:$5|un jorn|$5 jorns}}.\nOra vos vos dête branchiér et pués chouèsir un contresegno novél. Se cela demanda vint pas de vos\nou ben se vos vos éte rapelâ de voutron contresegno originâl et que vos en voléd pas més changiér,\nvos pouede ignorar cél mèssâjo et continuar a empleyér voutron viely contresegno.",
-       "passwordreset-emailtext-user": "L’utilisator $1 dessus {{SITENAME}} at demandâ na remês’a zérô de voutron contresegno por {{SITENAME}}\n($4). {{PLURAL:$3|Cél comptio utilisator-que est associyê|Celos comptios utilisators-que sont associyês}}\na cel’adrèce èlèctronica :\n\n$2\n\n{{PLURAL:$3|Cél contresegno temporèro èxpirerat|Celos contresegnos temporèros èxpireront}} dens {{PLURAL:$5|un jorn|$5 jorns}}.\nOra vos vos dête branchiér et pués chouèsir un contresegno novél. Se cela demanda vint pas de vos\nou ben se vos vos éte rapelâ de voutron contresegno originâl et que vos en voléd pas més changiér,\nvos pouede ignorar cél mèssâjo et continuar a empleyér voutron viely contresegno.",
+       "passwordreset-emailtext-ip": "Yon (de sûr vos, avouéc l’adrèce IP $1) at demandâ na remês’a zérô de voutron\ncontresegno por {{SITENAME}} ($4). {{PLURAL:$3|Ceti comptio utilisator est associyê|Cetos comptios utilisators sont associyês}}\na cel’adrèce èlèctronica :\n\n$2\n\n{{PLURAL:$3|Cél contresegno temporèro èxpirerat|Celos contresegnos temporèros èxpireront}} dens {{PLURAL:$5|un jorn|$5 jorns}}.\nOra vos vos dête branchiér et pués chouèsir un contresegno novél. Se cela demanda vint pas de vos\nou ben se vos vos éte rapelâ de voutron contresegno originâl et que vos en voléd pas més changiér,\nvos pouede ignorar cél mèssâjo et continuar a empleyér voutron viely contresegno.",
+       "passwordreset-emailtext-user": "L’utilisator $1 dessus {{SITENAME}} at demandâ na remês’a zérô de voutron contresegno por {{SITENAME}}\n($4). {{PLURAL:$3|Ceti comptio utilisator est associyê|Cetos comptios utilisators sont associyês}}\na cel’adrèce èlèctronica :\n\n$2\n\n{{PLURAL:$3|Cél contresegno temporèro èxpirerat|Celos contresegnos temporèros èxpireront}} dens {{PLURAL:$5|un jorn|$5 jorns}}.\nOra vos vos dête branchiér et pués chouèsir un contresegno novél. Se cela demanda vint pas de vos\nou ben se vos vos éte rapelâ de voutron contresegno originâl et que vos en voléd pas més changiér,\nvos pouede ignorar cél mèssâjo et continuar a empleyér voutron viely contresegno.",
        "passwordreset-emailelement": "Nom d’utilisator :\n$1\n\nContresegno temporèro :\n$2",
        "passwordreset-emailsentemail": "Se cel’adrèce èlèctronica est associyêe a voutron comptio, adonc un mèssâjo de remês’a zérô de contresegno serat mandâ.",
        "passwordreset-emailsentusername": "S’y at un’adrèce èlèctronica associyêe a cél nom d’utilisator, adonc un mèssâjo de remês’a zérô de contresegno serat mandâ.",
        "readonlywarning": "<strong>Atencion : la bâsa de balyês est étâye cotâye por na rotina d’entretin, cen fât que vos porréd vêr pas encartar voutros changements orendrêt.</strong>\nVos pouede copiyér et côlar voutron tèxto dedens un fichiér tèxto et pués l’encartar por ples târd.\n\nL’administrator sistèmo que l’at cotâ at balyê cel’èxplicacion : $1",
        "protectedpagewarning": "<strong>Atencion : cela pâge est étâye protègiêe por que solament los utilisators qu’ant los drêts d’administrator la pouessont changiér.</strong>\nLa dèrriére entrâ du jornâl est balyêe ce-desot coment rèference :",
        "semiprotectedpagewarning": "<strong>Nota :</strong> cela pâge est étâye protègiêe por que solament los utilisators encartâs la pouessont changiér.\nLa dèrriére entrâ du jornâl est balyêe ce-desot coment rèference :",
-       "cascadeprotectedwarning": "<strong>Atencion :</strong> cela pâge est étâye protègiêe por que solament los utilisators qu’ant los drêts d’administrator la pouessont changiér, el est transcllua dedens {{PLURAL:$1|cela pâge protègiêe|celes pâges protègiêes}}-que avouéc la « protèccion en cascâda » activâye :",
+       "cascadeprotectedwarning": "<strong>Atencion :</strong> cela pâge est étâye protègiêe por que solament los utilisators qu’ant los drêts d’administrator la pouessont changiér, el est transcllua dedens {{PLURAL:$1|ceta pâge protègiêe|cetes pâges protègiêes}} avouéc la « protèccion en cascâda » activâye :",
        "titleprotectedwarning": "<strong>Atencion : cela pâge est étâye protègiêe por que de [[Special:ListGroupRights|drêts spècificos]] sont nècèssèros por la povêr fâre.</strong>\nLa dèrriére entrâ du jornâl est balyêe ce-desot coment rèference :",
        "templatesused": "{{PLURAL:$1|Modèlo empleyê|Modèlos empleyês}} per cela pâge :",
        "templatesusedpreview": "{{PLURAL:$1|Modèlo empleyê|Modèlos empleyês}} dedens cél apèrçu :",
        "sectioneditnotsupported-title": "Changement de sèccion pas recognu",
        "sectioneditnotsupported-text": "Lo changement d’una sèccion est pas recognu dens cela pâge.",
        "permissionserrors": "Fôta de pèrmission",
-       "permissionserrorstext": "Vos éd pas la pèrmission de fâre l’accion demandâye por {{PLURAL:$1|cela rêson|celes rêsons}}-que :",
-       "permissionserrorstext-withaction": "Vos éd pas la pèrmission de $2 por {{PLURAL:$1|cela rêson|celes rêsons}}-que :",
+       "permissionserrorstext": "Vos éd pas la pèrmission de fâre l’accion demandâye por {{PLURAL:$1|ceta rêson|cetes rêsons}} :",
+       "permissionserrorstext-withaction": "Vos éd pas la pèrmission de $2 por {{PLURAL:$1|ceta rêson|cetes rêsons}} :",
        "contentmodelediterror": "Vos pouede pas changiér cela vèrsion, son modèlo de contegnu est <code>$1</code>, cen que sè difèrence du modèlo de contegnu d’ora de la pâge <code>$2</code>.",
        "recreate-moveddeleted-warn": "<strong>Atencion : vos éte aprés refâre na pâge qu’est étâye suprimâye dês devant.</strong>\n\nDemandâd-vos se fôt franc continuar son changement.\nLo jornâl de les suprèssions et des dèplacements de cela pâge est balyê ce-desot por comoditât :",
        "moveddeleted-notice": "Cela pâge est étâye suprimâye.\nLo jornâl de les suprèssions et des dèplacements de cela pâge est balyê ce-desot coment rèference.",
        "logdelete-text": "Los èvènements du jornâl suprimâs continueront a aparêtre dedens los jornâls, mas na partia de lor contegnu serat inaccèssibla u publico.",
        "revdelete-text-others": "Los ôtros administrators porront adés arrevar u contegnu cachiê et lo refâre, a muens que des rèstriccions de més seyont dèfenies.",
        "revdelete-confirm": "Se vos plét, confirmâd qu’o est franc cen que vos voléd fâre, que vos en compregnéd les consèquences, et pués que vos o féte en acôrd avouéc les [[{{MediaWiki:Policy-url}}|politiques]].",
-       "revdelete-suppress-text": "La rèprèssion dêt étre empleyêe <strong>ren que</strong> dens celos câs-que :\n* Enformacions que pôvont étre difamatouères\n* Enformacions a sè que vant pas avouéc\n*: <em>adrèce, numerô de tèlèfono, numerô d’identificacion nacionâl, et tot cen que vat avouéc</em>",
+       "revdelete-suppress-text": "La rèprèssion dêt étre empleyêe <strong>ren que</strong> dens cetos câs :\n* Enformacions que pôvont étre difamatouères\n* Enformacions a sè que vant pas avouéc\n*: <em>adrèce, numerô de tèlèfono, numerô d’identificacion nacionâl, et tot cen que vat avouéc</em>",
        "revdelete-legend": "Dèfenir de rèstriccions de visibilitât",
        "revdelete-hide-text": "Tèxto de la vèrsion",
        "revdelete-hide-image": "Cachiér lo contegnu du fichiér",
        "mergehistory-from": "Pâge d’origina :",
        "mergehistory-into": "Pâge de dèstinacion :",
        "mergehistory-list": "Historico des changements que pôvont étre fusionâs",
-       "mergehistory-merge": "Celes vèrsions-que de [[:$1]] pôvont étre fusionâyes dedens [[:$2]].\nEmpleyéd la colona de botons de chouèx por fusionar mas que les vèrsions fêtes dês lo comencement tant qu’a la dâta spècifiâye.\nNotâd que l’usâjo des lims de navegacion rebeterat a zérô cela colona.",
+       "mergehistory-merge": "Cetes vèrsions de [[:$1]] pôvont étre fusionâyes dedens [[:$2]].\nEmpleyéd la colona de botons de chouèx por fusionar mas que les vèrsions fêtes dês lo comencement tant qu’a la dâta spècifiâye.\nNotâd que l’usâjo des lims de navegacion rebeterat a zérô cela colona.",
        "mergehistory-go": "Montrar los changements que pôvont étre fusionâs",
        "mergehistory-submit": "Fusionar les vèrsions",
        "mergehistory-empty": "Niona vèrsion pôt étre fusionâye.",
        "upload_directory_read_only": "Lo rèpèrtouèro de tèlèchargement ($1) est pas enscriptiblo per lo sèrvior Vouèbe.",
        "uploaderror": "Fôta pendent lo tèlèchargement",
        "upload-recreate-warning": "<strong>Atencion : un fichiér avouéc cél nom est étâ suprimâ dèplaciê.</strong>\n\nLo jornâl de les suprèssions et des dèplacements de cela pâge est balyê ique por comoditât :",
-       "uploadtext": "Empleyéd lo formulèro ce-desot por tèlèchargiér de fichiérs.\nPor vêre rechèrchiér de fichiérs tèlèchargiês dês devant, vêde la [[Special:FileList|lista des fichiérs tèlèchargiês]]. Los (re-)tèlèchargements sont asse-ben encartâs dessus lo [[Special:Log/upload|jornâl des tèlèchargements]], et les suprèssions dessus lo [[Special:Log/delete|jornâl de les suprèssions]].\n\nPor rapondre un fichiér dedens na pâge, empleyéd un lim de yona de celes fôrmes-que :\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Fichiér.jpg]]</nowiki></code></strong> por empleyér la vèrsion en plêna largior du fichiér\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Fichiér.png|200px|thumb|left|tèxto dèscriptif]]</nowiki></code></strong> por empleyér na miniatura de 200 pixèls de lârjo dedens na bouèt’a gôche avouéc « tèxto dèscriptif » coment dèscripcion\n* <strong><code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:Fichiér.ogg]]</nowiki></code></strong> por liyér tot drêt vers lo fichiér sen lo fâre vêre",
+       "uploadtext": "Empleyéd lo formulèro ce-desot por tèlèchargiér de fichiérs.\nPor vêre rechèrchiér de fichiérs tèlèchargiês dês devant, vêde la [[Special:FileList|lista des fichiérs tèlèchargiês]]. Los (re-)tèlèchargements sont asse-ben encartâs dessus lo [[Special:Log/upload|jornâl des tèlèchargements]], et les suprèssions dessus lo [[Special:Log/delete|jornâl de les suprèssions]].\n\nPor rapondre un fichiér dedens na pâge, empleyéd un lim de yona de cetes fôrmes :\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Fichiér.jpg]]</nowiki></code></strong> por empleyér la vèrsion en plêna largior du fichiér\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Fichiér.png|200px|thumb|left|tèxto dèscriptif]]</nowiki></code></strong> por empleyér na miniatura de 200 pixèls de lârjo dedens na bouèt’a gôche avouéc « tèxto dèscriptif » coment dèscripcion\n* <strong><code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:Fichiér.ogg]]</nowiki></code></strong> por liyér tot drêt vers lo fichiér sen lo fâre vêre",
        "upload-permitted": "Tipo{{PLURAL:$2||s}} de fichiérs ôtorisâ{{PLURAL:$2||s}} : $1.",
        "upload-preferred": "Tipo{{PLURAL:$2||s}} de fichiérs prèferâ{{PLURAL:$2||s}} : $1.",
        "upload-prohibited": "Tipo{{PLURAL:$2||s}} de fichiérs dèfendu{{PLURAL:$2||s}} : $1.",
        "file-thumbnail-no": "Lo nom du fichiér comence per <strong>$1</strong>.\nSemble étre un’émâge en talye rèduita <em>(miniatura)</em>.\nSe vos éd cel’émâge en hôta rèsolucion, tèlèchargiéd-la, ôtrament changiéd son nom, se vos plét.",
        "fileexists-forbidden": "Un fichiér avouéc cél nom ègziste ja et pôt pas étre ècllafâ.\nSe vos voléd adés tèlèchargiér voutron fichiér, se vos plét tornâd arriér et pués empleyéd un novél nom.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Un fichiér avouéc cél nom ègziste ja dedens lo dèpôt de fichiérs partagiê.\nSe vos voléd adés tèlèchargiér voutron fichiér, se vos plét tornâd arriér et pués empleyéd un novél nom.\n[[File:$1|thumb|center|$1]]",
-       "file-exists-duplicate": "Cél fichiér est un doblo de {{PLURAL:$1|cél fichiér|celos fichiérs}}-que :",
+       "file-exists-duplicate": "Cél fichiér est un doblo de {{PLURAL:$1|ceti fichiér|cetos fichiérs}} :",
        "file-deleted-duplicate": "Un fichiér pariér a ceti ([[:$1]]) est ja étâ suprimâ.\nVos devriâd controlar lo jornâl de les suprèssions de cél fichiér devant que lo tornar tèlèchargiér.",
        "file-deleted-duplicate-notitle": "Un fichiér pariér a ceti est ja étâ suprimâ et pués lo titro rèprimâ.\nVos devriâd demandar a quârqu’un avouéc la possibilitât de vêre les balyês du fichiér rèprimâ por ègzamenar la situacion devant que lo tornar tèlèchargiér.",
        "uploadwarning": "Atencion !",
        "filehist-filesize": "Talye du fichiér",
        "filehist-comment": "Comentèro",
        "imagelinks": "Usâjo du fichiér",
-       "linkstoimage": "{{PLURAL:$1|Cela pâge-que emplèye|Celes $1 pâges-que emplèyont}} cél fichiér :",
-       "linkstoimage-more": "Més {{PLURAL:$1|d’una pâge emplèye|de $1 pâges emplèyont}} cél fichiér.\nCela lista-que montre mas que {{PLURAL:$1|la premiére pâge qu’emplèye|les $1 premiéres pâges qu’emplèyont}} cél fichiér.\nNa [[Special:WhatLinksHere/$2|lista complèta]] est disponibla.",
+       "linkstoimage": "{{PLURAL:$1|Ceta pâge emplèye|Cetes $1 pâges emplèyont}} cél fichiér :",
+       "linkstoimage-more": "Més {{PLURAL:$1|d’una pâge emplèye|de $1 pâges emplèyont}} cél fichiér.\nCeta lista montre mas que {{PLURAL:$1|la premiére pâge qu’emplèye|les $1 premiéres pâges qu’emplèyont}} cél fichiér.\nNa [[Special:WhatLinksHere/$2|lista complèta]] est disponibla.",
        "nolinkstoimage": "Niona pâge emplèye cél fichiér.",
        "morelinkstoimage": "Vêde [[Special:WhatLinksHere/$1|més de lims]] de vers cél fichiér.",
        "linkstoimage-redirect": "$1 (redirèccion de fichiér) $2",
        "unusedtemplatestext": "Cela pâge liste totes les pâges de l’èspâço de noms « {{ns:template}} » que sont pas rapondues dedens nion’ôtra pâge.\nOubliâd pas de controlar s’y at gins d’ôtro lim de vers los modèlos devant que los suprimar.",
        "unusedtemplateswlh": "ôtros lims",
        "randompage": "Pâge a l’hasârd",
-       "randompage-nopages": "Y at gins de pâge dedens {{PLURAL:$2|cél èspâço|celos èspâços}} de noms-que : $1.",
+       "randompage-nopages": "Y at gins de pâge dedens {{PLURAL:$2|cet’èspâço|cetos èspâços}} de noms : $1.",
        "randomincategory": "Pâge a l’hasârd dedens la catègoria",
        "randomincategory-invalidcategory": "« $1 » est pas un nom de catègoria justo.",
        "randomincategory-nopages": "Y at gins de pâge dedens la catègoria [[:Category:$1|$1]].",
        "double-redirect-fixed-maintenance": "Corrèccion ôtomatica de la redirèccion dobla de [[$1]] de vers [[$2]] dens un ovrâjo d’entretin.",
        "double-redirect-fixer": "Corrèctor de redirèccion",
        "brokenredirects": "Redirèccions câsses",
-       "brokenredirectstext": "Celes redirèccions-que mènont vers de pâges inègzistentes :",
+       "brokenredirectstext": "Cetes redirèccions mènont vers de pâges inègzistentes :",
        "brokenredirects-edit": "changiér",
        "brokenredirects-delete": "suprimar",
        "withoutinterwiki": "Pâges sen lims entèrlengoues",
-       "withoutinterwiki-summary": "Celes pâges-que ant gins de lim de vers des vèrsions en ôtres lengoues.",
+       "withoutinterwiki-summary": "Cetes pâges ant gins de lim de vers des vèrsions en ôtres lengoues.",
        "withoutinterwiki-legend": "Prèfixo",
        "withoutinterwiki-submit": "Montrar",
        "fewestrevisions": "Pâges les muens changiêes",
        "ntransclusions": "empleyê dessus $1 pâge{{PLURAL:$1||s}}",
        "specialpage-empty": "Y at gins de rèsultat a fâre vêre.",
        "lonelypages": "Pâges orfenes",
-       "lonelypagestext": "Celes pâges-que sont ni liyêes ni transcllues per d’ôtres pâges de {{SITENAME}}.",
+       "lonelypagestext": "Cetes pâges sont ni liyêes ni transcllues per d’ôtres pâges de {{SITENAME}}.",
        "uncategorizedpages": "Pâges sen catègories",
        "uncategorizedcategories": "Catègories sen catègories",
        "uncategorizedimages": "Fichiérs sen catègories",
        "wantedpages-summary": "Lista de les pâges inègzistentes qu’ant lo més de lims de vers lor, en èxcllusent les pâges qu’ant ren que de redirèccions pouentent vers lor. Por avêr na lista de les pâges inègzistentes qu’ant de redirèccions pouentent vers lor, vêde la [[{{#special:BrokenRedirects}}|lista de les redirèccions câsses]].",
        "wantedpages-badtitle": "Titro pas justo dedens l’ensemblo de rèsultats : $1",
        "wantedfiles": "Fichiérs demandâs",
-       "wantedfiletext-cat": "Celos fichiérs-que sont empleyês, mas ègzistont pas. Los fichiérs de dèpôts de defôr pôvont étre listâs mémo s’ègzistont. Tôs celos fôx positifs seront <del>barrâs</del>. Et pués les pâges qu’apondont de fichiérs qu’ègzistont pas sont listâs dedens [[:$1]].",
-       "wantedfiletext-cat-noforeign": "Celos fichiérs-que sont empleyês, mas ègzistont pas. Et pués les pâges qu’apondont de fichiérs qu’ègzistont pas sont listâs dedens [[:$1]].",
-       "wantedfiletext-nocat": "Celos fichiérs-que sont empleyês, mas ègzistont pas. Los fichiérs de dèpôts de defôr pôvont étre listâs mémo s’ègzistont. Tôs celos fôx positifs seront <del>barrâs</del>.",
-       "wantedfiletext-nocat-noforeign": "Celos fichiérs-que sont empleyês, mas ègzistont pas.",
+       "wantedfiletext-cat": "Cetos fichiérs sont empleyês, mas ègzistont pas. Los fichiérs de dèpôts de defôr pôvont étre listâs mémo s’ègzistont. Tôs celos fôx positifs seront <del>barrâs</del>. Et pués les pâges qu’apondont de fichiérs qu’ègzistont pas sont listâs dedens [[:$1]].",
+       "wantedfiletext-cat-noforeign": "Cetos fichiérs sont empleyês, mas ègzistont pas. Et pués les pâges qu’apondont de fichiérs qu’ègzistont pas sont listâs dedens [[:$1]].",
+       "wantedfiletext-nocat": "Cetos fichiérs sont empleyês, mas ègzistont pas. Los fichiérs de dèpôts de defôr pôvont étre listâs mémo s’ègzistont. Tôs celos fôx positifs seront <del>barrâs</del>.",
+       "wantedfiletext-nocat-noforeign": "Cetos fichiérs sont empleyês, mas ègzistont pas.",
        "wantedtemplates": "Modèlos demandâs",
        "mostlinked": "Pâges les ples liyêes",
        "mostlinkedcategories": "Catègories les ples liyêes",
        "shortpages": "Pâges côrtes",
        "longpages": "Pâges longes",
        "deadendpages": "Pâges en charriére borgne",
-       "deadendpagestext": "Celes pâges-que contegnont gins de lim de vers d’ôtres pâges de {{SITENAME}}.",
+       "deadendpagestext": "Cetes pâges contegnont gins de lim de vers d’ôtres pâges de {{SITENAME}}.",
        "protectedpages": "Pâges protègiêes",
        "protectedpages-indef": "Mas que les protèccions sen fin",
        "protectedpages-summary": "Cela pâge liste les pâges ègzistentes que sont ora protègiêes. Por na lista des titros protègiês contre la crèacion, vêde [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]].",
        "ancientpages": "Pâges les ples vielyes",
        "move": "Dèplaciér",
        "movethispage": "Dèplaciér cela pâge",
-       "unusedimagestext": "Celos fichiérs-que ègzistont, mas sont pas apondus a niona pâge.\nSe vos plét, notâd que d’ôtros setos Vouèbe pôvont avêr un lim de vers un fichiér avouéc un’URL drêta, adonc un fichiér pôt adés étre listâ ique pendent qu’il est en usâjo actif.",
-       "unusedcategoriestext": "Celes catègories-que ègzistont, mas nion’ôtra pâge niona catègoria les emplèye.",
+       "unusedimagestext": "Cetos fichiérs ègzistont, mas sont pas apondus a niona pâge.\nSe vos plét, notâd que d’ôtros setos Vouèbe pôvont avêr un lim de vers un fichiér avouéc un’URL drêta, adonc un fichiér pôt adés étre listâ ique pendent qu’il est en usâjo actif.",
+       "unusedcategoriestext": "Cetes catègories ègzistont, mas nion’ôtra pâge niona catègoria les emplèye.",
        "notargettitle": "Niona ciba",
        "notargettext": "Vos éd pas spècifiâ na pâge un utilisator ciba que vos voléd fâre cel’accion.",
        "nopagetitle": "Niona pâge ciba d’ense",
        "cachedspecial-refresh-now": "Vêre la ples novèla.",
        "categories": "Catègories",
        "categories-submit": "Montrar",
-       "categoriespagetext": "{{PLURAL:$1|Cela catègoria-que contint|Celes catègories-que contegnont}} de pâges de mèdiâs.\nLes [[Special:UnusedCategories|catègories pas empleyêes]] sont pas montrâyes ique.\nVêde avouéc les [[Special:WantedCategories|catègories demandâyes]].",
+       "categoriespagetext": "{{PLURAL:$1|Ceta catègoria contint|Cetes catègories contegnont}} de pâges de mèdiâs.\nLes [[Special:UnusedCategories|catègories pas empleyêes]] sont pas montrâyes ique.\nVêde avouéc les [[Special:WantedCategories|catègories demandâyes]].",
        "categoriesfrom": "Fâre vêre les catègories dês :",
        "deletedcontributions": "Contribucions suprimâyes",
        "deletedcontributions-title": "Contribucions suprimâyes",
        "protect-locked-blocked": "Vos pouede pas changiér los nivéls de protèccion tant que vos éte blocâ{{GENDER:||ye}}.\nVê-que la configuracion d’ora de la pâge <strong>$1</strong> :",
        "protect-locked-dblock": "Los nivéls de protèccion pôvont pas étre changiês, la bâsa de balyês est cotâye.\nVê-que la configuracion d’ora de la pâge <strong>$1</strong> :",
        "protect-locked-access": "Voutron comptio at pas los drêts nècèssèros por changiér los nivéls de protèccion de pâges.\nVê-que la configuracion d’ora de la pâge <strong>$1</strong> :",
-       "protect-cascadeon": "Ora cela pâge est protègiêe, el est transcllua dedens {{PLURAL:$1|cela pâge-que qu’est étâye protègiêe|celes pâges-que que sont étâyes protègiêes}} avouéc lo chouèx « protèccion en cascâda » activâ.\nLos changements du nivél de protèccion de cela pâge afècteront pas la protèccion en cascâda.",
+       "protect-cascadeon": "Ora cela pâge est protègiêe, el est transcllua dedens {{PLURAL:$1|ceta pâge qu’est étâye protègiêe|cetes pâges que sont étâyes protègiêes}} avouéc lo chouèx « protèccion en cascâda » activâ.\nLos changements du nivél de protèccion de cela pâge afècteront pas la protèccion en cascâda.",
        "protect-default": "Ôtorisar tôs los utilisators",
        "protect-fallback": "Ôtorisar mas que los utilisators avouéc lo drêt « $1 »",
        "protect-level-autoconfirmed": "Ôtorisar mas que los utilisators ôtoconfirmâs",
        "restriction-level-all": "tôs los nivéls",
        "undelete": "Vêre les pâges suprimâyes",
        "undeletepage": "Vêre et refâre de pâges suprimâyes",
-       "undeletepagetitle": "<strong>Cela lista-que contint de vèrsions suprimâyes de [[:$1|$1]].</strong>",
+       "undeletepagetitle": "<strong>Ceta lista contint de vèrsions suprimâyes de [[:$1|$1]].</strong>",
        "viewdeletedpage": "Vêre les pâges suprimâyes",
-       "undeletepagetext": "{{PLURAL:$1|Cela pâge-que est étâye suprimâye et pués sè trôve|Celes pâges-que sont étâyes suprimâyes et pués sè trôvont}} adés dedens les arch·ives, de yô que pô{{PLURAL:$1||von}}t étre refêt{{PLURAL:$1|a|es}}.\nLes arch·ives pôvont étre neteyêes règuliérement.",
+       "undeletepagetext": "{{PLURAL:$1|Ceta pâge est étâye suprimâye et pués sè trôve|Cetes pâges sont étâyes suprimâyes et pués sè trôvont}} adés dedens les arch·ives, de yô que pô{{PLURAL:$1||von}}t étre refêt{{PLURAL:$1|a|es}}.\nLes arch·ives pôvont étre neteyêes règuliérement.",
        "undelete-fieldset-title": "Refâre les vèrsions",
        "undeleteextrahelp": "Por refâre l’historico complèt de la pâge, lèssiéd totes les câses pas pouentâyes et pués cllicâd dessus <strong><em>{{int:undeletebtn}}</em></strong>.\nPor fâre na rèstoracion a mêtiêt, pouentâd les câses que corrèspondont a les vèrsions a refâre et pués cllicâd dessus <strong><em>{{int:undeletebtn}}</em></strong>.",
        "undeleterevisions": "$1 {{PLURAL:$1|vèrsion suprimâye|vèrsions suprimâyes}}",
        "whatlinkshere": "Pâges liyêes",
        "whatlinkshere-title": "Pâges que pouentont vers « $1 »",
        "whatlinkshere-page": "Pâge :",
-       "linkshere": "Celes pâges-que contegnont un lim de vers <strong>[[:$1]]</strong> :",
+       "linkshere": "Cetes pâges contegnont un lim de vers <strong>[[:$1]]</strong> :",
        "nolinkshere": "Niona pâge contint de lim de vers <strong>[[:$1]]</strong>.",
        "nolinkshere-ns": "Niona pâge contint de lim de vers <strong>[[:$1]]</strong> dedens l’èspâço de noms chouèsi.",
        "isredirect": "pâge de redirèccion",
        "import-interwiki-submit": "Importar",
        "import-mapping-default": "Importar ux endrêts per dèfôt",
        "import-mapping-namespace": "Importar vers un èspâço de noms :",
-       "import-mapping-subpage": "Importar coment sot-pâges de cela pâge-que :",
+       "import-mapping-subpage": "Importar coment sot-pâges de ceta pâge :",
        "import-upload-filename": "Nom du fichiér :",
        "import-comment": "Comentèro :",
        "importtext": "Se vos plét, èxportâd lo fichiér dês lo vouiqui d’origina en empleyent son [[Special:Export|outil d’èxportacion]].\nEncartâd-lo sur voutron ordenator et pués tèlèchargiéd-lo ique.",
        "import-error-special": "La pâge « $1 » est pas étâye importâye, el est a un èspâço de noms spèciâl qu’ôtorise gins de pâge.",
        "import-error-invalid": "La pâge « $1 » est pas étâye importâye, lo nom que lyé serêt étâye importâye desot est pas justo sur cél vouiqui.",
        "import-error-unserialize": "La vèrsion $2 de la pâge « $1 » pôt pas étre dèssèrialisâye. La vèrsion ére raportâye coment empleyent lo modèlo de contegnu $3 sèrialisâ en $4.",
-       "import-error-bad-location": "La vèrsion $2 qu’emplèye lo modèlo de contegnu $3 at pas possu étre stocâye dessus « $1 » sur cél vouiqui-que, cél modèlo est pas recognu sur cela pâge.",
+       "import-error-bad-location": "La vèrsion $2 qu’emplèye lo modèlo de contegnu $3 at pas possu étre stocâye dessus « $1 » sur ceti vouiqui, cél modèlo est pas recognu sur cela pâge.",
        "import-options-wrong": "{{PLURAL:$2|Crouyo chouèx|Crouyos chouèx}} : <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "La pâge racena balyêe est un titro pas justo.",
        "import-rootpage-nosubpage": "L’èspâço de noms « $1 » de la pâge racena ôtorise pas les sot-pâges.",
        "nocredits": "Y at gins d’enformacion d’atribucion disponibla por cela pâge.",
        "spamprotectiontitle": "Filtro de protèccion contre los mèssâjos cofos",
        "spamprotectiontext": "Lo tèxto que vos éd volu encartar est étâ blocâ per lo filtro contre los mèssâjos cofos.\nO est probâblament diu a un lim de vers un seto de defôr qu’aparêt sur la lista nêre.",
-       "spamprotectionmatch": "Cél tèxto-que est cen qu’at dècllenchiê noutron filtro contre los mèssâjos cofos : $1",
+       "spamprotectionmatch": "Ceti tèxto est cen qu’at dècllenchiê noutron filtro contre los mèssâjos cofos : $1",
        "spambot_username": "Neteyâjo de mèssâjos cofos per MediaWiki",
        "spam_reverting": "Rètablissement de la dèrriére vèrsion que contint gins de lim de vers $1",
        "spam_blanking": "Totes les vèrsions que contegnont de lims de vers $1 sont blanchies",
        "scarytranscludefailed-httpstatus": "[La rècupèracion de modèlo at pas reussi por $1 : HTTP $2]",
        "scarytranscludetoolong": "[L’URL est trop longe]",
        "deletedwhileediting": "<strong>Atencion :</strong> cela pâge est étâye suprimâye aprés que vos vos éte betâ a la changiér !",
-       "confirmrecreate": "L’utilisat{{GENDER:$1|or|rice}} [[User:$1|$1]] ([[User talk:$1|Discussion]]) at suprimâ cela pâge, pendent que vos vos érâd betâ a la changiér, por cela rêson-que :\n: <em>$2</em>\nSe vos plét, confirmâd que vos voléd franc refâre cela pâge.",
-       "confirmrecreate-noreason": "L’utilisat{{GENDER:$1|or|rice}} [[User:$1|$1]] ([[User talk:$1|Discussion]]) at suprimâ cela pâge, pendent que vos vos érâd betâ a la changiér. Se vos plét, confirmâd que vos voléd franc refâre cela pâge.",
+       "confirmrecreate": "L’utilisat{{GENDER:$1|or|rice}} [[User:$1|$1]] ([[User talk:$1|discussion]]) at suprimâ cela pâge, pendent que vos vos érâd betâ a la changiér, por ceta rêson :\n: <em>$2</em>\nSe vos plét, confirmâd que vos voléd franc refâre cela pâge.",
+       "confirmrecreate-noreason": "L’utilisat{{GENDER:$1|or|rice}} [[User:$1|$1]] ([[User talk:$1|discussion]]) at suprimâ cela pâge, pendent que vos vos érâd betâ a la changiér. Se vos plét, confirmâd que vos voléd franc refâre cela pâge.",
        "recreate": "Refâre",
        "confirm_purge_button": "Confirmar",
-       "confirm-purge-top": "Voléd-vos purgiér lo cache de ceta pâge ?",
-       "confirm-purge-bottom": "Purgiér una pâge èface lo cache et pués fôrce la dèrriére vèrsion a étre montrâ.",
+       "confirm-purge-top": "Vos voléd purgiér lo cacho de cela pâge ?",
+       "confirm-purge-bottom": "Purgiér na pâge vouede lo cacho et pués fôrce la dèrriére vèrsion a étre montrâye.",
        "confirm-watch-button": "D’acôrd",
-       "confirm-watch-top": "Apondre ceta pâge a voutra lista de survelyence ?",
+       "confirm-watch-top": "Apondre cela pâge a voutra lista de gouârda ?",
        "confirm-unwatch-button": "D’acôrd",
-       "confirm-unwatch-top": "Enlevar ceta pâge de voutra lista de survelyence ?",
+       "confirm-unwatch-top": "Enlevar cela pâge de voutra lista de gouârda ?",
        "semicolon-separator": "&nbsp;;&#32;",
        "colon-separator": "&nbsp;:&#32;",
        "percent": "$1&#160;%",
+       "quotation-marks": "« $1 »",
        "imgmultipageprev": "← pâge devant",
        "imgmultipagenext": "pâge aprés →",
        "imgmultigo": "Emmodar !",
        "imgmultigoto": "Alar a la pâge $1",
+       "img-lang-default": "(lengoua per dèfôt)",
+       "img-lang-info": "Montrar cel’émâge en $1 $2.",
        "img-lang-go": "Emmodar",
-       "ascending_abbrev": "que crêt",
-       "descending_abbrev": "que dècrêt",
+       "ascending_abbrev": "crès.",
+       "descending_abbrev": "dècr.",
        "table_pager_next": "Pâge aprés",
        "table_pager_prev": "Pâge devant",
        "table_pager_first": "Premiére pâge",
        "table_pager_last": "Dèrriére pâge",
-       "table_pager_limit": "Montrar $1 èlèment{{PLURAL:$1||s}} per pâge",
-       "table_pager_limit_label": "Rèsultats per pâge :",
+       "table_pager_limit": "Montrar $1 piéces per pâge",
+       "table_pager_limit_label": "Piéces per pâge :",
        "table_pager_limit_submit": "Emmodar",
-       "table_pager_empty": "Gins de rèsultat",
+       "table_pager_empty": "Nion rèsultat",
        "autosumm-blank": "Pâge blanchia",
        "autosumm-replace": "Contegnu remplaciê per « $1 »",
-       "autoredircomment": "Pâge redirigiê vers [[$1]]",
+       "autoredircomment": "Pâge redirigiêe vers [[$1]]",
        "autosumm-new": "Pâge fêta avouéc « $1 »",
+       "autosumm-newblank": "Pâge voueda fêta",
        "size-bytes": "$1 o",
        "size-kilobytes": "$1 Kio",
        "size-megabytes": "$1 Mio",
        "size-gigabytes": "$1 Gio",
-       "lag-warn-normal": "Los changements que dâtont de muens de $1 {{PLURAL:$1|seconda|secondes}} pôvont pas aparêtre dens ceta lista.",
-       "lag-warn-high": "En rêson d’un retârd important du sèrvor de bâsa de balyês, los changements que dâtont de muens de $1 {{PLURAL:$1|seconda|secondes}} pôvont pas aparêtre dens ceta lista.",
-       "watchlistedit-normal-title": "Changiér la lista de survelyence",
-       "watchlistedit-normal-legend": "Enlevar des titros de la lista de survelyence",
-       "watchlistedit-normal-explain": "Los titros de voutra lista de survelyence sont montrâs ce-desot.\nPor enlevar un titro (et sa pâge de discussion), pouentâd la câsa a coutâ et pués clicâd sur lo boton « {{int:Watchlistedit-normal-submit}} ».\nVos pouede asse-ben changiér la [[Special:EditWatchlist/raw|lista en fôrma bruta]].",
+       "lag-warn-normal": "Los changements que dâtont de muens de $1 second{{PLURAL:$1|a|es}} pôvont pas aparêtre dedens cela lista.",
+       "lag-warn-high": "A côsa d’un retârd important du sèrvior de bâsa de balyês, los changements que dâtont de muens de $1 second{{PLURAL:$1|a|es}} pôvont pas aparêtre dedens cela lista.",
+       "watchlistedit-normal-title": "Changiér la lista de gouârda",
+       "watchlistedit-normal-legend": "Enlevar de titros de la lista de gouârda",
+       "watchlistedit-normal-explain": "Los titros de voutra lista de gouârda sont montrâs ce-desot.\nPor enlevar un titro, pouentâd la câsa a coutâ et pués cllicâd sur lo boton « {{int:Watchlistedit-normal-submit}} ».\nVos pouede asse-ben [[Special:EditWatchlist/raw|changiér la lista en fôrma bruta]].",
        "watchlistedit-normal-submit": "Enlevar los titros",
-       "watchlistedit-normal-done": "{{PLURAL:$1|Yon titro at étâ enlevâ|$1 titros ont étâ enlevâs}} de voutra lista de survelyence :",
-       "watchlistedit-raw-title": "Changiér la lista de survelyence en fôrma bruta",
-       "watchlistedit-raw-legend": "Changement de la lista de survelyence en fôrma bruta",
-       "watchlistedit-raw-explain": "Los titros de voutra lista de survelyence sont montrâs ce-desot et pôvont étre changiês en los apondent ou ben en los enlevent de la lista ;\nyon titro per legne.\nQuand vos éd feni, clicâd sur lo boton « {{int:Watchlistedit-raw-submit}} ».\nVos pouede asse-ben utilisar l’[[Special:EditWatchlist|èditor normal]].",
+       "watchlistedit-normal-done": "{{PLURAL:$1|Un titro est étâ enlevâ|$1 titros sont étâs enlevâs}} de voutra lista de gouârda :",
+       "watchlistedit-raw-title": "Changiér la lista de gouârda en fôrma bruta",
+       "watchlistedit-raw-legend": "Changement de la lista de gouârda en fôrma bruta",
+       "watchlistedit-raw-explain": "Los titros de voutra lista de gouârda sont montrâs ce-desot et pôvont étre changiês en los apondent los enlevent de la lista ;\nun titro per legne.\nQuand vos éd chavono, cllicâd sur lo boton « {{int:Watchlistedit-raw-submit}} ».\nVos pouede asse-ben [[Special:EditWatchlist|empleyér l’èditor standârd]].",
        "watchlistedit-raw-titles": "Titros :",
-       "watchlistedit-raw-submit": "Betar a jorn la lista de survelyence",
-       "watchlistedit-raw-done": "Voutra lista de survelyence at étâ betâ a jorn.",
-       "watchlistedit-raw-added": "{{PLURAL:$1|Yon titro at étâ apondu|$1 titros ont étâ apondus}} :",
-       "watchlistedit-raw-removed": "{{PLURAL:$1|Yon titro at étâ enlevâ|$1 titros ont étâ enlevâs}} :",
-       "watchlisttools-view": "Lista de survelyence",
-       "watchlisttools-edit": "Vêre et changiér la lista de survelyence",
-       "watchlisttools-raw": "Changiér la lista de survelyence en fôrma bruta",
+       "watchlistedit-raw-submit": "Betar a jorn la lista de gouârda",
+       "watchlistedit-raw-done": "Voutra lista de gouârda est étâye betâye a jorn.",
+       "watchlistedit-raw-added": "{{PLURAL:$1|Un titro est étâ apondu|$1 titros sont étâs apondus}} :",
+       "watchlistedit-raw-removed": "{{PLURAL:$1|Un titro est étâ enlevâ|$1 titros sont étâs enlevâs}} :",
+       "watchlistedit-clear-title": "Vouedar la lista de gouârda",
+       "watchlistedit-clear-legend": "Vouedar la lista de gouârda",
+       "watchlistedit-clear-explain": "Tôs los titros seront enlevâs de voutra lista de gouârda",
+       "watchlistedit-clear-titles": "Titros :",
+       "watchlistedit-clear-submit": "Vouedar la lista de gouârda (o est por de bon !)",
+       "watchlistedit-clear-done": "Voutra lista de gouârda est étâye vouedâye.",
+       "watchlistedit-clear-removed": "{{PLURAL:$1|Un titro est étâ enlevâ|$1 titros sont étâs enlevâs}} :",
+       "watchlistedit-too-many": "Y at trop de pâges a fâre vêre ique.",
+       "watchlisttools-clear": "Vouedar la lista de gouârda",
+       "watchlisttools-view": "Lista de gouârda",
+       "watchlisttools-edit": "Vêre et changiér la lista de gouârda",
+       "watchlisttools-raw": "Changiér la lista de gouârda en fôrma bruta",
        "iranian-calendar-m1": "de farvardin",
        "iranian-calendar-m2": "d’ordibehèch·ete",
        "iranian-calendar-m3": "de c’hordâde",
        "hebrew-calendar-m10-gen": "de tamouz",
        "hebrew-calendar-m11-gen": "d’av",
        "hebrew-calendar-m12-gen": "d’èloul",
-       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discutar]])",
-       "duplicate-defaultsort": "'''Atencion :''' la cllâf de tri per dèfôt « $2 » ècllafe cela « $1 ».",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discussion]])",
+       "timezone-local": "Locâl",
+       "duplicate-defaultsort": "<strong>Atencion :</strong> la cllâf de chouèx per dèfôt « $2 » ècllafe la vielye cllâf « $1 ».",
+       "duplicate-displaytitle": "<strong>Atencion :</strong> lo titro de viua « $2 » ècllafe lo viely titro « $1 ».",
+       "invalid-indicator-name": "<strong>Fôta :</strong> l’atribut <code>name</code> des endiquiors d’ètat de la pâge dêt pas étre vouedo.",
        "version": "Vèrsion",
-       "version-extensions": "Èxtensions enstalâs",
-       "version-skins": "Habelyâjos",
+       "version-extensions": "Èxtensions enstalâyes",
+       "version-skins": "Habelyâjos enstalâs",
        "version-specialpages": "Pâges spèciâles",
-       "version-parserhooks": "Grèfons du parsor",
+       "version-parserhooks": "Èxtensions parsiors",
        "version-variables": "Variâbles",
-       "version-antispam": "Prèvencion du spame",
+       "version-antispam": "Prèvencion de mèssâjos cofos",
        "version-other": "De totes sôrtes",
-       "version-mediahandlers": "Maneyors de mèdia",
-       "version-hooks": "Grèfons",
-       "version-parser-extensiontags": "Balises d’èxtension du parsor",
-       "version-parser-function-hooks": "Grèfons de les fonccions du parsor",
-       "version-hook-name": "Nom du grèfon",
-       "version-hook-subscribedby": "Soscrit per",
+       "version-mediahandlers": "Maneyors de mèdiâs",
+       "version-hooks": "Èxtensions",
+       "version-parser-extensiontags": "Balises d’èxtensions parsiors",
+       "version-parser-function-hooks": "Èxtensions de les fonccions parsiors",
+       "version-hook-name": "Nom de l’èxtension",
+       "version-hook-subscribedby": "Abonâs :",
        "version-version": "($1)",
-       "version-license": "Licence",
-       "version-poweredby-credits": "Ceti vouiqui fonccione grâce a '''[https://www.mediawiki.org/ MediaWiki]''', copyright © 2001-$1 $2.",
+       "version-no-ext-name": "[nion nom]",
+       "version-license": "Licence MediaWiki",
+       "version-ext-license": "Licence",
+       "version-ext-colheader-name": "Èxtension",
+       "version-skin-colheader-name": "Habelyâjo",
+       "version-ext-colheader-version": "Vèrsion",
+       "version-ext-colheader-license": "Licence",
+       "version-ext-colheader-description": "Dèscripcion",
+       "version-ext-colheader-credits": "Ôtors",
+       "version-license-title": "Licence por $1",
+       "version-license-not-found": "Nion’enformacion dètalyêe de la licence est étâye trovâye por cel’èxtension.",
+       "version-credits-title": "Grant-marci por $1",
+       "version-credits-not-found": "Nion’enformacion dètalyêe du grant-marci est étâye trovâye por cel’èxtension.",
+       "version-poweredby-credits": "Cél vouiqui fonccione grâce a <strong>[https://www.mediawiki.org/ MediaWiki]</strong>, copyright © 2001-$1 $2.",
        "version-poweredby-others": "ôtros",
-       "version-license-info": "MediaWiki est una programeria libra ; vos la pouede tornar distribuar et / ou changiér d’aprés los tèrmos de la Licence publica g·ènèrala GNU coment publeyê per la Free Software Foundation ; seye la vèrsion 2 de la Licence, ou ben (a voutron chouèx) tota novèla vèrsion.\n\nMediaWiki est distribuâ dens l’èsperance que serat utila, mas SEN GINS DE GARANTIA ; sen mémo la garantia emplicita de COMÈRCIALISACION ou ben d’ADAPTACION A UN USÂJO PARTICULIÉR. Vêde la Licence publica g·ènèrala GNU por més de dètalys.\n\nVos devriâd avêr reçu un [{{SERVER}}{{SCRIPTPATH}}/COPYING ègzemplèro de la Licence publica g·ènèrala GNU] avouéc ceti programo ; ôtrament, ècrîde a la « Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA » ou ben [//www.gnu.org/licenses/old-licenses/gpl-2.0.html liéséd-la en legne].",
-       "version-software": "Programeries enstalâs",
+       "version-poweredby-translators": "traductors de translatewiki.net",
+       "version-credits-summary": "Nos tegnens a remarciér cetes gens por lor contribucion a [[Special:Version|MediaWiki]].",
+       "version-license-info": "MediaWiki est na programeria libra ; vos la pouede redistribuar et / ou changiér d’aprés los tèrmos de la Licence publica g·ènèrâla GNU coment publeyêe per la Free Software Foundation ; ou ben la vèrsion 2 de la Licence ou ben (a voutron chouèx) tota vèrsion ples novèla.\n\nMediaWiki est distribuâ dens l’èsperance que serat utila, mas SEN NIONA GARANTIA ; sen mémo la garantia sosentendua de COMÈRCIALISACION d’ADAPTACION A UN USÂJO PARTICULIÉR. Vêde la Licence publica g·ènèrâla GNU por més de dètalys.\n\nVos devriâd avêr reçu na [{{SERVER}}{{SCRIPTPATH}}/COPYING copia de la Licence publica g·ènèrâla GNU] avouéc cél programo ; ôtrament, ècrîde a la « Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA » ou ben [//www.gnu.org/licenses/old-licenses/gpl-2.0.html liéséd-la en legne].",
+       "version-software": "Programeries enstalâyes",
        "version-software-product": "Marchandie",
        "version-software-version": "Vèrsion",
-       "version-entrypoints": "URL de pouent d’entrâ",
+       "version-entrypoints": "URLs des pouents d’entrâ",
        "version-entrypoints-header-entrypoint": "Pouent d’entrâ",
        "version-entrypoints-header-url": "URL",
+       "version-libraries": "Bibliotèques enstalâyes",
+       "version-libraries-library": "Bibliotèca",
+       "version-libraries-version": "Vèrsion",
+       "version-libraries-license": "Licence",
+       "version-libraries-description": "Dèscripcion",
+       "version-libraries-authors": "Ôtors",
+       "redirect": "Redirigiér per ID de fichiér, utilisator, pâge, vèrsion ou ben jornâl",
+       "redirect-summary": "Cela pâge spèciâla redirige vers un fichiér (lo nom de fichiér balyê), na pâge (l’ID de vèrsion de pâge balyê), na pâge d’utilisator (l’ID numerico a l’utilisator balyê) ou ben un’entrâ de jornâl (l’ID du jornâl balyê). Usâjo : [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] ou ben [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Emmodar",
+       "redirect-lookup": "Rechèrche :",
+       "redirect-value": "Valor :",
+       "redirect-user": "ID a l’utilisator",
+       "redirect-page": "ID de pâge",
+       "redirect-revision": "Vèrsion de la pâge",
+       "redirect-file": "Nom du fichiér",
+       "redirect-logid": "ID du jornâl",
+       "redirect-not-exists": "Valor entrovâbla",
        "fileduplicatesearch": "Rechèrche des fichiérs en doblo",
        "fileduplicatesearch-summary": "Rechèrche des fichiérs en doblo d’aprés lor mârca de chaplâjo.",
        "fileduplicatesearch-filename": "Nom du fichiér :",
        "fileduplicatesearch-submit": "Rechèrchiér",
        "fileduplicatesearch-info": "$1 × $2 pixèls<br />Talye du fichiér : $3<br />Tipo MIME : $4",
        "fileduplicatesearch-result-1": "Lo fichiér « $1 » at gins de doblo pariér.",
-       "fileduplicatesearch-result-n": "Lo fichiér « $1 » at $2 {{PLURAL:$2|doblo pariér|doblos pariérs}}.",
-       "fileduplicatesearch-noresults": "Nion fichiér apelâ « $1 » at étâ trovâ.",
+       "fileduplicatesearch-result-n": "Lo fichiér « $1 » at {{PLURAL:$2|1 doblo pariér|$2 doblos pariérs}}.",
+       "fileduplicatesearch-noresults": "Nion fichiér apelâ « $1 » est étâ trovâ.",
        "specialpages": "Pâges spèciâles",
-       "specialpages-note": "* Pâges spèciâles normales.\n* <span class=\"mw-specialpagerestricted\">Pâges spèciâles rètrentes.</span>\n* <span class=\"mw-specialpagecached\">Pâges spèciâles solament en cache (porriant étre dèpassâs).</span>",
-       "specialpages-group-maintenance": "Rapôrts de mantegnence",
+       "specialpages-note-top": "Lègenda",
+       "specialpages-note": "* Pâges spèciâles normales.\n* <span class=\"mw-specialpagerestricted\">Pâges spèciâles rètrentes.</span>",
+       "specialpages-group-maintenance": "Rapôrts d’entretin",
        "specialpages-group-other": "Ôtres pâges spèciâles",
-       "specialpages-group-login": "Sè branchiér / fâre un compto",
-       "specialpages-group-changes": "Dèrriérs changements et jornals",
-       "specialpages-group-media": "Rapôrts et tèlèchargements de fichiérs mèdia",
-       "specialpages-group-users": "Usanciérs et drêts apondus",
+       "specialpages-group-login": "Sè branchiér / fâre un comptio",
+       "specialpages-group-changes": "Dèrriérs changements et jornâls",
+       "specialpages-group-media": "Rapôrts de mèdiâs et tèlèchargements",
+       "specialpages-group-users": "Utilisators et drêts",
        "specialpages-group-highuse": "Pâges d’usâjo important",
        "specialpages-group-pages": "Listes de pâges",
        "specialpages-group-pagetools": "Outils por les pâges",
-       "specialpages-group-wiki": "Balyês du vouiqui et outils",
-       "specialpages-group-redirects": "Pâges spèciâles redirigiês",
-       "specialpages-group-spam": "Outils anti-spame",
-       "specialpages-group-developer": "Outils u dèvelopor",
+       "specialpages-group-wiki": "Balyês et outils",
+       "specialpages-group-redirects": "Pâges spèciâles que redirijont",
+       "specialpages-group-spam": "Outils contre los mèssâjos cofos",
+       "specialpages-group-developer": "Outils u dèvelopior",
        "blankpage": "Pâge voueda",
-       "intentionallyblankpage": "Ceta pâge est lèssiê èxprès voueda.",
-       "external_image_whitelist": "  #Lèssiéd ceta legne justo d’ense.<pre>\n#Endicâd los bocons d’èxprèssions racionèles (solament la partia endicâ entre-mié los //) ce-desot.\n#Corrèspondront avouéc los lims hipèrtèxtos de les émâges (ben liyês) de defôr.\n#Celos que corrèspondont sè montreront coment des émâges, ôtrament solament un lim de vers l’émâge serat montrâ.\n#Les legnes que començont per un # seront considèrâs coment des comentèros.\n#Ceta lista est pas sensibla a la câssa.\n\n#Betâd tôs los bocons d’èxprèssions racionèles (*RegEx*) en-dessus de ceta legne. Lèssiéd ceta legne justo d’ense.</pre>",
-       "tags": "Balises des changements valides",
+       "intentionallyblankpage": "Cela pâge est lèssiêe volontèrament voueda.",
+       "external_image_whitelist": " #Lèssiéd ceta legne justo d’ense.<pre>\n#Endicâd los bocons d’èxprèssions racionâles (solament la partia endicâye entre-mié los //) ce-desot.\n#Corrèspondront avouéc les URLs de les émâges (ben liyêes) de defôr.\n#Celos que corrèspondont sè faront vêre coment d’émâges, ôtrament solament un lim de vers l’émâge serat montrâ.\n#Les legnes que començont per un # seront considèrâyes coment de comentèros.\n#O est pas sensiblo a la câssa.\n\n#Betâd tôs los bocons d’èxprèssions racionâles d’amont ceta legne. Lèssiéd cela legne justo d’ense.</pre>",
+       "tags": "Balises des changements justos",
        "tag-filter": "Filtrar les [[Special:Tags|balises]] :",
        "tag-filter-submit": "Filtrar",
        "tag-list-wrapper": "([[Special:Tags|Balis{{PLURAL:$1|a|es}}]] : $2)",
        "tags-title": "Balises",
-       "tags-intro": "Ceta pâge liste les balises que la programeria pôt utilisar por marcar un changement et lor significacion.",
+       "tags-intro": "Cela pâge liste les balises que la programeria pôt empleyér por marcar un changement et lor significacion.",
        "tags-tag": "Nom de la balisa",
-       "tags-display-header": "Aparence dens les listes de changements",
+       "tags-display-header": "Aparence dedens les listes de changements",
        "tags-description-header": "Dèscripcion complèta de la balisa",
+       "tags-source-header": "Sôrsa",
+       "tags-active-header": "Actif ?",
        "tags-hitcount-header": "Changements balisâs",
+       "tags-actions-header": "Accions",
+       "tags-active-yes": "Ouè",
+       "tags-active-no": "Nan",
+       "tags-source-extension": "Dèfenia per un’èxtension",
+       "tags-source-manual": "Aplicâye a la man per los utilisators et los robots",
+       "tags-source-none": "Dèpassâye",
        "tags-edit": "changiér",
+       "tags-delete": "suprimar",
+       "tags-activate": "activar",
+       "tags-deactivate": "dèsactivar",
        "tags-hitcount": "$1 changement{{PLURAL:$1||s}}",
+       "tags-manage-no-permission": "Vos éd pas la pèrmission de maneyér les balises de changement.",
+       "tags-manage-blocked": "Vos pouede pas maneyér les balises de changement quand vos éte blocâ{{GENDER:||ye}}.",
+       "tags-create-heading": "Fâre na balisa novèla",
+       "tags-create-explanation": "Per dèfôt, les balises novèles fêtes seront disponibles por los utilisators et los robots.",
+       "tags-create-tag-name": "Nom de la balisa :",
+       "tags-create-reason": "Rêson :",
+       "tags-create-submit": "Fâre",
+       "tags-create-no-name": "Vos dête spècifiar un nom de balisa.",
        "comparepages": "Comparar des pâges",
        "compare-page1": "Pâge 1",
        "compare-page2": "Pâge 2",
index eb1494d..c44f500 100644 (file)
        "api-error-nomodule": "Erro interno: Non hai ningún módulo de cargas.",
        "api-error-ok-but-empty": "Erro interno: Non hai resposta do servidor.",
        "api-error-overwrite": "Non está permitido sobrescribir un ficheiro existente.",
+       "api-error-ratelimited": "Está intentando subir máis ficheiros nun pequeno espazo de tempo do que permite este wiki.\nPor favor, inténteo de novo nuns minutos.",
        "api-error-stashfailed": "Erro interno: O servidor non puido almacenar o ficheiro temporal.",
        "api-error-publishfailed": "Erro interno: O servidor non puido publicar o ficheiro temporal.",
        "api-error-stasherror": "Houbo un erro ao subir o ficheiro ao depósito.",
index 5a86e1a..c9fd973 100644 (file)
        "api-error-nomodule": "שגיאה פנימית: מודול ההעלאה אינו מוגדר.",
        "api-error-ok-but-empty": "שגיאה פנימית: אין תשובה מהשרת.",
        "api-error-overwrite": "לא מותרת החלפת קובץ קיים.",
-       "api-error-ratelimited": "× ×\99ס×\99ת ×\9c×\94×¢×\9c×\95ת ×\91×\96×\9e×\9f ×§×¦×¨ ×\99×\95תר ×§×\91צ×\99×\9d ×\9e×\9b×\9e×\94 ×©×\94×\95×\95×\99ק×\99 ×\94×\96×\94 ×\9eרשה.\nנא לנסות שוב בעוד מספר דקות.",
+       "api-error-ratelimited": "× ×\99ס×\99ת ×\9c×\94×¢×\9c×\95ת ×\91×\96×\9e×\9f ×§×¦×¨ ×\99×\95תר ×§×\91צ×\99×\9d ×\9e×\94×\9b×\9e×\95ת ×\94×\9e×\99ר×\91×\99ת ×\94×\9e×\95תרת ×\91×\90תר ×\94×\95×\95×\99ק×\99 ×\94×\96ה.\nנא לנסות שוב בעוד מספר דקות.",
        "api-error-stashfailed": "שגיאה פנימית: השרת נכשל באחסון הקובץ הזמני.",
        "api-error-publishfailed": "שגיאה פנימית: השרת נכשל בפרסום הקובץ הזמני.",
        "api-error-stasherror": "הייתה שגיאה בהעלאת הקובץ לסליק.",
index 3d24b10..140f56a 100644 (file)
@@ -13,7 +13,7 @@
                        "Lucas"
                ]
        },
-       "tog-underline": "Souliyen lyen yo :",
+       "tog-underline": "Souliyen lyen yo&nbsp;:",
        "tog-hideminor": "Kache tout modifikasyon resan yo ki tou piti",
        "tog-hidepatrolled": "Kache modifikasyon yo ki fèk fèt pou moun ki ap veye yo",
        "tog-newpageshidepatrolled": "Kache paj ki siveye yo nan mitan lis nouvo paj yo",
@@ -28,7 +28,7 @@
        "tog-watchmoves": "Ajoute paj mwen deplase yo nan lis swivi mwen",
        "tog-watchdeletion": "Ajoute paj mwen efase yo nan lis swivi mwen",
        "tog-watchrollback": "Ajoute paj kote mwen ranvèse chanjman yo nan lis swivi mwen",
-       "tog-minordefault": "Make tout modifikasyon mwen yo \"tou piti\" pa defo",
+       "tog-minordefault": "Make tout modifikasyon kòm «&nbsp;tou piti&nbsp;» pa defo",
        "tog-previewontop": "Montre kout je anvan zòn modifikasyon",
        "tog-previewonfirst": "Montre kout je pou chak premye modifikasyon",
        "tog-enotifwatchlistpages": "Voye yon imèl ban mwen lè youn nan paj m ap swiv yo chanje",
        "versionrequired": "Vèsion $1 de MediaWiki nesesè",
        "versionrequiredtext": "Vèzion $1 de MediaWiki nesesè pou itilize paj sa. Wè [[Special:Version|version page]].",
        "ok": "OK",
-       "retrievedfrom": "Rekipere depi « $1 »",
+       "retrievedfrom": "Rekipere depi «&nbsp;$1&nbsp;»",
        "youhavenewmessages": "Ou genyen $1 ($2).",
        "youhavenewmessagesmanyusers": "Ou gen $2 de plizyè itilizatè $2.",
        "youhavenewmessagesmulti": "Ou genyen nouvo mesaj sou $1.",
        "viewsourceold": "Wè kòd paj la",
        "editlink": "modifye",
        "viewsourcelink": "wè kòd paj la",
-       "editsectionhint": "Modifye seksyon : $1",
+       "editsectionhint": "Modifye seksyon&nbsp;: $1",
        "toc": "Kontni yo",
        "showtoc": "montre",
        "hidetoc": "kache",
        "confirmable-yes": "Wi",
        "confirmable-no": "Non",
        "thisisdeleted": "Ou vle wè oubyen restore $1 ?",
-       "viewdeleted": "Wè $1 ?",
+       "viewdeleted": "Wè $1&nbsp;?",
        "restorelink": "{{PLURAL:$1|yon revizion efase|$1 revizion efase yo}}",
        "feedlinks": "Fil:",
        "feed-invalid": "Kalite fil sa envalid.",
index 6b2b6cf..dba5fd5 100644 (file)
@@ -24,6 +24,7 @@
        "tog-hideminor": "Guherandinên biçûk ji listêya guherandinên dawî veşêre",
        "tog-hidepatrolled": "Guherandinên hatine kontrolkirin ji nav guherandinên dawî veşêre",
        "tog-newpageshidepatrolled": "Rûpelên hatine kontrolkirin ji lîsteya rûpelên nû veşêre",
+       "tog-hidecategorization": "Kategorîzekirina rûpelan veşêre",
        "tog-extendwatchlist": "Lîsteya şopandinê berfireh bike ji bo dîtina hemû guherandinan, ne tenê yên nû",
        "tog-usenewrc": "Weşandina zêdetir (JavaScript pêwîst e)",
        "tog-numberheadings": "Sernavan otomatîk bihejmêre",
@@ -51,6 +52,7 @@
        "tog-watchlisthideminor": "Guhertinên biçûk ji lîsteya şopandinê veşêre",
        "tog-watchlisthideliu": "Guherandinên bikarhênerên qeydkirî ji lîsteya şopandinê veşêre",
        "tog-watchlisthideanons": "Guherandinên bikarhênerên neqeydkirî ji lîsteya şopandinê veşêre",
+       "tog-watchlisthidecategorization": "Kategorîzekirina rûpelan veşêre",
        "tog-ccmeonemails": "Kopiyên e-nameyên min ji bikarhênerên din re şandî, ji min re jî bişîne.",
        "tog-diffonly": "Li cem guhertinan, naveroka rûpelê nîşan nede",
        "tog-showhiddencats": "Kategoriyên veşartî bibîne",
        "editingold": "'''Hişyarî: Tu li ser guhertoyeke kevn a vê rûpelê dixebitî.\nHeke tu qeyd bikî, hemû guhertinên piştî vê revîzyonê winda dibin.\n'''",
        "yourdiff": "Cudahî",
        "copyrightwarning": "Hemû tevkariyên {{SITENAME}} di bin $2 de tên belav kirin (ji bo hûragahiyan li $1 binêre).\nEger tu nexwazî ku nivîsên te bê dilrehmî bên guherandin û li gora keyfa herkesî bên belavkirin, li vir neweşîne.<br />\nTu soz didî ku te ev bi xwe nivîsand an jî ji çavkaniyekê azad an geliyane (''public domain'') girt.\n'''Berhemên mafên wan parastî bê destûr neweşîne!'''",
-       "protectedpagewarning": "'''Hişyarî:  Ev rûpel tê parastin. Bi tenê bikarhênerên ku xwediyên mafên \"koordînatoriyê\" ne, dikarin vê rûpelê biguherînin.'''",
+       "protectedpagewarning": "<strong>Hişyarî: Ev rûpel tê parastin. Tenê bikarhênerên ku xwediyê mafên rêveberiyê ne, dikarin vê rûpelê biguherînin.</strong>",
        "semiprotectedpagewarning": "'''Hişyarî:''' Ev rûpel tê parastin, lewma tenê bikarhênerên tomarkirî dikarin vê biguherînin.\nGuhertina herî dawî bi referansa li jêr hatiye piştrastkirin:",
        "templatesused": "{{PLURAL:$1|Şablona|Şablonên}} ku li ser vê rûpelê {{PLURAL:$1|tê|tên}} bikaranîn:",
        "templatesusedpreview": "{{PLURAL:$1|Şablona|Şablonên}} di vê pêşdîtinê de {{PLURAL:$1|tê|tên}} bikaranîn:",
        "rcshowhidemine": "Guherandinên min $1",
        "rcshowhidemine-show": "nîşan bide",
        "rcshowhidemine-hide": "veşêre",
+       "rcshowhidecategorization": "Kategorîzekirina rûpelan $1",
        "rcshowhidecategorization-show": "Nîşan bide",
        "rcshowhidecategorization-hide": "Veşêre",
        "rclinks": "$1 guherandinên di $2 rojên dawî de nîşan bide<br />$3",
index ced7c58..27d023b 100644 (file)
@@ -5,7 +5,8 @@
                        "Hosseinblue",
                        "Lakzon",
                        "Mjbmr",
-                       "Macofe"
+                       "Macofe",
+                       "Huji"
                ]
        },
        "tog-underline": "کڕ(خط)کیشائن ژێر پیوندەل:",
        "exbeforeblank": "محتوای صفحه قبل از خالی‌کردن این بود: «$1»",
        "delete-confirm": "حذف «$1»",
        "delete-legend": "حۀذف کردن/پاک کردن",
-       "historywarning": "<strong>هشدار:</strong> صفحه‌ای که در حال پاک‌کردن آن هستید دارای یک تاریخچه همراه $1 {{PLURAL:$1|بازبینی|بازبینی‌ها}} است:",
+       "historywarning": "<strong>هشدار:</strong> صفحه‌ای که در حال پاک‌کردن آن هستید دارای یک تاریخچه همراه $1 {{PLURAL:$1|بازبینی|بازبینی}} است:",
        "historyaction-submit": "نیشان دائن",
        "confirmdeletetext": "شما در حال حذف کردن یک صفحه یا تصویر از پایگاه داده‌ها همراه با تمام تاریخچهٔ آن هستید.\nلطفاً این عمل را تأیید کنید و اطمینان حاصل کنید که عواقب این کار را می‌دانید و این عمل را مطابق با [[{{MediaWiki:Policy-url}}|سیاست‌ها]] انجام می‌دهید.",
        "actioncomplete": "عملكرد كامل بيه",
index 40f07fd..37e4a51 100644 (file)
        "nstab-template": "Vörlaag",
        "nstab-help": "Hülp",
        "nstab-category": "Kategorie",
+       "mainpage-nstab": "Hööftsiet",
        "nosuchaction": "Disse Aktschoon gifft dat nich",
        "nosuchactiontext": "De in de URL angeven Akschoon warrt nich ünnerstütt.\nVillicht hest du in de URL en Tippfehler oder büst en verkehrten Lenk nagahn.\nDat kann aver ok op en Bug in de Software henwiesen, de op {{SITENAME}} bruukt warrt.",
        "nosuchspecialpage": "Disse Spezialsiet gifft dat nich",
index a3a56a5..ed5c2b4 100644 (file)
        "feedback-useragent": "A label denoting the user agent in the feedback that is posted to the feedback page.\n{{Identical|User agent}}",
        "searchsuggest-search": "Greyed out default text in the simple search box in the Vector skin. (It disappears and lets the user enter the requested search terms when the search box receives focus.)\n\n{{Identical|Search}}",
        "searchsuggest-containing": "Label used in the special item of the search suggestions list which gives the user an option to perform a full text search for the term.",
+       "api-error-autoblocked": "API error message that can be used for client side localisation of API errors.",
        "api-error-badaccess-groups": "API error message that can be used for client side localisation of API errors.",
        "api-error-badtoken": "API error message that can be used for client side localisation of API errors.",
+       "api-error-blocked": "API error message that can be used for client side localisation of API errors.",
        "api-error-copyuploaddisabled": "API error message that can be used for client side localisation of API errors.",
        "api-error-duplicate": "API error message that can be used for client side localisation of API errors. Parameters:\n* $1 - a number of files",
        "api-error-duplicate-archive": "API error message that can be used for client side localisation of API errors. Parameters:\n* $1 - a number of files",
        "api-error-nomodule": "API error message that can be used for client side localisation of API errors.",
        "api-error-ok-but-empty": "API error message that can be used for client side localisation of API errors.",
        "api-error-overwrite": "API error message that can be used for client side localisation of API errors.",
-       "api-error-ratelimited": "API error message that can be used for client side localisation of API errors.",
+       "api-error-ratelimited": "API error message that can be used for client side localisation of API errors.\n\nCf. {{msg-mw|Actionthrottledtext}}",
        "api-error-stashfailed": "API error message that can be used for client side localisation of API errors.",
        "api-error-publishfailed": "API error message that can be used for client side localisation of API errors.",
        "api-error-stasherror": "API error message that can be used for client side localisation of API errors.",
index 6bd7f31..c9c9069 100644 (file)
        "restriction-level-autoconfirmed": "अर्धसंरक्षितम्",
        "restriction-level-all": "कोऽपि स्तरः ।",
        "undelete": "अपमर्जितपुटानि अवलोकयतु ।",
-       "undeletepage": "à¤\85पमरà¥\8dà¤\9cितपà¥\81à¤\9fानि à¤¦à¥\83षà¥\8dà¤\9fà¥\8dवा à¤ªà¥\81नसà¥\8dथापयतà¥\81 à¥¤",
+       "undeletepage": "à¤\85पमरà¥\8dà¤\9cितपà¥\81षà¥\8dठानि à¤¦à¥\83षà¥\8dà¤\9fà¥\8dवा à¤ªà¥\81नसà¥\8dथापयतà¥\81",
        "undeletepagetitle": "'''अधः [[:$1|$1]] इत्येतेषाम् अपनीतावृत्तीनां दर्शनं भवति ।",
        "viewdeletedpage": "अपमर्जितपुटानि अवलोकयतु ।",
        "undeletepagetext": "{{PLURAL:$1|$1 पृष्ठं|$1 पृष्ठानि}} इत्येतानि अपनीतानि किन्तु एतानि लेखागारे सन्ति अपि च पुनस्थापितानि कर्तुं शक्यते ।",
index a0c8837..ae2588b 100644 (file)
        "userpage-userdoesnotexist": "Користувач під назвою \"<nowiki>$1</nowiki>\" не зареєстрований. Переконайтеся, що ви хочете створити/редагувати цю сторінку.",
        "userpage-userdoesnotexist-view": "Обліковий запис користувача „$1“ не зареєстровано.",
        "blocked-notice-logextract": "Цей користувач наразі заблокований.\nОстанній запис у журналі блокувань такий:",
-       "clearyourcache": "'''Увага:''' Після збереження слід очистити кеш оглядача, щоб побачити зміни.\n* '''Firefox / Safari:''' тримайте ''Shift'', коли натискаєте ''Оновити'', або натисніть ''Ctrl-F5'' чи ''Ctrl-Shift-R'' (''⌘-R'' на Apple Mac)\n* '''Google Chrome:''' натисніть ''Ctrl-Shift-R'' (''⌘-Shift-R'' на Apple Mac)\n* '''Internet Explorer:''' тримайте ''Ctrl'', коли натискаєте ''Оновити'', або натисніть ''Ctrl-F5''\n* '''Opera:''' очистіть кеш за допомогою ''Інструменти → Налаштування''",
+       "clearyourcache": "<strong>Увага:</strong> Після збереження слід очистити кеш оглядача, щоб побачити зміни.\n* <strong>Firefox / Safari:</strong> тримайте <em>Shift</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em> чи <em>Ctrl-Shift-R</em> (<em>⌘-R</em> на Apple Mac)\n* <strong>Google Chrome:</strong> натисніть <em>Ctrl-Shift-R</em> (<em>⌘-Shift-R</em> на Apple Mac)\n* <strong>Internet Explorer:</strong> тримайте <em>Ctrl</em>, коли натискаєте <em>Оновити</em>, або натисніть <em>Ctrl-F5</em>\n* <strong>Opera:</strong> очистіть кеш за допомогою <em>Інструменти → Налаштування</em> (<em>Opera → Побажання</em> на Apple Mac) та перейдіть на <em>Привітність & безпека → очистити дані браузера → кеш</em>",
        "usercssyoucanpreview": "'''Підказка:''' використовуйте кнопку «{{int:showpreview}}», щоб протестувати ваш новий css-файл перед збереженням.",
        "userjsyoucanpreview": "'''Підказка:''' використовуйте кнопку «{{int:showpreview}}», щоб протестувати ваш новий код JavaScript перед збереженням.",
        "usercsspreview": "'''Пам'ятайте, що це лише попередній перегляд вашого css-файлу.'''\n'''Його ще не збережено!'''",
        "tooltip-ca-nstab-category": "Сторінка категорії",
        "tooltip-minoredit": "Позначити це редагування як незначне",
        "tooltip-save": "Зберегти ваші зміни",
+       "tooltip-publish": "Опублікувати ваші зміни",
        "tooltip-preview": "Попередній перегляд сторінки, будь ласка, використовуйте перед збереженням!",
        "tooltip-diff": "Показати зміни, що зроблені відносно початкового тексту.",
        "tooltip-compareselectedversions": "Переглянути різницю між двома вказаними версіями цієї сторінки.",
        "api-error-nomodule": "Внутрішня помилка: Відсутній модуль завантажень.",
        "api-error-ok-but-empty": "Внутрішня помилка: сервер не відповідає.",
        "api-error-overwrite": "Заміну існуючого файлу не дозволено.",
+       "api-error-ratelimited": "Ви намагаєтесь завантажити більше файлів за короткий проміжок часу, ніж дозволено у цій вікі. Будь ласка, спробуйте за декілька хвилин.",
        "api-error-stashfailed": "Внутрішня помилка: сервер не зміг зберегти тимчасовий файл.",
        "api-error-publishfailed": "Внутрішня помилка: сервер не зміг опублікувати тимчасовий файл.",
        "api-error-stasherror": "Сталася помилка при завантаженні файлу у сховище.",
old mode 100644 (file)
new mode 100755 (executable)
index 8993146..f9dd58c
@@ -216,7 +216,7 @@ class ConvertExtensionToRegistration extends Maintenance {
        }
 
        public function handleHooks( $realName, $value ) {
-               foreach ( $value as $hookName => $handlers ) {
+               foreach ( $value as $hookName => &$handlers ) {
                        foreach ( $handlers as $func ) {
                                if ( $func instanceof Closure ) {
                                        $this->error( "Error: Closures cannot be converted to JSON. " .
@@ -230,6 +230,9 @@ class ConvertExtensionToRegistration extends Maintenance {
                                        );
                                }
                        }
+                       if ( count( $handlers ) === 1 ) {
+                               $handlers = $handlers[0];
+                       }
                }
                $this->json[$realName] = $value;
        }
diff --git a/maintenance/mssql/archives/named_constraints.sql b/maintenance/mssql/archives/named_constraints.sql
deleted file mode 100644 (file)
index 94b77ea..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-DECLARE @fullyQualifiedTableName nvarchar(max),
-@tableName sysname,
-@fieldName sysname,
-@constr sysname,
-@constrNew sysname,
-@sqlcmd nvarchar(max),
-@sqlcreate nvarchar(max)
-
-SET @fullyQualifiedTableName = '/*_*//*$tableName*/'
-SET @tableName = '/*$tableName*/'
-SET @fieldName = '/*$fieldName*/'
-
-SELECT @constr = CONSTRAINT_NAME
-FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
-WHERE TABLE_NAME = @tableName
-AND CONSTRAINT_CATALOG = '/*$wgDBname*/'
-AND CONSTRAINT_SCHEMA = '/*$wgDBmwschema*/'
-AND CONSTRAINT_TYPE = 'CHECK'
-AND CONSTRAINT_NAME LIKE ('CK__' + left(@tableName,9) + '__' + left(@fieldName,5) + '%')
-
-SELECT @constrNew = CONSTRAINT_NAME
-FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
-WHERE TABLE_NAME = @tableName
-AND CONSTRAINT_CATALOG = '/*$wgDBname*/'
-AND CONSTRAINT_SCHEMA = '/*$wgDBmwschema*/'
-AND CONSTRAINT_TYPE = 'CHECK'
-AND CONSTRAINT_NAME = (@fieldName + '_ckc')
-
-IF @constr IS NOT NULL
-BEGIN
-  SET @sqlcmd =  'ALTER TABLE ' + @fullyQualifiedTableName + ' DROP CONSTRAINT [' + @constr + ']'
-  EXECUTE sp_executesql @sqlcmd
-END
-IF @constrNew IS NULL
-BEGIN
-  SET @sqlcreate =  'ALTER TABLE ' + @fullyQualifiedTableName + ' WITH NOCHECK ADD CONSTRAINT ' + @fieldName + '_ckc CHECK /*$checkConstraint*/;'
-  EXECUTE sp_executesql @sqlcreate
-END
\ No newline at end of file
diff --git a/maintenance/mssql/archives/patch-add-cl_collation_ext_index.sql b/maintenance/mssql/archives/patch-add-cl_collation_ext_index.sql
new file mode 100644 (file)
index 0000000..8137dc6
--- /dev/null
@@ -0,0 +1,2 @@
+-- @since 1.27
+CREATE INDEX /*i*/cl_collation_ext ON /*_*/categorylinks (cl_collation, cl_to, cl_type, cl_from);
diff --git a/maintenance/mssql/archives/patch-bot_passwords.sql b/maintenance/mssql/archives/patch-bot_passwords.sql
new file mode 100644 (file)
index 0000000..7718ffa
--- /dev/null
@@ -0,0 +1,13 @@
+--
+-- This table contains a user's bot passwords: passwords that allow access to
+-- the account via the API with limited rights.
+--
+CREATE TABLE /*_*/bot_passwords (
+       bp_user int NOT NULL REFERENCES /*_*/mwuser(user_id) ON DELETE CASCADE,
+       bp_app_id nvarchar(32) NOT NULL,
+       bp_password nvarchar(255) NOT NULL,
+       bp_token nvarchar(255) NOT NULL,
+       bp_restrictions nvarchar(max) NOT NULL,
+       bp_grants nvarchar(max) NOT NULL,
+       PRIMARY KEY (bp_user, bp_app_id)
+);
diff --git a/maintenance/mssql/archives/patch-categorylinks-constraints.sql b/maintenance/mssql/archives/patch-categorylinks-constraints.sql
new file mode 100644 (file)
index 0000000..cf9b565
--- /dev/null
@@ -0,0 +1,20 @@
+DECLARE @baseSQL nvarchar(max),
+       @SQL nvarchar(max),
+       @id sysname;--
+
+SET @baseSQL = 'ALTER TABLE /*_*/categorylinks DROP CONSTRAINT ';--
+
+SELECT @id = cc.name
+FROM sys.check_constraints cc
+JOIN sys.columns c
+       ON c.object_id = cc.parent_object_id
+       AND c.column_id = cc.parent_column_id
+WHERE
+       cc.parent_object_id = OBJECT_ID('/*_*/categorylinks')
+       AND c.name = 'cl_type';--
+
+SET @SQL = @baseSQL + @id;--
+
+EXEC sp_executesql @SQL;--
+
+ALTER TABLE /*_*/categorylinks ADD CONSTRAINT cl_type_ckc CHECK (cl_type IN('page', 'subcat', 'file'));
diff --git a/maintenance/mssql/archives/patch-drop-page_counter.sql b/maintenance/mssql/archives/patch-drop-page_counter.sql
new file mode 100644 (file)
index 0000000..54ab9f7
--- /dev/null
@@ -0,0 +1,19 @@
+DECLARE @sql nvarchar(max),
+       @id sysname;--
+
+SET @sql = 'ALTER TABLE /*_*/page DROP CONSTRAINT ';--
+
+SELECT @id = df.name
+FROM sys.default_constraints df
+JOIN sys.columns c
+       ON c.object_id = df.parent_object_id
+       AND c.column_id = df.parent_column_id
+WHERE
+       df.parent_object_id = OBJECT_ID('/*_*/page')
+       AND c.name = 'page_counter';--
+
+SET @sql = @sql + @id;--
+
+EXEC sp_executesql @sql;--
+
+ALTER TABLE /*_*/page DROP COLUMN page_counter;
diff --git a/maintenance/mssql/archives/patch-drop-rc_cur_time.sql b/maintenance/mssql/archives/patch-drop-rc_cur_time.sql
new file mode 100644 (file)
index 0000000..01c46d3
--- /dev/null
@@ -0,0 +1,19 @@
+DECLARE @sql nvarchar(max),
+       @id sysname;--
+
+SET @sql = 'ALTER TABLE /*_*/recentchanges DROP CONSTRAINT ';--
+
+SELECT @id = df.name
+FROM sys.default_constraints df
+JOIN sys.columns c
+       ON c.object_id = df.parent_object_id
+       AND c.column_id = df.parent_column_id
+WHERE
+       df.parent_object_id = OBJECT_ID('/*_*/recentchanges')
+       AND c.name = 'rc_cur_time';--
+
+SET @sql = @sql + @id;--
+
+EXEC sp_executesql @sql;--
+
+ALTER TABLE /*_*/recentchanges DROP COLUMN rc_cur_time;
diff --git a/maintenance/mssql/archives/patch-drop-ss_total_views.sql b/maintenance/mssql/archives/patch-drop-ss_total_views.sql
new file mode 100644 (file)
index 0000000..7525ed5
--- /dev/null
@@ -0,0 +1,19 @@
+DECLARE @sql nvarchar(max),
+       @id sysname;--
+
+SET @sql = 'ALTER TABLE /*_*/site_stats DROP CONSTRAINT ';--
+
+SELECT @id = df.name
+FROM sys.default_constraints df
+JOIN sys.columns c
+       ON c.object_id = df.parent_object_id
+       AND c.column_id = df.parent_column_id
+WHERE
+       df.parent_object_id = OBJECT_ID('/*_*/site_stats')
+       AND c.name = 'ss_total_views';--
+
+SET @sql = @sql + @id;--
+
+EXEC sp_executesql @sql;--
+
+ALTER TABLE /*_*/site_stats DROP COLUMN ss_total_views;
diff --git a/maintenance/mssql/archives/patch-drop-user_options.sql b/maintenance/mssql/archives/patch-drop-user_options.sql
new file mode 100644 (file)
index 0000000..ab37956
--- /dev/null
@@ -0,0 +1,19 @@
+DECLARE @sql nvarchar(max),
+       @id sysname;--
+
+SET @sql = 'ALTER TABLE /*_*/mwuser DROP CONSTRAINT ';--
+
+SELECT @id = df.name
+FROM sys.default_constraints df
+JOIN sys.columns c
+       ON c.object_id = df.parent_object_id
+       AND c.column_id = df.parent_column_id
+WHERE
+       df.parent_object_id = OBJECT_ID('/*_*/mwuser')
+       AND c.name = 'user_options';--
+
+SET @sql = @sql + @id;--
+
+EXEC sp_executesql @sql;--
+
+ALTER TABLE /*_*/mwuser DROP COLUMN user_options;
diff --git a/maintenance/mssql/archives/patch-filearchive-constraints.sql b/maintenance/mssql/archives/patch-filearchive-constraints.sql
new file mode 100644 (file)
index 0000000..cefead5
--- /dev/null
@@ -0,0 +1,34 @@
+DECLARE @baseSQL nvarchar(max),
+       @SQL nvarchar(max),
+       @id sysname;--
+
+SET @baseSQL = 'ALTER TABLE /*_*/filearchive DROP CONSTRAINT ';--
+
+SELECT @id = cc.name
+FROM sys.check_constraints cc
+JOIN sys.columns c
+       ON c.object_id = cc.parent_object_id
+       AND c.column_id = cc.parent_column_id
+WHERE
+       cc.parent_object_id = OBJECT_ID('/*_*/filearchive')
+       AND c.name = 'fa_major_mime';--
+
+SET @SQL = @baseSQL + @id;--
+
+EXEC sp_executesql @SQL;--
+
+SELECT @id = cc.name
+FROM sys.check_constraints cc
+JOIN sys.columns c
+       ON c.object_id = cc.parent_object_id
+       AND c.column_id = cc.parent_column_id
+WHERE
+       cc.parent_object_id = OBJECT_ID('/*_*/filearchive')
+       AND c.name = 'fa_media_type';--
+
+SET @SQL = @baseSQL + @id;--
+
+EXEC sp_executesql @SQL;--
+
+ALTER TABLE /*_*/filearchive ADD CONSTRAINT fa_major_mime_ckc check (fa_major_mime IN('unknown', 'application', 'audio', 'image', 'text', 'video', 'message', 'model', 'multipart'));--
+ALTER TABLE /*_*/filearchive ADD CONSTRAINT fa_media_type_ckc check (fa_media_type in('UNKNOWN', 'BITMAP', 'DRAWING', 'AUDIO', 'VIDEO', 'MULTIMEDIA', 'OFFICE', 'TEXT', 'EXECUTABLE', 'ARCHIVE'));
diff --git a/maintenance/mssql/archives/patch-filearchive-schema.sql b/maintenance/mssql/archives/patch-filearchive-schema.sql
new file mode 100644 (file)
index 0000000..cf1c01f
--- /dev/null
@@ -0,0 +1,120 @@
+-- MediaWiki looks for lines ending with semicolons and sends them as separate queries
+-- However here we *really* need this all to be sent as a single batch. As such, DO NOT
+-- remove the -- from the end of each statement.
+
+DECLARE @temp table (
+       fa_id int,
+       fa_name nvarchar(255),
+       fa_archive_name nvarchar(255),
+       fa_storage_group nvarchar(16),
+       fa_storage_key nvarchar(64),
+       fa_deleted_user int,
+       fa_deleted_timestamp varchar(14),
+       fa_deleted_reason nvarchar(max),
+       fa_size int,
+       fa_width int,
+       fa_height int,
+       fa_metadata nvarchar(max),
+       fa_bits int,
+       fa_media_type varchar(16),
+       fa_major_mime varchar(16),
+       fa_minor_mime nvarchar(100),
+       fa_description nvarchar(255),
+       fa_user int,
+       fa_user_text nvarchar(255),
+       fa_timestamp varchar(14),
+       fa_deleted tinyint,
+       fa_sha1 nvarchar(32)
+);--
+
+INSERT INTO @temp
+SELECT * FROM /*_*/filearchive;--
+
+DROP TABLE /*_*/filearchive;--
+
+CREATE TABLE /*_*/filearchive (
+  fa_id int NOT NULL PRIMARY KEY IDENTITY,
+  fa_name nvarchar(255) NOT NULL default '',
+  fa_archive_name nvarchar(255) default '',
+  fa_storage_group nvarchar(16),
+  fa_storage_key nvarchar(64) default '',
+  fa_deleted_user int,
+  fa_deleted_timestamp varchar(14) default '',
+  fa_deleted_reason nvarchar(max),
+  fa_size int default 0,
+  fa_width int default 0,
+  fa_height int default 0,
+  fa_metadata varbinary(max),
+  fa_bits int default 0,
+  fa_media_type varchar(16) default null,
+  fa_major_mime varchar(16) not null default 'unknown',
+  fa_minor_mime nvarchar(100) default 'unknown',
+  fa_description nvarchar(255),
+  fa_user int default 0 REFERENCES /*_*/mwuser(user_id) ON DELETE SET NULL,
+  fa_user_text nvarchar(255),
+  fa_timestamp varchar(14) default '',
+  fa_deleted tinyint NOT NULL default 0,
+  fa_sha1 nvarchar(32) NOT NULL default '',
+  CONSTRAINT fa_major_mime_ckc check (fa_major_mime in('unknown', 'application', 'audio', 'image', 'text', 'video', 'message', 'model', 'multipart', 'chemical')),
+  CONSTRAINT fa_media_type_ckc check (fa_media_type in('UNKNOWN', 'BITMAP', 'DRAWING', 'AUDIO', 'VIDEO', 'MULTIMEDIA', 'OFFICE', 'TEXT', 'EXECUTABLE', 'ARCHIVE'))
+);--
+
+CREATE INDEX /*i*/fa_name ON /*_*/filearchive (fa_name, fa_timestamp);--
+CREATE INDEX /*i*/fa_storage_group ON /*_*/filearchive (fa_storage_group, fa_storage_key);--
+CREATE INDEX /*i*/fa_deleted_timestamp ON /*_*/filearchive (fa_deleted_timestamp);--
+CREATE INDEX /*i*/fa_user_timestamp ON /*_*/filearchive (fa_user_text,fa_timestamp);--
+CREATE INDEX /*i*/fa_sha1 ON /*_*/filearchive (fa_sha1);--
+
+SET IDENTITY_INSERT /*_*/filearchive ON;--
+
+INSERT INTO /*_*/filearchive
+(
+       fa_id,
+       fa_name,
+       fa_archive_name,
+       fa_storage_group,
+       fa_storage_key,
+       fa_deleted_user,
+       fa_deleted_timestamp,
+       fa_deleted_reason,
+       fa_size,
+       fa_width,
+       fa_height,
+       fa_metadata,
+       fa_bits,
+       fa_media_type,
+       fa_major_mime,
+       fa_minor_mime,
+       fa_description,
+       fa_user,
+       fa_user_text,
+       fa_timestamp,
+       fa_deleted,
+       fa_sha1
+)
+SELECT
+       fa_id,
+       fa_name,
+       fa_archive_name,
+       fa_storage_group,
+       fa_storage_key,
+       fa_deleted_user,
+       fa_deleted_timestamp,
+       fa_deleted_reason,
+       fa_size,
+       fa_width,
+       fa_height,
+       CONVERT(varbinary(max), fa_metadata, 0),
+       fa_bits,
+       fa_media_type,
+       fa_major_mime,
+       fa_minor_mime,
+       fa_description,
+       fa_user,
+       fa_user_text,
+       fa_timestamp,
+       fa_deleted,
+       fa_sha1
+FROM @temp t;--
+
+SET IDENTITY_INSERT /*_*/filearchive OFF;
diff --git a/maintenance/mssql/archives/patch-il_from_namespace.sql b/maintenance/mssql/archives/patch-il_from_namespace.sql
new file mode 100644 (file)
index 0000000..e4ac98f
--- /dev/null
@@ -0,0 +1,4 @@
+ALTER TABLE /*_*/imagelinks
+  ADD il_from_namespace int NOT NULL default 0;
+
+CREATE INDEX /*i*/il_backlinks_namespace ON /*_*/imagelinks (il_from_namespace,il_to,il_from);
\ No newline at end of file
diff --git a/maintenance/mssql/archives/patch-image-constraints.sql b/maintenance/mssql/archives/patch-image-constraints.sql
new file mode 100644 (file)
index 0000000..0aeb627
--- /dev/null
@@ -0,0 +1,34 @@
+DECLARE @baseSQL nvarchar(max),
+       @SQL nvarchar(max),
+       @id sysname;--
+
+SET @baseSQL = 'ALTER TABLE /*_*/image DROP CONSTRAINT ';--
+
+SELECT @id = cc.name
+FROM sys.check_constraints cc
+JOIN sys.columns c
+       ON c.object_id = cc.parent_object_id
+       AND c.column_id = cc.parent_column_id
+WHERE
+       cc.parent_object_id = OBJECT_ID('/*_*/image')
+       AND c.name = 'img_major_mime';--
+
+SET @SQL = @baseSQL + @id;--
+
+EXEC sp_executesql @SQL;--
+
+SELECT @id = cc.name
+FROM sys.check_constraints cc
+JOIN sys.columns c
+       ON c.object_id = cc.parent_object_id
+       AND c.column_id = cc.parent_column_id
+WHERE
+       cc.parent_object_id = OBJECT_ID('/*_*/image')
+       AND c.name = 'img_media_type';--
+
+SET @SQL = @baseSQL + @id;--
+
+EXEC sp_executesql @SQL;--
+
+ALTER TABLE /*_*/image ADD CONSTRAINT img_major_mime_ckc check (img_major_mime IN('unknown', 'application', 'audio', 'image', 'text', 'video', 'message', 'model', 'multipart'));--
+ALTER TABLE /*_*/image ADD CONSTRAINT img_media_type_ckc check (img_media_type in('UNKNOWN', 'BITMAP', 'DRAWING', 'AUDIO', 'VIDEO', 'MULTIMEDIA', 'OFFICE', 'TEXT', 'EXECUTABLE', 'ARCHIVE'));
diff --git a/maintenance/mssql/archives/patch-image-schema.sql b/maintenance/mssql/archives/patch-image-schema.sql
new file mode 100644 (file)
index 0000000..213b438
--- /dev/null
@@ -0,0 +1,84 @@
+-- MediaWiki looks for lines ending with semicolons and sends them as separate queries
+-- However here we *really* need this all to be sent as a single batch. As such, DO NOT
+-- remove the -- from the end of each statement.
+
+DECLARE @temp table (
+       img_name varbinary(255),
+       img_size int,
+       img_width int,
+       img_height int,
+       img_metadata varbinary(max),
+       img_bits int,
+       img_media_type varchar(16),
+       img_major_mime varchar(16),
+       img_minor_mime nvarchar(100),
+       img_description nvarchar(255),
+       img_user int,
+       img_user_text nvarchar(255),
+       img_timestamp nvarchar(14),
+       img_sha1 nvarchar(32)
+);--
+
+INSERT INTO @temp
+SELECT * FROM /*_*/image;--
+
+DROP TABLE /*_*/image;--
+
+CREATE TABLE /*_*/image (
+  img_name nvarchar(255) NOT NULL default '' PRIMARY KEY,
+  img_size int NOT NULL default 0,
+  img_width int NOT NULL default 0,
+  img_height int NOT NULL default 0,
+  img_metadata varbinary(max) NOT NULL,
+  img_bits int NOT NULL default 0,
+  img_media_type varchar(16) default null,
+  img_major_mime varchar(16) not null default 'unknown',
+  img_minor_mime nvarchar(100) NOT NULL default 'unknown',
+  img_description nvarchar(255) NOT NULL,
+  img_user int REFERENCES /*_*/mwuser(user_id) ON DELETE SET NULL,
+  img_user_text nvarchar(255) NOT NULL,
+  img_timestamp nvarchar(14) NOT NULL default '',
+  img_sha1 nvarchar(32) NOT NULL default '',
+  CONSTRAINT img_major_mime_ckc check (img_major_mime IN('unknown', 'application', 'audio', 'image', 'text', 'video', 'message', 'model', 'multipart', 'chemical')),
+  CONSTRAINT img_media_type_ckc check (img_media_type in('UNKNOWN', 'BITMAP', 'DRAWING', 'AUDIO', 'VIDEO', 'MULTIMEDIA', 'OFFICE', 'TEXT', 'EXECUTABLE', 'ARCHIVE'))
+);--
+
+CREATE INDEX /*i*/img_usertext_timestamp ON /*_*/image (img_user_text,img_timestamp);--
+CREATE INDEX /*i*/img_size ON /*_*/image (img_size);--
+CREATE INDEX /*i*/img_timestamp ON /*_*/image (img_timestamp);--
+CREATE INDEX /*i*/img_sha1 ON /*_*/image (img_sha1);--
+CREATE INDEX /*i*/img_media_mime ON /*_*/image (img_media_type,img_major_mime,img_minor_mime);--
+
+INSERT INTO /*_*/image
+(
+       img_name,
+       img_size,
+       img_width,
+       img_height,
+       img_metadata,
+       img_bits,
+       img_media_type,
+       img_major_mime,
+       img_minor_mime,
+       img_description,
+       img_user,
+       img_user_text,
+       img_timestamp,
+       img_sha1
+)
+SELECT
+       img_name,
+       img_size,
+       img_width,
+       img_height,
+       img_metadata,
+       img_bits,
+       img_media_type,
+       img_major_mime,
+       img_minor_mime,
+       img_description,
+       img_user,
+       img_user_text,
+       img_timestamp,
+       img_sha1
+FROM @temp t;
diff --git a/maintenance/mssql/archives/patch-kill-cl_collation_index.sql b/maintenance/mssql/archives/patch-kill-cl_collation_index.sql
new file mode 100644 (file)
index 0000000..7f75a62
--- /dev/null
@@ -0,0 +1,7 @@
+--
+-- Kill cl_collation index.
+-- @since 1.27
+--
+
+DROP INDEX /*i*/cl_collation ON /*_*/categorylinks;
+
diff --git a/maintenance/mssql/archives/patch-oldimage-constraints.sql b/maintenance/mssql/archives/patch-oldimage-constraints.sql
new file mode 100644 (file)
index 0000000..69ede2c
--- /dev/null
@@ -0,0 +1,34 @@
+DECLARE @baseSQL nvarchar(max),
+       @SQL nvarchar(max),
+       @id sysname;--
+
+SET @baseSQL = 'ALTER TABLE /*_*/oldimage DROP CONSTRAINT ';--
+
+SELECT @id = cc.name
+FROM sys.check_constraints cc
+JOIN sys.columns c
+       ON c.object_id = cc.parent_object_id
+       AND c.column_id = cc.parent_column_id
+WHERE
+       cc.parent_object_id = OBJECT_ID('/*_*/oldimage')
+       AND c.name = 'oi_major_mime';--
+
+SET @SQL = @baseSQL + @id;--
+
+EXEC sp_executesql @SQL;--
+
+SELECT @id = cc.name
+FROM sys.check_constraints cc
+JOIN sys.columns c
+       ON c.object_id = cc.parent_object_id
+       AND c.column_id = cc.parent_column_id
+WHERE
+       cc.parent_object_id = OBJECT_ID('/*_*/oldimage')
+       AND c.name = 'oi_media_type';--
+
+SET @SQL = @baseSQL + @id;--
+
+EXEC sp_executesql @SQL;--
+
+ALTER TABLE /*_*/oldimage ADD CONSTRAINT oi_major_mime_ckc check (oi_major_mime IN('unknown', 'application', 'audio', 'image', 'text', 'video', 'message', 'model', 'multipart'));--
+ALTER TABLE /*_*/oldimage ADD CONSTRAINT oi_media_type_ckc check (oi_media_type in('UNKNOWN', 'BITMAP', 'DRAWING', 'AUDIO', 'VIDEO', 'MULTIMEDIA', 'OFFICE', 'TEXT', 'EXECUTABLE', 'ARCHIVE'));
diff --git a/maintenance/mssql/archives/patch-oldimage-schema.sql b/maintenance/mssql/archives/patch-oldimage-schema.sql
new file mode 100644 (file)
index 0000000..3391c1b
--- /dev/null
@@ -0,0 +1,91 @@
+-- MediaWiki looks for lines ending with semicolons and sends them as separate queries
+-- However here we *really* need this all to be sent as a single batch. As such, DO NOT
+-- remove the -- from the end of each statement.
+
+DECLARE @temp table (
+       oi_name varbinary(255),
+       oi_archive_name varbinary(255),
+       oi_size int,
+       oi_width int,
+       oi_height int,
+       oi_bits int,
+       oi_description nvarchar(255),
+       oi_user int,
+       oi_user_text nvarchar(255),
+       oi_timestamp varchar(14),
+       oi_metadata nvarchar(max),
+       oi_media_type varchar(16),
+       oi_major_mime varchar(16),
+       oi_minor_mime nvarchar(100),
+       oi_deleted tinyint,
+       oi_sha1 nvarchar(32)
+);--
+
+INSERT INTO @temp
+SELECT * FROM /*_*/oldimage;--
+
+DROP TABLE /*_*/oldimage;--
+
+CREATE TABLE /*_*/oldimage (
+  oi_name nvarchar(255) NOT NULL default '',
+  oi_archive_name nvarchar(255) NOT NULL default '',
+  oi_size int NOT NULL default 0,
+  oi_width int NOT NULL default 0,
+  oi_height int NOT NULL default 0,
+  oi_bits int NOT NULL default 0,
+  oi_description nvarchar(255) NOT NULL,
+  oi_user int REFERENCES /*_*/mwuser(user_id),
+  oi_user_text nvarchar(255) NOT NULL,
+  oi_timestamp varchar(14) NOT NULL default '',
+  oi_metadata varbinary(max) NOT NULL,
+  oi_media_type varchar(16) default null,
+  oi_major_mime varchar(16) not null default 'unknown',
+  oi_minor_mime nvarchar(100) NOT NULL default 'unknown',
+  oi_deleted tinyint NOT NULL default 0,
+  oi_sha1 nvarchar(32) NOT NULL default '',
+  CONSTRAINT oi_major_mime_ckc check (oi_major_mime IN('unknown', 'application', 'audio', 'image', 'text', 'video', 'message', 'model', 'multipart', 'chemical')),
+  CONSTRAINT oi_media_type_ckc check (oi_media_type IN('UNKNOWN', 'BITMAP', 'DRAWING', 'AUDIO', 'VIDEO', 'MULTIMEDIA', 'OFFICE', 'TEXT', 'EXECUTABLE', 'ARCHIVE'))
+);--
+
+CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text, oi_timestamp);--
+CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name, oi_timestamp);--
+CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name, oi_archive_name);--
+CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1);--
+
+INSERT INTO /*_*/oldimage
+(
+       oi_name,
+       oi_archive_name,
+       oi_size,
+       oi_width,
+       oi_height,
+       oi_bits,
+       oi_description,
+       oi_user,
+       oi_user_text,
+       oi_timestamp,
+       oi_metadata,
+       oi_media_type,
+       oi_major_mime,
+       oi_minor_mime,
+       oi_deleted,
+       oi_sha1
+)
+SELECT
+       oi_name,
+       oi_archive_name,
+       oi_size,
+       oi_width,
+       oi_height,
+       oi_bits,
+       oi_description,
+       oi_user,
+       oi_user_text,
+       oi_timestamp,
+       CONVERT(varbinary(max), oi_metadata, 0),
+       oi_media_type,
+       oi_major_mime,
+       oi_minor_mime,
+       oi_deleted,
+       oi_sha1
+FROM @temp t;
diff --git a/maintenance/mssql/archives/patch-pl_from_namespace.sql b/maintenance/mssql/archives/patch-pl_from_namespace.sql
new file mode 100644 (file)
index 0000000..b3bbd78
--- /dev/null
@@ -0,0 +1,4 @@
+ALTER TABLE /*_*/pagelinks
+       ADD pl_from_namespace int NOT NULL default 0;
+
+CREATE INDEX /*i*/pl_backlinks_namespace ON /*_*/pagelinks (pl_from_namespace,pl_namespace,pl_title,pl_from);
diff --git a/maintenance/mssql/archives/patch-pp_sortkey.sql b/maintenance/mssql/archives/patch-pp_sortkey.sql
new file mode 100644 (file)
index 0000000..b13b605
--- /dev/null
@@ -0,0 +1,8 @@
+-- Add a 'sortkey' field to page_props so pages can be efficiently
+-- queried by the numeric value of a property.
+
+ALTER TABLE /*_*/page_props
+        ADD pp_sortkey float DEFAULT NULL;
+
+CREATE UNIQUE INDEX /*i*/pp_propname_sortkey_page
+        ON /*_*/page_props ( pp_propname, pp_sortkey, pp_page );
diff --git a/maintenance/mssql/archives/patch-tl_from_namespace.sql b/maintenance/mssql/archives/patch-tl_from_namespace.sql
new file mode 100644 (file)
index 0000000..9655165
--- /dev/null
@@ -0,0 +1,4 @@
+ALTER TABLE /*_*/templatelinks
+       ADD tl_from_namespace int NOT NULL default 0;
+
+CREATE INDEX /*i*/tl_backlinks_namespace ON /*_*/templatelinks (tl_from_namespace,tl_namespace,tl_title,tl_from);
diff --git a/maintenance/mssql/archives/patch-uploadstash-constraints.sql b/maintenance/mssql/archives/patch-uploadstash-constraints.sql
new file mode 100644 (file)
index 0000000..1cd668c
--- /dev/null
@@ -0,0 +1,20 @@
+DECLARE @baseSQL nvarchar(max),
+       @SQL nvarchar(max),
+       @id sysname;--
+
+SET @baseSQL = 'ALTER TABLE /*_*/uploadstash DROP CONSTRAINT ';--
+
+SELECT @id = cc.name
+FROM sys.check_constraints cc
+JOIN sys.columns c
+       ON c.object_id = cc.parent_object_id
+       AND c.column_id = cc.parent_column_id
+WHERE
+       cc.parent_object_id = OBJECT_ID('/*_*/uploadstash')
+       AND c.name = 'us_media_type';--
+
+SET @SQL = @baseSQL + @id;--
+
+EXEC sp_executesql @SQL;--
+
+ALTER TABLE /*_*/uploadstash ADD CONSTRAINT us_media_type_ckc check (us_media_type in('UNKNOWN', 'BITMAP', 'DRAWING', 'AUDIO', 'VIDEO', 'MULTIMEDIA', 'OFFICE', 'TEXT', 'EXECUTABLE', 'ARCHIVE'));
index 86bd735..bd1a1b5 100644 (file)
@@ -38,7 +38,6 @@ CREATE TABLE /*_*/mwuser (
    user_newpassword  NVARCHAR(255)  NOT NULL DEFAULT '',
    user_newpass_time varchar(14) NULL DEFAULT NULL,
    user_email        NVARCHAR(255)  NOT NULL DEFAULT '',
-   user_options      NVARCHAR(MAX) NOT NULL DEFAULT '',
    user_touched      varchar(14)      NOT NULL DEFAULT '',
    user_token        NCHAR(32)      NOT NULL DEFAULT '',
    user_email_authenticated varchar(14) DEFAULT NULL,
@@ -101,6 +100,20 @@ CREATE TABLE /*_*/user_properties (
 CREATE UNIQUE CLUSTERED INDEX /*i*/user_properties_user_property ON /*_*/user_properties (up_user,up_property);
 CREATE INDEX /*i*/user_properties_property ON /*_*/user_properties (up_property);
 
+--
+-- This table contains a user's bot passwords: passwords that allow access to
+-- the account via the API with limited rights.
+--
+CREATE TABLE /*_*/bot_passwords (
+       bp_user int NOT NULL REFERENCES /*_*/mwuser(user_id) ON DELETE CASCADE,
+       bp_app_id nvarchar(32) NOT NULL,
+       bp_password nvarchar(255) NOT NULL,
+       bp_token nvarchar(255) NOT NULL,
+       bp_restrictions nvarchar(max) NOT NULL,
+       bp_grants nvarchar(max) NOT NULL,
+       PRIMARY KEY (bp_user, bp_app_id)
+);
+
 
 --
 -- Core of the wiki: each page has an entry here which identifies
@@ -218,11 +231,13 @@ CREATE INDEX /*i*/ar_revid ON /*_*/archive (ar_rev_id);
 --
 CREATE TABLE /*_*/pagelinks (
    pl_from INT NOT NULL REFERENCES /*_*/page(page_id) ON DELETE CASCADE,
+   pl_from_namespace int NOT NULL DEFAULT 0,
    pl_namespace INT NOT NULL DEFAULT 0,
    pl_title NVARCHAR(255) NOT NULL DEFAULT '',
 );
 CREATE UNIQUE INDEX /*i*/pl_from ON /*_*/pagelinks (pl_from,pl_namespace,pl_title);
 CREATE UNIQUE INDEX /*i*/pl_namespace ON /*_*/pagelinks (pl_namespace,pl_title,pl_from);
+CREATE INDEX /*i*/pl_backlinks_namespace ON /*_*/pagelinks (pl_from_namespace,pl_namespace,pl_title,pl_from);
 
 
 --
@@ -230,12 +245,14 @@ CREATE UNIQUE INDEX /*i*/pl_namespace ON /*_*/pagelinks (pl_namespace,pl_title,p
 --
 CREATE TABLE /*_*/templatelinks (
   tl_from int NOT NULL REFERENCES /*_*/page(page_id) ON DELETE CASCADE,
+  tl_from_namespace int NOT NULL default 0,
   tl_namespace int NOT NULL default 0,
   tl_title nvarchar(255) NOT NULL default ''
 );
 
 CREATE UNIQUE INDEX /*i*/tl_from ON /*_*/templatelinks (tl_from,tl_namespace,tl_title);
 CREATE UNIQUE INDEX /*i*/tl_namespace ON /*_*/templatelinks (tl_namespace,tl_title,tl_from);
+CREATE INDEX /*i*/tl_backlinks_namespace ON /*_*/templatelinks (tl_from_namespace,tl_namespace,tl_title,tl_from);
 
 
 --
@@ -246,6 +263,7 @@ CREATE UNIQUE INDEX /*i*/tl_namespace ON /*_*/templatelinks (tl_namespace,tl_tit
 CREATE TABLE /*_*/imagelinks (
   -- Key to page_id of the page containing the image / media link.
   il_from int NOT NULL REFERENCES /*_*/page(page_id) ON DELETE CASCADE,
+  il_from_namespace int NOT NULL default 0,
 
   -- Filename of target image.
   -- This is also the page_title of the file's description page;
@@ -255,6 +273,7 @@ CREATE TABLE /*_*/imagelinks (
 
 CREATE UNIQUE INDEX /*i*/il_from ON /*_*/imagelinks (il_from,il_to);
 CREATE UNIQUE INDEX /*i*/il_to ON /*_*/imagelinks (il_to,il_from);
+CREATE INDEX /*i*/il_backlinks_namespace ON /*_*/imagelinks (il_from_namespace,il_to,il_from);
 
 --
 -- Track category inclusions *used inline*
@@ -313,8 +332,8 @@ CREATE INDEX /*i*/cl_sortkey ON /*_*/categorylinks (cl_to,cl_type,cl_sortkey,cl_
 -- Used by the API (and some extensions)
 CREATE INDEX /*i*/cl_timestamp ON /*_*/categorylinks (cl_to,cl_timestamp);
 
--- FIXME: Not used, delete this
-CREATE INDEX /*i*/cl_collation ON /*_*/categorylinks (cl_collation);
+-- Used when updating collation (e.g. updateCollation.php)
+CREATE INDEX /*i*/cl_collation_ext ON /*_*/categorylinks (cl_collation, cl_to, cl_type, cl_from);
 
 --
 -- Track all existing categories.  Something is a category if 1) it has an en-
@@ -375,6 +394,9 @@ CREATE TABLE /*_*/externallinks (
 
 CREATE INDEX /*i*/el_from ON /*_*/externallinks (el_from);
 CREATE INDEX /*i*/el_index ON /*_*/externallinks (el_index);
+-- el_to index intentionally not added; we cannot index nvarchar(max) columns,
+-- but we also cannot restrict el_to to a smaller column size as the external
+-- link may be larger.
 
 --
 -- Track interlanguage links
@@ -536,7 +558,7 @@ CREATE TABLE /*_*/image (
   -- Filename.
   -- This is also the title of the associated description page,
   -- which will be in namespace 6 (NS_FILE).
-  img_name varbinary(255) NOT NULL default 0x PRIMARY KEY,
+  img_name nvarchar(255) NOT NULL default '' PRIMARY KEY,
 
   -- File size in bytes.
   img_size int NOT NULL default 0,
@@ -600,11 +622,12 @@ CREATE INDEX /*i*/img_media_mime ON /*_*/image (img_media_type,img_major_mime,im
 --
 CREATE TABLE /*_*/oldimage (
   -- Base filename: key to image.img_name
-  oi_name varbinary(255) NOT NULL default 0x REFERENCES /*_*/image(img_name) ON DELETE CASCADE ON UPDATE CASCADE,
+  -- Not a FK because deleting images removes them from image
+  oi_name nvarchar(255) NOT NULL default '',
 
   -- Filename of the archived file.
   -- This is generally a timestamp and '!' prepended to the base name.
-  oi_archive_name varbinary(255) NOT NULL default 0x,
+  oi_archive_name nvarchar(255) NOT NULL default '',
 
   -- Other fields as in image...
   oi_size int NOT NULL default 0,
@@ -616,7 +639,7 @@ CREATE TABLE /*_*/oldimage (
   oi_user_text nvarchar(255) NOT NULL,
   oi_timestamp varchar(14) NOT NULL default '',
 
-  oi_metadata nvarchar(max) NOT NULL,
+  oi_metadata varbinary(max) NOT NULL,
   oi_media_type varchar(16) default null,
   oi_major_mime varchar(16) not null default 'unknown',
   oi_minor_mime nvarchar(100) NOT NULL default 'unknown',
@@ -629,7 +652,6 @@ CREATE TABLE /*_*/oldimage (
 
 CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
 CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
--- oi_archive_name truncated to 14 to avoid key length overflow
 CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name);
 CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1);
 
@@ -668,7 +690,7 @@ CREATE TABLE /*_*/filearchive (
   fa_size int default 0,
   fa_width int default 0,
   fa_height int default 0,
-  fa_metadata nvarchar(max),
+  fa_metadata varbinary(max),
   fa_bits int default 0,
   fa_media_type varchar(16) default null,
   fa_major_mime varchar(16) not null default 'unknown',
@@ -766,11 +788,6 @@ CREATE TABLE /*_*/recentchanges (
   rc_id int NOT NULL PRIMARY KEY IDENTITY,
   rc_timestamp varchar(14) not null default '',
 
-  -- This is no longer used
-  -- Field kept in database for downgrades
-  -- @todo: add drop patch with 1.24
-  rc_cur_time varchar(14) NOT NULL default '',
-
   -- As in revision
   rc_user int NOT NULL default 0 REFERENCES /*_*/mwuser(user_id),
   rc_user_text nvarchar(255) NOT NULL,
@@ -1157,11 +1174,13 @@ CREATE INDEX /*i*/pt_timestamp ON /*_*/protected_titles (pt_timestamp);
 CREATE TABLE /*_*/page_props (
   pp_page int NOT NULL REFERENCES /*_*/page(page_id) ON DELETE CASCADE,
   pp_propname nvarchar(60) NOT NULL,
-  pp_value nvarchar(max) NOT NULL
+  pp_value nvarchar(max) NOT NULL,
+  pp_sortkey float DEFAULT NULL
 );
 
 CREATE UNIQUE INDEX /*i*/pp_page_propname ON /*_*/page_props (pp_page,pp_propname);
 CREATE UNIQUE INDEX /*i*/pp_propname_page ON /*_*/page_props (pp_propname,pp_page);
+CREATE UNIQUE INDEX /*i*/pp_propname_sortkey_page ON /*_*/page_props (pp_propname,pp_sortkey,pp_page);
 
 
 -- A table to log updates, one text key row per update.
index d20cbe8..dbce7a7 100755 (executable)
@@ -70,7 +70,7 @@ COMMITMSG=$(cat <<END
 Update OOjs UI to v$OOJSUI_VERSION
 
 Release notes:
- https://phabricator.wikimedia.org/diffusion/GOJU/browse/master/History.md;v$OOJS_VERSION
+ https://phabricator.wikimedia.org/diffusion/GOJU/browse/master/History.md;v$OOJSUI_VERSION
 END
 )
 
index be13559..4334237 100644 (file)
@@ -1213,6 +1213,8 @@ return [
                        'upload-form-label-usage-filename',
                        'api-error-unknownerror',
                        'api-error-unknown-warning',
+                       'api-error-autoblocked',
+                       'api-error-blocked',
                        'api-error-badaccess-groups',
                        'api-error-badtoken',
                        'api-error-copyuploaddisabled',
index 62613b8..625c02e 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:01Z
+ * Date: 2016-05-03T22:58:02Z
  */
 ( function ( OO ) {
 
index c928838..eeb4b28 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:05Z
+ * Date: 2016-05-03T22:58:06Z
  */
 .oo-ui-element-hidden {
        display: none !important;
index 72e4db8..268a680 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:05Z
+ * Date: 2016-05-03T22:58:06Z
  */
 .oo-ui-element-hidden {
        display: none !important;
@@ -53,6 +53,9 @@
        font-weight: bold;
        text-decoration: none;
 }
+.oo-ui-buttonElement > .oo-ui-buttonElement-button:focus {
+       outline: 0;
+}
 .oo-ui-buttonElement.oo-ui-iconElement > .oo-ui-buttonElement-button > .oo-ui-iconElement-icon {
        margin-left: 0;
 }
@@ -60,8 +63,7 @@
        margin-left: 0.46875em;
 }
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(0, 0, 0, 0.2);
-       outline: 0;
+       box-shadow: inset 0 0 0 1px #347bff, 0 0 0 1px #347bff;
 }
 .oo-ui-buttonElement-frameless > .oo-ui-buttonElement-button .oo-ui-indicatorElement-indicator {
        margin-right: 0;
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        color: #444444;
 }
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
-       color: #2962cc;
-}
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        color: #347bff;
 }
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label {
+       color: #2962cc;
+}
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        color: #1f4999;
        box-shadow: none;
 }
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
-       color: #2962cc;
-}
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        color: #347bff;
 }
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label {
+       color: #2962cc;
+}
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        color: #1f4999;
        box-shadow: none;
 }
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label,
-.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:focus > .oo-ui-labelElement-label {
-       color: #8c130d;
-}
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        color: #d11d13;
 }
+.oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover > .oo-ui-labelElement-label {
+       color: #8c130d;
+}
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active > .oo-ui-labelElement-label,
 .oo-ui-buttonElement-frameless.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        color: #73100a;
           -moz-transition: background 100ms, color 100ms, border-color 100ms, box-shadow 100ms;
                transition: background 100ms, color 100ms, border-color 100ms, box-shadow 100ms;
 }
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:hover,
-.oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:focus {
-       outline: 0;
-}
 .oo-ui-buttonElement-framed > input.oo-ui-buttonElement-button,
 .oo-ui-buttonElement-framed.oo-ui-labelElement > .oo-ui-buttonElement-button > .oo-ui-labelElement-label {
        line-height: 1.2em;
        background-color: #ebebeb;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2);
+       border-color: #347bff;
+       box-shadow: inset 0 0 0 1px #347bff;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
        color: #347bff;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover {
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
        background-color: #ebf2ff;
        border-color: #859dcc;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #1f4999;
-       border-color: #1f4999;
-}
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
        color: #1f4999;
        background-color: #999999;
        color: #ffffff;
 }
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
+       border-color: #347bff;
+       box-shadow: inset 0 0 0 1px #347bff;
+}
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button {
        color: #347bff;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover {
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
        background-color: #ebf2ff;
        border-color: #859dcc;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #1f4999;
-       border-color: #1f4999;
-}
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
        color: #1f4999;
        background-color: #999999;
        color: #ffffff;
 }
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
+       border-color: #347bff;
+       box-shadow: inset 0 0 0 1px #347bff;
+}
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
        color: #d11d13;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:hover {
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
        background-color: #fbe8e7;
        border-color: #b77c79;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #73100a;
-       border-color: #73100a;
-}
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
        color: #73100a;
        background-color: #999999;
        color: #ffffff;
 }
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
+       border-color: #d11d13;
+       box-shadow: inset 0 0 0 1px #d11d13;
+}
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button {
        color: #ffffff;
        background-color: #347bff;
        border-color: #347bff;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:hover {
-       background: #2962cc;
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
+       background-color: #2962cc;
        border-color: #2962cc;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #ffffff;
-       border-color: #347bff;
-}
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
        color: #ffffff;
        background-color: #999999;
        color: #ffffff;
 }
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-progressive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
+       border-color: #347bff;
+       box-shadow: inset 0 0 0 1px #ffffff;
+}
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button {
        color: #ffffff;
        background-color: #347bff;
        border-color: #347bff;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:hover {
-       background: #2962cc;
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
+       background-color: #2962cc;
        border-color: #2962cc;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #ffffff;
-       border-color: #347bff;
-}
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
        color: #ffffff;
        background-color: #999999;
        color: #ffffff;
 }
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-constructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
+       border-color: #347bff;
+       box-shadow: inset 0 0 0 1px #ffffff;
+}
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button {
        color: #ffffff;
        background-color: #d11d13;
        border-color: #d11d13;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:hover {
-       background: #8c130d;
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:hover {
+       background-color: #8c130d;
        border-color: #8c130d;
 }
-.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive > .oo-ui-buttonElement-button:focus {
-       box-shadow: inset 0 0 0 1px #ffffff;
-       border-color: #d11d13;
-}
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:active,
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled.oo-ui-buttonElement-pressed > .oo-ui-buttonElement-button {
        color: #ffffff;
        background-color: #999999;
        color: #ffffff;
 }
+.oo-ui-buttonElement-framed.oo-ui-widget-enabled.oo-ui-flaggedElement-primary.oo-ui-flaggedElement-destructive.oo-ui-widget-enabled > .oo-ui-buttonElement-button:focus {
+       border-color: #d11d13;
+       box-shadow: inset 0 0 0 1px #ffffff;
+}
 .oo-ui-clippableElement-clippable {
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
 .oo-ui-decoratedOptionWidget.oo-ui-widget-disabled .oo-ui-indicatorElement-indicator {
        opacity: 0.2;
 }
+.oo-ui-radioSelectWidget:focus {
+       outline: 0;
+}
+.oo-ui-radioSelectWidget:focus .oo-ui-radioOptionWidget.oo-ui-optionWidget-selected .oo-ui-radioInputWidget [type="radio"] + span {
+       border-width: 2px;
+}
 .oo-ui-radioOptionWidget {
        cursor: default;
        padding: 0.25em 0;
        border-radius: 0;
        margin-left: -1px;
 }
+.oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed .oo-ui-buttonElement-button:focus {
+       z-index: 2;
+}
 .oo-ui-buttonGroupWidget .oo-ui-buttonElement-framed:first-child .oo-ui-buttonElement-button {
        border-bottom-left-radius: 2px;
        border-top-left-radius: 2px;
index 7128e7a..ddfee91 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:01Z
+ * Date: 2016-05-03T22:58:02Z
  */
 ( function ( OO ) {
 
@@ -5021,6 +5021,7 @@ OO.ui.SelectWidget = function OoUiSelectWidget( config ) {
                toggle: 'onToggle'
        } );
        this.$element.on( {
+               focus: this.onFocus.bind( this ),
                mousedown: this.onMouseDown.bind( this ),
                mouseover: this.onMouseOver.bind( this ),
                mouseleave: this.onMouseLeave.bind( this )
@@ -5101,6 +5102,19 @@ OO.ui.SelectWidget.static.passAllFilter = function () {
 
 /* Methods */
 
+/**
+ * Handle focus events
+ *
+ * @private
+ * @param {jQuery.Event} event
+ */
+OO.ui.SelectWidget.prototype.onFocus = function () {
+       // The styles for focus state depend on one of the items being selected.
+       if ( !this.getSelectedItem() ) {
+               this.selectItem( this.getFirstSelectableItem() );
+       }
+};
+
 /**
  * Handle mouse down events.
  *
index 33f1e0f..c1150d0 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:01Z
+ * Date: 2016-05-03T22:58:02Z
  */
 ( function ( OO ) {
 
index c4eff7c..957dcfc 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:05Z
+ * Date: 2016-05-03T22:58:06Z
  */
 .oo-ui-popupTool .oo-ui-popupWidget-popup,
 .oo-ui-popupTool .oo-ui-popupWidget-anchor {
index 4194cdf..1faf7e5 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:05Z
+ * Date: 2016-05-03T22:58:06Z
  */
 .oo-ui-popupTool .oo-ui-popupWidget-popup,
 .oo-ui-popupTool .oo-ui-popupWidget-anchor {
index 2817324..49b9674 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:01Z
+ * Date: 2016-05-03T22:58:02Z
  */
 ( function ( OO ) {
 
index 8851558..4de8cea 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:05Z
+ * Date: 2016-05-03T22:58:06Z
  */
 .oo-ui-draggableElement-handle,
 .oo-ui-draggableElement-handle.oo-ui-widget {
 .oo-ui-selectFileWidget-dropTarget {
        cursor: default;
        height: 5.5em;
-       text-align: left;
        padding: 0;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel,
        position: absolute;
        right: 0.5em;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget {
-       text-align: center;
-}
 .oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel {
        display: block;
 }
 .oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-clearButton {
        display: none;
 }
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button {
+       cursor: no-drop;
+}
 .oo-ui-selectFileWidget:last-child {
        margin-right: 0;
 }
index 2cd8473..5c48ea5 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:05Z
+ * Date: 2016-05-03T22:58:06Z
  */
 .oo-ui-draggableElement-handle,
 .oo-ui-draggableElement-handle.oo-ui-widget {
 .oo-ui-buttonSelectWidget:last-child {
        margin-right: 0;
 }
+.oo-ui-buttonSelectWidget:focus {
+       outline: 0;
+}
+.oo-ui-buttonSelectWidget:focus .oo-ui-buttonOptionWidget.oo-ui-optionWidget-selected .oo-ui-buttonElement-button {
+       border-color: #347bff;
+       box-shadow: inset 0 0 0 1px #347bff;
+       z-index: 2;
+}
 .oo-ui-buttonSelectWidget .oo-ui-buttonOptionWidget .oo-ui-buttonElement-button {
        border-radius: 0;
        margin-left: -1px;
 .oo-ui-selectFileWidget-dropTarget {
        cursor: default;
        height: 5.5em;
-       text-align: left;
        padding: 0;
 }
 .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel,
        position: absolute;
        right: 0.5em;
 }
-.oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget {
-       text-align: center;
-}
 .oo-ui-selectFileWidget-empty .oo-ui-selectFileWidget-dropTarget .oo-ui-selectFileWidget-dropLabel {
        display: block;
 }
 .oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-clearButton {
        display: none;
 }
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget,
+.oo-ui-selectFileWidget-empty.oo-ui-widget-disabled .oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button,
+.oo-ui-selectFileWidget-notsupported .oo-ui-selectFileWidget-dropTarget .oo-ui-buttonElement-button {
+       cursor: no-drop;
+}
 .oo-ui-selectFileWidget:last-child {
        margin-right: 0;
 }
           -moz-box-sizing: border-box;
                box-sizing: border-box;
        vertical-align: middle;
-       padding: 0 0.4em;
-       margin: 0.1em;
        height: 1.7em;
        line-height: 1.7em;
        background-color: #eeeeee;
-       border: 1px solid #cccccc;
        color: #555555;
+       margin: 0.1em;
+       border: 1px solid #cccccc;
        border-radius: 2px;
+       padding: 0 0.4em;
 }
 .oo-ui-capsuleItemWidget.oo-ui-labelElement .oo-ui-labelElement-label {
        display: inline-block;
 .oo-ui-capsuleItemWidget:focus {
        outline: 0;
        border-color: #347bff;
+       box-shadow: inset 0 0 0 1px #347bff;
 }
 .oo-ui-capsuleItemWidget.oo-ui-widget-disabled {
+       background-color: #f3f3f3;
        color: #cccccc;
-       text-shadow: 0 1px 1px #ffffff;
        border-color: #dddddd;
-       background-color: #f3f3f3;
+       text-shadow: 0 1px 1px #ffffff;
 }
 .oo-ui-capsuleItemWidget > .oo-ui-buttonElement {
-       margin-top: -1.4em;
-       padding-left: 0.3em;
+       display: none;
+}
+.oo-ui-capsuleItemWidget.oo-ui-widget-enabled {
+       padding-right: 1.5375em;
+}
+.oo-ui-capsuleItemWidget.oo-ui-widget-enabled > .oo-ui-buttonElement {
+       display: block;
+       position: absolute;
+       top: 0;
+       right: 0;
+       bottom: 0;
+}
+.oo-ui-capsuleItemWidget.oo-ui-widget-enabled .oo-ui-buttonElement-button {
+       display: block;
+       width: 1.5375em;
+       height: 100%;
+}
+.oo-ui-capsuleItemWidget.oo-ui-widget-enabled .oo-ui-buttonElement-button .oo-ui-indicator-clear {
+       position: absolute;
+       top: 0;
+       right: 0.3em;
+       bottom: 0;
+       height: auto;
 }
 .oo-ui-searchWidget-query {
        position: absolute;
index 895cea4..366aa38 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:01Z
+ * Date: 2016-05-03T22:58:02Z
  */
 ( function ( OO ) {
 
@@ -332,74 +332,23 @@ OO.ui.mixin.DraggableGroupElement.prototype.onItemDropOrDragEnd = function () {
  * @fires reorder
  */
 OO.ui.mixin.DraggableGroupElement.prototype.onDragOver = function ( e ) {
-       var dragOverObj, $optionWidget, itemOffset, itemMidpoint, itemBoundingRect,
-               itemSize, cssOutput, dragPosition, overIndex, itemPosition, after,
-               targetIndex = null,
+       var overIndex, targetIndex,
                item = this.getDragItem(),
-               dragItemIndex = item.getIndex(),
-               clientX = e.originalEvent.clientX,
-               clientY = e.originalEvent.clientY;
+               dragItemIndex = item.getIndex();
 
        // Get the OptionWidget item we are dragging over
-       dragOverObj = this.getElementDocument().elementFromPoint( clientX, clientY );
-       $optionWidget = $( dragOverObj ).closest( '.oo-ui-draggableElement' );
-       if ( $optionWidget[ 0 ] ) {
-               itemOffset = $optionWidget.offset();
-               itemBoundingRect = $optionWidget[ 0 ].getBoundingClientRect();
-               itemPosition = $optionWidget.position();
-               overIndex = $optionWidget.data( 'index' );
-       }
+       overIndex = $( e.target ).closest( '.oo-ui-draggableElement' ).data( 'index' );
+
+       if ( overIndex !== undefined && overIndex !== dragItemIndex ) {
+               targetIndex = overIndex + ( overIndex > dragItemIndex ? 1 : 0 );
 
-       if (
-               itemOffset &&
-               overIndex !== dragItemIndex
-       ) {
-               if ( this.orientation === 'horizontal' ) {
-                       // Calculate where the mouse is relative to the item width
-                       itemSize = itemBoundingRect.width;
-                       itemMidpoint = itemBoundingRect.left + itemSize / 2;
-                       dragPosition = clientX;
-                       // Which side of the item we hover over will dictate
-                       // where to drop the selected item, on the left or
-                       // on the right
-                       cssOutput = {
-                               left: dragPosition < itemMidpoint ? itemPosition.left : itemPosition.left + itemSize,
-                               top: itemPosition.top
-                       };
-               } else {
-                       // Calculate where the mouse is relative to the item height
-                       itemSize = itemBoundingRect.height;
-                       itemMidpoint = itemBoundingRect.top + itemSize / 2;
-                       dragPosition = clientY;
-                       // Which side of the item we hover over will dictate
-                       // where to drop the selected item, on the top or
-                       // on the bottom
-                       cssOutput = {
-                               top: dragPosition < itemMidpoint ? itemPosition.top : itemPosition.top + itemSize,
-                               left: itemPosition.left
-                       };
-               }
-               // Store whether we are before or after an item to rearrange
-               // For horizontal layout, we need to account for RTL, as this is flipped
-               if ( this.orientation === 'horizontal' && this.dir === 'rtl' ) {
-                       after = dragPosition < itemMidpoint;
-               } else {
-                       after = dragPosition > itemMidpoint;
-               }
-               targetIndex = overIndex + ( after ? 1 : 0 );
-               // Check the targetIndex isn't immediately to the left or right of the current item (a no-op)
-               if ( targetIndex === dragItemIndex || targetIndex === dragItemIndex + 1 ) {
-                       targetIndex = null;
-               }
-       }
-       if ( targetIndex !== null ) {
                if ( targetIndex > 0 ) {
                        this.$group.children().eq( targetIndex - 1 ).after( item.$element );
                } else {
                        this.$group.prepend( item.$element );
                }
-               // Move item in itemsOrder array. Needs to account for left shift if the item is moved forward.
-               this.itemsOrder.splice( targetIndex - ( targetIndex > dragItemIndex ? 1 : 0 ), 0,
+               // Move item in itemsOrder array
+               this.itemsOrder.splice( overIndex, 0,
                        this.itemsOrder.splice( dragItemIndex, 1 )[ 0 ]
                );
                this.updateIndexes();
index b1a3c3b..764f40c 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:05Z
+ * Date: 2016-05-03T22:58:06Z
  */
 .oo-ui-actionWidget.oo-ui-pendingElement-pending {
        background-image: /* @embed */ url(themes/apex/images/textures/pending.gif);
index d323fec..deda1d0 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:05Z
+ * Date: 2016-05-03T22:58:06Z
  */
 .oo-ui-window {
        background: transparent;
index 7f33fad..93a870c 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.17.0
+ * OOjs UI v0.17.1
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2016-04-26T21:34:01Z
+ * Date: 2016-05-03T22:58:02Z
  */
 ( function ( OO ) {
 
index 79e8731..bdb5ce8 100644 (file)
         */
        function togglingHandler( $toggle, $collapsible, e, options ) {
                var wasCollapsed, $textContainer, collapseText, expandText;
-
-               if ( options === undefined ) {
-                       options = {};
-               }
+               options = options || {};
 
                if ( e ) {
                        if (
         * @chainable
         */
        $.fn.makeCollapsible = function ( options ) {
-               if ( options === undefined ) {
-                       options = {};
-               }
+               options = options || {};
 
                this.each( function () {
                        var $collapsible, collapseText, expandText, $caption, $toggle, actionHandler, buildDefaultToggleLink,
index 4c618d4..cfdf797 100644 (file)
@@ -112,7 +112,6 @@ abbr[title],
 .mw-plusminus-neg,
 .mw-plusminus-null {
        unicode-bidi: -moz-isolate;
-       unicode-bidi: -webkit-isolate;
        unicode-bidi: isolate;
 }
 
@@ -120,7 +119,6 @@ abbr[title],
 span.comment {
        font-style: italic;
        unicode-bidi: -moz-isolate;
-       unicode-bidi: -webkit-isolate;
        unicode-bidi: isolate;
 }
 
index 66b3fb2..99d465b 100644 (file)
@@ -35,7 +35,6 @@ span.reference {
        line-height: 1;
        vertical-align: super;
        unicode-bidi: -moz-isolate;
-       unicode-bidi: -webkit-isolate;
        unicode-bidi: isolate;
 }
 
index 3d97711..fdd4a8a 100644 (file)
                                disambiguation: OO.getProp( suggestionPage, 'pageprops', 'disambiguation' ) !== undefined,
                                imageUrl: OO.getProp( suggestionPage, 'thumbnail', 'source' ),
                                description: OO.getProp( suggestionPage, 'terms', 'description' ),
-                               // sort index
+                               // Sort index
                                index: suggestionPage.index
                        };
 
                                        missing: false,
                                        redirect: true,
                                        disambiguation: false,
-                                       description: mw.msg( 'mw-widgets-titleinput-description-redirect', suggestionPage.title )
+                                       description: mw.msg( 'mw-widgets-titleinput-description-redirect', suggestionPage.title ),
+                                       // Sort index, just below its target
+                                       index: suggestionPage.index + 0.5
                                };
                                titles.push( redirects[ i ] );
                        }
index 1f21fc6..9cef1c4 100644 (file)
                'fileexists-shared-forbidden',
                'invalidtitle',
                'notloggedin',
+               'autoblocked',
+               'blocked',
 
                // Stash-specific errors - expanded
                'stashfailed',
index 7d7b413..a35ce7a 100644 (file)
@@ -3,6 +3,16 @@
        margin-bottom: 0.1em;
 }
 
+.apihelp-header.apihelp-module-name {
+       /*
+        * This element is explicitly set to dir="ltr" in HTML.
+        * Set explicit alignment so that CSSJanus will flip it to "right";
+        * otherwise the alignment will be automatically set to "left" according
+        * to the element's direction, and this will have an inconsistent look.
+        */
+       text-align: left;
+}
+
 div.apihelp-linktrail {
        font-size: smaller;
 }
index 7a980f7..572e2f8 100644 (file)
@@ -12,11 +12,10 @@ directory and make a symbolic link:
  mediawiki/skins$ ln -s ../../skins-trunk/FooBar
 
 The default skin Vector can be installed by cloning from Git:
-    git clone https://git.wikimedia.org/git/mediawiki/skins/Vector.git
+    git clone https://phabricator.wikimedia.org/diffusion/SVEC/Vector
 
 Other skins are also available:
-    https://gerrit.wikimedia.org/r/#/admin/projects/?filter=mediawiki%252Fskins%252F
-    https://git.wikimedia.org/project/mediawiki
+    https://phabricator.wikimedia.org/diffusion/SKIN/
 
 
 Please note that under POSIX systems (Linux...), parent of a symbolic path
index eb9adea..bb71610 100644 (file)
@@ -61,6 +61,18 @@ class WfAppendQueryTest extends MediaWikiTestCase {
                                'baz=quux&foo=baz',
                                'http://www.example.org/index.php?foo=bar&baz=quux&foo=baz',
                                'Modify query string'
+                       ],
+                       [
+                               'http://www.example.org/index.php#baz',
+                               'foo=bar',
+                               'http://www.example.org/index.php?foo=bar#baz',
+                               'URL with fragment'
+                       ],
+                       [
+                               'http://www.example.org/index.php?foo=bar#baz',
+                               'quux=blah',
+                               'http://www.example.org/index.php?foo=bar&quux=blah#baz',
+                               'URL with query string and fragment'
                        ]
                ];
        }
index cf34b18..224b0cb 100644 (file)
@@ -581,4 +581,29 @@ class MessageTest extends MediaWikiLangTestCase {
                $msg = unserialize( serialize( $msg ) );
                $this->assertEquals( 'Hauptseite', $msg->plain() );
        }
+
+       /**
+        * @covers Message::newFromSpecifier
+        * @dataProvider provideNewFromSpecifier
+        */
+       public function testNewFromSpecifier( $value, $expectedText ) {
+               $message = Message::newFromSpecifier( $value );
+               $this->assertInstanceOf( Message::class, $message );
+               $this->assertSame( $expectedText, $message->text() );
+       }
+
+       public function provideNewFromSpecifier() {
+               $messageSpecifier = $this->getMockForAbstractClass( MessageSpecifier::class );
+               $messageSpecifier->expects( $this->any() )->method( 'getKey' )->willReturn( 'mainpage' );
+               $messageSpecifier->expects( $this->any() )->method( 'getParams' )->willReturn( [] );
+
+               return [
+                       'string' => [ 'mainpage', 'Main Page' ],
+                       'array' => [ [ 'youhavenewmessages', 'foo', 'bar' ], 'You have foo (bar).' ],
+                       'Message' => [ new Message( 'youhavenewmessages', [ 'foo', 'bar' ] ), 'You have foo (bar).' ],
+                       'RawMessage' => [ new RawMessage( 'foo ($1)', [ 'bar' ] ), 'foo (bar)' ],
+                       'MessageSpecifier' => [ $messageSpecifier, 'Main Page' ],
+               ];
+       }
 }
+
index 27c0c60..0120d79 100644 (file)
@@ -50,7 +50,7 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
                                self::$default,
                                $merge,
                        ],
-                       // No current hooks, adding one for "FooBaz"
+                       // No current hooks, adding one for "FooBaz" in string format
                        [
                                [],
                                [ 'Hooks' => [ 'FooBaz' => 'FooBazCallback' ] ] + self::$default,
@@ -62,6 +62,12 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
                                [ 'Hooks' => [ 'FooBaz' => 'FooBazCallback' ] ] + self::$default,
                                [ 'FooBaz' => [ 'PriorCallback', 'FooBazCallback' ] ] + $merge,
                        ],
+                       // No current hooks, adding one for "FooBaz" in verbose array format
+                       [
+                               [],
+                               [ 'Hooks' => [ 'FooBaz' => [ 'FooBazCallback' ] ] ] + self::$default,
+                               [ 'FooBaz' => [ 'FooBazCallback' ] ] + $merge,
+                       ],
                        // Hook for "BarBaz", adding one for "FooBaz"
                        [
                                [ 'BarBaz' => [ 'BarBazCallback' ] ],
index e87f41d..3815416 100644 (file)
@@ -267,21 +267,9 @@ class SessionTest extends MediaWikiTestCase {
        }
 
        public function testTokens() {
-               $rc = new \ReflectionClass( Session::class );
-               if ( !method_exists( $rc, 'newInstanceWithoutConstructor' ) ) {
-                       $this->markTestSkipped(
-                               'ReflectionClass::newInstanceWithoutConstructor isn\'t available'
-                       );
-               }
-
-               // Instead of actually constructing the Session, we use reflection to
-               // bypass the constructor and plug a mock SessionBackend into the
-               // private fields to avoid having to actually create a SessionBackend.
-               $backend = new DummySessionBackend;
-               $session = $rc->newInstanceWithoutConstructor();
+               $session = TestUtils::getDummySession();
                $priv = \TestingAccessWrapper::newFromObject( $session );
-               $priv->backend = $backend;
-               $priv->index = 42;
+               $backend = $priv->backend;
 
                $token = \TestingAccessWrapper::newFromObject( $session->getToken() );
                $this->assertArrayHasKey( 'wsTokenSecrets', $backend->data );
@@ -313,4 +301,72 @@ class SessionTest extends MediaWikiTestCase {
                $this->assertArrayNotHasKey( 'wsTokenSecrets', $backend->data );
 
        }
+
+       /**
+        * @dataProvider provideSecretsRoundTripping
+        * @param mixed $data
+        */
+       public function testSecretsRoundTripping( $data ) {
+               $session = TestUtils::getDummySession();
+
+               // Simple round-trip
+               $session->setSecret( 'secret', $data );
+               $this->assertNotEquals( $data, $session->get( 'secret' ) );
+               $this->assertEquals( $data, $session->getSecret( 'secret', 'defaulted' ) );
+       }
+
+       public static function provideSecretsRoundTripping() {
+               return [
+                       [ 'Foobar' ],
+                       [ 42 ],
+                       [ [ 'foo', 'bar' => 'baz', 'subarray' => [ 1, 2, 3 ] ] ],
+                       [ (object)[ 'foo', 'bar' => 'baz', 'subarray' => [ 1, 2, 3 ] ] ],
+                       [ true ],
+                       [ false ],
+                       [ null ],
+               ];
+       }
+
+       public function testSecrets() {
+               $logger = new \TestLogger;
+               $session = TestUtils::getDummySession( null, -1, $logger );
+
+               // Simple defaulting
+               $this->assertEquals( 'defaulted', $session->getSecret( 'test', 'defaulted' ) );
+
+               // Bad encrypted data
+               $session->set( 'test', 'foobar' );
+               $logger->setCollect( true );
+               $this->assertEquals( 'defaulted', $session->getSecret( 'test', 'defaulted' ) );
+               $logger->setCollect( false );
+               $this->assertSame( [
+                       [ LogLevel::WARNING, 'Invalid sealed-secret format' ]
+               ], $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Tampered data
+               $session->setSecret( 'test', 'foobar' );
+               $encrypted = $session->get( 'test' );
+               $session->set( 'test', $encrypted . 'x' );
+               $logger->setCollect( true );
+               $this->assertEquals( 'defaulted', $session->getSecret( 'test', 'defaulted' ) );
+               $logger->setCollect( false );
+               $this->assertSame( [
+                       [ LogLevel::WARNING, 'Sealed secret has been tampered with, aborting.' ]
+               ], $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Unserializable data
+               $iv = \MWCryptRand::generate( 16, true );
+               list( $encKey, $hmacKey ) = \TestingAccessWrapper::newFromObject( $session )->getSecretKeys();
+               $ciphertext = openssl_encrypt( 'foobar', 'aes-256-ctr', $encKey, OPENSSL_RAW_DATA, $iv );
+               $sealed = base64_encode( $iv ) . '.' . base64_encode( $ciphertext );
+               $hmac = hash_hmac( 'sha256', $sealed, $hmacKey, true );
+               $encrypted = base64_encode( $hmac ) . '.' . $sealed;
+               $session->set( 'test', $encrypted );
+               \MediaWiki\suppressWarnings();
+               $this->assertEquals( 'defaulted', $session->getSecret( 'test', 'defaulted' ) );
+               \MediaWiki\restoreWarnings();
+       }
+
 }