For notes on 1.33.x and older releases, see HISTORY.
=== Configuration changes for system administrators in 1.34 ===
+
==== New configuration ====
* …
* …
=== External library changes in 1.34 ===
+
==== New external libraries ====
* …
* …
=== Deprecations in 1.34 ===
-* The MWNamespace class is deprecated. Use MediaWikiServices::getNamespaceInfo.
+* The MWNamespace class is deprecated. Use NamespaceInfo.
* ExtensionRegistry->load() is deprecated, as it breaks dependency checking.
Instead, use ->queue().
* User::isBlocked() is deprecated since it does not tell you if the user is
instead.
* The Config argument to ChangesListSpecialPage::checkStructuredFilterUiEnabled
is deprecated. Pass only the User argument.
+* WatchedItem::getUser is deprecated. Use getUserIdentity.
+* Passing a Title as the first parameter to the getTimestampById method of
+ RevisionStore is deprecated. Omit it, passing only the remaining parameters.
+* Title::getPreviousRevisionId and Title::getNextRevisionId are deprecated. Use
+ RevisionLookup::getPreviousRevision and RevisionLookup::getNextRevision.
+* The Title parameter to RevisionLookup::getPreviousRevision and
+ RevisionLookup::getNextRevision is deprecated and should be omitted.
+* MWHttpRequest::factory is deprecated. Use HttpRequestFactory.
+* The Http class is deprecated. For the request, get, and post methods, use
+ HttpRequestFactory. For isValidURI, use MWHttpRequest::isValidURI. For
+ getProxy, use (string)$wgHTTPProxy. For createMultiClient, construct a
+ MultiHttpClient directly.
+* Http::$httpEngine is deprecated and has no replacement. The default 'guzzle'
+ engine will eventually be made the only engine for HTTP requests.
=== Other changes in 1.34 ===
* …
/**
* Proxy to use for CURL requests.
*/
-$wgHTTPProxy = false;
+$wgHTTPProxy = '';
/**
* Local virtual hosts.
}
/**
+ * Move a page without taking user permissions into account. Only checks if the move is itself
+ * invalid, e.g., trying to move a special page or trying to move a page onto one that already
+ * exists.
+ *
+ * @param User $user
+ * @param string|null $reason
+ * @param bool|null $createRedirect
+ * @param string[] $changeTags Change tags to apply to the entry in the move log
+ * @return Status
+ */
+ public function move(
+ User $user, $reason = null, $createRedirect = true, array $changeTags = []
+ ) {
+ $status = $this->isValidMove();
+ if ( !$status->isOK() ) {
+ return $status;
+ }
+
+ return $this->moveUnsafe( $user, $reason, $createRedirect, $changeTags );
+ }
+
+ /**
+ * Same as move(), but with permissions checks.
+ *
+ * @param User $user
+ * @param string|null $reason
+ * @param bool|null $createRedirect Ignored if user doesn't have suppressredirect permission
+ * @param string[] $changeTags Change tags to apply to the entry in the move log
+ * @return Status
+ */
+ public function moveIfAllowed(
+ User $user, $reason = null, $createRedirect = true, array $changeTags = []
+ ) {
+ $status = $this->isValidMove();
+ $status->merge( $this->checkPermissions( $user, $reason ) );
+ if ( $changeTags ) {
+ $status->merge( ChangeTags::canAddTagsAccompanyingChange( $changeTags, $user ) );
+ }
+
+ if ( !$status->isOK() ) {
+ // Auto-block user's IP if the account was "hard" blocked
+ $user->spreadAnyEditBlock();
+ return $status;
+ }
+
+ // Check suppressredirect permission
+ if ( !$user->isAllowed( 'suppressredirect' ) ) {
+ $createRedirect = true;
+ }
+
+ return $this->moveUnsafe( $user, $reason, $createRedirect, $changeTags );
+ }
+
+ /**
+ * Moves *without* any sort of safety or sanity checks. Hooks can still fail the move, however.
+ *
* @param User $user
* @param string $reason
* @param bool $createRedirect
- * @param string[] $changeTags Change tags to apply to the entry in the move log. Caller
- * should perform permission checks with ChangeTags::canAddTagsAccompanyingChange
+ * @param string[] $changeTags Change tags to apply to the entry in the move log
* @return Status
*/
- public function move( User $user, $reason, $createRedirect, array $changeTags = [] ) {
+ private function moveUnsafe( User $user, $reason, $createRedirect, array $changeTags ) {
global $wgCategoryCollation;
$status = Status::newGood();
/** @var bool If set to true, blocked users will no longer be allowed to log in */
private $blockDisablesLogin;
+ /** @var NamespaceInfo */
+ private $nsInfo;
+
/**
* @param SpecialPageFactory $specialPageFactory
* @param string[] $whitelistRead
* @param string[] $whitelistReadRegexp
* @param bool $emailConfirmToEdit
* @param bool $blockDisablesLogin
+ * @param NamespaceInfo $nsInfo
*/
public function __construct(
SpecialPageFactory $specialPageFactory,
use Psr\Log\LoggerInterface;
use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
/**
* Send information about this MediaWiki instance to MediaWiki.org.
$json = FormatJson::encode( $data );
$queryString = rawurlencode( str_replace( ' ', '\u0020', $json ) ) . ';';
$url = 'https://www.mediawiki.org/beacon/event?' . $queryString;
- return Http::post( $url ) !== false;
+ return MediaWikiServices::getInstance()->getHttpRequestFactory()->post( $url ) !== null;
}
/**
* @return Revision|null
*/
public function getPrevious() {
- $title = $this->getTitle();
- $rec = self::getRevisionLookup()->getPreviousRevision( $this->mRecord, $title );
- return $rec ? new Revision( $rec, self::READ_NORMAL, $title ) : null;
+ $rec = self::getRevisionLookup()->getPreviousRevision( $this->mRecord );
+ return $rec ? new Revision( $rec, self::READ_NORMAL, $this->getTitle() ) : null;
}
/**
* @return Revision|null
*/
public function getNext() {
- $title = $this->getTitle();
- $rec = self::getRevisionLookup()->getNextRevision( $this->mRecord, $title );
- return $rec ? new Revision( $rec, self::READ_NORMAL, $title ) : null;
+ $rec = self::getRevisionLookup()->getNextRevision( $this->mRecord );
+ return $rec ? new Revision( $rec, self::READ_NORMAL, $this->getTitle() ) : null;
}
/**
/**
* Get rev_timestamp from rev_id, without loading the rest of the row
*
- * @param Title $title
+ * @param Title $title (ignored since 1.34)
* @param int $id
* @param int $flags
* @return string|bool False if not found
*/
static function getTimestampFromId( $title, $id, $flags = 0 ) {
- return self::getRevisionStore()->getTimestampFromId( $title, $id, $flags );
+ return self::getRevisionStore()->getTimestampFromId( $id, $flags );
}
/**
* MCR migration note: this replaces Revision::getPrevious
*
* @param RevisionRecord $rev
- * @param Title|null $title if known (optional)
+ * @param int $flags (optional) $flags include:
+ * IDBAccessObject::READ_LATEST: Select the data from the master
*
* @return RevisionRecord|null
*/
- public function getPreviousRevision( RevisionRecord $rev, Title $title = null );
+ public function getPreviousRevision( RevisionRecord $rev, $flags = 0 );
/**
* Get next revision for this title
* MCR migration note: this replaces Revision::getNext
*
* @param RevisionRecord $rev
- * @param Title|null $title if known (optional)
+ * @param int $flags (optional) $flags include:
+ * IDBAccessObject::READ_LATEST: Select the data from the master
*
* @return RevisionRecord|null
*/
- public function getNextRevision( RevisionRecord $rev, Title $title = null );
+ public function getNextRevision( RevisionRecord $rev, $flags = 0 );
+
+ /**
+ * Get rev_timestamp from rev_id, without loading the rest of the row.
+ *
+ * MCR migration note: this replaces Revision::getTimestampFromId
+ *
+ * @param int $id
+ * @param int $flags
+ * @return string|bool False if not found
+ * @since 1.34 (present earlier in RevisionStore)
+ */
+ public function getTimestampFromId( $id, $flags = 0 );
/**
* Load a revision based on a known page ID and current revision ID from the DB
/**
* @param int $mode DB_MASTER or DB_REPLICA
+ * @param array $groups
*
* @return IDatabase
*/
- private function getDBConnection( $mode ) {
+ private function getDBConnection( $mode, $groups = [] ) {
$lb = $this->getDBLoadBalancer();
- return $lb->getConnection( $mode, [], $this->wikiId );
+ return $lb->getConnection( $mode, $groups, $this->wikiId );
}
/**
}
/**
- * Get the revision before $rev in the page's history, if any.
- * Will return null for the first revision but also for deleted or unsaved revisions.
- *
- * MCR migration note: this replaces Revision::getPrevious
- *
- * @see Title::getPreviousRevisionID
- * @see PageArchive::getPreviousRevision
+ * Implementation of getPreviousRevision and getNextRevision.
*
* @param RevisionRecord $rev
- * @param Title|null $title if known (optional)
- *
+ * @param int $flags
+ * @param string $dir 'next' or 'prev'
* @return RevisionRecord|null
*/
- public function getPreviousRevision( RevisionRecord $rev, Title $title = null ) {
+ private function getRelativeRevision( RevisionRecord $rev, $flags, $dir ) {
+ $op = $dir === 'next' ? '>' : '<';
+ $sort = $dir === 'next' ? 'ASC' : 'DESC';
+
if ( !$rev->getId() || !$rev->getPageId() ) {
// revision is unsaved or otherwise incomplete
return null;
return null;
}
- if ( $title === null ) {
- // this would fail for deleted revisions
- $title = $this->getTitle( $rev->getPageId(), $rev->getId() );
+ list( $dbType, ) = DBAccessObjectUtils::getDBOptions( $flags );
+ $db = $this->getDBConnection( $dbType, [ 'contributions' ] );
+
+ $ts = $this->getTimestampFromId( $rev->getId(), $flags );
+ if ( $ts === false ) {
+ // XXX Should this be moved into getTimestampFromId?
+ $ts = $db->selectField( 'archive', 'ar_timestamp',
+ [ 'ar_rev_id' => $rev->getId() ], __METHOD__ );
+ if ( $ts === false ) {
+ // XXX Is this reachable? How can we have a page id but no timestamp?
+ return null;
+ }
}
+ $ts = $db->addQuotes( $db->timestamp( $ts ) );
+
+ $revId = $db->selectField( 'revision', 'rev_id',
+ [
+ 'rev_page' => $rev->getPageId(),
+ "rev_timestamp $op $ts OR (rev_timestamp = $ts AND rev_id $op {$rev->getId()})"
+ ],
+ __METHOD__,
+ [
+ 'ORDER BY' => "rev_timestamp $sort, rev_id $sort",
+ 'IGNORE INDEX' => 'rev_timestamp', // Probably needed for T159319
+ ]
+ );
- $prev = $title->getPreviousRevisionID( $rev->getId() );
- if ( !$prev ) {
+ if ( $revId === false ) {
return null;
}
- return $this->getRevisionByTitle( $title, $prev );
+ return $this->getRevisionById( intval( $revId ) );
}
/**
- * Get the revision after $rev in the page's history, if any.
- * Will return null for the latest revision but also for deleted or unsaved revisions.
+ * Get the revision before $rev in the page's history, if any.
+ * Will return null for the first revision but also for deleted or unsaved revisions.
*
- * MCR migration note: this replaces Revision::getNext
+ * MCR migration note: this replaces Revision::getPrevious
*
- * @see Title::getNextRevisionID
+ * @see Title::getPreviousRevisionID
+ * @see PageArchive::getPreviousRevision
*
* @param RevisionRecord $rev
- * @param Title|null $title if known (optional)
+ * @param int $flags (optional) $flags include:
+ * IDBAccessObject::READ_LATEST: Select the data from the master
*
* @return RevisionRecord|null
*/
- public function getNextRevision( RevisionRecord $rev, Title $title = null ) {
- if ( !$rev->getId() || !$rev->getPageId() ) {
- // revision is unsaved or otherwise incomplete
- return null;
- }
-
- if ( $rev instanceof RevisionArchiveRecord ) {
- // revision is deleted, so it's not part of the page history
- return null;
+ public function getPreviousRevision( RevisionRecord $rev, $flags = 0 ) {
+ if ( $flags instanceof Title ) {
+ // Old calling convention, we don't use Title here anymore
+ wfDeprecated( __METHOD__ . ' with Title', '1.34' );
+ $flags = 0;
}
- if ( $title === null ) {
- // this would fail for deleted revisions
- $title = $this->getTitle( $rev->getPageId(), $rev->getId() );
- }
+ return $this->getRelativeRevision( $rev, $flags, 'prev' );
+ }
- $next = $title->getNextRevisionID( $rev->getId() );
- if ( !$next ) {
- return null;
+ /**
+ * Get the revision after $rev in the page's history, if any.
+ * Will return null for the latest revision but also for deleted or unsaved revisions.
+ *
+ * MCR migration note: this replaces Revision::getNext
+ *
+ * @see Title::getNextRevisionID
+ *
+ * @param RevisionRecord $rev
+ * @param int $flags (optional) $flags include:
+ * IDBAccessObject::READ_LATEST: Select the data from the master
+ * @return RevisionRecord|null
+ */
+ public function getNextRevision( RevisionRecord $rev, $flags = 0 ) {
+ if ( $flags instanceof Title ) {
+ // Old calling convention, we don't use Title here anymore
+ wfDeprecated( __METHOD__ . ' with Title', '1.34' );
+ $flags = 0;
}
- return $this->getRevisionByTitle( $title, $next );
+ return $this->getRelativeRevision( $rev, $flags, 'next' );
}
/**
}
/**
- * Get rev_timestamp from rev_id, without loading the rest of the row
+ * Get rev_timestamp from rev_id, without loading the rest of the row.
+ *
+ * Historically, there was an extra Title parameter that was passed before $id. This is no
+ * longer needed and is deprecated in 1.34.
*
* MCR migration note: this replaces Revision::getTimestampFromId
*
- * @param Title $title
* @param int $id
* @param int $flags
* @return string|bool False if not found
*/
- public function getTimestampFromId( $title, $id, $flags = 0 ) {
+ public function getTimestampFromId( $id, $flags = 0 ) {
+ if ( $id instanceof Title ) {
+ // Old deprecated calling convention supported for backwards compatibility
+ $id = $flags;
+ $flags = func_num_args() > 2 ? func_get_arg( 2 ) : 0;
+ }
$db = $this->getDBConnectionRefForQueryFlags( $flags );
- $conds = [ 'rev_id' => $id ];
- $conds['rev_page'] = $title->getArticleID();
- $timestamp = $db->selectField( 'revision', 'rev_timestamp', $conds, __METHOD__ );
+ $timestamp =
+ $db->selectField( 'revision', 'rev_timestamp', [ 'rev_id' => $id ], __METHOD__ );
return ( $timestamp !== false ) ? wfTimestamp( TS_MW, $timestamp ) : false;
}
},
'GenderCache' => function ( MediaWikiServices $services ) : GenderCache {
- return new GenderCache();
+ return new GenderCache( $services->getNamespaceInfo() );
},
'HttpRequestFactory' =>
},
'NamespaceInfo' => function ( MediaWikiServices $services ) : NamespaceInfo {
- return new NamespaceInfo( $services->getMainConfig() );
+ return new NamespaceInfo( new ServiceOptions( NamespaceInfo::$constructorOptions,
+ $services->getMainConfig() ) );
},
'NameTableStoreFactory' => function ( MediaWikiServices $services ) : NameTableStoreFactory {
DefaultPreferencesFactory::$constructorOptions, $services->getMainConfig() ),
$services->getContentLanguage(),
AuthManager::singleton(),
- $services->getLinkRendererFactory()->create()
+ $services->getLinkRendererFactory()->create(),
+ $services->getNamespaceInfo()
);
$factory->setLogger( LoggerFactory::getInstance( 'preferences' ) );
array $changeTags = []
) {
global $wgUser;
- $err = $this->isValidMoveOperation( $nt, $auth, $reason );
- if ( is_array( $err ) ) {
- // Auto-block user's IP if the account was "hard" blocked
- $wgUser->spreadAnyEditBlock();
- return $err;
- }
- // Check suppressredirect permission
- if ( $auth && !$wgUser->isAllowed( 'suppressredirect' ) ) {
- $createRedirect = true;
- }
$mp = new MovePage( $this, $nt );
- $status = $mp->move( $wgUser, $reason, $createRedirect, $changeTags );
+ $method = $auth ? 'moveIfAllowed' : 'move';
+ $status = $mp->$method( $wgUser, $reason, $createRedirect, $changeTags );
if ( $status->isOK() ) {
return true;
} else {
* @return int|bool New revision ID, or false if none exists
*/
private function getRelativeRevisionID( $revId, $flags, $dir ) {
- $revId = (int)$revId;
- if ( $dir === 'next' ) {
- $op = '>';
- $sort = 'ASC';
- } elseif ( $dir === 'prev' ) {
- $op = '<';
- $sort = 'DESC';
- } else {
- throw new InvalidArgumentException( '$dir must be "next" or "prev"' );
- }
-
- if ( $flags & self::GAID_FOR_UPDATE ) {
- $db = wfGetDB( DB_MASTER );
- } else {
- $db = wfGetDB( DB_REPLICA, 'contributions' );
- }
-
- // Intentionally not caring if the specified revision belongs to this
- // page. We only care about the timestamp.
- $ts = $db->selectField( 'revision', 'rev_timestamp', [ 'rev_id' => $revId ], __METHOD__ );
- if ( $ts === false ) {
- $ts = $db->selectField( 'archive', 'ar_timestamp', [ 'ar_rev_id' => $revId ], __METHOD__ );
- if ( $ts === false ) {
- // Or should this throw an InvalidArgumentException or something?
- return false;
- }
+ $rl = MediaWikiServices::getInstance()->getRevisionLookup();
+ $rlFlags = $flags === self::GAID_FOR_UPDATE ? IDBAccessObject::READ_LATEST : 0;
+ $rev = $rl->getRevisionById( $revId, $rlFlags );
+ if ( !$rev ) {
+ return false;
}
- $ts = $db->addQuotes( $ts );
-
- $revId = $db->selectField( 'revision', 'rev_id',
- [
- 'rev_page' => $this->getArticleID( $flags ),
- "rev_timestamp $op $ts OR (rev_timestamp = $ts AND rev_id $op $revId)"
- ],
- __METHOD__,
- [
- 'ORDER BY' => "rev_timestamp $sort, rev_id $sort",
- 'IGNORE INDEX' => 'rev_timestamp', // Probably needed for T159319
- ]
- );
-
- if ( $revId === false ) {
+ $oldRev = $dir === 'next'
+ ? $rl->getNextRevision( $rev, $rlFlags )
+ : $rl->getPreviousRevision( $rev, $rlFlags );
+ if ( !$oldRev ) {
return false;
- } else {
- return intval( $revId );
}
+ return $oldRev->getId();
}
/**
* Get the revision ID of the previous revision
*
+ * @deprecated since 1.34, use RevisionLookup::getPreviousRevision
* @param int $revId Revision ID. Get the revision that was before this one.
* @param int $flags Title::GAID_FOR_UPDATE
* @return int|bool Old revision ID, or false if none exists
/**
* Get the revision ID of the next revision
*
+ * @deprecated since 1.34, use RevisionLookup::getNextRevision
* @param int $revId Revision ID. Get the revision that was after this one.
* @param int $flags Title::GAID_FOR_UPDATE
* @return int|bool Next revision ID, or false if none exists
$titles = $pageSet->getGoodTitles();
$title = reset( $titles );
if ( $title ) {
+ // XXX $title isn't actually used, can we just get rid of the previous six lines?
$timestamp = MediaWikiServices::getInstance()->getRevisionStore()
- ->getTimestampFromId( $title, $params['torevid'], IDBAccessObject::READ_LATEST );
+ ->getTimestampFromId( $params['torevid'], IDBAccessObject::READ_LATEST );
if ( $timestamp ) {
$timestamp = $dbw->timestamp( $timestamp );
} else {
"apihelp-edit-param-text": "문서 내용.",
"apihelp-edit-param-summary": "편집 요약. 또한 $1section=new 및 $1sectiontitle이 설정되어 있지 않을 때 문단 제목.",
"apihelp-edit-param-tags": "이 판에 적용할 태그를 변경합니다.",
- "apihelp-edit-param-minor": "ì\82¬ì\86\8cí\95\9c í\8e¸ì§\91.",
+ "apihelp-edit-param-minor": "ì\9d´ í\8e¸ì§\91ì\9d\84 ì\82¬ì\86\8cí\95\9c í\8e¸ì§\91ì\9c¼ë¡\9c í\91\9cì\8b\9cí\95©ë\8b\88ë\8b¤.",
"apihelp-edit-param-notminor": "사소하지 않은 편집.",
"apihelp-edit-param-bot": "이 편집을 봇 편집으로 표시.",
"apihelp-edit-param-basetimestamp": "기본 판의 타임스탬프이며, 편집 충돌을 발견하기 위해 사용됩니다. [[Special:ApiHelp/query+revisions|action=query&prop=revisions&rvprop=timestamp]]를 통해 가져올 수 있습니다.",
"Hex",
"Mainframe98",
"Southparkfan",
- "Elroy"
+ "Elroy",
+ "Rots61"
]
},
"apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Documentatie]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api E-maillijst]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-aankondigingen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bugs & verzoeken]\n</div>\n<strong>Status:</strong> De MediaWiki API is een stabiele interface die actief ondersteund en verbeterd wordt. Hoewel we het proberen te voorkomen, is het mogelijk dat er soms wijzigingen worden aangebracht die bepaalde API-verzoek kunnen verhinderen; abonneer u op de [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ e-maillijst mediawiki-api-announce] voor meldingen over wijzigingen.\n\n<strong>Foutieve verzoeken:</strong> als de API foutieve verzoeken ontvangt, wordt er geantwoord met een HTTP-header met de sleutel \"MediaWiki-API-Error\" en daarna worden de waarde van de header en de foutcode op dezelfde waarde ingesteld. Zie [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Foutmeldingen en waarschuwingen]] voor meer informatie.\n\n<p class=\"mw-apisandbox-link\"><strong>Testen:</strong> u kunt [[Special:ApiSandbox|eenvoudig API-verzoeken testen]].</p>",
"apihelp-edit-param-sectiontitle": "De naam van een nieuwe sectie.",
"apihelp-edit-param-text": "Pagina-inhoud.",
"apihelp-edit-param-tags": "De labels voor de revisie wijzigen.",
- "apihelp-edit-param-minor": "Kleine bewerking.",
+ "apihelp-edit-param-minor": "Mankeer deze bewerking als een kleine bewerking.",
"apihelp-edit-param-notminor": "Niet-kleine bewerking.",
"apihelp-edit-param-bot": "Deze bewerking markeren als een botbewerking.",
"apihelp-edit-param-createonly": "De pagina niet bewerken als die al bestaat.",
"Woytecr",
"InternerowyGołąb",
"CiaPan",
- "Vlad5250"
+ "Vlad5250",
+ "Railfail536"
]
},
"apihelp-main-extended-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:Special:MyLanguage/API:Main_page|Dokumentacja]]\n* [[mw:Special:MyLanguage/API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Lista dyskusyjna]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Ogłoszenia dotyczące API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Błędy i propozycje]\n</div>\n<strong>Stan:</strong> Wszystkie funkcje opisane na tej stronie powinny działać, ale API nadal jest aktywnie rozwijane i mogą się zmienić w dowolnym czasie. Subskrybuj [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ listę dyskusyjną mediawiki-api-announce], aby móc na bieżąco dowiadywać się o aktualizacjach.\n\n<strong>Błędne żądania:</strong> Gdy zostanie wysłane błędne żądanie do API, zostanie wysłany w odpowiedzi nagłówek HTTP z kluczem \"MediaWiki-API-Error\" i zarówno jego wartość jak i wartość kodu błędu wysłanego w odpowiedzi będą miały taką samą wartość. Aby uzyskać więcej informacji, zobacz [[mw:Special:MyLanguage/API:Errors_and_warnings|API: Błędy i ostrzeżenia]].\n\n<strong>Testowanie:</strong> Aby łatwo testować żądania API, zobacz [[Special:ApiSandbox]].",
"apihelp-edit-param-text": "Zawartość strony.",
"apihelp-edit-param-summary": "Opis edycji. Także tytuł sekcji gdy użyto $1section=new, a nie ustawiono $1sectiontitle.",
"apihelp-edit-param-tags": "Znaczniki zmian do zastosowania w tej edycji.",
- "apihelp-edit-param-minor": "Drobna zmiana.",
+ "apihelp-edit-param-minor": "Oznacz tą zmianę jako drobną zmianę.",
"apihelp-edit-param-notminor": "Nie oznaczaj tej zmiany jako drobną.",
"apihelp-edit-param-bot": "Oznacz tę edycję jako edycję bota.",
"apihelp-edit-param-basetimestamp": "Czas wersji, która jest edytowana. Służy do wykrywania konfliktów edycji. Można pobrać poprzez [[Special:ApiHelp/query+revisions|action=query&prop=revisions&rvprop=timestamp]].",
protected $misses = 0;
protected $missLimit = 1000;
+ /** @var NamespaceInfo */
+ private $nsInfo;
+
+ public function __construct( NamespaceInfo $nsInfo = null ) {
+ $this->nsInfo = $nsInfo ?? MediaWikiServices::getInstance()->getNamespaceInfo();
+ }
+
/**
* @deprecated in 1.28 see MediaWikiServices::getInstance()->getGenderCache()
* @return GenderCache
public function doLinkBatch( $data, $caller = '' ) {
$users = [];
foreach ( $data as $ns => $pagenames ) {
- if ( !MWNamespace::hasGenderDistinction( $ns ) ) {
+ if ( !$this->nsInfo->hasGenderDistinction( $ns ) ) {
continue;
}
foreach ( array_keys( $pagenames ) as $username ) {
if ( !$titleObj ) {
continue;
}
- if ( !MWNamespace::hasGenderDistinction( $titleObj->getNamespace() ) ) {
+ if ( !$this->nsInfo->hasGenderDistinction( $titleObj->getNamespace() ) ) {
continue;
}
$users[] = $titleObj->getText();
* @file
*/
+use MediaWiki\MediaWikiServices;
+
/**
* Example class for HTTP accessible external objects.
* Only supports reading, not storing.
*/
class ExternalStoreHttp extends ExternalStoreMedium {
public function fetchFromURL( $url ) {
- return Http::get( $url, [], __METHOD__ );
+ return MediaWikiServices::getInstance()->getHttpRequestFactory()->
+ get( $url, [], __METHOD__ );
}
public function store( $location, $data ) {
}
/**
- * Like a Http:get request, but with custom User-Agent.
- * @see Http::get
+ * Like a HttpRequestFactory::get request, but with custom User-Agent.
+ * @see HttpRequestFactory::get
+ * @todo Can this use HttpRequestFactory::get() but just pass the 'userAgent' option?
* @param string $url
* @param string $timeout
* @param array $options
$this->repo->descriptionCacheExpiry ?: $cache::TTL_UNCACHEABLE,
function ( $oldValue, &$ttl, array &$setOpts ) use ( $renderUrl, $fname ) {
wfDebug( "Fetching shared description from $renderUrl\n" );
- $res = Http::get( $renderUrl, [], $fname );
+ $res = MediaWikiServices::getInstance()->getHttpRequestFactory()->
+ get( $renderUrl, [], $fname );
if ( !$res ) {
$ttl = WANObjectCache::TTL_UNCACHEABLE;
}
$this->repo->descriptionCacheExpiry ?: $cache::TTL_UNCACHEABLE,
function ( $oldValue, &$ttl, array &$setOpts ) use ( $renderUrl, $fname ) {
wfDebug( "Fetching shared description from $renderUrl\n" );
- $res = Http::get( $renderUrl, [], $fname );
+ $res = MediaWikiServices::getInstance()->getHttpRequestFactory()->
+ get( $renderUrl, [], $fname );
if ( !$res ) {
$ttl = WANObjectCache::TTL_UNCACHEABLE;
}
protected $curlOptions = [];
protected $headerText = "";
+ /**
+ * @throws RuntimeException
+ */
+ public function __construct() {
+ if ( !function_exists( 'curl_init' ) ) {
+ throw new RuntimeException(
+ __METHOD__ . ': curl (https://www.php.net/curl) is not installed' );
+ }
+
+ parent::__construct( ...func_get_args() );
+ }
+
/**
* @param resource $fh
* @param string $content
/**
* @param string $url Url to use. If protocol-relative, will be expanded to an http:// URL
- * @param array $options (optional) extra params to pass (see Http::request())
+ * @param array $options (optional) extra params to pass (see HttpRequestFactory::create())
* @param string $caller The method making this request, for profiling
* @param Profiler|null $profiler An instance of the profiler for profiling, or null
* @throws Exception
*/
use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\MediaWikiServices;
/**
* Various HTTP related functions
+ * @deprecated since 1.34
* @ingroup HTTP
*/
class Http {
- public static $httpEngine = false;
+ /** @deprecated since 1.34, just use the default engine */
+ public static $httpEngine = null;
/**
* Perform an HTTP request
*
+ * @deprecated since 1.34, use HttpRequestFactory::request()
+ *
* @param string $method HTTP method. Usually GET/POST
* @param string $url Full URL to act on. If protocol-relative, will be expanded to an http:// URL
- * @param array $options Options to pass to MWHttpRequest object.
- * Possible keys for the array:
- * - timeout Timeout length in seconds
- * - connectTimeout Timeout for connection, in seconds (curl only)
- * - postData An array of key-value pairs or a url-encoded form data
- * - proxy The proxy to use.
- * Otherwise it will use $wgHTTPProxy (if set)
- * Otherwise it will use the environment variable "http_proxy" (if set)
- * - noProxy Don't use any proxy at all. Takes precedence over proxy value(s).
- * - sslVerifyHost Verify hostname against certificate
- * - sslVerifyCert Verify SSL certificate
- * - caInfo Provide CA information
- * - maxRedirects Maximum number of redirects to follow (defaults to 5)
- * - followRedirects Whether to follow redirects (defaults to false).
- * Note: this should only be used when the target URL is trusted,
- * to avoid attacks on intranet services accessible by HTTP.
- * - userAgent A user agent, if you want to override the default
- * MediaWiki/$wgVersion
- * - logger A \Psr\Logger\LoggerInterface instance for debug logging
- * - username Username for HTTP Basic Authentication
- * - password Password for HTTP Basic Authentication
- * - originalRequest Information about the original request (as a WebRequest object or
- * an associative array with 'ip' and 'userAgent').
+ * @param array $options Options to pass to MWHttpRequest object. See HttpRequestFactory::create
+ * docs
* @param string $caller The method making this request, for profiling
* @return string|bool (bool)false on failure or a string on success
*/
public static function request( $method, $url, array $options = [], $caller = __METHOD__ ) {
- $logger = LoggerFactory::getInstance( 'http' );
- $logger->debug( "$method: $url" );
-
- $options['method'] = strtoupper( $method );
-
- if ( !isset( $options['timeout'] ) ) {
- $options['timeout'] = 'default';
- }
- if ( !isset( $options['connectTimeout'] ) ) {
- $options['connectTimeout'] = 'default';
- }
-
- $req = MWHttpRequest::factory( $url, $options, $caller );
- $status = $req->execute();
-
- if ( $status->isOK() ) {
- return $req->getContent();
- } else {
- $errors = $status->getErrorsByType( 'error' );
- $logger->warning( Status::wrap( $status )->getWikiText( false, false, 'en' ),
- [ 'error' => $errors, 'caller' => $caller, 'content' => $req->getContent() ] );
- return false;
- }
+ $ret = MediaWikiServices::getInstance()->getHttpRequestFactory()->request(
+ $method, $url, $options, $caller );
+ return is_string( $ret ) ? $ret : false;
}
/**
* Simple wrapper for Http::request( 'GET' )
- * @see Http::request()
+ *
+ * @deprecated since 1.34, use HttpRequestFactory::get()
+ *
* @since 1.25 Second parameter $timeout removed. Second parameter
* is now $options which can be given a 'timeout'
*
/**
* Simple wrapper for Http::request( 'POST' )
- * @see Http::request()
+ *
+ * @deprecated since 1.34, use HttpRequestFactory::post()
*
* @param string $url
* @param array $options
/**
* A standard user-agent we can use for external requests.
+ *
+ * @deprecated since 1.34, use HttpRequestFactory::getUserAgent()
* @return string
*/
public static function userAgent() {
- global $wgVersion;
- return "MediaWiki/$wgVersion";
+ return MediaWikiServices::getInstance()->getHttpRequestFactory()->getUserAgent();
}
/**
*
* @todo FIXME this is wildly inaccurate and fails to actually check most stuff
*
+ * @deprecated since 1.34, use MWHttpRequest::isValidURI
* @param string $uri URI to check for validity
* @return bool
*/
public static function isValidURI( $uri ) {
- return (bool)preg_match(
- '/^https?:\/\/[^\/\s]\S*$/D',
- $uri
- );
+ return MWHttpRequest::isValidURI( $uri );
}
/**
* Gets the relevant proxy from $wgHTTPProxy
*
- * @return mixed The proxy address or an empty string if not set.
+ * @deprecated since 1.34, use $wgHTTPProxy directly
+ * @return string The proxy address or an empty string if not set.
*/
public static function getProxy() {
- global $wgHTTPProxy;
+ wfDeprecated( __METHOD__, '1.34' );
- if ( $wgHTTPProxy ) {
- return $wgHTTPProxy;
- }
-
- return "";
+ global $wgHTTPProxy;
+ return (string)$wgHTTPProxy;
}
/**
* Get a configured MultiHttpClient
+ *
+ * @deprecated since 1.34, construct it directly
* @param array $options
* @return MultiHttpClient
*/
public static function createMultiClient( array $options = [] ) {
+ wfDeprecated( __METHOD__, '1.34' );
+
global $wgHTTPConnectTimeout, $wgHTTPTimeout, $wgHTTPProxy;
return new MultiHttpClient( $options + [
namespace MediaWiki\Http;
use CurlHttpRequest;
-use DomainException;
+use GuzzleHttpRequest;
use Http;
use MediaWiki\Logger\LoggerFactory;
use MWHttpRequest;
use PhpHttpRequest;
use Profiler;
-use GuzzleHttpRequest;
+use RuntimeException;
+use Status;
/**
* Factory creating MWHttpRequest objects.
*/
class HttpRequestFactory {
-
/**
* Generate a new MWHttpRequest object
* @param string $url Url to use
- * @param array $options (optional) extra params to pass (see Http::request())
+ * @param array $options Possible keys for the array:
+ * - timeout Timeout length in seconds
+ * - connectTimeout Timeout for connection, in seconds (curl only)
+ * - postData An array of key-value pairs or a url-encoded form data
+ * - proxy The proxy to use.
+ * Otherwise it will use $wgHTTPProxy (if set)
+ * Otherwise it will use the environment variable "http_proxy" (if set)
+ * - noProxy Don't use any proxy at all. Takes precedence over proxy value(s).
+ * - sslVerifyHost Verify hostname against certificate
+ * - sslVerifyCert Verify SSL certificate
+ * - caInfo Provide CA information
+ * - maxRedirects Maximum number of redirects to follow (defaults to 5)
+ * - followRedirects Whether to follow redirects (defaults to false).
+ * Note: this should only be used when the target URL is trusted,
+ * to avoid attacks on intranet services accessible by HTTP.
+ * - userAgent A user agent, if you want to override the default
+ * MediaWiki/$wgVersion
+ * - logger A \Psr\Logger\LoggerInterface instance for debug logging
+ * - username Username for HTTP Basic Authentication
+ * - password Password for HTTP Basic Authentication
+ * - originalRequest Information about the original request (as a WebRequest object or
+ * an associative array with 'ip' and 'userAgent').
* @param string $caller The method making this request, for profiling
- * @throws DomainException
+ * @throws RuntimeException
* @return MWHttpRequest
* @see MWHttpRequest::__construct
*/
public function create( $url, array $options = [], $caller = __METHOD__ ) {
if ( !Http::$httpEngine ) {
Http::$httpEngine = 'guzzle';
- } elseif ( Http::$httpEngine == 'curl' && !function_exists( 'curl_init' ) ) {
- throw new DomainException( __METHOD__ . ': curl (https://www.php.net/curl) is not ' .
- 'installed, but Http::$httpEngine is set to "curl"' );
}
if ( !isset( $options['logger'] ) ) {
case 'curl':
return new CurlHttpRequest( $url, $options, $caller, Profiler::instance() );
case 'php':
- if ( !wfIniGetBool( 'allow_url_fopen' ) ) {
- throw new DomainException( __METHOD__ . ': allow_url_fopen ' .
- 'needs to be enabled for pure PHP http requests to ' .
- 'work. If possible, curl should be used instead. See ' .
- 'https://www.php.net/curl.'
- );
- }
return new PhpHttpRequest( $url, $options, $caller, Profiler::instance() );
default:
- throw new DomainException( __METHOD__ . ': The setting of Http::$httpEngine is not valid.' );
+ throw new RuntimeException( __METHOD__ . ': The requested engine is not valid.' );
}
}
return function_exists( 'curl_init' ) || wfIniGetBool( 'allow_url_fopen' );
}
+ /**
+ * Perform an HTTP request
+ *
+ * @since 1.34
+ * @param string $method HTTP method. Usually GET/POST
+ * @param string $url Full URL to act on. If protocol-relative, will be expanded to an http://
+ * URL
+ * @param array $options See HttpRequestFactory::create
+ * @param string $caller The method making this request, for profiling
+ * @return string|null null on failure or a string on success
+ */
+ public function request( $method, $url, array $options = [], $caller = __METHOD__ ) {
+ $logger = LoggerFactory::getInstance( 'http' );
+ $logger->debug( "$method: $url" );
+
+ $options['method'] = strtoupper( $method );
+
+ if ( !isset( $options['timeout'] ) ) {
+ $options['timeout'] = 'default';
+ }
+ if ( !isset( $options['connectTimeout'] ) ) {
+ $options['connectTimeout'] = 'default';
+ }
+
+ $req = $this->create( $url, $options, $caller );
+ $status = $req->execute();
+
+ if ( $status->isOK() ) {
+ return $req->getContent();
+ } else {
+ $errors = $status->getErrorsByType( 'error' );
+ $logger->warning( Status::wrap( $status )->getWikiText( false, false, 'en' ),
+ [ 'error' => $errors, 'caller' => $caller, 'content' => $req->getContent() ] );
+ return null;
+ }
+ }
+
+ /**
+ * Simple wrapper for request( 'GET' ), parameters have same meaning as for request()
+ *
+ * @since 1.34
+ * @param string $url
+ * @param array $options
+ * @param string $caller
+ * @return string|null
+ */
+ public function get( $url, array $options = [], $caller = __METHOD__ ) {
+ $this->request( 'GET', $url, $options, $caller );
+ }
+
+ /**
+ * Simple wrapper for request( 'POST' ), parameters have same meaning as for request()
+ *
+ * @since 1.34
+ * @param string $url
+ * @param array $options
+ * @param string $caller
+ * @return string|null
+ */
+ public function post( $url, array $options = [], $caller = __METHOD__ ) {
+ $this->request( 'POST', $url, $options, $caller );
+ }
+
+ /**
+ * @return string
+ */
+ public function getUserAgent() {
+ global $wgVersion;
+
+ return "MediaWiki/$wgVersion";
+ }
}
/**
* @param string $url Url to use. If protocol-relative, will be expanded to an http:// URL
- * @param array $options (optional) extra params to pass (see Http::request())
+ * @param array $options (optional) extra params to pass (see HttpRequestFactory::create())
* @param string $caller The method making this request, for profiling
* @param Profiler|null $profiler An instance of the profiler for profiling, or null
* @throws Exception
/**
* Generate a new request object
- * Deprecated: @see HttpRequestFactory::create
+ * @deprecated since 1.34, use HttpRequestFactory instead
* @param string $url Url to use
- * @param array|null $options (optional) extra params to pass (see Http::request())
+ * @param array|null $options (optional) extra params to pass (see HttpRequestFactory::create())
* @param string $caller The method making this request, for profiling
* @throws DomainException
* @return MWHttpRequest
if ( self::isLocalURL( $this->url ) || $this->noProxy ) {
$this->proxy = '';
} else {
- $this->proxy = Http::getProxy();
+ global $wgHTTPProxy;
+ $this->proxy = (string)$wgHTTPProxy;
}
}
$this->reqHeaders['X-Forwarded-For'] = $originalRequest['ip'];
$this->reqHeaders['X-Original-User-Agent'] = $originalRequest['userAgent'];
}
+
+ /**
+ * Check that the given URI is a valid one.
+ *
+ * This hardcodes a small set of protocols only, because we want to
+ * deterministically reject protocols not supported by all HTTP-transport
+ * methods.
+ *
+ * "file://" specifically must not be allowed, for security reasons
+ * (see <https://www.mediawiki.org/wiki/Special:Code/MediaWiki/r67684>).
+ *
+ * @todo FIXME this is wildly inaccurate and fails to actually check most stuff
+ *
+ * @since 1.34
+ * @param string $uri URI to check for validity
+ * @return bool
+ */
+ public static function isValidURI( $uri ) {
+ return (bool)preg_match(
+ '/^https?:\/\/[^\/\s]\S*$/D',
+ $uri
+ );
+ }
}
private $fopenErrors = [];
+ public function __construct() {
+ if ( !wfIniGetBool( 'allow_url_fopen' ) ) {
+ throw new RuntimeException( __METHOD__ . ': allow_url_fopen needs to be enabled for ' .
+ 'pure PHP http requests to work. If possible, curl should be used instead. See ' .
+ 'https://www.php.net/curl.'
+ );
+ }
+
+ parent::__construct( ...func_get_args() );
+ }
+
/**
* @param string $url
* @return string
# quicker and sorts out user-agent problems which might
# otherwise prevent importing from large sites, such
# as the Wikimedia cluster, etc.
- $data = Http::request(
+ $data = MediaWikiServices::getInstance()->getHttpRequestFactory()->request(
$method,
$url,
[
<?php
+use MediaWiki\MediaWikiServices;
use Psr\Log\LoggerInterface;
/**
// @todo FIXME!
$src = $wikiRevision->getSrc();
- $data = Http::get( $src, [], __METHOD__ );
+ $data = MediaWikiServices::getInstance()->getHttpRequestFactory()->
+ get( $src, [], __METHOD__ );
if ( !$data ) {
$this->logger->debug( "IMPORT: couldn't fetch source $src\n" );
fclose( $f );
}
try {
- $text = Http::get( $url . $file, [ 'timeout' => 3 ], __METHOD__ );
+ $text = MediaWikiServices::getInstance()->getHttpRequestFactory()->
+ get( $url . $file, [ 'timeout' => 3 ], __METHOD__ );
} catch ( Exception $e ) {
- // Http::get throws with allow_url_fopen = false and no curl extension.
+ // HttpRequestFactory::get can throw with allow_url_fopen = false and no curl
+ // extension.
$text = null;
}
unlink( $dir . $file );
<?php
use MediaWiki\MediaWikiServices;
+use MediaWiki\User\UserIdentity;
/**
* Job to clear a users watchlist in batches.
}
/**
- * @param User $user User to clear the watchlist for.
+ * @param UserIdentity $user User to clear the watchlist for.
* @param int $maxWatchlistId The maximum wl_id at the time the job was first created.
*
* @return ClearUserWatchlistJob
*/
- public static function newForUser( User $user, $maxWatchlistId ) {
+ public static function newForUser( UserIdentity $user, $maxWatchlistId ) {
return new self( [ 'userId' => $user->getId(), 'maxWatchlistId' => $maxWatchlistId ] );
}
use MediaWiki\MediaWikiServices;
use MessageLocalizer;
use MWException;
-use MWNamespace;
use MWTimestamp;
+use NamespaceInfo;
use OutputPage;
use Parser;
use ParserOptions;
/** @var LinkRenderer */
protected $linkRenderer;
+ /** @var NamespaceInfo */
+ protected $nsInfo;
+
/**
* TODO Make this a const when we drop HHVM support (T192166)
*
];
/**
+ * Do not call this directly. Get it from MediaWikiServices.
+ *
* @param array|Config $options Config accepted for backwards compatibility
* @param Language $contLang
* @param AuthManager $authManager
* @param LinkRenderer $linkRenderer
+ * @param NamespaceInfo|null $nsInfo
*/
public function __construct(
$options,
Language $contLang,
AuthManager $authManager,
- LinkRenderer $linkRenderer
+ LinkRenderer $linkRenderer,
+ NamespaceInfo $nsInfo = null
) {
if ( $options instanceof Config ) {
wfDeprecated( __METHOD__ . ' with Config parameter', '1.34' );
$options->assertRequiredOptions( self::$constructorOptions );
+ if ( !$nsInfo ) {
+ wfDeprecated( __METHOD__ . ' with no NamespaceInfo argument', '1.34' );
+ $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
+ }
$this->options = $options;
$this->contLang = $contLang;
$this->authManager = $authManager;
$this->linkRenderer = $linkRenderer;
+ $this->nsInfo = $nsInfo;
$this->logger = new NullLogger();
}
* @param array &$defaultPreferences
*/
protected function searchPreferences( &$defaultPreferences ) {
- foreach ( MWNamespace::getValidNamespaces() as $n ) {
+ foreach ( $this->nsInfo->getValidNamespaces() as $n ) {
$defaultPreferences['searchNs' . $n] = [
'type' => 'api',
];
# Do the actual move.
$mp = new MovePage( $ot, $nt );
- $valid = $mp->isValidMove();
- if ( !$valid->isOK() ) {
- $this->showForm( $valid->getErrorsArray() );
- return;
- }
- $permStatus = $mp->checkPermissions( $user, $this->reason );
- if ( !$permStatus->isOK() ) {
- $this->showForm( $permStatus->getErrorsArray(), true );
- return;
- }
+ $userPermitted = $mp->checkPermissions( $user, $this->reason )->isOK();
- $status = $mp->move( $user, $this->reason, $createRedirect );
+ $status = $mp->moveIfAllowed( $user, $this->reason, $createRedirect );
if ( !$status->isOK() ) {
- $this->showForm( $status->getErrorsArray() );
+ $this->showForm( $status->getErrorsArray(), !$userPermitted );
return;
}
* @file
*/
+use MediaWiki\Config\ServiceOptions;
+
/**
* This is a utility class for dealing with namespaces that encodes all the "magic" behaviors of
* them based on index. The textual names of the namespaces are handled by Language.php.
/** @var int[]|null Valid namespaces cache */
private $validNamespaces = null;
- /** @var Config */
- private $config;
+ /** @var ServiceOptions */
+ private $options;
+
+ /**
+ * TODO Make this const when HHVM support is dropped (T192166)
+ *
+ * @since 1.34
+ * @var array
+ */
+ public static $constructorOptions = [
+ 'AllowImageMoving',
+ 'CanonicalNamespaceNames',
+ 'CapitalLinkOverrides',
+ 'CapitalLinks',
+ 'ContentNamespaces',
+ 'ExtraNamespaces',
+ 'ExtraSignatureNamespaces',
+ 'NamespaceContentModels',
+ 'NamespaceProtection',
+ 'NamespacesWithSubpages',
+ 'NonincludableNamespaces',
+ 'RestrictionLevels',
+ ];
/**
- * @param Config $config
+ * @param ServiceOptions $options
*/
- public function __construct( Config $config ) {
- $this->config = $config;
+ public function __construct( ServiceOptions $options ) {
+ $options->assertRequiredOptions( self::$constructorOptions );
+ $this->options = $options;
}
/**
*/
public function isMovable( $index ) {
$result = !( $index < NS_MAIN ||
- ( $index == NS_FILE && !$this->config->get( 'AllowImageMoving' ) ) );
+ ( $index == NS_FILE && !$this->options->get( 'AllowImageMoving' ) ) );
/**
* @since 1.20
public function getCanonicalNamespaces() {
if ( $this->canonicalNamespaces === null ) {
$this->canonicalNamespaces =
- [ NS_MAIN => '' ] + $this->config->get( 'CanonicalNamespaceNames' );
+ [ NS_MAIN => '' ] + $this->options->get( 'CanonicalNamespaceNames' );
$this->canonicalNamespaces +=
ExtensionRegistry::getInstance()->getAttribute( 'ExtensionNamespaces' );
- if ( is_array( $this->config->get( 'ExtraNamespaces' ) ) ) {
- $this->canonicalNamespaces += $this->config->get( 'ExtraNamespaces' );
+ if ( is_array( $this->options->get( 'ExtraNamespaces' ) ) ) {
+ $this->canonicalNamespaces += $this->options->get( 'ExtraNamespaces' );
}
Hooks::run( 'CanonicalNamespaces', [ &$this->canonicalNamespaces ] );
}
* @return bool
*/
public function isContent( $index ) {
- return $index == NS_MAIN || in_array( $index, $this->config->get( 'ContentNamespaces' ) );
+ return $index == NS_MAIN || in_array( $index, $this->options->get( 'ContentNamespaces' ) );
}
/**
*/
public function wantSignatures( $index ) {
return $this->isTalk( $index ) ||
- in_array( $index, $this->config->get( 'ExtraSignatureNamespaces' ) );
+ in_array( $index, $this->options->get( 'ExtraSignatureNamespaces' ) );
}
/**
* @return bool
*/
public function hasSubpages( $index ) {
- return !empty( $this->config->get( 'NamespacesWithSubpages' )[$index] );
+ return !empty( $this->options->get( 'NamespacesWithSubpages' )[$index] );
}
/**
* @return array Array of namespace indices
*/
public function getContentNamespaces() {
- $contentNamespaces = $this->config->get( 'ContentNamespaces' );
+ $contentNamespaces = $this->options->get( 'ContentNamespaces' );
if ( !is_array( $contentNamespaces ) || $contentNamespaces === [] ) {
return [ NS_MAIN ];
} elseif ( !in_array( NS_MAIN, $contentNamespaces ) ) {
if ( in_array( $index, $this->alwaysCapitalizedNamespaces ) ) {
return true;
}
- $overrides = $this->config->get( 'CapitalLinkOverrides' );
+ $overrides = $this->options->get( 'CapitalLinkOverrides' );
if ( isset( $overrides[$index] ) ) {
// CapitalLinkOverrides is explicitly set
return $overrides[$index];
}
// Default to the global setting
- return $this->config->get( 'CapitalLinks' );
+ return $this->options->get( 'CapitalLinks' );
}
/**
* @return bool
*/
public function isNonincludable( $index ) {
- $namespaces = $this->config->get( 'NonincludableNamespaces' );
+ $namespaces = $this->options->get( 'NonincludableNamespaces' );
return $namespaces && in_array( $index, $namespaces );
}
* @return null|string Default model name for the given namespace, if set
*/
public function getNamespaceContentModel( $index ) {
- return $this->config->get( 'NamespaceContentModels' )[$index] ?? null;
+ return $this->options->get( 'NamespaceContentModels' )[$index] ?? null;
}
/**
* @return array
*/
public function getRestrictionLevels( $index, User $user = null ) {
- if ( !isset( $this->config->get( 'NamespaceProtection' )[$index] ) ) {
+ if ( !isset( $this->options->get( 'NamespaceProtection' )[$index] ) ) {
// All levels are valid if there's no namespace restriction.
// But still filter by user, if necessary
- $levels = $this->config->get( 'RestrictionLevels' );
+ $levels = $this->options->get( 'RestrictionLevels' );
if ( $user ) {
$levels = array_values( array_filter( $levels, function ( $level ) use ( $user ) {
$right = $level;
// First, get the list of groups that can edit this namespace.
$namespaceGroups = [];
$combine = 'array_merge';
- foreach ( (array)$this->config->get( 'NamespaceProtection' )[$index] as $right ) {
+ foreach ( (array)$this->options->get( 'NamespaceProtection' )[$index] as $right ) {
if ( $right == 'sysop' ) {
$right = 'editprotected'; // BC
}
// group that can edit the namespace but would be blocked by the
// restriction.
$usableLevels = [ '' ];
- foreach ( $this->config->get( 'RestrictionLevels' ) as $level ) {
+ foreach ( $this->options->get( 'RestrictionLevels' ) as $level ) {
$right = $level;
if ( $right == 'sysop' ) {
$right = 'editprotected'; // BC
return true;
}
+ /**
+ * Alias of isLoggedIn() with a name that describes its actual functionality. UserIdentity has
+ * only this new name and not the old isLoggedIn() variant.
+ *
+ * @return bool True if user is registered on this wiki, i.e., has a user ID. False if user is
+ * anonymous or has no local account (which can happen when importing). This is equivalent to
+ * getId() != 0 and is provided for code readability.
+ * @since 1.34
+ */
+ public function isRegistered() {
+ return $this->getId() != 0;
+ }
+
/**
* Get whether the user is logged in
* @return bool
*/
public function isLoggedIn() {
- return $this->getId() != 0;
+ return $this->isRegistered();
}
/**
* @return bool
*/
public function isAnon() {
- return !$this->isLoggedIn();
+ return !$this->isRegistered();
}
/**
*/
public function equals( UserIdentity $user );
+ /**
+ * @since 1.34
+ *
+ * @return bool True if user is registered on this wiki, i.e., has a user ID. False if user is
+ * anonymous or has no local account (which can happen when importing). This must be
+ * equivalent to getId() != 0 and is provided for code readability.
+ */
+ public function isRegistered();
}
return $this->getName() === $user->getName();
}
+ /**
+ * @since 1.34
+ *
+ * @return bool True if user is registered on this wiki, i.e., has a user ID. False if user is
+ * anonymous or has no local account (which can happen when importing). This is equivalent to
+ * getId() != 0 and is provided for code readability.
+ */
+ public function isRegistered() {
+ return $this->getId() != 0;
+ }
}
* @file
* @ingroup Watchlist
*/
+
use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentity;
use Wikimedia\Rdbms\DBReadOnlyError;
/**
$this->actualStore = $actualStore;
}
- public function countWatchedItems( User $user ) {
+ public function countWatchedItems( UserIdentity $user ) {
return $this->actualStore->countWatchedItems( $user );
}
);
}
- public function getWatchedItem( User $user, LinkTarget $target ) {
+ public function getWatchedItem( UserIdentity $user, LinkTarget $target ) {
return $this->actualStore->getWatchedItem( $user, $target );
}
- public function loadWatchedItem( User $user, LinkTarget $target ) {
+ public function loadWatchedItem( UserIdentity $user, LinkTarget $target ) {
return $this->actualStore->loadWatchedItem( $user, $target );
}
- public function getWatchedItemsForUser( User $user, array $options = [] ) {
+ public function getWatchedItemsForUser( UserIdentity $user, array $options = [] ) {
return $this->actualStore->getWatchedItemsForUser( $user, $options );
}
- public function isWatched( User $user, LinkTarget $target ) {
+ public function isWatched( UserIdentity $user, LinkTarget $target ) {
return $this->actualStore->isWatched( $user, $target );
}
- public function getNotificationTimestampsBatch( User $user, array $targets ) {
+ public function getNotificationTimestampsBatch( UserIdentity $user, array $targets ) {
return $this->actualStore->getNotificationTimestampsBatch( $user, $targets );
}
- public function countUnreadNotifications( User $user, $unreadLimit = null ) {
+ public function countUnreadNotifications( UserIdentity $user, $unreadLimit = null ) {
return $this->actualStore->countUnreadNotifications( $user, $unreadLimit );
}
throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
}
- public function addWatch( User $user, LinkTarget $target ) {
+ public function addWatch( UserIdentity $user, LinkTarget $target ) {
throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
}
- public function addWatchBatchForUser( User $user, array $targets ) {
+ public function addWatchBatchForUser( UserIdentity $user, array $targets ) {
throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
}
- public function removeWatch( User $user, LinkTarget $target ) {
+ public function removeWatch( UserIdentity $user, LinkTarget $target ) {
throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
}
public function setNotificationTimestampsForUser(
- User $user,
+ UserIdentity $user,
$timestamp,
array $targets = []
) {
throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
}
- public function updateNotificationTimestamp( User $editor, LinkTarget $target, $timestamp ) {
+ public function updateNotificationTimestamp(
+ UserIdentity $editor, LinkTarget $target, $timestamp
+ ) {
throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
}
- public function resetAllNotificationTimestampsForUser( User $user ) {
+ public function resetAllNotificationTimestampsForUser( UserIdentity $user ) {
throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
}
public function resetNotificationTimestamp(
- User $user,
+ UserIdentity $user,
Title $title,
$force = '',
$oldid = 0
throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
}
- public function clearUserWatchedItems( User $user ) {
+ public function clearUserWatchedItems( UserIdentity $user ) {
throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
}
- public function clearUserWatchedItemsUsingJobQueue( User $user ) {
+ public function clearUserWatchedItemsUsingJobQueue( UserIdentity $user ) {
throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
}
- public function removeWatchBatchForUser( User $user, array $titles ) {
+ public function removeWatchBatchForUser( UserIdentity $user, array $titles ) {
throw new DBReadOnlyError( null, self::DB_READONLY_ERROR );
}
- public function getLatestNotificationTimestamp( $timestamp, User $user, LinkTarget $target ) {
+ public function getLatestNotificationTimestamp(
+ $timestamp, UserIdentity $user, LinkTarget $target
+ ) {
return wfTimestampOrNull( TS_MW, $timestamp );
}
}
*/
use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentity;
/**
* Representation of a pair of user and title for watchlist entries.
private $linkTarget;
/**
- * @var User
+ * @var UserIdentity
*/
private $user;
private $notificationTimestamp;
/**
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $linkTarget
* @param null|string $notificationTimestamp the value of the wl_notificationtimestamp field
*/
public function __construct(
- User $user,
+ UserIdentity $user,
LinkTarget $linkTarget,
$notificationTimestamp
) {
}
/**
+ * @deprecated since 1.34, use getUserIdentity()
* @return User
*/
public function getUser() {
+ return User::newFromIdentity( $this->user );
+ }
+
+ /**
+ * @return UserIdentity
+ */
+ public function getUserIdentity() {
return $this->user;
}
<?php
-use Wikimedia\Rdbms\IDatabase;
use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentity;
use Wikimedia\Assert\Assert;
+use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\LoadBalancer;
/**
* 'end' => string (format accepted by wfTimestamp) requires 'dir' option,
* timestamp to end enumerating
* 'watchlistOwner' => User user whose watchlist items should be listed if different
- * than the one specified with $user param,
- * requires 'watchlistOwnerToken' option
+ * than the one specified with $user param, requires
+ * 'watchlistOwnerToken' option
* 'watchlistOwnerToken' => string a watchlist token used to access another user's
* watchlist, used with 'watchlistOwnerToken' option
* 'limit' => int maximum numbers of items to return
/**
* For simple listing of user's watchlist items, see WatchedItemStore::getWatchedItemsForUser
*
- * @param User $user
+ * @param UserIdentity $user
* @param array $options Allowed keys:
* 'sort' => string optional sorting by namespace ID and title
* one of the self::SORT_* constants
* specified using the form option
* @return WatchedItem[]
*/
- public function getWatchedItemsForUser( User $user, array $options = [] ) {
- if ( $user->isAnon() ) {
+ public function getWatchedItemsForUser( UserIdentity $user, array $options = [] ) {
+ if ( !$user->isRegistered() ) {
// TODO: should this just return an empty array or rather complain loud at this point
// as e.g. ApiBase::getWatchlistUser does?
return [];
return $conds;
}
- private function getWatchlistOwnerId( User $user, array $options ) {
+ private function getWatchlistOwnerId( UserIdentity $user, array $options ) {
if ( array_key_exists( 'watchlistOwner', $options ) ) {
/** @var User $watchlistOwner */
$watchlistOwner = $options['watchlistOwner'];
- $ownersToken = $watchlistOwner->getOption( 'watchlisttoken' );
+ $ownersToken =
+ $watchlistOwner->getOption( 'watchlisttoken' );
$token = $options['watchlistOwnerToken'];
if ( $ownersToken == '' || !hash_equals( $ownersToken, $token ) ) {
throw ApiUsageException::newWithMessage( null, 'apierror-bad-watchlist-token', 'bad_wltoken' );
);
}
- private function getWatchedItemsForUserQueryConds( IDatabase $db, User $user, array $options ) {
+ private function getWatchedItemsForUserQueryConds(
+ IDatabase $db, UserIdentity $user, array $options
+ ) {
$conds = [ 'wl_user' => $user->getId() ];
if ( $options['namespaceIds'] ) {
$conds['wl_namespace'] = array_map( 'intval', $options['namespaceIds'] );
<?php
+use MediaWiki\User\UserIdentity;
use Wikimedia\Rdbms\IResultWrapper;
use Wikimedia\Rdbms\IDatabase;
*
* @warning Any joins added *must* join on a unique key of the target table
* unless you really know what you're doing.
- * @param User $user
+ * @param UserIdentity $user
* @param array $options Options from
* WatchedItemQueryService::getWatchedItemsWithRecentChangeInfo()
* @param IDatabase $db Database connection being used for the query
* @param array &$dbOptions Options for Database::select()
* @param array &$joinConds Join conditions for Database::select()
*/
- public function modifyWatchedItemsWithRCInfoQuery( User $user, array $options, IDatabase $db,
- array &$tables, array &$fields, array &$conds, array &$dbOptions, array &$joinConds
+ public function modifyWatchedItemsWithRCInfoQuery( UserIdentity $user, array $options,
+ IDatabase $db, array &$tables, array &$fields, array &$conds, array &$dbOptions,
+ array &$joinConds
);
/**
* Modify the results from WatchedItemQueryService::getWatchedItemsWithRecentChangeInfo()
* before they're returned.
*
- * @param User $user
+ * @param UserIdentity $user
* @param array $options Options from
* WatchedItemQueryService::getWatchedItemsWithRecentChangeInfo()
* @param IDatabase $db Database connection being used for the query
* [ $recentChangeInfo['rc_timestamp'], $recentChangeInfo['rc_id'] ] from the first item
* removed.
*/
- public function modifyWatchedItemsWithRCInfo( User $user, array $options, IDatabase $db,
+ public function modifyWatchedItemsWithRCInfo( UserIdentity $user, array $options, IDatabase $db,
array &$items, $res, &$startFrom
);
<?php
-use Wikimedia\Rdbms\IDatabase;
use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentity;
use Wikimedia\Assert\Assert;
-use Wikimedia\ScopedCallback;
+use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\ILBFactory;
use Wikimedia\Rdbms\LoadBalancer;
+use Wikimedia\ScopedCallback;
/**
* Storage layer class for WatchedItems.
} );
}
- private function getCacheKey( User $user, LinkTarget $target ) {
+ private function getCacheKey( UserIdentity $user, LinkTarget $target ) {
return $this->cache->makeKey(
(string)$target->getNamespace(),
$target->getDBkey(),
}
private function cache( WatchedItem $item ) {
- $user = $item->getUser();
+ $user = $item->getUserIdentity();
$target = $item->getLinkTarget();
$key = $this->getCacheKey( $user, $target );
$this->cache->set( $key, $item );
$this->stats->increment( 'WatchedItemStore.cache' );
}
- private function uncache( User $user, LinkTarget $target ) {
+ private function uncache( UserIdentity $user, LinkTarget $target ) {
$this->cache->delete( $this->getCacheKey( $user, $target ) );
unset( $this->cacheIndex[$target->getNamespace()][$target->getDBkey()][$user->getId()] );
$this->stats->increment( 'WatchedItemStore.uncache' );
}
}
- private function uncacheUser( User $user ) {
+ private function uncacheUser( UserIdentity $user ) {
$this->stats->increment( 'WatchedItemStore.uncacheUser' );
foreach ( $this->cacheIndex as $ns => $dbKeyArray ) {
foreach ( $dbKeyArray as $dbKey => $userArray ) {
}
/**
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
*
* @return WatchedItem|false
*/
- private function getCached( User $user, LinkTarget $target ) {
+ private function getCached( UserIdentity $user, LinkTarget $target ) {
return $this->cache->get( $this->getCacheKey( $user, $target ) );
}
* Return an array of conditions to select or update the appropriate database
* row.
*
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
*
* @return array
*/
- private function dbCond( User $user, LinkTarget $target ) {
+ private function dbCond( UserIdentity $user, LinkTarget $target ) {
return [
'wl_user' => $user->getId(),
'wl_namespace' => $target->getNamespace(),
*
* @since 1.30
*
- * @param User $user
+ * @param UserIdentity $user
*
* @return bool true on success, false when too many items are watched
*/
- public function clearUserWatchedItems( User $user ) {
+ public function clearUserWatchedItems( UserIdentity $user ) {
if ( $this->countWatchedItems( $user ) > $this->updateRowsPerQuery ) {
return false;
}
return true;
}
- private function uncacheAllItemsForUser( User $user ) {
+ private function uncacheAllItemsForUser( UserIdentity $user ) {
$userId = $user->getId();
foreach ( $this->cacheIndex as $ns => $dbKeyIndex ) {
foreach ( $dbKeyIndex as $dbKey => $userIndex ) {
*
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
*/
- public function clearUserWatchedItemsUsingJobQueue( User $user ) {
+ public function clearUserWatchedItemsUsingJobQueue( UserIdentity $user ) {
$job = ClearUserWatchlistJob::newForUser( $user, $this->getMaxId() );
$this->queueGroup->push( $job );
}
/**
* @since 1.31
- * @param User $user
+ * @param UserIdentity $user
* @return int
*/
- public function countWatchedItems( User $user ) {
+ public function countWatchedItems( UserIdentity $user ) {
$dbr = $this->getConnectionRef( DB_REPLICA );
$return = (int)$dbr->selectField(
'watchlist',
}
/**
- * @param User $user
+ * @param UserIdentity $user
* @param TitleValue[] $titles
* @return bool
* @throws MWException
*/
- public function removeWatchBatchForUser( User $user, array $titles ) {
+ public function removeWatchBatchForUser( UserIdentity $user, array $titles ) {
if ( $this->readOnlyMode->isReadOnly() ) {
return false;
}
- if ( $user->isAnon() ) {
+ if ( !$user->isRegistered() ) {
return false;
}
if ( !$titles ) {
/**
* @since 1.27
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
* @return bool
*/
- public function getWatchedItem( User $user, LinkTarget $target ) {
- if ( $user->isAnon() ) {
+ public function getWatchedItem( UserIdentity $user, LinkTarget $target ) {
+ if ( !$user->isRegistered() ) {
return false;
}
/**
* @since 1.27
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
* @return WatchedItem|bool
*/
- public function loadWatchedItem( User $user, LinkTarget $target ) {
- // Only loggedin user can have a watchlist
- if ( $user->isAnon() ) {
+ public function loadWatchedItem( UserIdentity $user, LinkTarget $target ) {
+ // Only registered user can have a watchlist
+ if ( !$user->isRegistered() ) {
return false;
}
/**
* @since 1.27
- * @param User $user
+ * @param UserIdentity $user
* @param array $options
* @return WatchedItem[]
*/
- public function getWatchedItemsForUser( User $user, array $options = [] ) {
+ public function getWatchedItemsForUser( UserIdentity $user, array $options = [] ) {
$options += [ 'forWrite' => false ];
$dbOptions = [];
/**
* @since 1.27
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
* @return bool
*/
- public function isWatched( User $user, LinkTarget $target ) {
+ public function isWatched( UserIdentity $user, LinkTarget $target ) {
return (bool)$this->getWatchedItem( $user, $target );
}
/**
* @since 1.27
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget[] $targets
* @return array
*/
- public function getNotificationTimestampsBatch( User $user, array $targets ) {
+ public function getNotificationTimestampsBatch( UserIdentity $user, array $targets ) {
$timestamps = [];
foreach ( $targets as $target ) {
$timestamps[$target->getNamespace()][$target->getDBkey()] = false;
}
- if ( $user->isAnon() ) {
+ if ( !$user->isRegistered() ) {
return $timestamps;
}
/**
* @since 1.27
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
* @throws MWException
*/
- public function addWatch( User $user, LinkTarget $target ) {
+ public function addWatch( UserIdentity $user, LinkTarget $target ) {
$this->addWatchBatchForUser( $user, [ $target ] );
}
/**
* @since 1.27
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget[] $targets
* @return bool
* @throws MWException
*/
- public function addWatchBatchForUser( User $user, array $targets ) {
+ public function addWatchBatchForUser( UserIdentity $user, array $targets ) {
if ( $this->readOnlyMode->isReadOnly() ) {
return false;
}
- // Only logged-in user can have a watchlist
- if ( $user->isAnon() ) {
+ // Only registered user can have a watchlist
+ if ( !$user->isRegistered() ) {
return false;
}
/**
* @since 1.27
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
* @return bool
* @throws MWException
*/
- public function removeWatch( User $user, LinkTarget $target ) {
+ public function removeWatch( UserIdentity $user, LinkTarget $target ) {
return $this->removeWatchBatchForUser( $user, [ $target ] );
}
* only the specified titles will be updated, and this will be done immediately (not deferred).
*
* @since 1.27
- * @param User $user
+ * @param UserIdentity $user
* @param string|int $timestamp Value to set the "last viewed" timestamp to (null to clear)
* @param LinkTarget[] $targets Titles to set the timestamp for; [] means the entire watchlist
* @return bool
*/
- public function setNotificationTimestampsForUser( User $user, $timestamp, array $targets = [] ) {
- // Only loggedin user can have a watchlist
- if ( $user->isAnon() || $this->readOnlyMode->isReadOnly() ) {
+ public function setNotificationTimestampsForUser(
+ UserIdentity $user, $timestamp, array $targets = []
+ ) {
+ // Only registered user can have a watchlist
+ if ( !$user->isRegistered() || $this->readOnlyMode->isReadOnly() ) {
return false;
}
return true;
}
- public function getLatestNotificationTimestamp( $timestamp, User $user, LinkTarget $target ) {
+ public function getLatestNotificationTimestamp(
+ $timestamp, UserIdentity $user, LinkTarget $target
+ ) {
$timestamp = wfTimestampOrNull( TS_MW, $timestamp );
if ( $timestamp === null ) {
return null; // no notification
/**
* Schedule a DeferredUpdate that sets all of the "last viewed" timestamps for a given user
* to the same value.
- * @param User $user
+ * @param UserIdentity $user
* @param string|int|null $timestamp Value to set all timestamps to, null to clear them
*/
- public function resetAllNotificationTimestampsForUser( User $user, $timestamp = null ) {
- // Only loggedin user can have a watchlist
- if ( $user->isAnon() ) {
+ public function resetAllNotificationTimestampsForUser( UserIdentity $user, $timestamp = null ) {
+ // Only registered user can have a watchlist
+ if ( !$user->isRegistered() ) {
return;
}
/**
* @since 1.27
- * @param User $editor
+ * @param UserIdentity $editor
* @param LinkTarget $target
* @param string|int $timestamp
* @return int[]
*/
- public function updateNotificationTimestamp( User $editor, LinkTarget $target, $timestamp ) {
+ public function updateNotificationTimestamp(
+ UserIdentity $editor, LinkTarget $target, $timestamp
+ ) {
$dbw = $this->getConnectionRef( DB_MASTER );
$uids = $dbw->selectFieldValues(
'watchlist',
/**
* @since 1.27
- * @param User $user
+ * @param UserIdentity $user
* @param Title $title
* @param string $force
* @param int $oldid
* @return bool
*/
- public function resetNotificationTimestamp( User $user, Title $title, $force = '', $oldid = 0 ) {
+ public function resetNotificationTimestamp(
+ UserIdentity $user, Title $title, $force = '', $oldid = 0
+ ) {
$time = time();
- // Only loggedin user can have a watchlist
- if ( $this->readOnlyMode->isReadOnly() || $user->isAnon() ) {
+ // Only registered user can have a watchlist
+ if ( $this->readOnlyMode->isReadOnly() || !$user->isRegistered() ) {
return false;
}
- if ( !Hooks::run( 'BeforeResetNotificationTimestamp', [ &$user, &$title, $force, &$oldid ] ) ) {
+ // Hook expects User, not UserIdentity
+ $userObj = User::newFromId( $user->getId() );
+ if ( !Hooks::run( 'BeforeResetNotificationTimestamp',
+ [ &$userObj, &$title, $force, &$oldid ] )
+ ) {
return false;
}
+ if ( !$userObj->equals( $user ) ) {
+ $user = $userObj;
+ }
$item = null;
if ( $force != 'force' ) {
}
/**
- * @param User $user
+ * @param UserIdentity $user
* @return MapCacheLRU|null The map contains prefixed title keys and TS_MW values
*/
- private function getPageSeenTimestamps( User $user ) {
+ private function getPageSeenTimestamps( UserIdentity $user ) {
$key = $this->getPageSeenTimestampsKey( $user );
return $this->latestUpdateCache->getWithSetCallback(
}
/**
- * @param User $user
+ * @param UserIdentity $user
* @return string
*/
- private function getPageSeenTimestampsKey( User $user ) {
+ private function getPageSeenTimestampsKey( UserIdentity $user ) {
return $this->stash->makeGlobalKey(
'watchlist-recent-updates',
$this->lbFactory->getLocalDomainID(),
return "{$target->getNamespace()}:{$target->getDBkey()}";
}
- private function getNotificationTimestamp( User $user, Title $title, $item, $force, $oldid ) {
+ private function getNotificationTimestamp(
+ UserIdentity $user, Title $title, $item, $force, $oldid
+ ) {
if ( !$oldid ) {
// No oldid given, assuming latest revision; clear the timestamp.
return null;
/**
* @since 1.27
- * @param User $user
+ * @param UserIdentity $user
* @param int|null $unreadLimit
* @return int|bool
*/
- public function countUnreadNotifications( User $user, $unreadLimit = null ) {
+ public function countUnreadNotifications( UserIdentity $user, $unreadLimit = null ) {
$dbr = $this->getConnectionRef( DB_REPLICA );
$queryOptions = [];
}
/**
- * @param User $user
+ * @param UserIdentity $user
* @param Title[] $titles
*/
- private function uncacheTitlesForUser( User $user, array $titles ) {
+ private function uncacheTitlesForUser( UserIdentity $user, array $titles ) {
foreach ( $titles as $title ) {
$this->uncache( $user, $title );
}
* @file
* @ingroup Watchlist
*/
+
use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentity;
use Wikimedia\Rdbms\DBUnexpectedError;
/**
*
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
*
* @return int
*/
- public function countWatchedItems( User $user );
+ public function countWatchedItems( UserIdentity $user );
/**
* @since 1.31
*
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
*
* @return WatchedItem|false
*/
- public function getWatchedItem( User $user, LinkTarget $target );
+ public function getWatchedItem( UserIdentity $user, LinkTarget $target );
/**
* Loads an item from the db
*
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
*
* @return WatchedItem|false
*/
- public function loadWatchedItem( User $user, LinkTarget $target );
+ public function loadWatchedItem( UserIdentity $user, LinkTarget $target );
/**
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
* @param array $options Allowed keys:
* 'forWrite' => bool defaults to false
* 'sort' => string optional sorting by namespace ID and title
*
* @return WatchedItem[]
*/
- public function getWatchedItemsForUser( User $user, array $options = [] );
+ public function getWatchedItemsForUser( UserIdentity $user, array $options = [] );
/**
* Must be called separately for Subject & Talk namespaces
*
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
*
* @return bool
*/
- public function isWatched( User $user, LinkTarget $target );
+ public function isWatched( UserIdentity $user, LinkTarget $target );
/**
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget[] $targets
*
* @return array multi-dimensional like $return[$namespaceId][$titleString] = $timestamp,
* - string|null value of wl_notificationtimestamp,
* - false if $target is not watched by $user.
*/
- public function getNotificationTimestampsBatch( User $user, array $targets );
+ public function getNotificationTimestampsBatch( UserIdentity $user, array $targets );
/**
* Must be called separately for Subject & Talk namespaces
*
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
*/
- public function addWatch( User $user, LinkTarget $target );
+ public function addWatch( UserIdentity $user, LinkTarget $target );
/**
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget[] $targets
*
* @return bool success
*/
- public function addWatchBatchForUser( User $user, array $targets );
+ public function addWatchBatchForUser( UserIdentity $user, array $targets );
/**
- * Removes an entry for the User watching the LinkTarget
+ * Removes an entry for the UserIdentity watching the LinkTarget
* Must be called separately for Subject & Talk namespaces
*
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
*
* @return bool success
* @throws DBUnexpectedError
* @throws MWException
*/
- public function removeWatch( User $user, LinkTarget $target );
+ public function removeWatch( UserIdentity $user, LinkTarget $target );
/**
* @since 1.31
*
- * @param User $user The user to set the timestamps for
+ * @param UserIdentity $user The user to set the timestamps for
* @param string|null $timestamp Set the update timestamp to this value
* @param LinkTarget[] $targets List of targets to update. Default to all targets
*
* @return bool success
*/
public function setNotificationTimestampsForUser(
- User $user,
+ UserIdentity $user,
$timestamp,
array $targets = []
);
*
* @since 1.31
*
- * @param User $user The user to reset the timestamps for
+ * @param UserIdentity $user The user to reset the timestamps for
*/
- public function resetAllNotificationTimestampsForUser( User $user );
+ public function resetAllNotificationTimestampsForUser( UserIdentity $user );
/**
* @since 1.31
*
- * @param User $editor The editor that triggered the update. Their notification
+ * @param UserIdentity $editor The editor that triggered the update. Their notification
* timestamp will not be updated(they have already seen it)
* @param LinkTarget $target The target to update timestamps for
* @param string $timestamp Set the update timestamp to this value
*
* @return int[] Array of user IDs the timestamp has been updated for
*/
- public function updateNotificationTimestamp( User $editor, LinkTarget $target, $timestamp );
+ public function updateNotificationTimestamp(
+ UserIdentity $editor, LinkTarget $target, $timestamp );
/**
* Reset the notification timestamp of this entry
*
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
* @param Title $title
* @param string $force Whether to force the write query to be executed even if the
* page is not watched or the notification timestamp is already NULL.
*
* @return bool success Whether a job was enqueued
*/
- public function resetNotificationTimestamp( User $user, Title $title, $force = '', $oldid = 0 );
+ public function resetNotificationTimestamp(
+ UserIdentity $user, Title $title, $force = '', $oldid = 0 );
/**
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
* @param int|null $unreadLimit
*
* @return int|bool The number of unread notifications
* true if greater than or equal to $unreadLimit
*/
- public function countUnreadNotifications( User $user, $unreadLimit = null );
+ public function countUnreadNotifications( UserIdentity $user, $unreadLimit = null );
/**
* Check if the given title already is watched by the user, and if so
*
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
*/
- public function clearUserWatchedItems( User $user );
+ public function clearUserWatchedItems( UserIdentity $user );
/**
* Queues a job that will clear the users watchlist using the Job Queue.
*
* @since 1.31
*
- * @param User $user
+ * @param UserIdentity $user
*/
- public function clearUserWatchedItemsUsingJobQueue( User $user );
+ public function clearUserWatchedItemsUsingJobQueue( UserIdentity $user );
/**
* @since 1.32
*
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget[] $targets
*
* @return bool success
*/
- public function removeWatchBatchForUser( User $user, array $targets );
+ public function removeWatchBatchForUser( UserIdentity $user, array $targets );
/**
* Convert $timestamp to TS_MW or return null if the page was visited since then by $user
* Usage of this method should be limited to WatchedItem* classes
*
* @param string|null $timestamp Value of wl_notificationtimestamp from the DB
- * @param User $user
+ * @param UserIdentity $user
* @param LinkTarget $target
* @return string TS_MW timestamp or null
*/
- public function getLatestNotificationTimestamp( $timestamp, User $user, LinkTarget $target );
+ public function getLatestNotificationTimestamp(
+ $timestamp, UserIdentity $user, LinkTarget $target );
}
"error": "Wōh",
"databaseerror": "Cȳþþuhordes wōh",
"databaseerror-textcl": "Gecyþneshordfræge misgedwild belamp",
+ "databaseerror-query": "Æsce: $1",
+ "databaseerror-function": "Wice: $1",
"databaseerror-error": "Wōg: $1",
"laggedslavemode": "'''Warnung:''' Wēnunga næbbe se tramet nīwlīca nīwunga.",
"readonly": "Ġifhord locen",
"botpasswords-editexisting": "تعديل كلمة سر موجودة للبوت",
"botpasswords-label-needsreset": "(تحتاج كلمة المرور إلى إعادة الضبط)",
"botpasswords-label-appid": "اسم البوت:",
- "botpasswords-label-create": "Ø£Ù\86شأ",
+ "botpasswords-label-create": "Ø¥Ù\86شاء",
"botpasswords-label-update": "تحديث",
"botpasswords-label-cancel": "ألغ",
"botpasswords-label-delete": "احذف",
"actions": "Parilaksana",
"namespaces": "Genah pesengan",
"variants": "kawentenan sane lianan",
- "navigation-heading": "menu navigasi",
+ "navigation-heading": "Menu navigasi",
"errorpagetitle": "kaluputan",
"returnto": "mabalik ring $1",
"tagline": "Saka {{SITENAME}}",
"talkpagelinktext": "Wicara",
"specialpage": "Lembar sane kautamayang",
"personaltools": "pekakas pribadi",
- "talk": "rembug\n\nngarembug (kata kerja)",
+ "talk": "Rembug",
"views": "Pekantenan",
"toolbox": "Pekakas",
"viewhelppage": "cingak lembar pamitutlung",
"savearticle": "simpen lembar",
"preview": "tayangan sadurungnyane",
"showpreview": "cingak sane lintang",
- "showdiff": "cingak pagentosan",
+ "showdiff": "Cingak pagentosan",
"anoneditwarning": "<strong>Pingetan:</strong> Ida dané nénten kacatet ngranjing. Alamat IP ida dané jagi kacatet ring sejarah (indik sané dumunan) ring lembar puniki. Yening ida dane <strong>[$1 log in]</strong> utawi <strong>[$2 create an account]</strong>, your edits will be attributed to your username, along with other benefits.",
"newarticle": "(Anyar)",
"newarticletext": "ida dane ngiring pranala nuju lembar sane durung wenten. yening jagi ngaryanang lembar punika, ketik daging lembar ring kotak sane wenten ring beten puniki. (cingak [$1 lembar wantuan] anggen wacana salanturnyane). yening ida dane nenten nyelapang neked ring lembar puniki, klik tombol \"back\" ring \"penjelajah web\" ida dane.",
"action-edit": "benahang lembar puniki",
"nchanges": "$1{{PLURAL:$1|panguwahan|uwah-uwahan}}",
"enhancedrc-history": "babad",
- "recentchanges": "pagentosan sane anyar",
+ "recentchanges": "Pagentosan anyar",
"recentchanges-legend": "pilihan panguwahan sane anyar",
"recentchanges-feed-description": "molihang pagentosan anyar ring wiki ring \"umpan\" puniki",
"recentchanges-label-newpage": "panguwahan puniki ngaryanin lembar anyar",
"recentchanges-label-minor": "niki panguwahan kidik",
"recentchanges-label-bot": "penguwahan puniki kalaksanayang antuk bot",
"recentchanges-label-unpatrolled": "panguwahan puniki durung kapatroli",
- "rcnotefrom": "Ring beten puniki inggih punika {{PLURAL:$5|panguwahan|panguwahan}} saking <strong>$3, $4</strong> (kaedengang ngantos <strong>$1</strong> panguwahan).",
+ "rcnotefrom": "Ring beten puniki inggih punika {{PLURAL:$5|panguwahan}} saking <strong>$3, $4</strong> (kaedengang ngantos <strong>$1</strong> panguwahan).",
"rclistfrom": "edengang penguwahan sane anyar wit saking $3 $2",
"rcshowhideminor": "$1 uwahan kidik",
"rcshowhideminor-show": "Edengang",
"namespace": "Genah pesengan",
"invert": "uliang pilihan",
"tooltip-invert": "Centang kotak puniki mangdané ngengkebang lembar sané kauwah ring genah wastan sané kapilih (miwah genah wastan sané mapaiketan yéning kacentang)",
- "blanknamespace": "utama",
+ "blanknamespace": "(Utama)",
"contributions": "kawigunan {{GENDER:$1|penganggo}}",
"contributions-title": "Kontribusi pangangge anggen $1",
"mycontris": "kawigunan",
"tooltip-n-randompage": "edengang polah-palih lembar",
"tooltip-n-help": "genah anggen ngarereh",
"tooltip-t-whatlinkshere": "kepahan sami lembar wiki sane maduwe pranala nuju lembar puniki",
- "tooltip-t-recentchangeslinked": "pagentosan sane anyar lembar-lembar sane maduwe pranala nuju lembar puniki",
+ "tooltip-t-recentchangeslinked": "Pagentosan anyar lembar sane maduwe pranala nuju lembar puniki",
"tooltip-feed-atom": "\"atom feed\" anggen lembar puniki",
- "tooltip-t-contributions": "Daptar kepahan kawigunan {{GENDER:$1|penganggo niki}",
+ "tooltip-t-contributions": "Daptar kepahan kawigunan {{GENDER:$1|penganggo niki}}",
"tooltip-t-emailuser": "Ngirim surel majeng ring {{GENDER:$1|penganggo puniki}}",
"tooltip-t-upload": "ngunggahang file",
- "tooltip-t-specialpages": "kepahan sami lembar istimewa",
+ "tooltip-t-specialpages": "Kepahan sami lembar istimewa",
"tooltip-t-print": "kawentenan lian sane macetak ring lembar puniki",
"tooltip-t-permalink": "Pranala ajeg kaanggen ngubah lembar puniki",
"tooltip-ca-nstab-main": "cingak dagingnyane lembar puniki",
"tooltip-ca-nstab-help": "cingak lembar pamitutlung",
"tooltip-ca-nstab-category": "cingak lembar kategori",
"tooltip-minoredit": "pingetin puniki dados panguwahan kidik",
- "tooltip-save": "simpen pagentosan ida dane",
- "tooltip-preview": "pagentosan sane dumun duwen ida dane, mangda anggen niki sadurung jagi nyimpen!",
- "tooltip-diff": "cingak pagentosan sane sampun ida dane laksanayang",
+ "tooltip-save": "Nyimpen pagentosan ida dane",
+ "tooltip-preview": "Pagentosan sane dumun duwen ida dane, mangda anggen niki sadurung jagi nyimpen!",
+ "tooltip-diff": "Cingak pagentosan sane sampun ida dane laksanayang",
"tooltip-compareselectedversions": "cingak binane makekalih kepahan lembar sane kasudi",
"tooltip-watch": "imbuhin lembar niki ring daftar paninjoan ida dane",
"tooltip-rollback": "\"nguliang\" muwungan jagi ngabecikang ring lembar puniki nuju haturan sane untat ngangge apisan klik",
"action-hideuser": "блякаваньне імя ўдзельніка і яго хаваньне",
"action-ipblock-exempt": "абыход блякаваньняў IP-адрасоў, аўтаблякаваньняў і блякаваньняў дыяпазонаў",
"action-unblockself": "разблякаваньне самога сябе",
+ "action-noratelimit": "адсутнасьць абмежаваньня хуткасьці",
+ "action-reupload-own": "перазапіс уласных існых файлаў",
"nchanges": "$1 {{PLURAL:$1|зьмена|зьмены|зьменаў}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|з апошняга візыту}}",
"enhancedrc-history": "гісторыя",
"linksearch-pat": "Узор для пошуку:",
"linksearch-ns": "Прастора назваў:",
"linksearch-ok": "Шукаць",
- "linksearch-text": "Ð\9cожна Ñ\9eжÑ\8bваÑ\86Ñ\8c Ñ\81Ñ\8bмбалÑ\96 падÑ\81Ñ\82аноÑ\9eкÑ\96, напÑ\80Ñ\8bклад, «*.wikipedia.org».\nÐ\9dеабÑ\85однÑ\8b дамÑ\8dн пеÑ\80Ñ\88ага Ñ\9eзÑ\80оÑ\9eнÑ\8e, напÑ\80Ñ\8bклад, «*.org».<br />\n{{PLURAL:$2|1=Ð\9fÑ\80аÑ\82акол, Ñ\8fкÑ\96 падÑ\82Ñ\80Ñ\8bмлÑ\96ваеÑ\86Ñ\86а|Ð\9fÑ\80аÑ\82аколÑ\8b, Ñ\8fкÑ\96Ñ\8f падÑ\82Ñ\80Ñ\8bмлÑ\96ваÑ\8eÑ\86Ñ\86а}}: $1 (дапомна http://, калі пратакол не пазначаны).",
+ "linksearch-text": "Ð\9cожна Ñ\9eжÑ\8bваÑ\86Ñ\8c Ñ\81Ñ\8bмбалÑ\96 падÑ\81Ñ\82аноÑ\9eкÑ\96, напÑ\80Ñ\8bклад, «*.wikipedia.org».\nÐ\9dеабÑ\85однÑ\8b дамÑ\8dн пеÑ\80Ñ\88ага Ñ\9eзÑ\80оÑ\9eнÑ\8e, напÑ\80Ñ\8bклад, «*.org».<br />\n{{PLURAL:$2|1=Ð\9fÑ\80аÑ\82акол, Ñ\8fкÑ\96 падÑ\82Ñ\80Ñ\8bмлÑ\96ваеÑ\86Ñ\86а|Ð\9fÑ\80аÑ\82аколÑ\8b, Ñ\8fкÑ\96Ñ\8f падÑ\82Ñ\80Ñ\8bмлÑ\96ваÑ\8eÑ\86Ñ\86а}}: $1 (па змоÑ\9eÑ\87анÑ\8cнÑ\96 http://, калі пратакол не пазначаны).",
"linksearch-line": "Спасылка на $1 з $2",
"linksearch-error": "Сымбалі падстаноўкі могуць ужывацца толькі ў пачатку адрасоў.",
"listusersfrom": "Паказаць удзельнікаў ад:",
"Joao Xavier",
"Surfo",
"YvesNevelsteen",
- "Vlad5250"
+ "Vlad5250",
+ "Mirin"
]
},
"tog-underline": "Substrekado de ligiloj:",
"histfirst": "plej malnova",
"histlast": "plej nova",
"historysize": "({{PLURAL:$1|1 bajto|$1 bajtoj}})",
- "historyempty": "(malplena)",
+ "historyempty": "malplena",
"history-feed-title": "Historio de redaktoj",
"history-feed-description": "Revizia historio por ĉi tiu paĝo en la vikio",
"history-feed-item-nocomment": "$1 ĉe $2",
"rcfilters-watchlist-markseen-button": "Marku ĉiujn ŝanĝojn viditaj",
"rcfilters-watchlist-edit-watchlist-button": "Redakti vian atentaron",
"rcfilters-watchlist-showupdated": "Ŝanĝoj en paĝoj, kiujn vi ne vizitis post la ŝanĝo, aperas <strong>grase</strong>, kun plenigitaj buletoj.",
+ "rcfilters-watchlist-preference-label": "Uzi fasadon ne uzantan JavaScript",
"rcfilters-target-page-placeholder": "Enigu nomon de paĝo (aŭ kategorio)",
"rcnotefrom": "Malsupre estas la {{PLURAL:$5|ŝanĝo|ŝanĝoj}} ekde <strong>$3, $4</strong> (montrante ĝis <strong>$1</strong>).",
"rclistfrom": "Montri novajn ŝanĝojn ekde \"$3 $2\"",
"uploadstash-thumbnail": "Vidi bildeton",
"uploadstash-exception": "Ne eblas alŝuti en kaŝkonservejon ($1): \"$2\".",
"uploadstash-bad-path-unrecognized-thumb-name": "Nerekonita miniatura nomo.",
+ "uploadstash-zero-length": "Longo de dosiero estas nul.",
"invalid-chunk-offset": "Malvalida deŝovo de dosierpeco",
"img-auth-accessdenied": "Atingo malpermisita",
"img-auth-nopathinfo": "Mankas informo pri vojo.\nVia servilo estu agordita por sendi la variablojn REQUEST_URI kaj/aŭ PATH_INFO.\nSe ĝi jam estas, provu aktivigon de $wgUsePathInfo.\nVidu https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
"speciallogtitlelabel": "Celo (titolo aŭ {{ns:user}}:salutnomo por uzanto):",
"log": "Protokoloj",
"logeventslist-submit": "Montri",
+ "logeventslist-tag-log": "Protokolo de etikedoj",
"all-logs-page": "Ĉiuj publikaj protokoloj",
"alllogstext": "Suma kompilaĵo de ĉiuj protokoloj de {{SITENAME}}.\nVi povas plistrikti la mendon per selektado de protokola speco, la salutnomo (inkluzivante uskladon) aŭ la efika paĝo (ankaŭ inkluzivas uskladon).",
"logempty": "Neniaj artikoloj en la protokolo.",
"deleteprotected": "Vi ne povas forigi ĉi tiun paĝon ĉar ĝi estis protektita.",
"deleting-backlinks-warning": "<strong>Atentigo:</strong>\n[[Special:WhatLinksHere/{{FULLPAGENAME}}|Aliaj paĝoj]] ligas al aŭ transkludas tiun ĉi forigotan paĝon.",
"rollback": "Restarigi antaŭan redakton",
+ "rollback-confirmation-confirm": "Bonvolu konfirmi:",
+ "rollback-confirmation-yes": "Amasmalfari",
+ "rollback-confirmation-no": "Nuligi",
"rollbacklink": "malfari",
"rollbacklinkcount": "nuligi $1 {{PLURAL:$1|redakton|redaktojn}}",
"rollbacklinkcount-morethan": "nuligi pli ol $1 {{PLURAL:$1|redakton|redaktojn}}",
"ipb-sitewide": "Tutreteja",
"ipb-partial": "Parta",
"ipb-pages-label": "Paĝoj",
+ "ipb-namespaces-label": "Nomspacoj",
"badipaddress": "Neniu uzanto, aŭ la IP-adreso estas misformita.",
"blockipsuccesssub": "Forbaro sukcesis.",
"blockipsuccesstext": "[[Special:Contributions/$1|$1]] estas forbarita. <br />\nVidu la [[Special:BlockList|liston de forbaroj]] por kontroli.",
"ipb-blocklist-contribs": "Kontribuoj de {{GENDER:$1|$1}}",
"ipb-blocklist-duration-left": "$1 restas",
"block-expiry": "Blokdaŭro",
+ "block-prevent-edit": "Redaktado",
+ "block-reason": "Kialo:",
"unblockip": "Malforbari IP-adreson/nomon",
"unblockiptext": "Per la jena formulo vi povas repovigi al iu\nforbarita IP-adreso/nomo la povon enskribi en la vikio.",
"ipusubmit": "Forigi ĉi tiun forbaron",
"blocklist-userblocks": "Kaŝi konto-forbarojn",
"blocklist-tempblocks": "Kaŝi provizorajn forbarojn",
"blocklist-addressblocks": "Kaŝi unuopajn IP-adresajn forbarojn",
+ "blocklist-type": "Tipo:",
"blocklist-rangeblocks": "Kaŝi blokojn de intervalo",
"blocklist-timestamp": "Tempindiko",
"blocklist-target": "Celo",
"pageinfo-display-title": "Montrita titolo",
"pageinfo-default-sort": "Pravaloro de ordiga ŝlosilo",
"pageinfo-length": "Paĝgrandeco (en bajtoj)",
+ "pageinfo-namespace": "Nomspaco",
"pageinfo-article-id": "Paĝa identigo",
"pageinfo-language": "Lingvo de paĝa enhavo",
"pageinfo-language-change": "ŝanĝi",
"confirm-unwatch-top": "Ĉu forigi tiun ĉi paĝon el via atentaro?",
"confirm-rollback-button": "Bone",
"confirm-rollback-top": "Malfaru redaktojn al ĉi tiu paĝo?",
+ "confirm-mcrundo-title": "Malfari ŝanĝon",
+ "mcrundofailed": "Malfaro malsukcesis",
"quotation-marks": "„$1“",
"imgmultipageprev": "← antaŭa paĝo",
"imgmultipagenext": "sekva paĝo →",
"tag-list-wrapper": "[[Special:Tags|{{PLURAL:$1|Etikedo|Etikedoj}}]]: $2",
"tag-mw-contentmodelchange": "ŝanĝo de enhavomodelo",
"tag-mw-contentmodelchange-description": "Redaktoj kiuj [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:ChangeContentModel ŝanĝas la enhavmodelon] de paĝo",
+ "tag-mw-undo": "Malfari",
"tags-title": "Etikedoj",
"tags-intro": "Ĉi tiu paĝo montras la etikedojn kun kiuj la programaro markus redakton, kaj iliaj signifoj.",
"tags-tag": "Etikeda nomo",
"compare-title-not-exists": "La titolo kiun vi specifis ne ekzistas.",
"compare-revision-not-exists": "La revizio kiun vi specifis ne ekzistas.",
"diff-form": "Malsamoj",
+ "diff-form-submit": "Montri diferencojn",
"permanentlink": "Konstanta ligilo",
+ "permanentlink-revid": "Identigilo de revizio",
+ "permanentlink-submit": "Iri al revizio",
"dberr-problems": "Bedaŭrinde, ĉi tiu retejo suferas pro teknikaj problemoj.",
"dberr-again": "Bonvolu atendi kelkajn minutojn kaj reŝargi.",
"dberr-info": "(Ne eblas konekti la datumbazon: $1)",
"special-characters-group-thai": "Taja",
"special-characters-group-lao": "laŭa",
"special-characters-group-khmer": "kmera",
+ "special-characters-group-canadianaboriginal": "Kanada Indiĝena",
"special-characters-title-endash": "mallonga streketo",
"special-characters-title-emdash": "longa streketo",
"special-characters-title-minus": "minus-signo",
"mw-widgets-categoryselector-add-category-placeholder": "Aldoni kategorion",
"mw-widgets-usersmultiselect-placeholder": "Aldoni pliajn...",
"mw-widgets-titlesmultiselect-placeholder": "Aldoni pliajn...",
+ "date-range-from": "De dato:",
+ "date-range-to": "Ĝis dato:",
"sessionmanager-tie": "Kombini diversajn tipojn de ensaluta peto ne estas permisita: $1.",
"sessionprovider-generic": "$1 seancoj",
"sessionprovider-mediawiki-session-cookiesessionprovider": "kuketaj seancoj",
"log-action-filter-suppress-reblock": "Forigi uzanton per reforbari",
"log-action-filter-upload-upload": "Novalŝuta",
"log-action-filter-upload-overwrite": "Realŝuta",
+ "log-action-filter-upload-revert": "Restarigi",
"authmanager-authn-not-in-progress": "Aŭtentigado ne estas progresanta aŭ la seancaj datumoj perdiĝis. Bonvolu provi denove ekde la komenco.",
"authmanager-authn-no-primary": "La provizita legitimaĵo ne povus esti aŭtentikigita.",
"authmanager-authn-no-local-user": "La provizitaj legitimaĵoj ne estas asociitaj kun ajna uzanto de ĉi tiu vikio.",
"revid": "revizio $1",
"pageid": "Identigilo de paĝo $1",
"pagedata-title": "Paĝaj datumoj",
+ "pagedata-bad-title": "Nevalida titolo: \"$1\".",
+ "passwordpolicies": "Reguloj pri pasvortoj",
"passwordpolicies-group": "Grupo",
"passwordpolicies-policies": "Politiko",
"passwordpolicies-policy-minimalpasswordlength": "Pasvortoj devas esti longaj almenaŭ $1 {{PLURAL:$1|1 signon|$1 signojn}}.",
"createacct-reason": "Razlog",
"createacct-reason-ph": "Zašto stvarate još jedan račun?",
"createacct-reason-help": "Poruka koja se prikazuje u evidenciji stvaranja suradničkih računa",
- "createacct-submit": "Stvorite svoj suradnički račun",
+ "createacct-submit": "Stvori svoj suradnički račun",
"createacct-another-submit": "Otvori račun",
"createacct-continue-submit": "Pritisni za stvaranje računa",
"createacct-another-continue-submit": "Nastavi za stvaranje računa",
"sp-contributions-newonly": "Pokaži samo stranice koje je suradnik započeo",
"sp-contributions-hideminor": "Sakrij manje izmjene",
"sp-contributions-submit": "Traži",
+ "sp-contributions-outofrange": "Nije moguće pokazati rezultate. Traženi raspon IP adresa veći je od CIDR limita /$1.",
"whatlinkshere": "Što vodi ovamo",
"whatlinkshere-title": "Stranice koje vode na »$1«",
"whatlinkshere-page": "Stranica:",
"action-editmyusercss": "saját szerkesztői CSS-fájlok szerkesztése",
"action-editmyuserjson": "saját szerkesztői JSON-fájlok szerkesztése",
"action-editmyuserjs": "saját szerkesztői JavaScript-fájlok szerkesztése",
+ "action-viewsuppressed": "minden felhasználó elől elrejtett változtatások megtekintése",
+ "action-hideuser": "felhasználói név blokkolása és elrejtése a külvilág elől",
"action-ipblock-exempt": "IP-, auto- és tartományblokkok megkerülése",
"action-unblockself": "saját felhasználói fiók blokkjának feloldása",
"action-noratelimit": "sebességkorlát figyelmen kívül hagyása",
"action-reupload-own": "a saját maga által feltöltött fájlok felülírása",
+ "action-nominornewtalk": "vitalapok apró szerkesztése új üzenetről való értesítés kiküldése nélkül",
"action-markbotedits": "visszaállított szerkesztések botként való jelölése",
"action-patrolmarks": "járőrök jelzéseinek megtekintése a friss változásokban",
"action-override-export-depth": "lapok exportálása a hivatkozott lapokkal együtt, legfeljebb 5-ös mélységig",
+ "action-suppressredirect": "átirányítások készítésének kihagyása a lapok régi nevén átnevezéskor",
"nchanges": "$1 változtatás",
"enhancedrc-since-last-visit": "$1 az utolsó látogatás óta",
"enhancedrc-history": "történet",
"passwordpolicies-policyflag-forcechange": "lecserélés követelése bejelentkezéskor",
"passwordpolicies-policyflag-suggestchangeonlogin": "lecserélés ajánlása bejelentkezéskor",
"unprotected-js": "Biztonsági okokból JavaScript nem tölthető be védtelen lapokról. Kérlek egyedül a MediaWiki névtérben készíts JavaScriptet, vagy szerkesztői allapként.",
- "userlogout-continue": "Amennyiben ki szeretnél jelentkezni, [$1 használd a kijelentkezési oldalt]."
+ "userlogout-continue": "Amennyiben ki szeretnél jelentkezni, [$1 használd a kijelentkezési oldalt].",
+ "userlogout-sessionerror": "Sikertelen kijelentkezés munkamenethiba miatt. Kérlek [$1 próbáld újra]."
}
"badaccess-group0": "Արտունութիւն չունիք այս գործողութիւնը կատարել:",
"badaccess-groups": "Տուեալ գործողութիւնը միայն $1 {{PLURAL:$2|խումբի|խումբերի}} մասնակիցները կ՛րնան կատարել։",
"ok": "Լաւ",
- "pagetitle": "Միացէ՛ք {{SITENAME}} նախագիծին",
+ "pagetitle": "",
"retrievedfrom": "Վերցուած է «$1» էջէն",
"youhavenewmessages": "{{PLURAL:$3|Դուք ունիք}} $1 ($2)։",
"youhavenewmessagesfromusers": "{{PLURAL:$4|Դուք ունիք}} $1 {{PLURAL:$3|այլ մասնակից|$3 մասնակիցէն}} ($2):",
"download": "undhuh",
"unwatchedpages": "Kaca kang ora ingawasan",
"listredirects": "Pratélan alihan",
- "unusedtemplates": "Cithakan kang ora kanggo",
+ "unusedtemplates": "Cithakan kang ora kaanggo",
"unusedtemplatestext": "Kaca iki isi kabèh kaca ing mandala aran {{ns:template}} kang ora kaanggo ing kaca liya.\nAja lali mesthèkaké ana-orané pranala liya kang ngener cithakané sadurungé panjenengan mbusek.",
"unusedtemplateswlh": "pranala liya-liyané",
"randompage": "Kaca sembarang",
"withoutinterwiki-summary": "Kaca-kaca ing ngisor iki ora nggayut menyang vèrsi basa liyané.",
"withoutinterwiki-legend": "Préfiks",
"withoutinterwiki-submit": "Tuduhna",
- "fewestrevisions": "Artikel kang owahé sithik dhéwé",
+ "fewestrevisions": "Artikel kang owahé sathithik dhéwé",
"nbytes": "$1 {{PLURAL:$1|bét|bét}}",
"ncategories": "$1 {{PLURAL:$1|kategori|kategori}}",
"ninterwikis": "$1 {{PLURAL:$1|interwiki|interwiki}}",
"uncategorizedcategories": "Kategori kang tanpa kategori",
"uncategorizedimages": "Barkas kang tanpa kategori",
"uncategorizedtemplates": "Cithakan kang durung kawènèhan kategori",
- "unusedcategories": "Kategori kang ora kanggo",
- "unusedimages": "Barkas kang ora kanggo",
+ "unusedcategories": "Kategori kang ora kaanggo",
+ "unusedimages": "Barkas kang ora kaanggo",
"wantedcategories": "Kategori kang kapéngini",
"wantedpages": "Kaca kang kapéngini",
"wantedpages-badtitle": "Sesirah ora sah ing omboyakan kasil: $1",
"rcfilters-savedqueries-already-saved": "Disse filtrene er allerede lagret. Endre innstillingene dine for å opprette et nytt lagret filter.",
"rcfilters-restore-default-filters": "Gjenopprett standardfiltre",
"rcfilters-clear-all-filters": "Nullstill alle filtre",
- "rcfilters-show-new-changes": "Vis nye endringer siden $1",
+ "rcfilters-show-new-changes": "Vis nye endringer etter $1",
"rcfilters-search-placeholder": "Filtrer endringer (bruk menyen eller søk etter et filternavn)",
"rcfilters-invalid-filter": "Ugyldig filter",
"rcfilters-empty-filter": "Ingen aktive filtre. Alle bidrag vises.",
"revid": "versjon $1",
"interfaceadmin-info": "$1\n\nLøyva for endring av CSS/JS/JSON-filer som gjeld heile nettstaden vart nyleg skilde ut frå <code>editinterface</code>-retten. Om du ikkje skjøner kvifor du får denne feilmeldinga, sjå [[mw:MediaWiki_1.32/interface-admin]].",
"passwordpolicies-policy-passwordcannotmatchusername": "Passordet kan ikkje vera det same som brukarnamnet",
- "passwordpolicies-policy-passwordcannotmatchblacklist": "Passordet kan ikkje passa med svartelista passord"
+ "passwordpolicies-policy-passwordcannotmatchblacklist": "Passordet kan ikkje passa med svartelista passord",
+ "userlogout-sessionerror": "Utlogging gjekk ikkje grunna ein øktfeil. [$1 Freist om att]."
}
"Lancine.kounfantoh.fofana",
"Lanciné.kounfantoh.fofana",
"Youssoufkadialy",
- "Amire80"
+ "Amire80",
+ "Nafadji Mory Diané"
]
},
"sunday": "ߞߊ߯ߙߌߟߏ߲",
"navigation-heading": "ߛߏ߲߯ߓߊߟߌ߫ ߓߏߟߏ߲ߘߊ",
"errorpagetitle": "ߝߎ߬ߕߎ߲߬ߕߌ",
"returnto": "ߌ ߞߐߛߊ߬ߦߌ߲߬ ߦߊ߲߬ ߡߊ߬$1",
- "tagline": "ߞߊ߬ ߝߘߊ߫ {{SITENAMEP}}",
+ "tagline": "ߞߊ߬ ߝߘߊ߫{{SITENAMEP}}",
"help": "ߘߍ߬ߡߍ߲߬ߠߌ",
"help-mediawiki": "ߘߍ߬ߡߍ߲߬ߠߌ߲ ߞߊ߬ ߓߍ߲߬ ߥߞߌ-ߟߊߛߋߢߊߥߙߍ ߡߊ߬",
"search": "ߢߌߣߌ߲ߠߌ",
"nstab-category": "ߦߌߟߡߊ",
"mainpage-nstab": "ߓߏ߬ߟߏ߲߬ߘߊ",
"nosuchspecialpage": "ߘߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲߬ ߛߎ߮ ߏ߬ ߝߋ߲߫ ߕߍ߫ ߦߊ߲߬",
+ "nospecialpagetext": "<strong>ߊߟߎ߫ ߓߘߊ߫ ߞߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲ ߘߏ߫ ߢߌߣߌ߲߫ ߡߍ߲ ߕߺߴߦߋ߲߬.</strong>\nߞߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲߫ ߓߘߍ߬ߡߊ ߟߎ߬ ߛߙߍߘߍ ߦߋ߫ ߢߌ߲߬ ߠߋ߫ ߞߊ߲߬ [[Special:SpecialPages|{{int:specialpages}}]].",
"badtitle": "ߞߎ߲߬ߕߐ߰ ߖߎ߮",
"viewsource": "ߊ߬ ߛߎ߲ ߘߐߜߍ߫",
"viewsource-title": "ߣߌ߲߬ $1 ߛߎ߲ ߘߐߜߍ߫",
"content-model-wikitext": "ߥߞߌ߫ ߞߟߏߜߍ",
"viewpagelogs": "ߞߐߜߍ ߣߌ߲߬ ߜߊ߲߬ߞߎ߲߬ߠߌ߲ ߠߎ߬ ߦߋ߫",
"currentrev-asof": "$1 ߟߊ߫ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߕߊ߬ߡߌ߲߬ߣߍ߲",
- "revisionasof": "ߊ߬ ߡߊߛߊ߬ߦߌ߲ ߦߊ߲߬ ߓߊ߫ $1",
+ "revisionasof": "ߊ߬ ߡߊߛߊ߬ߦߌ߲ ߦߊ߲߬ ߓߊ߫ 1$",
"revision-info": "{{GENDER:$6|$2}} ߟߊ߫ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ $2",
"previousrevision": "→ ߡߊ߬ߛߊ߬ߦߌ߲߬ߠߌ߲ ߞߘߐ߬ߡߊ߲",
"nextrevision": "ߡߊ߬ߛߋ߬ߦߌ߲߬ߣߍ߲߬ ߞߎߘߊ →",
"currentrevisionlink": "ߡߊ߬ߛߊ߬ߦߌ߲߬ߠߌ߲ ߕߊ߬ߡߌ߲߬ߣߍ߲",
"cur": "ߞߍߞߎߘߊ",
"last": "ߢߍߕߊ",
+ "history-fieldset-title": "ߣߐ߬ߡߊ߬ߛߊߦߌ߲ ߠߎ߬ ߛߍ߲ߛߍ߲߫",
"histfirst": "ߞߘߐ߬ߡߊ߲ ߠߎ߬",
"histlast": "ߞߎߘߊ ߟߎ߬",
"history-feed-title": "ߡߊ߬ߛߊ߬ߦߌ߲߬ߠߌ߲ ߘߐ߬ߝߐ",
"history-feed-description": "ߞߐߜߍ ߣߌ߲߬ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߘߐ߬ߝߐ߸ ߥߞߌ ߘߐ߫",
"rev-delundel": "ߊ߬ ߦߋߢߊ ߡߊߦߟߍ߬ߡߊ߲߫",
"history-title": "$1 ߡߛߊ߬ߦߌ߲߬ߠߌ߲ ߘߐ߬ߝߐ",
- "lineno": "ߛߌ߬ߕߊߙߌ $1:",
+ "lineno": "$1 ߛߌ߬ߕߊߙߌ",
+ "compareselectedversions": "ߘߟߊߡߌߘߊ߫ ߛߎߥߊ߲ߘߌߣߍ߲ ߠߎ߬ ߟߊߢߐ߲߯ߡߊ߫",
"editundo": "ߊ߬ ߘߐߛߊ߬߸ ߊ߬ ߓߟߏߞߊ߬߸ ߊ߬ ߓߙߐߕߐ߫",
"diff-empty": "ߝߊߙߊ߲ߝߊ߯ߛߌ߫ ߕߴߊ߬ߟߎ߬ ߕߍ߫",
"searchresults": "ߢߌߣߌ߲ߠߌ߲ ߞߐߝߟߌ ߟߎ߬",
"searchall": "ߊ߬ ߓߍ߯",
"search-nonefound": "ߖߋ߬ߓߟߌ߬ ߛߌ߫ ߕߍ߫ ߢߌ߬ߣߌ߲߬ߞߊ߬ߟߌ ߣߌ߲߫ ߞߊ߲߬.",
"mypreferences": "ߟߊ߬ߝߌ߬ߛߦߊ߬ߟߌ",
+ "group-sysop": "ߡߙߊ߬ߟߌ߬ߟߊ",
"right-writeapi": "ߛߓߍߟߌ API ߟߊߓߊ߯ߙߊ߫",
"newuserlogpage": "ߖߊ߬ߕߋ߬ߘߊ߬ ߓߘߊ߫ ߟߊߞߊ߬ ߌ ߜߊ߲߬ߞߎ߲߬",
"action-edit": "ߞߐߜߍ ߣߌ߲߬ ߡߊߦߟߍ߬ߡߊ߲߬",
"recentchanges": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߫ ߞߎߘߊ",
"recentchanges-legend": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߞߎߘߊ ߟߎ߫ ߟߊ߬ߓߍ߲߬ߢߐ߰ߡߦߊ߬ߘߊ",
"recentchanges-summary": "ߥߞߌ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߞߎ߲ߓߊ ߡߍ߲ ߠߎ߬ ߞߍߣߍ߲߫ ߞߐߜߍ ߣߌ߲߬ ߞߊ߲߬߸ ߏ߬ ߟߎ߫ ߣߐ߬ߣߐ߬.",
+ "recentchanges-noresult": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߬ ߛߌ߫ ߓߍ߲߬ߢߐ߲߰ߦߊ߬ߣߍ߲߬ ߕߍ߫ ߛߎߡߊ߲ߡߕߊ ߢߌ߲߬ ߠߎ߫ ߡߊ߬ ߕߎ߬ߡߊ߬ ߟߊߕߍ߰ߣߍ߲ ߦߌ߬ߘߊ ߘߐ߫.",
"recentchanges-label-newpage": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߣߌ߲߬ ߓߘߊ߫ ߘߐߜߍ߫ ߞߎߘߊ ߟߊߘߊ߲߫",
"recentchanges-label-minor": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߘߋ߬ߣߍ߲ ߠߋ߫ ߦߋ߫",
"recentchanges-label-bot": "ߡߐ߰ߡߐ߮ ߟߋ߫ ߣߐ߬ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ ߣߌ߲߬ ߞߍ߫ ߟߊ߫",
"randompage": "ߓߍ߲߬ߛߋ߲߬ߡߊ߬ ߞߐߜߍ",
"statistics": "ߖߊ߬ߕߋ߬ߛߎ߬ߓߐ ߟߎ߬",
"nbytes": "$1 {{PLURAL:$1|byte|bytes}}",
+ "nmembers": "$1 {{PLURAL:$1|ߛߌ߲߬ߝߏ߲ |members}}",
"prefixindex": "ߞߐߜߍ߫ ߡߍ߲ ߠߎ߬ ߓߍ߯ ߟߊߝߟߐߣߍ߲߫...",
"listusers": "ߟߊ߬ߓߊ߰ߙߊ߬ߟߊ ߛߙߍߘߍ",
"newpages": "ߘߐߜߍ߫ ߞߎߘߊ",
"booksources-search": "ߢߌߣߌ߲ߠߌ߲",
"specialloguserlabel": "ߞߍߓߊ߮ :",
"log": "ߘߏ߲߬",
+ "logempty": "ߦߙߍߞߍߟߌ߫ ߛߌ߫ ߓߍ߲߬ߢߐ߲߰ߦߊ߬ߣߍ߲߬ ߕߍ߫ ߝߐ߰ߓߍ ߟߎ߬ ߘߐ߫",
"allpages": "ߞߐߜߍ ߟߎ߬ ߓߍ߯",
"allarticles": "ߞߐߜߍ ߟߎ߬ ߓߍ߯",
"allpagessubmit": "ߥߊ߫",
"tooltip-t-recentchangeslinked": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߬ ߞߎߘߊ ߟߎ߬ ߞߐߜߍ߫ ߘߐ߫ ߡߍ߲ ߣߌ߫ ߞߐߜߍ ߣߌ߲߬ ߕߎ߲߰ߣߍ߲߫",
"tooltip-feed-atom": "ߞߐߜߍ ߣߌ߲߬ ߝߕߌ߫ ߓߊߟߏ",
"tooltip-t-contributions": "{{GENDER:$1|ߟߊ߬ߓߊ߰ߙߊ߬ߟߊ}} ߟߊ߫ ߓߟߏߓߌߟߊߢߐ߲߮ߞߊ߲ ߛߙߍߘߍ",
+ "tooltip-t-emailuser": " ߢߎߡߍߙߋ߲ߞߏ߲ߘߏ ߟߊߕߊ߯ ߟߊߓߊ߯ߙߟߊ ߣߌ߲߬ ߡߊ߬{{GENDER:$1|ߟߊߓߊ߯ߙߟߊ(ߡߏ߬ߛߏ) }}",
"tooltip-t-upload": "ߞߐߕߐ߮ ߟߎ߫ ߟߊߦߟߍ߬",
"tooltip-t-specialpages": "ߘߎ߲߬ߘߎ߬ߡߊ߬ ߞߐߜߍ߫ ߟߎ߫ ߛߙߍߘߍ",
"tooltip-t-print": " ߞߐߜߍ ߣߌ߲߬ ߜߌ߬ߙߌ߲߬ߘߌ߬ߕߊ߬ߡߊ ߛߎ߮",
"revdelete-text-file": "ړنگې شوې بڼې به لا تر اوسه پورې د مخ پېښليک کې ښکاري، خو د هغو ځينو برخو ته به عام خلک لاسرسی و نه لري.",
"logdelete-text": "ړنگې شوې بڼې به لا تر اوسه پورې د مخ پېښليک کې ښکاري، خو د هغو ځينو برخو ته به عام خلک لاسرسی و نه لري.",
"revdelete-text-others": "نور پازوالان به لا هم د پټ راز محتوياتو ته لاسرسی ومومي او دا یې له منځه یوسي، مګر که نه بل ډول مشخص شوی.",
- "revdelete-confirm": "Ù\84Ø·Ù\81ا دا تاÛ\8cÛ\8cد Ú©Ú\93ئ Ú\86Û\90 تاسÙ\88 دا کار Ú©Ù\88Ù\84 غÙ\88اÚ\93ئØ\8c دا Ú\86Û\90 تاسÙ\88 پاÛ\8cÙ\84Û\90 Ù¾Ù\87 پاÙ\85 Ú©Û\90 Ù\84رئ اÙ\88 تاسÙ\88 Û\8cÛ\90 سرÙ\87 Ù\85طابÙ\82ت Ú©Ù\88ئ[[{{MediaWiki:Policy-url}}|پاÙ\84Û\8cسÛ\8d]].",
+ "revdelete-confirm": "Ù\84Ø·Ù\81ا دا تاÛ\8cÛ\8cد Ú©Ú\93ئ Ú\86Û\90 تاسÙ\88 دا کار Ú©Ù\88Ù\84 غÙ\88اÚ\93ئØ\8c تاسÙ\88 پاÛ\8cÙ\84Û\90 Ù¾Ù\87 پاÙ\85 Ú©Û\90 Ù\84رئ اÙ\88 [[{{MediaWiki:Policy-url}}|پاÙ\84Û\8cسÛ\8d]] تÙ\87 Ù\85Ù\88 Ù\87Ù\85 Ù\81کر دÛ\8c.",
"revdelete-legend": "د ښکارېدنې محدوديتونه ټاکل",
"revdelete-hide-text": "د مخکتنې متن",
"revdelete-hide-image": "د دوتنې مېنځپانگه پټول",
"tog-norollbackdiff": "Төннөрүү кэнниттэн барыллар уратыларын көрдөрүмэ",
"tog-useeditwarning": "Уларытыыларбын бигэргэппэккэ сирэйтэн тахсаары гыннахпына сэрэтээр",
"tog-prefershttps": "Манна киирэргэ куруук көмүскэллээх холбонууну туттарга",
+ "tog-showrollbackconfirmation": "Сигэни баттаатахха дьайыыга бигэргэтиини көрдөр",
"underline-always": "Куруук",
"underline-never": "Аннынан тардыма",
"underline-default": "Браузер туруоруутунан",
"badretype": "Аһарыктарыҥ сөп түбэспэтилэр.",
"usernameinprogress": "Бу аатынан бэлиэтэнии бара турар.\nБука диэн кэтэһэ түс.",
"userexists": "Суруйбут аатыҥ бэлиэр баар.\nБука диэн, атын аатта тал.",
+ "createacct-normalization": "Эн бэлиэтэммит аатыҥ техника хааччаҕын учуоттаан маннык буолуо «$2».",
"loginerror": "Ааккын система билбэтэ",
"createacct-error": "Бэлиэтэнии кэмигэр алҕас таҕыста",
"createaccounterror": "Саҥа аат бэлиэтиир кыах суох: $1",
"resetpass-abort-generic": "Аһарыгы уларытыыны кэҥэтии тохтотто.",
"resetpass-expired": "Аһарыгыҥ болдьоҕо ааспыт эбит. Бука диэн, саҥа аһарыкта туруорун.",
"resetpass-expired-soft": "Аһарыгыҥ болдьоҕо бүппүт, онон уларытыллыахтаах эбит. Бука диэн атын аһарыкта суруй эбэтэр маны баттаан кэлин киллэрээр \"{{int:authprovider-resetpass-skip-label}}\".",
- "resetpass-validity-soft": "Аһарыгыҥ алҕастаах: $1\n\nБука диэн саҥа аһарыкта суруй эбэтэр кэлин киллэриэххин баҕарар буоллаххына маны баттаа \"{{int:authprovider-resetpass-skip-label}}\"",
+ "resetpass-validity": "Аһарыгыҥ алҕастаах: $1\n\nСаҥа аһарыкта туруорун дуу.",
+ "resetpass-validity-soft": "Аһарыгыҥ алҕастаах: $1\n\nБука диэн саҥа аһарыкта суруй эбэтэр кэлин суруйуоххун баҕарар буоллаххына маны баттаа \n\"{{int:authprovider-resetpass-skip-label}}\"",
"passwordreset": "Аһарыгы саҥаттан",
"passwordreset-text-one": "Урукку аһарыгы уларытарга бу форманы толор.",
"passwordreset-text-many": "{{PLURAL:$1|Быстах аһарыгы электрон почтаҕар ыыттарарга түннүктэртэн биирдэстэригэр суруй.}}",
"diff-paragraph-moved-toold": "Параграф көһөрүллүбүт. Баттаан урукку сиригэр көс.",
"difference-missing-revision": "$2 барыл бу тэҥнээһиҥҥэ ($1) көстүбэтэ.\n\nБу үксүн хайыы-үйэ сотуллубут сирэйи кытта тэҥнээри эргэрбит сигэнэн кэллэххэ баар буолааччы.\nСиһилии баҕар [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} сотуу сурунаалыгар] баара буолуо.",
"searchresults": "Булулунна",
+ "search-filter-title-prefix": "Мантан саҕаланар «$1» сирэйдэри эрэ көрдөө",
"search-filter-title-prefix-reset": "Сирэйдэри барытын көрдөөһүн",
"searchresults-title": "Көрдөөһүн түмүгэ \"$1\"",
"titlematches": "Ыстатыйалар ааттара хоһулаһар",
"stub-threshold-disabled": "Арахсыбыт",
"recentchangesdays": "Хас хонук иһинэн уларытыылары көрдөрөргө:",
"recentchangesdays-max": "(улааппыта $1 күн)",
- "recentchangescount": "Саҥа Ñ\83лаÑ\80Ñ\8bÑ\82Ñ\8bÑ\8bлаÑ\80 көÑ\80дөÑ\80үллÑ\8dр ахсааннара:",
- "prefs-help-recentchangescount": "Ð\91Ñ\83 Ñ\81аҥа көннөÑ\80үүлÑ\8dÑ\80и, Ñ\81иÑ\80Ñ\8dй Ñ\83Ñ\81Ñ\82Ñ\83оÑ\80Ñ\83йалаÑ\80Ñ\8bн Ñ\83онна Ñ\81Ñ\83Ñ\80Ñ\83нааллаÑ\80Ñ\8b көÑ\80дөÑ\80Ó©Ñ\80.",
+ "recentchangescount": "Саҥа Ñ\83лаÑ\80Ñ\8bÑ\82Ñ\8bÑ\8bлаÑ\80 иÑ\81пииһÑ\8dкÑ\82Ñ\8dÑ\80игÑ\8dÑ\80, Ñ\81иÑ\80Ñ\8dй Ñ\83Ñ\81Ñ\82Ñ\83оÑ\80Ñ\83йаÑ\82Ñ\8bгаÑ\80 Ñ\83онна Ñ\81Ñ\83Ñ\80Ñ\83нааллаÑ\80га көÑ\80дөÑ\80үллÑ\8dÑ\80 Ñ\83лаÑ\80Ñ\8bÑ\82Ñ\8bÑ\8bлар ахсааннара:",
+ "prefs-help-recentchangescount": "УлааппÑ\8bÑ\82а: 1000",
"prefs-help-watchlist-token2": "Бу кэтиир испииһэгиҥ ситим-ханаалын кистэлэҥ күлүүһэ.\nБу күлүүһүнэн ким баҕарар эн испииһэккин көрүөн сөп, онон кимиэхэ да биэримэ. Хаһан баҕарар [[Special:ResetTokens|маны баттаан уларытыаххын]] сөп.",
"savedprefs": "Эн туруорууларыҥ олохтоннулар.",
"savedrights": "{{GENDER:$1|$1}} кыттааччы бөлөҕө бигэргэннэ.",
"default": "чопчу ыйыллыбатаҕына маннык",
"prefs-files": "Билэлэр",
"prefs-custom-css": "Бэйэ CSS",
+ "prefs-custom-json": "Тус бэйэ JSON-а",
"prefs-custom-js": "Бэйэ JS",
"prefs-common-config": "Бары тиэмэлэргэ биир CSS/JS",
"prefs-reset-intro": "Бу сирэй көмөтүнэн туруорууларгын саҥаттан туруорар турукка төннөрүөххүн сөп.\nМаны бигэргэттэххинэ билигин баар туруоруулары дэбигис сөргүппэккин.",
"prefs-displaywatchlist": "Көстүүтүн туруоруулара",
"prefs-changesrc": "Көстүбүт уларытыылар",
"prefs-changeswatchlist": "Көрдөр;ллэр уларытыылар",
+ "prefs-pageswatchlist": "Кэтэбилгэ сылдьар сирэйдэр",
"prefs-tokenwatchlist": "Токен",
"prefs-diffs": "Уратылара",
"prefs-help-prefershttps": "Аныгыскы киириигэр үлэлиир буолуо.",
"group-autoconfirmed": "Аптамаатынан бигэргэтиллибит кыттааччылар",
"group-bot": "Роботтар",
"group-sysop": "Дьаһабыллар",
+ "group-interface-admin": "Алтыһаан дьаһабыллара",
"group-bureaucrat": "Бюрокрааттар",
"group-suppress": "Ревизордар",
"group-all": "(бары)",
"group-autoconfirmed-member": "{{GENDER:$1|аптамаатынан бигэргэтиллибит кыттааччы}}",
"group-bot-member": "{{GENDER:$1|робот}}",
"group-sysop-member": "{{GENDER:$1|дьаһабыл}}",
+ "group-interface-admin-member": "{{GENDER:$1|алтыһаан дьаһабыла}}",
"group-bureaucrat-member": "{{GENDER:$1|бүрэкирээт}}",
"group-suppress-member": "{{GENDER:$1|ревизор}}",
"grouppage-user": "{{ns:project}}:Кыттааччылар",
"grouppage-autoconfirmed": "{{ns:project}}:Аптамаатынан бигэргэммит кыттааччылар",
"grouppage-bot": "{{ns:project}}:Роботтар",
"grouppage-sysop": "{{ns:project}}:Дьаһабыллар",
+ "grouppage-interface-admin": "{{ns:project}}:Алтыһаан дьаһабыллара",
"grouppage-bureaucrat": "{{ns:project}}:Бюрокрааттар",
"grouppage-suppress": "{{ns:project}}:Ревизордар",
"right-read": "Сирэйдэри көрүү",
- "right-edit": "СиÑ\80Ñ\8dйдÑ\8dÑ\80и Ñ\83ларытыы",
+ "right-edit": "Уларытыы",
"right-createpage": "Сирэйдэри оҥоруу (ырытыы сирэйдэриттэн ураты)",
"right-createtalk": "Ырытыы сирэйдэрин оҥоруу",
"right-createaccount": "Саҥа кыттааччыны бэлиэтээһин",
"right-reupload-own": "Билэлэри суруттарбыт киһи бэйэтэ иккистээн суруттарыыта",
"right-reupload-shared": "Уопсай ыскылаат билэлэрин локальнай ыскылаат билэлэринэн уларытыы",
"right-upload_by_url": "URL аадырыстан билэлэри киллэрии",
- "right-purge": "Ð\9aÑ\8dÑ\8dһи бигÑ\8dÑ\80гÑ\8dÑ\82Ñ\8dÑ\80 Ñ\81иÑ\80Ñ\8dйÑ\8d Ñ\81Ñ\83оÑ\85 ыраастааһын",
+ "right-purge": "СиÑ\80Ñ\8dй кÑ\8dÑ\8dһин ыраастааһын",
"right-autoconfirmed": "IP түргэнигэр олоҕурбут хааччахтан тутулуктаныма",
"right-bot": "аптамаат быһыытынан ааҕыллар",
"right-nominornewtalk": "Ырытыы сирэйдэригэр кыра көннөрүүлэр суох буоллахтарына саҥа этии эрэсиимэ холбонор",
"right-editusercss": "Атын кыттааччылар CSS-билэлэрин уларытыы",
"right-edituserjson": "Атын кыттааччылар JSON-билэлэрин уларытыы",
"right-edituserjs": "Атын кыттааччылар JS-билэлэрин уларытыы",
+ "right-editsitecss": "CSS-билэлэри уларытыы",
+ "right-editsitejson": "JSON-билэлэри уларытыы",
+ "right-editsitejs": "JavaScript-билэлэри уларытыы",
"right-editmyusercss": "Кыттааччы CSS-билэтин уларытыы",
+ "right-editmyuserjson": "Тус бэйэ JSON-билэлэрин уларытыы",
"right-editmyuserjs": "Бэйэ JavaScript-билэлэрин уларытыы",
"right-viewmywatchlist": "Бэйэ кэтиир тиһигин көрүү",
"right-editmywatchlist": "Бэйэ кэтиир тиһигин уларытыы. Болҕой, сорох дьайыыларыҥ бу быраабы биэрбэтэҕиҥ да иһин сирэйдэри тиһиккэ эбиэхтэрин сөп.",
"action-changetags": "ханнык баҕарар тиэктэри сурунаал биирдиилээн уларытыыларыгар уонна суруктарыгар эбэри уонна сотору көҥүллээ",
"action-deletechangetags": "тиэктэри билии олоҕуттан сотуу",
"action-purge": "сирэй кээһин ыраастааһын",
+ "action-bigdelete": "уһун устуоруйалаах сирэйдэри сотуу",
+ "action-blockemail": "эл. суругу ыытары бобуу",
+ "action-bot": "аптамаат быһыытынан ааҕыллар",
+ "action-editinterface": "кыттааччы алтыһаанын уларытыы",
+ "action-editusercss": "атын кыттааччылар CSS-билэлэрин уларытыы",
+ "action-edituserjson": "атын кыттааччылар JSON-билэлэрин уларытыы",
+ "action-edituserjs": "Атын кыттааччылар JavaScript-билэлэрин уларытыы",
+ "action-editsitecss": "ситим-сир CSS-билэлэрин уларытыы",
"nchanges": "$1 {{PLURAL:$1|уларытыы|уларытыылар}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|тиһэх сылдьыыгыттан}}",
"enhancedrc-history": "устуоруйата",
"yourpasswordagain": "Поново унеси лозинку:",
"createacct-yourpasswordagain": "Потврдите лозинку",
"createacct-yourpasswordagain-ph": "Поново унесите лозинку",
- "userlogin-remembermypassword": "Ð\9eÑ\81Ñ\82ави ме пÑ\80иÑ\98авÑ\99еног/Ñ\83",
+ "userlogin-remembermypassword": "Ð\9dе одÑ\98авÑ\99Ñ\83Ñ\98 ме",
"userlogin-signwithsecure": "Користите безбедну везу",
"cannotlogin-title": "Пријава није могућа",
"cannotlogin-text": "Пријава није могућа",
"copyrightwarning": "Имајте на уму да се сви доприноси на овом викију сматрају као објављени под лиценцом $2 (више на $1).\nАко не желите да се ваши текстови мењају и размењују без ограничења, онда их не шаљите овде.<br />\nИсто тако обећавате да сте Ви аутор текста, или да сте га умножили са извора који је у јавном власништву.\n<strong>Не шаљите радове заштићене ауторским правима без дозволе!</strong>",
"copyrightwarning2": "Имајте на уму да се сви доприноси на овом викију могу мењати, враћати или брисати од других корисника.\nАко не желите да се ваши текстови слободно мењају и расподељују, не шаљите их овде.<br />\nИсто тако обећавате да сте ви аутор текста, или да сте га умножили с извора који је у јавном власништву (више на $1).\n<strong>Не шаљите радове заштићене ауторским правима без дозволе!</strong>",
"editpage-cannot-use-custom-model": "Модел садржаја ове странице се не може променити.",
- "longpageerror": "<strong>Грешка: текст који сте унели је величине {{PLURAL:$1|један килобајт|$1 килобајта}}, што је веће од {{PLURAL:$2|дозвољеног једног килобајта|дозвољена $2 килобајта|дозвољених $2 килобајта}}.</strong>\nСтраница не може бити сачувана.",
+ "longpageerror": "<strong>Грешка: текст који сте проследили је величине {{PLURAL:$1|један килобајт|$1 килобајта}}, што је веће од {{PLURAL:$2|дозвољеног једног килобајта|дозвољена $2 килобајта|дозвољених $2 килобајта}}.</strong>\nСтраница не може бити сачувана.",
"readonlywarning": "<strong>Упозорење: база података је закључана ради одржавања, тако да тренутно нећете моћи да сачувате измене.</strong>\nМожда бисте желели сачувати текст за касније у некој текстуалној датотеци.\n\nСистемски администратор је навео следеће објашњење: $1",
"protectedpagewarning": "<strong>Упозорење: Ова страница је заштићена, тако да само корисници са администраторским овлашћењима могу да је уређују.</strong>\nНајновији унос у дневнику је наведен испод као референца:",
"semiprotectedpagewarning": "<strong>Напомена:</strong> Ова страница је заштићена, тако да само аутоматски потврђени корисници могу да је уређују.\nНајновији унос у дневнику је наведен испод као референца:",
"upload": "Отпремање датотеке",
"uploadbtn": "Отпреми датотеку",
"reuploaddesc": "Назад на образац за отпремање",
- "upload-tryagain": "Пошаљи измењени опис датотеке",
+ "upload-tryagain": "Проследи измењени опис датотеке",
"upload-tryagain-nostash": "Пошаљите ре-отпремљену датотеку и измењен опис",
"uploadnologin": "Нисте пријављени",
"uploadnologintext": "$1 да бисте отпремали датотеке.",
"filetype-unwanted-type": "<strong>„.$1“</strong> је непожељан тип датотеке.\n{{PLURAL:$3|Пожељан тип датотеке је|Пожељни типови датотека су}} $2.",
"filetype-banned-type": "<strong>„.$1“</strong> {{PLURAL:$4|није допуштен тип датотеке|нису допуштени типови датотека}}.\n{{PLURAL:$3|Дозвољен тип датотеке је|Дозвољени типови датотека су}} $2.",
"filetype-missing": "Ова датотека нема проширење (нпр. „.jpg“).",
- "empty-file": "Ð\9fоÑ\81лаÑ\82а даÑ\82оÑ\82ека је празна.",
+ "empty-file": "Ð\94аÑ\82оÑ\82ека коÑ\98Ñ\83 Ñ\81Ñ\82е пÑ\80оÑ\81ледили је празна.",
"file-too-large": "Послата датотека је превелика.",
"filename-tooshort": "Назив датотеке је прекратак.",
"filetype-banned": "Овај тип датотеке је забрањен.",
"emailnotarget": "Непостојеће или наважеће корисничко име примаоца.",
"emailtarget": "Унос корисничког имена примаоца",
"emailusername": "Корисничко име:",
- "emailusernamesubmit": "Пошаљи",
+ "emailusernamesubmit": "Проследи",
"email-legend": "Слање е-поруке кориснику/ци пројекта {{SITENAME}}",
"emailfrom": "Од:",
"emailto": "За:",
"deleting-subpages-warning": "<strong>Упозорење:</strong> Страница коју желите избрисати има [[Special:PrefixIndex/{{FULLPAGENAME}}/|{{PLURAL:$1|подстраницу|$1 подстранице|$1 подстраница|51=преко 50 подстраница}}]].",
"rollback": "Врати измене",
"rollback-confirmation-confirm": "Потврдите:",
+ "rollback-confirmation-yes": "Врати",
+ "rollback-confirmation-no": "Откажи",
"rollbacklink": "врати",
"rollbacklinkcount": "врати $1 {{PLURAL:$1|измену|измене|измена}}",
"rollbacklinkcount-morethan": "врати више од $1 {{PLURAL:$1|измене|измене|измена}}",
"mycontris": "Доприноси",
"anoncontribs": "Доприноси",
"contribsub2": "За {{GENDER:$3|$1}} ($2)",
+ "contributions-subtitle": "За {{GENDER:$3|$1}}",
"contributions-userdoesnotexist": "Кориснички налог „$1“ није отворен.",
+ "negative-namespace-not-supported": "Именски простори са негативним вредностима нису подржани.",
"nocontribs": "Нису пронађене промене које одговарају овим критеријумима.",
"uctop": "тренутна",
"month": "од месеца (и раније):",
"blocklist-userblocks": "Сакриј блокаде налога",
"blocklist-tempblocks": "Сакриј привремене блокаде",
"blocklist-addressblocks": "Сакриј појединачне блокаде IP-а",
+ "blocklist-type": "Тип:",
+ "blocklist-type-opt-all": "Све",
"blocklist-type-opt-sitewide": "На нивоу сајта",
"blocklist-type-opt-partial": "Делимично",
"blocklist-rangeblocks": "Сакриј блокаде опсега",
"watchlistedit-normal-done": "{{PLURAL:$1|1=Једна страница је уклоњена|$1 странице су уклоњене|$1 страница је уклоњено}} с вашег списка надгледања:",
"watchlistedit-raw-title": "Уређивање необрађеног списка надгледања",
"watchlistedit-raw-legend": "Уређивање необрађеног списка надгледања",
- "watchlistedit-raw-explain": "Ð\9dаÑ\81лови Ñ\81а Ñ\81пиÑ\81ка надгледаÑ\9aа Ñ\81Ñ\83 пÑ\80иказани иÑ\81под и могÑ\83 Ñ\81е Ñ\83Ñ\80еÑ\92иваÑ\82и додаваÑ\9aем или Ñ\83клаÑ\9aаÑ\9aем Ñ\81Ñ\82авки Ñ\81а Ñ\81пиÑ\81ка;\nÑ\98едан наÑ\81лов по Ñ\80едÑ\83.\nÐ\9aада завÑ\80Ñ\88иÑ\82е, кликниÑ\82е на â\80\9e{{int:Watchlistedit-raw-submit}}â\80\9c.\nÐ\9cожеÑ\82е да [[Special:EditWatchlist|коÑ\80иÑ\81Ñ\82иÑ\82е и обиÑ\87ан уређивач]].",
+ "watchlistedit-raw-explain": "Ð\9dаÑ\81лови Ñ\81а Ñ\81пиÑ\81ка надгледаÑ\9aа Ñ\81Ñ\83 пÑ\80иказани иÑ\81под и могÑ\83 Ñ\81е Ñ\83Ñ\80еÑ\92иваÑ\82и додаваÑ\9aем или Ñ\83клаÑ\9aаÑ\9aем Ñ\81Ñ\82авки Ñ\81а Ñ\81пиÑ\81ка;\nÑ\98едан наÑ\81лов по Ñ\80едÑ\83.\nÐ\9aада завÑ\80Ñ\88иÑ\82е, кликниÑ\82е на â\80\9e{{int:Watchlistedit-raw-submit}}â\80\9d.\nÐ\9cожеÑ\82е да [[Special:EditWatchlist|коÑ\80иÑ\81Ñ\82иÑ\82е и Ñ\81Ñ\82андаÑ\80дни уређивач]].",
"watchlistedit-raw-titles": "Наслови:",
"watchlistedit-raw-submit": "Ажурирај списак",
"watchlistedit-raw-done": "Ваш списак надгледања је ажуриран.",
"htmlform-int-toolow": "Наведена вредност је испод минимума од $1",
"htmlform-int-toohigh": "Наведена вредност је изнад максимума од $1",
"htmlform-required": "Ова вредност је обавезна.",
- "htmlform-submit": "Постави",
+ "htmlform-submit": "Проследи",
"htmlform-reset": "Врати промене",
"htmlform-selectorother-other": "Друго",
"htmlform-no": "Не",
"page_first": "ilk",
"page_last": "son",
"histlegend": "Fark seçimi: Karşılaştırmayı istediğiniz 2 sürümün önündeki daireleri işaretleyip, \"{{int:Compareselectedversions}}\" düğmesine basın.<br />\nTanımlar: '''({{int:cur}})''' = son revizyon ile arasındaki fark, '''({{int:last}})''' = bir önceki revizyon ile arasındaki fark, '''{{int:minoreditletter}}''' = küçük değişiklik.",
- "history-fieldset-title": "Geçmişe gözat",
+ "history-fieldset-title": "Revizyonları filtrele",
"history-show-deleted": "Sadece silinen sürümler",
"histfirst": "en eski",
"histlast": "en yeni",
"historysize": "({{PLURAL:$1|1 bayt|$1 bayt}})",
- "historyempty": "(boş)",
+ "historyempty": "boş",
"history-feed-title": "Değişiklik geçmişi",
"history-feed-description": "Viki üzerindeki bu sayfanın değişiklik geçmişi.",
"history-feed-item-nocomment": "$1, $2'de",
"right-reupload-own": "Kendisinin yüklediği bir dosyanın üzerine yaz",
"right-reupload-shared": "Paylaşılan ortam deposundaki dosyaları yerel olarak geçersiz kıl",
"right-upload_by_url": "Bir URL adresinden dosya yükle",
- "right-purge": "Doğrulama yapmadan bir sayfa için site belleğini temizle",
+ "right-purge": "Bir sayfa için site önbelleğini temizle",
"right-autoconfirmed": "IP-tabanlı hız limitleri etkilenme",
"right-bot": "Otomatik bir işlem gibi muamele gör",
"right-nominornewtalk": "Kullanıcı tartışma sayfalarında yaptığı küçük değişiklikler kullanıcıya yeni mesaj bildirimiyle bildirilmez",
"grant-delete": "Sayfaları, sürümleri ve günlük girdileri sil",
"grant-editinterface": "MediaWiki alanadını, sitewide'ı ve kullanıcı JSON'unu düzenle",
"grant-editmycssjs": "Kullanıcı CSS/JSON/JavaScript'ini düzenle",
- "grant-editmyoptions": "Kullanıcı tercihlerini Düzenle",
+ "grant-editmyoptions": "Kullanıcı tercihlerinizi ve JSON yapılandırmanızı düzenleyin",
"grant-editmywatchlist": "İzleme listeni düzenle",
"grant-editsiteconfig": "Sitewide ve kullanıcı CSS/JS değiştir",
"grant-editpage": "Mevcut sayfaları düzenle",
"rcfilters-savedqueries-already-saved": "Bu filtreler zaten kaydedildi. Yeni bir Kayıtlı Filtre oluşturmak için ayarlarınızı değiştirin.",
"rcfilters-restore-default-filters": "Varsayılan süzgeçleri geri getir",
"rcfilters-clear-all-filters": "Tüm süzgeçleri temizle",
- "rcfilters-show-new-changes": "Yeni değişiklikleri görüntüle",
+ "rcfilters-show-new-changes": "$1 tarihinden bu yana yapılan yeni değişiklikleri görüntüleyin",
"rcfilters-search-placeholder": "Son değişiklikleri filtrele (menüyü kullanın veya süzgeç adını arayın)",
"rcfilters-invalid-filter": "Geçersiz süzgeç",
"rcfilters-empty-filter": "Etkin süzgeç bulunmuyor. Tüm katkıları gösteriliyor.",
"rcfilters-watchlist-markseen-button": "Tüm değişiklikleri görüldü olarak işaretle",
"rcfilters-watchlist-edit-watchlist-button": "İzlenen sayfaların listesini düzenle",
"rcfilters-watchlist-showupdated": "Gerçekleştirilen değişikliklerden bu yana ziyaret etmediğiniz sayfalarda yapılan değişiklikler <strong>koyu</strong> renktedir.",
- "rcfilters-preference-label": "Son değişikliklerin geliştirilmiş sürümünü gizle",
- "rcfilters-preference-help": "2017 arayüz tasarımını ve bu andan sonra eklenen tüm araçları geri alır.",
- "rcfilters-watchlist-preference-label": "İzleme listesinin geliştirilmiş sürümünü gizle",
- "rcfilters-watchlist-preference-help": "2017 arayüz tasarımını ve bu andan sonra eklenen tüm araçları geri alır.",
+ "rcfilters-preference-label": "JavaScript olmayan bir arayüz kullanın",
+ "rcfilters-preference-help": "Filtre olmadan arama yapma veya işlevselliği vurgulamadan SonDeğişiklikler'i yükler.",
+ "rcfilters-watchlist-preference-label": "JavaScript olmayan bir arayüz kullanın",
+ "rcfilters-watchlist-preference-help": "Filtre Listesini arama olmadan veya işlevselliği vurgulayarak İzleme Listesi'ni yükler.",
"rcfilters-target-page-placeholder": "Bir sayfa (ya da kategori) adı girin",
"rcnotefrom": "<strong>$3, $4</strong> tarihinden itibaren yapılan {{PLURAL:$5|değişiklik|değişiklik}} aşağıdadır (<strong>$1</strong> tarhine kadar olanlar gösterilmektedir).",
"rclistfromreset": "Tarih seçimini sıfırla",
"apisandbox-loading-results": "API sonuçları alınıyor...",
"apisandbox-results-error": "API sorgusu yanıtı yüklenirken bir hata oluştu: $1.",
"apisandbox-request-url-label": "İstek URL:",
- "apisandbox-request-time": "İstek zamanı: $1",
+ "apisandbox-request-time": "İstek zamanı: {{PLURAL:$1|$1 ms}}",
"apisandbox-continue": "Devam et",
"apisandbox-continue-clear": "Temizle",
"apisandbox-multivalue-all-namespaces": "$1 (Tüm isim alanları)",
"enotif_body_intro_moved": "{{SITENAME}} sayfası $1, $2 tarafından $PAGEEDITDATE tarihinde {{GENDER:$2|taşındı}}, mevcut revizyon için bakınız: $3.",
"enotif_body_intro_restored": "{{SITENAME}} sayfası $1, $2 tarafından $PAGEEDITDATE tarihinde {{GENDER:$2|geri getirildi}}, mevcut revizyon için bakınız: $3.",
"enotif_body_intro_changed": "{{SITENAME}} sayfası $1, $2 tarafından $PAGEEDITDATE tarihinde {{GENDER:$2|değiştirildi}}, mevcut revizyon için bakınız: $3.",
- "enotif_lastvisited": "Son ziyaretinizden bu yana olan tüm değişiklikleri görmek için $1'e bakın.",
+ "enotif_lastvisited": "Son ziyaretinizden bu yana yapılan tüm değişiklikler için bakınız: $1",
"enotif_lastdiff": "Bu değişikliği görmek için, $1 sayfasına bakınız.",
"enotif_anon_editor": "anonim kullanıcı $1",
"enotif_body": "Sayın $WATCHINGUSERNAME,\n\n$PAGEINTRO $NEWPAGE\n\nEditörün girdiği özet: $PAGESUMMARY $PAGEMINOREDIT\n\nEditörün iletişim bilgileri:\ne-posta: $PAGEEDITOR_EMAIL\nviki: $PAGEEDITOR_WIKI\n\nBahsi geçen sayfayı oturum açarak ziyaret edinceye kadar sayfayla ilgili başka bildirim gönderilmeyecektir. Ayrıca izleme listenizdeki tüm sayfaların bildirim durumlarını sıfırlayabilirsiniz.\n\n{{SITENAME}} bildirim sistemi\n\n--\nE-posta bildirim ayarlarınızı değiştirmek için aşağıdaki sayfayı ziyaret ediniz:\n{{canonicalurl:{{#special:Preferences}}}}\n\nİzleme listesi ayarlarınızı değiştirmek için aşağıdaki sayfayı ziyaret ediniz:\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nSayfayı izleme listenizden silmek için aşağıdaki sayfayı ziyaret ediniz:\n$UNWATCHURL\n\nGeri bildirim ve daha fazla yardım için:\n$HELPPAGE",
"deletepage": "Sayfayı sil",
"confirm": "Onayla",
"excontent": "eski içerik: '$1'",
- "excontentauthor": "eski içerik: '$1' ('[[Special:Contributions/$2|$2]]' katkıda bulunmuş olan tek kullanıcı)",
+ "excontentauthor": "eski içerik: '$1' ve katkıda bulunmuş olan tek kullanıcı \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|mesaj]])",
"exbeforeblank": "Silinmeden önceki içerik: '$1'",
"delete-confirm": "\"$1\" sayfasını sil",
"delete-legend": "Sil",
"historywarning": "<strong>Uyarı:</strong> Silmek üzere olduğunuz sayfanın yaklaşık olarak $1 sürüme sahip bir geçmişi var:",
- "historyaction-submit": "Göster",
+ "historyaction-submit": "Revizyonları göster",
"confirmdeletetext": "Bu sayfayı veya dosyayı tüm geçmişi ile birlikte veritabanından kalıcı olarak silmek üzeresiniz.\nBu işlemden kaynaklı doğabilecek sonuçların farkında iseniz ve işlemin [[{{MediaWiki:Policy-url}}|Silme kurallarına]] uygun olduğuna eminseniz, işlemi onaylayın.",
"actioncomplete": "İşlem tamamlandı",
"actionfailed": "İşlem başarısız oldu",
"mycontris": "Katkılar",
"anoncontribs": "Katkılar",
"contribsub2": "{{GENDER:$3|$1}} ($2) tarafından",
+ "contributions-subtitle": "{{GENDER:$3|$1}} için",
"contributions-userdoesnotexist": "\"$1\" kullanıcı hesabı kayıtlı değil.",
"nocontribs": "Bu kriterlere uyan değişiklik bulunamadı",
"uctop": "güncel",
"sp-contributions-newbies-sub": "Yeni kullanıcılar için",
"sp-contributions-newbies-title": "Yeni hesaplar için kullanıcı katkıları",
"sp-contributions-blocklog": "engelleme günlüğü",
- "sp-contributions-suppresslog": "kullanıcının silinen katkıları",
- "sp-contributions-deleted": "kullanıcının silinen katkıları",
+ "sp-contributions-suppresslog": "{{GENDER:$1|kullanıcının}} baskılanmış katkıları",
+ "sp-contributions-deleted": "{{GENDER:$1|kullanıcının}} silinen katkıları",
"sp-contributions-uploads": "yüklenenler",
"sp-contributions-logs": "günlükler",
"sp-contributions-talk": "mesaj",
"Hello903hello",
"Fitoschido",
"Kanashimi",
- "Roy17"
+ "Roy17",
+ "Tang891228"
]
},
"tog-underline": "連結加底線:",
"title-invalid-talk-namespace": "所請求嘅版面標題指去未開嘅討論版。",
"title-invalid-characters": "所請求嘅版面標題有「$1」呢個無效字符。",
"title-invalid-relative": "標題有相對路徑。因為用戶嘅瀏覽器經常處理唔到相對路徑(./, ../),所以相對路徑無效。",
- "title-invalid-magic-tilde": "所請求嘅版面標題有無效嘅波浪線魔法字(<nowiki>~~~</nowiki>)。",
+ "title-invalid-magic-tilde": "所請求嘅版面標題有無效嘅波浪線魔術字(<nowiki>~~~</nowiki>)。",
"title-invalid-too-long": "所請求嘅版面標題太長。標題用UTF-8編碼嗰時嘅長度唔應該超過 $1 {{PLURAL:$1|字節}}",
"title-invalid-leading-colon": "所請求嘅版面標題開頭有無效冒號。",
"perfcached": "以下嘅資料係嚟自快取,可能唔係最新嘅。 最多有{{PLURAL:$1|一個結果|$1個結果}}響快取度。",
"Hello903hello",
"Luuva",
"Davidzdh",
- "WQL"
+ "WQL",
+ "Tang891228"
]
},
"tog-underline": "底線標示連結:",
"booksources-search": "搜尋",
"booksources-text": "下列清單包含其他銷售新書籍或二手書籍的網站連結,可會有你想尋找書籍的進一部資訊:",
"booksources-invalid-isbn": "您提供的 ISBN 不正確,請檢查複製的來源是否有誤。",
- "magiclink-tracking-rfc": "使用 RFC 魔法連結的頁面",
- "magiclink-tracking-rfc-desc": "此頁面使用 RFC 魔法連結的頁面,請參考 [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] 的如何遷移。",
- "magiclink-tracking-pmid": "使用 PMID 魔法連結的頁面",
- "magiclink-tracking-pmid-desc": "此頁面使用 PMID 魔法連結的頁面,請參考 [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] 的如何遷移。",
- "magiclink-tracking-isbn": "使用 ISBN 魔法連結的頁面",
- "magiclink-tracking-isbn-desc": "此頁面使用 ISBN 魔法連結的頁面,請參考 [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org] 的如何遷移。",
+ "magiclink-tracking-rfc": "使用 RFC 魔術連結的頁面",
+ "magiclink-tracking-rfc-desc": "此頁面使用RFC魔術連結,請參考[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org]以了解如何遷移。",
+ "magiclink-tracking-pmid": "使用 PMID 魔術連結的頁面",
+ "magiclink-tracking-pmid-desc": "此頁面使用PMID魔術連結,請參考[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org]以了解如何遷移。",
+ "magiclink-tracking-isbn": "使用 ISBN 魔術連結的頁面",
+ "magiclink-tracking-isbn-desc": "此頁面使用ISBN魔術連結,請參考[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Magic_links mediawiki.org]以了解如何遷移。",
"specialloguserlabel": "執行者:",
"speciallogtitlelabel": "目標(標題或以 {{ns:user}}:使用者名稱 表示使用者):",
"log": "日誌",
* @author Platonides
*/
+use MediaWiki\MediaWikiServices;
+
require_once __DIR__ . '/Benchmarker.php';
/**
}
private function doRequest( $proto ) {
- Http::get( "$proto://localhost/", [], __METHOD__ );
+ MediaWikiServices::getInstance()->getHttpRequestFactory()->
+ get( "$proto://localhost/", [], __METHOD__ );
}
// bench function 1
* @author Antoine Musso <hashar at free dot fr>
*/
+use MediaWiki\MediaWikiServices;
+
require_once __DIR__ . '/Maintenance.php';
/**
$retval = [];
while ( true ) {
- $json = Http::get(
+ $json = MediaWikiServices::getInstance()->getHttpRequestFactory()->get(
wfAppendQuery( 'https://www.mediawiki.org/w/api.php', $params ),
[],
__METHOD__
* @ingroup Maintenance
*/
+use MediaWiki\MediaWikiServices;
+
require_once __DIR__ . '/Maintenance.php';
/**
$url = wfAppendQuery( $baseUrl, [
'action' => 'raw',
'title' => "MediaWiki:{$page}" ] );
- $text = Http::get( $url, [], __METHOD__ );
+ $text = MediaWikiServices::getInstance()->getHttpRequestFactory()->
+ get( $url, [], __METHOD__ );
$wikiPage = WikiPage::factory( $title );
$content = ContentHandler::makeContent( $text, $wikiPage->getTitle() );
while ( true ) {
$url = wfAppendQuery( $baseUrl, $data );
- $strResult = Http::get( $url, [], __METHOD__ );
+ $strResult = MediaWikiServices::getInstance()->getHttpRequestFactory()->
+ get( $url, [], __METHOD__ );
$result = FormatJson::decode( $strResult, true );
$page = null;
$url = rtrim( $this->source, '?' ) . '?' . $url;
}
- $json = Http::get( $url );
+ $json = MediaWikiServices::getInstance()->getHttpRequestFactory()->get( $url );
$data = json_decode( $json, true );
if ( is_array( $data ) ) {
<?php
+use MediaWiki\MediaWikiServices;
use Wikimedia\TestingAccessWrapper;
abstract class MWHttpRequestTestCase extends PHPUnit\Framework\TestCase {
protected static $httpEngine;
protected $oldHttpEngine;
+ /** @var HttpRequestFactory */
+ private $factory;
+
public function setUp() {
parent::setUp();
$this->oldHttpEngine = Http::$httpEngine;
Http::$httpEngine = static::$httpEngine;
+ $this->factory = MediaWikiServices::getInstance()->getHttpRequestFactory();
+
try {
- $request = MWHttpRequest::factory( 'null:' );
- } catch ( DomainException $e ) {
+ $request = $factory->create( 'null:' );
+ } catch ( RuntimeException $e ) {
$this->markTestSkipped( static::$httpEngine . ' engine not supported' );
}
// --------------------
public function testIsRedirect() {
- $request = MWHttpRequest::factory( 'http://httpbin.org/get' );
+ $request = $this->factory->create( 'http://httpbin.org/get' );
$status = $request->execute();
$this->assertTrue( $status->isGood() );
$this->assertFalse( $request->isRedirect() );
- $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/1' );
+ $request = $this->factory->create( 'http://httpbin.org/redirect/1' );
$status = $request->execute();
$this->assertTrue( $status->isGood() );
$this->assertTrue( $request->isRedirect() );
}
public function testgetFinalUrl() {
- $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3' );
+ $request = $this->factory->create( 'http://httpbin.org/redirect/3' );
if ( !$request->canFollowRedirects() ) {
$this->markTestSkipped( 'cannot follow redirects' );
}
$this->assertTrue( $status->isGood() );
$this->assertNotSame( 'http://httpbin.org/get', $request->getFinalUrl() );
- $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3', [ 'followRedirects'
+ $request = $this->factory->create( 'http://httpbin.org/redirect/3', [ 'followRedirects'
=> true ] );
$status = $request->execute();
$this->assertTrue( $status->isGood() );
$this->assertSame( 'http://httpbin.org/get', $request->getFinalUrl() );
$this->assertResponseFieldValue( 'url', 'http://httpbin.org/get', $request );
- $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3', [ 'followRedirects'
+ $request = $this->factory->create( 'http://httpbin.org/redirect/3', [ 'followRedirects'
=> true ] );
$status = $request->execute();
$this->assertTrue( $status->isGood() );
return;
}
- $request = MWHttpRequest::factory( 'http://httpbin.org/redirect/3', [ 'followRedirects'
+ $request = $this->factory->create( 'http://httpbin.org/redirect/3', [ 'followRedirects'
=> true, 'maxRedirects' => 1 ] );
$status = $request->execute();
$this->assertTrue( $status->isGood() );
}
public function testSetCookie() {
- $request = MWHttpRequest::factory( 'http://httpbin.org/cookies' );
+ $request = $this->factory->create( 'http://httpbin.org/cookies' );
$request->setCookie( 'foo', 'bar' );
$request->setCookie( 'foo2', 'bar2', [ 'domain' => 'example.com' ] );
$status = $request->execute();
}
public function testSetCookieJar() {
- $request = MWHttpRequest::factory( 'http://httpbin.org/cookies' );
+ $request = $this->factory->create( 'http://httpbin.org/cookies' );
$cookieJar = new CookieJar();
$cookieJar->setCookie( 'foo', 'bar', [ 'domain' => 'httpbin.org' ] );
$cookieJar->setCookie( 'foo2', 'bar2', [ 'domain' => 'example.com' ] );
$this->assertTrue( $status->isGood() );
$this->assertResponseFieldValue( 'cookies', [ 'foo' => 'bar' ], $request );
- $request = MWHttpRequest::factory( 'http://httpbin.org/cookies/set?foo=bar' );
+ $request = $this->factory->create( 'http://httpbin.org/cookies/set?foo=bar' );
$cookieJar = new CookieJar();
$request->setCookieJar( $cookieJar );
$status = $request->execute();
$this->markTestIncomplete( 'CookieJar does not handle deletion' );
- // $request = MWHttpRequest::factory( 'http://httpbin.org/cookies/delete?foo' );
+ // $request = $this->factory->create( 'http://httpbin.org/cookies/delete?foo' );
// $cookieJar = new CookieJar();
// $cookieJar->setCookie( 'foo', 'bar', [ 'domain' => 'httpbin.org' ] );
// $cookieJar->setCookie( 'foo2', 'bar2', [ 'domain' => 'httpbin.org' ] );
}
public function testGetResponseHeaders() {
- $request = MWHttpRequest::factory( 'http://httpbin.org/response-headers?Foo=bar' );
+ $request = $this->factory->create( 'http://httpbin.org/response-headers?Foo=bar' );
$status = $request->execute();
$this->assertTrue( $status->isGood() );
$headers = array_change_key_case( $request->getResponseHeaders(), CASE_LOWER );
}
public function testSetHeader() {
- $request = MWHttpRequest::factory( 'http://httpbin.org/headers' );
+ $request = $this->factory->create( 'http://httpbin.org/headers' );
$request->setHeader( 'Foo', 'bar' );
$status = $request->execute();
$this->assertTrue( $status->isGood() );
}
public function testGetStatus() {
- $request = MWHttpRequest::factory( 'http://httpbin.org/status/418' );
+ $request = $this->factory->create( 'http://httpbin.org/status/418' );
$status = $request->execute();
$this->assertFalse( $status->isOK() );
$this->assertSame( $request->getStatus(), 418 );
}
public function testSetUserAgent() {
- $request = MWHttpRequest::factory( 'http://httpbin.org/user-agent' );
+ $request = $this->factory->create( 'http://httpbin.org/user-agent' );
$request->setUserAgent( 'foo' );
$status = $request->execute();
$this->assertTrue( $status->isGood() );
}
public function testSetData() {
- $request = MWHttpRequest::factory( 'http://httpbin.org/post', [ 'method' => 'POST' ] );
+ $request = $this->factory->create( 'http://httpbin.org/post', [ 'method' => 'POST' ] );
$request->setData( [ 'foo' => 'bar', 'foo2' => 'bar2' ] );
$status = $request->execute();
$this->assertTrue( $status->isGood() );
return;
}
- $request = MWHttpRequest::factory( 'http://httpbin.org/ip' );
+ $request = $this->factory->create( 'http://httpbin.org/ip' );
$data = '';
$request->setCallback( function ( $fh, $content ) use ( &$data ) {
$data .= $content;
}
public function testBasicAuthentication() {
- $request = MWHttpRequest::factory( 'http://httpbin.org/basic-auth/user/pass', [
+ $request = $this->factory->create( 'http://httpbin.org/basic-auth/user/pass', [
'username' => 'user',
'password' => 'pass',
] );
$this->assertTrue( $status->isGood() );
$this->assertResponseFieldValue( 'authenticated', true, $request );
- $request = MWHttpRequest::factory( 'http://httpbin.org/basic-auth/user/pass', [
+ $request = $this->factory->create( 'http://httpbin.org/basic-auth/user/pass', [
'username' => 'user',
'password' => 'wrongpass',
] );
}
public function testFactoryDefaults() {
- $request = MWHttpRequest::factory( 'http://acme.test' );
+ $request = $this->factory->create( 'http://acme.test' );
$this->assertInstanceOf( MWHttpRequest::class, $request );
}
/**
* Reset the Title-related services that need resetting
* for each test
+ *
+ * @todo We need to reset all services on every test
*/
private function resetTitleServices() {
$services = MediaWikiServices::getInstance();
$services->resetServiceForTesting( '_MediaWikiTitleCodec' );
$services->resetServiceForTesting( 'LinkRenderer' );
$services->resetServiceForTesting( 'LinkRendererFactory' );
+ $services->resetServiceForTesting( 'NamespaceInfo' );
}
/**
'comment' => $comment,
] );
}
+
+ /**
+ * Returns a PHPUnit constraint that matches anything other than a fixed set of values. This can
+ * be used to whitelist values, e.g.
+ * $mock->expects( $this->never() )->method( $this->anythingBut( 'foo', 'bar' ) );
+ * which will throw if any unexpected method is called.
+ *
+ * @param mixed ...$values Values that are not matched
+ */
+ protected function anythingBut( ...$values ) {
+ return $this->logicalNot( $this->logicalOr(
+ ...array_map( [ $this, 'matches' ], $values )
+ ) );
+ }
}
->value['revision'];
$store = MediaWikiServices::getInstance()->getRevisionStore();
- $result = $store->getTimestampFromId(
- $page->getTitle(),
- $rev->getId()
- );
+ $result = $store->getTimestampFromId( $rev->getId() );
$this->assertSame( $rev->getTimestamp(), $result );
}
->value['revision'];
$store = MediaWikiServices::getInstance()->getRevisionStore();
- $result = $store->getTimestampFromId(
- $page->getTitle(),
- $rev->getId() + 1
- );
+ $result = $store->getTimestampFromId( $rev->getId() + 1 );
$this->assertFalse( $result );
}
$this->assertEquals( $latestRevision, $newRevision->getPrevious()->getId() );
}
+ /**
+ * @covers Title::getPreviousRevisionID
+ * @covers Title::getRelativeRevisionID
+ * @covers MediaWiki\Revision\RevisionStore::getPreviousRevision
+ * @covers MediaWiki\Revision\RevisionStore::getRelativeRevision
+ */
+ public function testTitleGetPreviousRevisionID() {
+ $oldestId = $this->testPage->getOldestRevision()->getId();
+ $latestId = $this->testPage->getLatest();
+
+ $title = $this->testPage->getTitle();
+
+ $this->assertFalse( $title->getPreviousRevisionID( $oldestId ) );
+
+ $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
+ $newId = $this->testPage->getRevision()->getId();
+
+ $this->assertEquals( $latestId, $title->getPreviousRevisionID( $newId ) );
+ }
+
+ /**
+ * @covers Title::getPreviousRevisionID
+ * @covers Title::getRelativeRevisionID
+ */
+ public function testTitleGetPreviousRevisionID_invalid() {
+ $this->assertFalse( $this->testPage->getTitle()->getPreviousRevisionID( 123456789 ) );
+ }
+
/**
* @covers Revision::getNext
*/
$this->assertEquals( $rev2->getId(), $rev1->getNext()->getId() );
}
+ /**
+ * @covers Title::getNextRevisionID
+ * @covers Title::getRelativeRevisionID
+ * @covers MediaWiki\Revision\RevisionStore::getNextRevision
+ * @covers MediaWiki\Revision\RevisionStore::getRelativeRevision
+ */
+ public function testTitleGetNextRevisionID() {
+ $title = $this->testPage->getTitle();
+
+ $origId = $this->testPage->getLatest();
+
+ $this->assertFalse( $title->getNextRevisionID( $origId ) );
+
+ $this->testPage->doEditContent( new WikitextContent( __METHOD__ ), __METHOD__ );
+ $newId = $this->testPage->getLatest();
+
+ $this->assertSame( $this->testPage->getLatest(), $title->getNextRevisionID( $origId ) );
+ }
+
+ /**
+ * @covers Title::getNextRevisionID
+ * @covers Title::getRelativeRevisionID
+ */
+ public function testTitleGetNextRevisionID_invalid() {
+ $this->assertFalse( $this->testPage->getTitle()->getNextRevisionID( 123456789 ) );
+ }
+
/**
* @covers Revision::newNullRevision
*/
]
] );
+ // Reset services since we modified $wgLocalInterwikis
$this->overrideMwServices();
}
<?php
+use MediaWiki\MediaWikiServices;
use Wikimedia\TestingAccessWrapper;
/**
$url = $this->backend->getFileHttpUrl( [ 'src' => $source ] );
if ( $url !== null ) { // supported
- $data = Http::request( "GET", $url, [], __METHOD__ );
+ $data = MediaWikiServices::getInstance()->getHttpRequestFactory()->
+ get( $url, [], __METHOD__ );
$this->assertEquals( $content, $data,
"HTTP GET of URL has right contents ($backendName)." );
}
* @covers Http::getProxy
*/
public function testGetProxy() {
+ $this->hideDeprecated( 'Http::getProxy' );
+
$this->setMwGlobals( 'wgHTTPProxy', false );
$this->assertEquals(
'',
* @return DefaultPreferencesFactory
*/
protected function getPreferencesFactory() {
+ $mockNsInfo = $this->createMock( NamespaceInfo::class );
+ $mockNsInfo->method( 'getValidNamespaces' )->willReturn( [
+ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK
+ ] );
+ $mockNsInfo->expects( $this->never() )
+ ->method( $this->anythingBut( 'getValidNamespaces', '__destruct' ) );
+
return new DefaultPreferencesFactory(
new ServiceOptions( DefaultPreferencesFactory::$constructorOptions, $this->config ),
new Language(),
AuthManager::singleton(),
- MediaWikiServices::getInstance()->getLinkRenderer()
+ MediaWikiServices::getInstance()->getLinkRenderer(),
+ $mockNsInfo
);
}
* @file
*/
-use MediaWiki\MediaWikiServices;
+use MediaWiki\Config\ServiceOptions;
class NamespaceInfoTest extends MediaWikiTestCase {
-
- /** @var NamespaceInfo */
- private $obj;
-
- protected function setUp() {
- parent::setUp();
-
- $this->setMwGlobals( [
- 'wgContentNamespaces' => [ NS_MAIN ],
- 'wgNamespacesWithSubpages' => [
+ private function newObj( array $options = [] ) : NamespaceInfo {
+ $defaults = [
+ 'AllowImageMoving' => true,
+ 'CanonicalNamespaceNames' => [
+ NS_TALK => 'Talk',
+ NS_USER => 'User',
+ NS_USER_TALK => 'User_talk',
+ NS_SPECIAL => 'Special',
+ NS_MEDIA => 'Media',
+ ],
+ 'CapitalLinkOverrides' => [],
+ 'CapitalLinks' => true,
+ 'ContentNamespaces' => [ NS_MAIN ],
+ 'ExtraNamespaces' => [],
+ 'ExtraSignatureNamespaces' => [],
+ 'NamespaceContentModels' => [],
+ 'NamespaceProtection' => [],
+ 'NamespacesWithSubpages' => [
NS_TALK => true,
NS_USER => true,
NS_USER_TALK => true,
],
- 'wgCapitalLinks' => true,
- 'wgCapitalLinkOverrides' => [],
- 'wgNonincludableNamespaces' => [],
- ] );
-
- $this->obj = MediaWikiServices::getInstance()->getNamespaceInfo();
+ 'NonincludableNamespaces' => [],
+ 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop' ],
+ ];
+ return new NamespaceInfo(
+ new ServiceOptions( NamespaceInfo::$constructorOptions, $options, $defaults ) );
}
/**
- * @todo Write more texts, handle $wgAllowImageMoving setting
+ * @todo Write more tests, handle $wgAllowImageMoving setting
* @covers NamespaceInfo::isMovable
*/
public function testIsMovable() {
- $this->assertFalse( $this->obj->isMovable( NS_SPECIAL ) );
+ $this->assertFalse( $this->newObj()->isMovable( NS_SPECIAL ) );
}
private function assertIsSubject( $ns ) {
- $this->assertTrue( $this->obj->isSubject( $ns ) );
+ $this->assertTrue( $this->newObj()->isSubject( $ns ) );
}
private function assertIsNotSubject( $ns ) {
- $this->assertFalse( $this->obj->isSubject( $ns ) );
+ $this->assertFalse(
+ $this->newObj()->isSubject( $ns ) );
}
/**
- * Please make sure to change testIsTalk() if you change the assertions below
+ * @param int $ns
+ * @param bool $expected
+ * @dataProvider provideIsSubject
* @covers NamespaceInfo::isSubject
*/
- public function testIsSubject() {
- // Special namespaces
- $this->assertIsSubject( NS_MEDIA );
- $this->assertIsSubject( NS_SPECIAL );
-
- // Subject pages
- $this->assertIsSubject( NS_MAIN );
- $this->assertIsSubject( NS_USER );
- $this->assertIsSubject( 100 ); # user defined
-
- // Talk pages
- $this->assertIsNotSubject( NS_TALK );
- $this->assertIsNotSubject( NS_USER_TALK );
- $this->assertIsNotSubject( 101 ); # user defined
- }
-
- private function assertIsTalk( $ns ) {
- $this->assertTrue( $this->obj->isTalk( $ns ) );
- }
-
- private function assertIsNotTalk( $ns ) {
- $this->assertFalse( $this->obj->isTalk( $ns ) );
+ public function testIsSubject( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->isSubject( $ns ) );
}
/**
- * Reverse of testIsSubject().
- * Please update testIsSubject() if you change assertions below
+ * @param int $ns
+ * @param bool $expected
+ * @dataProvider provideIsSubject
* @covers NamespaceInfo::isTalk
*/
- public function testIsTalk() {
- // Special namespaces
- $this->assertIsNotTalk( NS_MEDIA );
- $this->assertIsNotTalk( NS_SPECIAL );
+ public function testIsTalk( $ns, $expected ) {
+ $this->assertSame( !$expected, $this->newObj()->isTalk( $ns ) );
+ }
- // Subject pages
- $this->assertIsNotTalk( NS_MAIN );
- $this->assertIsNotTalk( NS_USER );
- $this->assertIsNotTalk( 100 ); # user defined
+ public function provideIsSubject() {
+ return [
+ // Special namespaces
+ [ NS_MEDIA, true ],
+ [ NS_SPECIAL, true ],
- // Talk pages
- $this->assertIsTalk( NS_TALK );
- $this->assertIsTalk( NS_USER_TALK );
- $this->assertIsTalk( 101 ); # user defined
+ // Subject pages
+ [ NS_MAIN, true ],
+ [ NS_USER, true ],
+ [ 100, true ],
+
+ // Talk pages
+ [ NS_TALK, false ],
+ [ NS_USER_TALK, false ],
+ [ 101, false ],
+ ];
}
/**
*/
public function testGetSubject() {
// Special namespaces are their own subjects
- $this->assertEquals( NS_MEDIA, $this->obj->getSubject( NS_MEDIA ) );
- $this->assertEquals( NS_SPECIAL, $this->obj->getSubject( NS_SPECIAL ) );
+ $obj = $this->newObj();
+ $this->assertEquals( NS_MEDIA, $obj->getSubject( NS_MEDIA ) );
+ $this->assertEquals( NS_SPECIAL, $obj->getSubject( NS_SPECIAL ) );
- $this->assertEquals( NS_MAIN, $this->obj->getSubject( NS_TALK ) );
- $this->assertEquals( NS_USER, $this->obj->getSubject( NS_USER_TALK ) );
+ $this->assertEquals( NS_MAIN, $obj->getSubject( NS_TALK ) );
+ $this->assertEquals( NS_USER, $obj->getSubject( NS_USER_TALK ) );
}
/**
* @covers NamespaceInfo::getTalk
*/
public function testGetTalk() {
- $this->assertEquals( NS_TALK, $this->obj->getTalk( NS_MAIN ) );
- $this->assertEquals( NS_TALK, $this->obj->getTalk( NS_TALK ) );
- $this->assertEquals( NS_USER_TALK, $this->obj->getTalk( NS_USER ) );
- $this->assertEquals( NS_USER_TALK, $this->obj->getTalk( NS_USER_TALK ) );
+ $obj = $this->newObj();
+ $this->assertEquals( NS_TALK, $obj->getTalk( NS_MAIN ) );
+ $this->assertEquals( NS_TALK, $obj->getTalk( NS_TALK ) );
+ $this->assertEquals( NS_USER_TALK, $obj->getTalk( NS_USER ) );
+ $this->assertEquals( NS_USER_TALK, $obj->getTalk( NS_USER_TALK ) );
}
/**
* @covers NamespaceInfo::getTalk
*/
public function testGetTalkExceptionsForNsMedia() {
- $this->assertNull( $this->obj->getTalk( NS_MEDIA ) );
+ $this->assertNull( $this->newObj()->getTalk( NS_MEDIA ) );
}
/**
* @covers NamespaceInfo::getTalk
*/
public function testGetTalkExceptionsForNsSpecial() {
- $this->assertNull( $this->obj->getTalk( NS_SPECIAL ) );
+ $this->assertNull( $this->newObj()->getTalk( NS_SPECIAL ) );
}
/**
* @covers NamespaceInfo::getAssociated
*/
public function testGetAssociated() {
- $this->assertEquals( NS_TALK, $this->obj->getAssociated( NS_MAIN ) );
- $this->assertEquals( NS_MAIN, $this->obj->getAssociated( NS_TALK ) );
+ $this->assertEquals( NS_TALK, $this->newObj()->getAssociated( NS_MAIN ) );
+ $this->assertEquals( NS_MAIN, $this->newObj()->getAssociated( NS_TALK ) );
}
# ## Exceptions with getAssociated()
* @covers NamespaceInfo::getAssociated
*/
public function testGetAssociatedExceptionsForNsMedia() {
- $this->assertNull( $this->obj->getAssociated( NS_MEDIA ) );
+ $this->assertNull( $this->newObj()->getAssociated( NS_MEDIA ) );
}
/**
* @covers NamespaceInfo::getAssociated
*/
public function testGetAssociatedExceptionsForNsSpecial() {
- $this->assertNull( $this->obj->getAssociated( NS_SPECIAL ) );
+ $this->assertNull( $this->newObj()->getAssociated( NS_SPECIAL ) );
}
/**
* @covers NamespaceInfo::equals
*/
public function testEquals() {
- $this->assertTrue( $this->obj->equals( NS_MAIN, NS_MAIN ) );
- $this->assertTrue( $this->obj->equals( NS_MAIN, 0 ) ); // In case we make NS_MAIN 'MAIN'
- $this->assertTrue( $this->obj->equals( NS_USER, NS_USER ) );
- $this->assertTrue( $this->obj->equals( NS_USER, 2 ) );
- $this->assertTrue( $this->obj->equals( NS_USER_TALK, NS_USER_TALK ) );
- $this->assertTrue( $this->obj->equals( NS_SPECIAL, NS_SPECIAL ) );
- $this->assertFalse( $this->obj->equals( NS_MAIN, NS_TALK ) );
- $this->assertFalse( $this->obj->equals( NS_USER, NS_USER_TALK ) );
- $this->assertFalse( $this->obj->equals( NS_PROJECT, NS_TEMPLATE ) );
+ $obj = $this->newObj();
+ $this->assertTrue( $obj->equals( NS_MAIN, NS_MAIN ) );
+ $this->assertTrue( $obj->equals( NS_MAIN, 0 ) ); // In case we make NS_MAIN 'MAIN'
+ $this->assertTrue( $obj->equals( NS_USER, NS_USER ) );
+ $this->assertTrue( $obj->equals( NS_USER, 2 ) );
+ $this->assertTrue( $obj->equals( NS_USER_TALK, NS_USER_TALK ) );
+ $this->assertTrue( $obj->equals( NS_SPECIAL, NS_SPECIAL ) );
+ $this->assertFalse( $obj->equals( NS_MAIN, NS_TALK ) );
+ $this->assertFalse( $obj->equals( NS_USER, NS_USER_TALK ) );
+ $this->assertFalse( $obj->equals( NS_PROJECT, NS_TEMPLATE ) );
}
/**
+ * @param int $ns1
+ * @param int $ns2
+ * @param bool $expected
+ * @dataProvider provideSubjectEquals
* @covers NamespaceInfo::subjectEquals
*/
- public function testSubjectEquals() {
- $this->assertSameSubject( NS_MAIN, NS_MAIN );
- $this->assertSameSubject( NS_MAIN, 0 ); // In case we make NS_MAIN 'MAIN'
- $this->assertSameSubject( NS_USER, NS_USER );
- $this->assertSameSubject( NS_USER, 2 );
- $this->assertSameSubject( NS_USER_TALK, NS_USER_TALK );
- $this->assertSameSubject( NS_SPECIAL, NS_SPECIAL );
- $this->assertSameSubject( NS_MAIN, NS_TALK );
- $this->assertSameSubject( NS_USER, NS_USER_TALK );
+ public function testSubjectEquals( $ns1, $ns2, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->subjectEquals( $ns1, $ns2 ) );
+ }
- $this->assertDifferentSubject( NS_PROJECT, NS_TEMPLATE );
- $this->assertDifferentSubject( NS_SPECIAL, NS_MAIN );
+ public function provideSubjectEquals() {
+ return [
+ [ NS_MAIN, NS_MAIN, true ],
+ // In case we make NS_MAIN 'MAIN'
+ [ NS_MAIN, 0, true ],
+ [ NS_USER, NS_USER, true ],
+ [ NS_USER, 2, true ],
+ [ NS_USER_TALK, NS_USER_TALK, true ],
+ [ NS_SPECIAL, NS_SPECIAL, true ],
+ [ NS_MAIN, NS_TALK, true ],
+ [ NS_USER, NS_USER_TALK, true ],
+
+ [ NS_PROJECT, NS_TEMPLATE, false ],
+ [ NS_SPECIAL, NS_MAIN, false ],
+ [ NS_MEDIA, NS_SPECIAL, false ],
+ [ NS_SPECIAL, NS_MEDIA, false ],
+ ];
}
/**
- * @covers NamespaceInfo::subjectEquals
+ * @dataProvider provideHasTalkNamespace
+ * @covers NamespaceInfo::hasTalkNamespace
+ *
+ * @param int $ns
+ * @param bool $expected
*/
- public function testSpecialAndMediaAreDifferentSubjects() {
- $this->assertDifferentSubject(
- NS_MEDIA, NS_SPECIAL,
- "NS_MEDIA and NS_SPECIAL are different subject namespaces"
- );
- $this->assertDifferentSubject(
- NS_SPECIAL, NS_MEDIA,
- "NS_SPECIAL and NS_MEDIA are different subject namespaces"
- );
+ public function testHasTalkNamespace( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->hasTalkNamespace( $ns ) );
}
public function provideHasTalkNamespace() {
}
/**
- * @dataProvider provideHasTalkNamespace
- * @covers NamespaceInfo::hasTalkNamespace
- *
- * @param int $index
+ * @param int $ns
* @param bool $expected
+ * @param array $contentNamespaces
+ * @covers NamespaceInfo::isContent
+ * @dataProvider provideIsContent
*/
- public function testHasTalkNamespace( $index, $expected ) {
- $actual = $this->obj->hasTalkNamespace( $index );
- $this->assertSame( $actual, $expected, "NS $index" );
+ public function testIsContent( $ns, $expected, $contentNamespaces = [ NS_MAIN ] ) {
+ $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
+ $this->assertSame( $expected, $obj->isContent( $ns ) );
}
- private function assertIsContent( $ns ) {
- $this->assertTrue( $this->obj->isContent( $ns ) );
- }
-
- private function assertIsNotContent( $ns ) {
- $this->assertFalse( $this->obj->isContent( $ns ) );
+ public function provideIsContent() {
+ return [
+ [ NS_MAIN, true ],
+ [ NS_MEDIA, false ],
+ [ NS_SPECIAL, false ],
+ [ NS_TALK, false ],
+ [ NS_USER, false ],
+ [ NS_CATEGORY, false ],
+ [ 100, false ],
+ [ 100, true, [ NS_MAIN, 100, 252 ] ],
+ [ 252, true, [ NS_MAIN, 100, 252 ] ],
+ [ NS_MAIN, true, [ NS_MAIN, 100, 252 ] ],
+ // NS_MAIN is always content
+ [ NS_MAIN, true, [] ],
+ ];
}
/**
- * @covers NamespaceInfo::isContent
+ * @param int $ns
+ * @param bool $expected
+ * @covers NamespaceInfo::isWatchable
+ * @dataProvider provideIsWatchable
*/
- public function testIsContent() {
- // NS_MAIN is a content namespace per DefaultSettings.php
- // and per function definition.
-
- $this->assertIsContent( NS_MAIN );
-
- // Other namespaces which are not expected to be content
-
- $this->assertIsNotContent( NS_MEDIA );
- $this->assertIsNotContent( NS_SPECIAL );
- $this->assertIsNotContent( NS_TALK );
- $this->assertIsNotContent( NS_USER );
- $this->assertIsNotContent( NS_CATEGORY );
- $this->assertIsNotContent( 100 );
+ public function testIsWatchable( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->isWatchable( $ns ) );
}
- /**
- * Similar to testIsContent() but alters the $wgContentNamespaces
- * global variable.
- * @covers NamespaceInfo::isContent
- */
- public function testIsContentAdvanced() {
- global $wgContentNamespaces;
-
- // Test that user defined namespace #252 is not content
- $this->assertIsNotContent( 252 );
-
- // Bless namespace # 252 as a content namespace
- $wgContentNamespaces[] = 252;
-
- $this->assertIsContent( 252 );
-
- // Makes sure NS_MAIN was not impacted
- $this->assertIsContent( NS_MAIN );
- }
+ public function provideIsWatchable() {
+ return [
+ // Specials namespaces are not watchable
+ [ NS_MEDIA, false ],
+ [ NS_SPECIAL, false ],
- private function assertIsWatchable( $ns ) {
- $this->assertTrue( $this->obj->isWatchable( $ns ) );
- }
+ // Core defined namespaces are watchables
+ [ NS_MAIN, true ],
+ [ NS_TALK, true ],
- private function assertIsNotWatchable( $ns ) {
- $this->assertFalse( $this->obj->isWatchable( $ns ) );
+ // Additional, user defined namespaces are watchables
+ [ 100, true ],
+ [ 101, true ],
+ ];
}
/**
- * @covers NamespaceInfo::isWatchable
+ * @param int $ns
+ * @param int $expected
+ * @param array|null $namespacesWithSubpages To pass to constructor
+ * @covers NamespaceInfo::hasSubpages
+ * @dataProvider provideHasSubpages
*/
- public function testIsWatchable() {
- // Specials namespaces are not watchable
- $this->assertIsNotWatchable( NS_MEDIA );
- $this->assertIsNotWatchable( NS_SPECIAL );
-
- // Core defined namespaces are watchables
- $this->assertIsWatchable( NS_MAIN );
- $this->assertIsWatchable( NS_TALK );
-
- // Additional, user defined namespaces are watchables
- $this->assertIsWatchable( 100 );
- $this->assertIsWatchable( 101 );
+ public function testHasSubpages( $ns, $expected, array $namespacesWithSubpages = null ) {
+ $obj = $this->newObj( $namespacesWithSubpages
+ ? [ 'NamespacesWithSubpages' => $namespacesWithSubpages ]
+ : [] );
+ $this->assertSame( $expected, $obj->hasSubpages( $ns ) );
}
- private function assertHasSubpages( $ns ) {
- $this->assertTrue( $this->obj->hasSubpages( $ns ) );
- }
+ public function provideHasSubpages() {
+ return [
+ // Special namespaces:
+ [ NS_MEDIA, false ],
+ [ NS_SPECIAL, false ],
- private function assertHasNotSubpages( $ns ) {
- $this->assertFalse( $this->obj->hasSubpages( $ns ) );
+ // Namespaces without subpages
+ [ NS_MAIN, false ],
+ [ NS_MAIN, true, [ NS_MAIN => true ] ],
+ [ NS_MAIN, false, [ NS_MAIN => false ] ],
+
+ // Some namespaces with subpages
+ [ NS_TALK, true ],
+ [ NS_USER, true ],
+ [ NS_USER_TALK, true ],
+ ];
}
/**
- * @covers NamespaceInfo::hasSubpages
+ * @param $contentNamespaces To pass to constructor
+ * @param array $expected
+ * @dataProvider provideGetContentNamespaces
+ * @covers NamespaceInfo::getContentNamespaces
*/
- public function testHasSubpages() {
- global $wgNamespacesWithSubpages;
-
- // Special namespaces:
- $this->assertHasNotSubpages( NS_MEDIA );
- $this->assertHasNotSubpages( NS_SPECIAL );
+ public function testGetContentNamespaces( $contentNamespaces, array $expected ) {
+ $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
+ $this->assertSame( $expected, $obj->getContentNamespaces() );
+ }
- // Namespaces without subpages
- $this->assertHasNotSubpages( NS_MAIN );
+ public function provideGetContentNamespaces() {
+ return [
+ // Non-array
+ [ '', [ NS_MAIN ] ],
+ [ false, [ NS_MAIN ] ],
+ [ null, [ NS_MAIN ] ],
+ [ 5, [ NS_MAIN ] ],
- $wgNamespacesWithSubpages[NS_MAIN] = true;
- $this->assertHasSubpages( NS_MAIN );
+ // Empty array
+ [ [], [ NS_MAIN ] ],
- $wgNamespacesWithSubpages[NS_MAIN] = false;
- $this->assertHasNotSubpages( NS_MAIN );
+ // NS_MAIN is forced to be content even if unwanted
+ [ [ NS_USER, NS_CATEGORY ], [ NS_MAIN, NS_USER, NS_CATEGORY ] ],
- // Some namespaces with subpages
- $this->assertHasSubpages( NS_TALK );
- $this->assertHasSubpages( NS_USER );
- $this->assertHasSubpages( NS_USER_TALK );
- }
-
- /**
- * @covers NamespaceInfo::getContentNamespaces
- */
- public function testGetContentNamespaces() {
- global $wgContentNamespaces;
-
- $this->assertEquals(
- [ NS_MAIN ],
- $this->obj->getContentNamespaces(),
- '$wgContentNamespaces is an array with only NS_MAIN by default'
- );
-
- # test !is_array( $wgcontentNamespaces )
- $wgContentNamespaces = '';
- $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
-
- $wgContentNamespaces = false;
- $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
-
- $wgContentNamespaces = null;
- $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
-
- $wgContentNamespaces = 5;
- $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
-
- # test $wgContentNamespaces === []
- $wgContentNamespaces = [];
- $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
-
- # test !in_array( NS_MAIN, $wgContentNamespaces )
- $wgContentNamespaces = [ NS_USER, NS_CATEGORY ];
- $this->assertEquals(
- [ NS_MAIN, NS_USER, NS_CATEGORY ],
- $this->obj->getContentNamespaces(),
- 'NS_MAIN is forced in $wgContentNamespaces even if unwanted'
- );
-
- # test other cases, return $wgcontentNamespaces as is
- $wgContentNamespaces = [ NS_MAIN ];
- $this->assertEquals(
- [ NS_MAIN ],
- $this->obj->getContentNamespaces()
- );
-
- $wgContentNamespaces = [ NS_MAIN, NS_USER, NS_CATEGORY ];
- $this->assertEquals(
- [ NS_MAIN, NS_USER, NS_CATEGORY ],
- $this->obj->getContentNamespaces()
- );
+ // In other cases, return as-is
+ [ [ NS_MAIN ], [ NS_MAIN ] ],
+ [ [ NS_MAIN, NS_USER, NS_CATEGORY ], [ NS_MAIN, NS_USER, NS_CATEGORY ] ],
+ ];
}
/**
* @covers NamespaceInfo::getSubjectNamespaces
*/
public function testGetSubjectNamespaces() {
- $subjectsNS = $this->obj->getSubjectNamespaces();
+ $subjectsNS = $this->newObj()->getSubjectNamespaces();
$this->assertContains( NS_MAIN, $subjectsNS,
"Talk namespaces should have NS_MAIN" );
$this->assertNotContains( NS_TALK, $subjectsNS,
* @covers NamespaceInfo::getTalkNamespaces
*/
public function testGetTalkNamespaces() {
- $talkNS = $this->obj->getTalkNamespaces();
+ $talkNS = $this->newObj()->getTalkNamespaces();
$this->assertContains( NS_TALK, $talkNS,
"Subject namespaces should have NS_TALK" );
$this->assertNotContains( NS_MAIN, $talkNS,
"Subject namespaces should not have NS_SPECIAL" );
}
- private function assertIsCapitalized( $ns ) {
- $this->assertTrue( $this->obj->isCapitalized( $ns ) );
- }
-
- private function assertIsNotCapitalized( $ns ) {
- $this->assertFalse( $this->obj->isCapitalized( $ns ) );
- }
-
/**
- * Some namespaces are always capitalized per code definition
- * in NamespaceInfo::$alwaysCapitalizedNamespaces
- * @covers NamespaceInfo::isCapitalized
- */
- public function testIsCapitalizedHardcodedAssertions() {
- // NS_MEDIA and NS_FILE are treated the same
- $this->assertEquals(
- $this->obj->isCapitalized( NS_MEDIA ),
- $this->obj->isCapitalized( NS_FILE ),
- 'NS_MEDIA and NS_FILE have same capitalization rendering'
- );
-
- // Boths are capitalized by default
- $this->assertIsCapitalized( NS_MEDIA );
- $this->assertIsCapitalized( NS_FILE );
-
- // Always capitalized namespaces
- // @see NamespaceInfo::$alwaysCapitalizedNamespaces
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
- }
-
- /**
- * Follows up for testIsCapitalizedHardcodedAssertions() but alter the
- * global $wgCapitalLink setting to have extended coverage.
- *
- * NamespaceInfo::isCapitalized() rely on two global settings:
- * $wgCapitalLinkOverrides = []; by default
- * $wgCapitalLinks = true; by default
- * This function test $wgCapitalLinks
- *
- * Global setting correctness is tested against the NS_PROJECT and
- * NS_PROJECT_TALK namespaces since they are not hardcoded nor specials
+ * @param int $ns
+ * @param bool $expected
+ * @param bool $capitalLinks To pass to constructor
+ * @param array $capitalLinkOverrides To pass to constructor
+ * @dataProvider provideIsCapitalized
* @covers NamespaceInfo::isCapitalized
*/
- public function testIsCapitalizedWithWgCapitalLinks() {
- $this->assertIsCapitalized( NS_PROJECT );
- $this->assertIsCapitalized( NS_PROJECT_TALK );
-
- $this->setMwGlobals( 'wgCapitalLinks', false );
-
- // hardcoded namespaces (see above function) are still capitalized:
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
-
- // setting is correctly applied
- $this->assertIsNotCapitalized( NS_PROJECT );
- $this->assertIsNotCapitalized( NS_PROJECT_TALK );
+ public function testIsCapitalized(
+ $ns, $expected, $capitalLinks = true, array $capitalLinkOverrides = []
+ ) {
+ $obj = $this->newObj( [
+ 'CapitalLinks' => $capitalLinks,
+ 'CapitalLinkOverrides' => $capitalLinkOverrides,
+ ] );
+ $this->assertSame( $expected, $obj->isCapitalized( $ns ) );
}
- /**
- * Counter part for NamespaceInfo::testIsCapitalizedWithWgCapitalLinks() now
- * testing the $wgCapitalLinkOverrides global.
- *
- * @todo split groups of assertions in autonomous testing functions
- * @covers NamespaceInfo::isCapitalized
- */
- public function testIsCapitalizedWithWgCapitalLinkOverrides() {
- global $wgCapitalLinkOverrides;
-
- // Test default settings
- $this->assertIsCapitalized( NS_PROJECT );
- $this->assertIsCapitalized( NS_PROJECT_TALK );
-
- // hardcoded namespaces (see above function) are capitalized:
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
-
- // Hardcoded namespaces remains capitalized
- $wgCapitalLinkOverrides[NS_SPECIAL] = false;
- $wgCapitalLinkOverrides[NS_USER] = false;
- $wgCapitalLinkOverrides[NS_MEDIAWIKI] = false;
-
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
-
- $wgCapitalLinkOverrides[NS_PROJECT] = false;
- $this->assertIsNotCapitalized( NS_PROJECT );
-
- $wgCapitalLinkOverrides[NS_PROJECT] = true;
- $this->assertIsCapitalized( NS_PROJECT );
-
- unset( $wgCapitalLinkOverrides[NS_PROJECT] );
- $this->assertIsCapitalized( NS_PROJECT );
+ public function provideIsCapitalized() {
+ return [
+ // Test default settings
+ [ NS_PROJECT, true ],
+ [ NS_PROJECT_TALK, true ],
+ [ NS_MEDIA, true ],
+ [ NS_FILE, true ],
+
+ // Always capitalized no matter what
+ [ NS_SPECIAL, true, false ],
+ [ NS_USER, true, false ],
+ [ NS_MEDIAWIKI, true, false ],
+
+ // Even with an override too
+ [ NS_SPECIAL, true, false, [ NS_SPECIAL => false ] ],
+ [ NS_USER, true, false, [ NS_USER => false ] ],
+ [ NS_MEDIAWIKI, true, false, [ NS_MEDIAWIKI => false ] ],
+
+ // Overrides work for other namespaces
+ [ NS_PROJECT, false, true, [ NS_PROJECT => false ] ],
+ [ NS_PROJECT, true, false, [ NS_PROJECT => true ] ],
+
+ // NS_MEDIA is treated like NS_FILE, and ignores NS_MEDIA overrides
+ [ NS_MEDIA, false, true, [ NS_FILE => false, NS_MEDIA => true ] ],
+ [ NS_MEDIA, true, false, [ NS_FILE => true, NS_MEDIA => false ] ],
+ [ NS_FILE, false, true, [ NS_FILE => false, NS_MEDIA => true ] ],
+ [ NS_FILE, true, false, [ NS_FILE => true, NS_MEDIA => false ] ],
+ ];
}
/**
* @covers NamespaceInfo::hasGenderDistinction
*/
public function testHasGenderDistinction() {
+ $obj = $this->newObj();
+
// Namespaces with gender distinctions
- $this->assertTrue( $this->obj->hasGenderDistinction( NS_USER ) );
- $this->assertTrue( $this->obj->hasGenderDistinction( NS_USER_TALK ) );
+ $this->assertTrue( $obj->hasGenderDistinction( NS_USER ) );
+ $this->assertTrue( $obj->hasGenderDistinction( NS_USER_TALK ) );
// Other ones, "genderless"
- $this->assertFalse( $this->obj->hasGenderDistinction( NS_MEDIA ) );
- $this->assertFalse( $this->obj->hasGenderDistinction( NS_SPECIAL ) );
- $this->assertFalse( $this->obj->hasGenderDistinction( NS_MAIN ) );
- $this->assertFalse( $this->obj->hasGenderDistinction( NS_TALK ) );
+ $this->assertFalse( $obj->hasGenderDistinction( NS_MEDIA ) );
+ $this->assertFalse( $obj->hasGenderDistinction( NS_SPECIAL ) );
+ $this->assertFalse( $obj->hasGenderDistinction( NS_MAIN ) );
+ $this->assertFalse( $obj->hasGenderDistinction( NS_TALK ) );
}
/**
* @covers NamespaceInfo::isNonincludable
*/
public function testIsNonincludable() {
- global $wgNonincludableNamespaces;
-
- $wgNonincludableNamespaces = [ NS_USER ];
-
- $this->assertTrue( $this->obj->isNonincludable( NS_USER ) );
- $this->assertFalse( $this->obj->isNonincludable( NS_TEMPLATE ) );
- }
-
- private function assertSameSubject( $ns1, $ns2, $msg = '' ) {
- $this->assertTrue( $this->obj->subjectEquals( $ns1, $ns2 ), $msg );
+ $obj = $this->newObj( [ 'NonincludableNamespaces' => [ NS_USER ] ] );
+ $this->assertTrue( $obj->isNonincludable( NS_USER ) );
+ $this->assertFalse( $obj->isNonincludable( NS_TEMPLATE ) );
}
- private function assertDifferentSubject( $ns1, $ns2, $msg = '' ) {
- $this->assertFalse( $this->obj->subjectEquals( $ns1, $ns2 ), $msg );
+ /**
+ * @dataProvider provideGetCategoryLinkType
+ * @covers NamespaceInfo::getCategoryLinkType
+ *
+ * @param int $ns
+ * @param string $expected
+ */
+ public function testGetCategoryLinkType( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->getCategoryLinkType( $ns ) );
}
public function provideGetCategoryLinkType() {
[ 101, 'page' ],
];
}
-
- /**
- * @dataProvider provideGetCategoryLinkType
- * @covers NamespaceInfo::getCategoryLinkType
- *
- * @param int $index
- * @param string $expected
- */
- public function testGetCategoryLinkType( $index, $expected ) {
- $actual = $this->obj->getCategoryLinkType( $index );
- $this->assertSame( $expected, $actual, "NS $index" );
- }
}
}
/**
+ * @covers User::isRegistered
* @covers User::isLoggedIn
* @covers User::isAnon
*/
public function testLoggedIn() {
$user = $this->getMutableTestUser()->getUser();
+ $this->assertTrue( $user->isRegistered() );
$this->assertTrue( $user->isLoggedIn() );
$this->assertFalse( $user->isAnon() );
// Non-existent users are perceived as anonymous
$user = User::newFromName( 'UTNonexistent' );
+ $this->assertFalse( $user->isRegistered() );
$this->assertFalse( $user->isLoggedIn() );
$this->assertTrue( $user->isAnon() );
$user = new User;
+ $this->assertFalse( $user->isRegistered() );
$this->assertFalse( $user->isLoggedIn() );
$this->assertTrue( $user->isAnon() );
}
<?php
+use MediaWiki\User\UserIdentityValue;
+
/**
* @author Addshore
*
$noWriteService = new NoWriteWatchedItemStore( $innerService );
$this->setExpectedException( DBReadOnlyError::class );
- $noWriteService->addWatch( $this->getTestSysop()->getUser(), new TitleValue( 0, 'Foo' ) );
+ $noWriteService->addWatch(
+ new UserIdentityValue( 1, 'MockUser', 0 ), new TitleValue( 0, 'Foo' ) );
}
public function testAddWatchBatchForUser() {
$noWriteService = new NoWriteWatchedItemStore( $innerService );
$this->setExpectedException( DBReadOnlyError::class );
- $noWriteService->addWatchBatchForUser( $this->getTestSysop()->getUser(), [] );
+ $noWriteService->addWatchBatchForUser( new UserIdentityValue( 1, 'MockUser', 0 ), [] );
}
public function testRemoveWatch() {
$noWriteService = new NoWriteWatchedItemStore( $innerService );
$this->setExpectedException( DBReadOnlyError::class );
- $noWriteService->removeWatch( $this->getTestSysop()->getUser(), new TitleValue( 0, 'Foo' ) );
+ $noWriteService->removeWatch(
+ new UserIdentityValue( 1, 'MockUser', 0 ), new TitleValue( 0, 'Foo' ) );
}
public function testSetNotificationTimestampsForUser() {
$this->setExpectedException( DBReadOnlyError::class );
$noWriteService->setNotificationTimestampsForUser(
- $this->getTestSysop()->getUser(),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
'timestamp',
[]
);
$this->setExpectedException( DBReadOnlyError::class );
$noWriteService->updateNotificationTimestamp(
- $this->getTestSysop()->getUser(),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'Foo' ),
'timestamp'
);
$this->setExpectedException( DBReadOnlyError::class );
$noWriteService->resetNotificationTimestamp(
- $this->getTestSysop()->getUser(),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
Title::newFromText( 'Foo' )
);
}
$noWriteService = new NoWriteWatchedItemStore( $innerService );
$return = $noWriteService->countWatchedItems(
- $this->getTestSysop()->getUser()
+ new UserIdentityValue( 1, 'MockUser', 0 )
);
$this->assertEquals( __METHOD__, $return );
}
$noWriteService = new NoWriteWatchedItemStore( $innerService );
$return = $noWriteService->getWatchedItem(
- $this->getTestSysop()->getUser(),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'Foo' )
);
$this->assertEquals( __METHOD__, $return );
$noWriteService = new NoWriteWatchedItemStore( $innerService );
$return = $noWriteService->loadWatchedItem(
- $this->getTestSysop()->getUser(),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'Foo' )
);
$this->assertEquals( __METHOD__, $return );
$noWriteService = new NoWriteWatchedItemStore( $innerService );
$return = $noWriteService->getWatchedItemsForUser(
- $this->getTestSysop()->getUser(),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
[]
);
$this->assertEquals( __METHOD__, $return );
$noWriteService = new NoWriteWatchedItemStore( $innerService );
$return = $noWriteService->isWatched(
- $this->getTestSysop()->getUser(),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'Foo' )
);
$this->assertEquals( __METHOD__, $return );
$noWriteService = new NoWriteWatchedItemStore( $innerService );
$return = $noWriteService->getNotificationTimestampsBatch(
- $this->getTestSysop()->getUser(),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
[ new TitleValue( 0, 'Foo' ) ]
);
$this->assertEquals( __METHOD__, $return );
$noWriteService = new NoWriteWatchedItemStore( $innerService );
$return = $noWriteService->countUnreadNotifications(
- $this->getTestSysop()->getUser(),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
88
);
$this->assertEquals( __METHOD__, $return );
<?php
+use MediaWiki\User\UserIdentityValue;
use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\LoadBalancer;
use Wikimedia\TestingAccessWrapper;
/**
* @param int $id
+ * @param string[] $extraMethods Extra methods that are expected might be called
* @return PHPUnit_Framework_MockObject_MockObject|User
*/
- private function getMockNonAnonUserWithId( $id ) {
+ private function getMockNonAnonUserWithId( $id, array $extraMethods = [] ) {
$mock = $this->getMockBuilder( User::class )->getMock();
- $mock->expects( $this->any() )
- ->method( 'isAnon' )
- ->will( $this->returnValue( false ) );
- $mock->expects( $this->any() )
- ->method( 'getId' )
- ->will( $this->returnValue( $id ) );
+ $mock->method( 'isRegistered' )->willReturn( true );
+ $mock->method( 'getId' )->willReturn( $id );
+ $methods = array_merge( [
+ 'isRegistered',
+ 'getId',
+ ], $extraMethods );
+ $mock->expects( $this->never() )->method( $this->anythingBut( ...$methods ) );
return $mock;
}
/**
* @param int $id
+ * @param string[] $extraMethods Extra methods that are expected might be called
* @return PHPUnit_Framework_MockObject_MockObject|User
*/
- private function getMockUnrestrictedNonAnonUserWithId( $id ) {
- $mock = $this->getMockNonAnonUserWithId( $id );
- $mock->expects( $this->any() )
- ->method( 'isAllowed' )
- ->will( $this->returnValue( true ) );
- $mock->expects( $this->any() )
- ->method( 'isAllowedAny' )
- ->will( $this->returnValue( true ) );
- $mock->expects( $this->any() )
- ->method( 'useRCPatrol' )
- ->will( $this->returnValue( true ) );
+ private function getMockUnrestrictedNonAnonUserWithId( $id, array $extraMethods = [] ) {
+ $mock = $this->getMockNonAnonUserWithId( $id,
+ array_merge( [ 'isAllowed', 'isAllowedAny', 'useRCPatrol' ], $extraMethods ) );
+ $mock->method( 'isAllowed' )->willReturn( true );
+ $mock->method( 'isAllowedAny' )->willReturn( true );
+ $mock->method( 'useRCPatrol' )->willReturn( true );
return $mock;
}
* @return PHPUnit_Framework_MockObject_MockObject|User
*/
private function getMockNonAnonUserWithIdAndRestrictedPermissions( $id, $notAllowedAction ) {
- $mock = $this->getMockNonAnonUserWithId( $id );
+ $mock = $this->getMockNonAnonUserWithId( $id,
+ [ 'isAllowed', 'isAllowedAny', 'useRCPatrol', 'useNPPatrol' ] );
- $mock->expects( $this->any() )
- ->method( 'isAllowed' )
+ $mock->method( 'isAllowed' )
->will( $this->returnCallback( function ( $action ) use ( $notAllowedAction ) {
return $action !== $notAllowedAction;
} ) );
- $mock->expects( $this->any() )
- ->method( 'isAllowedAny' )
+ $mock->method( 'isAllowedAny' )
->will( $this->returnCallback( function ( ...$actions ) use ( $notAllowedAction ) {
return !in_array( $notAllowedAction, $actions );
} ) );
+ $mock->method( 'useRCPatrol' )->willReturn( false );
+ $mock->method( 'useNPPatrol' )->willReturn( false );
return $mock;
}
* @return PHPUnit_Framework_MockObject_MockObject|User
*/
private function getMockNonAnonUserWithIdAndNoPatrolRights( $id ) {
- $mock = $this->getMockNonAnonUserWithId( $id );
+ $mock = $this->getMockNonAnonUserWithId( $id,
+ [ 'isAllowed', 'isAllowedAny', 'useRCPatrol', 'useNPPatrol' ] );
$mock->expects( $this->any() )
->method( 'isAllowed' )
return $mock;
}
- private function getMockAnonUser() {
- $mock = $this->getMockBuilder( User::class )->getMock();
- $mock->expects( $this->any() )
- ->method( 'isAnon' )
- ->will( $this->returnValue( true ) );
- return $mock;
- }
-
private function getFakeRow( array $rowValues ) {
$fakeRow = new stdClass();
foreach ( $rowValues as $valueName => $value ) {
$queryService = $this->newService( $mockDb );
$user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
- $otherUser = $this->getMockUnrestrictedNonAnonUserWithId( 2 );
+ $otherUser = $this->getMockUnrestrictedNonAnonUserWithId( 2, [ 'getOption' ] );
$otherUser->expects( $this->once() )
->method( 'getOption' )
->with( 'watchlisttoken' )
$queryService = $this->newService( $mockDb );
$user = $this->getMockUnrestrictedNonAnonUserWithId( 1 );
- $otherUser = $this->getMockUnrestrictedNonAnonUserWithId( 2 );
+ $otherUser = $this->getMockUnrestrictedNonAnonUserWithId( 2, [ 'getOption' ] );
$otherUser->expects( $this->once() )
->method( 'getOption' )
->with( 'watchlisttoken' )
$queryService = $this->newService( $mockDb );
- $items = $queryService->getWatchedItemsForUser( $this->getMockAnonUser() );
+ $items = $queryService->getWatchedItemsForUser(
+ new UserIdentityValue( 0, 'AnonUser', 0 ) );
$this->assertEmpty( $items );
}
<?php
use MediaWiki\Linker\LinkTarget;
+use MediaWiki\User\UserIdentityValue;
use Wikimedia\Rdbms\LBFactory;
use Wikimedia\Rdbms\LoadBalancer;
use Wikimedia\ScopedCallback;
return $mock;
}
- /**
- * @param int $id
- * @return PHPUnit_Framework_MockObject_MockObject|User
- */
- private function getMockNonAnonUserWithId( $id ) {
- $mock = $this->createMock( User::class );
- $mock->expects( $this->any() )
- ->method( 'isAnon' )
- ->will( $this->returnValue( false ) );
- $mock->expects( $this->any() )
- ->method( 'getId' )
- ->will( $this->returnValue( $id ) );
- $mock->expects( $this->any() )
- ->method( 'getUserPage' )
- ->will( $this->returnValue( Title::makeTitle( NS_USER, 'MockUser' ) ) );
- return $mock;
- }
-
- /**
- * @return User
- */
- private function getAnonUser() {
- return User::newFromName( 'Anon_User' );
- }
-
private function getFakeRow( array $rowValues ) {
$fakeRow = new stdClass();
foreach ( $rowValues as $valueName => $value ) {
}
public function testClearWatchedItems() {
- $user = $this->getMockNonAnonUserWithId( 7 );
+ $user = new UserIdentityValue( 7, 'MockUser', 0 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
}
public function testClearWatchedItems_tooManyItemsWatched() {
- $user = $this->getMockNonAnonUserWithId( 7 );
+ $user = new UserIdentityValue( 7, 'MockUser', 0 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->once() )
}
public function testCountWatchedItems() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 1 ) )
}
public function testCountUnreadNotifications() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 1 ) )
* @dataProvider provideIntWithDbUnsafeVersion
*/
public function testCountUnreadNotifications_withUnreadLimit_overLimit( $limit ) {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 1 ) )
* @dataProvider provideIntWithDbUnsafeVersion
*/
public function testCountUnreadNotifications_withUnreadLimit_underLimit( $limit ) {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->exactly( 1 ) )
);
$store->duplicateEntry(
- Title::newFromText( 'Old_Title' ),
- Title::newFromText( 'New_Title' )
+ new TitleValue( 0, 'Old_Title' ),
+ new TitleValue( 0, 'New_Title' )
);
}
);
$store->duplicateEntry(
- Title::newFromText( 'Old_Title' ),
- Title::newFromText( 'New_Title' )
+ new TitleValue( 0, 'Old_Title' ),
+ new TitleValue( 0, 'New_Title' )
);
}
);
$store->duplicateAllAssociatedEntries(
- Title::newFromText( 'Old_Title' ),
- Title::newFromText( 'New_Title' )
+ new TitleValue( 0, 'Old_Title' ),
+ new TitleValue( 0, 'New_Title' )
);
}
public function provideLinkTargetPairs() {
return [
- [ Title::newFromText( 'Old_Title' ), Title::newFromText( 'New_Title' ) ],
+ [ new TitleValue( 0, 'Old_Title' ), new TitleValue( 0, 'New_Title' ) ],
[ new TitleValue( 0, 'Old_Title' ), new TitleValue( 0, 'New_Title' ) ],
];
}
);
$store->addWatch(
- $this->getMockNonAnonUserWithId( 1 ),
- Title::newFromText( 'Some_Page' )
+ new UserIdentityValue( 1, 'MockUser', 0 ),
+ new TitleValue( 0, 'Some_Page' )
);
}
);
$store->addWatch(
- $this->getAnonUser(),
- Title::newFromText( 'Some_Page' )
+ new UserIdentityValue( 0, 'AnonUser', 0 ),
+ new TitleValue( 0, 'Some_Page' )
);
}
$this->assertFalse(
$store->addWatchBatchForUser(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
[ new TitleValue( 0, 'Some_Page' ), new TitleValue( 1, 'Some_Page' ) ]
)
);
$this->getMockReadOnlyMode()
);
- $mockUser = $this->getMockNonAnonUserWithId( 1 );
+ $mockUser = new UserIdentityValue( 1, 'MockUser', 0 );
$this->assertTrue(
$store->addWatchBatchForUser(
$this->assertFalse(
$store->addWatchBatchForUser(
- $this->getAnonUser(),
+ new UserIdentityValue( 0, 'AnonUser', 0 ),
[ new TitleValue( 0, 'Other_Page' ) ]
)
);
}
public function testAddWatchBatchReturnsTrue_whenGivenEmptyList() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$mockDb = $this->getMockDb();
$mockDb->expects( $this->never() )
->method( 'insert' );
);
$watchedItem = $store->loadWatchedItem(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'SomeDbKey' )
);
$this->assertInstanceOf( WatchedItem::class, $watchedItem );
$this->assertFalse(
$store->loadWatchedItem(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
$this->assertFalse(
$store->loadWatchedItem(
- $this->getAnonUser(),
+ new UserIdentityValue( 0, 'AnonUser', 0 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
$this->getMockReadOnlyMode()
);
- $titleValue = new TitleValue( 0, 'SomeDbKey' );
$this->assertTrue(
$store->removeWatch(
- $this->getMockNonAnonUserWithId( 1 ),
- Title::newFromTitleValue( $titleValue )
+ new UserIdentityValue( 1, 'MockUser', 0 ),
+ new TitleValue( 0, 'SomeDbKey' )
)
);
}
$this->getMockReadOnlyMode()
);
- $titleValue = new TitleValue( 0, 'SomeDbKey' );
$this->assertFalse(
$store->removeWatch(
- $this->getMockNonAnonUserWithId( 1 ),
- Title::newFromTitleValue( $titleValue )
+ new UserIdentityValue( 1, 'MockUser', 0 ),
+ new TitleValue( 0, 'SomeDbKey' )
)
);
}
$this->assertFalse(
$store->removeWatch(
- $this->getAnonUser(),
+ new UserIdentityValue( 0, 'AnonUser', 0 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
);
$watchedItem = $store->getWatchedItem(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'SomeDbKey' )
);
$this->assertInstanceOf( WatchedItem::class, $watchedItem );
$mockDb->expects( $this->never() )
->method( 'selectRow' );
- $mockUser = $this->getMockNonAnonUserWithId( 1 );
+ $mockUser = new UserIdentityValue( 1, 'MockUser', 0 );
$linkTarget = new TitleValue( 0, 'SomeDbKey' );
$cachedItem = new WatchedItem( $mockUser, $linkTarget, '20151212010101' );
$this->assertFalse(
$store->getWatchedItem(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
$this->assertFalse(
$store->getWatchedItem(
- $this->getAnonUser(),
+ new UserIdentityValue( 0, 'AnonUser', 0 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
$mockCache,
$this->getMockReadOnlyMode()
);
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$watchedItems = $store->getWatchedItemsForUser( $user );
$mockDb = $this->getMockDb();
$mockCache = $this->getMockCache();
$mockLoadBalancer = $this->getMockLBFactory( $mockDb, $dbType );
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$mockDb->expects( $this->once() )
->method( 'select' )
$this->setExpectedException( InvalidArgumentException::class );
$store->getWatchedItemsForUser(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
[ 'sort' => 'foo' ]
);
}
$this->assertTrue(
$store->isWatched(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
$this->assertFalse(
$store->isWatched(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
$this->assertFalse(
$store->isWatched(
- $this->getAnonUser(),
+ new UserIdentityValue( 0, 'AnonUser', 0 ),
new TitleValue( 0, 'SomeDbKey' )
)
);
0 => [ 'SomeDbKey' => '20151212010101', ],
1 => [ 'AnotherDbKey' => null, ],
],
- $store->getNotificationTimestampsBatch( $this->getMockNonAnonUserWithId( 1 ), $targets )
+ $store->getNotificationTimestampsBatch(
+ new UserIdentityValue( 1, 'MockUser', 0 ), $targets )
);
}
[
0 => [ 'OtherDbKey' => false, ],
],
- $store->getNotificationTimestampsBatch( $this->getMockNonAnonUserWithId( 1 ), $targets )
+ $store->getNotificationTimestampsBatch(
+ new UserIdentityValue( 1, 'MockUser', 0 ), $targets )
);
}
new TitleValue( 1, 'AnotherDbKey' ),
];
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$cachedItem = new WatchedItem( $user, $targets[0], '20151212010101' );
$mockDb = $this->getMockDb();
new TitleValue( 1, 'AnotherDbKey' ),
];
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$cachedItems = [
new WatchedItem( $user, $targets[0], '20151212010101' ),
new WatchedItem( $user, $targets[1], null ),
0 => [ 'SomeDbKey' => false, ],
1 => [ 'AnotherDbKey' => false, ],
],
- $store->getNotificationTimestampsBatch( $this->getAnonUser(), $targets )
+ $store->getNotificationTimestampsBatch(
+ new UserIdentityValue( 0, 'AnonUser', 0 ), $targets )
);
}
$this->assertFalse(
$store->resetNotificationTimestamp(
- $this->getAnonUser(),
+ new UserIdentityValue( 0, 'AnonUser', 0 ),
Title::newFromText( 'SomeDbKey' )
)
);
$this->assertFalse(
$store->resetNotificationTimestamp(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
Title::newFromText( 'SomeDbKey' )
)
);
}
public function testResetNotificationTimestamp_item() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$title = Title::newFromText( 'SomeDbKey' );
$mockDb = $this->getMockDb();
}
public function testResetNotificationTimestamp_noItemForced() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$title = Title::newFromText( 'SomeDbKey' );
$mockDb = $this->getMockDb();
}
public function testResetNotificationTimestamp_oldidSpecifiedLatestRevisionForced() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$oldid = 22;
$title = $this->getMockTitle( 'SomeTitle' );
$title->expects( $this->once() )
}
public function testResetNotificationTimestamp_oldidSpecifiedNotLatestRevisionForced() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$oldid = 22;
$title = $this->getMockTitle( 'SomeDbKey' );
$title->expects( $this->once() )
}
public function testResetNotificationTimestamp_notWatchedPageForced() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$oldid = 22;
$title = $this->getMockTitle( 'SomeDbKey' );
$title->expects( $this->once() )
}
public function testResetNotificationTimestamp_futureNotificationTimestampForced() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$oldid = 22;
$title = $this->getMockTitle( 'SomeDbKey' );
$title->expects( $this->once() )
}
public function testResetNotificationTimestamp_futureNotificationTimestampNotForced() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$oldid = 22;
$title = $this->getMockTitle( 'SomeDbKey' );
$title->expects( $this->once() )
$this->getMockCache(),
$this->getMockReadOnlyMode()
);
- $this->assertFalse( $store->setNotificationTimestampsForUser( $this->getAnonUser(), '' ) );
+ $this->assertFalse( $store->setNotificationTimestampsForUser(
+ new UserIdentityValue( 0, 'AnonUser', 0 ), '' ) );
}
public function testSetNotificationTimestampsForUser_allRows() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$timestamp = '20100101010101';
$store = $this->newWatchedItemStore(
}
public function testSetNotificationTimestampsForUser_nullTimestamp() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$timestamp = null;
$store = $this->newWatchedItemStore(
}
public function testSetNotificationTimestampsForUser_specificTargets() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$timestamp = '20100101010101';
$targets = [ new TitleValue( 0, 'Foo' ), new TitleValue( 0, 'Bar' ) ];
$this->assertEquals(
[ 2, 3 ],
$store->updateNotificationTimestamp(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'SomeDbKey' ),
'20151212010101'
)
);
$watchers = $store->updateNotificationTimestamp(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
new TitleValue( 0, 'SomeDbKey' ),
'20151212010101'
);
}
public function testUpdateNotificationTimestamp_clearsCachedItems() {
- $user = $this->getMockNonAnonUserWithId( 1 );
+ $user = new UserIdentityValue( 1, 'MockUser', 0 );
$titleValue = new TitleValue( 0, 'SomeDbKey' );
$mockDb = $this->getMockDb();
$store->getWatchedItem( $user, $titleValue );
$store->updateNotificationTimestamp(
- $this->getMockNonAnonUserWithId( 1 ),
+ new UserIdentityValue( 1, 'MockUser', 0 ),
$titleValue,
'20151212010101'
);
const Page = require( 'wdio-mediawiki/Page' ),
- Api = require( 'wdio-mediawiki/Api' );
+ Api = require( 'wdio-mediawiki/Api' ),
+ Util = require( 'wdio-mediawiki/Util' );
class HistoryPage extends Page {
get heading() { return browser.element( '#firstHeading' ); }
super.openTitle( title, { action: 'history' } );
}
+ toggleRollbackConfirmationSetting( enable ) {
+ Util.waitForModuleState( 'mediawiki.api', 'ready', 5000 );
+ return browser.execute( function ( enable ) {
+ return new mw.Api().saveOption(
+ 'showrollbackconfirmation',
+ enable ? '1' : '0'
+ );
+ }, enable );
+ }
+
vandalizePage( name, content ) {
let vandalUsername = 'Evil_' + browser.options.username;
// Enable rollback confirmation for admin user
// Requires user to log in again, handled by deleteCookie() call in beforeEach function
UserLoginPage.loginAdmin();
-
- UserLoginPage.waitForScriptsToBeReady();
- browser.execute( function () {
- return ( new mw.Api() ).saveOption(
- 'showrollbackconfirmation',
- '1'
- );
- } );
+ HistoryPage.toggleRollbackConfirmationSetting( true );
} );
beforeEach( function () {
// Disable rollback confirmation for admin user
// Requires user to log in again, handled by deleteCookie() call in beforeEach function
UserLoginPage.loginAdmin();
-
- UserLoginPage.waitForScriptsToBeReady();
- browser.execute( function () {
- return ( new mw.Api() ).saveOption(
- 'showrollbackconfirmation',
- '0'
- );
- } );
+ HistoryPage.toggleRollbackConfirmationSetting( false );
} );
beforeEach( function () {
-const Page = require( './Page' ),
- Util = require( 'wdio-mediawiki/Util' );
+const Page = require( './Page' );
class LoginPage extends Page {
get username() { return browser.element( '#wpName1' ); }
loginAdmin() {
this.login( browser.options.username, browser.options.password );
}
-
- waitForScriptsToBeReady() {
- Util.waitForModuleState( 'mediawiki.api' );
- }
}
module.exports = new LoginPage();