Merge "Fix ConfirmLinkSecondaryAuthenticationProvider error message name"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 27 May 2016 16:19:59 +0000 (16:19 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 27 May 2016 16:19:59 +0000 (16:19 +0000)
77 files changed:
includes/DefaultSettings.php
includes/Linker.php
includes/MediaWiki.php
includes/WebRequest.php
includes/api/ApiMain.php
includes/api/ApiStashEdit.php
includes/api/i18n/it.json
includes/api/i18n/ta.json
includes/api/i18n/uk.json
includes/cache/LinkBatch.php
includes/cache/LinkCache.php
includes/changes/ChangesList.php
includes/changes/EnhancedChangesList.php
includes/changes/RCCacheEntryFactory.php
includes/collation/IcuCollation.php
includes/deferred/LinksUpdate.php
includes/installer/MysqlInstaller.php
includes/jobqueue/jobs/RefreshLinksJob.php
includes/linker/LinkRenderer.php
includes/linker/LinkRendererFactory.php
includes/parser/LinkHolderArray.php
includes/parser/Parser.php
includes/specialpage/LoginSignupSpecialPage.php
includes/title/MediaWikiTitleCodec.php
languages/i18n/bho.json
languages/i18n/dty.json
languages/i18n/en.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/gor.json
languages/i18n/gu.json
languages/i18n/he.json
languages/i18n/inh.json
languages/i18n/it.json
languages/i18n/jut.json
languages/i18n/kk-cyrl.json
languages/i18n/ko.json
languages/i18n/ksh.json
languages/i18n/lb.json
languages/i18n/pms.json
languages/i18n/sr-ec.json
languages/i18n/ta.json
maintenance/convertExtensionToRegistration.php
mw-config/config.css
package.json
resources/Resources.php
resources/src/mediawiki.legacy/commonPrint.css
resources/src/mediawiki.legacy/oldshared.css
resources/src/mediawiki.skinning/content.css
resources/src/mediawiki.special/mediawiki.special.apisandbox.js
resources/src/mediawiki.special/mediawiki.special.js [deleted file]
resources/src/mediawiki.special/mediawiki.special.recentchanges.js
tests/TestsAutoLoader.php
tests/parser/parserTests.txt
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/includes/LinkerTest.php
tests/phpunit/includes/MergeHistoryTest.php
tests/phpunit/includes/TestUserRegistry.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiQueryWatchlistIntegrationTest.php
tests/phpunit/includes/api/ApiTestCase.php
tests/phpunit/includes/auth/LocalPasswordPrimaryAuthenticationProviderTest.php
tests/phpunit/includes/cache/GenderCacheTest.php
tests/phpunit/includes/changes/CategoryMembershipChangeTest.php
tests/phpunit/includes/changes/EnhancedChangesListTest.php
tests/phpunit/includes/changes/OldChangesListTest.php
tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php
tests/phpunit/includes/changes/TestRecentChangesHelper.php
tests/phpunit/includes/linker/LinkRendererFactoryTest.php
tests/phpunit/includes/session/BotPasswordSessionProviderTest.php
tests/phpunit/includes/session/CookieSessionProviderTest.php
tests/phpunit/includes/session/SessionBackendTest.php
tests/phpunit/includes/title/MediaWikiTitleCodecTest.php
tests/phpunit/includes/user/BotPasswordTest.php
tests/phpunit/includes/user/CentralIdLookupTest.php
tests/phpunit/includes/user/LocalIdLookupTest.php
tests/phpunit/includes/user/UserTest.php
tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js

index cef0215..c6d9010 100644 (file)
@@ -4458,7 +4458,7 @@ $wgPasswordPolicy = [
  * @since 1.27
  * @deprecated since 1.27, for use during development only
  */
-$wgDisableAuthManager = true;
+$wgDisableAuthManager = false;
 
 /**
  * Configure AuthManager
index 0b80c08..71cb4e4 100644 (file)
@@ -138,22 +138,30 @@ class Linker {
         * Return the CSS colour of a known link
         *
         * @since 1.16.3
-        * @param Title $t
+        * @param LinkTarget $t
         * @param int $threshold User defined threshold
         * @return string CSS class
         */
-       public static function getLinkColour( $t, $threshold ) {
-               $colour = '';
-               if ( $t->isRedirect() ) {
+       public static function getLinkColour( LinkTarget $t, $threshold ) {
+               $linkCache = MediaWikiServices::getInstance()->getLinkCache();
+               // Make sure the target is in the cache
+               $id = $linkCache->addLinkObj( $t );
+               if ( $id == 0 ) {
+                       // Doesn't exist
+                       return '';
+               }
+
+               if ( $linkCache->getGoodLinkFieldObj( $t, 'redirect' ) ) {
                        # Page is a redirect
-                       $colour = 'mw-redirect';
-               } elseif ( $threshold > 0 && $t->isContentPage() &&
-                       $t->exists() && $t->getLength() < $threshold
+                       return 'mw-redirect';
+               } elseif ( $threshold > 0 && MWNamespace::isContent( $t->getNamespace() )
+                       && $linkCache->getGoodLinkFieldObj( $t, 'length' ) < $threshold
                ) {
                        # Page is a stub
-                       $colour = 'stub';
+                       return 'stub';
                }
-               return $colour;
+
+               return '';
        }
 
        /**
@@ -235,6 +243,8 @@ class Linker {
                        return $linkRenderer->makeKnownLink( $target, $text, $customAttribs, $query );
                } elseif ( in_array( 'broken', $options, true ) ) {
                        return $linkRenderer->makeBrokenLink( $target, $text, $customAttribs, $query );
+               } elseif ( in_array( 'noclasses', $options, true ) ) {
+                       return $linkRenderer->makePreloadedLink( $target, $text, '', $customAttribs, $query );
                } else {
                        return $linkRenderer->makeLink( $target, $text, $customAttribs, $query );
                }
index ff469e4..55f9e9e 100644 (file)
@@ -667,10 +667,10 @@ class MediaWiki {
                $trxLimits = $this->config->get( 'TrxProfilerLimits' );
                $trxProfiler = Profiler::instance()->getTransactionProfiler();
                $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
-               if ( $request->wasPosted() ) {
-                       $trxProfiler->setExpectations( $trxLimits['POST'], __METHOD__ );
-               } else {
+               if ( $request->hasSafeMethod() ) {
                        $trxProfiler->setExpectations( $trxLimits['GET'], __METHOD__ );
+               } else {
+                       $trxProfiler->setExpectations( $trxLimits['POST'], __METHOD__ );
                }
 
                // If the user has forceHTTPS set to true, or if the user
index 2333c78..152a3d2 100644 (file)
@@ -1249,6 +1249,26 @@ HTML;
                $this->ip = $ip;
        }
 
+       /**
+        * Check if this request uses a "safe" HTTP method
+        *
+        * Safe methods are verbs (e.g. GET/HEAD/OPTIONS) used for obtaining content. Such requests
+        * are not expected to mutate content, especially in ways attributable to the client. Verbs
+        * like POST and PUT are typical of non-safe requests which often change content.
+        *
+        * @return bool
+        * @see https://tools.ietf.org/html/rfc7231#section-4.2.1
+        * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
+        * @since 1.28
+        */
+       public function hasSafeMethod() {
+               if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
+                       return false; // CLI mode
+               }
+
+               return in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS', 'TRACE' ] );
+       }
+
        /**
         * Whether this request should be identified as being "safe"
         *
@@ -1268,21 +1288,15 @@ HTML;
         * @since 1.28
         */
        public function isSafeRequest() {
-               if ( !isset( $_SERVER['REQUEST_METHOD'] ) ) {
-                       return false; // CLI mode
-               }
-
-               if ( $_SERVER['REQUEST_METHOD'] === 'POST' ) {
-                       return $this->markedAsSafe;
-               } elseif ( in_array( $_SERVER['REQUEST_METHOD'], [ 'GET', 'HEAD', 'OPTIONS' ] ) ) {
-                       return true; // HTTP "safe methods"
+               if ( $this->markedAsSafe && $this->wasPosted() ) {
+                       return true; // marked as a "safe" POST
                }
 
-               return false; // PUT/DELETE
+               return $this->hasSafeMethod();
        }
 
        /**
-        * Mark this request is identified as being nullipotent even if it is a POST request
+        * Mark this request as identified as being nullipotent even if it is a POST request
         *
         * POST requests are often used due to the need for a client payload, even if the request
         * is otherwise equivalent to a "safe method" request.
index 7f9b98b..ce9587f 100644 (file)
@@ -1391,15 +1391,13 @@ class ApiMain extends ApiBase {
        protected function setRequestExpectations( ApiBase $module ) {
                $limits = $this->getConfig()->get( 'TrxProfilerLimits' );
                $trxProfiler = Profiler::instance()->getTransactionProfiler();
-               if ( $this->getRequest()->wasPosted() ) {
-                       if ( $module->isWriteMode() ) {
-                               $trxProfiler->setExpectations( $limits['POST'], __METHOD__ );
-                       } else {
-                               $trxProfiler->setExpectations( $limits['POST-nonwrite'], __METHOD__ );
-                               $this->getRequest()->markAsSafeRequest();
-                       }
-               } else {
+               if ( $this->getRequest()->hasSafeMethod() ) {
                        $trxProfiler->setExpectations( $limits['GET'], __METHOD__ );
+               } elseif ( $this->getRequest()->wasPosted() && !$module->isWriteMode() ) {
+                       $trxProfiler->setExpectations( $limits['POST-nonwrite'], __METHOD__ );
+                       $this->getRequest()->markAsSafeRequest();
+               } else {
+                       $trxProfiler->setExpectations( $limits['POST'], __METHOD__ );
                }
        }
 
index 93003cc..e739e51 100644 (file)
@@ -46,6 +46,10 @@ class ApiStashEdit extends ApiBase {
                $user = $this->getUser();
                $params = $this->extractRequestParams();
 
+               if ( $user->isBot() ) { // sanity
+                       $this->dieUsage( 'This interface is not supported for bots', 'botsnotsupported' );
+               }
+
                $page = $this->getTitleOrPageId( $params );
                $title = $page->getTitle();
 
@@ -123,6 +127,8 @@ class ApiStashEdit extends ApiBase {
                        $status = 'busy';
                }
 
+               $this->getStats()->increment( "editstash.cache_stores.$status" );
+
                $this->getResult()->addValue( null, $this->getModuleName(), [ 'status' => $status ] );
        }
 
@@ -259,6 +265,10 @@ class ApiStashEdit extends ApiBase {
         * @return stdClass|bool Returns false on cache miss
         */
        public static function checkCache( Title $title, Content $content, User $user ) {
+               if ( $user->isBot() ) {
+                       return false; // bots never stash - don't pollute stats
+               }
+
                $cache = ObjectCache::getLocalClusterInstance();
                $logger = LoggerFactory::getInstance( 'StashEdit' );
                $stats = RequestContext::getMain()->getStats();
index d1acea6..28ddcf9 100644 (file)
@@ -35,6 +35,7 @@
        "apihelp-block-param-watchuser": "Segui la pagina utente e le pagine di discussione utente dell'utente o dell'indirizzo IP.",
        "apihelp-block-example-ip-simple": "Blocca l'indirizzo IP <kbd>192.0.2.5</kbd> per tre giorni con motivazione <kbd>First strike</kbd>.",
        "apihelp-block-example-user-complex": "Blocca l'utente <kbd>Vandal</kbd> a tempo indeterminato con motivazione <kbd>Vandalism</kbd>, e impediscigli la creazione di nuovi account e l'invio di e-mail.",
+       "apihelp-changeauthenticationdata-description": "Modificare i dati di autenticazione per l'utente corrente.",
        "apihelp-changeauthenticationdata-example-password": "Tentativo di modificare la password dell'utente corrente a <kbd>ExamplePassword</kbd>.",
        "apihelp-checktoken-description": "Verifica la validità di un token da <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.",
        "apihelp-checktoken-param-type": "Tipo di token in corso di test.",
@@ -43,6 +44,7 @@
        "apihelp-checktoken-example-simple": "Verifica la validità di un token <kbd>csrf</kbd>.",
        "apihelp-clearhasmsg-description": "Cancella il flag <code>hasmsg</code> per l'utente corrente.",
        "apihelp-clearhasmsg-example-1": "Cancella il flag <code>hasmsg</code> per l'utente corrente.",
+       "apihelp-clientlogin-example-login": "Avvia il processo di accesso alla wiki come utente <kbd>Example</kbd> con password <kbd>ExamplePassword</kbd>.",
        "apihelp-clientlogin-example-login2": "Continua l'accesso dopo una risposta dell'<samp>UI</samp> per l'autenticazione a due fattori, fornendo un <var>OATHToken</var> di <kbd>987654</kbd>.",
        "apihelp-compare-description": "Ottieni le differenze tra 2 pagine.\n\nUn numero di revisione, il titolo di una pagina, o un ID di pagina deve essere indicato sia per il \"da\" che per lo \"a\".",
        "apihelp-compare-param-fromtitle": "Primo titolo da confrontare.",
@@ -53,7 +55,7 @@
        "apihelp-compare-param-torev": "Seconda revisione da confrontare.",
        "apihelp-compare-example-1": "Crea un diff tra revisione 1 e revisione 2.",
        "apihelp-createaccount-description": "Crea un nuovo account utente.",
-       "apihelp-createaccount-example-create": "Avviare il processo di creazione utente <kbd>Example</kbd> con password <kbd>ExamplePassword</kbd>.",
+       "apihelp-createaccount-example-create": "Avvia il processo di creazione utente <kbd>Example</kbd> con password <kbd>ExamplePassword</kbd>.",
        "apihelp-createaccount-param-name": "Nome utente.",
        "apihelp-createaccount-param-password": "Password (verrà ignorata se è impostato <var>$1mailpassword</var>).",
        "apihelp-createaccount-param-domain": "Dominio per l'autenticazione esterna (opzionale).",
        "apihelp-import-param-namespace": "Importa in questo namespace. Non può essere usato insieme a <var>$1rootpage</var>.",
        "apihelp-import-param-rootpage": "Importa come sottopagina di questa pagina. Non può essere usato insieme a <var>$1namespace</var>.",
        "apihelp-import-example-import": "Importa [[meta:Help:ParserFunctions]] nel namespace 100 con cronologia completa.",
-       "apihelp-linkaccount-description": "Collegamento di un account di un provider di terze parti all'utente corrente.",
-       "apihelp-login-description": "Accedi e ottieni i cookie di autenticazione.\n\nQuesta azione deve essere usata esclusivamente in combinazione con [[Special:BotPasswords]]; utilizzarla per l'accesso all'account principale è deprecato e può fallire senza preavviso. Per accedere in modo sicuro all'account principale, usa <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
+       "apihelp-linkaccount-description": "Collegamento di un'utenza di un provider di terze parti all'utente corrente.",
+       "apihelp-login-description": "Accedi e ottieni i cookie di autenticazione.\n\nQuesta azione deve essere usata esclusivamente in combinazione con [[Special:BotPasswords]]; utilizzarla per l'accesso all'account principale è deprecato e può fallire senza preavviso. Per accedere in modo sicuro all'utenza principale, usa <kbd>[[Special:ApiHelp/clientlogin|action=clientlogin]]</kbd>.",
+       "apihelp-login-description-nobotpasswords": "Accedi e ottieni i cookies di autenticazione.\n\nQuesta azione è deprecata e può fallire senza preavviso. Per accedere in modo sicuro, usa [[Special:ApiHelp/clientlogin|action=clientlogin]].",
        "apihelp-login-description-nonauthmanager": "Accedi e ottieni i cookie di autenticazione.\n\nIn caso di accesso riuscito, i cookies necessari saranno inclusi nella intestazioni di risposta HTTP. In caso di accesso fallito, ulteriori tentativi potrebbero essere limitati, in modo da contenere gli attacchi automatizzati per indovinare le password.",
        "apihelp-login-param-name": "Nome utente.",
        "apihelp-login-param-password": "Password.",
        "apihelp-query+allusers-param-excludegroup": "Escludi gli utenti nei gruppi indicati.",
        "apihelp-query+allusers-param-prop": "Quali pezzi di informazioni includere:",
        "apihelp-query+allusers-param-limit": "Quanti nomi utente totali restituire.",
-       "apihelp-query+authmanagerinfo-description": "Recuperare informazioni circa l'attuale stato di autenticazione.",
-       "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Verificare se lo stato di autenticazione dell'utente attuale è sufficiente per la specifica operazione sensibile alla sicurezza.",
+       "apihelp-query+authmanagerinfo-description": "Recupera informazioni circa l'attuale stato di autenticazione.",
+       "apihelp-query+authmanagerinfo-param-securitysensitiveoperation": "Verifica se lo stato di autenticazione dell'utente attuale è sufficiente per la specifica operazione sensibile alla sicurezza.",
+       "apihelp-query+authmanagerinfo-param-requestsfor": "Recupera informazioni circa le richieste di autenticazione necessarie per la specifica azione di autenticazione.",
+       "apihelp-query+filerepoinfo-example-securitysensitiveoperation": "Verificare se l'autenticazione è sufficiente per l'azione <kbd>foo</kbd>.",
        "apihelp-query+backlinks-description": "Trova tutte le pagine che puntano a quella specificata.",
        "apihelp-query+backlinks-param-namespace": "Il namespace da elencare.",
        "apihelp-query+backlinks-param-dir": "La direzione in cui elencare.",
        "apihelp-query+watchlistraw-param-totitle": "Il titolo (con prefisso namespace) al quale interrompere l'elenco.",
        "apihelp-query+watchlistraw-example-simple": "Elenca le pagine fra gli osservati speciali dell'utente attuale.",
        "apihelp-query+watchlistraw-example-generator": "Recupera le informazioni sulle pagine fra gli osservati speciali dell'utente attuale.",
+       "apihelp-removeauthenticationdata-description": "Rimuove i dati di autenticazione per l'utente corrente.",
        "apihelp-removeauthenticationdata-example-simple": "Tentativo di rimuovere gli attuali dati utente per <kbd>FooAuthenticationRequest</kbd>.",
+       "apihelp-resetpassword-description": "Invia una mail per reimpostare la password di un utente.",
        "apihelp-resetpassword-param-user": "Utente in corso di ripristino.",
-       "apihelp-resetpassword-param-email": "Indirizzo email dell'utente in corso di ripristino.",
-       "apihelp-resetpassword-param-capture": "Ritorna le password temporanee che erano state inviate. Richiede il diritto utente <code>passwordreset</code>.",
-       "apihelp-resetpassword-example-user": "Invia un'email per reimpostare la password all'utente <kbd>Esempio</kbd>.",
+       "apihelp-resetpassword-param-email": "Indirizzo di posta elettronica dell'utente in corso di ripristino.",
+       "apihelp-resetpassword-param-capture": "Restituisce le password temporanee che erano state inviate. Richiede il diritto utente <code>passwordreset</code>.",
+       "apihelp-resetpassword-example-user": "Invia una mail per reimpostare la password all'utente <kbd>Example</kbd>.",
        "apihelp-revisiondelete-description": "Cancella e ripristina le versioni.",
        "apihelp-revisiondelete-param-type": "Tipo di cancellazione della versione effettuata.",
        "apihelp-revisiondelete-param-hide": "Cosa nascondere per ogni versione.",
        "apihelp-undelete-param-title": "Titolo della pagina da ripristinare.",
        "apihelp-undelete-param-reason": "Motivo per il ripristino.",
        "apihelp-undelete-param-tags": "Modifica etichette da applicare all'elemento del registro delle cancellazioni.",
-       "apihelp-unlinkaccount-description": "Rimuovere un account di terze parti collegato all'utente corrente.",
+       "apihelp-unlinkaccount-description": "Rimuove un'utenza di terze parti collegata all'utente corrente.",
+       "apihelp-unlinkaccount-example-simple": "Tentativo di rimuovere il collegamento dell'utente corrente per il provider associato con <kbd>FooAuthenticationRequest</kbd>.",
        "apihelp-upload-param-watch": "Osserva la pagina.",
        "apihelp-upload-param-file": "Contenuto del file.",
        "apihelp-upload-example-url": "Carica da un URL.",
        "api-help-permissions": "{{PLURAL:$1|Permesso|Permessi}}:",
        "api-help-open-in-apisandbox": "<small>[apri in una sandbox]</small>",
        "api-help-authmanager-general-usage": "La procedura generale per usare questo modulo é:\n# Ottenere i campi disponibili da <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> con <kbd>amirequestsfor=$4</kbd>, e un token <kbd>$5</kbd> da <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.\n# Mostra i campi all'utente e ottieni i dati che invia.\n# Esegui un post a questo modulo, fornendo <var>$1returnurl</var> e ogni campo rilevante.\n# Controlla <samp>status</samp> nella response.\n#* Se hai ricevuto <samp>PASS</samp> o <samp>FAIL</samp>, hai finito. L'operazione nel primo caso è andata a buon fine, nel secondo no.\n#* Se hai ricevuto <samp>UI</samp>, mostra i nuovi campi all'utente e ottieni i dati che invia. Esegui un post a questo modulo con <var>$1continue</var> e i campi rilevanti settati, quindi ripeti il punto 4.\n#* Se hai ricevuto <samp>REDIRECT</samp>, dirigi l'utente a <samp>redirecttarget</samp> e aspetta che ritorni a <var>$1returnurl</var>. A quel punto esegui un post a questo modulo con <var>$1continue</var> e ogni campo passato all'URL di ritorno, e ripeti il punto 4.\n#* Se hai ricevuto <samp>RESTART</samp>, vuol dire che l'autenticazione ha funzionato ma non abbiamo un account collegato. Potresti considerare questo caso come <samp>UI</samp> o come <samp>FAIL</samp>.",
-       "api-help-authmanagerhelper-additional-params": "Questo modulo accetta parametri aggiuntivi a seconda delle richieste di autenticazione disponibili. Utilizzare <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> con <kbd>amirequestsfor=$1</kbd> (o una precedente risposta da questo modulo, se applicabile) per determinare le richieste disponibili e i campi usati da queste.",
+       "api-help-authmanagerhelper-preservestate": "Conserva lo stato da un precedente tentativo di accesso non riuscito, se possibile.",
+       "api-help-authmanagerhelper-returnurl": "URL di ritorno per i flussi di autenticazione di terze parti, deve essere assoluto. E' necessario fornirlo, oppure va fornito <var>$1continue</var>.\n\nAlla ricezione di una risposta <samp>REDIRECT</samp>, in genere si apre un browser o una vista web all'URL specificato <samp>redirecttarget</samp> per un flusso di autenticazione di terze parti. Quando questo è completato, la terza parte invierà il browser o la vista web a questo URL. Dovresti estrarre qualsiasi parametro POST o della richiesta dall'URL e passarli come un request <var>$1continue</var> a questo modulo API.",
+       "api-help-authmanagerhelper-additional-params": "Questo modulo accetta parametri aggiuntivi a seconda delle richieste di autenticazione disponibili. Utilizza <kbd>[[Special:ApiHelp/query+authmanagerinfo|action=query&meta=authmanagerinfo]]</kbd> con <kbd>amirequestsfor=$1</kbd> (o una precedente risposta da questo modulo, se applicabile) per determinare le richieste disponibili e i campi usati da queste.",
        "api-credits-header": "Crediti"
 }
index 04e9a43..5626b70 100644 (file)
@@ -2,9 +2,13 @@
        "@metadata": {
                "authors": [
                        "AntanO",
-                       "கலைவாணன்"
+                       "கலைவாணன்",
+                       "Info-farmer"
                ]
        },
+       "apihelp-main-param-action": "எச்செயலை செயற்படுத்த",
+       "apihelp-main-param-format": "பெற விரும்பும்  கோப்பு வடிவம்",
+       "apihelp-main-param-requestid": "இங்கு கொடுக்கப்படும் மதிப்பானது, விளைவில் இணையும். கோரிக்கைகளை வேறுபடுத்தப் பயன்படலாம்.",
        "apihelp-import-param-namespace": "இதனைப் பெயர்வெளிக்கு இறக்குமதி செய்யவும். <kbd>$1rootpage</kbd> அளவுருவை மீறச்செய்யும்.",
        "apihelp-import-param-rootpage": "இப்பக்கத்தின் துணைப்பக்கமாக இறக்குமதி செய்யவும். <kbd>$1namespace</kbd> அளவுரு வழங்கப்பட்டிருந்தால் இது புறக்கணிக்கப்படும்.",
        "api-help-source": "மூலம்: $1",
index a525f2e..0802c53 100644 (file)
@@ -10,7 +10,8 @@
                        "Macofe",
                        "Mix Gerder",
                        "Piramidion",
-                       "Andriykopanytsia"
+                       "Andriykopanytsia",
+                       "Максим Підліснюк"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Документація]]\n* [[mw:API:FAQ|ЧаПи]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Список розсилки]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Оголошення API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Баґи і запити]\n</div>\n<strong>Статус:</strong> Усі функції, вказані на цій сторінці, мають працювати, але API далі перебуває в активній розробці і може змінитися у будь-який момент. Підпишіться на [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ список розсилки mediawiki-api-announce], щоб помічати оновлення.\n\n<strong>Хибні запити:</strong> Коли до API надсилаються хибні запити, буде відіслано HTTP-шапку з ключем «MediaWiki-API-Error», а тоді і значення шапки, і код помилки, надіслані назад, будуть встановлені з тим же значенням. Більше інформації див. на [[mw:API:Errors_and_warnings|API: Errors and warnings]].\n\n<strong>Тестування:</strong> Для зручності тестування запитів API, див. [[Special:ApiSandbox]].",
@@ -39,6 +40,7 @@
        "apihelp-block-param-watchuser": "Спостерігати за сторінкою користувача чи IP-адреси і сторінкою обговорення.",
        "apihelp-block-example-ip-simple": "Блокувати IP-адресу <kbd>192.0.2.5</kbd> на три дні з причиною <kbd>First strike</kbd>.",
        "apihelp-block-example-user-complex": "Блокувати користувача<kbd>Vandal</kbd> на невизначений термін з причиною <kbd>Vandalism</kbd> і заборонити створення нових облікових записів та надсилання електронної пошти.",
+       "apihelp-changeauthenticationdata-description": "Зміна параметрів аутентифікації для поточного користувача.",
        "apihelp-checktoken-description": "Перевірити коректність токена з <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.",
        "apihelp-checktoken-param-type": "Тип токена, який тестується.",
        "apihelp-checktoken-param-token": "Токен для тесту.",
        "apihelp-query+siteinfo-paramvalue-prop-variables": "Видає список змінних ID.",
        "apihelp-query+siteinfo-paramvalue-prop-protocols": "Видає список протоколів, дозволених у зовнішніх посиланнях.",
        "apihelp-query+siteinfo-paramvalue-prop-defaultoptions": "Видає значення налаштувань користувача за замовчуванням.",
+       "apihelp-query+siteinfo-paramvalue-prop-uploaddialog": "Повертає конфігурацію діалогу завантаження.",
        "apihelp-query+siteinfo-param-filteriw": "Видати лише локальні або лише нелокальні елементи карти інтервікі.",
        "apihelp-query+siteinfo-param-showalldb": "Перелічити усі сервери баз даних, а не лише той, який робить найбільшу затримку.",
        "apihelp-query+siteinfo-param-numberingroup": "Перераховує кількість користувачів у групах користувачів.",
index a7dd570..04d2524 100644 (file)
@@ -21,6 +21,7 @@
  * @ingroup Cache
  */
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Class representing a list of titles
@@ -116,7 +117,7 @@ class LinkBatch {
         * @return array Mapping PDBK to ID
         */
        public function execute() {
-               $linkCache = LinkCache::singleton();
+               $linkCache = MediaWikiServices::getInstance()->getLinkCache();
 
                return $this->executeInto( $linkCache );
        }
@@ -151,23 +152,26 @@ class LinkBatch {
                        return [];
                }
 
+               $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
                // For each returned entry, add it to the list of good links, and remove it from $remaining
 
                $ids = [];
                $remaining = $this->data;
                foreach ( $res as $row ) {
-                       $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+                       $title = new TitleValue( (int)$row->page_namespace, $row->page_title );
                        $cache->addGoodLinkObjFromRow( $title, $row );
-                       $ids[$title->getPrefixedDBkey()] = $row->page_id;
+                       $pdbk = $titleFormatter->getPrefixedDBkey( $title );
+                       $ids[$pdbk] = $row->page_id;
                        unset( $remaining[$row->page_namespace][$row->page_title] );
                }
 
                // The remaining links in $data are bad links, register them as such
                foreach ( $remaining as $ns => $dbkeys ) {
                        foreach ( $dbkeys as $dbkey => $unused ) {
-                               $title = Title::makeTitle( $ns, $dbkey );
+                               $title = new TitleValue( (int)$ns, $dbkey );
                                $cache->addBadLinkObj( $title );
-                               $ids[$title->getPrefixedDBkey()] = 0;
+                               $pdbk = $titleFormatter->getPrefixedDBkey( $title );
+                               $ids[$pdbk] = 0;
                        }
                }
 
@@ -218,7 +222,7 @@ class LinkBatch {
                        return false;
                }
 
-               $genderCache = GenderCache::singleton();
+               $genderCache = MediaWikiServices::getInstance()->getGenderCache();
                $genderCache->doLinkBatch( $this->data, $this->caller );
 
                return true;
index de44f9b..3fd29f3 100644 (file)
@@ -230,7 +230,9 @@ class LinkCache {
         */
        public function addLinkObj( LinkTarget $nt ) {
                $key = $this->titleFormatter->getPrefixedDBkey( $nt );
-               if ( $this->isBadLink( $key ) || $nt->isExternal() ) {
+               if ( $this->isBadLink( $key ) || $nt->isExternal()
+                       || $nt->inNamespace( NS_SPECIAL )
+               ) {
                        return 0;
                }
                $id = $this->getGoodLinkID( $key );
index b938ff0..9948040 100644 (file)
@@ -21,6 +21,8 @@
  *
  * @file
  */
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\MediaWikiServices;
 
 class ChangesList extends ContextSource {
        /**
@@ -39,6 +41,11 @@ class ChangesList extends ContextSource {
        /** @var BagOStuff */
        protected $watchMsgCache;
 
+       /**
+        * @var LinkRenderer
+        */
+       protected $linkRenderer;
+
        /**
         * Changeslist constructor
         *
@@ -54,6 +61,7 @@ class ChangesList extends ContextSource {
                }
                $this->preCacheMessages();
                $this->watchMsgCache = new HashBagOStuff( [ 'maxKeys' => 50 ] );
+               $this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
        }
 
        /**
@@ -337,8 +345,10 @@ class ChangesList extends ContextSource {
         */
        public function insertLog( &$s, $title, $logtype ) {
                $page = new LogPage( $logtype );
-               $logname = $page->getName()->setContext( $this->getContext() )->escaped();
-               $s .= $this->msg( 'parentheses' )->rawParams( Linker::linkKnown( $title, $logname ) )->escaped();
+               $logname = $page->getName()->setContext( $this->getContext() )->text();
+               $s .= $this->msg( 'parentheses' )->rawParams(
+                       $this->linkRenderer->makeKnownLink( $title, $logname )
+               )->escaped();
        }
 
        /**
@@ -363,9 +373,9 @@ class ChangesList extends ContextSource {
                                'oldid' => $rc->mAttribs['rc_last_oldid']
                        ];
 
-                       $diffLink = Linker::linkKnown(
+                       $diffLink = $this->linkRenderer->makeKnownLink(
                                $rc->getTitle(),
-                               $this->message['diff'],
+                               new HtmlArmor( $this->message['diff'] ),
                                [],
                                $query
                        );
@@ -375,9 +385,9 @@ class ChangesList extends ContextSource {
                } else {
                        $diffhist = $diffLink . $this->message['pipe-separator'];
                        # History link
-                       $diffhist .= Linker::linkKnown(
+                       $diffhist .= $this->linkRenderer->makeKnownLink(
                                $rc->getTitle(),
-                               $this->message['hist'],
+                               new HtmlArmor( $this->message['hist'] ),
                                [],
                                [
                                        'curid' => $rc->mAttribs['rc_cur_id'],
@@ -415,7 +425,7 @@ class ChangesList extends ContextSource {
                        $params = [ 'redirect' => 'no' ];
                }
 
-               $articlelink = Linker::link(
+               $articlelink = $this->linkRenderer->makeLink(
                        $rc->getTitle(),
                        null,
                        [ 'class' => 'mw-changeslist-title' ],
index 1070877..099a295 100644 (file)
@@ -54,7 +54,8 @@ class EnhancedChangesList extends ChangesList {
                // message is set by the parent ChangesList class
                $this->cacheEntryFactory = new RCCacheEntryFactory(
                        $context,
-                       $this->message
+                       $this->message,
+                       $this->linkRenderer
                );
        }
 
@@ -390,9 +391,9 @@ class EnhancedChangesList extends ChangesList {
                } elseif ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
                        $link = '<span class="history-deleted">' . $rcObj->timestamp . '</span> ';
                } else {
-                       $link = Linker::linkKnown(
+                       $link = $this->linkRenderer->makeKnownLink(
                                $rcObj->getTitle(),
-                               $rcObj->timestamp,
+                               new HtmlArmor( $rcObj->timestamp ),
                                [],
                                $params
                        );
@@ -524,26 +525,24 @@ class EnhancedChangesList extends ChangesList {
                        ) {
                                $links['total-changes'] = $nchanges[$n];
                        } else {
-                               $links['total-changes'] = Linker::link(
+                               $links['total-changes'] = $this->linkRenderer->makeKnownLink(
                                        $block0->getTitle(),
-                                       $nchanges[$n],
+                                       new HtmlArmor( $nchanges[$n] ),
                                        [],
                                        $queryParams + [
                                                'diff' => $currentRevision,
                                                'oldid' => $last->mAttribs['rc_last_oldid'],
-                                       ],
-                                       [ 'known', 'noclasses' ]
+                                       ]
                                );
                                if ( $sinceLast > 0 && $sinceLast < $n ) {
-                                       $links['total-changes-since-last'] = Linker::link(
+                                       $links['total-changes-since-last'] = $this->linkRenderer->makeKnownLink(
                                                        $block0->getTitle(),
-                                                       $sinceLastVisitMsg[$sinceLast],
+                                                       new HtmlArmor( $sinceLastVisitMsg[$sinceLast] ),
                                                        [],
                                                        $queryParams + [
                                                                'diff' => $currentRevision,
                                                                'oldid' => $unvisitedOldid,
-                                                       ],
-                                                       [ 'known', 'noclasses' ]
+                                                       ]
                                                );
                                }
                        }
@@ -558,9 +557,9 @@ class EnhancedChangesList extends ChangesList {
                        $params = $queryParams;
                        $params['action'] = 'history';
 
-                       $links['history'] = Linker::linkKnown(
+                       $links['history'] = $this->linkRenderer->makeKnownLink(
                                        $block0->getTitle(),
-                                       $this->message['enhancedrc-history'],
+                                       new HtmlArmor( $this->message['enhancedrc-history'] ),
                                        [],
                                        $params
                                );
@@ -618,9 +617,11 @@ class EnhancedChangesList extends ChangesList {
                if ( $logType ) {
                        $logPage = new LogPage( $logType );
                        $logTitle = SpecialPage::getTitleFor( 'Log', $logType );
-                       $logName = $logPage->getName()->escaped();
+                       $logName = $logPage->getName()->text();
                        $data['logLink'] = $this->msg( 'parentheses' )
-                               ->rawParams( Linker::linkKnown( $logTitle, $logName ) )->escaped();
+                               ->rawParams(
+                                       $this->linkRenderer->makeKnownLink( $logTitle, $logName )
+                               )->escaped();
                } else {
                        $data['articleLink'] = $this->getArticleLink( $rcObj, $rcObj->unpatrolled, $rcObj->watched );
                }
@@ -710,9 +711,10 @@ class EnhancedChangesList extends ChangesList {
                }
 
                $retVal = ' ' . $this->msg( 'parentheses' )
-                               ->rawParams( $rc->difflink . $this->message['pipe-separator'] . Linker::linkKnown(
+                               ->rawParams( $rc->difflink . $this->message['pipe-separator']
+                                       . $this->linkRenderer->makeKnownLink(
                                                $pageTitle,
-                                               $this->message['hist'],
+                                               new HtmlArmor( $this->message['hist'] ),
                                                [],
                                                $query
                                        ) )->escaped();
index 549bc37..2c5c8b1 100644 (file)
@@ -19,6 +19,7 @@
  *
  * @file
  */
+use MediaWiki\Linker\LinkRenderer;
 
 class RCCacheEntryFactory {
 
@@ -28,13 +29,22 @@ class RCCacheEntryFactory {
        /* @var string[] */
        private $messages;
 
+       /**
+        * @var LinkRenderer
+        */
+       private $linkRenderer;
+
        /**
         * @param IContextSource $context
         * @param string[] $messages
+        * @param LinkRenderer $linkRenderer
         */
-       public function __construct( IContextSource $context, $messages ) {
+       public function __construct(
+               IContextSource $context, $messages, LinkRenderer $linkRenderer
+       ) {
                $this->context = $context;
                $this->messages = $messages;
+               $this->linkRenderer = $linkRenderer;
        }
 
        /**
@@ -99,7 +109,7 @@ class RCCacheEntryFactory {
 
                // New unpatrolled pages
                if ( $cacheEntry->unpatrolled && $type == RC_NEW ) {
-                       $clink = Linker::linkKnown( $cacheEntry->getTitle() );
+                       $clink = $this->linkRenderer->makeKnownLink( $cacheEntry->getTitle() );
                // Log entries
                } elseif ( $type == RC_LOG ) {
                        $logType = $cacheEntry->mAttribs['rc_log_type'];
@@ -108,7 +118,7 @@ class RCCacheEntryFactory {
                                $clink = $this->getLogLink( $logType );
                        } else {
                                wfDebugLog( 'recentchanges', 'Unexpected log entry with no log type in recent changes' );
-                               $clink = Linker::link( $cacheEntry->getTitle() );
+                               $clink = $this->linkRenderer->makeLink( $cacheEntry->getTitle() );
                        }
                // Log entries (old format) and special pages
                } elseif ( $cacheEntry->mAttribs['rc_namespace'] == NS_SPECIAL ) {
@@ -116,7 +126,7 @@ class RCCacheEntryFactory {
                        $clink = '';
                // Edits
                } else {
-                       $clink = Linker::linkKnown( $cacheEntry->getTitle() );
+                       $clink = $this->linkRenderer->makeKnownLink( $cacheEntry->getTitle() );
                }
 
                return $clink;
@@ -125,10 +135,12 @@ class RCCacheEntryFactory {
        private function getLogLink( $logType ) {
                $logtitle = SpecialPage::getTitleFor( 'Log', $logType );
                $logpage = new LogPage( $logType );
-               $logname = $logpage->getName()->escaped();
+               $logname = $logpage->getName()->text();
 
                $logLink = $this->context->msg( 'parentheses' )
-                       ->rawParams( Linker::linkKnown( $logtitle, $logname ) )->escaped();
+                       ->rawParams(
+                               $this->linkRenderer->makeKnownLink( $logtitle, $logname )
+                       )->escaped();
 
                return $logLink;
        }
@@ -242,9 +254,9 @@ class RCCacheEntryFactory {
                if ( !$showDiffLinks || !$lastOldid || in_array( $type, $logTypes ) ) {
                        $lastLink = $lastMessage;
                } else {
-                       $lastLink = Linker::linkKnown(
+                       $lastLink = $this->linkRenderer->makeKnownLink(
                                $cacheEntry->getTitle(),
-                               $lastMessage,
+                               new HtmlArmor( $lastMessage ),
                                [],
                                $this->buildDiffQueryParams( $cacheEntry )
                        );
index a374b13..f6a9dc7 100644 (file)
@@ -188,20 +188,11 @@ class IcuCollation extends Collation {
        }
 
        public function getSortKey( $string ) {
-               // intl extension produces non null-terminated
-               // strings. Appending '' fixes it so that it doesn't generate
-               // a warning on each access in debug php.
-               MediaWiki\suppressWarnings();
-               $key = $this->mainCollator->getSortKey( $string ) . '';
-               MediaWiki\restoreWarnings();
-               return $key;
+               return $this->mainCollator->getSortKey( $string );
        }
 
        public function getPrimarySortKey( $string ) {
-               MediaWiki\suppressWarnings();
-               $key = $this->primaryCollator->getSortKey( $string ) . '';
-               MediaWiki\restoreWarnings();
-               return $key;
+               return $this->primaryCollator->getSortKey( $string );
        }
 
        public function getFirstLetter( $string ) {
index bd4238c..07b5614 100644 (file)
@@ -933,6 +933,14 @@ class LinksUpdate extends SqlDataUpdate implements EnqueueableDataUpdate {
                $this->mRevision = $revision;
        }
 
+       /**
+        * @since 1.28
+        * @return null|Revision
+        */
+       public function getRevision() {
+               return $this->mRevision;
+       }
+
        /**
         * Set the User who triggered this LinksUpdate
         *
index 799ca58..1bd3f51 100644 (file)
@@ -540,7 +540,7 @@ class MysqlInstaller extends DatabaseInstaller {
 
                        foreach ( $createHostList as $host ) {
                                $fullName = $this->buildFullUserName( $dbUser, $host );
-                               if ( !$this->userDefinitelyExists( $dbUser, $host ) ) {
+                               if ( !$this->userDefinitelyExists( $host, $dbUser ) ) {
                                        try {
                                                $this->db->begin( __METHOD__ );
                                                $this->db->query( "CREATE USER $fullName IDENTIFIED BY $escPass", __METHOD__ );
index b7653be..9711496 100644 (file)
@@ -244,6 +244,7 @@ class RefreshLinksJob extends Job {
                        // Needed by things like Echo notifications which need
                        // to know which user caused the links update
                        if ( $update instanceof LinksUpdate ) {
+                               $update->setRevision( $revision );
                                if ( !empty( $this->params['triggeringUser'] ) ) {
                                        $userInfo = $this->params['triggeringUser'];
                                        if ( $userInfo['userId'] ) {
index cb3acce..432dcb2 100644 (file)
@@ -52,13 +52,6 @@ class LinkRenderer {
         */
        private $expandUrls = false;
 
-       /**
-        * Whether extra classes should be added
-        *
-        * @var bool
-        */
-       private $noClasses = false;
-
        /**
         * @var int
         */
@@ -111,20 +104,6 @@ class LinkRenderer {
                return $this->expandUrls;
        }
 
-       /**
-        * @param bool $no
-        */
-       public function setNoClasses( $no ) {
-               $this->noClasses = $no;
-       }
-
-       /**
-        * @return bool
-        */
-       public function getNoClasses() {
-               return $this->noClasses;
-       }
-
        /**
         * @param int $threshold
         */
@@ -172,9 +151,6 @@ class LinkRenderer {
         */
        private function getLegacyOptions( $isKnown ) {
                $options = [ 'stubThreshold' => $this->stubThreshold ];
-               if ( $this->noClasses ) {
-                       $options[] = 'noclasses';
-               }
                if ( $this->forceArticlePath ) {
                        $options[] = 'forcearticlepath';
                }
@@ -249,14 +225,18 @@ class LinkRenderer {
        }
 
        /**
+        * If you have already looked up the proper CSS classes using Linker::getLinkColour()
+        * or some other method, use this to avoid looking it up again.
+        *
         * @param LinkTarget $target
         * @param string|HtmlArmor|null $text
+        * @param string $classes CSS classes to add
         * @param array $extraAttribs
         * @param array $query
         * @return string
         */
-       public function makeKnownLink(
-               LinkTarget $target, $text = null, array $extraAttribs = [], array $query = []
+       public function makePreloadedLink(
+               LinkTarget $target, $text = null, $classes, array $extraAttribs = [], array $query = []
        ) {
                // Run begin hook
                $ret = $this->runBeginHook( $target, $text, $extraAttribs, $query, true );
@@ -265,22 +245,7 @@ class LinkRenderer {
                }
                $target = $this->normalizeTarget( $target );
                $url = $this->getLinkURL( $target, $query );
-               $attribs = [];
-               if ( !$this->noClasses ) {
-                       $classes = [];
-                       if ( $target->isExternal() ) {
-                               $classes[] = 'extiw';
-                       }
-                       $title = Title::newFromLinkTarget( $target );
-                       $colour = Linker::getLinkColour( $title, $this->stubThreshold );
-                       if ( $colour !== '' ) {
-                               $classes[] = $colour;
-                       }
-                       if ( $classes ) {
-                               $attribs['class'] = implode( ' ', $classes );
-                       }
-               }
-
+               $attribs = [ 'class' => $classes ];
                $prefixedText = $this->titleFormatter->getPrefixedText( $target );
                if ( $prefixedText !== '' ) {
                        $attribs['title'] = $prefixedText;
@@ -297,6 +262,34 @@ class LinkRenderer {
                return $this->buildAElement( $target, $text, $attribs, true );
        }
 
+       /**
+        * @param LinkTarget $target
+        * @param string|HtmlArmor|null $text
+        * @param array $extraAttribs
+        * @param array $query
+        * @return string
+        */
+       public function makeKnownLink(
+               LinkTarget $target, $text = null, array $extraAttribs = [], array $query = []
+       ) {
+               $classes = [];
+               if ( $target->isExternal() ) {
+                       $classes[] = 'extiw';
+               }
+               $colour = Linker::getLinkColour( $target, $this->stubThreshold );
+               if ( $colour !== '' ) {
+                       $classes[] = $colour;
+               }
+
+               return $this->makePreloadedLink(
+                       $target,
+                       $text,
+                       $classes ? implode( ' ', $classes ) : '',
+                       $extraAttribs,
+                       $query
+               );
+       }
+
        /**
         * @param LinkTarget $target
         * @param string|HtmlArmor|null $text
@@ -326,7 +319,7 @@ class LinkRenderer {
                }
 
                $url = $this->getLinkURL( $target, $query );
-               $attribs = $this->noClasses ? [] : [ 'class' => 'new' ];
+               $attribs = [ 'class' => 'new' ];
                $prefixedText = $this->titleFormatter->getPrefixedText( $target );
                if ( $prefixedText !== '' ) {
                        // This ends up in parser cache!
index 3a30772..7124be1 100644 (file)
@@ -67,10 +67,6 @@ class LinkRendererFactory {
        public function createFromLegacyOptions( array $options ) {
                $linkRenderer = $this->create();
 
-               if ( in_array( 'noclasses', $options, true ) ) {
-                       $linkRenderer->setNoClasses( true );
-               }
-
                if ( in_array( 'forcearticlepath', $options, true ) ) {
                        $linkRenderer->setForceArticlePath( true );
                }
index 8575e69..1c6f404 100644 (file)
@@ -287,9 +287,10 @@ class LinkHolderArray {
                $colours = [];
                $linkCache = LinkCache::singleton();
                $output = $this->parent->getOutput();
+               $linkRenderer = $this->parent->getLinkRenderer();
+               $threshold = $linkRenderer->getStubThreshold();
 
                $dbr = wfGetDB( DB_SLAVE );
-               $threshold = $this->parent->getOptions()->getStubThreshold();
 
                # Sort by namespace
                ksort( $this->internals );
@@ -352,9 +353,6 @@ class LinkHolderArray {
                                $pdbk = $title->getPrefixedDBkey();
                                $linkCache->addGoodLinkObjFromRow( $title, $s );
                                $output->addLink( $title, $s->page_id );
-                               # @todo FIXME: Convoluted data flow
-                               # The redirect status and length is passed to getLinkColour via the LinkCache
-                               # Use formal parameters instead
                                $colours[$pdbk] = Linker::getLinkColour( $title, $threshold );
                                // add id to the extension todolist
                                $linkcolour_ids[$s->page_id] = $pdbk;
@@ -387,6 +385,8 @@ class LinkHolderArray {
                                }
                                if ( $displayText === '' ) {
                                        $displayText = null;
+                               } else {
+                                       $displayText = new HtmlArmor( $displayText );
                                }
                                if ( !isset( $colours[$pdbk] ) ) {
                                        $colours[$pdbk] = 'new';
@@ -395,15 +395,16 @@ class LinkHolderArray {
                                if ( $colours[$pdbk] == 'new' ) {
                                        $linkCache->addBadLinkObj( $title );
                                        $output->addLink( $title, 0 );
-                                       $type = [ 'broken' ];
+                                       $link = $linkRenderer->makeBrokenLink(
+                                               $title, $displayText, $attribs, $query
+                                       );
                                } else {
-                                       if ( $colours[$pdbk] != '' ) {
-                                               $attribs['class'] = $colours[$pdbk];
-                                       }
-                                       $type = [ 'known', 'noclasses' ];
+                                       $link = $linkRenderer->makePreloadedLink(
+                                               $title, $displayText, $colours[$pdbk], $attribs, $query
+                                       );
                                }
-                               $replacePairs[$searchkey] = Linker::link( $title, $displayText,
-                                               $attribs, $query, $type );
+
+                               $replacePairs[$searchkey] = $link;
                        }
                }
                $replacer = new HashtableReplacer( $replacePairs, 1 );
@@ -429,11 +430,12 @@ class LinkHolderArray {
                # Make interwiki link HTML
                $output = $this->parent->getOutput();
                $replacePairs = [];
-               $options = [
-                       'stubThreshold' => $this->parent->getOptions()->getStubThreshold(),
-               ];
+               $linkRenderer = $this->parent->getLinkRenderer();
                foreach ( $this->interwikis as $key => $link ) {
-                       $replacePairs[$key] = Linker::link( $link['title'], $link['text'], [], [], $options );
+                       $replacePairs[$key] = $linkRenderer->makeLink(
+                               $link['title'],
+                               new HtmlArmor( $link['text'] )
+                       );
                        $output->addInterwikiLink( $link['title'] );
                }
                $replacer = new HashtableReplacer( $replacePairs, 1 );
@@ -573,9 +575,6 @@ class LinkHolderArray {
                                                $entry['pdbk'] = $varPdbk;
 
                                                // set pdbk and colour
-                                               # @todo FIXME: Convoluted data flow
-                                               # The redirect status and length is passed to getLinkColour via the LinkCache
-                                               # Use formal parameters instead
                                                $colours[$varPdbk] = Linker::getLinkColour( $variantTitle, $threshold );
                                                $linkcolour_ids[$s->page_id] = $pdbk;
                                        }
index b5e5d80..17098f7 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  * @ingroup Parser
  */
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\MediaWikiServices;
 
 /**
  * @defgroup Parser Parser
@@ -248,6 +250,11 @@ class Parser {
        /** @var SectionProfiler */
        protected $mProfiler;
 
+       /**
+        * @var LinkRenderer
+        */
+       protected $mLinkRenderer;
+
        /**
         * @param array $conf
         */
@@ -888,6 +895,24 @@ class Parser {
                return $this->mPreprocessor;
        }
 
+       /**
+        * Get a LinkRenderer instance to make links with
+        *
+        * @since 1.28
+        * @return LinkRenderer
+        */
+       public function getLinkRenderer() {
+               if ( !$this->mLinkRenderer ) {
+                       $this->mLinkRenderer = MediaWikiServices::getInstance()
+                               ->getLinkRendererFactory()->create();
+                       $this->mLinkRenderer->setStubThreshold(
+                               $this->getOptions()->getStubThreshold()
+                       );
+               }
+
+               return $this->mLinkRenderer;
+       }
+
        /**
         * Replaces all occurrences of HTML-style comments and the given tags
         * in the text with a random marker and returns the next text. The output
@@ -2329,7 +2354,7 @@ class Parser {
                        # batch file existence checks for NS_FILE and NS_MEDIA
                        if ( $iw == '' && $nt->isAlwaysKnown() ) {
                                $this->mOutput->addLink( $nt );
-                               $s .= $this->makeKnownLinkHolder( $nt, $text, [], $trail, $prefix );
+                               $s .= $this->makeKnownLinkHolder( $nt, $text, $trail, $prefix );
                        } else {
                                # Links will be added to the output link list after checking
                                $s .= $holders->makeHolder( $nt, $text, [], $trail, $prefix );
@@ -2347,22 +2372,20 @@ class Parser {
         *
         * @param Title $nt
         * @param string $text
-        * @param array|string $query
         * @param string $trail
         * @param string $prefix
         * @return string HTML-wikitext mix oh yuck
         */
-       public function makeKnownLinkHolder( $nt, $text = '', $query = [], $trail = '', $prefix = '' ) {
+       protected function makeKnownLinkHolder( $nt, $text = '', $trail = '', $prefix = '' ) {
                list( $inside, $trail ) = Linker::splitTrail( $trail );
 
-               if ( is_string( $query ) ) {
-                       $query = wfCgiToArray( $query );
-               }
                if ( $text == '' ) {
                        $text = htmlspecialchars( $nt->getPrefixedText() );
                }
 
-               $link = Linker::linkKnown( $nt, "$prefix$text$inside", [], $query );
+               $link = $this->getLinkRenderer()->makeKnownLink(
+                       $nt, new HtmlArmor( "$prefix$text$inside" )
+               );
 
                return $this->armorLinks( $link ) . $trail;
        }
index bd6b08f..0b5e70b 100644 (file)
@@ -652,37 +652,26 @@ abstract class LoginSignupSpecialPage extends AuthManagerSpecialPage {
                                if ( $wgLoginLanguageSelector && $this->mLanguage ) {
                                        $linkq .= '&uselang=' . $this->mLanguage;
                                }
-                               $createOrLoginHref = $linkTitle->getLocalURL( $linkq );
-
-                               if ( $this->getUser()->isLoggedIn() ) {
-                                       $createOrLoginHtml = Html::rawElement( 'div',
-                                               [ 'class' => 'mw-ui-vform-field' ],
-                                               Html::element( 'a',
-                                                       [
-                                                               'id' => 'mw-createaccount-join',
-                                                               'href' => $createOrLoginHref,
-                                                               // put right after all auth inputs in the tab order
-                                                               'tabindex' => 100,
-                                                       ],
-                                                       $this->msg( 'userlogin-createanother' )->escaped()
-                                               )
-                                       );
-                               } else {
-                                       $createOrLoginHtml = Html::rawElement( 'div',
-                                               [ 'id' => 'mw-createaccount-cta',
-                                                       'class' => 'mw-ui-vform-field' ],
-                                               $this->msg( 'userlogin-noaccount' )->escaped()
-                                               . Html::element( 'a',
-                                                       [
-                                                               'id' => 'mw-createaccount-join',
-                                                               'href' => $createOrLoginHref,
-                                                               'class' => 'mw-ui-button',
-                                                               'tabindex' => 100,
-                                                       ],
-                                                       $this->msg( 'userlogin-joinproject' )->escaped()
-                                               )
-                                       );
-                               }
+
+                               $loggedIn = $this->getUser()->isLoggedIn();
+                               $createOrLoginHtml = Html::rawElement( 'div',
+                                       [ 'id' => 'mw-createaccount' . ( !$loggedIn ? '-cta' : '' ),
+                                               'class' => ( $loggedIn ? 'mw-form-related-link-container' : 'mw-ui-vform-field' ) ],
+                                       ( $loggedIn ? '' : $this->msg( 'userlogin-noaccount' )->escaped() )
+                                       . Html::element( 'a',
+                                               [
+                                                       'id' => 'mw-createaccount-join' . ( $loggedIn ? '-loggedin' : '' ),
+                                                       'href' => $linkTitle->getLocalURL( $linkq ),
+                                                       'class' => ( $loggedIn ? '' : 'mw-ui-button' ),
+                                                       'tabindex' => 100,
+                                               ],
+                                               $this->msg(
+                                                       ( $this->getUser()->isLoggedIn() ?
+                                                               'userlogin-createanother' :
+                                                               'userlogin-joinproject'
+                                                       ) )->escaped()
+                                       )
+                               );
                                $form->addFooterText( $createOrLoginHtml );
                        }
                }
index 38e9ecd..a937e75 100644 (file)
@@ -105,10 +105,16 @@ class MediaWikiTitleCodec implements TitleFormatter, TitleParser {
         */
        public function formatTitle( $namespace, $text, $fragment = '', $interwiki = '' ) {
                if ( $namespace !== false ) {
-                       $namespace = $this->getNamespaceName( $namespace, $text );
+                       // Try to get a namespace name, but fallback
+                       // to empty string if it doesn't exist
+                       try {
+                               $nsName = $this->getNamespaceName( $namespace, $text );
+                       } catch ( InvalidArgumentException $e ) {
+                               $nsName = '';
+                       }
 
-                       if ( $namespace !== '' ) {
-                               $text = $namespace . ':' . $text;
+                       if ( $namespace !== 0 ) {
+                               $text = $nsName . ':' . $text;
                        }
                }
 
index fa75a26..dfd246e 100644 (file)
@@ -45,7 +45,7 @@
        "tog-ccmeonemails": "हमरा द्वारा अन्य सदस्यन के भेजल गइल ईमेल के कॉपी हमरो के भेजल जाय",
        "tog-diffonly": "अन्तर देखावत समय अंतर की नीचे पन्ना के सामग्री मत देखावल जाव।",
        "tog-showhiddencats": "छिपल श्रेणियन के भी देखावल जाय",
-       "tog-norollbackdiff": "सà¤\82पादन à¤°à¥\8bलबà¥\88à¤\95 à¤\95à¤\87ला à¤\95à¥\87 à¤¬à¤¾à¤¦ à¤\85नà¥\8dतर मत देखावल जाव",
+       "tog-norollbackdiff": "रà¥\8bलबà¥\88à¤\95 à¤\95à¤\87ला à¤\95à¥\87 à¤¬à¤¾à¤¦ à¤\85à¤\82तर मत देखावल जाव",
        "tog-useeditwarning": "जो हम कौनों पन्ना पर संपादन करत घरी परिवर्तन के बिना सहेजले छोड़ देईं त हमके खबर कइल जाव",
        "tog-prefershttps": "जब खाता में लॉगिन करीं त हमेशा सुरक्षित कनेक्शन के प्रयोग कइल जाय",
        "underline-always": "हमेशा",
        "morenotlisted": "इ सूची पूर्ण नइखे।",
        "mypage": "पन्ना",
        "mytalk": "हमार बातचीत पन्ना",
-       "anontalk": "à¤\87 à¤\86à¤\87॰पà¥\80 à¤\96ातिर à¤µà¤¾à¤°à¥\8dता",
+       "anontalk": "बातà¤\9aà¥\80त",
        "navigation": "नेविगेशन",
        "and": "&#32;अउर",
        "qbfind": "खोज",
        "nstab-template": "टेम्पलेट",
        "nstab-help": "मदद पन्ना",
        "nstab-category": "श्रेणी",
+       "mainpage-nstab": "मुख्य पन्ना",
        "nosuchaction": "अईसन कौनो कार्रवाई नाहि",
        "nosuchactiontext": "इ यू॰आर॰एल द्वारा निर्दिष्ट क्रिया अवैध बा।\nरउआ यू॰आर॰एल गलत लिखले होखब, या कउनो गलत कड़ी के प्रयोग कइले होखब।\nइ {{SITENAME}} के सॉफ़्टवेयर में त्रुटि भी हो सकत बा।",
        "nosuchspecialpage": "अईसन कौनो ख़ाश पन्ना नाहि",
        "laggedslavemode": "'''चेतावनी:''' इ पन्ना पर हाल के बदलाव ना होखे के आशंका बा।",
        "readonly": "डेटाबेस लॉक बा",
        "enterlockreason": "लॉक करे के कारण दिहीं, साथे लॉक खुले के समय के लगभग आकलन दिहीं।",
-       "readonlytext": "शायद à¤®à¥\87à¤\82à¤\9fà¥\87ननà¥\8dस à¤\95à¥\87 à¤\9aलतà¥\87 à¤¡à¤¾à¤\9fाबà¥\87स à¤¨à¤¯à¤¾ à¤¸à¤\82पादन à¤\86 à¤\85नà¥\8dय à¤¬à¤¦à¤²à¤¾à¤µ à¤¸à¥\87 à¤²à¥\89à¤\95 à¤\95रल à¤\97à¤\88ल à¤¬à¤¾, à¤\9cà¥\87à¤\95रा à¤¬à¤¾à¤¦ à¤\87 à¤\95à¥\87 à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤¸à¥\8dथितà¥\80 à¤®à¥\87à¤\82 à¤\86 à¤\9cायà¥\87 à¤\95à¥\87 à¤\9aाहà¥\80à¤\82।\n\nà¤\9cà¤\89न à¤ªà¥\8dरबनà¥\8dधà¤\95 à¤\87 à¤\95à¥\87 à¤²à¥\89à¤\95 à¤\95à¤\87लà¥\87 à¤°à¤¹à¤²à¤¨ à¤\89 à¤¹à¤\87 à¤\95ारण à¤¦à¥\87हलà¥\87 à¤¬à¤¾à¤¡à¤¼à¤¨: $1",
+       "readonlytext": "डाà¤\9fाबà¥\87स à¤¨à¤¯à¤¾ à¤¸à¤\82पादन à¤\86 à¤\85नà¥\8dय à¤¬à¤¦à¤²à¤¾à¤µ à¤\96ातिर à¤²à¥\89à¤\95 à¤\95रल à¤\97à¤\87ल à¤¬à¤¾, à¤¶à¤¾à¤¯à¤¦ à¤°à¥\81à¤\9fà¥\80न à¤®à¥\87à¤\82à¤\9fà¥\87ननà¥\8dस à¤\95à¥\87 à¤\9aलतà¥\87, à¤\9cà¥\87à¤\95रा à¤¬à¤¾à¤¦ à¤\8f à¤\95à¥\87 à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤¸à¥\8dथितà¥\80 à¤®à¥\87à¤\82 à¤\86 à¤\9cायà¥\87 à¤\95à¥\87 à¤\9aाहà¥\80à¤\82।\n\nà¤\9cà¤\89न à¤¸à¤¿à¤¸à¥\8dà¤\9fम à¤ªà¥\8dरबà¤\82धà¤\95 à¤\8fह à¤\95à¥\87 à¤²à¥\89à¤\95 à¤\95à¤\87लà¥\87 à¤°à¤¹à¤²à¤¨ à¤\95ारण à¤¦à¥\87हलà¥\87 à¤¬à¤¾à¤¡à¤¼à¤¨ à¤\95ि: $1",
        "missing-article": "डेटाबास ऊ पन्ना के पाठ्य के ना खोज पाईल जौन ई के खोजे के रहल, नामित \"$1\" $2.\nई सब साधारणत: निम्नलिखीत अप्रचलित अन्तर अथवा एगो पन्ना पर इतिहास के लिंक जौन मिटा दिहल गईल बा के कारण भईल।\n\nयदि ई बात नईखे, त हो सकत बा सॉफ्टवेयर में बग पावत होखब।\nकृपया ई एगो  [[Special:ListUsers/sysop|प्रबन्धक]] के यू आर एल के बारे में एगो नोट बनाके खबर करीं।",
        "missingarticle-rev": "(संशोधन#: $1)",
        "missingarticle-diff": "(अंतर: $1, $2)",
        "nocookieslogin": "{{SITENAME}} प्रयोगकर्ता लोग के खाता में प्रवेश करावे खातिर कुकिज के प्रयोग करेला।\nराउर कुकिज असक्षम बा।\nकृपया उ के सक्षम करीं आ फिर से कोशिश करीं",
        "nocookiesfornew": "स्रोत के पुष्टि ना हो पावे के कारण इ खाता निर्मित ना करल गइल। \nसुनिश्चित करीं कि रउआ कुकीज़ सक्षम कइले बानी, पृष्ठ के पुनः लोड करीं आ पुनः प्रयास करीं।",
        "noname": "रउआ उपयुक्त प्रयोगकर्ता नाम नईखीं निर्दिष्ट कईले।",
-       "loginsuccesstitle": "à¤\96ाता à¤ªà¥\8dरवà¥\87श à¤®à¥\87à¤\82 à¤¸à¤«à¤²",
+       "loginsuccesstitle": "लà¥\89à¤\97िन à¤ªà¥\82रा",
        "loginsuccess": "''' \"$1\" के रुप में रउआ {{SITENAME}} में अब प्रवेश कर चुकल बानी।'''",
-       "nosuchuser": "\"$1\" à¤¨à¤¾à¤® à¤¸à¥\87 à¤\95à¥\8cनà¥\8b à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता à¤¨à¤\88à¤\96न।\nपà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता à¤¨à¤¾à¤® à¤¸à¤\82वà¥\87दनशà¥\80ल à¤®à¤¾à¤®à¤²à¤¾ à¤¬à¤¾à¥¤\nशबà¥\8dद-वरà¥\8dतनà¥\80 à¤\95à¥\87 à¤\9cाà¤\81à¤\9a à¤\95रà¥\80à¤\82, à¤\86 à¤\9aाहà¥\87 [[Special:CreateAccount|एगो नया खाता बनाईं]]।",
+       "nosuchuser": "\"$1\" à¤¨à¤¾à¤\81व à¤\95à¥\87 à¤\95à¥\8cनà¥\8b à¤ªà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता à¤¨à¤\87à¤\96न।\nपà¥\8dरयà¥\8bà¤\97à¤\95रà¥\8dता à¤¨à¤¾à¤® à¤¸à¤\82वà¥\87दनशà¥\80ल à¤®à¤¾à¤®à¤²à¤¾ à¤¬à¤¾à¥¤\nशबà¥\8dद à¤\86 à¤\87सà¥\8dपà¥\87लिà¤\82à¤\97 à¤\95à¥\87 à¤\9cाà¤\81à¤\9a à¤\95रà¥\80à¤\82, à¤¯à¤¾ [[Special:CreateAccount|एगो नया खाता बनाईं]]।",
        "nosuchusershort": "ई नाम से कौनो प्रयोगकर्ता नईखन \"$1\".\nआपन शब्द-वर्तनी के जाँच करीं।",
        "nouserspecified": "रउआ एगो प्रयोगकर्ता नाम निर्दिष्ट करे के बा।",
        "login-userblocked": "ई प्रयोगकर्ता के खाता निष्क्रिय हो चुकल बा। प्रवेश के आज्ञा नईखे।",
        "wrongpasswordempty": "गुप्त-शब्द खाली बा। कृपया फिर से कोशिश करीं।",
        "passwordtooshort": "गुप्त-शब्द कम से कम {{PLURAL:$1|1 अक्षर|$1 अक्षर}} के होवे के चाहीं।",
        "passwordtoolong": "गुप्त-शब्द {{PLURAL:$1|$1 अक्षर}} से अधिक लमहर नइखे हो सकत।",
+       "passwordtoopopular": "अक्सरहा बीछल जाए वाला पासवर्ड ना इस्तेमाल होखी। कौनों अउरी खास अलग टाइप के पासवर्ड चुनीं।",
        "password-name-match": "राउर गुप्त-शब्द राउर प्रयोगकर्ता नाम से अलग होवे के चाहीं।",
        "password-login-forbidden": "इस सदस्यनाम आ गुप्तशब्द के प्रयोग वर्जित बा।",
        "mailmypassword": "गुप्तशब्द रिसेट करीं",
index ac8d893..f9b4c86 100644 (file)
        "versionrequiredtext": "ये पाना प्रयोग गर्नका लागि MediaWiki $1 संस्करण चाहिन्छ ।\nहेर  [[Special:Version|version page]]",
        "ok": "भयो",
        "retrievedfrom": " \"$1\" बठे निकालिया",
-       "youhavenewmessages": "तमखी लेखा($2)मी $1 छ ।",
+       "youhavenewmessages": "तमखी लेखा($3)मी $1 ($2) छ ।",
        "youhavenewmessagesfromusers": "तमखी लेखा {{PLURAL:$3|प्रयोगकर्ता|$3 प्रयोगकर्तान}}($2)बठे$1",
        "youhavenewmessagesmanyusers": "तमलाई धेरै प्रयोगकर्ताहरू($2) बठे $1 छ ।",
        "newmessageslinkplural": "{{PLURAL:$1|एक नौलो रैबार|999=नौला रैबारहरू}}",
        "viewsource-title": " $1 को स्रोत हेर",
        "actionthrottled": "कार्य रोकिईयो",
        "actionthrottledtext": "स्पामको रोकथामको लागि , तमीलाई यो कार्य नापै समयमी मैथै पटक गद्दाबठे सिमित गरियाको छ, र तमीले आफ्नो सिमा पार गरिसक्याछौ ।\nकृपया केही मिनेट पछि पुन: प्रयास गर  ।",
+       "protectedpagetext": "यो पृष्ठ सम्पादन हुनबठे बचाउन सम्पादनमी तथा अन्य कार्यमी रोक लगाइया छ।",
        "viewsourcetext": "तम ये पृष्ठको स्रोत हेद्दु सकुन्छौ और उईको नक्कल उताद्दु सकुन्छौ |",
        "viewyourtext": "यै पानामी रह्याका '''तमरा सम्पादनहरू''' हेद्द या प्रतिलिपी गद्द सक्द्या हौ :",
        "editinginterface": "<strong>चेतावनी:</strong> तमी यै पानालाई सम्पादन गद्द लाग्याछौ, जनले सफ्टवेयरको लागि \nइन्टरफेस सामग्रीहरू प्रदान गरन्छ।\nयै पानामी गरियाको परिवर्तनले यै विकिमी अरु प्रयोगकर्तानको इन्टरफेसको प्रदर्शनमी प्रभाव पडन्छ ।",
        "namespaceprotected": "तमलाई '''$1'''  नेमस्पेसमी रह्याका पानाहरू सम्पादन गद्या अनुमति छैन ।",
        "customcssprotected": "तमलाई यो  पानो सम्पादन गद्दे अनुमति छैन, किनकी यैमी कुनै अर्को प्रयोगकर्ताको व्यक्तिगत अभिरुचीहरू संग्रहित छन् ।",
        "customjsprotected": "तमलाई यो जाभास्कृप्ट पानो सम्पादन गद्दे अनुमति छैन, किनकी यैमी कुनै अर्को प्रयोगकर्ताको व्यक्तिगत अभिरुचीहरू संग्रहित छन् ।",
+       "mycustomcssprotected": "ये CSS पृष्ठ सम्पादन गद्दका लागि तमलाइ अनुमति छैन ।",
+       "mycustomjsprotected": "ये JavaScript पृष्ठ सम्पादन गद्द लागि तमलाई अनुमति छैन।",
+       "myprivateinfoprotected": "तमसँग तमरो निजी जानकारीहरू सम्पादन ग्द्दे अनुमती छैन |",
+       "mypreferencesprotected": "तमसंग तमरो अभिरुचीहरू सम्पादन ग्द्दे अनुमती छैन |",
        "ns-specialprotected": "विशेष पृष्ठहरू सम्पादन अद्दु नाइँ सकिनो।",
        "titleprotected": "[[User:$1|$1]]द्वारा ये शीर्षक निर्माणहुनबठे जोगाइया छ।\nकारण <em>$2</em> हो ।",
        "filereadonlyerror": "फाइल \"$1\" लाई परिवर्तन अद्दु नाइँ सकिनो क्याईकि फाइल भण्डार  \"$2\" केवल पढ्ने स्थिति (read-only mode)मी छ।\n\nयेलाई सुरक्षित गर्ने प्रवन्धकले  यो कारण दियाकाछन् : ''$3''।",
index 5398858..f135f40 100644 (file)
        "cannotauth-not-allowed-title": "Permission denied",
        "cannotauth-not-allowed": "You are not allowed to use this page",
        "changecredentials" : "Change credentials",
-       "changecredentials-submit": "Change",
+       "changecredentials-submit": "Change credentials",
        "changecredentials-submit-cancel": "Cancel",
        "changecredentials-invalidsubpage": "$1 is not a valid credential type.",
        "changecredentials-success": "Your credentials have been changed.",
        "removecredentials" : "Remove credentials",
-       "removecredentials-submit": "Remove",
+       "removecredentials-submit": "Remove credentials",
        "removecredentials-submit-cancel": "Cancel",
        "removecredentials-invalidsubpage": "$1 is not a valid credential type.",
        "removecredentials-success": "Your credentials have been removed.",
index f085e45..d26f674 100644 (file)
@@ -50,7 +50,8 @@
                        "Olimar",
                        "Mikahama",
                        "01miki10",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "BiscuitMan"
                ]
        },
        "tog-underline": "Linkkien alleviivaus:",
        "noname": "Et ole määritellyt kelvollista käyttäjänimeä.",
        "loginsuccesstitle": "Olet kirjautunut sisään",
        "loginsuccess": "'''Olet kirjautunut sivustolle {{SITENAME}} käyttäjänä $1.'''",
-       "nosuchuser": "Käyttäjää ”$1” ei ole olemassa. Nimet ovat kirjainkoosta riippuvaisia. Tarkista kirjoititko nimen oikein, tai [[Special:CreateAccount|luo uusi käyttäjätunnus]].",
+       "nosuchuser": "Käyttäjää \"$1\" ei ole olemassa.\nNimet ovat kirjainkoosta riippuvaisia. \nTarkista kirjoititko nimen oikein, tai [[Special:CreateAccount|luo uusi käyttäjätunnus]].",
        "nosuchusershort": "Käyttäjää nimeltä ”$1” ei ole. Kirjoititko nimen oikein?",
        "nouserspecified": "Käyttäjätunnusta ei ole määritelty.",
        "login-userblocked": "Tämä käyttäjä on estetty. Kirjautuminen ei ole sallittua.",
index 7842756..6a17b7b 100644 (file)
        "confirm-unwatch-button": "Valider",
        "confirm-unwatch-top": "Supprimer cette page de votre liste de suivi ?",
        "confirm-rollback-button": "Valider",
-       "confirm-rollback-top": "Annuler les modifications de cette page?",
+       "confirm-rollback-top": "Révoquer les modifications de cette page?",
        "semicolon-separator": "&nbsp;;&#32;",
        "colon-separator": "&nbsp;:&#32;",
        "percent": "$1&#160;%",
index 2264280..5d9c38c 100644 (file)
@@ -86,7 +86,7 @@
        "october": "Oktober",
        "november": "Nopember",
        "december": "Desember",
-       "january-gen": "Yanuari",
+       "january-gen": "Januwari",
        "february-gen": "Peburuwari",
        "march-gen": "Maret",
        "april-gen": "April",
        "broken-file-category": "Halaamani wolo pranala berkas ma lorusa",
        "about": "Tomimbihu",
        "article": "Tuwango halaman",
-       "newwindow": "huowa to janela bohu",
+       "newwindow": "hu'owa to janela bohu",
        "cancel": "Batali",
        "moredotdotdot": "Uweewo",
        "morenotlisted": "Daputari boti dipo ganapu",
        "faq": "FAQ",
        "faqpage": "Project:FAQ",
        "actions": "Huhutu",
-       "namespaces": "Ruang tanggulo",
+       "namespaces": "Huwali lo tanggulo",
        "variants": "Varian",
        "navigation-heading": "Menu navigasi",
        "errorpagetitle": "Lotaalawa",
        "print": "Cetaki",
        "view": "Bilohi",
        "view-foreign": "Bilohi to $1",
-       "edit": "Monguba",
+       "edit": "Momoli'o",
        "edit-local": "Ubawa deskripsi lokal",
        "create": "Mohutu",
        "create-local": "Duhengi deskripsi lokal",
        "unprotectthispage": "ubawa dudaha halaman botiye",
        "newpage": "Halaman bohu",
        "talkpage": "Bisalayi halaman boti",
-       "talkpagelinktext": "bisala",
+       "talkpagelinktext": "lo'iya",
        "specialpage": "Halaman uda'a",
        "personaltools": "Pilaakasi lo hihilawo",
        "articlepage": "Bilohi tuango halaman",
        "redirectedfrom": "Pilobale lonto $1",
        "redirectpagesub": "Halaman pilobaleyalo",
        "redirectto": "Mobale ode",
-       "lastmodifiedat": "Halaman botiye iluba pulitiyo $1, $2.",
+       "lastmodifiedat": "Halaman botiye biloli'o pulitiyo $1, $2.",
        "viewcount": "Halaman botiye ma hilu'o {{PLURAL:$1|$1 kali}}.<br />",
        "protectedpage": "Halaman udaha-daha",
        "jumpto": "Lumanti'a ode",
        "currentevents-url": "Project:U yilowali baharu",
        "disclaimers": "Momaahu",
        "disclaimerpage": "Project:Momaahu umum",
-       "edithelp": "Momantu monguba",
+       "edithelp": "Wubodu momoli'o",
        "helppage-top-gethelp": "Tuulungi",
        "mainpage": "Halaman Bungaliyo",
        "mainpage-description": "Halaman bungaliyo",
        "policy-url": "Project:Kebijakan",
        "portal": "Buubu'a leembo'a",
        "portal-url": "Project:Buubu'a lembo'a",
-       "privacy": "Tinepo lo privasi",
-       "privacypage": "Project:Tinepo lo privasi",
+       "privacy": "Tinepo privasi",
+       "privacypage": "Project:Tinepo privasi",
        "badaccess": "Tilala haku momu'o",
        "badaccess-group0": "Yi'o diya o iijini mohutu kalaja u hepohilemu",
        "badaccess-groups": "Huhutu hepohilemu bilatasiyaliyo to pengguna {{PLURAL:$2|lembo'a}}$1.",
        "newmessageslinkplural": "{{PLURAL:$1|tuwawu tahuli bohu|999=tahuli bohu}}",
        "newmessagesdifflinkplural": "{{PLURAL:$1|iluba|999=u iluba}} pulitiyo",
        "youhavenewmessagesmulti": "Yio lootapu tahuli bohu to $1",
-       "editsection": "monguba",
+       "editsection": "boli'a",
        "editold": "monguba",
        "viewsourceold": "Bilohi bungoliyo",
-       "editlink": "monguba",
+       "editlink": "boli'a",
        "viewsourcelink": "Bilohi bungoliyo",
-       "editsectionhint": "Monguba tayadu:$1",
+       "editsectionhint": "Momoli'o tayadu:$1",
        "toc": "Tuwango",
        "showtoc": "popobilehe",
        "hidetoc": "wanto'a",
        "site-atom-feed": "Paalo $1 Atom",
        "page-rss-feed": "Paalo $1 RSS",
        "page-atom-feed": "Paalo $1 Atom",
-       "red-link-title": "$1 (halaman dila sadi-sadia)",
+       "red-link-title": "$1 (halaman diila sadi-sadia)",
        "sort-descending": "Urutiya detibawa",
        "sort-ascending": "Urutiya deyitaato",
        "nstab-main": "Halaman",
        "watchthis": "Dahayi halaman botiye",
        "savearticle": "Tahuwa halaman",
        "showpreview": "Bilohi pratayang",
-       "showdiff": "Popobilohe u lo'ubawa",
+       "showdiff": "Popobilohe u loboli'a",
        "anoneditwarning": "<strong>Mopo'eela:</strong> Yi'o diipo tilumuwo. Alamat IP olemu ma ontonga lo tawu daata wonu yi'o momoli'o. Wonu Yi'o <strong>[$1 tumuwoto log]</strong> meyalo <strong>[$2 mohutu akun]</strong>, u biloli'umu madiatribusikan ode tanggulumu, wolo huna uweewoliyo.",
        "loginreqlink": "tumuwoto log",
        "newarticletext": "Yi'o lodudu'a wumbuta ode halaman diya'a. \nWonu mohutu halaman botiye, ketik tuwango halaman to kotak to tibawa botiye (bilohi [$1 halaman wubodu] ode habari wumbutiyo). \nWonu Yi'o ja sangaja tilumuwota ode halaman botiye, kutiya tombol <strong>mohuwalingo</strong>.",
        "moveddeleted-notice": "Halaman botiye ma yiluluto.\nSebagai referensi, botiya log piloluluta wawu piloheyiya halaman botiye.",
        "viewpagelogs": "Bilohi log lo halaman botiye",
        "currentrev-asof": "Revisi pulitiyo to $1",
-       "revisionasof": "Iluba to $1",
+       "revisionasof": "Biloli'o to $1",
        "revision-info": "Biloli'o per $1 oleh {{GENDER:$6|$2}}$7",
-       "previousrevision": "Iluba yilaluma'o",
+       "previousrevision": "Biloli'o yilaluma'o",
        "nextrevision": "Revisi lapatiyoma'o →",
        "currentrevisionlink": "Revisi pulitiyo",
        "cur": "mst",
        "right-writeapi": "Mopohuna API moluladu",
        "newuserlogpage": "Log ta ohu'uwo bohu",
        "enhancedrc-history": "riwayati",
-       "recentchanges": "Lo'ubawa u bohu",
-       "recentchanges-legend": "Tulawolo boli'o u bohu",
+       "recentchanges": "Boheli loboli'a mola",
+       "recentchanges-legend": "Tulawotolo boheli loboli'a mola",
        "recentchanges-summary": "Mololohe u yilo'ubawa bohu to halaman wiki botiye.",
        "recentchanges-label-newpage": "Monguba utiye mohutu halaman bohu",
-       "recentchanges-label-minor": "Utiye iluba kiki'o",
-       "recentchanges-label-bot": "Longuba utiye kilaraja lo bot",
-       "recentchanges-label-unpatrolled": "U bilili'a botiye diipo pilatroli",
-       "recentchanges-label-plusminus": "Lo'ubawa tu'udu halaman boti to delomo bita",
+       "recentchanges-label-minor": "Utiye biloli'o ngo'idi",
+       "recentchanges-label-bot": "Lomoli'a utiye kilaraja lo bot",
+       "recentchanges-label-unpatrolled": "U biloli'a botiye diipo pilatroli",
+       "recentchanges-label-plusminus": "Loboli'o tu'udu halaman boti to delomo bita",
        "recentchanges-legend-heading": "<strong>Keterangan:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (bilohi olo [[Special:NewPages|list of new pages]])",
-       "rclistfrom": "Popobilohe u lobohuwa lonto $2, $3",
-       "rcshowhideminor": "$1 iluba kiki'o",
+       "rclistfrom": "Popobilohe u loboli'a lonto $2, $3",
+       "rcshowhideminor": "$1 biloli'o ngo'idi",
        "rcshowhideminor-show": "Popobilohe",
        "rcshowhideminor-hide": "Wanto'a",
        "rcshowhidebots": "$1 bot",
        "rcshowhidebots-hide": "Wanto'a",
        "rcshowhideliu": "$1 ta ohu'uwo to daputari",
        "rcshowhideliu-hide": "Wanto'a",
-       "rcshowhideanons": "$1 iluba lo ta ja'otawa",
+       "rcshowhideanons": "$1 biloli'o lo tawu weewo",
        "rcshowhideanons-show": "Popobilohe",
        "rcshowhideanons-hide": "Wanto'a",
-       "rcshowhidemine": "$1 iluba'u",
+       "rcshowhidemine": "$1 biloli'u'u",
        "rcshowhidemine-show": "Popobilohe",
        "rcshowhidemine-hide": "Wanto'a",
        "rclinks": "Popobilohe $1 u yilo'boli'a pulitiyo to delomo $2 dulahu pulitiyo<br />$3",
        "minoreditletter": "k",
        "newpageletter": "B",
        "boteditletter": "b",
-       "rc-change-size-new": "$1 {{PLURAL:$1|bita}} lapato iluba",
+       "rc-change-size-new": "$1 {{PLURAL:$1|bita}} lapato biloli'o",
        "recentchangeslinked": "Lo'ubawa wayitiyo",
-       "recentchangeslinked-toolbox": "Lo'ubawa wayitiyo",
+       "recentchangeslinked-toolbox": "Loboli'o wayitiyo",
        "recentchangeslinked-title": "Lo'ubawa a'aayita wolo $1",
        "recentchangeslinked-summary": "Utiye daputari lo'ubawa to halaman a'ayita wolo halaman tuwawu (meyalo tayadu to kategori tuwawu)\nHalaman to [[Special:Watchlist|he'awasiyamu]] ontonga <strong>cetakiya mohulodu</strong>.",
        "recentchangeslinked-page": "Tanggulo halaman:",
        "rollbacklinkcount": "pohuwalinga $1 {{PLURAL:$1|biloli'o}}",
        "protectlogpage": "Log mopo'aamani",
        "namespace": "Ruangtanggulo",
-       "invert": "Pohuwalinga u pilili",
-       "tooltip-invert": "Centang kotak botiye u mopowanto'o halaman yiloboli'a to delomo huwali lo tanggulo u pilili (wawu huwali lo tanggulo a'ayita wanu dicentang)",
-       "namespace_association": "Huwali tanggulo a'aayita",
-       "tooltip-namespace_association": "Centang halaman botiye u mopowayito huwali lo tanggulo biisalawa meyalo subjek u a'ayita wolo huwali lo tanggulo u pilili.",
+       "invert": "Pohuwalinga tilulawoto",
+       "tooltip-invert": "Centang kotak botiye u mopowanto'o halaman yiloboli'a to delomo huwali lo tanggulo tilulawoto (wawu huwali lo tanggulo a'ayita wanu dicentang)",
+       "namespace_association": "Huwali lo tanggulo a'aayita",
+       "tooltip-namespace_association": "Centang halaman botiye u mopowayito huwali lo tanggulo lo'iyawa meyalo subjek u a'ayita wolo huwali lo tanggulo u tilulawoto.",
        "blanknamespace": "Bungaliyo",
        "contributions": "Kontribusi {{GENDER:$1|Ta ohu'uwo}}",
        "mycontris": "Kontribusi",
        "tooltip-pt-logout": "Lumuwalo log",
        "tooltip-pt-createaccount": "Yi'o popoyingowaliyo mohutu akun wawu tumuwoto log; openu utiye ja wajibu",
        "tooltip-ca-talk": "Tombilu tomimbihu tuwango halaman",
-       "tooltip-ca-edit": "Ubawa halaman botiye",
+       "tooltip-ca-edit": "Boli'a halaman botiye",
        "tooltip-ca-addsection": "Mulai tayade bohu",
        "tooltip-ca-viewsource": "Halaman botiye daha-daha. Yi'o bo mowali momilohe bungo",
-       "tooltip-ca-history": "Iluba pulitiyo to halaman botiye",
+       "tooltip-ca-history": "Biloli'o pulitiyo to halaman botiye",
        "tooltip-ca-move": "Heyiya halaman botiye",
        "tooltip-ca-watch": "Popoduhengama'o halaman botiye to daputari he'awasiyalo",
        "tooltip-search": "Lolohe {{SITENAME}}",
        "tooltip-search-go": "Lolohe halaman tuwawu wolo tanggula delo odiye wonu woluwo",
        "tooltip-search-fulltext": "Lolohe halaman o tulade odiye",
-       "tooltip-p-logo": "Bilehi halamani bungaliyo",
+       "tooltip-p-logo": "Bilohi halaman bungaliyo",
        "tooltip-n-mainpage": "Bilohi halaman bungaliyo",
        "tooltip-n-mainpage-description": "Bilohi halaman bungaliyo",
        "tooltip-n-portal": "Tomimbihu poroyek, wolo u mowali pohutuwomu, to'utonu mololohe u tuwawu",
        "tooltip-n-currentevents": "Lolohe habari tomimbihu u yilowali baharu",
-       "tooltip-n-recentchanges": "Daputari iluba bohu todelomo wiki",
+       "tooltip-n-recentchanges": "Daputari boheli loboli'a mola to delomo wiki botiye",
        "tooltip-n-randompage": "Popobilohe totonula halaman",
-       "tooltip-n-help": "Taambati pololohela yibodu",
-       "tooltip-t-whatlinkshere": "Daputari nga'amila halaman wiki o wumbuta ode halaman boti",
-       "tooltip-t-recentchangeslinked": "U yilo'ubawa baharu to halaman o wumbuta ode halaman botiye",
+       "tooltip-n-help": "Taambati pololohela wubodu",
+       "tooltip-t-whatlinkshere": "Daputari nga'amila halaman wiki owumbuta ode halaman botiye",
+       "tooltip-t-recentchangeslinked": "Boheli loboli'a mola to halaman owumbuta ode halaman botiye",
        "tooltip-feed-atom": "Paalo atom ode halaman botiya",
        "tooltip-t-contributions": "Daputari kontribusi {{GENDER:$1|ta ohu'uwo botiye}}",
        "tooltip-t-upload": "Detohe berkas-berkas",
        "tooltip-t-permalink": "Wumbuta kakali u mopo'opiyohe halaman botiye",
        "tooltip-ca-nstab-main": "Bilohi tuwango halaman",
        "tooltip-ca-nstab-user": "Bilohi halaman pengguna",
-       "tooltip-ca-nstab-special": "Utiye halaman istimewa, wawu ja mowali ubaalo",
+       "tooltip-ca-nstab-special": "Utiye halaman istimewa, wawu ja mowali boli'olo",
        "tooltip-ca-nstab-project": "Bilohi halaman poroyek",
        "tooltip-ca-nstab-image": "Bilohi berkas lo halaman",
        "tooltip-ca-nstab-template": "Bilohi template",
        "tooltip-ca-nstab-category": "Bilohi kategori halaman",
-       "tooltip-save": "Tahuwa u ilubamu",
-       "tooltip-preview": "Bilohipo u ilubamu. Popopasiya utiye to'u diipo molahu.",
-       "tooltip-diff": "Bilohi u lo'ubawa pilohutumu",
+       "tooltip-save": "Tahuwa u biloli'umu",
+       "tooltip-preview": "Bilohipo u biloli'umu. Popopasiya utiye to'u diipo molahu.",
+       "tooltip-diff": "Bilohi u loboli'o pilohutumu",
        "tooltip-rollback": "\"Wuwalingo\" lopobatali u pilo'opiyohu to halaman botiye ode kontributor pulitiyo pe'enta lo klik.",
        "tooltip-undo": "\"wuwalingo\" lopobatali u biloli'a botiye wawu lomu'o kotak momoli'o wolo mode pratayang. Alasani mowali duhengalo to kotak limbu-limbu'o.",
        "tooltip-summary": "Tuwota tulade limbu-limbu'o",
-       "simpleantispam-label": "Momarakira anti-spam.\n<strong>kekeya</strong> tuwangalo!",
+       "simpleantispam-label": "Momarakisa anti-spam.\n<strong>kekeya</strong> tuwangalo!",
        "pageinfo-toolboxlink": "Halaman habari",
        "previousdiff": "← Biloli'o to'udiipo",
        "nextdiff": "Biloli'o lapatiyoma'o →",
index e3ae604..e4444d9 100644 (file)
        "versionrequired": "મીડીયાવિકિનું $1 સંસ્કરણ જરૂરી",
        "versionrequiredtext": "આ પાનાના વપરાશ માટે મીડિયાવિકિનું $1 સંસ્કરણ જરૂરી.\n\nજુઓ [[Special:Version|સંસ્કરણ પાનું]].",
        "ok": "મંજૂર",
-       "retrievedfrom": "\"$1\"થી લીધેલું",
+       "retrievedfrom": "\"$1\" થી મેળવેલ",
        "youhavenewmessages": "{{PLURAL:$3|તમારી પાસે}} $1 ($2).",
        "youhavenewmessagesfromusers": "આપને માટે {{PLURAL:$3|અન્ય સભ્ય|$3 અન્ય સભ્યો}} તરફથી $1 છે. ($2).",
        "youhavenewmessagesmanyusers": "આપને માટે ઘણાં સભ્યો તરફથી $1 છે ($2).",
index adffe21..d7e0e36 100644 (file)
        "recentchangesdays-max": "לכל היותר {{PLURAL:$1|יום אחד|יומיים|$1 ימים}}",
        "recentchangescount": "מספר העריכות שמוצגות כברירת מחדל:",
        "prefs-help-recentchangescount": "ההעדפה הזאת כוללת את דף השינויים האחרונים, דפי היסטוריית גרסאות ויומנים.",
-       "prefs-help-watchlist-token2": "×\96×\94×\95 ×\94×\9eפת×\97 ×\94ס×\95×\93×\99 ×\9c×\94×\96× ×\94 ×©×\9c ×¨×©×\99×\9eת ×\94×\9eעק×\91 ×©×\9c×\9a.\n×\9b×\9c ×\9e×\99 ×©×\99×\95×\93×¢ ×\90×\95ת×\95 ×\99×\9b×\95×\9c ×\9cקר×\95×\90 ×\90ת ×¨×©×\99×\9eת ×\94×\9eעק×\91 ×©×\9c×\9a, ×\9c×\9b×\9f ×\90×\99×\9f ×\9cשתף ×\90×\95ת×\95.\n×\9cמקרה הצורך, אפשר [[Special:ResetTokens|לאפס את האסימון]].",
+       "prefs-help-watchlist-token2": "×\96×\94×\95 ×\94×\9eפת×\97 ×\94ס×\95×\93×\99 ×\9c×\94×\96× ×\94 ×©×\9c ×¨×©×\99×\9eת ×\94×\9eעק×\91 ×©×\9c×\9a.\n×\9b×\9c ×\9e×\99 ×©×\99×\95×\93×¢ ×\90×\95ת×\95 ×\99×\9b×\95×\9c ×\9cקר×\95×\90 ×\90ת ×¨×©×\99×\9eת ×\94×\9eעק×\91 ×©×\9c×\9a, ×\9c×\9b×\9f ×\90×\99×\9f ×\9cשתף ×\90×\95ת×\95.\n×\91מקרה הצורך, אפשר [[Special:ResetTokens|לאפס את האסימון]].",
        "savedprefs": "ההעדפות שלך נשמרו.",
        "savedrights": "ההרשאות של {{GENDER:$1|המשתמש|המשתמשת}} \"$1\" נשמרו.",
        "timezonelegend": "אזור זמן:",
        "rollbacklinkcount": "שחזור {{PLURAL:$1|עריכה אחת|$1 עריכות}}",
        "rollbacklinkcount-morethan": "שחזור יותר מ{{PLURAL:$1|עריכה אחת|־$1 עריכות}}",
        "rollbackfailed": "השחזור נכשל",
-       "rollback-missingparam": "חסר פרמטר נדרש בבקשה.",
+       "rollback-missingparam": "חסרים פרמטרים נדרשים להגשת הבקשה.",
        "cantrollback": "לא ניתן לשחזר את העריכה;\nהתורם האחרון הוא היחיד שכתב בדף זה.",
        "alreadyrolled": "לא ניתן לשחזר את העריכה של [[User:$2|$2]] ([[User talk:$2|שיחה]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]) בדף [[:$1]]; הדף כבר נערך או שוחזר.\n\nהעריכה האחרונה הייתה של [[User:$3|$3]] ([[User talk:$3|שיחה]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "תקציר העריכה היה: <em>$1</em>.",
        "revertpage": "שוחזר מעריכות של [[Special:Contributions/$2|$2]] ([[User talk:$2|שיחה]]) לעריכה האחרונה של [[User:$1|$1]]",
        "revertpage-nouser": "שוחזר מעריכות של משתמש מוסתר לעריכה האחרונה של {{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "שוחזר מעריכות של $1 לעריכה האחרונה של $2",
-       "rollback-success-notify": "ער×\99×\9b×\95ת ×©×\9c $1 ×©×\95×\97×\96ר×\95;\n× ×\95×\97×\96ר ×\9c×\92רס×\94 ×\94×\90×\97ר×\95× ×\94 ×\9e×\90ת $2 [$3 הצגת שינויים]",
+       "rollback-success-notify": "ש×\95×\97×\96ר ×\9eער×\99×\9b×\95ת ×©×\9c $1 ×\9cער×\99×\9b×\94 ×\94×\90×\97ר×\95× ×\94 ×©×\9c $2. [$3 הצגת שינויים]",
        "sessionfailure-title": "בעיה בחיבור",
        "sessionfailure": "נראה שיש בעיה בחיבורכם לאתר;\nפעולתכם בוטלה כאמצעי זהירות נגד התחזות לתקשורת ממחשבכם.\nאנא חזרו לדף הקודם, העלו אותו מחדש ונסו שוב.",
        "changecontentmodel": "שינוי מודל התוכן של דף",
index cc8a44d..3cc20c6 100644 (file)
@@ -89,7 +89,7 @@
        "january-gen": "АгIой бетт",
        "february-gen": "Саь-кур",
        "march-gen": "Мутт-хьал бетт",
-       "april-gen": "Тушоли бетт",
+       "april-gen": "Тушоли",
        "may-gen": "Села бетт",
        "june-gen": "Этинга бетт",
        "july-gen": "Баьцамеа бетт",
        "about": "Сурт оттадар",
        "article": "Йоазув",
        "newwindow": "&nbsp;(керда кора чу)",
-       "cancel": "ЭÑ\88аÑ\86",
+       "cancel": "ЮÑ\85адаккÑ\85а",
        "moredotdotdot": "ДIахо...",
        "morenotlisted": "Ер список хьалйиза яц.",
        "mypage": "ОагIув",
        "savearticle": "ОагӀув дIаязъе",
        "preview": "Хьалхе бӀаргтассар",
        "showpreview": "Хьалххе бIаргтохар",
-       "showdiff": "Даь хувцамаш",
+       "showdiff": "Даь дола хувцамаш",
        "anoneditwarning": "<strong>Теркам бе!</strong> Хьо автор хинна система чуваьннавац. Нагахьа санна Iа моллагIа хувцам бой, Хьа IP-адрес дийла массанен бIаргагуш хургда. Нагахьа санна Хьо <strong>[$1 хьачувоале]</strong> е <strong>[$2 учёта яздар хьакхолле]</strong>, нийсдараш (хувцамаш) бувзам болаш хургда Хьа доакъашхой цIерца, иштта кхыдола толажагIи гIойленагIи дола дикаьш хургда Хьона.",
        "summary-preview": "Лоацам ба:",
        "subject-preview": "Кортале хургья:",
        "lineno": "МугI $1:",
        "compareselectedversions": "Хьаржа доржамаша тарона тIа хьажа",
        "editundo": "юхадаккха",
+       "diff-multi-sameuser": "({{PLURAL:$1|цхьа юкъ хулаш йола верси|$1 юкъ хулаш йола версеш}}гуш яц цу доакъашхочун)",
        "searchresults": "Лахар чакхдоалаш корадаьр",
        "searchresults-title": "«$1» лахар",
        "notextmatches": "ОагIувнаша яздамий вIашагIакхетараш дац",
        "searchprofile-articles-tooltip": "$1 чу лахар",
        "searchprofile-images-tooltip": "Файлаш лахар",
        "searchprofile-everything-tooltip": "Массайола оагIонаш тIа лахар (дувцар оттадара оагIонаш чулоацаш)",
-       "searchprofile-advanced-tooltip": "IоÑ\87Ñ\83Ñ\8fзаÑ\8fÑ\8c Ñ\86IеÑ\80аÑ\80енаÑ\88каÑ\85 лаха",
+       "searchprofile-advanced-tooltip": "IоÑ\87Ñ\83Ñ\8fзаÑ\8fÑ\8c Ñ\86IеÑ\80ий Ð¼Ð¾Ñ\82Ñ\82аÑ\88ка лаха",
        "search-result-size": "$1 ({{PLURAL:$2|$2 дош|$2 дешаш}})",
        "search-result-category-size": "{{PLURAL:$1|1=$1 дакъа|$1 дакъаш}} ({{PLURAL:$2|1=$2 кIалцатег|$2 кIалцатегаш}}, {{PLURAL:$3|1=$3 паьла|$3 паьлий}})",
        "search-redirect": "(дIа-сахьожадар $1 тIара)",
        "search-relatedarticle": "шоайл дола",
        "searchrelated": "гаргара",
        "searchall": "деррига",
+       "search-showingresults": "{{PLURAL:$4|Кораяьй <strong>$1</strong> — цхьа оагӀув|Из дош корадаьд <strong>$3</strong> оагӀонашка, царех гойта $2 оагӀув}}",
        "search-nonefound": "Хьа дехар дара вIаши нийса доагIаш хилар корадаьдац.",
        "powersearch-legend": " Доккха тахкар",
        "powersearch-ns": " ЦIерий аренашкахь лахар",
        "right-createtalk": "дувцама оагIувний хьакхоллам",
        "right-move": "ОагIувний цIи хувца",
        "right-movefile": "Паьлий цIи хувца",
+       "right-writeapi": "ДIаяздара лаьрххIа API пайда эцар",
        "newuserlogpage": "Доакъашхой дIаязбаь таптар",
        "rightslog": "Дакъалаьцархочунна бокъона тептар",
        "action-read": "Укх оагIуви дешам",
        "recentchangeslinked-feed": "Гаргалон хувцамаш",
        "recentchangeslinked-toolbox": "Укханца вIашагIдувзаденна хувцамаш",
        "recentchangeslinked-title": "$1ца вIашидувзаденна хувцамаш",
-       "recentchangeslinked-summary": "Ð\95Ñ\80, Iинк Ñ\8fÑ\8c Ð¹Ð¾Ð»Ð° Ð¾Ð°Ð³IÑ\83в (е Ñ\83кÑ\85 Ñ\86аÑ\82егаÑ\87Ñ\83 Ñ\87Ñ\83йоагIаÑ\80аÑ\88), Ð´Ñ\83кÑ\85а Ñ\85а Ð¹Ð¾Ð°Ñ\86аÑ\88 Ñ\85Ñ\8cийÑ\86а Ð¾Ð°Ð³IÑ\83внаÑ\88кий Ð´Ð°Ð³Ð°Ñ\80ле Ñ\8f.\n[[Special:Watchlist|ШÑ\83н Ñ\82еÑ\80кама Ð´Ð°Ð³Ð°Ñ\80ленаÑ\88каÑ\85]] Ñ\87Ñ\83йоагIа Ð¾Ð°Ð³IÑ\83внаÑ\88 '''белгалаÑ\8fÑ\8c Ñ\8f'''.",
+       "recentchangeslinked-summary": "Ð\91елгалаÑ\8fÑ\8c Ð¹Ð¾Ð»Ð° Ð¾Ð°Ð³IÑ\83в Ñ\82IаÑ\85Ñ\8cожавеÑ\88 (е Ð±ÐµÐ»Ð³Ð°Ð»Ð°Ñ\8fÑ\8c Ð¹Ð¾Ð»Ð° ÐºÐ°Ñ\82егоÑ\80ен Ñ\8eкÑ\8aейоагIаÑ\88) Ð¹Ð¾Ð»Ñ\87а Ð¾Ð°Ð³IонаÑ\88Ñ\82а Ð´Ð°Ñ\8c Ñ\85Ñ\83вÑ\86амаÑ\88 Ð´Ð° ÐµÑ\80аÑ\88.\n[[Special:Watchlist|Ð¥Ñ\8cа Ð·ÐµÐ¼ Ð±Ð°Ñ\80а Ñ\81пиÑ\81ок]] Ñ\8eкÑ\8aейоагIаÑ\88 Ð¾Ð°Ð³IонаÑ\88 '''белгалаÑ\8fÑ\8cй'''.",
        "recentchangeslinked-page": "ОагIон цIи",
        "recentchangeslinked-to": "Вешта, белгаляьккха оагIон тIахьожавеш дола оагIонашта даь хувцамаш хьахьокха.",
        "upload": "Файл чуяккха",
        "undelete-search-submit": "Хьалáха",
        "namespace": "ЦIерий мотт",
        "invert": "Харжар юхадаккха",
+       "tooltip-invert": "Оттае ер белгало, хержа цIерий мотта чу а (белгалъяь яле вIашагIъювзаенна цIерий мотта чу а), оагIонаш тIа а даь хувцамаш къайладоахаргдолаш",
        "namespace_association": "Ювзаенна мотт",
+       "tooltip-namespace_association": "Оттае ер белгало, иштта хержа цIерий моттаца вIашагIъювзаенна дувца оттадара цIерий мотт (е кхыяр) юкъейоаккхаргйолаш",
        "blanknamespace": "(Кертера)",
        "contributions": "{{GENDER:$1|Доакъашхочун}} къахьегам",
        "contributions-title": "$1 дакъалаьцархочунна къахьегам",
        "sp-contributions-submit": "Хьалáха",
        "whatlinkshere": "ТIахьожаяргаш укхаза",
        "whatlinkshere-title": "\"$1\" тIахьожавеш йола оагIонаш",
-       "whatlinkshere-page": "ОагIув",
+       "whatlinkshere-page": "ОагIув:",
        "linkshere": "ТIехьайоагIа оагIонаш тIахьожаву «'''[[:$1]]'''»:",
        "nolinkshere": "'''[[:$1]]''' оагIув тIа, кхыдола оагIувашкара Iинкаш йоацаш я",
        "isredirect": "оагIув-дIа-сахьожадар",
        "tooltip-t-whatlinkshere": "Укхаза тIахьожавеш йола оагIонаш",
        "tooltip-t-recentchangeslinked": "Укх оагIуво тIахьожавеш йолча оагIонай тIеххьара хувцамаш",
        "tooltip-feed-rss": "Укх оагIувна RSSчу гойтар",
-       "tooltip-feed-atom": "Укх оаг|увна Atomчу гойтар",
+       "tooltip-feed-atom": "Укх оагIонна лаьрххIа Atom чу трансляци яр",
        "tooltip-t-contributions": "{{GENDER:$1|Укх доакъашхочо хийца}} йола оагIонаш",
        "tooltip-t-emailuser": "Укх дакъалаьцархочоа зIы яхьийта",
        "tooltip-t-upload": "Файлаш чуяккха",
        "exif-orientation": "Сурта белгало",
        "exif-xresolution": "ПхьорагIа разрешени",
        "exif-yresolution": "УрагIа разрешени",
+       "exif-datetime": "Файл хийца хинна таьрахьи хаи",
        "exif-imagedescription": "Сурта цIи",
+       "exif-make": "Камера кийчъяь арахийцар",
        "exif-model": "Камера модель",
        "exif-software": "Программни Iалашдар",
        "exif-artist": "Яздархо",
        "exif-colorspace": "Бесай мотт",
        "exif-pixelxdimension": "Сурта шорал",
        "exif-pixelydimension": "Сурта лакхал",
+       "exif-datetimeoriginal": "Оригинальни таьрахьи хаи",
        "exif-datetimedigitized": "Оцифровк яь таьрахь а, ха а",
        "exif-writer": "Яздама да",
        "exif-languagecode": "Мотт",
index d4670e4..91bfe2b 100644 (file)
        "createacct-email-ph": "Inserisci il tuo indirizzo email",
        "createacct-another-email-ph": "Inserisci l'indirizzo di posta elettronica",
        "createaccountmail": "Usa una password casuale temporanea e inviala all'indirizzo di posta elettronica specificato",
+       "createaccountmail-help": "Può essere utilizzato per creare un'utenza per un'altra persona senza doverne conoscere la password.",
        "createacct-realname": "Nome reale (opzionale)",
        "createaccountreason": "Motivo:",
        "createacct-reason": "Motivo",
        "createacct-reason-ph": "Perché stai creando un'altra utenza",
-       "createacct-reason-help": "Messaggio visualizzato nel registro della creazione dell'account",
+       "createacct-reason-help": "Messaggio visualizzato nel registro della creazione dell'utenza",
        "createacct-submit": "Crea la tua utenza",
        "createacct-another-submit": "Crea utenza",
-       "createacct-another-continue-submit": "Continuare la creazione dell'account",
+       "createacct-another-continue-submit": "Continua la creazione dell'utenza",
        "createacct-benefit-heading": "{{SITENAME}} cresce grazie a persone come te.",
        "createacct-benefit-body1": "{{PLURAL:$1|modifica|modifiche}}",
        "createacct-benefit-body2": "{{PLURAL:$1|pagina|pagine}}",
        "nocookiesnew": "La registrazione è stata completata, ma non è stato possibile accedere a {{SITENAME}} perché i cookie sono disattivati. Riprovare l'accesso con il nome utente e la password appena creati dopo aver attivato i cookie nel proprio browser.",
        "nocookieslogin": "L'accesso a {{SITENAME}} richiede l'uso dei cookie, che risultano disattivati. Riprovare l'accesso dopo aver attivato i cookie nel proprio browser.",
        "nocookiesfornew": "L'account utente non è stato creato, poiché non abbiamo potuto confermare la sua fonte.\nAssicurati di avere attivato i cookie, ricarica questa pagina e riprova.",
-       "createacct-loginerror": "L'account è stato creato correttamente ma non è stato possibile farti accedere in modo automatico. Per piacere procedi a [[Special:UserLogin|accesso manuale]].",
+       "createacct-loginerror": "L'utenza è stata creata correttamente, ma non è stato possibile farti accedere in modo automatico. Procedi con l'[[Special:UserLogin|accesso manuale]].",
        "noname": "Il nome utente indicato non è valido.",
        "loginsuccesstitle": "Accesso effettuato",
        "loginsuccess": "'''Sei stato connesso al server di {{SITENAME}} con il nome utente di \"$1\".'''",
        "passwordreset-emailsent-capture": "È stata inviata una email di reimpostazione della password, il contenuto è riportato di seguito.",
        "passwordreset-emailerror-capture": "È stata generata una email di reimpostazione della password, riportata di seguito. L'invio {{GENDER:$2|all'utente}} non è riuscito: $1",
        "passwordreset-ignored": "La reimpostazione della password non è stata gestita. Forse nessun provider è configurato?",
+       "passwordreset-invalideamil": "Indirizzo di posta elettronica non valido",
+       "passwordreset-nodata": "Non è stato fornito né un nome utente né un indirizzo di posta elettronica",
        "changeemail": "Modifica o rimuovi indirizzo di posta elettronica",
        "changeemail-header": "Completa questo modulo per cambiare il tuo indirizzo email. Se vuoi rimuovere l'associazione di qualsiasi indirizzo email dalla tua utenza, lascia il nuovo indirizzo email vuoto quando invii il modulo.",
        "changeemail-passwordrequired": "Sarà necessario inserire la password per confermare la modifica.",
        "log-action-filter-suppress-reblock": "Soppressione utente da ri-blocco",
        "log-action-filter-upload-upload": "Nuovo caricamento",
        "log-action-filter-upload-overwrite": "Ricaricamento",
+       "authmanager-authn-not-in-progress": "L'autenticazione non è in corso o i dati della sessione sono andati persi. Si prega di ricominciare dall'inizio.",
        "authmanager-authn-no-local-user": "Le credenziali fornite non sono associate a nessun utente di questo wiki.",
-       "authmanager-authn-autocreate-failed": "Creazione automatica di un account locale fallita: $1",
-       "authmanager-create-disabled": "La creazione di account è disabilitata.",
-       "authmanager-create-from-login": "Per creare il tuo account, per piacere completa i campi qui sotto.",
-       "authmanager-create-not-in-progress": "La creazione di un account non è in corso o i dati della sessione sono andati persi. Si prega di ricominciare nuovamente dall'inizio.",
-       "authmanager-create-no-primary": "Le credenziali fornite non possono essere utilizzate per la creazione dell'account.",
+       "authmanager-authn-no-local-user-link": "Le credenziali fornite sono valide ma non sono associate a nessun utente di questa wiki. Accedi in un modo diverso o crea un nuovo utente, e avrai un'opzione per collegare le tue credenziali precedenti a quell'utenza.",
+       "authmanager-authn-autocreate-failed": "Creazione automatica di un'utenza locale fallita: $1",
+       "authmanager-change-not-supported": "Le credenziali fornite non possono essere modificate, dato che non verrebbero usate da nulla.",
+       "authmanager-create-disabled": "La creazione di utenze è disabilitata.",
+       "authmanager-create-from-login": "Per creare la tua utenza, completa i campi qui sotto.",
+       "authmanager-create-not-in-progress": "La creazione di un'utenza non è in corso o i dati della sessione sono andati persi. Si prega di ricominciare nuovamente dall'inizio.",
+       "authmanager-create-no-primary": "Le credenziali fornite non possono essere utilizzate per la creazione dell'utenza.",
+       "authmanager-link-no-primary": "Le credenziali fornite non possono essere utilizzate per il collegamento dell'utenza.",
+       "authmanager-link-not-in-progress": "Il collegamento dell'utenza non è in corso o i dati della sessione sono andati persi. Si prega di ricominciare dall'inizio.",
        "authmanager-authplugin-setpass-failed-message": "Il plugin di autenticazione ha impedito la modifica della password.",
-       "authmanager-authplugin-create-fail": "Il plugin di autenticazione ha impedito la creazione dell'account.",
+       "authmanager-authplugin-create-fail": "Il plugin di autenticazione ha impedito la creazione dell'utenza.",
        "authmanager-authplugin-setpass-denied": "Il plugin di autenticazione non consente di cambiare le password.",
-       "authmanager-autocreate-noperm": "La creazione automatica di un account non è permessa.",
+       "authmanager-authplugin-setpass-bad-domain": "Dominio non valido.",
+       "authmanager-autocreate-noperm": "La creazione automatica dell'utenza non è permessa.",
+       "authmanager-autocreate-exception": "La creazione automatica di utenze è temporaneamente disabilitata a causa di errori precedenti.",
        "authmanager-userdoesnotexist": "L'utenza \"$1\" non è registrata.",
        "authmanager-userlogin-remembermypassword-help": "Se la password deve essere ricordata più a lungo rispetto alla durata della sessione.",
        "authmanager-password-help": "Password per l'autenticazione.",
        "authmanager-realname-help": "Nome reale dell'utente",
        "authmanager-provider-password": "Autenticazione basata su password",
        "authmanager-provider-password-domain": "Autenticazione con password o basata su dominio",
-       "authprovider-confirmlink-message": "Basandosi sui recenti tentativi di login, i seguenti accounts possono essere collegati al tuo account wiki. Collegarli ti consente di effettuare il login tramite essi. Si prega di selezionare quelli che devono essere collegati.",
-       "authprovider-confirmlink-success-line": "$1: Collegato correttamente.",
-       "authprovider-confirmlink-failed": "Il collegamento dell'account non è pienamente riuscito: $1",
+       "authprovider-confirmlink-message": "Basandosi sui recenti tentativi di accesso, le seguenti utenze possono essere collegate al tuo account wiki. Collegarli ti consente di effettuare l'accesso tramite di esse. Si prega di selezionare quelli che devono essere collegate.",
+       "authprovider-confirmlink-success-line": "$1: collegato correttamente.",
+       "authprovider-confirmlink-failed": "Il collegamento dell'utenza non è pienamente riuscito: $1",
        "authform-nosession-login": "L'autenticazione ha avuto successo, ma il tuo browser non è in grado di \"ricordare\" che ti sei collegato.\n\n$1",
        "authform-newtoken": "Token mancante. $1",
        "authform-notoken": "Token mancante",
        "authform-wrongtoken": "Token errato",
+       "specialpage-securitylevel-not-allowed-title": "Non consentito",
        "specialpage-securitylevel-not-allowed": "Siamo spiacenti, non sei autorizzato ad utilizzare questa pagina perché la tua identità non può essere verificata.",
        "authpage-cannot-login-continue": "Impossibile continuare l'accesso. La tua sessione è probabilmente scaduta.",
-       "authpage-cannot-link": "Impossibile avviare il collegamento dell'account.",
-       "authpage-cannot-link-continue": "Impossibile continuare il collegamento dell'account. La tua sessione è probabilmente scaduta.",
+       "authpage-cannot-create-continue": "Impossibile continuare la creazione dell'utenza. La tua sessione è probabilmente scaduta.",
+       "authpage-cannot-link": "Impossibile avviare il collegamento dell'utenza.",
+       "authpage-cannot-link-continue": "Impossibile continuare il collegamento dell'utenza. La tua sessione è probabilmente scaduta.",
        "cannotauth-not-allowed-title": "Permesso negato",
        "changecredentials": "Modifica delle credenziali",
        "changecredentials-submit": "Modifica",
        "changecredentials-submit-cancel": "Annulla",
        "changecredentials-invalidsubpage": "$1 non è una tipologia di credenziale valida.",
+       "changecredentials-success": "Le tue credenziali sono state modificate.",
+       "removecredentials": "Rimuovi credenziali",
        "removecredentials-submit": "Rimuovi",
        "removecredentials-submit-cancel": "Annulla",
-       "cannotlink-no-provider-title": "Non ci sono account collegabili.",
-       "cannotlink-no-provider": "Non ci sono account collegabili.",
-       "linkaccounts-success-text": "L'account è stato collegato."
+       "removecredentials-invalidsubpage": "$1 non è una tipologia di credenziale valida.",
+       "removecredentials-success": "Le tue credenziali sono state eliminate.",
+       "cannotlink-no-provider-title": "Non ci sono utenze collegabili",
+       "cannotlink-no-provider": "Non ci sono utenze collegabili.",
+       "linkaccounts": "Collega utenze",
+       "linkaccounts-success-text": "L'utenza è stata collegata.",
+       "unlinkaccounts": "Scollega utenze",
+       "unlinkaccounts-success": "L'utenza è stata scollegata."
 }
index f446921..57d4099 100644 (file)
        "morenotlisted": "Listen ä ett komplett.",
        "mypage": "Siid",
        "mytalk": "Diskusjon",
-       "anontalk": "Diskusjonssiid for denn IP-adress",
+       "anontalk": "Diskusjonssiid",
        "navigation": "Navigasjon",
        "and": "&#32;å",
        "qbfind": "Syeg",
        "viewsource-title": "Sie tjeljkoden te $1",
        "actionthrottled": "Begrænsneng å hånjleng",
        "viewsourcetext": "Du ken dog se og åfskreve'n keldekode til æ side:",
+       "exception-nologin": "Ikke loggen på",
        "welcomeuser": "Wælkomen, $1!",
        "welcomecreation-msg": "Det konto ä bløwen opretten.\nGlæmm ett å ønda din [[Special:Preferences|instellenge for {{SITENAME}}]].",
        "yourname": "Det brugenaun:",
        "createacct-emailoptional": "E-mailadress (walgfri)",
        "createacct-email-ph": "Intast dej e-mailadress",
        "createacct-another-email-ph": "Intast e-mailadress",
+       "createaccountreason": "Begrunjels:",
        "createacct-submit": "Oprett det konto",
+       "createacct-another-submit": "Oprett konto",
        "createacct-benefit-heading": "{{SITENAME}} laws å menske som du.",
        "createacct-benefit-body1": "{{PLURAL:$1|redigiireng|redigiirenge}}",
        "createacct-benefit-body2": "{{PLURAL:$1|siid|side}}",
        "pt-createaccount": "Oprett konto",
        "pt-userlogout": "Logg å",
        "retypenew": "Djentast ny adgångskode",
+       "botpasswords-label-delete": "Slett",
        "resetpass-submit-cancel": "Åbryd",
        "passwordreset": "Nullstell adgångskode",
+       "passwordreset-username": "Brugenaun:",
        "bold_sample": "Fied tekst",
        "bold_tip": "Fied tekst",
        "italic_sample": "Kursiw tekst",
        "history-feed-title": "Versjonshistori",
        "history-feed-item-nocomment": "$1 mä $2",
        "rev-delundel": "ønda sijtbarhed",
+       "revdelete-show-file-submit": "Ja",
+       "revdelete-log": "Begrunjels:",
        "history-title": "$1: Versjonshistorik",
        "difference-title": "Forskell mellem versjone å \"$1\"",
        "lineno": "Linje $1:",
        "badsig": "Syntaksi i signaturen ä udjylji; kontrolliir wenlist den brugtje HTML.",
        "badsiglength": "Din signatur ä for lång. Den ma hyest inholj $1 {{PLURAL:$1|tejn}}.",
        "yourgender": "Hwant forträkke du å blyw beskriiwen?",
+       "email": "E-mail",
        "prefs-help-realname": "Åndjiels å rijti naun ä walgfritj.\nHwes du wælge å oplys det naun, wil dä blyw brugtj te å tilskriiw dej det arbejt.",
        "prefs-editor": "Redigiirengsprogramme",
        "prefs-preview": "Forhånjswisneng",
        "rcshowhideminor-show": "Wis",
        "rcshowhideminor-hide": "Sjul",
        "rcshowhidebots": "$1 robotte",
-       "rcshowhidebots-show": "Sjul",
+       "rcshowhidebots-show": "Wis",
        "rcshowhidebots-hide": "Sjul",
        "rcshowhideliu": "$1 registriirtje bruga",
        "rcshowhideliu-show": "Wis",
        "rcshowhidemine": "$1 ejne bidraw",
        "rcshowhidemine-show": "Wis",
        "rcshowhidemine-hide": "Sjul",
+       "rcshowhidecategorization-show": "Wis",
+       "rcshowhidecategorization-hide": "Sjul",
        "rclinks": "Wis siensti $1 øndrenge i di sisti $2 daw<br />$3",
        "diff": "forskell",
        "hist": "historik",
index 38ef8c0..0499518 100644 (file)
        "fewestrevisions": "Ең аз түзетілген беттер",
        "nbytes": "$1 {{PLURAL:$1|байт|байт}}",
        "ncategories": "$1 {{PLURAL:$1|Санат|Санаттар}}",
-       "ninterwikis": "$1 {{PLURAL:$1|интеруики|интеруикилер}}",
+       "ninterwikis": "$1 {{PLURAL:$1|интеруики|интеруики}}",
        "nlinks": "$1 сілтеме",
        "nmembers": "$1 {{PLURAL:$1|мүше|мүше}}",
        "nmemberschanged": "$1 → $2 {{PLURAL:$2|мүше|мүше}}",
index f493da3..1a8bd45 100644 (file)
        "revertpage": "[[Special:Contributions/$2|$2]]([[User talk:$2|토론]])의 편집을 [[User:$1|$1]]의 마지막 판으로 되돌림",
        "revertpage-nouser": "숨긴 사용자의 편집을 {{GENDER:$1|[[User:$1|$1]]}}의 마지막 판으로 되돌림",
        "rollback-success": "$1의 편집을 되돌렸습니다.\n$2의 마지막 판으로 바뀌었습니다.",
+       "rollback-success-notify": "$1의 편집을 되돌렸습니다.\n$2의 마지막 판으로 바뀌었습니다. [$3 차이 보기]",
        "sessionfailure-title": "세션 실패",
        "sessionfailure": "로그인 세션에 문제가 발생한 것 같습니다.\n세션 하이재킹을 막기 위해 동작이 취소되었습니다.\n브라우저의 뒤로 버튼을 누르고 문서를 새로 고침한 후에 다시 시도해 주세요.",
        "changecontentmodel": "문서의 콘텐츠 모델을 변경",
        "confirm-unwatch-button": "확인",
        "confirm-unwatch-top": "이 문서를 주시문서 목록에서 뺄까요?",
        "confirm-rollback-button": "확인",
+       "confirm-rollback-top": "이 문서의 편집을 되돌리시겠습니까?",
        "quotation-marks": "“$1”",
        "imgmultipageprev": "← 이전 페이지",
        "imgmultipagenext": "다음 페이지 →",
index a5b3dbe..ce4419a 100644 (file)
        "confirm-watch-top": "Sulle mer di Sigg en Ding Oppaßleß opnämme?",
        "confirm-unwatch-button": "Lohß Jonn!",
        "confirm-unwatch-top": "Sulle mer di Sigg uß Dinger Oppaßleß erußnämme?",
+       "confirm-rollback-button": "Lohß Jonn!",
        "semicolon-separator": ";",
        "word-separator": "&#32;",
        "ellipsis": "&nbsp;…",
        "api-error-blacklisted": "Söhk Der ene anndere Nahme uß, dä mih drövver säht.",
        "randomrootpage": "Zofällige Aanfangs-Sigg",
        "log-action-filter-delete-delete": "En Sigg wohd fott jeschmeße",
+       "authmanager-authplugin-setpass-failed-title": "Dat Paßwoot ze änndere hät nit jeflupp",
        "authmanager-userdoesnotexist": "Ene Metmaacher mem Nahme „$1“ es nit ennjedrahre.",
        "authmanager-domain-help": "De Domäijn för de Zohjangsdaht vun ußerhallef beschtähtech ze krijje.",
+       "authmanager-email-label": "<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i>",
        "authmanager-email-help": "De Addräß för de <i lang=\"en\">e-mail</i>",
+       "authmanager-realname-label": "Der „reeschteje“ Nahme",
+       "authmanager-provider-temporarypassword": "Zweschepasswood:",
        "authprovider-resetpass-skip-label": "Övverjonn",
+       "specialpage-securitylevel-not-allowed-title": "Nit zohjelohße",
+       "cannotauth-not-allowed-title": "Zohjang verbodde.",
+       "changecredentials-submit": "Ändere",
        "changecredentials-submit-cancel": "Ophüre",
-       "removecredentials-submit": "Fott nämme"
+       "removecredentials-submit": "Fott nämme",
+       "removecredentials-submit-cancel": "Ophüre"
 }
index 62ed2a3..eb80b45 100644 (file)
        "confirm-watch-top": "Dës Säit op Är Iwwerwaachungslëscht bäisetzen?",
        "confirm-unwatch-button": "OK",
        "confirm-unwatch-top": "Dës Säit vun Ärer Iwwerwaachungslëscht erofhuelen?",
+       "confirm-rollback-button": "OK",
+       "confirm-rollback-top": "Ännerunge vun dëser Säit zrécksetzen?",
        "quotation-marks": "\"$1\"",
        "imgmultipageprev": "← Vireg Säit",
        "imgmultipagenext": "nächst Säit →",
        "removecredentials-submit": "Ewechhuelen",
        "removecredentials-submit-cancel": "Ofbriechen",
        "credentialsform-account": "Numm vum Kont:",
-       "linkaccounts": "Benotzerkonte verbannen"
+       "cannotlink-no-provider-title": "Et gëtt keng Benotzerkonte fir ze verlinken",
+       "linkaccounts": "Benotzerkonte verbannen",
+       "linkaccounts-submit": "Benotzerkonte verbannen"
 }
index f58bee7..3d32f5b 100644 (file)
        "newarticle": "(Neuv)",
        "newarticletext": "A l'é andaje dapress a na liura a na pàgina che a esist ancor nen.\nPër creé la pàgina, ch'a ancamin-a a scrive ant lë spassi sì-sota (vëdde la [$1 pàgina d'agiut] për savèjne ëd pì).\nS'a l'é rivà sì për eror, ch'a sgnaca ël boton '''andaré''' ëd sò navigador.",
        "anontalkpagetext": "----\n<em>Costa a l'é la pàgina ëd ciaciarade për n'utent anònim che a l'é ancó pa duvertasse un cont, ò pura che a lo deuvra nen.</em>\nAlora i l'oma da dovré ël nùmer d'adrëssa IP për deje n'identificassion a chiel o chila.\nN'adrëssa IP përparèj a peul esse partagià da vàire utent.\nSe chiel a l'é n'utent anònim e a l'ha l'impression d'arsèive dij coment sensa sust, për piasì [[Special:CreateAccount|ch'a crea un cont]] o [[Special:UserLogin|ch'a rintra ant ël sistema]] për evité dë fé confusion con d'àutri utent anònim.''",
-       "noarticletext": "Al moment costa pàgina a l'é veuida.\nA peul [[Special:Search/{{PAGENAME}}|sërché cost tìtol]] andrinta a d'àutre pàgine, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} sërché ant ij registr colegà],\no purament [{{fullurl:{{FULLPAGENAME}}|action=edit}} creé sta pàgina]</span>.",
+       "noarticletext": "Al moment costa pàgina a l'é veuida.\nA peul [[Special:Search/{{PAGENAME}}|sërché cost tìtol]] andrinta a d'àutre pàgine, <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} sërché ant ij registr colegà],\no bin [{{fullurl:{{FULLPAGENAME}}|action=edit}} creé sta pàgina]</span>.",
        "noarticletext-nopermission": "Al moment a-i é gnun test ansima a costa pàgina.\nA peul [[Special:Search/{{PAGENAME}}|sërché ës tìtol ëd pàgina]] an d'àutre pàgine,\no <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} sërché ant j'argistr colegà]</span>, ma a l'ha pa ël përmess ëd creé costa pàgina.",
        "missing-revision": "La revision nùmer $1 dla pàgina antitolà «{{FULLPAGENAME}}» a esist pa.\n\nSòn a l'é normalment causà da l'andèje dapress a na vej liura stòrica a na pàgina ch'a l'é stàita scancelà. Ij detaj a peulo esse trovà ant ël [registr ëd jë scancelament ëd {{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}}].",
        "userpage-userdoesnotexist": "Lë stranòm «<nowiki>$1</nowiki>» a l'é pa registrà. Për piasì ch'a varda se da bon a veul creé o modifiché costa pàgina.",
index 74e3dd9..b810ffd 100644 (file)
        "accmailtext": "Лозинка за {{GENDER:$1|корисника|корисницу}} [[User talk:$1|$1]] је послата на $2. Након пријаве, лозинка се може променити [[Special:ChangePassword|овде]].",
        "newarticle": "(нови)",
        "newarticletext": "Дошли сте на страницу која још не постоји.\nДа бисте је направили, почните да куцате у прозор испод овог текста (погледајте [$1 страницу за помоћ]).\nАко сте овде дошли грешком, вратите се на претходну страницу.",
-       "anontalkpagetext": "---- Ово је страница за разговор с анонимним корисником који још нема налог или га не користи.\nЗбог тога морамо да користимо бројчану ИП адресу како бисмо га препознали.\nТакву адресу може делити више корисника.\nАко сте анонимни корисник и мислите да су вам упућене примедбе, [[Special:CreateAccount|отворите налог]] или се [[Special:UserLogin|пријавите]] да бисте избегли будућу забуну с осталим анонимним корисницима.",
+       "anontalkpagetext": "----\n<em>Ово је страница за разговор с анонимним корисником који још нема налог или га не користи.</em>\nЗбог тога морамо да користимо бројчану ИП адресу како бисмо га препознали.\nТакву адресу може делити више корисника.\nАко сте анонимни корисник и мислите да су вам упућене примедбе, [[Special:CreateAccount|отворите налог]] или се [[Special:UserLogin|пријавите]] да бисте избегли будућу забуну с осталим анонимним корисницима.",
        "noarticletext": "На овој страници тренутно нема садржаја.\nМожете [[Special:Search/{{PAGENAME}}|потражити овај наслов]] на другим страницама,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} претражити сродне извештаје] или [{{fullurl:{{FULLPAGENAME}}|action=edit}} направити ову страницу]</span>.",
        "noarticletext-nopermission": "На овој страници тренутно нема садржаја.\nМожете [[Special:Search/{{PAGENAME}}|потражити овај наслов]] на другим страницама или <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} претражити сродне дневнике]</span>, али немате дозволу да направите ову страницу.",
        "missing-revision": "Не могу да пронађем измену бр. $1 на страници под називом „{{FULLPAGENAME}}“.\n\nОво се обично дешава када пратите застарелу везу до странице која је обрисана.\nВише информација можете пронаћи у [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневнику брисања].",
        "right-override-export-depth": "извоз страница укључујући и повазене странице до дубине од пет веза",
        "right-sendemail": "слање имејла другим корисницима",
        "right-passwordreset": "прегледање порука за обнављање лозинке",
-       "right-managechangetags": "пÑ\80авÑ\99еÑ\9aе Ð¸/или Ð±Ñ\80иÑ\81аÑ\9aе [[Special:Tags|ознака]] Ð¸Ð· Ð±Ð°Ð·Ðµ Ð¿Ð¾Ð´Ð°Ñ\82ака",
+       "right-managechangetags": "Ð\9fÑ\80авÑ\99еÑ\9aе Ð¸ (де)акÑ\82ивиÑ\80аÑ\9aе [[Special:Tags|ознака]]",
        "right-applychangetags": "примењивање [[Special:Tags|ознака]] на нечије измене",
        "right-changetags": "додавање и уклањање разних [[Special:Tags|ознака]] на појединачним изменама и уносима у дневницима",
        "grant-group-page-interaction": "Уређивање страница",
        "action-viewmyprivateinfo": "прегледање ваших личних података",
        "action-editmyprivateinfo": "уређивање ваших личних података",
        "action-editcontentmodel": "мењање модела садржаја странице",
-       "action-managechangetags": "прављење и/или брисање ознака из базе података",
+       "action-managechangetags": "прављење и (де)активирање ознака",
        "action-applychangetags": "додавање ознака на ваше измене",
        "action-changetags": "додавање и уклањање разних ознака на појединачним изменама и уносима у дневницима",
        "nchanges": "$1 {{PLURAL:$1|измена|измене|измена}}",
        "import-nonewrevisions": "Измене нису увезене (све су већ биле или присутне или прескочене због грешки).",
        "xml-error-string": "$1 у реду $2, колона $3 (бајт $4): $5",
        "import-upload": "Отпремање XML података",
-       "import-token-mismatch": "Губитак података о сесији.\nПокушајте поново.",
+       "import-token-mismatch": "Губитак података о сесији.\n\nМожда сте одјављени.\n<strong>Молимо Вас проверите да ли сте још увек пријављени и покушајте поново<strong>.\n\nАко и даље не ради, покушајте се [[Special:UserLogout|одјавити]] и поново пријавити и проверите да ли Ваш веб-пртраживач дозвољава колачиће са овог сајта.",
        "import-invalid-interwiki": "Не могу да увозим с наведеног викија.",
        "import-error-edit": "Страница „$1“ није увезена јер вам није дозвољено да је уређујете.",
        "import-error-create": "Страница „$1“ није увезена јер вам није дозвољено да је направите.",
index 1308537..8ca8832 100644 (file)
                        "Maathavan",
                        "தமிழ்க்குரிசில்",
                        "Nemo bis",
-                       "JAaron95"
+                       "JAaron95",
+                       "Info-farmer"
                ]
        },
-       "tog-underline": "à®\87ணà¯\88பà¯\8dபà¯\81à®\95ளà¯\81à®\95à¯\8dà®\95à¯\81 à®\85à®\9fிà®\95à¯\8dà®\95à¯\8bà®\9fிà®\9fà¯\81",
+       "tog-underline": "à®\85à®\9fிà®\95à¯\8dà®\95à¯\8bà®\9fிà®\9fà¯\8dà®\9fதà¯\8dதà¯\88 à®\87ணà¯\88:",
        "tog-hideminor": "அண்மைய மாற்றங்களில் சிறிய தொகுப்புகளை மறை",
        "tog-hidepatrolled": "அண்மைய மாற்றங்களில் ரோந்திட்ட தொகுப்புகளை மறைக்கவும்",
        "tog-newpageshidepatrolled": "பலமுறை பார்வையிட்ட பக்கங்களைப் புதியபக்கங்களின் பட்டியலில் காட்டவேண்டாம்.",
index aab2087..7c87e10 100644 (file)
@@ -59,6 +59,7 @@ class ConvertExtensionToRegistration extends Maintenance {
                $this->addArg( 'path', 'Location to the PHP entry point you wish to convert',
                        /* $required = */ true );
                $this->addOption( 'skin', 'Whether to write to skin.json', false, false );
+               $this->addOption( 'config-prefix', 'Custom prefix for configuration settings', false, true );
        }
 
        protected function getAllGlobals() {
@@ -92,6 +93,10 @@ class ConvertExtensionToRegistration extends Maintenance {
                $this->dir = dirname( realpath( $this->getArg( 0 ) ) );
                $this->json = [];
                $globalSettings = $this->getAllGlobals();
+               $configPrefix = $this->getOption( 'config-prefix', 'wg' );
+               if ( $configPrefix !== 'wg' ) {
+                       $this->json['config']['_prefix'] = $configPrefix;
+               }
                foreach ( $vars as $name => $value ) {
                        $realName = substr( $name, 2 ); // Strip 'wg'
                        if ( $realName === false ) {
@@ -113,9 +118,14 @@ class ConvertExtensionToRegistration extends Maintenance {
                                        $this->noLongerSupportedGlobals[$realName] . '). ' .
                                        "Please update the entry point before convert to registration.\n" );
                                $this->hasWarning = true;
-                       } elseif ( strpos( $name, 'wg' ) === 0 ) {
+                       } elseif ( strpos( $name, $configPrefix ) === 0 ) {
                                // Most likely a config setting
-                               $this->json['config'][$realName] = $value;
+                               $this->json['config'][substr( $name, strlen( $configPrefix ) )] = $value;
+                       } elseif ( $configPrefix !== 'wg' && strpos( $name, 'wg' ) === 0 ) {
+                               // Warn about this
+                               $this->output( 'Warning: Skipped global "' . $name . '" (' .
+                                       'config prefix is "' . $configPrefix . '"). ' .
+                                       "Please check that this setting isn't needed.\n" );
                        }
                }
 
index 9cfc40f..3aeb1ed 100644 (file)
 .success-message {
        font-weight: bold;
        font-size: 110%;
-       color: #0f0;
+       color: #008000;
 }
 
 .success-box {
index 8bc9ca4..6fde2e0 100644 (file)
     "grunt-contrib-watch": "1.0.0",
     "grunt-jscs": "2.8.0",
     "grunt-jsonlint": "1.0.7",
-    "grunt-karma": "1.0.0",
+    "grunt-karma": "0.12.2",
     "grunt-stylelint": "0.3.0",
     "karma": "0.13.22",
-    "karma-chrome-launcher": "1.0.1",
-    "karma-firefox-launcher": "1.0.0",
-    "karma-qunit": "1.0.0",
+    "karma-chrome-launcher": "0.2.2",
+    "karma-firefox-launcher": "0.1.7",
+    "karma-qunit": "0.1.9",
     "qunitjs": "1.22.0",
     "stylelint-config-wikimedia": "0.1.0"
   }
index bacf1e6..2d1e921 100644 (file)
@@ -1060,14 +1060,17 @@ return [
                        // @todo Load this message in content language
                        'colon-separator',
                ],
+               'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.htmlform.styles' => [
                'styles' => 'resources/src/mediawiki/mediawiki.htmlform.css',
                'position' => 'top',
+               'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.htmlform.ooui.styles' => [
                'styles' => 'resources/src/mediawiki/mediawiki.htmlform.ooui.css',
                'position' => 'top',
+               'targets' => [ 'desktop', 'mobile' ],
        ],
        'mediawiki.icon' => [
                'styles' => 'resources/src/mediawiki/mediawiki.icon.less',
@@ -1726,7 +1729,6 @@ return [
 
        'mediawiki.special' => [
                'position' => 'top',
-               'scripts' => 'resources/src/mediawiki.special/mediawiki.special.js',
                'styles' => 'resources/src/mediawiki.special/mediawiki.special.css',
                'targets' => [ 'desktop', 'mobile' ],
        ],
@@ -1737,7 +1739,6 @@ return [
                'styles' => 'resources/src/mediawiki.special/mediawiki.special.apisandbox.css',
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.apisandbox.js',
                'dependencies' => [
-                       'mediawiki.special',
                        'mediawiki.api',
                        'mediawiki.jqueryMsg',
                        'oojs-ui',
@@ -1880,7 +1881,6 @@ return [
        ],
        'mediawiki.special.recentchanges' => [
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.recentchanges.js',
-               'dependencies' => 'mediawiki.special',
                'position' => 'top',
        ],
        'mediawiki.special.search' => [
index d04e3a6..d387a2d 100644 (file)
@@ -249,6 +249,9 @@ div.thumbinner {
        padding: 3px !important;
        font-size: 94%;
        text-align: center;
+       /* new block formatting context,
+        * to clear background from floating content  */
+       overflow: hidden;
 }
 
 html .thumbimage {
@@ -261,8 +264,6 @@ html .thumbcaption {
        line-height: 1.4em;
        padding: 3px !important;
        font-size: 94%;
-       overflow: hidden;
-       word-wrap: break-word;
 }
 
 /* @noflip */
index c1b671b..f6c3cdb 100644 (file)
@@ -90,6 +90,9 @@ div.thumbinner {
        background-color: #f9f9f9;
        font-size: 94%;
        text-align: center;
+       /* new block formatting context,
+        * to clear background from floating content  */
+       overflow: hidden;
 }
 
 html .thumbimage {
@@ -102,8 +105,6 @@ html .thumbcaption {
        padding: 3px;
        font-size: 94%;
        text-align: left;
-       overflow: hidden;
-       word-wrap: break-word;
 }
 
 div.magnify {
index 3dacf64..85bf9f6 100644 (file)
@@ -152,6 +152,9 @@ div.thumbinner {
        background-color: #f9f9f9;
        font-size: 94%;
        text-align: center;
+       /* new block formatting context,
+        * to clear background from floating content  */
+       overflow: hidden;
 }
 
 html .thumbimage {
@@ -163,8 +166,6 @@ html .thumbcaption {
        line-height: 1.4em;
        padding: 3px;
        font-size: 94%;
-       overflow: hidden;
-       word-wrap: break-word;
        /* Default styles when there's no .mw-content-ltr or .mw-content-rtl, overridden below */
        text-align: left;
 }
index 7aeafec..3d90307 100644 (file)
        *
        * @class mw.special.ApiSandbox
        */
-       mw.special.ApiSandbox = ApiSandbox = {
+       ApiSandbox = {
                /**
                 * Initialize the UI
                 *
 
        $( ApiSandbox.init );
 
+       module.exports = ApiSandbox;
+
 }( jQuery, mediaWiki, OO ) );
diff --git a/resources/src/mediawiki.special/mediawiki.special.js b/resources/src/mediawiki.special/mediawiki.special.js
deleted file mode 100644 (file)
index 630d162..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/*!
- * Namespace for mediawiki.special.* modules
- */
-
-/**
- * @class mw.special
- * @singleton
- */
-mediaWiki.special = {};
index d43b62b..bdd45bc 100644 (file)
@@ -34,6 +34,6 @@
 
        $( rc.init );
 
-       mw.special.recentchanges = rc;
+       module.exports = rc;
 
 }( mediaWiki, jQuery ) );
index ebb6d90..8b100a2 100644 (file)
@@ -46,6 +46,7 @@ $wgAutoloadClasses += [
        'ResourceLoaderTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
        'ResourceLoaderFileModuleTestModule' => "$testDir/phpunit/ResourceLoaderTestCase.php",
        'TestUser' => "$testDir/phpunit/includes/TestUser.php",
+       'TestUserRegistry' => "$testDir/phpunit/includes/TestUserRegistry.php",
        'LessFileCompilationTest' => "$testDir/phpunit/LessFileCompilationTest.php",
 
        # tests/phpunit/includes
index 910e9ae..523953c 100644 (file)
@@ -2269,9 +2269,11 @@ Entities inside <pre>
 <nowiki> inside of #tag:pre
 !! wikitext
 {{#tag:pre|Foo <nowiki>&rarr;bar</nowiki>}}
-!! html
+!! html/php
 <pre>Foo &#8594;bar</pre>
 
+!! html/parsoid
+<pre about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"pi":[[{"k":"1"}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"#tag:pre","function":"#tag"},"params":{"1":{"wt":"Foo &lt;nowiki>&amp;rarr;bar&lt;/nowiki>"}},"i":0}}]}'>Foo <span typeof="mw:Entity">→</span>bar</pre>
 !! end
 
 !! test
@@ -2822,7 +2824,7 @@ parsoid
 !! wikitext
 {{echo|[{{fullurl:{{FULLPAGENAME}}|action=edit}} bar]}}
 !! html
-<p typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"[{{fullurl:{{FULLPAGENAME}}|action=edit}} bar]"}},"i":0}}]}'>[Main Page bar]</p>
+<p typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"[{{fullurl:{{FULLPAGENAME}}|action=edit}} bar]"}},"i":0}}]}'>[Main_Page bar]</p>
 !! end
 
 !! test
@@ -7489,7 +7491,7 @@ Example for such a section: == < ==
 <p><a href="#.3C">#&lt;</a><a href="#.3E">#&gt;</a>
 </p>
 !! html/parsoid
-<p><a rel="mw:WikiLink" href="./Main%20Page#%3C" title="Main Page">#&lt;</a><a rel="mw:WikiLink" href="./Main%20Page#%3E" title="Main Page">#></a></p>
+<p><a rel="mw:WikiLink" href="./Main_Page#%3C" title="Main Page">#&lt;</a><a rel="mw:WikiLink" href="./Main_Page#%3E" title="Main Page">#></a></p>
 !! end
 
 !! test
@@ -9940,7 +9942,7 @@ Parsoid: Page property magic word with magic word contents
 !! wikitext
 {{DISPLAYTITLE:''{{PAGENAME}}''}}
 !! html/parsoid
-<meta property="mw:PageProp/displaytitle" content="Main Page" about="#mwt2" typeof="mw:ExpandedAttrs" data-parsoid='{"src":"{{DISPLAYTITLE:&#39;&#39;{{PAGENAME}}&#39;&#39;}}"}' data-mw='{"attribs":[[{"txt":"content"},{"html":"&lt;i data-parsoid=&#39;{\"dsr\":[15,31,2,2]}&#39;>&lt;span about=\"#mwt1\" typeof=\"mw:Transclusion\" data-parsoid=&#39;{\"pi\":[[]],\"dsr\":[17,29,null,null]}&#39; data-mw=&#39;{\"parts\":[{\"template\":{\"target\":{\"wt\":\"PAGENAME\",\"function\":\"pagename\"},\"params\":{},\"i\":0}}]}&#39;>Main Page&lt;/span>&lt;/i>"}]]}'/>
+<meta property="mw:PageProp/displaytitle" content="Main_Page" about="#mwt2" typeof="mw:ExpandedAttrs" data-parsoid='{"src":"{{DISPLAYTITLE:&#39;&#39;{{PAGENAME}}&#39;&#39;}}"}' data-mw='{"attribs":[[{"txt":"content"},{"html":"&lt;i data-parsoid=&#39;{\"dsr\":[15,31,2,2]}&#39;>&lt;span about=\"#mwt1\" typeof=\"mw:Transclusion\" data-parsoid=&#39;{\"pi\":[[]],\"dsr\":[17,29,null,null]}&#39; data-mw=&#39;{\"parts\":[{\"template\":{\"target\":{\"wt\":\"PAGENAME\",\"function\":\"pagename\"},\"params\":{},\"i\":0}}]}&#39;>Main_Page&lt;/span>&lt;/i>"}]]}'/>
 !! end
 
 !! test
@@ -13953,7 +13955,7 @@ Redirected image
 !! wikitext
 [[Image:Barfoo.jpg]]
 !! html/php
-<p><a href="/wiki/File:Barfoo.jpg" title="File:Barfoo.jpg" class="mw-redirect">File:Barfoo.jpg</a>
+<p><a href="/wiki/File:Barfoo.jpg" class="mw-redirect" title="File:Barfoo.jpg">File:Barfoo.jpg</a>
 </p>
 !! end
 
index 9672cdc..47d96e1 100644 (file)
@@ -127,6 +127,42 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                self::prepareServices( new GlobalVarConfig() );
        }
 
+       /**
+        * Convenience method for getting an immutable test user
+        *
+        * @since 1.28
+        *
+        * @param string[] $groups Groups the test user should be in.
+        * @return TestUser
+        */
+       public static function getTestUser( $groups = [] ) {
+               return TestUserRegistry::getImmutableTestUser( $groups );
+       }
+
+       /**
+        * Convenience method for getting a mutable test user
+        *
+        * @since 1.28
+        *
+        * @param string[] $groups Groups the test user should be added in.
+        * @return TestUser
+        */
+       public static function getMutableTestUser( $groups = [] ) {
+               return TestUserRegistry::getMutableTestUser( __CLASS__, $groups );
+       }
+
+       /**
+        * Convenience method for getting an immutable admin test user
+        *
+        * @since 1.28
+        *
+        * @param string[] $groups Groups the test user should be added to.
+        * @return TestUser
+        */
+       public static function getTestSysop() {
+               return self::getTestUser( [ 'sysop', 'bureaucrat' ] );
+       }
+
        /**
         * Prepare service configuration for unit testing.
         *
@@ -321,7 +357,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
 
                $needsResetDB = false;
 
-               if ( $this->needsDB() ) {
+               if ( !self::$dbSetup || $this->needsDB() ) {
                        // set up a DB connection for this test to use
 
                        self::$useTemporaryTables = !$this->getCliArg( 'use-normal-tables' );
@@ -883,7 +919,7 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
        protected function insertPage( $pageName, $text = 'Sample page for unit test.' ) {
                $title = Title::newFromText( $pageName, 0 );
 
-               $user = User::newFromName( 'UTSysop' );
+               $user = static::getTestSysop()->getUser();
                $comment = __METHOD__ . ': Sample page for unit test.';
 
                // Avoid memory leak...?
@@ -934,36 +970,33 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
 
                        # Insert 0 user to prevent FK violations
                        # Anonymous user
-                       $this->db->insert( 'user', [
-                               'user_id' => 0,
-                               'user_name' => 'Anonymous' ], __METHOD__, [ 'IGNORE' ] );
+                       if ( !$this->db->selectField( 'user', '1', [ 'user_id' => 0 ] ) ) {
+                               $this->db->insert( 'user', [
+                                       'user_id' => 0,
+                                       'user_name' => 'Anonymous' ], __METHOD__, [ 'IGNORE' ] );
+                       }
 
                        # Insert 0 page to prevent FK violations
                        # Blank page
-                       $this->db->insert( 'page', [
-                               'page_id' => 0,
-                               'page_namespace' => 0,
-                               'page_title' => ' ',
-                               'page_restrictions' => null,
-                               'page_is_redirect' => 0,
-                               'page_is_new' => 0,
-                               'page_random' => 0,
-                               'page_touched' => $this->db->timestamp(),
-                               'page_latest' => 0,
-                               'page_len' => 0 ], __METHOD__, [ 'IGNORE' ] );
+                       if ( !$this->db->selectField( 'page', '1', [ 'page_id' => 0 ] ) ) {
+                               $this->db->insert( 'page', [
+                                       'page_id' => 0,
+                                       'page_namespace' => 0,
+                                       'page_title' => ' ',
+                                       'page_restrictions' => null,
+                                       'page_is_redirect' => 0,
+                                       'page_is_new' => 0,
+                                       'page_random' => 0,
+                                       'page_touched' => $this->db->timestamp(),
+                                       'page_latest' => 0,
+                                       'page_len' => 0 ], __METHOD__, [ 'IGNORE' ] );
+                       }
                }
 
                User::resetIdByNameCache();
 
                // Make sysop user
-               $user = User::newFromName( 'UTSysop' );
-
-               if ( $user->idForName() == 0 ) {
-                       $user->addToDatabase();
-                       TestUser::setPasswordForUser( $user, 'UTSysopPassword' );
-                       $user->addGroup( 'sysop' );
-                       $user->addGroup( 'bureaucrat' );
-               }
+               $user = static::getTestSysop()->getUser();
 
                // Make 1 page with 1 revision
                $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
@@ -1187,6 +1220,8 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                                TestingAccessWrapper::newFromClass( 'User' )
                                        ->getInProcessCache()
                                        ->clear();
+
+                               TestUserRegistry::clear();
                        }
 
                        $truncate = in_array( $db->getType(), [ 'oracle', 'mysql' ] );
index 1bf8729..0dc12c7 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use MediaWiki\MediaWikiServices;
+
 /**
  * @group Database
  */
@@ -34,35 +36,35 @@ class LinkerTest extends MediaWikiLangTestCase {
                        # ## ANONYMOUS USER ########################################
                        [
                                '<a href="/wiki/Special:Contributions/JohnDoe" '
-                                       . 'title="Special:Contributions/JohnDoe" '
-                                       . 'class="mw-userlink mw-anonuserlink">JohnDoe</a>',
+                                       . 'class="mw-userlink mw-anonuserlink" '
+                                       . 'title="Special:Contributions/JohnDoe">JohnDoe</a>',
                                0, 'JohnDoe', false,
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/::1" '
-                                       . 'title="Special:Contributions/::1" '
-                                       . 'class="mw-userlink mw-anonuserlink">::1</a>',
+                                       . 'class="mw-userlink mw-anonuserlink" '
+                                       . 'title="Special:Contributions/::1">::1</a>',
                                0, '::1', false,
                                'Anonymous with pretty IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/0:0:0:0:0:0:0:1" '
-                                       . 'title="Special:Contributions/0:0:0:0:0:0:0:1" '
-                                       . 'class="mw-userlink mw-anonuserlink">::1</a>',
+                                       . 'class="mw-userlink mw-anonuserlink" '
+                                       . 'title="Special:Contributions/0:0:0:0:0:0:0:1">::1</a>',
                                0, '0:0:0:0:0:0:0:1', false,
                                'Anonymous with almost pretty IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001" '
-                                       . 'title="Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001" '
-                                       . 'class="mw-userlink mw-anonuserlink">::1</a>',
+                                       . 'class="mw-userlink mw-anonuserlink" '
+                                       . 'title="Special:Contributions/0000:0000:0000:0000:0000:0000:0000:0001">::1</a>',
                                0, '0000:0000:0000:0000:0000:0000:0000:0001', false,
                                'Anonymous with full IPv6'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/::1" '
-                                       . 'title="Special:Contributions/::1" '
-                                       . 'class="mw-userlink mw-anonuserlink">AlternativeUsername</a>',
+                                       . 'class="mw-userlink mw-anonuserlink" '
+                                       . 'title="Special:Contributions/::1">AlternativeUsername</a>',
                                0, '::1', 'AlternativeUsername',
                                'Anonymous with pretty IPv6 and an alternative username'
                        ],
@@ -70,15 +72,15 @@ class LinkerTest extends MediaWikiLangTestCase {
                        # IPV4
                        [
                                '<a href="/wiki/Special:Contributions/127.0.0.1" '
-                                       . 'title="Special:Contributions/127.0.0.1" '
-                                       . 'class="mw-userlink mw-anonuserlink">127.0.0.1</a>',
+                                       . 'class="mw-userlink mw-anonuserlink" '
+                                       . 'title="Special:Contributions/127.0.0.1">127.0.0.1</a>',
                                0, '127.0.0.1', false,
                                'Anonymous with IPv4'
                        ],
                        [
                                '<a href="/wiki/Special:Contributions/127.0.0.1" '
-                                       . 'title="Special:Contributions/127.0.0.1" '
-                                       . 'class="mw-userlink mw-anonuserlink">AlternativeUsername</a>',
+                                       . 'class="mw-userlink mw-anonuserlink" '
+                                       . 'title="Special:Contributions/127.0.0.1">AlternativeUsername</a>',
                                0, '127.0.0.1', 'AlternativeUsername',
                                'Anonymous with IPv4 and an alternative username'
                        ],
@@ -420,4 +422,53 @@ class LinkerTest extends MediaWikiLangTestCase {
                $out = Linker::link( $title );
                $this->assertEquals( $expected, $out );
        }
+
+       /**
+        * @covers Linker::getLinkColour
+        */
+       public function testGetLinkColour() {
+               $linkCache = MediaWikiServices::getInstance()->getLinkCache();
+               $foobarTitle = Title::makeTitle( NS_MAIN, 'FooBar' );
+               $redirectTitle = Title::makeTitle( NS_MAIN, 'Redirect' );
+               $userTitle = Title::makeTitle( NS_USER, 'Someuser' );
+               $linkCache->addGoodLinkObj(
+                       1, // id
+                       $foobarTitle,
+                       10, // len
+                       0 // redir
+               );
+               $linkCache->addGoodLinkObj(
+                       2, // id
+                       $redirectTitle,
+                       10, // len
+                       1 // redir
+               );
+
+               $linkCache->addGoodLinkObj(
+                       3, // id
+                       $userTitle,
+                       10, // len
+                       0 // redir
+               );
+
+               $this->assertEquals(
+                       '',
+                       Linker::getLinkColour( $foobarTitle, 0 )
+               );
+
+               $this->assertEquals(
+                       'stub',
+                       Linker::getLinkColour( $foobarTitle, 20 )
+               );
+
+               $this->assertEquals(
+                       'mw-redirect',
+                       Linker::getLinkColour( $redirectTitle, 0 )
+               );
+
+               $this->assertEquals(
+                       '',
+                       Linker::getLinkColour( $userTitle, 20 )
+               );
+       }
 }
index 22f6fa6..f44ae32 100644 (file)
@@ -97,13 +97,12 @@ class MergeHistoryTest extends MediaWikiTestCase {
                );
 
                // Sysop with mergehistory permission
-               $sysop = User::newFromName( 'UTSysop' );
+               $sysop = static::getTestSysop()->getUser();
                $status = $mh->checkPermissions( $sysop, '' );
                $this->assertTrue( $status->isOK() );
 
                // Normal user
-               $notSysop = User::newFromName( 'UTNotSysop' );
-               $notSysop->addToDatabase();
+               $notSysop = static::getTestUser()->getUser();
                $status = $mh->checkPermissions( $notSysop, '' );
                $this->assertTrue( $status->hasMessage( 'mergehistory-fail-permission' ) );
        }
@@ -118,7 +117,8 @@ class MergeHistoryTest extends MediaWikiTestCase {
                        Title::newFromText( 'Merge2' )
                );
 
-               $mh->merge( User::newFromName( 'UTSysop' ) );
+               $sysop = static::getTestSysop()->getUser();
+               $mh->merge( $sysop );
                $this->assertEquals( $mh->getMergedRevisionCount(), 1 );
        }
 }
diff --git a/tests/phpunit/includes/TestUserRegistry.php b/tests/phpunit/includes/TestUserRegistry.php
new file mode 100644 (file)
index 0000000..4818b49
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * @since 1.28
+ */
+class TestUserRegistry {
+
+       /** @var TestUser[] (group key => TestUser) */
+       private static $testUsers = [];
+
+       /** @var int Count of users that have been generated */
+       private static $counter = 0;
+
+       /** @var int Random int, included in IDs */
+       private static $randInt;
+
+       public static function getNextId() {
+               if ( !self::$randInt ) {
+                       self::$randInt = mt_rand( 1, 0xFFFFFF );
+               }
+               return sprintf( '%06x.%03x', self::$randInt, ++self::$counter );
+       }
+
+       /**
+        * Get a TestUser object that the caller may modify.
+        *
+        * @since 1.28
+        *
+        * @param string $testName Caller's __CLASS__. Used to generate the
+        *  user's username.
+        * @param string[] $groups Groups the test user should be added to.
+        * @return TestUser
+        */
+       public static function getMutableTestUser( $testName, $groups = [] ) {
+               $id = self::getNextId();
+               $password = wfRandomString( 20 );
+               $testUser = new TestUser(
+                       "TestUser $testName $id",  // username
+                       "Name $id",                // real name
+                       "$id@mediawiki.test",      // e-mail
+                       $groups,                   // groups
+                       $password                  // password
+               );
+               $testUser->getUser()->clearInstanceCache();
+               return $testUser;
+       }
+
+       /**
+        * Get a TestUser object that the caller may not modify.
+        *
+        * Whenever possible, unit tests should use immutable users, because
+        * immutable users can be reused in multiple tests, which helps keep
+        * the unit tests fast.
+        *
+        * @since 1.28
+        *
+        * @param string[] $groups Groups the test user should be added to.
+        * @return TestUser
+        */
+       public static function getImmutableTestUser( $groups = [] ) {
+               $groups = array_unique( $groups );
+               sort( $groups );
+               $key = implode( ',', $groups );
+
+               $testUser = isset( self::$testUsers[$key] )
+                       ? self::$testUsers[$key]
+                       : false;
+
+               if ( !$testUser || !$testUser->getUser()->isLoggedIn() ) {
+                       $id = self::getNextId();
+                       // Hack! If this is the primary sysop account, make the username
+                       // be 'UTSysop', for back-compat, and for the sake of PHPUnit data
+                       // provider methods, which are executed before the test database
+                       // is set up. See T136348.
+                       if ( $groups === [ 'bureaucrat', 'sysop' ] ) {
+                               $username = 'UTSysop';
+                               $password = 'UTSysopPassword';
+                       } else {
+                               $username = "TestUser $id";
+                               $password = wfRandomString( 20 );
+                       }
+                       self::$testUsers[$key] = $testUser = new TestUser(
+                               $username,            // username
+                               "Name $id",           // real name
+                               "$id@mediawiki.test", // e-mail
+                               $groups,              // groups
+                               $password             // password
+                       );
+               }
+
+               $testUser->getUser()->clearInstanceCache();
+               return self::$testUsers[$key];
+       }
+
+       /**
+        * Clear the registry.
+        *
+        * TestUsers created by this class will not be deleted, but any handles
+        * to existing immutable TestUsers will be deleted, ensuring these users
+        * are not reused. We don't reset the counter or random string by design.
+        *
+        * @since 1.28
+        *
+        * @param string[] $groups Groups the test user should be added to.
+        * @return TestUser
+        */
+       public static function clear() {
+               self::$testUsers = [];
+       }
+}
index f1f9295..def9c5d 100644 (file)
@@ -21,14 +21,12 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
 
        protected function setUp() {
                parent::setUp();
-               self::$users['ApiQueryWatchlistIntegrationTestUser']
-                       = new TestUser( 'ApiQueryWatchlistIntegrationTestUser' );
-               self::$users['ApiQueryWatchlistIntegrationTestUser2']
-                       = new TestUser( 'ApiQueryWatchlistIntegrationTestUser2' );
+               self::$users['ApiQueryWatchlistIntegrationTestUser'] = $this->getMutableTestUser();
+               self::$users['ApiQueryWatchlistIntegrationTestUser2'] = $this->getMutableTestUser();
                $this->doLogin( 'ApiQueryWatchlistIntegrationTestUser' );
        }
 
-       private function getTestUser() {
+       private function getLoggedInTestUser() {
                return self::$users['ApiQueryWatchlistIntegrationTestUser']->getUser();
        }
 
@@ -36,10 +34,6 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
                return self::$users['ApiQueryWatchlistIntegrationTestUser2']->getUser();
        }
 
-       private function getSysopTestUser() {
-               return self::$users['sysop']->getUser();
-       }
-
        private function doPageEdit( User $user, LinkTarget $target, $content, $summary ) {
                $title = Title::newFromLinkTarget( $target );
                $page = WikiPage::factory( $title );
@@ -244,7 +238,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        private function cleanTestUsersWatchlist() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $store = $this->getWatchedItemStore();
                $items = $store->getWatchedItemsForUser( $user );
                foreach ( $items as $item ) {
@@ -257,7 +251,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
                // the user with the same user ID as user used here as the test user
                $this->cleanTestUsersWatchlist();
 
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdit(
                        $user,
@@ -290,7 +284,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testIdsPropParameter() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdit(
                        $user,
@@ -311,7 +305,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testTitlePropParameter() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdits(
@@ -351,7 +345,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testFlagsPropParameter() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $normalEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $minorEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageM' );
                $botEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageB' );
@@ -412,7 +406,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testUserPropParameter() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $userEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $anonEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageA' );
                $this->doPageEdit(
@@ -447,7 +441,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testUserIdPropParameter() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $userEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $anonEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageA' );
                $this->doPageEdit(
@@ -484,7 +478,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testCommentPropParameter() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdit(
                        $user,
@@ -508,7 +502,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testParsedCommentPropParameter() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdit(
                        $user,
@@ -532,7 +526,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testTimestampPropParameter() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdit(
                        $user,
@@ -551,7 +545,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testSizesPropParameter() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdit(
                        $user,
@@ -585,7 +579,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
                        'Create the page'
                );
                $store = $this->getWatchedItemStore();
-               $store->addWatch( $this->getTestUser(), $target );
+               $store->addWatch( $this->getLoggedInTestUser(), $target );
                $store->updateNotificationTimestamp(
                        $otherUser,
                        $target,
@@ -620,7 +614,8 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testPatrolPropParameter() {
-               $user = $this->getSysopTestUser();
+               $testUser = static::getTestSysop();
+               $user = $testUser->getUser();
                $this->setupPatrolledSpecificFixtures( $user );
 
                $result = $this->doListWatchlistRequest( [ 'wlprop' => 'patrol', ], $user );
@@ -639,7 +634,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
 
        private function createPageAndDeleteIt( LinkTarget $target ) {
                $this->doPageEdit(
-                       $this->getTestUser(),
+                       $this->getLoggedInTestUser(),
                        $target,
                        'Some Content',
                        'Create the page that will be deleted'
@@ -651,7 +646,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->createPageAndDeleteIt( $target );
 
-               $this->watchPages( $this->getTestUser(), [ $target ] );
+               $this->watchPages( $this->getLoggedInTestUser(), [ $target ] );
 
                $result = $this->doListWatchlistRequest( [ 'wlprop' => 'loginfo', ] );
 
@@ -671,7 +666,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testEmptyPropParameter() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdit(
                        $user,
@@ -694,7 +689,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testNamespaceParam() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdits(
@@ -729,7 +724,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testUserParam() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $otherUser = $this->getNonLoggedInTestUser();
                $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
@@ -766,7 +761,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testExcludeUserParam() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $otherUser = $this->getNonLoggedInTestUser();
                $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
@@ -803,7 +798,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testShowMinorParams() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdits(
                        $user,
@@ -837,7 +832,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testShowBotParams() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doBotPageEdit(
                        $user,
@@ -861,7 +856,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testShowAnonParams() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doAnonPageEdit(
                        $target,
@@ -890,7 +885,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testShowUnreadParams() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $otherUser = $this->getNonLoggedInTestUser();
                $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
@@ -948,7 +943,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testShowPatrolledParams() {
-               $user = $this->getSysopTestUser();
+               $user = static::getTestSysop()->getUser();
                $this->setupPatrolledSpecificFixtures( $user );
 
                $resultPatrolled = $this->doListWatchlistRequest( [
@@ -974,7 +969,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testNewAndEditTypeParameters() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdits(
@@ -1025,7 +1020,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testLogTypeParameters() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->createPageAndDeleteIt( $subjectTarget );
@@ -1093,7 +1088,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testExternalTypeParameters() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdit(
@@ -1129,7 +1124,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testCategorizeTypeParameter() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $categoryTarget = new TitleValue( NS_CATEGORY, 'ApiQueryWatchlistIntegrationTestCategory' );
                $this->doPageEdits(
@@ -1180,7 +1175,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testLimitParam() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target1 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $target2 = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
                $target3 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage2' );
@@ -1249,7 +1244,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testAllRevParam() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdits(
                        $user,
@@ -1299,7 +1294,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testDirParams() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdits(
@@ -1355,7 +1350,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testStartEndParams() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdit(
                        $user,
@@ -1390,7 +1385,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testContinueParam() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target1 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $target2 = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
                $target3 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage2' );
@@ -1456,7 +1451,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        public function testOwnerAndTokenParams() {
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdit(
-                       $this->getTestUser(),
+                       $this->getLoggedInTestUser(),
                        $target,
                        'Some Content',
                        'Create the page'
@@ -1509,7 +1504,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testGeneratorWatchlistPropInfo_returnsWatchedPages() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdit(
                        $user,
@@ -1541,7 +1536,7 @@ class ApiQueryWatchlistIntegrationTest extends ApiTestCase {
        }
 
        public function testGeneratorWatchlistPropRevisions_returnsWatchedItemsRevisions() {
-               $user = $this->getTestUser();
+               $user = $this->getLoggedInTestUser();
                $target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
                $this->doPageEdits(
                        $user,
index ff5640a..e90b46f 100644 (file)
@@ -8,11 +8,6 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
         */
        protected $apiContext;
 
-       /**
-        * @var array
-        */
-       protected $tablesUsed = [ 'user', 'user_groups', 'user_properties' ];
-
        protected function setUp() {
                global $wgServer, $wgDisableAuthManager;
 
@@ -22,18 +17,8 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
                ApiQueryInfo::resetTokenCache(); // tokens are invalid because we cleared the session
 
                self::$users = [
-                       'sysop' => new TestUser(
-                               'Apitestsysop',
-                               'Api Test Sysop',
-                               'api_test_sysop@example.com',
-                               [ 'sysop' ]
-                       ),
-                       'uploader' => new TestUser(
-                               'Apitestuser',
-                               'Api Test User',
-                               'api_test_user@example.com',
-                               []
-                       )
+                       'sysop' => static::getTestSysop(),
+                       'uploader' => static::getTestUser(),
                ];
 
                $this->setMwGlobals( [
@@ -162,15 +147,19 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
                }
        }
 
-       protected function doLogin( $user = 'sysop' ) {
-               if ( !array_key_exists( $user, self::$users ) ) {
-                       throw new MWException( "Can not log in to undefined user $user" );
+       protected function doLogin( $testUser = 'sysop' ) {
+               if ( $testUser === null ) {
+                       $testUser = static::getTestSysop();
+               } elseif ( is_string( $testUser ) && array_key_exists( $testUser, self::$users ) ) {
+                       $testUser = self::$users[ $testUser ];
+               } elseif ( !$testUser instanceof TestUser ) {
+                       throw new MWException( "Can not log in to undefined user $testUser" );
                }
 
                $data = $this->doApiRequest( [
                        'action' => 'login',
-                       'lgname' => self::$users[$user]->username,
-                       'lgpassword' => self::$users[$user]->password ] );
+                       'lgname' => $testUser->username,
+                       'lgpassword' => $testUser->password ] );
 
                $token = $data[0]['login']['token'];
 
@@ -178,8 +167,8 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
                        [
                                'action' => 'login',
                                'lgtoken' => $token,
-                               'lgname' => self::$users[$user]->username,
-                               'lgpassword' => self::$users[$user]->password,
+                               'lgname' => $testUser->username,
+                               'lgpassword' => $testUser->password,
                        ],
                        $data[2]
                );
@@ -187,7 +176,7 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
                if ( $data[0]['login']['result'] === 'Success' ) {
                        // DWIM
                        global $wgUser;
-                       $wgUser = self::$users[$user]->getUser();
+                       $wgUser = $testUser->getUser();
                        RequestContext::getMain()->setUser( $wgUser );
                }
 
index fa68dee..713c27e 100644 (file)
@@ -62,6 +62,10 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
        }
 
        public function testBasics() {
+               $user = $this->getMutableTestUser()->getUser();
+               $userName = $user->getName();
+               $lowerInitialUserName = mb_strtolower( $userName[0] ) . substr( $userName, 1 );
+
                $provider = new LocalPasswordPrimaryAuthenticationProvider();
 
                $this->assertSame(
@@ -69,8 +73,8 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
                        $provider->accountCreationType()
                );
 
-               $this->assertTrue( $provider->testUserExists( 'UTSysop' ) );
-               $this->assertTrue( $provider->testUserExists( 'uTSysop' ) );
+               $this->assertTrue( $provider->testUserExists( $userName ) );
+               $this->assertTrue( $provider->testUserExists( $lowerInitialUserName ) );
                $this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
                $this->assertFalse( $provider->testUserExists( '<invalid>' ) );
 
@@ -81,7 +85,7 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
                        $provider->accountCreationType()
                );
 
-               $this->assertTrue( $provider->testUserExists( 'UTSysop' ) );
+               $this->assertTrue( $provider->testUserExists( $userName ) );
                $this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
 
                $req = new PasswordAuthenticationRequest;
@@ -91,12 +95,9 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
        }
 
        public function testTestUserCanAuthenticate() {
+               $user = $this->getMutableTestUser()->getUser();
+               $userName = $user->getName();
                $dbw = wfGetDB( DB_MASTER );
-               $oldHash = $dbw->selectField( 'user', 'user_password', [ 'user_name' => 'UTSysop' ] );
-               $cb = new \ScopedCallback( function () use ( $dbw, $oldHash ) {
-                       $dbw->update( 'user', [ 'user_password' => $oldHash ], [ 'user_name' => 'UTSysop' ] );
-               } );
-               $id = \User::idFromName( 'UTSysop' );
 
                $provider = $this->getProvider();
 
@@ -104,23 +105,24 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
 
                $this->assertFalse( $provider->testUserCanAuthenticate( 'DoesNotExist' ) );
 
-               $this->assertTrue( $provider->testUserCanAuthenticate( 'UTSysop' ) );
-               $this->assertTrue( $provider->testUserCanAuthenticate( 'uTSysop' ) );
+               $this->assertTrue( $provider->testUserCanAuthenticate( $userName ) );
+               $lowerInitialUserName = mb_strtolower( $userName[0] ) . substr( $userName, 1 );
+               $this->assertTrue( $provider->testUserCanAuthenticate( $lowerInitialUserName ) );
 
                $dbw->update(
                        'user',
                        [ 'user_password' => \PasswordFactory::newInvalidPassword()->toString() ],
-                       [ 'user_name' => 'UTSysop' ]
+                       [ 'user_name' => $userName ]
                );
-               $this->assertFalse( $provider->testUserCanAuthenticate( 'UTSysop' ) );
+               $this->assertFalse( $provider->testUserCanAuthenticate( $userName ) );
 
                // Really old format
                $dbw->update(
                        'user',
                        [ 'user_password' => '0123456789abcdef0123456789abcdef' ],
-                       [ 'user_name' => 'UTSysop' ]
+                       [ 'user_name' => $userName ]
                );
-               $this->assertTrue( $provider->testUserCanAuthenticate( 'UTSysop' ) );
+               $this->assertTrue( $provider->testUserCanAuthenticate( $userName ) );
        }
 
        public function testSetPasswordResetFlag() {
@@ -139,22 +141,24 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
                $provider->setManager( $this->manager );
                $providerPriv = \TestingAccessWrapper::newFromObject( $provider );
 
+               $user = $this->getMutableTestUser()->getUser();
+               $userName = $user->getName();
                $dbw = wfGetDB( DB_MASTER );
                $row = $dbw->selectRow(
                        'user',
                        '*',
-                       [ 'user_name' => 'UTSysop' ],
+                       [ 'user_name' => $userName ],
                        __METHOD__
                );
 
                $this->manager->removeAuthenticationSessionData( null );
                $row->user_password_expires = wfTimestamp( TS_MW, time() + 200 );
-               $providerPriv->setPasswordResetFlag( 'UTSysop', \Status::newGood(), $row );
+               $providerPriv->setPasswordResetFlag( $userName, \Status::newGood(), $row );
                $this->assertNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
 
                $this->manager->removeAuthenticationSessionData( null );
                $row->user_password_expires = wfTimestamp( TS_MW, time() - 200 );
-               $providerPriv->setPasswordResetFlag( 'UTSysop', \Status::newGood(), $row );
+               $providerPriv->setPasswordResetFlag( $userName, \Status::newGood(), $row );
                $ret = $this->manager->getAuthenticationSessionData( 'reset-pass' );
                $this->assertNotNull( $ret );
                $this->assertSame( 'resetpass-expired', $ret->msg->getKey() );
@@ -162,7 +166,7 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
 
                $this->manager->removeAuthenticationSessionData( null );
                $row->user_password_expires = wfTimestamp( TS_MW, time() - 1 );
-               $providerPriv->setPasswordResetFlag( 'UTSysop', \Status::newGood(), $row );
+               $providerPriv->setPasswordResetFlag( $userName, \Status::newGood(), $row );
                $ret = $this->manager->getAuthenticationSessionData( 'reset-pass' );
                $this->assertNotNull( $ret );
                $this->assertSame( 'resetpass-expired-soft', $ret->msg->getKey() );
@@ -172,7 +176,7 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
                $row->user_password_expires = null;
                $status = \Status::newGood();
                $status->error( 'testing' );
-               $providerPriv->setPasswordResetFlag( 'UTSysop', $status, $row );
+               $providerPriv->setPasswordResetFlag( $userName, $status, $row );
                $ret = $this->manager->getAuthenticationSessionData( 'reset-pass' );
                $this->assertNotNull( $ret );
                $this->assertSame( 'resetpass-validity-soft', $ret->msg->getKey() );
@@ -180,12 +184,11 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
        }
 
        public function testAuthentication() {
+               $testUser = $this->getMutableTestUser();
+               $userName = $testUser->getUser()->getName();
+
                $dbw = wfGetDB( DB_MASTER );
-               $oldHash = $dbw->selectField( 'user', 'user_password', [ 'user_name' => 'UTSysop' ] );
-               $cb = new \ScopedCallback( function () use ( $dbw, $oldHash ) {
-                       $dbw->update( 'user', [ 'user_password' => $oldHash ], [ 'user_name' => 'UTSysop' ] );
-               } );
-               $id = \User::idFromName( 'UTSysop' );
+               $id = \User::idFromName( $userName );
 
                $req = new PasswordAuthenticationRequest();
                $req->action = AuthManager::ACTION_LOGIN;
@@ -230,8 +233,8 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
                );
 
                // Validation failure
-               $req->username = 'UTSysop';
-               $req->password = 'UTSysopPassword';
+               $req->username = $userName;
+               $req->password = $testUser->getPassword();
                $this->validity = \Status::newFatal( 'arbitrary-failure' );
                $ret = $provider->beginPrimaryAuthentication( $reqs );
                $this->assertEquals(
@@ -247,7 +250,7 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
                $this->manager->removeAuthenticationSessionData( null );
                $this->validity = \Status::newGood();
                $this->assertEquals(
-                       AuthenticationResponse::newPass( 'UTSysop' ),
+                       AuthenticationResponse::newPass( $userName ),
                        $provider->beginPrimaryAuthentication( $reqs )
                );
                $this->assertNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
@@ -255,19 +258,19 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
                // Successful auth after normalizing name
                $this->manager->removeAuthenticationSessionData( null );
                $this->validity = \Status::newGood();
-               $req->username = 'uTSysop';
+               $req->username = mb_strtolower( $userName[0] ) . substr( $userName, 1 );
                $this->assertEquals(
-                       AuthenticationResponse::newPass( 'UTSysop' ),
+                       AuthenticationResponse::newPass( $userName ),
                        $provider->beginPrimaryAuthentication( $reqs )
                );
                $this->assertNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
-               $req->username = 'UTSysop';
+               $req->username = $userName;
 
                // Successful auth with reset
                $this->manager->removeAuthenticationSessionData( null );
                $this->validity->error( 'arbitrary-warning' );
                $this->assertEquals(
-                       AuthenticationResponse::newPass( 'UTSysop' ),
+                       AuthenticationResponse::newPass( $userName ),
                        $provider->beginPrimaryAuthentication( $reqs )
                );
                $this->assertNotNull( $this->manager->getAuthenticationSessionData( 'reset-pass' ) );
@@ -287,7 +290,7 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
 
                // Correct handling of legacy encodings
                $password = ':B:salt:' . md5( 'salt-' . md5( "\xe1\xe9\xed\xf3\xfa" ) );
-               $dbw->update( 'user', [ 'user_password' => $password ], [ 'user_name' => 'UTSysop' ] );
+               $dbw->update( 'user', [ 'user_password' => $password ], [ 'user_name' => $userName ] );
                $req->password = 'áéíóú';
                $ret = $provider->beginPrimaryAuthentication( $reqs );
                $this->assertEquals(
@@ -301,7 +304,7 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
 
                $this->config->set( 'LegacyEncoding', true );
                $this->assertEquals(
-                       AuthenticationResponse::newPass( 'UTSysop' ),
+                       AuthenticationResponse::newPass( $userName ),
                        $provider->beginPrimaryAuthentication( $reqs )
                );
 
@@ -319,19 +322,19 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
                // Correct handling of really old password hashes
                $this->config->set( 'PasswordSalt', false );
                $password = md5( 'FooBar' );
-               $dbw->update( 'user', [ 'user_password' => $password ], [ 'user_name' => 'UTSysop' ] );
+               $dbw->update( 'user', [ 'user_password' => $password ], [ 'user_name' => $userName ] );
                $req->password = 'FooBar';
                $this->assertEquals(
-                       AuthenticationResponse::newPass( 'UTSysop' ),
+                       AuthenticationResponse::newPass( $userName ),
                        $provider->beginPrimaryAuthentication( $reqs )
                );
 
                $this->config->set( 'PasswordSalt', true );
                $password = md5( "$id-" . md5( 'FooBar' ) );
-               $dbw->update( 'user', [ 'user_password' => $password ], [ 'user_name' => 'UTSysop' ] );
+               $dbw->update( 'user', [ 'user_password' => $password ], [ 'user_name' => $userName ] );
                $req->password = 'FooBar';
                $this->assertEquals(
-                       AuthenticationResponse::newPass( 'UTSysop' ),
+                       AuthenticationResponse::newPass( $userName ),
                        $provider->beginPrimaryAuthentication( $reqs )
                );
 
@@ -407,29 +410,24 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
 
        /**
         * @dataProvider provideProviderChangeAuthenticationData
-        * @param string $user
+        * @param callable|bool $usernameTransform
         * @param string $type
         * @param bool $loginOnly
         * @param bool $changed
         */
-       public function testProviderChangeAuthenticationData( $user, $type, $loginOnly, $changed ) {
+       public function testProviderChangeAuthenticationData(
+                       $usernameTransform, $type, $loginOnly, $changed ) {
+               $testUser = $this->getMutableTestUser();
+               $user = $testUser->getUser()->getName();
+               if ( is_callable( $usernameTransform ) ) {
+                       $user = call_user_func( $usernameTransform, $user );
+               }
                $cuser = ucfirst( $user );
-               $oldpass = 'UTSysopPassword';
+               $oldpass = $testUser->getPassword();
                $newpass = 'NewPassword';
 
                $dbw = wfGetDB( DB_MASTER );
-               $oldHash = $dbw->selectField( 'user', 'user_password', [ 'user_name' => $cuser ] );
                $oldExpiry = $dbw->selectField( 'user', 'user_password_expires', [ 'user_name' => $cuser ] );
-               $cb = new \ScopedCallback( function () use ( $dbw, $cuser, $oldHash, $oldExpiry ) {
-                       $dbw->update(
-                               'user',
-                               [
-                                       'user_password' => $oldHash,
-                                       'user_password_expires' => $oldExpiry,
-                               ],
-                               [ 'user_name' => $cuser ]
-                       );
-               } );
 
                $this->mergeMwGlobalArrayValue( 'wgHooks', [
                        'ResetPasswordExpiration' => [ function ( $user, &$expires ) {
@@ -525,12 +523,12 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
 
        public static function provideProviderChangeAuthenticationData() {
                return [
-                       [ 'UTSysop', AuthenticationRequest::class, false, false ],
-                       [ 'UTSysop', PasswordAuthenticationRequest::class, false, true ],
-                       [ 'UTSysop', AuthenticationRequest::class, true, false ],
-                       [ 'UTSysop', PasswordAuthenticationRequest::class, true, true ],
-                       [ 'uTSysop', PasswordAuthenticationRequest::class, false, true ],
-                       [ 'uTSysop', PasswordAuthenticationRequest::class, true, true ],
+                       [ false, AuthenticationRequest::class, false, false ],
+                       [ false, PasswordAuthenticationRequest::class, false, true ],
+                       [ false, AuthenticationRequest::class, true, false ],
+                       [ false, PasswordAuthenticationRequest::class, true, true ],
+                       [ 'ucfirst', PasswordAuthenticationRequest::class, false, true ],
+                       [ 'ucfirst', PasswordAuthenticationRequest::class, true, true ],
                ];
        }
 
@@ -640,10 +638,6 @@ class LocalPasswordPrimaryAuthenticationProviderTest extends \MediaWikiTestCase
                // We have to cheat a bit to avoid having to add a new user to
                // the database to test the actual setting of the password works right
                $dbw = wfGetDB( DB_MASTER );
-               $oldHash = $dbw->selectField( 'user', 'user_password', [ 'user_name' => $user ] );
-               $cb = new \ScopedCallback( function () use ( $dbw, $user, $oldHash ) {
-                       $dbw->update( 'user', [ 'user_password' => $oldHash ], [ 'user_name' => $user ] );
-               } );
 
                $user = \User::newFromName( 'UTSysop' );
                $req->username = $user->getName();
index e329f8d..9c723c0 100644 (file)
@@ -6,36 +6,30 @@
  */
 class GenderCacheTest extends MediaWikiLangTestCase {
 
+       /** @var string[] User key => username */
+       private static $nameMap;
+
        function addDBDataOnce() {
                // ensure the correct default gender
                $this->mergeMwGlobalArrayValue( 'wgDefaultUserOptions', [ 'gender' => 'unknown' ] );
 
-               $user = User::newFromName( 'UTMale' );
-               if ( $user->getId() == 0 ) {
-                       $user->addToDatabase();
-                       TestUser::setPasswordForUser( $user, 'UTMalePassword' );
-               }
-               // ensure the right gender
-               $user->setOption( 'gender', 'male' );
-               $user->saveSettings();
+               $male = $this->getMutableTestUser()->getUser();
+               $male->setOption( 'gender', 'male' );
+               $male->saveSettings();
+
+               $female = $this->getMutableTestUser()->getUser();
+               $female->setOption( 'gender', 'female' );
+               $female->saveSettings();
 
-               $user = User::newFromName( 'UTFemale' );
-               if ( $user->getId() == 0 ) {
-                       $user->addToDatabase();
-                       TestUser::setPasswordForUser( $user, 'UTFemalePassword' );
-               }
-               // ensure the right gender
-               $user->setOption( 'gender', 'female' );
-               $user->saveSettings();
+               $default = $this->getMutableTestUser()->getUser();
+               $default->setOption( 'gender', null );
+               $default->saveSettings();
 
-               $user = User::newFromName( 'UTDefaultGender' );
-               if ( $user->getId() == 0 ) {
-                       $user->addToDatabase();
-                       TestUser::setPasswordForUser( $user, 'UTDefaultGenderPassword' );
-               }
-               // ensure the default gender
-               $user->setOption( 'gender', null );
-               $user->saveSettings();
+               self::$nameMap = [
+                       'UTMale'          => $male->getName(),
+                       'UTFemale'        => $female->getName(),
+                       'UTDefaultGender' => $default->getName()
+               ];
        }
 
        /**
@@ -44,8 +38,9 @@ class GenderCacheTest extends MediaWikiLangTestCase {
         * @dataProvider provideUserGenders
         * @covers GenderCache::getGenderOf
         */
-       public function testUserName( $username, $expectedGender ) {
+       public function testUserName( $userKey, $expectedGender ) {
                $genderCache = GenderCache::singleton();
+               $username = isset( self::$nameMap[$userKey] ) ? self::$nameMap[$userKey] : $userKey;
                $gender = $genderCache->getGenderOf( $username );
                $this->assertEquals( $gender, $expectedGender, "GenderCache normal" );
        }
@@ -56,10 +51,10 @@ class GenderCacheTest extends MediaWikiLangTestCase {
         * @dataProvider provideUserGenders
         * @covers GenderCache::getGenderOf
         */
-       public function testUserObjects( $username, $expectedGender ) {
+       public function testUserObjects( $userKey, $expectedGender ) {
+               $username = isset( self::$nameMap[$userKey] ) ? self::$nameMap[$userKey] : $userKey;
                $genderCache = GenderCache::singleton();
-               $user = User::newFromName( $username );
-               $gender = $genderCache->getGenderOf( $user );
+               $gender = $genderCache->getGenderOf( $username );
                $this->assertEquals( $gender, $expectedGender, "GenderCache normal" );
        }
 
@@ -79,22 +74,13 @@ class GenderCacheTest extends MediaWikiLangTestCase {
         * test strip of subpages to avoid unnecessary queries
         * against the never existing username
         *
-        * @dataProvider provideStripSubpages
+        * @dataProvider provideUserGenders
         * @covers GenderCache::getGenderOf
         */
-       public function testStripSubpages( $pageWithSubpage, $expectedGender ) {
+       public function testStripSubpages( $userKey, $expectedGender ) {
+               $username = isset( self::$nameMap[$userKey] ) ? self::$nameMap[$userKey] : $userKey;
                $genderCache = GenderCache::singleton();
-               $gender = $genderCache->getGenderOf( $pageWithSubpage );
+               $gender = $genderCache->getGenderOf( "$username/subpage" );
                $this->assertEquals( $gender, $expectedGender, "GenderCache must strip of subpages" );
        }
-
-       public static function provideStripSubpages() {
-               return [
-                       [ 'UTMale/subpage', 'male' ],
-                       [ 'UTFemale/subpage', 'female' ],
-                       [ 'UTDefaultGender/subpage', 'unknown' ],
-                       [ 'UTNotExist/subpage', 'unknown' ],
-                       [ '127.0.0.1/subpage', 'unknown' ],
-               ];
-       }
 }
index 1d86fb4..e44de09 100644 (file)
@@ -29,6 +29,11 @@ class CategoryMembershipChangeTest extends MediaWikiLangTestCase {
         */
        private static $pageRev = null;
 
+       /**
+        * @var User
+        */
+       private static $revUser = null;
+
        /**
         * @var string
         */
@@ -54,6 +59,7 @@ class CategoryMembershipChangeTest extends MediaWikiLangTestCase {
 
                $page = WikiPage::factory( $title );
                self::$pageRev = $page->getRevision();
+               self::$revUser = User::newFromId( self::$pageRev->getUser( Revision::RAW ) );
        }
 
        private function newChange( Revision $revision = null ) {
@@ -114,7 +120,7 @@ class CategoryMembershipChangeTest extends MediaWikiLangTestCase {
 
                $this->assertTrue( strlen( self::$lastNotifyArgs[0] ) === 14 );
                $this->assertEquals( 'Category:CategoryName', self::$lastNotifyArgs[1]->getPrefixedText() );
-               $this->assertEquals( 'UTSysop', self::$lastNotifyArgs[2]->getName() );
+               $this->assertEquals( self::$revUser->getName(), self::$lastNotifyArgs[2]->getName() );
                $this->assertEquals( '(recentchanges-page-added-to-category: ' . self::$pageName . ')',
                        self::$lastNotifyArgs[3] );
                $this->assertEquals( self::$pageName, self::$lastNotifyArgs[4]->getPrefixedText() );
@@ -135,7 +141,7 @@ class CategoryMembershipChangeTest extends MediaWikiLangTestCase {
 
                $this->assertTrue( strlen( self::$lastNotifyArgs[0] ) === 14 );
                $this->assertEquals( 'Category:CategoryName', self::$lastNotifyArgs[1]->getPrefixedText() );
-               $this->assertEquals( 'UTSysop', self::$lastNotifyArgs[2]->getName() );
+               $this->assertEquals( self::$revUser->getName(), self::$lastNotifyArgs[2]->getName() );
                $this->assertEquals( '(recentchanges-page-removed-from-category: ' . self::$pageName . ')',
                        self::$lastNotifyArgs[3] );
                $this->assertEquals( self::$pageName, self::$lastNotifyArgs[4]->getPrefixedText() );
index b8be8d4..308e6de 100644 (file)
@@ -121,7 +121,7 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase {
         * @return RecentChange
         */
        private function getEditChange( $timestamp ) {
-               $user = $this->getTestUser();
+               $user = $this->getMutableTestUser()->getUser();
                $recentChange = $this->testRecentChangesHelper->makeEditRecentChange(
                        $user, 'Cat', $timestamp, 5, 191, 190, 0, 0
                );
@@ -139,7 +139,7 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase {
                $wikiPage = new WikiPage( Title::newFromText( 'Category:Foo' ) );
                $wikiPage->doEditContent( new WikitextContent( 'Some random text' ), 'category page created' );
 
-               $user = $this->getTestUser();
+               $user = $this->getMutableTestUser()->getUser();
                $recentChange = $this->testRecentChangesHelper->makeCategorizationRecentChange(
                        $user, 'Category:Foo', $wikiPage->getId(), $thisId, $lastId, $timestamp
                );
@@ -147,19 +147,6 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase {
                return $recentChange;
        }
 
-       /**
-        * @return User
-        */
-       private function getTestUser() {
-               $user = User::newFromName( 'TestRecentChangesUser' );
-
-               if ( !$user->getId() ) {
-                       $user->addToDatabase();
-               }
-
-               return $user;
-       }
-
        private function createCategorizationLine( $recentChange ) {
                $enhancedChangesList = $this->newEnhancedChangesList();
                $cacheEntry = $this->testRecentChangesHelper->getCacheEntry( $recentChange );
index bc70a13..51cfadc 100644 (file)
@@ -150,7 +150,7 @@ class OldChangesListTest extends MediaWikiLangTestCase {
        }
 
        private function getNewBotEditChange() {
-               $user = $this->getTestUser();
+               $user = $this->getMutableTestUser()->getUser();
 
                $recentChange = $this->testRecentChangesHelper->makeNewBotEditRecentChange(
                        $user, 'Abc', '20131103212153', 5, 191, 190, 0, 0
@@ -160,7 +160,7 @@ class OldChangesListTest extends MediaWikiLangTestCase {
        }
 
        private function getLogChange( $logType, $logAction ) {
-               $user = $this->getTestUser();
+               $user = $this->getMutableTestUser()->getUser();
 
                $recentChange = $this->testRecentChangesHelper->makeLogRecentChange(
                        $logType, $logAction, $user, 'Abc', '20131103212153', 0, 0
@@ -170,7 +170,7 @@ class OldChangesListTest extends MediaWikiLangTestCase {
        }
 
        private function getEditChange() {
-               $user = $this->getTestUser();
+               $user = $this->getMutableTestUser()->getUser();
                $recentChange = $this->testRecentChangesHelper->makeEditRecentChange(
                        $user, 'Cat', '20131103212153', 5, 191, 190, 0, 0
                );
@@ -183,18 +183,8 @@ class OldChangesListTest extends MediaWikiLangTestCase {
                return new OldChangesList( $context );
        }
 
-       private function getTestUser() {
-               $user = User::newFromName( 'TestRecentChangesUser' );
-
-               if ( !$user->getId() ) {
-                       $user->addToDatabase();
-               }
-
-               return $user;
-       }
-
        private function getContext() {
-               $user = $this->getTestUser();
+               $user = $this->getMutableTestUser()->getUser();
                $context = $this->testRecentChangesHelper->getTestContext( $user );
                $context->setLanguage( 'qqx' );
 
index 602340b..16f210b 100644 (file)
@@ -1,5 +1,8 @@
 <?php
 
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\MediaWikiServices;
+
 /**
  * @covers RCCacheEntryFactory
  *
@@ -14,6 +17,11 @@ class RCCacheEntryFactoryTest extends MediaWikiLangTestCase {
         */
        private $testRecentChangesHelper;
 
+       /**
+        * @var LinkRenderer;
+        */
+       private $linkRenderer;
+
        public function __construct( $name = null, array $data = [], $dataName = '' ) {
                parent::__construct( $name, $data, $dataName );
 
@@ -26,133 +34,113 @@ class RCCacheEntryFactoryTest extends MediaWikiLangTestCase {
                $this->setMwGlobals( [
                        'wgArticlePath' => '/wiki/$1'
                ] );
+
+               $this->linkRenderer = new LinkRenderer(
+                       MediaWikiServices::getInstance()->getTitleFormatter()
+               );
        }
 
-       /**
-        * @dataProvider editChangeProvider
-        */
-       public function testNewFromRecentChange( $expected, $context, $messages,
-               $recentChange, $watched
-       ) {
-               $cacheEntryFactory = new RCCacheEntryFactory( $context, $messages );
-               $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, $watched );
+       public function testNewFromRecentChange() {
+               $user = $this->getMutableTestUser()->getUser();
+               $recentChange = $this->testRecentChangesHelper->makeEditRecentChange(
+                       $user,
+                       'Xyz',
+                       5, // curid
+                       191, // thisid
+                       190, // lastid
+                       '20131103212153',
+                       0, // counter
+                       0 // number of watching users
+               );
+               $cacheEntryFactory = new RCCacheEntryFactory(
+                       $this->getContext(),
+                       $this->getMessages(),
+                       $this->linkRenderer
+               );
+               $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, false );
 
                $this->assertInstanceOf( 'RCCacheEntry', $cacheEntry );
 
-               $this->assertEquals( $watched, $cacheEntry->watched, 'watched' );
-               $this->assertEquals( $expected['timestamp'], $cacheEntry->timestamp, 'timestamp' );
-               $this->assertEquals(
-                       $expected['numberofWatchingusers'], $cacheEntry->numberofWatchingusers,
-                       'watching users'
-               );
-               $this->assertEquals( $expected['unpatrolled'], $cacheEntry->unpatrolled, 'unpatrolled' );
+               $this->assertEquals( false, $cacheEntry->watched, 'watched' );
+               $this->assertEquals( '21:21', $cacheEntry->timestamp, 'timestamp' );
+               $this->assertEquals( 0, $cacheEntry->numberofWatchingusers, 'watching users' );
+               $this->assertEquals( false, $cacheEntry->unpatrolled, 'unpatrolled' );
 
-               $this->assertUserLinks( 'TestRecentChangesUser', $cacheEntry );
+               $this->assertUserLinks( $user->getName(), $cacheEntry );
                $this->assertTitleLink( 'Xyz', $cacheEntry );
 
-               $this->assertQueryLink( 'cur', $expected['cur'], $cacheEntry->curlink, 'cur link' );
-               $this->assertQueryLink( 'prev', $expected['diff'], $cacheEntry->lastlink, 'prev link' );
-               $this->assertQueryLink( 'diff', $expected['diff'], $cacheEntry->difflink, 'diff link' );
+               $diff = [ 'curid' => 5, 'diff' => 191, 'oldid' => 190 ];
+               $cur = [ 'curid' => 5, 'diff' => 0, 'oldid' => 191 ];
+               $this->assertQueryLink( 'cur', $cur, $cacheEntry->curlink, 'cur link' );
+               $this->assertQueryLink( 'prev', $diff, $cacheEntry->lastlink, 'prev link' );
+               $this->assertQueryLink( 'diff', $diff, $cacheEntry->difflink, 'diff link' );
        }
 
-       public function editChangeProvider() {
-               return [
-                       [
-                               [
-                                       'title' => 'Xyz',
-                                       'user' => 'TestRecentChangesUser',
-                                       'diff' => [ 'curid' => 5, 'diff' => 191, 'oldid' => 190 ],
-                                       'cur' => [ 'curid' => 5, 'diff' => 0, 'oldid' => 191 ],
-                                       'timestamp' => '21:21',
-                                       'numberofWatchingusers' => 0,
-                                       'unpatrolled' => false
-                               ],
-                               $this->getContext(),
-                               $this->getMessages(),
-                               $this->testRecentChangesHelper->makeEditRecentChange(
-                                       $this->getTestUser(),
-                                       'Xyz',
-                                       5, // curid
-                                       191, // thisid
-                                       190, // lastid
-                                       '20131103212153',
-                                       0, // counter
-                                       0 // number of watching users
-                               ),
-                               false
-                       ]
+       public function testNewForDeleteChange() {
+               $expected = [
+                       'title' => 'Abc',
+                       'user' => 'TestRecentChangesUser',
+                       'timestamp' => '21:21',
+                       'numberofWatchingusers' => 0,
+                       'unpatrolled' => false
                ];
-       }
-
-       /**
-        * @dataProvider deleteChangeProvider
-        */
-       public function testNewForDeleteChange( $expected, $context, $messages, $recentChange, $watched ) {
-               $cacheEntryFactory = new RCCacheEntryFactory( $context, $messages );
-               $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, $watched );
+               $user = $this->getMutableTestUser()->getUser();
+               $recentChange = $this->testRecentChangesHelper->makeLogRecentChange(
+                       'delete',
+                       'delete',
+                       $user,
+                       'Abc',
+                       '20131103212153',
+                       0, // counter
+                       0 // number of watching users
+               );
+               $cacheEntryFactory = new RCCacheEntryFactory(
+                       $this->getContext(),
+                       $this->getMessages(),
+                       $this->linkRenderer
+               );
+               $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, false );
 
                $this->assertInstanceOf( 'RCCacheEntry', $cacheEntry );
 
-               $this->assertEquals( $watched, $cacheEntry->watched, 'watched' );
-               $this->assertEquals( $expected['timestamp'], $cacheEntry->timestamp, 'timestamp' );
-               $this->assertEquals(
-                       $expected['numberofWatchingusers'],
-                       $cacheEntry->numberofWatchingusers, 'watching users'
-               );
-               $this->assertEquals( $expected['unpatrolled'], $cacheEntry->unpatrolled, 'unpatrolled' );
+               $this->assertEquals( false, $cacheEntry->watched, 'watched' );
+               $this->assertEquals( '21:21', $cacheEntry->timestamp, 'timestamp' );
+               $this->assertEquals( 0, $cacheEntry->numberofWatchingusers, 'watching users' );
+               $this->assertEquals( false, $cacheEntry->unpatrolled, 'unpatrolled' );
 
                $this->assertDeleteLogLink( $cacheEntry );
-               $this->assertUserLinks( 'TestRecentChangesUser', $cacheEntry );
+               $this->assertUserLinks( $user->getName(), $cacheEntry );
 
                $this->assertEquals( 'cur', $cacheEntry->curlink, 'cur link for delete log or rev' );
                $this->assertEquals( 'diff', $cacheEntry->difflink, 'diff link for delete log or rev' );
                $this->assertEquals( 'prev', $cacheEntry->lastlink, 'pref link for delete log or rev' );
        }
 
-       public function deleteChangeProvider() {
-               return [
-                       [
-                               [
-                                       'title' => 'Abc',
-                                       'user' => 'TestRecentChangesUser',
-                                       'timestamp' => '21:21',
-                                       'numberofWatchingusers' => 0,
-                                       'unpatrolled' => false
-                               ],
-                               $this->getContext(),
-                               $this->getMessages(),
-                               $this->testRecentChangesHelper->makeLogRecentChange(
-                                       'delete',
-                                       'delete',
-                                       $this->getTestUser(),
-                                       'Abc',
-                                       '20131103212153',
-                                       0, // counter
-                                       0 // number of watching users
-                               ),
-                               false
-                       ]
-               ];
-       }
-
-       /**
-        * @dataProvider revUserDeleteProvider
-        */
-       public function testNewForRevUserDeleteChange( $expected, $context, $messages,
-               $recentChange, $watched
-       ) {
-               $cacheEntryFactory = new RCCacheEntryFactory( $context, $messages );
-               $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, $watched );
+       public function testNewForRevUserDeleteChange() {
+               $user = $this->getMutableTestUser()->getUser();
+               $recentChange = $this->testRecentChangesHelper->makeDeletedEditRecentChange(
+                       $user,
+                       'Zzz',
+                       '20131103212153',
+                       191, // thisid
+                       190, // lastid
+                       '20131103212153',
+                       0, // counter
+                       0 // number of watching users
+               );
+               $cacheEntryFactory = new RCCacheEntryFactory(
+                       $this->getContext(),
+                       $this->getMessages(),
+                       $this->linkRenderer
+               );
+               $cacheEntry = $cacheEntryFactory->newFromRecentChange( $recentChange, false );
 
                $this->assertInstanceOf( 'RCCacheEntry', $cacheEntry );
 
-               $this->assertEquals( $watched, $cacheEntry->watched, 'watched' );
-               $this->assertEquals( $expected['timestamp'], $cacheEntry->timestamp, 'timestamp' );
-               $this->assertEquals(
-                       $expected['numberofWatchingusers'],
-                       $cacheEntry->numberofWatchingusers, 'watching users'
-               );
-               $this->assertEquals( $expected['unpatrolled'], $cacheEntry->unpatrolled, 'unpatrolled' );
+               $this->assertEquals( false, $cacheEntry->watched, 'watched' );
+               $this->assertEquals( '21:21', $cacheEntry->timestamp, 'timestamp' );
+               $this->assertEquals( 0, $cacheEntry->numberofWatchingusers, 'watching users' );
+               $this->assertEquals( false, $cacheEntry->unpatrolled, 'unpatrolled' );
 
                $this->assertRevDel( $cacheEntry );
                $this->assertTitleLink( 'Zzz', $cacheEntry );
@@ -162,35 +150,6 @@ class RCCacheEntryFactoryTest extends MediaWikiLangTestCase {
                $this->assertEquals( 'prev', $cacheEntry->lastlink, 'pref link for delete log or rev' );
        }
 
-       public function revUserDeleteProvider() {
-               return [
-                       [
-                               [
-                                       'title' => 'Zzz',
-                                       'user' => 'TestRecentChangesUser',
-                                       'diff' => '',
-                                       'cur' => '',
-                                       'timestamp' => '21:21',
-                                       'numberofWatchingusers' => 0,
-                                       'unpatrolled' => false
-                               ],
-                               $this->getContext(),
-                               $this->getMessages(),
-                               $this->testRecentChangesHelper->makeDeletedEditRecentChange(
-                                       $this->getTestUser(),
-                                       'Zzz',
-                                       '20131103212153',
-                                       191, // thisid
-                                       190, // lastid
-                                       '20131103212153',
-                                       0, // counter
-                                       0 // number of watching users
-                               ),
-                               false
-                       ]
-               ];
-       }
-
        private function assertUserLinks( $user, $cacheEntry ) {
                $this->assertTag(
                        [
@@ -308,18 +267,8 @@ class RCCacheEntryFactoryTest extends MediaWikiLangTestCase {
                ];
        }
 
-       private function getTestUser() {
-               $user = User::newFromName( 'TestRecentChangesUser' );
-
-               if ( !$user->getId() ) {
-                       $user->addToDatabase();
-               }
-
-               return $user;
-       }
-
        private function getContext() {
-               $user = $this->getTestUser();
+               $user = $this->getMutableTestUser()->getUser();
                $context = $this->testRecentChangesHelper->getTestContext( $user );
 
                $title = Title::newFromText( 'RecentChanges', NS_SPECIAL );
index faa1dcc..cac3d43 100644 (file)
@@ -1,4 +1,6 @@
 <?php
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\MediaWikiServices;
 
 /**
  * Helper for generating test recent changes entries.
@@ -100,7 +102,10 @@ class TestRecentChangesHelper {
        public function getCacheEntry( $recentChange ) {
                $rcCacheFactory = new RCCacheEntryFactory(
                        new RequestContext(),
-                       [ 'diff' => 'diff', 'cur' => 'cur', 'last' => 'last' ]
+                       [ 'diff' => 'diff', 'cur' => 'cur', 'last' => 'last' ],
+                       new LinkRenderer(
+                               MediaWikiServices::getInstance()->getTitleFormatter()
+                       )
                );
                return $rcCacheFactory->newFromRecentChange( $recentChange, false );
        }
index bd3103b..20e0108 100644 (file)
@@ -21,11 +21,6 @@ class LinkRendererFactoryTest extends MediaWikiLangTestCase {
 
        public static function provideCreateFromLegacyOptions() {
                return [
-                       [
-                               [ 'noclasses' ],
-                               'getNoClasses',
-                               true
-                       ],
                        [
                                [ 'forcearticlepath' ],
                                'getForceArticlePath',
index d4b1587..9bc41c0 100644 (file)
@@ -67,7 +67,8 @@ class BotPasswordSessionProviderTest extends MediaWikiTestCase {
                $passwordFactory->init( \RequestContext::getMain()->getConfig() );
                $passwordHash = $passwordFactory->newFromPlaintext( 'foobaz' );
 
-               $userId = \CentralIdLookup::factory( 'local' )->centralIdFromName( 'UTSysop' );
+               $sysop = static::getTestSysop()->getUser();
+               $userId = \CentralIdLookup::factory( 'local' )->centralIdFromName( $sysop->getName() );
 
                $dbw = wfGetDB( DB_MASTER );
                $dbw->delete(
@@ -182,7 +183,7 @@ class BotPasswordSessionProviderTest extends MediaWikiTestCase {
 
        public function testNewSessionInfoForRequest() {
                $provider = $this->getProvider();
-               $user = \User::newFromName( 'UTSysop' );
+               $user = static::getTestSysop()->getUser();
                $request = $this->getMock( 'FauxRequest', [ 'getIP' ] );
                $request->expects( $this->any() )->method( 'getIP' )
                        ->will( $this->returnValue( '127.0.0.1' ) );
@@ -209,7 +210,7 @@ class BotPasswordSessionProviderTest extends MediaWikiTestCase {
                $provider = $this->getProvider();
                $provider->setLogger( $logger );
 
-               $user = \User::newFromName( 'UTSysop' );
+               $user = static::getTestSysop()->getUser();
                $request = $this->getMock( 'FauxRequest', [ 'getIP' ] );
                $request->expects( $this->any() )->method( 'getIP' )
                        ->will( $this->returnValue( '127.0.0.1' ) );
index 9600184..b35b685 100644 (file)
@@ -165,7 +165,7 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                $provider->setConfig( $this->getConfig() );
                $provider->setManager( new SessionManager() );
 
-               $user = User::newFromName( 'UTSysop' );
+               $user = static::getTestSysop()->getUser();
                $id = $user->getId();
                $name = $user->getName();
                $token = $user->getToken( true );
@@ -390,7 +390,7 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
 
                $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
                $store = new TestBagOStuff();
-               $user = User::newFromName( 'UTSysop' );
+               $user = static::getTestSysop()->getUser();
                $anon = new User;
 
                $backend = new SessionBackend(
@@ -475,7 +475,7 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
                $provider->setManager( SessionManager::singleton() );
 
                $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
-               $user = User::newFromName( 'UTSysop' );
+               $user = static::getTestSysop()->getUser();
                $this->assertFalse( $user->requiresHTTPS(), 'sanity check' );
 
                $backend = new SessionBackend(
@@ -577,7 +577,7 @@ class CookieSessionProviderTest extends MediaWikiTestCase {
 
                $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
                $store = new TestBagOStuff();
-               $user = User::newFromName( 'UTSysop' );
+               $user = static::getTestSysop()->getUser();
                $anon = new User;
 
                $backend = new SessionBackend(
index 0b5f4c2..a3d5de7 100644 (file)
@@ -360,7 +360,7 @@ class SessionBackendTest extends MediaWikiTestCase {
        }
 
        public function testSetUser() {
-               $user = User::newFromName( 'UTSysop' );
+               $user = static::getTestSysop()->getUser();
 
                $this->provider = $this->getMock( 'DummySessionProvider', [ 'canChangeUser' ] );
                $this->provider->expects( $this->any() )->method( 'canChangeUser' )
@@ -484,7 +484,7 @@ class SessionBackendTest extends MediaWikiTestCase {
        }
 
        public function testSave() {
-               $user = User::newFromName( 'UTSysop' );
+               $user = static::getTestSysop()->getUser();
                $this->store = new TestBagOStuff();
                $testData = [ 'foo' => 'foo!', 'bar', [ 'baz', null ] ];
 
@@ -733,7 +733,7 @@ class SessionBackendTest extends MediaWikiTestCase {
        }
 
        public function testRenew() {
-               $user = User::newFromName( 'UTSysop' );
+               $user = static::getTestSysop()->getUser();
                $this->store = new TestBagOStuff();
                $testData = [ 'foo' => 'foo!', 'bar', [ 'baz', null ] ];
 
@@ -829,7 +829,7 @@ class SessionBackendTest extends MediaWikiTestCase {
                        $handler->enable = true;
                }
 
-               $backend = $this->getBackend( User::newFromName( 'UTSysop' ) );
+               $backend = $this->getBackend( static::getTestSysop()->getUser() );
                \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = true;
 
                $resetSingleton = TestUtils::setSessionManagerSingleton( $this->manager );
index e55a3a4..4d914e4 100644 (file)
@@ -164,6 +164,7 @@ class MediaWikiTitleCodecTest extends MediaWikiTestCase {
                        // getGenderCache() provides a mock that considers first
                        // names ending in "a" to be female.
                        [ NS_USER, 'Lisa_Müller', '', 'de', 'Benutzerin:Lisa Müller' ],
+                       [ 1000000, 'Invalid_namespace', '', 'en', ':Invalid namespace' ],
                ];
        }
 
index 629c6e5..1ef5082 100644 (file)
@@ -7,6 +7,13 @@ use MediaWiki\Session\SessionManager;
  * @group Database
  */
 class BotPasswordTest extends MediaWikiTestCase {
+
+       /** @var TestUser */
+       private $testUser;
+
+       /** @var string */
+       private $testUserName;
+
        protected function setUp() {
                parent::setUp();
 
@@ -20,11 +27,14 @@ class BotPasswordTest extends MediaWikiTestCase {
                        'wgUserrightsInterwikiDelimiter' => '@',
                ] );
 
+               $this->testUser = $this->getMutableTestUser();
+               $this->testUserName = $this->testUser->getUser()->getName();
+
                $mock1 = $this->getMockForAbstractClass( 'CentralIdLookup' );
                $mock1->expects( $this->any() )->method( 'isAttached' )
                        ->will( $this->returnValue( true ) );
                $mock1->expects( $this->any() )->method( 'lookupUserNames' )
-                       ->will( $this->returnValue( [ 'UTSysop' => 42, 'UTDummy' => 43, 'UTInvalid' => 0 ] ) );
+                       ->will( $this->returnValue( [ $this->testUserName => 42, 'UTDummy' => 43, 'UTInvalid' => 0 ] ) );
                $mock1->expects( $this->never() )->method( 'lookupCentralIds' );
 
                $mock2 = $this->getMockForAbstractClass( 'CentralIdLookup' );
@@ -82,7 +92,7 @@ class BotPasswordTest extends MediaWikiTestCase {
        }
 
        public function testBasics() {
-               $user = User::newFromName( 'UTSysop' );
+               $user = $this->testUser->getUser();
                $bp = BotPassword::newFromUser( $user, 'BotPassword' );
                $this->assertInstanceOf( 'BotPassword', $bp );
                $this->assertTrue( $bp->isSaved() );
@@ -107,7 +117,7 @@ class BotPasswordTest extends MediaWikiTestCase {
        }
 
        public function testUnsaved() {
-               $user = User::newFromName( 'UTSysop' );
+               $user = $this->testUser->getUser();
                $bp = BotPassword::newUnsaved( [
                        'user' => $user,
                        'appId' => 'DoesNotExist'
@@ -132,7 +142,7 @@ class BotPasswordTest extends MediaWikiTestCase {
                $this->assertEquals( '{"IPAddresses":["127.0.0.0/8"]}', $bp->getRestrictions()->toJson() );
                $this->assertSame( [ 'test' ], $bp->getGrants() );
 
-               $user = User::newFromName( 'UTSysop' );
+               $user = $this->testUser->getUser();
                $bp = BotPassword::newUnsaved( [
                        'centralId' => 45,
                        'appId' => 'DoesNotExist'
@@ -142,7 +152,7 @@ class BotPasswordTest extends MediaWikiTestCase {
                $this->assertSame( 45, $bp->getUserCentralId() );
                $this->assertSame( 'DoesNotExist', $bp->getAppId() );
 
-               $user = User::newFromName( 'UTSysop' );
+               $user = $this->testUser->getUser();
                $bp = BotPassword::newUnsaved( [
                        'user' => $user,
                        'appId' => 'BotPassword'
@@ -159,7 +169,7 @@ class BotPasswordTest extends MediaWikiTestCase {
                        'appId' => str_repeat( 'X', BotPassword::APPID_MAXLENGTH + 1 ),
                ] ) );
                $this->assertNull( BotPassword::newUnsaved( [
-                       'user' => 'UTSysop',
+                       'user' => $this->testUserName,
                        'appId' => 'Ok',
                ] ) );
                $this->assertNull( BotPassword::newUnsaved( [
@@ -200,7 +210,7 @@ class BotPasswordTest extends MediaWikiTestCase {
 
                $this->assertNotInstanceOf( 'InvalidPassword', $bp1->getPassword(), 'sanity check' );
                $this->assertNotInstanceOf( 'InvalidPassword', $bp2->getPassword(), 'sanity check' );
-               BotPassword::invalidateAllPasswordsForUser( 'UTSysop' );
+               BotPassword::invalidateAllPasswordsForUser( $this->testUserName );
                $this->assertInstanceOf( 'InvalidPassword', $bp1->getPassword() );
                $this->assertNotInstanceOf( 'InvalidPassword', $bp2->getPassword() );
 
@@ -212,7 +222,7 @@ class BotPasswordTest extends MediaWikiTestCase {
                $this->assertNotNull( BotPassword::newFromCentralId( 42, 'BotPassword' ), 'sanity check' );
                $this->assertNotNull( BotPassword::newFromCentralId( 43, 'BotPassword' ), 'sanity check' );
 
-               BotPassword::removeAllPasswordsForUser( 'UTSysop' );
+               BotPassword::removeAllPasswordsForUser( $this->testUserName );
 
                $this->assertNull( BotPassword::newFromCentralId( 42, 'BotPassword' ) );
                $this->assertNotNull( BotPassword::newFromCentralId( 43, 'BotPassword' ) );
@@ -221,7 +231,7 @@ class BotPasswordTest extends MediaWikiTestCase {
        public function testLogin() {
                // Test failure when bot passwords aren't enabled
                $this->setMwGlobals( 'wgEnableBotPasswords', false );
-               $status = BotPassword::login( 'UTSysop@BotPassword', 'foobaz', new FauxRequest );
+               $status = BotPassword::login( "{$this->testUserName}@BotPassword", 'foobaz', new FauxRequest );
                $this->assertEquals( Status::newFatal( 'botpasswords-disabled' ), $status );
                $this->setMwGlobals( 'wgEnableBotPasswords', true );
 
@@ -235,7 +245,7 @@ class BotPasswordTest extends MediaWikiTestCase {
                        $manager->getProvider( MediaWiki\Session\BotPasswordSessionProvider::class ),
                        'sanity check'
                );
-               $status = BotPassword::login( 'UTSysop@BotPassword', 'foobaz', new FauxRequest );
+               $status = BotPassword::login( "{$this->testUserName}@BotPassword", 'foobaz', new FauxRequest );
                $this->assertEquals( Status::newFatal( 'botpasswords-no-provider' ), $status );
                ScopedCallback::consume( $reset );
 
@@ -257,7 +267,7 @@ class BotPasswordTest extends MediaWikiTestCase {
                $reset = MediaWiki\Session\TestUtils::setSessionManagerSingleton( $manager );
 
                // No "@"-thing in the username
-               $status = BotPassword::login( 'UTSysop', 'foobaz', new FauxRequest );
+               $status = BotPassword::login( $this->testUserName, 'foobaz', new FauxRequest );
                $this->assertEquals( Status::newFatal( 'botpasswords-invalid-name', '@' ), $status );
 
                // No base user
@@ -265,9 +275,9 @@ class BotPasswordTest extends MediaWikiTestCase {
                $this->assertEquals( Status::newFatal( 'nosuchuser', 'UTDummy' ), $status );
 
                // No bot password
-               $status = BotPassword::login( 'UTSysop@DoesNotExist', 'foobaz', new FauxRequest );
+               $status = BotPassword::login( "{$this->testUserName}@DoesNotExist", 'foobaz', new FauxRequest );
                $this->assertEquals(
-                       Status::newFatal( 'botpasswords-not-exist', 'UTSysop', 'DoesNotExist' ),
+                       Status::newFatal( 'botpasswords-not-exist', $this->testUserName, 'DoesNotExist' ),
                        $status
                );
 
@@ -275,11 +285,12 @@ class BotPasswordTest extends MediaWikiTestCase {
                $request = $this->getMock( 'FauxRequest', [ 'getIP' ] );
                $request->expects( $this->any() )->method( 'getIP' )
                        ->will( $this->returnValue( '10.0.0.1' ) );
-               $status = BotPassword::login( 'UTSysop@BotPassword', 'foobaz', $request );
+               $status = BotPassword::login( "{$this->testUserName}@BotPassword", 'foobaz', $request );
                $this->assertEquals( Status::newFatal( 'botpasswords-restriction-failed' ), $status );
 
                // Wrong password
-               $status = BotPassword::login( 'UTSysop@BotPassword', 'UTSysopPassword', new FauxRequest );
+               $status = BotPassword::login(
+                       "{$this->testUserName}@BotPassword", $this->testUser->password, new FauxRequest );
                $this->assertEquals( Status::newFatal( 'wrongpassword' ), $status );
 
                // Success!
@@ -289,7 +300,7 @@ class BotPasswordTest extends MediaWikiTestCase {
                        $request->getSession()->getProvider(),
                        'sanity check'
                );
-               $status = BotPassword::login( 'UTSysop@BotPassword', 'foobaz', $request );
+               $status = BotPassword::login( "{$this->testUserName}@BotPassword", 'foobaz', $request );
                $this->assertInstanceOf( 'Status', $status );
                $this->assertTrue( $status->isGood() );
                $session = $status->getValue();
index 1786261..feac641 100644 (file)
@@ -45,7 +45,7 @@ class CentralIdLookupTest extends MediaWikiTestCase {
                        $this->getMockForAbstractClass( 'CentralIdLookup' )
                );
 
-               $user = User::newFromName( 'UTSysop' );
+               $user = static::getTestSysop()->getUser();
                $this->assertSame( $user, $mock->checkAudience( $user ) );
 
                $user = $mock->checkAudience( CentralIdLookup::AUDIENCE_PUBLIC );
index c86fb6c..c91d8e0 100644 (file)
@@ -18,18 +18,14 @@ class LocalIdLookupTest extends MediaWikiTestCase {
 
        public function addDBData() {
                for ( $i = 1; $i <= 4; $i++ ) {
-                       $user = User::newFromName( "UTLocalIdLookup$i" );
-                       if ( $user->getId() == 0 ) {
-                               $user->addToDatabase();
-                       }
-                       $this->localUsers["UTLocalIdLookup$i"] = $user->getId();
+                       $this->localUsers[] = $this->getMutableTestUser()->getUser();
                }
 
-               User::newFromName( 'UTLocalIdLookup1' )->addGroup( 'local-id-lookup-test' );
+               $sysop = static::getTestSysop()->getUser();
 
                $block = new Block( [
-                       'address' => 'UTLocalIdLookup3',
-                       'by' => User::idFromName( 'UTSysop' ),
+                       'address' => $this->localUsers[2]->getName(),
+                       'by' => $sysop->getId(),
                        'reason' => __METHOD__,
                        'expiry' => '1 day',
                        'hideName' => false,
@@ -37,8 +33,8 @@ class LocalIdLookupTest extends MediaWikiTestCase {
                $block->insert();
 
                $block = new Block( [
-                       'address' => 'UTLocalIdLookup4',
-                       'by' => User::idFromName( 'UTSysop' ),
+                       'address' => $this->localUsers[3]->getName(),
+                       'by' => $sysop->getId(),
                        'reason' => __METHOD__,
                        'expiry' => '1 day',
                        'hideName' => true,
@@ -46,9 +42,14 @@ class LocalIdLookupTest extends MediaWikiTestCase {
                $block->insert();
        }
 
+       public function getLookupUser() {
+               return static::getTestUser( [ 'local-id-lookup-test' ] )->getUser();
+       }
+
        public function testLookupCentralIds() {
                $lookup = new LocalIdLookup();
-               $user1 = User::newFromName( 'UTLocalIdLookup1' );
+
+               $user1 = $this->getLookupUser();
                $user2 = User::newFromName( 'UTLocalIdLookup2' );
 
                $this->assertTrue( $user1->isAllowed( 'hideuser' ), 'sanity check' );
@@ -56,12 +57,15 @@ class LocalIdLookupTest extends MediaWikiTestCase {
 
                $this->assertSame( [], $lookup->lookupCentralIds( [] ) );
 
-               $expect = array_flip( $this->localUsers );
-               $expect[123] = 'X';
+               $expect = [];
+               foreach ( $this->localUsers as $localUser ) {
+                       $expect[$localUser->getId()] = $localUser->getName();
+               }
+               $expect[12345] = 'X';
                ksort( $expect );
 
                $expect2 = $expect;
-               $expect2[$this->localUsers['UTLocalIdLookup4']] = '';
+               $expect2[$this->localUsers[3]->getId()] = '';
 
                $arg = array_fill_keys( array_keys( $expect ), 'X' );
 
@@ -73,7 +77,7 @@ class LocalIdLookupTest extends MediaWikiTestCase {
 
        public function testLookupUserNames() {
                $lookup = new LocalIdLookup();
-               $user1 = User::newFromName( 'UTLocalIdLookup1' );
+               $user1 = $this->getLookupUser();
                $user2 = User::newFromName( 'UTLocalIdLookup2' );
 
                $this->assertTrue( $user1->isAllowed( 'hideuser' ), 'sanity check' );
@@ -81,12 +85,15 @@ class LocalIdLookupTest extends MediaWikiTestCase {
 
                $this->assertSame( [], $lookup->lookupUserNames( [] ) );
 
-               $expect = $this->localUsers;
+               $expect = [];
+               foreach ( $this->localUsers as $localUser ) {
+                       $expect[$localUser->getName()] = $localUser->getId();
+               }
                $expect['UTDoesNotExist'] = 'X';
                ksort( $expect );
 
                $expect2 = $expect;
-               $expect2['UTLocalIdLookup4'] = 'X';
+               $expect2[$this->localUsers[3]->getName()] = 'X';
 
                $arg = array_fill_keys( array_keys( $expect ), 'X' );
 
@@ -98,7 +105,7 @@ class LocalIdLookupTest extends MediaWikiTestCase {
 
        public function testIsAttached() {
                $lookup = new LocalIdLookup();
-               $user1 = User::newFromName( 'UTLocalIdLookup1' );
+               $user1 = $this->getLookupUser();
                $user2 = User::newFromName( 'DoesNotExist' );
 
                $this->assertTrue( $lookup->isAttached( $user1 ) );
@@ -130,7 +137,7 @@ class LocalIdLookupTest extends MediaWikiTestCase {
                $lookup = new LocalIdLookup();
                $this->assertSame(
                        $sharedDB && $sharedTable && $localDBSet,
-                       $lookup->isAttached( User::newFromName( 'UTLocalIdLookup1' ), 'shared' )
+                       $lookup->isAttached( $this->getLookupUser(), 'shared' )
                );
        }
 
index c9b6929..801ab91 100644 (file)
@@ -213,11 +213,7 @@ class UserTest extends MediaWikiTestCase {
         * @covers User::getEditCount
         */
        public function testEditCount() {
-               $user = User::newFromName( 'UnitTestUser' );
-
-               if ( !$user->getId() ) {
-                       $user->addToDatabase();
-               }
+               $user = $this->getMutableTestUser()->getUser();
 
                // let the user have a few (3) edits
                $page = WikiPage::factory( Title::newFromText( 'Help:UserTest_EditCount' ) );
@@ -249,17 +245,13 @@ class UserTest extends MediaWikiTestCase {
         * @covers User::getOption
         */
        public function testOptions() {
-               $user = User::newFromName( 'UnitTestUser' );
-
-               if ( !$user->getId() ) {
-                       $user->addToDatabase();
-               }
+               $user = $this->getMutableTestUser()->getUser();
 
                $user->setOption( 'userjs-someoption', 'test' );
                $user->setOption( 'cols', 200 );
                $user->saveSettings();
 
-               $user = User::newFromName( 'UnitTestUser' );
+               $user = User::newFromName( $user->getName() );
                $this->assertEquals( 'test', $user->getOption( 'userjs-someoption' ) );
                $this->assertEquals( 200, $user->getOption( 'cols' ) );
        }
@@ -298,7 +290,7 @@ class UserTest extends MediaWikiTestCase {
                                                'MinimalPasswordLength' => 6,
                                                'PasswordCannotMatchUsername' => true,
                                                'PasswordCannotMatchBlacklist' => true,
-                                               'MaximalPasswordLength' => 30,
+                                               'MaximalPasswordLength' => 40,
                                        ],
                                ],
                                'checks' => [
@@ -311,7 +303,8 @@ class UserTest extends MediaWikiTestCase {
                        ],
                ] );
 
-               $user = User::newFromName( 'Useruser' );
+               $user = static::getTestUser()->getUser();
+
                // Sanity
                $this->assertTrue( $user->isValidPassword( 'Password1234' ) );
 
@@ -322,18 +315,19 @@ class UserTest extends MediaWikiTestCase {
                $this->assertEquals( 'passwordtooshort', $user->getPasswordValidity( 'a' ) );
 
                // Maximum length
-               $longPass = str_repeat( 'a', 31 );
+               $longPass = str_repeat( 'a', 41 );
                $this->assertFalse( $user->isValidPassword( $longPass ) );
                $this->assertFalse( $user->checkPasswordValidity( $longPass )->isGood() );
                $this->assertFalse( $user->checkPasswordValidity( $longPass )->isOK() );
                $this->assertEquals( 'passwordtoolong', $user->getPasswordValidity( $longPass ) );
 
                // Matches username
-               $this->assertFalse( $user->checkPasswordValidity( 'Useruser' )->isGood() );
-               $this->assertTrue( $user->checkPasswordValidity( 'Useruser' )->isOK() );
-               $this->assertEquals( 'password-name-match', $user->getPasswordValidity( 'Useruser' ) );
+               $this->assertFalse( $user->checkPasswordValidity( $user->getName() )->isGood() );
+               $this->assertTrue( $user->checkPasswordValidity( $user->getName() )->isOK() );
+               $this->assertEquals( 'password-name-match', $user->getPasswordValidity( $user->getName() ) );
 
                // On the forbidden list
+               $user = User::newFromName( 'Useruser' );
                $this->assertFalse( $user->checkPasswordValidity( 'Passpass' )->isGood() );
                $this->assertEquals( 'password-login-forbidden', $user->getPasswordValidity( 'Passpass' ) );
        }
@@ -389,29 +383,23 @@ class UserTest extends MediaWikiTestCase {
         * @covers User::equals
         */
        public function testEquals() {
-               $first = User::newFromName( 'EqualUser' );
-               $second = User::newFromName( 'EqualUser' );
+               $first = $this->getMutableTestUser()->getUser();
+               $second = User::newFromName( $first->getName() );
 
                $this->assertTrue( $first->equals( $first ) );
                $this->assertTrue( $first->equals( $second ) );
                $this->assertTrue( $second->equals( $first ) );
 
-               $third = User::newFromName( '0' );
-               $fourth = User::newFromName( '000' );
+               $third = $this->getMutableTestUser()->getUser();
+               $fourth = $this->getMutableTestUser()->getUser();
 
                $this->assertFalse( $third->equals( $fourth ) );
                $this->assertFalse( $fourth->equals( $third ) );
 
                // Test users loaded from db with id
-               $user = User::newFromName( 'EqualUnitTestUser' );
-               if ( !$user->getId() ) {
-                       $user->addToDatabase();
-               }
-
-               $id = $user->getId();
-
-               $fifth = User::newFromId( $id );
-               $sixth = User::newFromName( 'EqualUnitTestUser' );
+               $user = $this->getMutableTestUser()->getUser();
+               $fifth = User::newFromId( $user->getId() );
+               $sixth = User::newFromName( $user->getName() );
                $this->assertTrue( $fifth->equals( $sixth ) );
        }
 
@@ -419,7 +407,7 @@ class UserTest extends MediaWikiTestCase {
         * @covers User::getId
         */
        public function testGetId() {
-               $user = User::newFromName( 'UTSysop' );
+               $user = static::getTestUser()->getUser();
                $this->assertTrue( $user->getId() > 0 );
 
        }
@@ -429,7 +417,7 @@ class UserTest extends MediaWikiTestCase {
         * @covers User::isAnon
         */
        public function testLoggedIn() {
-               $user = User::newFromName( 'UTSysop' );
+               $user = $this->getMutableTestUser()->getUser();
                $this->assertTrue( $user->isLoggedIn() );
                $this->assertFalse( $user->isAnon() );
 
@@ -447,7 +435,8 @@ class UserTest extends MediaWikiTestCase {
         * @covers User::checkAndSetTouched
         */
        public function testCheckAndSetTouched() {
-               $user = TestingAccessWrapper::newFromObject( User::newFromName( 'UTSysop' ) );
+               $user = $this->getMutableTestUser()->getUser();
+               $user = TestingAccessWrapper::newFromObject( $user );
                $this->assertTrue( $user->isLoggedIn() );
 
                $touched = $user->getDBTouched();
index ee854ae..edc2716 100644 (file)
@@ -4,7 +4,8 @@
        // TODO: verify checkboxes == [ 'nsassociated', 'nsinvert' ]
 
        QUnit.test( '"all" namespace disable checkboxes', 8, function ( assert ) {
-               var selectHtml, $env, $options;
+               var selectHtml, $env, $options,
+                       rc = require( 'mediawiki.special.recentchanges' );
 
                // from Special:Recentchanges
                selectHtml = '<select id="namespace" name="namespace" class="namespaceselector">'
@@ -32,7 +33,7 @@
                assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false );
 
                // Initiate the recentchanges module
-               mw.special.recentchanges.init();
+               rc.init();
 
                // By default
                assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );