* 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 '';
}
/**
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 );
}
$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
$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"
*
* @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.
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__ );
}
}
$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();
$status = 'busy';
}
+ $this->getStats()->increment( "editstash.cache_stores.$status" );
+
$this->getResult()->addValue( null, $this->getModuleName(), [ 'status' => $status ] );
}
* @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();
"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.",
"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.",
"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"
}
"@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",
"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]].",
"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": "Перераховує кількість користувачів у групах користувачів.",
* @ingroup Cache
*/
use MediaWiki\Linker\LinkTarget;
+use MediaWiki\MediaWikiServices;
/**
* Class representing a list of titles
* @return array Mapping PDBK to ID
*/
public function execute() {
- $linkCache = LinkCache::singleton();
+ $linkCache = MediaWikiServices::getInstance()->getLinkCache();
return $this->executeInto( $linkCache );
}
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;
}
}
return false;
}
- $genderCache = GenderCache::singleton();
+ $genderCache = MediaWikiServices::getInstance()->getGenderCache();
$genderCache->doLinkBatch( $this->data, $this->caller );
return true;
*/
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 );
*
* @file
*/
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\MediaWikiServices;
class ChangesList extends ContextSource {
/**
/** @var BagOStuff */
protected $watchMsgCache;
+ /**
+ * @var LinkRenderer
+ */
+ protected $linkRenderer;
+
/**
* Changeslist constructor
*
}
$this->preCacheMessages();
$this->watchMsgCache = new HashBagOStuff( [ 'maxKeys' => 50 ] );
+ $this->linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
}
/**
*/
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();
}
/**
'oldid' => $rc->mAttribs['rc_last_oldid']
];
- $diffLink = Linker::linkKnown(
+ $diffLink = $this->linkRenderer->makeKnownLink(
$rc->getTitle(),
- $this->message['diff'],
+ new HtmlArmor( $this->message['diff'] ),
[],
$query
);
} 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'],
$params = [ 'redirect' => 'no' ];
}
- $articlelink = Linker::link(
+ $articlelink = $this->linkRenderer->makeLink(
$rc->getTitle(),
null,
[ 'class' => 'mw-changeslist-title' ],
// message is set by the parent ChangesList class
$this->cacheEntryFactory = new RCCacheEntryFactory(
$context,
- $this->message
+ $this->message,
+ $this->linkRenderer
);
}
} 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
);
) {
$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' ]
+ ]
);
}
}
$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
);
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 );
}
}
$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();
*
* @file
*/
+use MediaWiki\Linker\LinkRenderer;
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;
}
/**
// 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'];
$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 ) {
$clink = '';
// Edits
} else {
- $clink = Linker::linkKnown( $cacheEntry->getTitle() );
+ $clink = $this->linkRenderer->makeKnownLink( $cacheEntry->getTitle() );
}
return $clink;
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;
}
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 )
);
}
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 ) {
$this->mRevision = $revision;
}
+ /**
+ * @since 1.28
+ * @return null|Revision
+ */
+ public function getRevision() {
+ return $this->mRevision;
+ }
+
/**
* Set the User who triggered this LinksUpdate
*
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__ );
// 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'] ) {
*/
private $expandUrls = false;
- /**
- * Whether extra classes should be added
- *
- * @var bool
- */
- private $noClasses = false;
-
/**
* @var int
*/
return $this->expandUrls;
}
- /**
- * @param bool $no
- */
- public function setNoClasses( $no ) {
- $this->noClasses = $no;
- }
-
- /**
- * @return bool
- */
- public function getNoClasses() {
- return $this->noClasses;
- }
-
/**
* @param int $threshold
*/
*/
private function getLegacyOptions( $isKnown ) {
$options = [ 'stubThreshold' => $this->stubThreshold ];
- if ( $this->noClasses ) {
- $options[] = 'noclasses';
- }
if ( $this->forceArticlePath ) {
$options[] = 'forcearticlepath';
}
}
/**
+ * 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 );
}
$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;
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
}
$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!
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 );
}
$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 );
$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;
}
if ( $displayText === '' ) {
$displayText = null;
+ } else {
+ $displayText = new HtmlArmor( $displayText );
}
if ( !isset( $colours[$pdbk] ) ) {
$colours[$pdbk] = 'new';
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 );
# 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 );
$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;
}
* @file
* @ingroup Parser
*/
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\MediaWikiServices;
/**
* @defgroup Parser Parser
/** @var SectionProfiler */
protected $mProfiler;
+ /**
+ * @var LinkRenderer
+ */
+ protected $mLinkRenderer;
+
/**
* @param array $conf
*/
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
# 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 );
*
* @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;
}
*/
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;
}
}
"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": " अउर",
"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": "गुप्तशब्द रिसेट करीं",
"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''।",
"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.",
"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.",
"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": " ; ",
"colon-separator": " : ",
"percent": "$1 %",
"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 →",
"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).",
"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": "שינוי מודל התוכן של דף",
"january-gen": "АгIой бетт",
"february-gen": "Саь-кур",
"march-gen": "Мутт-хьал бетт",
- "april-gen": "Тушоли бетт",
+ "april-gen": "Тушоли",
"may-gen": "Села бетт",
"june-gen": "Этинга бетт",
"july-gen": "Баьцамеа бетт",
"about": "Сурт оттадар",
"article": "Йоазув",
"newwindow": " (керда кора чу)",
- "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": "Мотт",
"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."
}
"morenotlisted": "Listen ä ett komplett.",
"mypage": "Siid",
"mytalk": "Diskusjon",
- "anontalk": "Diskusjonssiid for denn IP-adress",
+ "anontalk": "Diskusjonssiid",
"navigation": "Navigasjon",
"and": " å",
"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",
"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|мүше|мүше}}",
"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": "다음 페이지 →",
"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": " ",
"ellipsis": " …",
"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"
}
"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"
}
"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.",
"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“ није увезена јер вам није дозвољено да је направите.",
"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": "பலமுறை பார்வையிட்ட பக்கங்களைப் புதியபக்கங்களின் பட்டியலில் காட்டவேண்டாம்.",
$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() {
$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 ) {
$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" );
}
}
.success-message {
font-weight: bold;
font-size: 110%;
- color: #0f0;
+ color: #008000;
}
.success-box {
"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"
}
'mediawiki.special' => [
'position' => 'top',
- 'scripts' => 'resources/src/mediawiki.special/mediawiki.special.js',
'styles' => 'resources/src/mediawiki.special/mediawiki.special.css',
'targets' => [ 'desktop', 'mobile' ],
],
'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',
],
'mediawiki.special.recentchanges' => [
'scripts' => 'resources/src/mediawiki.special/mediawiki.special.recentchanges.js',
- 'dependencies' => 'mediawiki.special',
'position' => 'top',
],
'mediawiki.special.search' => [
padding: 3px !important;
font-size: 94%;
text-align: center;
+ /* new block formatting context,
+ * to clear background from floating content */
+ overflow: hidden;
}
html .thumbimage {
line-height: 1.4em;
padding: 3px !important;
font-size: 94%;
- overflow: hidden;
- word-wrap: break-word;
}
/* @noflip */
background-color: #f9f9f9;
font-size: 94%;
text-align: center;
+ /* new block formatting context,
+ * to clear background from floating content */
+ overflow: hidden;
}
html .thumbimage {
padding: 3px;
font-size: 94%;
text-align: left;
- overflow: hidden;
- word-wrap: break-word;
}
div.magnify {
background-color: #f9f9f9;
font-size: 94%;
text-align: center;
+ /* new block formatting context,
+ * to clear background from floating content */
+ overflow: hidden;
}
html .thumbimage {
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;
}
*
* @class mw.special.ApiSandbox
*/
- mw.special.ApiSandbox = ApiSandbox = {
+ ApiSandbox = {
/**
* Initialize the UI
*
$( ApiSandbox.init );
+ module.exports = ApiSandbox;
+
}( jQuery, mediaWiki, OO ) );
+++ /dev/null
-/*!
- * Namespace for mediawiki.special.* modules
- */
-
-/**
- * @class mw.special
- * @singleton
- */
-mediaWiki.special = {};
$( rc.init );
- mw.special.recentchanges = rc;
+ module.exports = rc;
}( mediaWiki, jQuery ) );
'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
!! 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
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.
*
$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' );
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...?
# 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' ) );
TestingAccessWrapper::newFromClass( 'User' )
->getInProcessCache()
->clear();
+
+ TestUserRegistry::clear();
}
$truncate = in_array( $db->getType(), [ 'oracle', 'mysql' ] );
<?php
+use MediaWiki\MediaWikiServices;
+
/**
* @group Database
*/
# ## 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'
],
# 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'
],
$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 )
+ );
+ }
}
);
// 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' ) );
}
Title::newFromText( 'Merge2' )
);
- $mh->merge( User::newFromName( 'UTSysop' ) );
+ $sysop = static::getTestSysop()->getUser();
+ $mh->merge( $sysop );
$this->assertEquals( $mh->getMergedRevisionCount(), 1 );
}
}
--- /dev/null
+<?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 = [];
+ }
+}
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();
}
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 );
}
private function cleanTestUsersWatchlist() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$store = $this->getWatchedItemStore();
$items = $store->getWatchedItemsForUser( $user );
foreach ( $items as $item ) {
// 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,
}
public function testIdsPropParameter() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdit(
$user,
}
public function testTitlePropParameter() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdits(
}
public function testFlagsPropParameter() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$normalEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$minorEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageM' );
$botEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageB' );
}
public function testUserPropParameter() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$userEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$anonEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageA' );
$this->doPageEdit(
}
public function testUserIdPropParameter() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$userEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$anonEditTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPageA' );
$this->doPageEdit(
}
public function testCommentPropParameter() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdit(
$user,
}
public function testParsedCommentPropParameter() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdit(
$user,
}
public function testTimestampPropParameter() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdit(
$user,
}
public function testSizesPropParameter() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdit(
$user,
'Create the page'
);
$store = $this->getWatchedItemStore();
- $store->addWatch( $this->getTestUser(), $target );
+ $store->addWatch( $this->getLoggedInTestUser(), $target );
$store->updateNotificationTimestamp(
$otherUser,
$target,
}
public function testPatrolPropParameter() {
- $user = $this->getSysopTestUser();
+ $testUser = static::getTestSysop();
+ $user = $testUser->getUser();
$this->setupPatrolledSpecificFixtures( $user );
$result = $this->doListWatchlistRequest( [ 'wlprop' => 'patrol', ], $user );
private function createPageAndDeleteIt( LinkTarget $target ) {
$this->doPageEdit(
- $this->getTestUser(),
+ $this->getLoggedInTestUser(),
$target,
'Some Content',
'Create the page that will be deleted'
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->createPageAndDeleteIt( $target );
- $this->watchPages( $this->getTestUser(), [ $target ] );
+ $this->watchPages( $this->getLoggedInTestUser(), [ $target ] );
$result = $this->doListWatchlistRequest( [ 'wlprop' => 'loginfo', ] );
}
public function testEmptyPropParameter() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdit(
$user,
}
public function testNamespaceParam() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdits(
}
public function testUserParam() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$otherUser = $this->getNonLoggedInTestUser();
$subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
}
public function testExcludeUserParam() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$otherUser = $this->getNonLoggedInTestUser();
$subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
}
public function testShowMinorParams() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdits(
$user,
}
public function testShowBotParams() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doBotPageEdit(
$user,
}
public function testShowAnonParams() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doAnonPageEdit(
$target,
}
public function testShowUnreadParams() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$otherUser = $this->getNonLoggedInTestUser();
$subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
}
public function testShowPatrolledParams() {
- $user = $this->getSysopTestUser();
+ $user = static::getTestSysop()->getUser();
$this->setupPatrolledSpecificFixtures( $user );
$resultPatrolled = $this->doListWatchlistRequest( [
}
public function testNewAndEditTypeParameters() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdits(
}
public function testLogTypeParameters() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
$this->createPageAndDeleteIt( $subjectTarget );
}
public function testExternalTypeParameters() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdit(
}
public function testCategorizeTypeParameter() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$categoryTarget = new TitleValue( NS_CATEGORY, 'ApiQueryWatchlistIntegrationTestCategory' );
$this->doPageEdits(
}
public function testLimitParam() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target1 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$target2 = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
$target3 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage2' );
}
public function testAllRevParam() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdits(
$user,
}
public function testDirParams() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$subjectTarget = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$talkTarget = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdits(
}
public function testStartEndParams() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdit(
$user,
}
public function testContinueParam() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target1 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$target2 = new TitleValue( 1, 'ApiQueryWatchlistIntegrationTestPage' );
$target3 = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage2' );
public function testOwnerAndTokenParams() {
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdit(
- $this->getTestUser(),
+ $this->getLoggedInTestUser(),
$target,
'Some Content',
'Create the page'
}
public function testGeneratorWatchlistPropInfo_returnsWatchedPages() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdit(
$user,
}
public function testGeneratorWatchlistPropRevisions_returnsWatchedItemsRevisions() {
- $user = $this->getTestUser();
+ $user = $this->getLoggedInTestUser();
$target = new TitleValue( 0, 'ApiQueryWatchlistIntegrationTestPage' );
$this->doPageEdits(
$user,
*/
protected $apiContext;
- /**
- * @var array
- */
- protected $tablesUsed = [ 'user', 'user_groups', 'user_properties' ];
-
protected function setUp() {
global $wgServer, $wgDisableAuthManager;
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( [
}
}
- 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'];
[
'action' => 'login',
'lgtoken' => $token,
- 'lgname' => self::$users[$user]->username,
- 'lgpassword' => self::$users[$user]->password,
+ 'lgname' => $testUser->username,
+ 'lgpassword' => $testUser->password,
],
$data[2]
);
if ( $data[0]['login']['result'] === 'Success' ) {
// DWIM
global $wgUser;
- $wgUser = self::$users[$user]->getUser();
+ $wgUser = $testUser->getUser();
RequestContext::getMain()->setUser( $wgUser );
}
}
public function testBasics() {
+ $user = $this->getMutableTestUser()->getUser();
+ $userName = $user->getName();
+ $lowerInitialUserName = mb_strtolower( $userName[0] ) . substr( $userName, 1 );
+
$provider = new LocalPasswordPrimaryAuthenticationProvider();
$this->assertSame(
$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>' ) );
$provider->accountCreationType()
);
- $this->assertTrue( $provider->testUserExists( 'UTSysop' ) );
+ $this->assertTrue( $provider->testUserExists( $userName ) );
$this->assertFalse( $provider->testUserExists( 'DoesNotExist' ) );
$req = new PasswordAuthenticationRequest;
}
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();
$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() {
$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() );
$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() );
$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() );
}
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;
);
// 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(
$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' ) );
// 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' ) );
// 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(
$this->config->set( 'LegacyEncoding', true );
$this->assertEquals(
- AuthenticationResponse::newPass( 'UTSysop' ),
+ AuthenticationResponse::newPass( $userName ),
$provider->beginPrimaryAuthentication( $reqs )
);
// 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 )
);
/**
* @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 ) {
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 ],
];
}
// 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();
*/
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()
+ ];
}
/**
* @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" );
}
* @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" );
}
* 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' ],
- ];
- }
}
*/
private static $pageRev = null;
+ /**
+ * @var User
+ */
+ private static $revUser = null;
+
/**
* @var string
*/
$page = WikiPage::factory( $title );
self::$pageRev = $page->getRevision();
+ self::$revUser = User::newFromId( self::$pageRev->getUser( Revision::RAW ) );
}
private function newChange( Revision $revision = null ) {
$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() );
$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() );
* @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
);
$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
);
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 );
}
private function getNewBotEditChange() {
- $user = $this->getTestUser();
+ $user = $this->getMutableTestUser()->getUser();
$recentChange = $this->testRecentChangesHelper->makeNewBotEditRecentChange(
$user, 'Abc', '20131103212153', 5, 191, 190, 0, 0
}
private function getLogChange( $logType, $logAction ) {
- $user = $this->getTestUser();
+ $user = $this->getMutableTestUser()->getUser();
$recentChange = $this->testRecentChangesHelper->makeLogRecentChange(
$logType, $logAction, $user, 'Abc', '20131103212153', 0, 0
}
private function getEditChange() {
- $user = $this->getTestUser();
+ $user = $this->getMutableTestUser()->getUser();
$recentChange = $this->testRecentChangesHelper->makeEditRecentChange(
$user, 'Cat', '20131103212153', 5, 191, 190, 0, 0
);
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' );
<?php
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\MediaWikiServices;
+
/**
* @covers RCCacheEntryFactory
*
*/
private $testRecentChangesHelper;
+ /**
+ * @var LinkRenderer;
+ */
+ private $linkRenderer;
+
public function __construct( $name = null, array $data = [], $dataName = '' ) {
parent::__construct( $name, $data, $dataName );
$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 );
$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(
[
];
}
- 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 );
<?php
+use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\MediaWikiServices;
/**
* Helper for generating test recent changes entries.
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 );
}
public static function provideCreateFromLegacyOptions() {
return [
- [
- [ 'noclasses' ],
- 'getNoClasses',
- true
- ],
[
[ 'forcearticlepath' ],
'getForceArticlePath',
$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(
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' ) );
$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' ) );
$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 );
$sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
$store = new TestBagOStuff();
- $user = User::newFromName( 'UTSysop' );
+ $user = static::getTestSysop()->getUser();
$anon = new User;
$backend = new SessionBackend(
$provider->setManager( SessionManager::singleton() );
$sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
- $user = User::newFromName( 'UTSysop' );
+ $user = static::getTestSysop()->getUser();
$this->assertFalse( $user->requiresHTTPS(), 'sanity check' );
$backend = new SessionBackend(
$sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
$store = new TestBagOStuff();
- $user = User::newFromName( 'UTSysop' );
+ $user = static::getTestSysop()->getUser();
$anon = new User;
$backend = new SessionBackend(
}
public function testSetUser() {
- $user = User::newFromName( 'UTSysop' );
+ $user = static::getTestSysop()->getUser();
$this->provider = $this->getMock( 'DummySessionProvider', [ 'canChangeUser' ] );
$this->provider->expects( $this->any() )->method( 'canChangeUser' )
}
public function testSave() {
- $user = User::newFromName( 'UTSysop' );
+ $user = static::getTestSysop()->getUser();
$this->store = new TestBagOStuff();
$testData = [ 'foo' => 'foo!', 'bar', [ 'baz', null ] ];
}
public function testRenew() {
- $user = User::newFromName( 'UTSysop' );
+ $user = static::getTestSysop()->getUser();
$this->store = new TestBagOStuff();
$testData = [ 'foo' => 'foo!', 'bar', [ 'baz', null ] ];
$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 );
// 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' ],
];
}
* @group Database
*/
class BotPasswordTest extends MediaWikiTestCase {
+
+ /** @var TestUser */
+ private $testUser;
+
+ /** @var string */
+ private $testUserName;
+
protected function setUp() {
parent::setUp();
'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' );
}
public function testBasics() {
- $user = User::newFromName( 'UTSysop' );
+ $user = $this->testUser->getUser();
$bp = BotPassword::newFromUser( $user, 'BotPassword' );
$this->assertInstanceOf( 'BotPassword', $bp );
$this->assertTrue( $bp->isSaved() );
}
public function testUnsaved() {
- $user = User::newFromName( 'UTSysop' );
+ $user = $this->testUser->getUser();
$bp = BotPassword::newUnsaved( [
'user' => $user,
'appId' => 'DoesNotExist'
$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'
$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'
'appId' => str_repeat( 'X', BotPassword::APPID_MAXLENGTH + 1 ),
] ) );
$this->assertNull( BotPassword::newUnsaved( [
- 'user' => 'UTSysop',
+ 'user' => $this->testUserName,
'appId' => 'Ok',
] ) );
$this->assertNull( BotPassword::newUnsaved( [
$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() );
$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' ) );
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 );
$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 );
$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
$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
);
$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!
$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();
$this->getMockForAbstractClass( 'CentralIdLookup' )
);
- $user = User::newFromName( 'UTSysop' );
+ $user = static::getTestSysop()->getUser();
$this->assertSame( $user, $mock->checkAudience( $user ) );
$user = $mock->checkAudience( CentralIdLookup::AUDIENCE_PUBLIC );
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,
$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,
$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' );
$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' );
public function testLookupUserNames() {
$lookup = new LocalIdLookup();
- $user1 = User::newFromName( 'UTLocalIdLookup1' );
+ $user1 = $this->getLookupUser();
$user2 = User::newFromName( 'UTLocalIdLookup2' );
$this->assertTrue( $user1->isAllowed( 'hideuser' ), 'sanity check' );
$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' );
public function testIsAttached() {
$lookup = new LocalIdLookup();
- $user1 = User::newFromName( 'UTLocalIdLookup1' );
+ $user1 = $this->getLookupUser();
$user2 = User::newFromName( 'DoesNotExist' );
$this->assertTrue( $lookup->isAttached( $user1 ) );
$lookup = new LocalIdLookup();
$this->assertSame(
$sharedDB && $sharedTable && $localDBSet,
- $lookup->isAttached( User::newFromName( 'UTLocalIdLookup1' ), 'shared' )
+ $lookup->isAttached( $this->getLookupUser(), 'shared' )
);
}
* @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' ) );
* @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' ) );
}
'MinimalPasswordLength' => 6,
'PasswordCannotMatchUsername' => true,
'PasswordCannotMatchBlacklist' => true,
- 'MaximalPasswordLength' => 30,
+ 'MaximalPasswordLength' => 40,
],
],
'checks' => [
],
] );
- $user = User::newFromName( 'Useruser' );
+ $user = static::getTestUser()->getUser();
+
// Sanity
$this->assertTrue( $user->isValidPassword( 'Password1234' ) );
$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' ) );
}
* @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 ) );
}
* @covers User::getId
*/
public function testGetId() {
- $user = User::newFromName( 'UTSysop' );
+ $user = static::getTestUser()->getUser();
$this->assertTrue( $user->getId() > 0 );
}
* @covers User::isAnon
*/
public function testLoggedIn() {
- $user = User::newFromName( 'UTSysop' );
+ $user = $this->getMutableTestUser()->getUser();
$this->assertTrue( $user->isLoggedIn() );
$this->assertFalse( $user->isAnon() );
* @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();
// 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">'
assert.strictEqual( $( '#nsassociated' ).prop( 'disabled' ), false );
// Initiate the recentchanges module
- mw.special.recentchanges.init();
+ rc.init();
// By default
assert.strictEqual( $( '#nsinvert' ).prop( 'disabled' ), true );