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.
+* RepoGroup::singleton(), RepoGroup::destroySingleton(),
+ RepoGroup::setSingleton(), wfFindFile(), and wfLocalFile() are all
+ deprecated. Use MediaWikiServices instead.
=== Other changes in 1.34 ===
* …
'ApiAuthManagerHelper' => __DIR__ . '/includes/api/ApiAuthManagerHelper.php',
'ApiBase' => __DIR__ . '/includes/api/ApiBase.php',
'ApiBlock' => __DIR__ . '/includes/api/ApiBlock.php',
+ 'ApiBlockInfoTrait' => __DIR__ . '/includes/api/ApiBlockInfoTrait.php',
'ApiCSPReport' => __DIR__ . '/includes/api/ApiCSPReport.php',
'ApiChangeAuthenticationData' => __DIR__ . '/includes/api/ApiChangeAuthenticationData.php',
'ApiCheckToken' => __DIR__ . '/includes/api/ApiCheckToken.php',
'UserNamePrefixSearch' => __DIR__ . '/includes/user/UserNamePrefixSearch.php',
'UserNotLoggedIn' => __DIR__ . '/includes/exception/UserNotLoggedIn.php',
'UserOptionsMaintenance' => __DIR__ . '/maintenance/userOptions.php',
+ 'UserOptionsUpdateJob' => __DIR__ . '/includes/jobqueue/jobs/UserOptionsUpdateJob.php',
'UserPasswordPolicy' => __DIR__ . '/includes/password/UserPasswordPolicy.php',
'UserRightsProxy' => __DIR__ . '/includes/user/UserRightsProxy.php',
'UserrightsPage' => __DIR__ . '/includes/specials/SpecialUserrights.php',
* FormattedRCFeed-specific options:
* - 'uri' -- [required] The address to which the messages are sent.
* The uri scheme of this string will be looked up in $wgRCEngines
- * to determine which RCFeedEngine class to use.
+ * to determine which FormattedRCFeed class to use.
* - 'formatter' -- [required] The class (implementing RCFeedFormatter) which will
* produce the text to send. This can also be an object of the class.
* Formatters available by default: JSONRCFeedFormatter, XMLRCFeedFormatter,
* can add to this to provide custom jobs.
* A job handler should either be a class name to be instantiated,
* or (since 1.30) a callback to use for creating the job object.
+ * The callback takes (Title, array map of parameters) as arguments.
*/
$wgJobClasses = [
'deletePage' => DeletePageJob::class,
'cdnPurge' => CdnPurgeJob::class,
'userGroupExpiry' => UserGroupExpiryJob::class,
'clearWatchlistNotifications' => ClearWatchlistNotificationsJob::class,
+ 'userOptionsUpdate' => UserOptionsUpdateJob::class,
'enqueue' => EnqueueJob::class, // local queue for multi-DC setups
'null' => NullJob::class,
];
/**
* Proxy to use for CURL requests.
*/
-$wgHTTPProxy = false;
+$wgHTTPProxy = '';
/**
* Local virtual hosts.
/**
* Find a file.
- * Shortcut for RepoGroup::singleton()->findFile()
- *
+ * @deprecated since 1.34, use MediaWikiServices
* @param string|LinkTarget $title String or LinkTarget object
* @param array $options Associative array of options (see RepoGroup::findFile)
* @return File|bool File, or false if the file does not exist
*/
function wfFindFile( $title, $options = [] ) {
- return RepoGroup::singleton()->findFile( $title, $options );
+ return MediaWikiServices::getInstance()->getRepoGroup()->findFile( $title, $options );
}
/**
* Get an object referring to a locally registered file.
* Returns a valid placeholder object if the file does not exist.
*
+ * @deprecated since 1.34, use MediaWikiServices
* @param Title|string $title
* @return LocalFile|null A File, or null if passed an invalid Title
*/
function wfLocalFile( $title ) {
- return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
+ return MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()->newFile( $title );
}
/**
use ParserFactory;
use PasswordFactory;
use ProxyLookup;
+use RepoGroup;
use ResourceLoader;
use SearchEngine;
use SearchEngineConfig;
return $this->getService( 'ReadOnlyMode' );
}
+ /**
+ * @since 1.34
+ * @return RepoGroup
+ */
+ public function getRepoGroup() : RepoGroup {
+ return $this->getService( 'RepoGroup' );
+ }
+
/**
* @since 1.33
* @return ResourceLoader
}
/**
+ * 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' ) );
);
},
+ 'RepoGroup' => function ( MediaWikiServices $services ) : RepoGroup {
+ $config = $services->getMainConfig();
+ return new RepoGroup(
+ $config->get( 'LocalFileRepo' ),
+ $config->get( 'ForeignFileRepos' ),
+ $services->getMainWANObjectCache()
+ );
+ },
+
'ResourceLoader' => function ( MediaWikiServices $services ) : ResourceLoader {
// @todo This should not take a Config object, but it's not so easy to remove because it
// exposes it in a getter, which is actually used.
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
$htmlForm
->setMethod( 'get' )
->setAction( wfScript() )
- ->setCollapsible( true )
+ ->setCollapsibleOptions( true )
->setId( 'mw-history-searchform' )
->setSubmitText( $this->msg( 'historyaction-submit' )->text() )
->setWrapperAttributes( [ 'id' => 'mw-history-search' ] )
*/
abstract class ApiBase extends ContextSource {
+ use ApiBlockInfoTrait;
+
/**
* @name Constants for ::getAllowedParams() arrays
* These constants are keys in the arrays returned by ::getAllowedParams()
if ( is_string( $error[0] ) && isset( self::$blockMsgMap[$error[0]] ) && $user->getBlock() ) {
list( $msg, $code ) = self::$blockMsgMap[$error[0]];
$status->fatal( ApiMessage::create( $msg, $code,
- [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
+ [ 'blockinfo' => $this->getBlockInfo( $user->getBlock() ) ]
) );
} else {
$status->fatal( ...$error );
foreach ( self::$blockMsgMap as $msg => list( $apiMsg, $code ) ) {
if ( $status->hasMessage( $msg ) && $user->getBlock() ) {
$status->replaceMessage( $msg, ApiMessage::create( $apiMsg, $code,
- [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $user->getBlock() ) ]
+ [ 'blockinfo' => $this->getBlockInfo( $user->getBlock() ) ]
) );
}
}
$this->dieWithError(
'apierror-autoblocked',
'autoblocked',
- [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ]
+ [ 'blockinfo' => $this->getBlockInfo( $block ) ]
);
} elseif ( !$block->isSitewide() ) {
$this->dieWithError(
'apierror-blocked-partial',
'blocked',
- [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ]
+ [ 'blockinfo' => $this->getBlockInfo( $block ) ]
);
} else {
$this->dieWithError(
'apierror-blocked',
'blocked',
- [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ]
+ [ 'blockinfo' => $this->getBlockInfo( $block ) ]
);
}
}
*/
class ApiBlock extends ApiBase {
+ use ApiBlockInfoTrait;
+
/**
* Blocks the user specified in the parameters for the given expiry, with the
* given reason, and with all other settings provided in the params. If the block
$this->dieWithError(
$status,
null,
- [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ]
+ [ 'blockinfo' => $this->getBlockInfo( $block ) ]
);
}
}
--- /dev/null
+<?php
+/**
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
+
+/**
+ * @ingroup API
+ */
+trait ApiBlockInfoTrait {
+
+ /**
+ * Get basic info about a given block
+ * @param Block $block
+ * @return array Array containing several keys:
+ * - blockid - ID of the block
+ * - blockedby - username of the blocker
+ * - blockedbyid - user ID of the blocker
+ * - blockreason - reason provided for the block
+ * - blockedtimestamp - timestamp for when the block was placed/modified
+ * - blockexpiry - expiry time of the block
+ * - systemblocktype - system block type, if any
+ */
+ private function getBlockInfo( Block $block ) {
+ $vals = [];
+ $vals['blockid'] = $block->getId();
+ $vals['blockedby'] = $block->getByName();
+ $vals['blockedbyid'] = $block->getBy();
+ $vals['blockreason'] = $block->getReason();
+ $vals['blockedtimestamp'] = wfTimestamp( TS_ISO_8601, $block->getTimestamp() );
+ $vals['blockexpiry'] = ApiResult::formatExpiry( $block->getExpiry(), 'infinite' );
+ $vals['blockpartial'] = !$block->isSitewide();
+ if ( $block->getSystemBlockType() !== null ) {
+ $vals['systemblocktype'] = $block->getSystemBlockType();
+ }
+ return $vals;
+ }
+
+}
*/
class ApiQueryUserInfo extends ApiQueryBase {
+ use ApiBlockInfoTrait;
+
const WL_UNREAD_LIMIT = 1000;
private $params = [];
$result->addValue( 'query', $this->getModuleName(), $r );
}
- /**
- * Get basic info about a given block
- * @param Block $block
- * @return array Array containing several keys:
- * - blockid - ID of the block
- * - blockedby - username of the blocker
- * - blockedbyid - user ID of the blocker
- * - blockreason - reason provided for the block
- * - blockedtimestamp - timestamp for when the block was placed/modified
- * - blockexpiry - expiry time of the block
- * - systemblocktype - system block type, if any
- */
- public static function getBlockInfo( Block $block ) {
- $vals = [];
- $vals['blockid'] = $block->getId();
- $vals['blockedby'] = $block->getByName();
- $vals['blockedbyid'] = $block->getBy();
- $vals['blockreason'] = $block->getReason();
- $vals['blockedtimestamp'] = wfTimestamp( TS_ISO_8601, $block->getTimestamp() );
- $vals['blockexpiry'] = ApiResult::formatExpiry( $block->getExpiry(), 'infinite' );
- $vals['blockpartial'] = !$block->isSitewide();
- if ( $block->getSystemBlockType() !== null ) {
- $vals['systemblocktype'] = $block->getSystemBlockType();
- }
- return $vals;
- }
-
/**
* Get central user info
* @param Config $config
if ( isset( $this->prop['blockinfo'] ) ) {
$block = $user->getBlock();
if ( $block ) {
- $vals = array_merge( $vals, self::getBlockInfo( $block ) );
+ $vals = array_merge( $vals, $this->getBlockInfo( $block ) );
}
}
$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 {
*/
class ApiUnblock extends ApiBase {
+ use ApiBlockInfoTrait;
+
/**
* Unblocks the specified user or provides the reason the unblock failed.
*/
$this->dieWithError(
$status,
null,
- [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ]
+ [ 'blockinfo' => $this->getBlockInfo( $block ) ]
);
}
}
"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]]를 통해 가져올 수 있습니다.",
"apihelp-edit-summary": "Säiten uleeën an änneren.",
"apihelp-edit-param-sectiontitle": "Den Titel fir en neien Abschnitt.",
"apihelp-edit-param-text": "Säiteninhalt.",
- "apihelp-edit-param-minor": "Kleng Ännerung.",
- "apihelp-edit-param-notminor": "Keng kleng Ännerung",
+ "apihelp-edit-param-minor": "Dës Ännerung als kleng Ännerung markéieren.",
+ "apihelp-edit-param-notminor": "Dës Ännerung net als keng kleng Ännerung markéieren esouguer wann d'Benotzerastellung \"{{int:tog-minordefault}}\" agestallt ass.",
"apihelp-edit-param-bot": "Dës Ännerung als eng Bot-Ännerung markéieren.",
"apihelp-edit-param-createonly": "D'Säit net ännere wann et se scho gëtt.",
"apihelp-edit-param-watch": "D'Säit op dem aktuelle Benotzer seng Iwwerwaachungslëscht dobäisetzen.",
"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]].",
"apihelp-edit-param-text": "頁面內容。",
"apihelp-edit-param-summary": "編輯摘要。 當未設定 $1section=new 與 $1sectiontitle 時也會當做章節標題。",
"apihelp-edit-param-tags": "更改套用到修訂的標籤。",
- "apihelp-edit-param-minor": "小編輯。",
- "apihelp-edit-param-notminor": "非小編輯。",
+ "apihelp-edit-param-minor": "標記此編輯為小編輯。",
+ "apihelp-edit-param-notminor": "不要標記此編輯為小編輯,即使有設定到「{{int:tog-minordefault}}」使用者偏好設定。",
"apihelp-edit-param-bot": "標記此編輯為機器人編輯。",
"apihelp-edit-param-basetimestamp": "基於修訂的時間戳記,用來檢測編輯衝突。也许可以取得[[Special:ApiHelp/query+revisions|action=query&prop=revisions&rvprop=timestamp]]認可。",
"apihelp-edit-param-starttimestamp": "當編輯程序開始的時間戳記,用於偵測編輯衝突。當編輯程序開始時(例如:當載入要編輯的頁面內容),使用 <var>[[Special:ApiHelp/main|curtimestamp]]</var> 可以取得一個適當值。",
throw new MWException( 'No cache key set, so cannot obtain or save the CacheHelper values.' );
}
- return wfMemcKey( ...array_values( $this->cacheKey ) );
+ return ObjectCache::getLocalClusterInstance()->makeKey(
+ ...array_values( $this->cacheKey )
+ );
}
/**
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();
*/
public function addLinkObj( LinkTarget $nt ) {
$key = $this->titleFormatter->getPrefixedDBkey( $nt );
- if ( $this->isBadLink( $key ) || $nt->isExternal()
- || $nt->inNamespace( NS_SPECIAL )
- ) {
+ if ( $this->isBadLink( $key ) || $nt->isExternal() || $nt->getNamespace() < 0 ) {
return 0;
}
$id = $this->getGoodLinkID( $key );
* @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
/** @var FileRepo[] */
protected $foreignRepos;
+ /** @var WANObjectCache */
+ protected $wanCache;
+
/** @var bool */
protected $reposInitialised = false;
/** @var ProcessCacheLRU */
protected $cache;
- /** @var RepoGroup */
- protected static $instance;
-
/** Maximum number of cache items */
const MAX_CACHE_SIZE = 500;
/**
- * Get a RepoGroup instance. At present only one instance of RepoGroup is
- * needed in a MediaWiki invocation, this may change in the future.
+ * @deprecated since 1.34, use MediaWikiServices::getRepoGroup
* @return RepoGroup
*/
static function singleton() {
- if ( self::$instance ) {
- return self::$instance;
- }
- global $wgLocalFileRepo, $wgForeignFileRepos;
- /** @var array $wgLocalFileRepo */
- self::$instance = new RepoGroup( $wgLocalFileRepo, $wgForeignFileRepos );
-
- return self::$instance;
+ return MediaWikiServices::getInstance()->getRepoGroup();
}
/**
- * Destroy the singleton instance, so that a new one will be created next
- * time singleton() is called.
+ * @deprecated since 1.34, use MediaWikiTestCase::overrideMwServices() or similar. This will
+ * cause bugs if you don't reset all other services that depend on this one at the same time.
*/
static function destroySingleton() {
- self::$instance = null;
+ MediaWikiServices::getInstance()->resetServiceForTesting( 'RepoGroup' );
}
/**
- * Set the singleton instance to a given object
- * Used by extensions which hook into the Repo chain.
- * It's not enough to just create a superclass ... you have
- * to get people to call into it even though all they know is RepoGroup::singleton()
- *
+ * @deprecated since 1.34, use MediaWikiTestCase::setService, this can mess up state of other
+ * tests
* @param RepoGroup $instance
*/
static function setSingleton( $instance ) {
- self::$instance = $instance;
+ $services = MediaWikiServices::getInstance();
+ $services->disableService( 'RepoGroup' );
+ $services->redefineService( 'RepoGroup',
+ function () use ( $instance ) {
+ return $instance;
+ }
+ );
}
/**
- * Construct a group of file repositories.
+ * Construct a group of file repositories. Do not call this -- use
+ * MediaWikiServices::getRepoGroup.
*
* @param array $localInfo Associative array for local repo's info
* @param array $foreignInfo Array of repository info arrays.
* Each info array is an associative array with the 'class' member
* giving the class name. The entire array is passed to the repository
* constructor as the first parameter.
+ * @param WANObjectCache $wanCache
*/
- function __construct( $localInfo, $foreignInfo ) {
+ function __construct( $localInfo, $foreignInfo, $wanCache ) {
$this->localInfo = $localInfo;
$this->foreignInfo = $foreignInfo;
$this->cache = new MapCacheLRU( self::MAX_CACHE_SIZE );
+ $this->wanCache = $wanCache;
}
/**
* Search repositories for an image.
- * You can also use wfFindFile() to do this.
*
* @param Title|string $title Title object or string
* @param array $options Associative array of options:
protected function newRepo( $info ) {
$class = $info['class'];
- $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
- $info['wanCache'] = $cache;
+ $info['wanCache'] = $this->wanCache;
return new $class( $info );
}
$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 $mAction = false;
/**
- * Whether the HTML form can be collapsed
- * @since 1.33
+ * Whether the form can be collapsed
+ * @since 1.34
* @var bool
*/
protected $mCollapsible = false;
/**
- * Whether the HTML form IS collapsed by default
- * @since 1.33
+ * Whether the form is collapsed by default
+ * @since 1.34
* @var bool
*/
protected $mCollapsed = false;
}
/**
- * Make the form collapsible
- * @since 1.33
- * @param bool $collapsed whether it should be by default
- * @return HTMLForm $this for chaining calls (since 1.20)
+ * Set whether the HTML form can be collapsed.
+ *
+ * @since 1.34
+ * @param bool $collapsedByDefault (optional) whether the form is collapsed by default
+ * @return HTMLForm $this for chaining calls
*/
- public function setCollapsible( $collapsed = false ) {
+ public function setCollapsibleOptions( $collapsedByDefault = false ) {
$this->mCollapsible = true;
- $this->mCollapsed = $collapsed;
+ $this->mCollapsed = $collapsedByDefault;
return $this;
}
'classes' => $classes,
'group' => new OOUI\StackLayout( [
'expanded' => false,
- 'classes' => [ 'oo-ui-fieldsetLayout-group mw-collapsible-content' ],
- 'items' => [
- new OOUI\Widget( [
- 'content' => new OOUI\HtmlSnippet( $html )
- ] ),
- ],
+ 'classes' => [ 'mw-collapsible-content' ],
] ),
+ 'items' => [
+ new OOUI\Widget( [
+ 'content' => new OOUI\HtmlSnippet( $html )
+ ] ),
+ ],
] + OOUI\Element::configFromHtmlAttributes( $this->mWrapperAttributes ) );
} else {
$content = new OOUI\HtmlSnippet( $html );
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 );
"config-license-help": "Multe wikis public pone tote le contributiones sub un [https://freedomdefined.org/Definition/Ia?uselang=ia licentia libere].\nIsto adjuta a crear un senso de proprietate communitari e incoragia le contribution in longe termino.\nIsto non es generalmente necessari pro un wiki private o de interprisa.\n\nSi tu vole poter usar texto de Wikipedia, e si tu vole que Wikipedia pote acceptar texto copiate de tu wiki, tu debe seliger <strong>{{int:config-license-cc-by-sa}}</strong>.\n\nWikipedia usava anteriormente le Licentia GNU pro Documentation Libere (GFDL).\nIste es un licentia valide, ma es difficile a comprender.\nIl es anque difficile reusar le contento licentiate sub GFDL.",
"config-email-settings": "Configuration de e-mail",
"config-enable-email": "Activar le e-mail sortiente",
- "config-enable-email-help": "Si tu vole que e-mail functiona, [Config-dbsupport-oracle/manual/en/mail.configuration.php le optiones de e-mail de PHP] debe esser configurate correctemente.\nSi tu non vole functiones de e-mail, tu pote disactivar los hic.",
+ "config-enable-email-help": "Si tu vole que e-mail functiona, [https://www.php.net/manual/en/mail.configuration.php le optiones de e-mail de PHP] debe esser configurate correctemente.\nSi tu non vole functiones de e-mail, tu pote disactivar los hic.",
"config-email-user": "Activar le e-mail de usator a usator",
"config-email-user-help": "Permitter a tote le usatores de inviar e-mail inter se, si illes lo ha activate in lor preferentias.",
"config-email-usertalk": "Activar notification de cambios in paginas de discussion de usatores",
<?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 ] );
}
--- /dev/null
+<?php
+/**
+ * Job that updates a user's preferences.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup JobQueue
+ */
+
+/**
+ * Job that updates a user's preferences
+ *
+ * The following job parameters are required:
+ * - userId: the user ID
+ * - options: a map of (option => value)
+ *
+ * @since 1.33
+ */
+class UserOptionsUpdateJob extends Job implements GenericParameterJob {
+ public function __construct( array $params ) {
+ parent::__construct( 'userOptionsUpdate', $params );
+ $this->removeDuplicates = true;
+ }
+
+ public function run() {
+ if ( !$this->params['options'] ) {
+ return true; // nothing to do
+ }
+
+ $user = User::newFromId( $this->params['userId'] );
+ $user->load( $user::READ_EXCLUSIVE );
+ if ( !$user->getId() ) {
+ return true;
+ }
+
+ foreach ( $this->params['options'] as $name => $value ) {
+ $user->setOption( $name, $value );
+ }
+
+ $user->saveSettings();
+
+ return true;
+ }
+}
$this->revision = $revision;
$this->audience = $audience;
$this->cacheKey = $this->parserCache->getKey( $page, $parserOptions );
- $keyPrefix = $this->cacheKey ?: wfMemcKey( 'articleview', 'missingcachekey' );
+ $keyPrefix = $this->cacheKey ?: ObjectCache::getLocalClusterInstance()->makeKey(
+ 'articleview', 'missingcachekey'
+ );
parent::__construct( 'ArticleView', $keyPrefix . ':revid:' . $revid );
}
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',
];
*/
class UDPRCFeedEngine extends RCFeedEngine {
/**
- * @see RCFeedEngine::send
+ * @see FormattedRCFeed::send
* @param array $feed
* @param string $line
* @return bool
# 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;
}
/**
* @return bool
*/
public function isMovable( $index ) {
- $result = !( $index < NS_MAIN ||
- ( $index == NS_FILE && !$this->config->get( 'AllowImageMoving' ) ) );
+ $result = $index >= NS_MAIN &&
+ ( $index != NS_FILE || $this->options->get( 'AllowImageMoving' ) );
/**
* @since 1.20
* For subject (non-talk) namespaces, returns the talk namespace
*
* @param int $index Namespace index
- * @return int|null If no associated namespace could be found
+ * @return int
*/
public function getAssociated( $index ) {
$this->isMethodValidFor( $index, __METHOD__ );
if ( $this->isSubject( $index ) ) {
return $this->getTalk( $index );
- } elseif ( $this->isTalk( $index ) ) {
- return $this->getSubject( $index );
- } else {
- return null;
}
+ return $this->getSubject( $index );
}
/**
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 ] );
}
* The input *must* be converted to lower case first
*
* @param string $name Namespace name
- * @return int
+ * @return int|null
*/
public function getCanonicalIndex( $name ) {
if ( $this->namespaceIndexes === false ) {
}
/**
- * Returns an array of the namespaces (by integer id) that exist on the
- * wiki. Used primarily by the api in help documentation.
+ * Returns an array of the namespaces (by integer id) that exist on the wiki. Used primarily by
+ * the API in help documentation. The array is sorted numerically and omits negative namespaces.
* @return array
*/
public function getValidNamespaces() {
* @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;
}
/**
* Determine which restriction levels it makes sense to use in a namespace,
* optionally filtered by a user's rights.
*
+ * @todo Move this to PermissionManager and remove the dependency here on permissions-related
+ * config settings.
+ *
* @param int $index Index to check
* @param User|null $user User to check
* @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": "احذف",
"confirmemail_pending": "تم إرسال كود التأكيد إلى بريدك الإلكتروني مؤخراً؛\nإذا كنت قد أنشأت حسابك للتو، من الأفضل أن تنتظر بضع دقائق قبل أن تطلب كوداً آخر.",
"confirmemail_send": "أرسل كود تأكيد",
"confirmemail_sent": "تم إرسال رسالة التأكيد، شكرا لك.",
- "confirmemail_oncreate": "تم إرسال كود تأكيد إلى عنوان بريدك الإلكتروني.\nالكود غير مطلوب للدخول إلى الموسوعة باسمك، ولكن يجب إدخاله قبل استخدامك أياً من خواص البريد الإلكتروني المستخدمة هنا في الويكي.",
+ "confirmemail_oncreate": "تم إرسال كود تأكيد إلى عنوان بريدك الإلكتروني.\nالكود غير مطلوب للدخول، ولكن يجب إدخاله قبل استخدامك أيًّا من خواص البريد الإلكتروني المستخدمة هنا في الويكي.",
"confirmemail_sendfailed": "لم يتمكن {{SITENAME}} من إرسال رسالة التأكيد إليك.\nمن فضلك تأكد من عنوان بريدك الإلكتروني بحثاً عن حروف غير صحيحة.\n\nأرجع خادم البريد: $1",
"confirmemail_invalid": "كود تأكيد غير صحيح.\nربما انتهت فترة صلاحيته.",
"confirmemail_needlogin": "يجب عليك $1 لتأكيد بريدك الإلكتروني.",
"tog-hideminor": "engkebang suntingan ring gentosan sane pinih anyar",
"tog-hidepatrolled": "engkebang suntingan mapatrol ring gentosan sane pinih anyar",
"tog-newpageshidepatrolled": "engkebang lembar mapatrol saking saking kepahan lembar anyar",
+ "tog-hidecategorization": "Engkebang kacané",
"tog-extendwatchlist": "kembangang kepahan pangiwasan antuk nampilang samian panguwahan, nenten sane anyar kewanten",
"tog-usenewrc": "aniang suntingan ring tampilan pagentosan sane pinih anyar lan kepahan pangiwasan manutin lembar",
"tog-numberheadings": "isinin nomor murda anggen cara otomatis",
"tog-enotifminoredits": "taler kirimang titiang email ring panguwahan alit",
"tog-enotifrevealaddr": "kirimang titiang alamat email ring catetan email",
"tog-shownumberswatching": "tampilang akehnyane sane ngiwasin",
- "tog-oldsig": "tanda tangan mangkin",
+ "tog-oldsig": "Tanda tangan mangkin",
"tog-fancysig": "dadosang tanda tangan dados teks wiki (nenten pranala otomatis)",
- "tog-uselivepreview": "anggen pratayang langsung(experimental)",
+ "tog-uselivepreview": "Anggen pratayang langsung ten anggen kaca sane malunan",
"tog-forceeditsummary": "elingang titiang yening kotak ringkesan suntingan kari kosong",
"tog-watchlisthideown": "engkebang panguwahan titiang saking kepahan pangiwasan",
"tog-watchlisthidebots": "engkebang panguwahan bot ring kepahan pangiwasan",
"tog-ccmeonemails": "kirimang titiang salinan email sane kirimang titiang ring anak lianan",
"tog-diffonly": "sampunang katampilang daging lembar ring ungkur binanne suntingan",
"tog-showhiddencats": "tampilang golongan sane kaengkebang",
- "tog-norollbackdiff": "sampunang tampilang binanne sesampun ngewaliang",
+ "tog-norollbackdiff": "Sampunang tampilang binanne sesampun ngewaliang",
"tog-useeditwarning": "elingang titiang yening ngalahin lembar panyuntingan sadurung nyimpen pagentosan",
- "tog-prefershttps": "setata nganggen sambungan sane aman rikala malebu log",
+ "tog-prefershttps": "Setata nganggen sambungan sane aman rikala malebu log",
"underline-always": "Setata",
"underline-never": "Nénten naénin",
"underline-default": "kulit utawi penjelajah paaban",
"category-media-header": "lembar ring golongan \"$1\"",
"category-empty": "\"mangkin, nenten madaging lembar utawi pekakas ring golongan puniki\"",
"hidden-categories": "{{plural:$1|punduhan sane kaengkebang| punduhan sane kaengkebang}}",
+ "hidden-category-category": "Kategori mengkeb",
"category-subcat-count": "{{PLURAL:$2| golongan puniki madue {{PLURAL:$1|$1 subkategori}} puniki, saking genepan $2.}}",
"category-article-count": "{{PLURAL:$2|golongan puniki madue{{PLURAL:$1|$1 lembar}}, saking total $2.}}",
"category-file-count": "{{PLURAL:$2|golongan puniki madue{{PLURAL:$1|$1 lembar}}, saking total $2.}}",
"about": "Indik",
"newwindow": "(bukak ring jendela anyar)",
"cancel": "Buwung",
+ "mypage": "Kaca",
"mytalk": "Wicara",
"anontalk": "Wicara",
"navigation": "Pengarah",
"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}}",
"help": "Tulung",
+ "help-mediawiki": "Pitulung MediaWiki",
"search": "Rereh",
"searchbutton": "Rereh",
"searcharticle": "lanturang",
"history": "sejarah pupulan",
"history_short": "kawentenan sane lawas",
+ "history_small": "babad",
"printableversion": "kawentenan lian sane macetak",
"permalink": "Pranala ajeg",
"view": "cingakin",
"protect_change": "gentos",
"newpage": "Lembar Anyar",
"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",
"disclaimers": "nungkas",
"disclaimerpage": "Project:Pengelidan lumrah",
"edithelp": "pamitulung panguwahan",
+ "helppage-top-gethelp": "Tulung",
"mainpage": "Kaca Utama",
"mainpage-description": "Lembar Utama",
"portal": "Pintu nuju sekha",
"portal-url": "Project:pamedal sekha",
"privacy": "kawicaksanaan padewekan",
"privacypage": "Project:kawicaksanan tanpaiket",
+ "ok": "OK",
"retrievedfrom": "kapolihang saking \"$1\"",
"youhavenewmessages": "{{PLURAL:$3|ida dane maduwe}} $1 ($2)",
+ "youhavenewmessagesfromusers": "{{PLURAL:$4|You have}} $1 ring {{PLURAL:$3|another user|$3 users}} ($2).",
+ "youhavenewmessagesmanyusers": "Ida dane ngelah $1 saking liyane ($2).",
"editsection": "gentos",
"editold": "mecikang",
"viewsourceold": "cingak witnyane",
"viewsourcelink": "cingak witnyane",
"editsectionhint": "ubah kepahan$1",
"toc": "kepahan dagingnyane",
+ "showtoc": "edengang",
+ "hidetoc": "engkebang",
+ "collapsible-expand": "buka",
+ "confirmable-confirm": "{{GENDER:$1|Ida}} dane yakin?",
+ "confirmable-yes": "Inggih",
+ "confirmable-no": "Nénten",
"site-atom-feed": "$1 \"atom feed\"",
"page-atom-feed": "$1 \"atom feed\"",
"red-link-title": "$1 (kaca tan wénten)",
"nstab-category": "golongan",
"mainpage-nstab": "Kaca Utama",
"nosuchspecialpage": "Ten wenten lembar spesial",
+ "error": "kaluputan",
+ "databaseerror": "Database kaluputan",
"missing-article": "data utama nenten prasida nemu tulisan saking lembar sane sepatutne wenten, inggih punika $1, $2\n\nindike puniki biasane keranayang olih pranala kaon nuju pabenahan sane dumun lembar sane sampun kaicalang\n\nyening nenten puniki sane ngranayang, ida dane minab sampun manggihin kaiwangang ring sajeroning piranti lunak.\nDurus sadokang indik puniki rin silih sinunggil anak \n\n[[Special:ListUsers/sysop|Pengurus]], antuk ngetik alamat URL sane katuju",
"missingarticle-rev": "(pabenahan#:$1)",
"badtitle": "murda sane nenten manut",
"yourpasswordagain": "jumunin kruna sandi",
"login": "Ngranjing log",
"nav-login-createaccount": "malebu log / ngawe pepalihan",
+ "logout": "Medal Log",
"userlogout": "medal saking Log",
+ "notloggedin": "Konden masuk log",
+ "userlogin-noaccount": "Durung madue akun?",
+ "userlogin-joinproject": "Indik {{SITENAME}}",
"createaccount": "ngajuang akun anyar",
"mailmypassword": "nyumu ngaryanin kruna sandi",
"loginlanguagelabel": "Basa: $1",
"pt-login": "Ngranjing log",
+ "pt-login-button": "Ngranjing log",
"pt-createaccount": "Ngajuang akun anyar",
"pt-userlogout": "Medal Log",
+ "botpasswords-label-create": "Ngae",
+ "botpasswords-label-cancel": "Buungan",
+ "botpasswords-label-delete": "Apus",
+ "botpasswords-label-resetpassword": "Nyumu kruna sandi",
"passwordreset": "Nyumu kruna sandi",
"bold_sample": "teks puniki mesurat tebel",
"bold_tip": "teks puniki mesurat tebel",
"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.",
"hiddencategories": "lembar niki inggih punika krama saking {{PLURAL:$1|1 golongan sane mengkeb|$1 golongan sane mengkeb}}",
"permissionserrorstext-withaction": "ida dané nénten madué kuasa ngranjing anggén $2, riantukan {{PLURAL:$1|alasan}} ring sor puniki:",
"recreate-moveddeleted-warn": "\"pingetan\" ida dane ngawe malih lembar sane naenin maapus.'''\n\nmangda kayunin malih napike pantes lanturang suntingan ida dane. puniki log pengapusan lan pangisidan saking lembar puniki:",
- "moveddeleted-notice": "lembar puniki sampun kaapus. anggen pewarah, puniki log pangapus lan pengisidan lembar puniki",
+ "moveddeleted-notice": "Lembar puniki sampun kaapus.\nAnggen pewarah, proteksi, lan pengisidan log saking lembar puniki cingakin pustaka beten.",
"content-model-wikitext": "tulisan wiki",
"post-expand-template-inclusion-warning": "pinget: ukuran templat sane keanggen kalangkung ageng. wenten templat sane kacampahang",
"post-expand-template-inclusion-category": "lembar sane maukuran templat sane nglangkungin wates",
"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-editmyuserjs": "рэдагаваньне вашых уласных JavaScript-файлаў",
"action-viewsuppressed": "прагляд вэрсіяў, схаваных ад усіх удзельнікаў",
"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": "Паказаць удзельнікаў ад:",
"category-subcat-count-limited": "Tumbung ini baisi {{PLURAL:$1|sub-tumbung|$1 sub-tutumbung}} barikut.",
"category-article-count": "{{PLURAL:$2|Tumbung ni baisi asa tungkaran barikut haja.|Tutumbung ngini baisi {{PLURAL:$1|tungkaran|$1 tutungkaran}}, matan $2 sabarataan.}}",
"category-article-count-limited": "Tumbung ini baisi {{PLURAL:$1|asa tungkaran|$1 tutungkaran}} barikut.",
- "category-file-count": "{{PLURAL:$2|Tumbung ngini wastu baisi satu barakas barikut.|Tumbung ngini baisi {{PLURAL:$1|barakas|$1 babarakas}} barikut, matan $2 sabarataan.}}",
+ "category-file-count": "{{PLURAL:$2|Tumbung ngini baisi {{PLURAL:$1|$1 barakas}}, matan jumlah $2.}}",
"category-file-count-limited": "Tumbung ngini baisi {{PLURAL:$1|barakas|$1 barakas}} barikut.",
"listingcontinuesabbrev": "samb.",
"index-category": "Tungkaran tasusun bapadalakan kata",
"actionthrottled": "Kalakuan dikiripi",
"actionthrottledtext": "Sawagai sabuting takaran anti-spam, Pian dibabatasi hagan balalaku kababanyakan dalam parhatan handap, wan Pian sudah limpuari batasan ngini.\nMuhun cubai pulang dalam babarapa minit.",
"protectedpagetext": "Tungkaran ngini sudah dilindungi hagan mancagah babakan.",
- "viewsourcetext": "Pian kawa maniringi wan manyalin asal mula tungkaran ngini:",
+ "viewsourcetext": "Pian kawa maniringi wan manyalin asal-mula tungkaran ngini.",
"viewyourtext": "Pian kawa maniringi wan salain asalmula matan '''babakan pian''' ka tungkaran ngini:",
"protectedinterface": "Tungkaran ini manyadiakan naskah antarmuha gasan parangkat lunak, wan dilindungi hagan mancagah tasalah puruk.",
"editinginterface": "'''Paringatan:''' Pian mambabak sabuting tungkaran nang dipuruk hagan manyadiakan naskah antarmuha gasan parangkat lunak.\nPaubahan ka tungkaran ngini akan bapangaruh matan tampaian antarmuha gasan pamakai lain.\nGasan tarjamahan, muhun pakai [https://translatewiki.net/wiki/Main_Page?setlang=bjn translatewiki.net], rangka gawian palokalan MediaWiki.",
"userlogin-yourname-ph": "Masukakan ngaran pamakai Pian",
"yourpassword": "Katasunduk:",
"userlogin-yourpassword": "Kata sandi",
+ "userlogin-yourpassword-ph": "Masukakan kata sandi",
"createacct-yourpassword-ph": "Masukakan kata sandi",
"yourpasswordagain": "Katik pulang katasunduk:",
"createacct-yourpasswordagain": "Konfirmasi kata sandi",
"createacct-yourpasswordagain-ph": "Masukakan pulang kata sandi",
+ "userlogin-remembermypassword": "Biarakan ulun tatap babuat",
"yourdomainname": "Domain Pian:",
"password-change-forbidden": "Pian kada kawa ma-ubah kata sunduk pada wiki ngini.",
"externaldberror": "Ada kasalahan apakah kacucukan basis data atawa Pian kada bulih mamutakhirakan akun luar.",
"logout": "Kaluar",
"userlogout": "Kaluar",
"notloggedin": "Balum babuat log",
+ "userlogin-noaccount": "Balum baisi akun?",
+ "userlogin-joinproject": "Gabung {{SITENAME}}",
"createaccount": "Ulah akun",
+ "userlogin-resetpassword-link": "Lupa kata sandi?",
+ "userlogin-helplink2": "Patulung babuat log",
"createacct-emailoptional": "Alamat surél/email (bagusnya diisi)",
"createacct-email-ph": "Masukakan alamat email Pian",
"createaccountmail": "Malalui suril",
"loginlanguagelabel": "Basa: $1",
"suspicious-userlogout": "Pamintaan Pian hagan kaluar log kada ditarima marga nangkaya dikirim matan panjalajah web rakai atawa tatangkap proxy.",
"pt-login": "Babuat log",
+ "pt-login-button": "Babuat log",
"pt-createaccount": "Ulah akun",
"pt-userlogout": "Kaluar",
"php-mail-error-unknown": "Kasalahan kada dipinandui dalam pungsi surat () PHP",
"semiprotectedpagewarning": "'''Catatan:''' Tungkaran ngini sudah dilindungi nang akibatnya pamakai tadaptar haja nang kawa mambabak.\nLog masuk pauncitnya disadiakan di bawah gasan rujukan:",
"cascadeprotectedwarning": "'''Paringatan:''' Tungkaran ngini sudah dilindungi nang akibatnya pamakai awan hak istimiwa pambakal haja nang kawa mambabak, sualnya ngini tamasuk dalam baumpat parlindungan barénténg {{PLURAL:$1|tungkaran|tutungkaran}}:",
"titleprotectedwarning": "'''Paringatan: Tungkaran ngini sudah dilindungi nang akibatnya [[Special:ListGroupRights|hak khas]] diparluakan hagan maulah ngini.'''\nLog masuk pauncitnya disadiakan di bawah gasan rujukan:",
- "templatesused": "{{PLURAL:$1|Citakan|Citakan}} nang digunakan di tungkaran ngini:",
+ "templatesused": "{{PLURAL:$1|Citakan|Citakan}} nang dipakai di tungkaran ngini:",
"templatesusedpreview": "{{PLURAL:$1|Citakan|Citakan}} nang digunakan di titilikan ngini:",
"templatesusedsection": "{{PLURAL:$1|Citakan|Cicitakan}} nang diguna'akan di hagian ini:",
"template-protected": "(dilindungi)",
"permissionserrorstext": "Pian kada baisi ijin gasan malakuakan itu, karana {{PLURAL:$1|alasan|alasan}} ini:",
"permissionserrorstext-withaction": "Pian kada baisi ijin gasan $2, karana {{PLURAL:$1|alasan|alasan}} ini:",
"recreate-moveddeleted-warn": "'''Paringatan: Pian maulah pulang sabuah tungkaran nang sabalumnya dihapus.'''\n\nPian partimbangakan dahulu sasuaikah hagan manarusakan pambabakan tungkaran ini.\nLog pahapusan wan paugahan gasan tungkaran ini disadiakan di sia:",
- "moveddeleted-notice": "Tungkaran ini sudah dihapus.\nLog pahapusan wan paugahan gasan tungkaran ini disadiakan di bawah ini gasan rujukan.",
+ "moveddeleted-notice": "Tungkaran ini sudah dihapus.\nLog pahapusan, palindungan, wan pamindahan matan tungkaran itu tasadia di bawah ini sabagai rujukan.",
"log-fulllog": "Tiringi samunyaan log",
"edit-hook-aborted": "Babakan ditinggalakan ulih kakait parser.\nIni kadada panjalasan.",
"edit-gone-missing": "Kada kawa mamutakhirakan tungkaran ini.\nIni cungul pinanya sudah tahapus.",
"currentrev": "Ralatan pahabisannya",
"currentrev-asof": "Ralatan pahanyarnya pada $1",
"revisionasof": "Ralatan matan $1",
- "revision-info": "Ralatan pada $1 ulih $2",
+ "revision-info": "Ralatan par $1 ulih {{GENDER:$6|$2}}$7",
"previousrevision": "←Ralatan talawas",
"nextrevision": "Ralatan salanjutnya→",
"currentrevisionlink": "Ralatan wayahini",
"page_first": "Panambaian",
"page_last": "Pauncitan",
"histlegend": "Pilihan mananding: tandai kutak-kutak radiu ralatan-ralatan nang handak ditanding wan picik enter atawa picikan di bawah.<br />Legend: '''({{int:cur}})''' =lainnya awan ralatan pahanyarnya, '''({{int:last}})''' = lainnya awan ralatan sabalumnya, '''{{int:minoreditletter}}''' = babakan sapalih.",
- "history-fieldset-title": "Tangadahi halam",
+ "history-fieldset-title": "Ralatan nang disaring",
"history-show-deleted": "Nang dihapus haja",
- "histfirst": "Palawasnya",
- "histlast": "Pahanyarnya",
+ "histfirst": "palawasnya",
+ "histlast": "pahanyarnya",
"historysize": "($1 {{PLURAL:$1|bita|bibita}})",
"historyempty": "(kusung)",
"history-feed-title": "Ralatan halam",
"mergelog": "Log panggabungan",
"revertmerge": "Walang panggabungan",
"mergelogpagetext": "Di bawah adalah daptar nang paling hanyar panggabungan matan sabuah tungkaran halam ka dalam nang lain.",
- "history-title": "Ralatan halam matan ''$1''",
+ "history-title": "Sajarah ralatan matan \"$1\"",
"difference-title": "$1: Pabidaan ralatan",
"difference-multipage": "(Nang balain antar tungkaran-tungkaran)",
"lineno": "Baris $1:",
"showhideselectedversions": "Tampaiakan/sungkupakan ralatan-ralatan",
"editundo": "walangi",
"diff-empty": "(Kadada bida)",
+ "diff-multi-sameuser": "({{PLURAL:$1|$1 ralatan antara}} ulih pamakai nang sama kada ditampaiakan)",
"diff-multi-manyusers": "({{PLURAL:$1|Asa ralatan tangah|$1 raralatan tangah}} ulih labih pada $2 {{PLURAL:$2|pamuruk|papamuruk}} kada ditampaiakan)",
"searchresults": "Kulihan panggagaian",
"searchresults-title": "Kulihan gagai gasan \"$1\"",
"search-result-category-size": "{{PLURAL:$1|1 angguta|$1 aangguta}} ({{PLURAL:$2|1 subtumbung|$2 subtutumbung}}, {{PLURAL:$3|1 barakas|$3 babarakas}})",
"search-redirect": "(Diugahakan matan $1)",
"search-section": "(hagian $1)",
+ "search-file-match": "(rasuk lawan isi barakas)",
"search-suggest": "Nginikah maksud Pian: $1",
"search-interwiki-caption": "Dingsanak rangka gawian",
"search-interwiki-default": "Kulihan $1",
"recentchanges": "Paubahan pahanyarnya",
"recentchanges-legend": "Pilihan paubahan pahanyarnya",
"recentchanges-summary": "Jajak paubahan wiki pahanyarnya pada tungkaran ngini",
+ "recentchanges-noresult": "Kadada paubahan dalam rantang waktu ngini nang rasuk lawan syarat.",
"recentchanges-feed-description": "Susuri paubahan pahanyarnya dalam wiki di kitihan ini",
"recentchanges-label-newpage": "Babakan ngini maulah sabuting tungkaran hanyar",
"recentchanges-label-minor": "Ngini sabuting babakan sapalih",
"recentchanges-label-plusminus": "Paubahan ukuran tungkaran dalam bita",
"recentchanges-legend-heading": "<strong>Katarangan:</strong>",
"recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (tiringi jua [[Special:NewPages|daptar tungkaran hanyar]])",
- "rcnotefrom": "Di bawah ngini paubahan tumatan '''$2''' (ditampaiakan sampai '''$1''' paubahan)",
+ "rcnotefrom": "Di bawah ngini adalah {{PLURAL:$5|paubahan}} tumatan <strong>$3, $4</strong> (ditampaiakan sampai <strong>$1</strong> paubahan).",
"rclistfrom": "Tampaiakan paubahan pahanyarnya matan $3 $2",
"rcshowhideminor": "$1 pambabakan sapalih",
+ "rcshowhideminor-show": "Tampaiakan",
"rcshowhideminor-hide": "Sungkupakan",
"rcshowhidebots": "$1 bot",
"rcshowhidebots-show": "Tampaiakan",
+ "rcshowhidebots-hide": "Sungkupakan",
"rcshowhideliu": "$1 pamakai tadaptar",
+ "rcshowhideliu-show": "Tampaiakan",
"rcshowhideliu-hide": "Sungkupakan",
"rcshowhideanons": "$1 pamakai kada bangaran",
+ "rcshowhideanons-show": "Tampaiakan",
"rcshowhideanons-hide": "Sungkupakan",
"rcshowhidepatr": "$1 babakan ta'awasi",
"rcshowhidemine": "$1 babakan ulun",
+ "rcshowhidemine-show": "Tampaiakan",
"rcshowhidemine-hide": "Sungkupakan",
"rclinks": "Tampaiakan $1 paubahan pahanyarnya dalam $2 hari tauncit",
"diff": "bida",
"querypage-disabled": "Tungkaran istimiwa ngini dikada-kawakan gasan alasan ginawi.",
"booksources": "Buku bamula",
"booksources-search-legend": "Gagai gasan buku asal mula",
+ "booksources-search": "Gagai",
"booksources-text": "Di bawah adalah sabuah daptar tautan ka situs lain nang manjual bubuku hanyar wan bakas, wan jua baisi panjalasan labih pasal bubuku nang Pian ugai:",
"booksources-invalid-isbn": "ISBN nang dibari mancungul kada sah; pariksa kalua-ai tasalah marekap matan asal-mula aslinya.",
"specialloguserlabel": "Pamakai:",
"delete-warning-toobig": "Tungkaran ngini baisi halam babakan ganal, labih pada $1 {{PLURAL:$1|ralatan|raralatan}}.\nMahapus ngini kawa mangaruhi databasis oparasi {{SITENAME}};\njalanakan awan ba-a-awas.",
"rollback": "Gulung bulik babakan",
"rollbacklink": "bulikakan",
+ "rollbacklinkcount": "bulikakan $1 {{PLURAL:$1|babakan}}",
"rollbackfailed": "Guling-bulik luput",
"cantrollback": "Kada kawa mambalikakan babakan;\npanyumbang tauncit adalah asa-asanya panulis tungkaran ngini.",
"alreadyrolled": "Kada kawa malakukan pambulikan ka ralatan tauncit [[:$1]] ulih [[User:$2|$2]] ([[User talk:$2|pandir]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]);\npamuruk lain sudah mambabak atawa malakukan pambulikan lawan tungkaran ini.\n\nBabakan tauncit dilakukan ulih [[User:$3|$3]] ([[User talk:$3|pandir]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
"contributions-title": "Sumbangan pamakai gasan $1",
"mycontris": "Sumbangan",
"anoncontribs": "Sumbangan",
- "contribsub2": "Gasan $1 ($2)",
+ "contribsub2": "Gasan {{GENDER:$3|$1}} ($2)",
"nocontribs": "Kadada paubahan nang rasuk lawan syarat itu.",
- "uctop": " atas",
+ "uctop": "wayah ini",
"month": "Matan bulan (wan sabalumnya):",
"year": "Matan tahun (wan sabalumnya):",
"sp-contributions-newbies": "Tampaiakan sumbangan papamakai hanyar haja",
"sp-contributions-search": "Gagai gasan sumbangan",
"sp-contributions-username": "Alamat IP atawa ngaran-pamakai:",
"sp-contributions-toponly": "Tampaiakan wastu ralatan nang paling atas (pauncitnya)",
+ "sp-contributions-newonly": "Hanya tampaiakan babakan nang barupa paulahan tungkaran",
"sp-contributions-submit": "Gagai",
"whatlinkshere": "Tautan apa di sia",
"whatlinkshere-title": "Tungkaran-tungkaran nang batautan ka ''$1''",
"revdelete-uname-unhid": "ngaran-pamuruk kada tasungkup",
"revdelete-restricted": "Talamar pambatasan hagan pambakal-pambakal",
"revdelete-unrestricted": "Buang pambatasan gasan pambakal-pambakal",
- "logentry-move-move": "$1 mamindahakan tungkaran $3 ka $4",
- "logentry-move-move-noredirect": "$1 diugah tungkaran $3 ka $4 awan-kada maninggalakan sabuah paugahan",
+ "logentry-move-move": "$1 {{GENDER:$2|mamindahakan}} tungkaran $3 ka $4",
+ "logentry-move-move-noredirect": "$1 {{GENDER:$2|mamindahakan}} tungkaran $3 ka $4 kada pakai maulah paugahan",
"logentry-move-move_redir": "$1 diugah tungkaran $3 ka $4 lung paugahan",
"logentry-move-move_redir-noredirect": "$1 diugah tungkaran $3 ka $4 lung sabuah paugahan awan-kada maninggalakan sabuah paugahan",
"logentry-patrol-patrol": "$1 diciri'i ralatan $4 matan tungkaran $3 taawasi",
"logentry-newusers-create": "$1 {{GENDER:$2|maulah}} akun pamakai",
"logentry-newusers-create2": "$1 ma-ulah sabuting akun pamakai $3",
"logentry-newusers-autocreate": "Akun $1 utumatis diulah",
+ "logentry-upload-upload": "$1 {{GENDER:$2|ma-unggah}} $3",
"rightsnone": "(kadada)",
"feedback-adding": "Manambahi kitihanbalik ka tungkaran...",
"feedback-bugcheck": "Harat! hanyar dipariksa bahwasa ngini lainan salah asa [$1 bug nang dipinandui].",
"edit-gone-missing": "পাতাটি হালনাগাদ হয়নি।\nসম্ভবতঃ পাতাটি মুছে ফেলা হয়েছে।",
"edit-conflict": "সম্পাদনা সংঘাত।",
"edit-no-change": "আপনার সম্পাদনাটি উপেক্ষা করা হয়েছে, কারণ লেখাতে কোনো পরিবর্তন করা হয়নি।",
- "edit-slots-cannot-add": "নিà¦\9aà§\87র {{PLURAL:$1|পাতাà¦\9fি|পাতাসমূহ}} এখানে সমর্থিত নয়: $2।",
+ "edit-slots-cannot-add": "নিà¦\9aà§\87র {{PLURAL:$1|সà§\8dলà¦\9fà¦\9fি|সà§\8dলà¦\9fসমূহ}} এখানে সমর্থিত নয়: $2।",
"edit-slots-cannot-remove": "নিচের {{PLURAL:$1|স্লট|স্লটসমূহ}} প্রয়োজন এবং বাদ দেওয়া যাবে না: $2।",
"edit-slots-missing": "নিচের {{PLURAL:$1|স্লট|স্লটসমূহ}} পাওয়া যায়নি: $2।",
"postedit-confirmation-created": "পাতাটি তৈরি করা হয়েছে।",
"action-changetags": "নির্দিষ্ট সংস্করণ এবং লগ ভুক্তিগুলিতে যথেচ্ছভাবে ট্যাগ সংযোজন ও অপসারণ করা",
"action-deletechangetags": "ডাটাবেজ থেকে ট্যাগ অপসরণ করার",
"action-purge": "এই পাতাটি শোধন করুন",
- "action-apihighlimits": "API কোয়েরি হিসাবে আরও উচ্চ লিমিট ব্যবহার করুন",
- "action-autoconfirmed": "à¦\86à¦\87পি-à¦à¦¿à¦¤à§\8dতিà¦\95 রà§\87à¦\9f সà§\80মানা দà§\8dবারা পà§\8dরà¦à¦¾à¦¬à¦¿à¦¤ নয়।",
- "action-bigdelete": "বিশাল à¦\87তিহাস সমà§\8dবলিত পাতা মà§\81à¦\9bà§\87 ফà§\87লà§\8b",
- "action-blockemail": "à¦\87-মà§\87à¦\87ল পাঠাতà§\87 à¦\95à§\8bনà§\8b বà§\8dযবহারà¦\95ারà§\80à¦\95à§\87 বাà¦\81ধা দাà¦\93",
- "action-bot": "সয়à¦\82à¦\95à§\8dরিয় পদà§\8dধতি হিসাবà§\87 à¦\9aিহà§\8dনিত à¦\95রণ",
+ "action-apihighlimits": "API কোয়েরিতে আরো উচ্চতর সীমা ব্যবহার করার",
+ "action-autoconfirmed": "à¦\86à¦\87পি-à¦à¦¿à¦¤à§\8dতিà¦\95 রà§\87à¦\9f সà§\80মার দà§\8dবারা পà§\8dরà¦à¦¾à¦¬à¦¿à¦¤ না হবার",
+ "action-bigdelete": "বিশাল à¦\87তিহাস সমà§\8dবলিত পাতা à¦\85পসারণ à¦\95রার",
+ "action-blockemail": "à¦\95à§\8bনà§\8b বà§\8dযবহারà¦\95ারà§\80à¦\95à§\87 à¦\87-মà§\87à¦\87ল পাঠানà§\8b থà§\87à¦\95à§\87 বাধা দà§\87য়ার",
+ "action-bot": "সà§\8dবয়à¦\82à¦\95à§\8dরিয় পদà§\8dধতি হিসাবà§\87 à¦\9aিহà§\8dনিতà¦\95রণ à¦\95রার",
"action-editprotected": "\"{{int:protect-level-sysop}}\" হিসেবে সুরক্ষিত পাতা সম্পাদনা করার",
"action-editsemiprotected": "\"{{int:protect-level-autoconfirmed}}\" হিসেবে সুরক্ষিত পাতা সম্পাদনা করার",
- "action-editinterface": "ব্যবহারকারী ইন্টারফেস সম্পাদনা",
- "action-editusercss": "অন্য ব্যবহারকারীগণের CSS ফাইল সম্পাদনা",
- "action-edituserjson": "অন্য ব্যবহারকারীগণের JSON ফাইল সম্পাদনা",
- "action-edituserjs": "অন্য ব্যবহারকারীগণের জাভাস্ক্রিপ্ট ফাইল সম্পাদনা",
+ "action-editinterface": "ব্যবহারকারী ইন্টারফেস সম্পাদনা করার",
+ "action-editusercss": "অন্য ব্যবহারকারীগণের CSS ফাইল সম্পাদনা করার",
+ "action-edituserjson": "অন্য ব্যবহারকারীগণের JSON ফাইল সম্পাদনা করার",
+ "action-edituserjs": "অন্য ব্যবহারকারীগণের জাভাস্ক্রিপ্ট ফাইল সম্পাদনা করার",
"action-editsitecss": "সাইটব্যাপী CSS সম্পাদনা করার",
"action-editsitejson": "সাইটব্যাপী JSON সম্পাদনা করার",
"action-editsitejs": "সাইটব্যাপী জাভাস্ক্রিপ্ট সম্পাদনা করার",
"action-editmyusercss": "স্ব ব্যবহারকারীর CSS ফাইল সম্পাদনা করার",
- "action-editmyuserjson": "à¦\86পনার নিà¦\9cসà§\8dব বà§\8dযবহারà¦\95ারà§\80 JSON ফাà¦\87ল সমà§\8dপাদনা à¦\95রা",
- "action-editmyuserjs": "à¦\86পনার নিà¦\9cসà§\8dব বà§\8dযবহারà¦\95ারà§\80 à¦\9cাà¦à¦¾à¦¸à§\8dà¦\95à§\8dরিপà§\8dà¦\9f ফাà¦\87ল সমà§\8dপাদনা à¦\95রà§\81ন",
- "action-viewsuppressed": "যà§\87à¦\95à§\8bন বà§\8dযবহারà¦\95ারà§\80র à¦\95াà¦\9b থà§\87à¦\95à§\87 লà§\81à¦\95ানà§\8b সà¦\82সà§\8dà¦\95রণà¦\97à§\81লি দà§\87à¦\96à§\81ন",
- "action-hideuser": "বà§\8dযবহারà¦\95ারà§\80à¦\95à§\87 বাধা দিন, à¦\8fবà¦\82 সরà§\8dবসাধারণà§\87র দà§\83ষà§\8dà¦\9fিসà§\80মা থà§\87à¦\95à§\87 সরিয়à§\87 নিন",
- "action-ipblock-exempt": "আইপি বাধা, স্বয়ংক্রিয় বাধা ও পরিসীমার বাধা এড়ানো",
+ "action-editmyuserjson": "সà§\8dব বà§\8dযবহারà¦\95ারà§\80 JSON ফাà¦\87ল সমà§\8dপাদনা à¦\95রার",
+ "action-editmyuserjs": "সà§\8dব বà§\8dযবহারà¦\95ারà§\80 à¦\9cাà¦à¦¾à¦¸à§\8dà¦\95à§\8dরিপà§\8dà¦\9f ফাà¦\87ল সমà§\8dপাদনা à¦\95রার",
+ "action-viewsuppressed": "যà§\87à¦\95à§\8bন বà§\8dযবহারà¦\95ারà§\80র à¦\95াà¦\9b থà§\87à¦\95à§\87 লà§\81à¦\95ানà§\8b সà¦\82সà§\8dà¦\95রণà¦\97à§\81লি দà§\87à¦\96ার",
+ "action-hideuser": "বà§\8dযবহারà¦\95ারà§\80à¦\95à§\87 বাধা দà§\87য়ার, à¦\8fবà¦\82 তা সরà§\8dবসাধারণà§\87র দà§\83ষà§\8dà¦\9fিসà§\80মা থà§\87à¦\95à§\87 লà§\81à¦\95ানà§\8bর",
+ "action-ipblock-exempt": "আইপি বাধা, স্বয়ংক্রিয় বাধা ও পরিসীমার বাধা এড়ানোর",
"action-unblockself": "নিজেকে বাধামুক্ত করার",
- "action-noratelimit": "রà§\87à¦\9f লিমিà¦\9fà§\87র à¦à¦¿à¦¤à§\8dতিতà§\87 পরিবরà§\8dতন হবà§\87 না",
- "action-reupload-own": "নিà¦\9cà§\87র দà§\8dবারা à¦\86পলà§\8bডà¦\95à§\83ত ফাà¦\87ল যা à¦\87তিমধà§\8dযà§\87à¦\87 বিদà§\8dযমান, সà§\87à¦\9fি মà§\81à¦\9bà§\87 পà§\81নরায় নতà§\81ন à¦\95রà§\87 à¦\86পলà§\8bড à¦\95রা",
- "action-nominornewtalk": "বারà§\8dতা লà§\87à¦\96ার মত à¦\86লাপ পাতায় à¦\95à§\8bনà§\8b à¦\85নà§\81লà§\8dলà§\87à¦\96à§\8dয সমà§\8dপাদনা নà§\87à¦\87",
- "action-markbotedits": "ফà§\87রত à¦\86না সমà§\8dপাদনাসমà§\82হà¦\95à§\87 বà¦\9f সমà§\8dপাদনা হিসà§\87বà§\87 à¦\9aিহà§\8dনিত à¦\95রà§\87",
- "action-patrolmarks": "সামà§\8dপà§\8dরতিà¦\95 পরিবরà§\8dতনà§\87র পরà§\80à¦\95à§\8dষিত à¦\9aিহà§\8dন দà§\87à¦\96াà¦\93",
- "action-override-export-depth": "লিà¦\82à¦\95সহ পাতা যার à¦\97à¦à§\80রতা ৫ à¦\8fর মধà§\8dযà§\87 সà§\87à¦\97à§\81লà§\8b রপà§\8dতানি à¦\95রà§\81ন",
- "action-suppressredirect": "পাতা স্থানান্তরের সময় মূল পাতা থেকে পুনর্নির্দেশ তৈরী করছে না",
+ "action-noratelimit": "রà§\87à¦\9f সà§\80মার দà§\8dবারা পà§\8dরà¦à¦¾à¦¬à¦¿à¦¤ না হবার",
+ "action-reupload-own": "নিà¦\9cà§\87র দà§\8dবারা à¦\86পলà§\8bডà¦\95à§\83ত ফাà¦\87ল পà§\81নরà§\8dলিà¦\96নà§\87র",
+ "action-nominornewtalk": "à¦\86লà§\8bà¦\9aনার পà§\83ষà§\8dঠাà¦\97à§\81লিতà§\87 à¦\85নà§\81লà§\8dলà§\87à¦\96à§\8dয সমà§\8dপাদনা নà§\87à¦\87 নতà§\81ন বারà§\8dতা পà§\8dরমà§\8dপà¦\9f à¦\9fà§\8dরিà¦\97ার à¦\95রার",
+ "action-markbotedits": "ফà§\87রত à¦\86না সমà§\8dপাদনাসমà§\82হà¦\95à§\87 বà¦\9f সমà§\8dপাদনা হিসà§\87বà§\87 à¦\9aিহà§\8dনিত à¦\95রার",
+ "action-patrolmarks": "সামà§\8dপà§\8dরতিà¦\95 পরিবরà§\8dতনà§\87র পরà§\80à¦\95à§\8dষণà§\87র à¦\9aিহà§\8dন দà§\87à¦\96ার",
+ "action-override-export-depth": "৫-à¦\8fর à¦\97à¦à§\80রতা পরà§\8dযনà§\8dত সà¦\82যà§\8bà¦\97à¦\95à§\83ত পাতাসহ পাতাà¦\97à§\81লি রপà§\8dতানি à¦\95রার",
+ "action-suppressredirect": "পাতা স্থানান্তর করার সময় উৎস পাতা থেকে পুনর্নির্দেশ তৈরী করার",
"nchanges": "$1টি {{PLURAL:$1|পরিবর্তন}}",
"enhancedrc-since-last-visit": "{{PLURAL:$1|সর্বশেষ প্রদর্শনের পর}} $1টি",
"enhancedrc-history": "ইতিহাস",
"rcfilters-filter-watchlist-notwatched-description": "আপনার নজরতালিকায় থাকা পাতাগুলি ব্যতীয় সবকিছু।",
"rcfilters-filtergroup-watchlistactivity": "নজরতালিকার কার্যক্রম",
"rcfilters-filter-watchlistactivity-unseen-label": "অদেখা পরিবর্তন",
- "rcfilters-filter-watchlistactivity-unseen-description": "à¦\86পনার নà¦\9cরতালিà¦\95ায় থাà¦\95া পাতাà¦\97à§\81লিতà§\87 পরিবরà§\8dতন যà§\87à¦\97à§\81লিতà§\87 à¦\86পনি সমà§\8dপাদনা à¦\95রার পর à¦\86র যাননি।",
+ "rcfilters-filter-watchlistactivity-unseen-description": "পাতাসমà§\82হà§\87র পরিবরà§\8dতন à¦\98à¦\9fার পর থà§\87à¦\95à§\87 à¦\86পনি যà§\87সব পাতা পরিদরà§\8dশন à¦\95রà§\87ননি।",
"rcfilters-filter-watchlistactivity-seen-label": "দেখা পরিবর্তন",
- "rcfilters-filter-watchlistactivity-seen-description": "à¦\86পনার নà¦\9cরতালিà¦\95ায় থাà¦\95া পাতাà¦\97à§\81লিতà§\87 পরিবরà§\8dতন যà§\87à¦\97à§\81লিতà§\87 à¦\86পনি সমà§\8dপাদনা à¦\95রার পর à¦\86র যাননি।",
+ "rcfilters-filter-watchlistactivity-seen-description": "পাতাসমà§\82হà§\87র পরিবরà§\8dতন à¦\98à¦\9fার পর থà§\87à¦\95à§\87 à¦\86পনি যà§\87সব পাতা পরিদরà§\8dশন à¦\95রà§\87à¦\9bà§\87ন।",
"rcfilters-filtergroup-changetype": "পরিবর্তনের ধরন",
"rcfilters-filter-pageedits-label": "পাতার সম্পাদনা",
"rcfilters-filter-pageedits-description": "উইকি বিষয়বস্তু, আলোচনা, বিষয়শ্রেণীর বিবরণ... ইত্যাদিতে সম্পাদনা",
"rcfilters-preference-help": "ছাঁকনিগুলি অনুসন্ধান বা আলোকপাতকরণ কার্যকারিতা ছাড়া সাম্প্রতিক পরিবর্তন লোড করে",
"rcfilters-watchlist-preference-label": "জাভাস্ক্রিপ্টহীন ইন্টারফেস ব্যবহার করুন",
"rcfilters-watchlist-preference-help": "ছাঁকনি অনুসন্ধান বা আলোকপাতকরণ বৈশিষ্ট্য ছাড়া নজরতালিকা লোড করে।",
- "rcfilters-filter-showlinkedfrom-label": "লিà¦\82à¦\95 à¦\95রা à¦\8fমন পাতাà¦\97à§\81লà§\8bর পরিবর্তন দেখান",
- "rcfilters-filter-showlinkedfrom-option-label": "নির্বাচিত পাতা থেকে <strong>পাতা লিংক করা</strong>",
- "rcfilters-filter-showlinkedto-label": "পাতা লিà¦\82à¦\95 à¦\95রা à¦\8fমন পাতাসমূহের পরিবর্তন দেখান",
- "rcfilters-filter-showlinkedto-option-label": "নির্বাচিত পাতা থেকে <strong>পাতা লিংক করা</strong>",
+ "rcfilters-filter-showlinkedfrom-label": "à¦\8fà¦\9fি থà§\87à¦\95à§\87 সà¦\82যà§\8bà¦\97à¦\95ারà§\80 পাতাসমà§\82হà§\87র পরিবর্তন দেখান",
+ "rcfilters-filter-showlinkedfrom-option-label": "নির্বাচিত পাতাটি থেকে <strong>সংযোগকারী পাতাসমূহ</strong>",
+ "rcfilters-filter-showlinkedto-label": "à¦\8fà¦\9fিতà§\87 সà¦\82যà§\8bà¦\97à¦\95ারà§\80 পাতাসমূহের পরিবর্তন দেখান",
+ "rcfilters-filter-showlinkedto-option-label": "নির্বাচিত পাতাটিতে <strong>সংযোগকারী পাতাসমূহ</strong>",
"rcfilters-target-page-placeholder": "একটি পাতার নাম (বা বিষয়শ্রেণী) লিখুন",
"rcnotefrom": "<strong>$2</strong>টা থেকে সংঘটিত পরিবর্তনগুলি (সর্বোচ্চ <strong>$1টি</strong> দেখানো হয়েছে)।",
"rclistfromreset": "তারিখ নির্বাচন পুনঃস্থাপন করুন",
"uploaded-script-svg": "আপলোডকৃত SVG ফাইলে স্ক্রিপ্টযোগ্য উপাদান \"$1\" পাওয়া গেছে।",
"uploaded-hostile-svg": "আপলোড করা SVG ফাইলের শৈলী উপাদানে অনিরাপদ সিএসএস পাওয়া গেছে।",
"uploaded-event-handler-on-svg": "এসভিজি ফাইলের জন্য <code>$1=\"$2\"</code> ইভেন্ট-হ্যান্ডলার বৈশিষ্ট্যটি নির্ধারণ করা অনুমোদিত নয়।",
- "uploaded-href-attribute-svg": "এসভিজি ফাইলের href বৈশিষ্ট্যগুলির জন্য কেবলমাত্র http:// বা https:// লক্ষ্যগুলি অনুমোদিত। অন্য বিষয় যেমন, <image>, শুধুমাত্র উপাত্ত ও বৈশিষ্ঠগুলো গ্রহণযোগ্য। <code><$1 $2=\"$3\"></code> পাওয়া গেছে।",
+ "uploaded-href-attribute-svg": "<a> উপাদান শুধুমাত্র উপাত্তে সংযোগ (href) করা যাবে: (এম্বেড করা ফাইল), http:// বা https://, বা খণ্ডিত (#, একই-নথি) লক্ষ্যগুলি। অন্যান্য উপাদানের জন্য, যেমন <image>, কেবলমাত্র উপাত্ত: ও খণ্ড অনুমোদিত। আপনার এসভিজি রপ্তানি করার সময় ছবি এম্বেড করার চেষ্টা করুন। <code><$1 $2=\"$3\"></code> পাওয়া গেছে।",
"uploaded-href-unsafe-target-svg": "অনিরাপদ উপাত্তে href পাওয়া গেছে: আপলোডকৃত SVG ফাইলে URI লক্ষ্য ছিল <code><$1 $2=\"$3\"></code>।",
"uploaded-animate-svg": "\"animate\" ট্যাগটি পাওয়া গেছে যা আপলোডকৃত এসভিজি ফাইলের <code><$1 $2=\"$3\"></code> - এই \"from\" অ্যাট্রিবিউটটি ব্যবহার করে href পরিবর্তন করতে পারে।",
"uploaded-setting-event-handler-svg": "ইভেন্ট-হ্যান্ডলার অ্যাট্রিবিউট নির্ধারণ করতে বাধা দেওয়া হয়েছে। আপলোডকৃত এসভিজি ফাইলে <code><$1 $2=\"$3\"></code> খুঁজে পাওয়া গেছে।",
"blocklogpage": "বাধা দানের লগ",
"blocklog-showlog": "এই ব্যবহারকারীকে পূর্বেও বাধা প্রদান করা হয়েছিলো।\nতথ্যসূত্র হিসেবে তাই পূর্বের বাধাদানের লগটি নিচে প্রদর্শন করা হচ্ছে:",
"blocklog-showsuppresslog": "এই ব্যবহারকারীকে পূর্বেও বাধা প্রদান ও লুকানো হয়েছিলো।\nতথ্যসূত্র হিসেবে তাই পূর্বের অপসারণ লগটি নিচে প্রদর্শন করা হচ্ছে:",
- "blocklogentry": "[[$1]] à¦\95à§\87 $2 মà§\87য়াদের জন্য বাধাদান করেছেন $3",
- "reblock-logentry": "[[$1]] এর ব্লক সেটিং পরিবর্তন করা হয়েছে যেটি শেষ হবে $2 $3 সময়ে",
+ "blocklogentry": "[[$1]] à¦\95à§\87 $2 সময়ের জন্য বাধাদান করেছেন $3",
+ "reblock-logentry": "[[$1]]-এর বাধাদান সেটিং পরিবর্তন করেছেন যেটি শেষ হবার মেয়াদ $2 $3",
"blocklogtext": "এটি ব্যবহারকারীদেরকে বাধা দানের বা বাধা তুলে নেওয়ার লগ।\nস্বয়ংক্রিয়ভাবে বাধাদানকৃত আইপি ঠিকানাগুলি এখানে তালিকাবদ্ধ করা হয়নি।\nবর্তমানে সক্রিয় নিষিদ্ধকরণ ও বাধাদানের তালিকার জন্য [[Special:BlockList| বাধাদান তালিকা]] দেখুন।",
"unblocklogentry": "$1-এর উপর বাধা তুলে নেয়া হয়েছে",
"block-log-flags-anononly": "কেবল বেনামী ব্যবহারকারীরা",
"ipb_expiry_old": "মেয়াদোত্তীর্ণের সময় অতীত হয়েছে।",
"ipb_expiry_temp": "লুকানো ব্যবহারকারীনাম বাধা চিরস্থায়ী হতে হবে।",
"ipb_hide_invalid": "এই অ্যাকাউন্ট বাধা দেয়া সম্ভব নয়; এটি {{PLURAL:$1|একের অধিক|$1টি}} সম্পাদনা করেছে।",
- "ipb_hide_partial": "লà§\81à¦\95ায়িত বà§\8dযবহারà¦\95ারà§\80 নামà§\87র বাধাদান à¦\85বশà§\8dযà¦\87 সাà¦\87à¦\9fà¦\93য়াà¦\87ড হতে হবে।",
+ "ipb_hide_partial": "লà§\81à¦\95ানà§\8b বà§\8dযবহারà¦\95ারà§\80 নামà§\87র বাধাদান à¦\85বশà§\8dযà¦\87 সাà¦\87à¦\9fবà§\8dযপà§\80 হতে হবে।",
"ipb_already_blocked": "\"$1\" ইতিমধ্যে বাধাপ্রাপ্ত।",
"ipb-needreblock": "$1 ইতিমধ্যেই বাধাপ্রাপ্ত আছেন। আপনি কি সেটিংস পরিবর্তন করতে চান?",
"ipb-otherblocks-header": "অন্যান্য {{PLURAL:$1|বাধা|বাধাসমূহ}}",
"revdelete-unrestricted": "এই সীমাবদ্ধতা প্রশাসকের ক্ষেত্রে তুলে নাও",
"logentry-block-block": "$1 {{GENDER:$4|$3}} কে $5 মেয়াদের জন্য {{GENDER:$2|বাধাদান}} করেছেন $6",
"logentry-block-unblock": "$1 {{GENDER:$4|$3}}-এর উপর থেকে বাধা তুলে {{GENDER:$2|নিয়েছেন}}",
- "logentry-block-reblock": "$1 {{GENDER:$4|$3}}-এর জন্য বাধাদান সেটিং $5 সময়ের জন্য {{GENDER:$2|পরিবর্তন}} করেছেন $6",
+ "logentry-block-reblock": "$1 {{GENDER:$4|$3}}-এর বাধাদান সেটিং {{GENDER:$2|পরিবর্তন করেছেন}} যেটি শেষ হবার মেয়াদ $5 $6",
+ "logentry-partialblock-block-page": "$2 {{PLURAL:$1|পাতাটি|পাতাগুলি}}",
+ "logentry-partialblock-block-ns": "$2 {{PLURAL:$1|নামস্থানটি|নামস্থানগুলি}}",
+ "logentry-partialblock-block": "$1 {{GENDER:$4|$3}} কে $7 সম্পাদনা করা থেকে $5 সময়ের জন্য {{GENDER:$2|বাধাদান করেছেন}} $6",
+ "logentry-partialblock-reblock": "$1 $7তে সম্পাদনা করা প্রতিরোধ করে {{GENDER:$4|$3}}-এর বাধাদান সেটিং {{GENDER:$2|পরিবর্তন করেছেন}} যেটি শেষ হবার মেয়াদ $5 $6",
+ "logentry-non-editing-block-block": "$1 {{GENDER:$4|$3}} কে সম্পাদনা-ছাড়া নির্দিষ্ট কর্ম করা থেকে $5 সময়ের জন্য {{GENDER:$2|বাধাদান করেছেন}} $6",
+ "logentry-non-editing-block-reblock": "$1 সম্পাদনা-ছাড়া নির্দিষ্ট কর্মের জন্য {{GENDER:$4|$3}}-এর বাধাদান সেটিং {{GENDER:$2|পরিবর্তন করেছেন}} যেটি শেষ হবার মেয়াদ $5 $6",
"logentry-suppress-block": "$1 {{GENDER:$4|$3}} কে $5 মেয়াদের জন্য {{GENDER:$2|বাধাদান}} করেছেন $6",
- "logentry-suppress-reblock": "$1 {{GENDER:$4|$3}}-à¦\8fর à¦\9cনà§\8dয বাধাদান সà§\87à¦\9fিà¦\82 $5 সময়à§\87র à¦\9cনà§\8dয {{GENDER:$2|পরিবরà§\8dতন}} à¦\95রà§\87à¦\9bà§\87ন $6",
+ "logentry-suppress-reblock": "$1 {{GENDER:$4|$3}}-à¦\8fর বাধাদান সà§\87à¦\9fিà¦\82 {{GENDER:$2|পরিবরà§\8dতন à¦\95রà§\87à¦\9bà§\87ন}} যà§\87à¦\9fি শà§\87ষ হবার মà§\87য়াদ $5 $6",
"logentry-import-upload": "$1 ফাইল আপলোড দ্বারা $3 {{GENDER:$2|আমদানি করেছেন}}",
"logentry-import-upload-details": "$1 ফাইল আপলোড দ্বারা $3 {{GENDER:$2|আমদানি করেছেন}} ($4টি {{PLURAL:$4|সংশোধন}})",
"logentry-import-interwiki": "$1 অন্য একটি উইকিতে থেকে $3 {{GENDER:$2|আমদানি করেছে}}",
"tag-mw-removed-redirect": "дӀаяьккхина дӀасхьажорг",
"tag-mw-changed-redirect-target": "хийцаран бахьна ду дӀасахьажорг",
"tag-mw-blank": "цӀанъяр",
+ "tag-mw-replace": "хийцар",
"tag-mw-rollback": "Юхаяккха",
"tag-mw-undo": "юхаяккхар",
"tags-title": "Билгалонаш",
"badretype": "De indtastede adgangskoder er ikke ens.",
"usernameinprogress": "En oprettelse af konto for dette brugernavn er allerede i gang.\nVent venligst.",
"userexists": "Det brugernavn, du har valgt, er allerede i brug.\nVælg venligst et andet brugernavn.",
- "createacct-normalization": "Dit brugernavn vil blive ændret til «$2» på grund af tekniske begrænsninger.",
+ "createacct-normalization": "Dit brugernavn vil blive ændret til \"$2\" på grund af tekniske begrænsninger.",
"loginerror": "Logon mislykket",
"createacct-error": "Fejl ved kontooprettelse",
"createaccounterror": "Kunne ikke oprette brugerkonto: $1",
"content-json-empty-array": "Tomt matrix",
"deprecated-self-close-category": "Sider, der bruger ugyldige, selvlukkende HTML-tags",
"deprecated-self-close-category-desc": "Siden bruger ugyldige selvlukkende HTML tags, som <code><b/></code> eller <code><span/></code>. De vil snart blive ændret i overensstemmelse med HTML5-specifikationen, så de ikke kan bruges i wikitext.",
- "duplicate-args-warning": "<strong>Advarsel</strong>: [[:$1]] kaldes [[:$2]] med flere end en værdi for \"$3\"-parameteren. Bare den sidst angitte værdien vil bruges.",
+ "duplicate-args-warning": "<strong>Advarsel</strong>: [[:$1]] kalder [[:$2]] med mere end en værdi for \"$3\"-parameteren. Kun den sidst angivne værdi vil blive brugt.",
"duplicate-args-category": "Sider der bruger samme argument mere end en gang i en skabelon",
"duplicate-args-category-desc": "Siden indeholder en skabelon hvor et argument er brugt mere end en gang, som <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> eller <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
"expensive-parserfunction-warning": "Advarsel: Der er for mange beregningstunge oversætter-funktionskald på denne side.\n\nDer bør være færre end {{PLURAL:$2|$2 kald}}, lige nu er der {{PLURAL:$1|$1 kald}}.",
"post-expand-template-argument-category": "Sider med udeladte skabelonparametre",
"parser-template-loop-warning": "Skabelonløkke fundet: [[$1]]",
"template-loop-category": "Sider med skabelonløkker",
- "template-loop-category-desc": "Siden indeholder en malløkke, altså en skabelon som kalder sig selv rekursivt.",
+ "template-loop-category-desc": "Siden indeholder en skabelonløkke, det vil sige en skabelon som kalder sig selv rekursivt.",
"parser-template-recursion-depth-warning": "En skabelon er rekursivt inkluderet for mange gange ($1)",
"language-converter-depth-warning": "Dybdegrænse for sprogkonvertering overskredet ($1)",
"node-count-exceeded-category": "Sider hvor antal noder er overskredet",
"page_first": "Starten",
"page_last": "Enden",
"histlegend": "Forklaring: (nuværende) = forskel til den nuværende\nversion, (forrige) = forskel til den forrige version, M = mindre ændring",
- "history-fieldset-title": "Søg efter versioner",
+ "history-fieldset-title": "Filtrer versioner",
"history-show-deleted": "Kun slettede revisioner",
"histfirst": "ældste",
"histlast": "nyeste",
"action-changetags": "tilføje og fjerne vilkårlige tags for enkelte versioner og logposter",
"action-deletechangetags": "slette tags fra databasen",
"action-purge": "rense denne side",
+ "action-bigdelete": "slet sider med store historikker",
+ "action-blockemail": "bloker en bruger fra at sende e-mails",
"action-bot": "blive behandlet som en automatiseret proces",
"nchanges": "$1 {{PLURAL:$1|ændring|ændringer}}",
"enhancedrc-since-last-visit": "$1 {{PLURAL:$1|siden sidste besøg}}",
"rcfilters-savedqueries-add-new-title": "Gem nuværende filterindstillinger",
"rcfilters-restore-default-filters": "Gendan standardfiltre",
"rcfilters-clear-all-filters": "Ryd alle filtre",
- "rcfilters-show-new-changes": "Vis seneste ændringer",
+ "rcfilters-show-new-changes": "Vis seneste ændringer siden $1",
"rcfilters-search-placeholder": "Filtrer ændringer (brug menuen eller søg på filternavn)",
"rcfilters-invalid-filter": "Ugyldigt filter",
"rcfilters-empty-filter": "Ingen aktive filtre. All bidrag vises.",
"delete-confirm": "Slet \"$1\"",
"delete-legend": "Slet",
"historywarning": "<strong>Advarsel:</strong> Siden du er ved at slette har en historie med $1 {{PLURAL:$1|version|versioner}}:",
- "historyaction-submit": "Vis",
+ "historyaction-submit": "Vis revisioner",
"confirmdeletetext": "Du er ved at slette en side sammen med hele dens tilhørende historik.\nBekræft venligst at du virkelig vil gøre dette, at du forstår konsekvenserne, og at du gør det i overensstemmelse med [[{{MediaWiki:Policy-url}}|retningslinjerne]].",
"actioncomplete": "Gennemført",
"actionfailed": "Handlingen mislykkedes",
"PerfektesChaos",
"Kurt Jansson",
"McDutchie",
- "Johanna Strodt (WMDE)"
+ "Johanna Strodt (WMDE)",
+ "Andi-3"
]
},
"tog-underline": "Links unterstreichen:",
"tog-hidepatrolled": "Kontrollierte Änderungen in den „Letzten Änderungen“ ausblenden",
"tog-newpageshidepatrolled": "Kontrollierte Seiten bei den „Neuen Seiten“ ausblenden",
"tog-hidecategorization": "Kategorisierungen von Seiten ausblenden",
- "tog-extendwatchlist": "Alle Änderungen in der Beobachtungsliste anzeigen, nicht nur die aktuellsten",
+ "tog-extendwatchlist": "Alle Änderungen in der Beobachtungsliste anzeigen, nicht nur die letzten",
"tog-usenewrc": "Änderungen auf „Letzte Änderungen“ und der Beobachtungsliste nach Seite gruppieren",
"tog-numberheadings": "Überschriften automatisch nummerieren",
"tog-editondblclick": "Seiten mit Doppelklick bearbeiten",
"previewnote": "'''Dies ist nur eine Vorschau.'''\nDie Seite wurde noch nicht gespeichert!",
"continue-editing": "Zum Bearbeitungsfeld gehen",
"previewconflict": "Diese Vorschau gibt den Inhalt des oberen Textfeldes wieder. So wird die Seite aussehen, wenn du jetzt speicherst.",
- "session_fail_preview": "Entschuldigung! Wir konnten deine Bearbeitung nicht verarbeiten, da Sitzungsdaten verloren gegangen sind.\n\nDu wurdest eventuell abgemeldet. <strong>Bitte verifiziere, dass du noch angemeldet bist und versuche es erneut</strong>.\nFalls dies nicht funktioniert, versuche dich [[Special:UserLogout|abzumelden]] und anschließend wieder anzumelden und überprüfe, ob dein Browser Cookies von dieser Website akzeptiert.",
+ "session_fail_preview": "Entschuldigung! Wir konnten deine Bearbeitung nicht verarbeiten, da Sitzungsdaten verloren gegangen sind.\n\nDu wurdest eventuell abgemeldet. <strong>Bitte stelle sicher, dass du noch angemeldet bist, und versuche es erneut</strong>.\nFalls dies nicht funktioniert, versuche dich [[Special:UserLogout|abzumelden]] und anschließend wieder anzumelden und überprüfe, ob dein Browser Cookies von dieser Website akzeptiert.",
"session_fail_preview_html": "Deine Bearbeitung konnte nicht gespeichert werden, da Sitzungsdaten verloren gegangen sind.\n\n<em>Da in {{SITENAME}} das Speichern von reinem HTML aktiviert ist, wurde die Vorschau ausgeblendet, um JavaScript-Attacken vorzubeugen.</em>\n\n<strong>Bitte versuche es erneut, indem du unter der folgenden Textvorschau nochmals auf „Seite speichern“ klickst.</strong>\nSollte das Problem bestehen bleiben, [[Special:UserLogout|melde dich ab]] und danach wieder an. Überprüfe, ob dein Browser Cookies von dieser Website akzeptiert.",
"token_suffix_mismatch": "'''Deine Bearbeitung wurde zurückgewiesen, da dein Browser Zeichen im Bearbeiten-Token verstümmelt hat.\nEine Speicherung kann den Seiteninhalt zerstören. Dies geschieht bisweilen durch die Benutzung eines anonymen Proxy-Dienstes, der fehlerhaft arbeitet.'''",
"edit_form_incomplete": "'''Der Inhalt des Bearbeitungsformulars hat den Server nicht vollständig erreicht. Bitte prüfe deine Bearbeitungen auf Vollständigkeit und versuche es erneut.'''",
"ncategories": "$1 {{PLURAL:$1|Kategori|Kategoriy}}",
"ninterwikis": "$1 {{PLURAL:$1|interwiki|interwikiy}}",
"nlinks": "$1 {{PLURAL:$1|link|linkî}}",
- "nmembers": "$1 {{PLURAL:$1|eza|ezayan}}",
+ "nmembers": "$1 {{PLURAL:$1|eza|ezayi}}",
"nmemberschanged": "$1 → $2 {{PLURAL:$1|eza|ezayan}}",
"nrevisions": "$1 {{PLURAL:$1|vurnayış|vurnayışi}}",
"nimagelinks": "$1 {{PLURAL:$1|pele de|pelan de}} gureyeno",
"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",
"rcfilters-savedqueries-already-saved": "Ezek a szűrők már el lettek mentve. Módosítsd a beállításokat egy új mentett szűrő készítéséhez.",
"rcfilters-restore-default-filters": "Alapértelmezett szűrők visszaállítása",
"rcfilters-clear-all-filters": "Összes szűrő kikapcsolása",
- "rcfilters-show-new-changes": "Legfrissebb változtatások megtekintése",
+ "rcfilters-show-new-changes": "$1-óta történt friss változtatások megtekintése",
"rcfilters-search-placeholder": "Változtatások szűrése (használd a menüt vagy keress szűrőkre)",
"rcfilters-invalid-filter": "Érvénytelen szűrő",
"rcfilters-empty-filter": "Nincs aktív szűrő. Minden közreműködés látható.",
"passwordpolicies-policy-passwordnotinlargeblacklist": "A jelszó nem szerepelhet a 100 000 leggyakrabban használt jelszó listáján .",
"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."
+ "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-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):",
"blocklist-editing-page": "paginas",
"blocklist-editing-ns": "spatios de nomines",
"ipblocklist-empty": "Le lista de blocadas es vacue.",
- "ipblocklist-no-results": "Le adresse IP o nomine de usator que tu requestava non es blocate.",
+ "ipblocklist-no-results": "Nulle blocadas trovate que corresponde al adresse IP o nomine de usator requestate.",
"blocklink": "blocar",
"unblocklink": "disblocar",
"change-blocklink": "cambiar blocada",
"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",
"prefs-files": "പ്രമാണങ്ങൾ",
"prefs-custom-css": "സ്വന്തം സി.എസ്.എസ്.",
"prefs-custom-json": "ഐച്ഛിക ജെസൺ",
- "prefs-custom-js": "à´¸àµ\8dവനàµ\8dà´¤à´\82 à´\9càµ\86.à´\8eà´¸àµ\8d.",
+ "prefs-custom-js": "à´¸àµ\8dവനàµ\8dà´¤à´\82 à´\9cാവാസàµ\8dà´\95àµ\8dà´°à´¿à´ªàµ\8dà´±àµ\8dà´±àµ\8d",
"prefs-common-config": "എല്ലാ ദൃശ്യരൂപങ്ങൾക്കുമായി പങ്ക് വെയ്ക്കപ്പെട്ട സി.എസ്.എസ്./ജെസൺ/ജാവാസ്ക്രിപ്റ്റ്:",
"prefs-reset-intro": "സൈറ്റിൽ സ്വതേയുണ്ടാവേണ്ട ക്രമീകരണങ്ങൾ പുനഃക്രമീകരിക്കാൻ താങ്കൾക്ക് ഈ താൾ ഉപയോഗിക്കാവുന്നതാണ്.\nഇത് തിരിച്ചു ചെയ്യാൻ സാദ്ധ്യമല്ല.",
"prefs-emailconfirm-label": "ഇമെയിൽ സ്ഥിരീകരണം:",
"allpages-hide-redirects": "ပြန်ညွှန်းများအား ဝှက်ရန်",
"cachedspecial-viewing-cached-ttl": "သင်သည် $1 အချိန်ကြာသွားနိုင်သော ဤစာမျက်နှာ၏ cached ဗားရှင်းကို ကြည့်ရှုနေခြင်း ဖြစ်ပါသည်။",
"cachedspecial-viewing-cached-ts": "သင်သည် ဤစာမျက်နှာ၏ အမှန်တကယ်မဟုတ်နိုင်သော cached ဗားရှင်းကို ကြည့်ရှုနေခြင်းဖြစ်သည်။",
+ "cachedspecial-refresh-now": "နောက်ဆုံးကို ကြည့်ရှုရန်။",
"categories": "ကဏ္ဍများ",
"categories-submit": "ပြသရန်",
"categoriespagetext": "အောက်ပါ {{PLURAL:$1|ကဏ္ဍ|ကဏ္ဍများ}}သည် ဤဝီကီတွင် အသုံးပြု သို့မဟုတ် အသုံးမပြုထားခြင်း ဖြစ်နိုင်သည်။ [[Special:WantedCategories|အလိုရှိသော ကဏ္ဍများ]]ကိုလည်း ကြည့်ပါ။",
"mycontris": "ဆောင်ရွက်ချက်များ",
"anoncontribs": "ဆောင်ရွက်ချက်များ",
"contribsub2": "{{GENDER:$3|$1}}အတွက် ($2)",
+ "contributions-subtitle": "{{GENDER:$3|$1}} အတွက်",
"contributions-userdoesnotexist": "အသုံးပြုသူအကောင့် \"$1\" သည် မှတ်ပုံမတင်ထားပါ။",
"nocontribs": "ဤသတ်မှတ်ချက်များနှင့် ကိုက်ညီသည့် ပြောင်းလဲမှုများ မရှိပါ။",
"uctop": "လက်ရှိ",
"createaccountblock": "အကောင့်ဖန်တီးခြင်းကို ပိတ်ထားသည်",
"emailblock": "အီးမေးကို ပိတ်ပင်ထားသည်",
"blocklist-nousertalk": "မိမိ၏ဆွေးနွေးချက်စာမျက်နှာကို တည်းဖြတ်မရနိုင်ပါ",
+ "blocklist-editing": "တည်းဖြတ်ခြင်း",
+ "blocklist-editing-page": "စာမျက်နှာများ",
+ "blocklist-editing-ns": "အမည်ညွှန်းများ",
"ipblocklist-empty": "ပိတ်ပင်ထားမှုစာရင်းသည် ဗလာဖြစ်နေသည်။",
"ipblocklist-no-results": "တောင်းဆိုလိုက်သော အိုင်ပီလိပ်စာ သို့မဟုတ် အသုံးပြုသူအမည်ကို မပိတ်ပင်ထားပါ။",
"blocklink": "ပိတ်ပင်",
"pageinfo-display-title": "ပြသခေါင်းစဉ်",
"pageinfo-default-sort": "ပုံမှန် စာလုံးစီကီး",
"pageinfo-length": "စာမျက်နှာ အလျား (ဘိုက်ဖြင့်)",
+ "pageinfo-namespace": "အမည်ညွှန်း",
"pageinfo-article-id": "စာမျက်နှာ အိုင်ဒီ",
"pageinfo-language": "စာမျက်နှာ စာကိုယ် ဘာသာစကား",
"pageinfo-language-change": "ပြောင်းလဲရန်",
"log-action-filter-protect-protect": "ကာကွယ်မှု",
"log-action-filter-rights-rights": "လူဖြင့် ပြောင်းလဲမှု",
"log-action-filter-rights-autopromote": "အလိုအလျောက် ပြောင်းလဲမှု",
+ "log-action-filter-upload-revert": "ပြန်ပြောင်းရန်",
"authmanager-create-disabled": "အကောင့်ဖန်တီးခြင်းကို ပိတ်ထားသည်။",
"authmanager-autocreate-noperm": "အလိုအလျာက် အကောင့်ဖန်တီးခြင်းကို ခွင့်မပြုပါ။",
"authmanager-autocreate-exception": "ရှေ့ကအမှားများကြောင့် အလိုအလျာက် အကောင့်ဖန်တီးခြင်းကို ယာယီပိတ်ထားသည်။",
"authmanager-realname-help": "အသုံးပြုသူ၏ အမည်ရင်း",
"authmanager-provider-temporarypassword": "ယာယီစကားဝှက်",
"authprovider-resetpass-skip-label": "ကျော်ရန်",
+ "specialpage-securitylevel-not-allowed-title": "ခွင့်မပြုပါ",
"cannotauth-not-allowed-title": "ခွင့်ပြုချက် ငြင်းပယ်လိုက်သည်",
"cannotauth-not-allowed": "သင်သည် ဤစာမျက်နှာကို အသုံးပြုခွင့်မရှိပါ",
"userjsispublic": "ကျေးဇူးပြု၍ မှတ်သားပါ- JavaScript စာမျက်နှာခွဲများတွင် အခြားအသုံးပြုသူများ ကြည့်ရှုနိုင်သော လျို့ဝှက်အပ်သည့်အချက်အလက် မပါဝင်သင့်ပါ။",
"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]."
}
"Babamamadidianee",
"Lancine.kounfantoh.fofana",
"Lanciné.kounfantoh.fofana",
- "Youssoufkadialy"
+ "Youssoufkadialy",
+ "Amire80",
+ "Nafadji Mory Diané"
]
},
"sunday": "ߞߊ߯ߙߌߟߏ߲",
"hidden-categories": "{{PLURAL:$1|ߦߌߟߡߊ߫ ߘߏ߲߰ߣߍ߲ |ߦߌߟߡߊ߫ ߘߏ߲߰ߣߍ߲ ߠߎ߬}}",
"category-subcat-count": "{{PLURAL:$2|ߦߟߊߡߊߙߋ߲ ߣߌ߲߬ ߠߎ߫ ߜߊ߲߰ߛߊ߲ ߠߋ߫ ߦߋ߫ ߦߌߟߡߊ ߣߌ߲߬ ߘߐ߫.|ߦߌߟߡߊ ߣߊ߬ߕߐ ߟߎ߬ ߘߐ߫߸ {{PLURAL:$1|ߦߌߟߡߊߙߋ߲|$1 ߦߌߟߡߊߙߋ߲ ߠߎ߬}} ߟߋ߬ ߦߴߊ߬ ߘߐ߫߸ ߞߙߎߞߙߍ ߟߎ߬ ߞߐߞߊ߲߬ $2}}",
"category-article-count": "{{PLURAL:$2|ߞߐߜߍ ߣߌ߲߬ ߘߐߙߐ߲߫ ߠߋ߬ ߦߋ߫ ߦߌߟߡߊ ߣߌ߲߬ ߘߐ߫.|ߖߡߊ߬ߦߊ߫ ߕߐ߮ ߣߊ߬ߕߊ {{PLURAL:$1|ߞߐߜߍ ߦߋ߫|$1 ߞߐߜߍ ߦߋ߫}} ߟߋ߬ ߦߋ߫ ߦߌߟߡߊ߫ ߘߌ߫߸ ߞߙߎߞߙߍ $2 ߞߐߞߊ߲߬}}",
- "category-file-count": "{{:$2|ߞߐߕߐ߮ ߣߌ߲߬ ߜߊ߲߰ߛߊ߲ ߠߋ߫ ߦߋ߫ ߦߌߟߡߊ ߣߌ߲߬ ߘߐ߫.|ߡߍ߲ ߠߎ߬ ߦߋ߫ ߣߌ߲߬ {{PLURAL:$1|ߞߐߕߐ߮ ߦߋ߫|$1 ߞߐߕߐ߮ ߟߎ߬ ߦߋ߫}} ߦߌߟߡߊ ߣߌ߲߬ ߘߐ߫߸ ߞߙߎߞߙߍ ߣߌ߲߬ $2 ߕߴߊ߬ ߘߐ߫.}}",
+ "category-file-count": "{{PLURAL:$2|ߞߐߕߐ߮ ߣߌ߲߬ ߜߊ߲߰ߛߊ߲ ߠߋ߫ ߦߋ߫ ߦߌߟߡߊ ߣߌ߲߬ ߘߐ߫.|ߡߍ߲ ߠߎ߬ ߦߋ߫ ߣߌ߲߬ {{PLURAL:$1|ߞߐߕߐ߮ ߦߋ߫|$1 ߞߐߕߐ߮ ߟߎ߬ ߦߋ߫}} ߦߌߟߡߊ ߣߌ߲߬ ߘߐ߫߸ ߞߙߎߞߙߍ ߣߌ߲߬ $2 ߕߴߊ߬ ߘߐ߫.}}",
"listingcontinuesabbrev": "ߖߊ߬ߕߋ߬ߘߊ",
"index-category": "ߞߐߜߍ߫ ߓߊߕߐ߲ߛߐ߲ ߠߎ߬",
"noindex-category": "ߞߐߜߍ߫ ߘߐߕߐ߲ߛߐ߲ߦߊߓߊߟߌ ߟߎ߬",
"about": "ߡߊ߬ߘߎ߮",
- "newwindow": "ߊ߬ ߟߊߞߊ߬ ߝߢߐߘߊ߫ ߞߎߘߊ߫ ߟߊ߫",
+ "newwindow": "(ߊ߬ ߟߊߞߊ߬ ߝߢߐߘߊ߫ ߞߎߘߊ߫ ߟߊ߫)",
"cancel": "ߊ߬ ߘߐߛߊ߬",
"moredotdotdot": "ߡߊߞߊ߬ߝߏ߬...",
"morenotlisted": "ߛߙߍߘߍ ߣߌ߲߬ ߘߝߊߓߊߟߌ߫ ߓߍ߫ ߞߍ߫.",
"navigation-heading": "ߛߏ߲߯ߓߊߟߌ߫ ߓߏߟߏ߲ߘߊ",
"errorpagetitle": "ߝߎ߬ߕߎ߲߬ߕߌ",
"returnto": "ߌ ߞߐߛߊ߬ߦߌ߲߬ ߦߊ߲߬ ߡߊ߬$1",
- "tagline": "ߞߊ߬ ߝߘߊ߫",
+ "tagline": "ߞߊ߬ ߝߘߊ߫{{SITENAMEP}}",
"help": "ߘߍ߬ߡߍ߲߬ߠߌ",
"help-mediawiki": "ߘߍ߬ߡߍ߲߬ߠߌ߲ ߞߊ߬ ߓߍ߲߬ ߥߞߌ-ߟߊߛߋߢߊߥߙߍ ߡߊ߬",
"search": "ߢߌߣߌ߲ߠߌ",
"print": "ߜߌ߬ߙߌ߲߬ߘߌ߬ߟߌ",
"view": "ߊ߬ ߘߐߜߍ߫",
"view-foreign": "ߊ߬ ߦߋ߫ ߦߊ߲߬ $1",
- "edit": "ß\8a߬ ß¡ß\8aß\9dß\8a߬ß\9fß\8b߲߬",
+ "edit": "ß\8a߬ ß¡ß\8aߦß\9fß\8d߬ߡß\8a߲߬",
"create": "ߟߊ߬ߘߊ߲߬ߠߌ",
"create-local": "ߕߌ߲߬ߞߎߘߎ߲ ߞߊ߲߬ߛߓߍ߬ߟߌ ߟߊߘߏ߲߬",
"delete": "ߊ߬ ߖߐ߬ߛߌ߬",
"undelete_short": "ߟߊ߬ߛߊ߬ߦߌ߲߬ߠߌ {{PLURAL:$1|ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߞߋߟߋ߲߫|$1 ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ ߠߎ߬}}",
+ "protect": "ߊ߬ ߟߊߞߊ߲ߘߊ߫",
+ "protect_change": "ߊ߬ ߡߊߦߟߍ߬ߡߊ߲߫",
"unprotect": "ߟߊ߬ߞߊ߲߬ߘߊ߬ߟߌ ߡߊߝߊ߬ߟߋ߲߬ߠߌ",
"newpage": "ߘߐߜߍ߫ ߞߎߘߊ",
"talkpagelinktext": "ߓߊ߬ߘߏ߬ߟߌ",
"talk": "ߓߊ߬ߘߏ߬ߓߊ߬ߘߌߦߊ",
"views": "ߦߌ߬ߘߊ߬ߟߌ",
"toolbox": "ߖߐ߯ߙߊ߲ ߠߎ߬",
+ "tool-link-emailuser": "ߢߎߡߍߙߋ߲ߞߏ߲ߘߏ߫ ߟߊߕߊ߯ {{GENDER:$1|ߟߊߓߊ߯ߙߟߊ ߣߌ߲߬ ߡߊ߬ }}",
+ "imagepage": "ߞߐߕߐ߮ ߞߐߜߍ ߘߐߜߍ߫",
+ "mediawikipage": "ߗߋߛߓߍ ߞߐߜߍ ߘߐߜߍ߫",
+ "templatepage": "ߞߙߊߞߏ ߞߐߜߍ ߘߐߜߍ߫",
+ "viewhelppage": "ߡߊ߬ߘߍ߬ߡߍ߲߬ߠߌ߲ ߞߐߜߍ ߘߐߜߍ߫",
+ "categorypage": "ߦߌߟߡߊ ߞߐߜߍ ߘߐߜߍ߫",
+ "viewtalkpage": "ߢߊߝߐߞߣߍ ߞߐߜߍ ߘߐߜߍ߫",
"otherlanguages": "ߞߊ߲ ߜߘߍ߫ ߟߎ߫ ߘߐ߫",
"redirectedfrom": "(ߌ ߟߊߞߎ߲߬ߛߌ߲߬ߣߍ߲߫ ߞߊ߬ ߓߐ߫ $1)",
"redirectto": "ߌ ߓߘߊ߫ ߟߊߞߎ߲߬ߛߌ߲߫ ߦߊ߲߬ ߠߊ߫:",
- "lastmodifiedat": "ߞߐߜߍ ߣߌ߲߬ ߡߊߝߊߟߋ߲߫ ߟߊߓߊ߲ ߞߍ߫ ߘߊ߫ $1߸ $2",
- "jumpto": "ߊ߬ ߕߌߙߌ߲߫",
+ "lastmodifiedat": "ߞߐߜߍ ߣߌ߲߬ ߡߊߦߟߍ߬ߡߊ߲߬ ߟߊߓߊ߲ ߞߍ߫ ߘߊ߫ $1߸ $2",
+ "protectedpage": "ߞߐߜߍ߫ ߡߊߞߊ߲ߞߊ߲ߣߍ߲",
+ "jumpto": "ߊ߬ ߕߌߙߌ߲߫:",
"jumptonavigation": "ߛߏ߲߯ߓߊߟߌ",
"jumptosearch": "ߊ߬ ߕߌߙߌ߲߫",
+ "pool-timeout": "ߘߊߕߎ߲߯ߠߌ߲ ߡߊ߬ߞߐ߬ߣߐ߲߬ߠߌ߲߬ ߕߎߡߊ ߓߘߊ߫ ߕߊ߬ߡߌ߲߬",
+ "pool-errorunknown": "ߝߌ߬ߟߌ߬ ߛߎ߲߫ ߟߐ߲ߓߊߟߌ",
+ "poolcounter-usage-error": "ߟߊߓߊ߯ߙߊߟߌ߫ ߝߟߌ $1",
"aboutsite": "ߞߊ߬ ߓߍ߲߬ {{SITENAME}}",
"aboutpage": "Project:About",
"copyrightpage": "{{ns:project}}: ߛߓߍߦߟߊ ߤߊߞߍ",
"disclaimers": "ߖߊ߲߬ߘߐ߬ߓߌ߬ߟߊ߬ߟߌ ߟߎ߬",
"disclaimerpage": "Project: ߖߊ߲߬ߘߐ߬ߓߌ߬ߟߊ߬ߟߌ ߡߎ߰ߡߍ",
"edithelp": "ߡߊ߬ߦߟߍ߬ߢߊ߲߬ߠߌ߲ ߘߍ߬ߡߍ߲߬ߠߌ߲",
+ "helppage-top-gethelp": "ߘߍ߬ߡߍ߲߬ߠߌ",
"mainpage": "ߓߏ߬ߟߏ߲߬ߘߊ",
"mainpage-description": "ߓߏ߬ߟߏ߲߬ߘߊ",
+ "policy-url": "ߣߕߊ߬ߘߐ߬ߛߌ߮: ߕߐ߲ ߠߎ߬",
"portal": "ߟߊ߬ߛߣߍ߬ߟߌ ߓߏ߬ߟߏ߲߬ߘߊ",
"portal-url": "Project:ߟߊ߬ߛߣߍ߬ߟߌ ߓߏ߬ߟߏ߲߬ߘߊ",
"privacy": "ߘߎ߲߬ߘߎ߬ߡߊ߬ ߤߊߞߍ",
"privacypage": "Project:ߞߊ߬ ߓߍ߲߬ ߘߎ߲߬ߘߎ߬ߡߊ߬ ߤߊߞߍ ߡߊ߬",
+ "ok": "ߏ߬ߞߍ߫",
"retrievedfrom": "ߊ߬ ߡߊߝߍߣߍ߲߫ ߦߊ߲߬ ߓߊ߫$1",
"youhavenewmessages": "{{PLURAL:$3|ߌ ߓߘߊ߫ ߗߋߛߓߍ߫ ߞߎߘߊ ߛߐ߬ߘߐ߲߬$1 $2 }}",
"editsection": "ߊ߬ ߡߊߦߟߍ߬ߡߊ߲߫",
"viewsourceold": "ߊ߬ ߛߎ߲ ߘߐߜߍ߫",
"editlink": "ߊ߬ ߡߊߦߟߍ߬ߡߊ߲߬",
"viewsourcelink": "ߊ߬ ߛߎ߲ ߠߊߓߊ߯ߙߊ߫",
- "editsectionhint": "$1:ߟߊߖߍ߲ߛߍ߲ߠߌ߫ ߦߙߐ",
+ "editsectionhint": "ߦߌߟߡߊ ߡߊߝߊ߬ߟߋ߲߬ߠߌ:$1",
"toc": "ߞߣߐߘߐ",
+ "showtoc": "ߦߌ߬ߘߊ߬ߟߌ",
+ "hidetoc": "ߢߡߊߘߏ߲߯ߠߌ",
+ "confirmable-confirm": "ߌ ߛߍ߬ߓߍ߫ ߓߊ߬ {{GENDER:$1|}}؟",
+ "confirmable-yes": "ߐ߲߬ߤߐ߲߫",
+ "confirmable-no": "ߍ߲߬ߍ߲߫",
+ "thisisdeleted": "ߦߊ߯ߟߊ߫ ߦߴߊ߬ ߝߍ߬ ߞߵߊ߬ ߦߌ߬ߘߊ߬ ߥߟߊ߫ ߞߵߊ߬ ߟߊߛߊߦߌ߲߬ ߞߎߘߊߞߍ߫ ߓߊ߬ $1؟",
+ "viewdeleted": "ߦߌ߬ߘߊ߬ߟߌ ߓߊ߬ $1؟",
"site-atom-feed": "$1 ߝߕߌ ߓߊߟߏ",
"page-atom-feed": "$1 ߝߕߌ ߓߊߟߏ",
- "red-link-title": "$1(ߞߐߜߍ ߏ߬ ߡߊ߫ ߟߊߘߊ߲߫ ߝߟߐ߫)",
+ "red-link-title": "ߞߐߜߍ߫ ߕߍ߫ ߦߋ߲߬ $1",
"nstab-main": "ߞߐߜߍ",
"nstab-user": "ߞߐߜߍ߫ ߟߊߓߊ߯ߙߕߊ",
"nstab-special": "ߘߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲",
"nstab-category": "ߦߌߟߡߊ",
"mainpage-nstab": "ߓߏ߬ߟߏ߲߬ߘߊ",
"nosuchspecialpage": "ߘߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲߬ ߛߎ߮ ߏ߬ ߝߋ߲߫ ߕߍ߫ ߦߊ߲߬",
+ "nospecialpagetext": "<strong>ߊߟߎ߫ ߓߘߊ߫ ߞߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲ ߘߏ߫ ߢߌߣߌ߲߫ ߡߍ߲ ߕߺߴߦߋ߲߬.</strong>\nߞߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲߫ ߓߘߍ߬ߡߊ ߟߎ߬ ߛߙߍߘߍ ߦߋ߫ ߢߌ߲߬ ߠߋ߫ ߞߊ߲߬ [[Special:SpecialPages|{{int:specialpages}}]].",
"badtitle": "ߞߎ߲߬ߕߐ߰ ߖߎ߮",
"viewsource": "ߊ߬ ߛߎ߲ ߘߐߜߍ߫",
"viewsource-title": "ߣߌ߲߬ $1 ߛߎ߲ ߘߐߜߍ߫",
"createacct-benefit-heading": "ߛߌ߲ߘߌߣߍ߲߫ ߦߴߌ ߢߐ߲߭ ߡߐ߱ ߟߎ߬ ߟߋ߬ ߓߟߏ߫",
"createacct-benefit-body1": "{{PLURAL:$1|ߊ߬ ߡߊߦߟߍ߬ߡߊ߲߬|ߊ߬ߟߎ߬ ߡߊߦߟߍ߬ߡߊ߲߬}}",
"createacct-benefit-body2": "$1 {{PLURAL:$1|ߘߐߜߍ|ߞߐߜߍ ߟߎ߬}}",
- "createacct-benefit-body3": "ߕߊ߬ߡߌ߲߬ߣߍ߲߬ ߞߎߘߊ {{plural:$1|ߓߟߏߓߌߟߊߢߐ߲߮ߞߊ߲ߠߊ|ߓߟߏߓߌߟߊߢߐ߲߮ߞߊ߲ߠߊ ߟߎ߬}}",
+ "createacct-benefit-body3": "ߕߊ߬ߡߌ߲߬ߣߍ߲߬ ߞߎߘߊ {{PLURAL:$1|ߓߟߏߓߌߟߊߢߐ߲߮ߞߊ߲ߠߊ|ߓߟߏߓߌߟߊߢߐ߲߮ߞߊ߲ߠߊ ߟߎ߬}}",
"loginlanguagelabel": "ߞߊ߲ $1",
"pt-login": "ߌ ߜߊ߲߬ߞߎ߲߬",
"pt-login-button": "ߌ ߜߊ߲߬ߞߎ߲߬",
"image_tip": "ߞߐߕߐ߮ ߘߐߘߏ߲߬ߣߍ߲",
"media_tip": "ߞߐߕߐ߮ ߛߘߌ߬ߜߋ߲",
"sig_tip": "ߌ ߟߊ߫ ߞߟߊ߬ߣߐ ߕߎ߬ߡߊ߬ߘߊ ߓߊ߬ߘߌ߬ߟߊ߲߬ߡߊ",
- "summary": "ߟߊ߬ߘߛߏ߬ߟߌ",
+ "summary": "ߟߊ߬ߘߛߏ߬ߟߌ:",
"minoredit": "ߣߌ߲߬ ߦߋ߫ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߘߋ߬ߣߍ߲ ߘߏ߫ ߟߋ߬ ߘߌ߫",
"watchthis": "ߘߐߜߍ ߣߌ߲߬ ߘߐߜߍ߫",
"savearticle": "ߊ߬ ߟߊߞߎ߲߬ߘߎ߬",
"newarticletext": "ߌ ߓߘߊ߫ ߛߘߌ߬ߜߋ߲ ߘߏ߫ ߟߊߓߊ߬ߕߏ߬ ߞߐߜߍ ߘߏ߫ ߘߐ߫߸ ߡߍ߲ ߕߴߦߋ߲߬ ߡߎߣߎ߲߬.\nߣߵߌ ߦߴߊ߬ ߝߍ߫ ߞߊ߬ ߞߐߜߍ ߘߏ߫ ߟߊߘߊ߲߫߸ ߛߓߍߟߌ ߘߊߡߌ߬ߣߊ߬ ߘߎ߰ߟߊ ߘߐ߫ (ߞߊ߬ [$1 ߘߍ߬ߡߍ߲߬ߠߌ߲ ߞߐߜߍ] ߦߋ߫߸ ߖߐ߲߬ߛߊ߬ ߌ ߘߌ߫ ߞߌ߬ߓߊ߬ߙߏ߬ ߖߐ߲ߖߐ߲ ߛߐ߬ߘߐ߲߬). ߣߵߌ ߘߏ߲߬ ߞߍ߫ ߘߊ߫ ߦߊ߲߬ ߝߎ߬ߕߎ߲߬ߕߌ߬ ߓߟߏߡߊ߬߸ ߌ ߟߊ߫ ߛߏ߲߯ߓߊߟߊ߲ <strong>back</strong> ߛߐ߲߬ߞߌ߲߫.",
"noarticletext": "ߛߓߍߟߌ߫ ߛߌ߫ ߕߍ߫ ߞߐߜߍ ߣߌ߲߭ ߞߊ߲߬ ߕߋ߲߫. ߌ ߘߌ߫ ߛߋ߫ ߞߐߜߍ ߣߌ߲߬ [[Special:Search/{{PAGENAME}}|search for this page title]] ߕߐ߮ ߢߌߣߌ߲߫ ߠߊ߫ ߞߐߜߍ ߕߐ߭ ߟߎ߬ ߘߐ߫߸ <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs]߸ ߥߟߊ߫ [{{fullurl:{{FULLPAGENAME}}|action=edit}} create this page]</span>.",
"noarticletext-nopermission": "ߛߓߍߟߌ߫ ߛߌ߫ ߕߍ߫ ߞߐߜߍ ߣߌ߲߭ ߞߊ߲߬ ߕߋ߲߫.\nߌ ߘߌ߫ ߛߋ߫ [[Special:Search/{{PAGENAME}}|search for this page title]] ߢߌߣߌ߲߫ ߠߊ߫ ߞߐߜߍ ߕߐ߭ ߟߎ߬ ߘߐ߫߸ ߥߟߊ߫ <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} search the related logs]</span> ߞߏ߬ߣߌ߲߬ ߘߌ߬ߢߍ߬ ߞߍߣߍ߲߫ ߕߴߌ ߡߊ߬ ߞߐߜߍ߫ ߣߌ߲߬ ߠߊߞߊ߭ ߘߐ߫.",
- "userpage-userdoesnotexist-view": "ߟߊ߬ߓߊ߰ߙߊ߬ ߕߐ߮ \"$1\" ߛߙߍߘߍߦߊߣߍ߲߫ ߕߍ߫",
+ "userpage-userdoesnotexist-view": "ߟߊ߬ߓߊ߰ߙߊ߬ ߕߐ߮ \"$1\" ߛߙߍߘߍߦߊߣߍ߲߫ ߕߍ߫.",
"previewnote": "<strong>ߌ ߖߊ߲߬ߓߌ߬ߟߊ߬ ߞߏ߫ ߣߌ߲߬ ߦߋ߫ ߢߍߝߟߍߟߌ ߘߐߙߐ߲߫ ߠߋ߬ ߘߌ߫. </strong> ߌ ߟߊ߫ ߡߝߊ߬ߟߋ߲߬ߠߌ ߟߎ߫ ߡߊ߫ ߟߊߞߎ߲߬ߘߎ߬ ߝߟߐ߫ ߘߋ߬ ߹",
"continue-editing": "ߥߊ߫ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߬ ߞߣߍ ߞߊ߲߬",
"editing": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߦߋ߫ ߛߋ߲߬ߠߊ߫ $1",
"creating": "$1 ߛߌ߲ߘߟߌ ߦߋ߫ ߛߋ߲߬ߠߊ߫",
"editingsection": "(ߛߌ߰ߘߊ߬)$1 ߡߊߦߟߍ߬ߡߊ߲ ߦߋ߫ ߛߋ߲߬ߠߊ߫",
"templatesused": "{{PLURAL:$1|ߞߙߊߞߏ|ߞߙߊߞߏ ߟߎ߫}} ߟߎ߫ ߟߊߓߊ߯ߙߊ߫ ߘߊ߫ ߞߐߜߍ ߣߌ߲߬ ߘߐ߫",
- "template-protected": "ߊ߬ ߟߊߞߊ߲ߘߊߣߍ߲ ߠߋ߬",
+ "template-protected": "(ߊ߬ ߟߊߞߊ߲ߘߊߣߍ߲ ߠߋ߬)",
"template-semiprotected": "(ߟߊ߬ߞߊ߲߬ߘߊ߬ߟߌ-ߝߊ߲߬ߞߋ߬ߟߋ߲߬ߡߊ)",
"hiddencategories": "ߞߐߜߍ ߣߌ߲߬ ߦߋ߫ ߢߌ߲߬ ߠߎ߫ ߛߌ߲߬ߝߏ߲ ߠߋ߬ ߘߌ߫{{PLURAL:$1|}}",
"permissionserrors": "ߝߌ߬ߟߌ߫ ߘߌ߬ߢߍ߬ߒߧߋ",
"content-model-wikitext": "ߥߞߌ߫ ߞߟߏߜߍ",
"viewpagelogs": "ߞߐߜߍ ߣߌ߲߬ ߜߊ߲߬ߞߎ߲߬ߠߌ߲ ߠߎ߬ ߦߋ߫",
"currentrev-asof": "$1 ߟߊ߫ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߕߊ߬ߡߌ߲߬ߣߍ߲",
- "revisionasof": "ߊ߬ ߡߊߛߊ߬ߦߌ߲ ߦߊ߲߬ ߓߊ߫",
- "revision-info": "{{ߞߊ߬ߘߌ߬ߛߊ߬:$6|$2}} ߟߊ߫ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ $2",
- "previousrevision": "ߡߊ߬ߛߊ߬ߦߌ߲߬ߠߌ߲ ߞߘߐ߬ߡߊ߲",
+ "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": "ߢߌߣߌ߲ߠߌ߲ ߞߐߝߟߌ ߟߎ߬",
"searchprofile-images-tooltip": "ߞߐߕߐ߮ ߟߎ߬ ߢߌߣߌ߲߫",
"searchprofile-everything-tooltip": "ߊ߬ ߞߣߐߘߐ ߓߍ߯ ߢߌߣߌ߲߫ (ߤߊߟߌ߬ ߞߎߡߊߢߐ߲߯ߦߊ߫ ߞߐߜߍ ߟߎ߬)",
"searchprofile-advanced-tooltip": "ߊ߬ ߢߌߣߌ߲߫ ߛߊ߲߬ߠߌ߲߬ߢߐ߲߮ ߠߎ߬ ߕߐ߮ ߞߣߍ ߘߐ߫",
- "search-result-size": "$1 ({{PLURAL:$2|1 ߞߎߡߊߘߋ߲ |$2 ߞߎߡߊߘߋ߲ ߠߎ߬ }})",
- "search-redirect": "ߌ ߟߊߞߎ߲߬ߛߌ߲߬ߣߍ߲߫ ߞߊ߬ ߓߐ߫ ߦߊ߲߬ $1",
+ "search-result-size": "$1 ({{PLURAL:$2|1 ߞߎߡߊߘߋ߲|$2 ߞߎߡߊߘߋ߲ ߠߎ߬}})",
+ "search-redirect": "(ߌ ߟߊߞߎ߲߬ߛߌ߲߬ߣߍ߲߫ ߞߊ߬ ߓߐ߫ ߦߊ߲߬ $1)",
"search-section": "(ߕߍߕߍ߮ $1)",
"search-suggest": "ߌ ߞߊ߲߫ ߦߋ߫ ߣߌ߲߬ ߠߋ߬ ߡߊ߬ $1",
"searchall": "ߊ߬ ߓߍ߯",
- "search-nonefound": "ߖߋ߬ߓߟߌ߬ ߛߌ߫ ߕߍ߫ ߢߌ߬ߣߌ߲߬ߞߊ߬ߟߌ ߣߌ߲߫ ߞߊ߲߬",
+ "search-nonefound": "ߖߋ߬ߓߟߌ߬ ߛߌ߫ ߕߍ߫ ߢߌ߬ߣߌ߲߬ߞߊ߬ߟߌ ߣߌ߲߫ ߞߊ߲߬.",
"mypreferences": "ߟߊ߬ߝߌ߬ߛߦߊ߬ߟߌ",
+ "group-sysop": "ߡߙߊ߬ߟߌ߬ߟߊ",
"right-writeapi": "ߛߓߍߟߌ API ߟߊߓߊ߯ߙߊ߫",
"newuserlogpage": "ߖߊ߬ߕߋ߬ߘߊ߬ ߓߘߊ߫ ߟߊߞߊ߬ ߌ ߜߊ߲߬ߞߎ߲߬",
"action-edit": "ߞߐߜߍ ߣߌ߲߬ ߡߊߦߟߍ߬ߡߊ߲߬",
"enhancedrc-history": "ߕߊ߬ߡߌ߲߬ߣߍ߲",
"recentchanges": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߫ ߞߎߘߊ",
"recentchanges-legend": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߞߎߘߊ ߟߎ߫ ߟߊ߬ߓߍ߲߬ߢߐ߰ߡߦߊ߬ߘߊ",
+ "recentchanges-summary": "ߥߞߌ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߞߎ߲ߓߊ ߡߍ߲ ߠߎ߬ ߞߍߣߍ߲߫ ߞߐߜߍ ߣߌ߲߬ ߞߊ߲߬߸ ߏ߬ ߟߎ߫ ߣߐ߬ߣߐ߬.",
+ "recentchanges-noresult": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߬ ߛߌ߫ ߓߍ߲߬ߢߐ߲߰ߦߊ߬ߣߍ߲߬ ߕߍ߫ ߛߎߡߊ߲ߡߕߊ ߢߌ߲߬ ߠߎ߫ ߡߊ߬ ߕߎ߬ߡߊ߬ ߟߊߕߍ߰ߣߍ߲ ߦߌ߬ߘߊ ߘߐ߫.",
"recentchanges-label-newpage": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߣߌ߲߬ ߓߘߊ߫ ߘߐߜߍ߫ ߞߎߘߊ ߟߊߘߊ߲߫",
"recentchanges-label-minor": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲߬ ߘߋ߬ߣߍ߲ ߠߋ߫ ߦߋ߫",
"recentchanges-label-bot": "ߡߐ߰ߡߐ߮ ߟߋ߫ ߣߐ߬ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ ߣߌ߲߬ ߞߍ߫ ߟߊ߫",
"recentchangeslinked-toolbox": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߫ ߓߌ߬ߟߊ߬ߢߐ߲߰ߡߊ",
"recentchangeslinked-title": "ߊ߬ ߟߌ߬ߤߟߊ ߡߊߦߟߍ߬ߡߊ߲߫ ߦߊ߲߬$1",
"recentchangeslinked-summary": "ߞߐߜߍ ߕߐ߮ ߟߊߘߏ߲߬߸ ߦߟߍ߬ߡߊ߲ ߡߍ߲ ߠߎ߬ ߘߏ߲߬ߣߍ߲߬ ߦߋ߫ ߞߐߜߍ ߟߎ߬ ߘߐ߫߸ ߥߟߊ߫ ߞߐߜߍ ߣߌ߲߬ ߘߐ߫߸ ߞߵߏ߬ ߦߋ߫. (ߖߐ߲߬ߛߊ߬ ߌ ߘߌ߫ ߦߌߟߡߊ ߛߌ߲߬ߝߏ߲ ߠߎ߬ ߦߋ߫߸ {{ns:category}}: ߦߌߟߡߊ ߕߐ߮ ߟߊߘߏ߲߬).ߞߵߊ߬ ߦߟߍ߬ߡߊ߲߬ ߞߐߜߍ ߣߌ߲߬ [[Special:Watchlist|your Watchlist]] ߘߌ߫߸ ߏ߬ ߦߋ߫ <strong>ߛߓߍߘߋ߲߫ ߞߎ߲ߓߊ</strong>",
- "recentchangeslinked-page": "ߘߐߜߍ ߕߐ߮",
+ "recentchangeslinked-page": "ߘߐߜߍ ߕߐ߮:",
"upload": "ߞߐߕߐ߮ ߟߊߦߟߍ߬",
"filedesc": "ߟߊߘߛߏߣߍ߲",
+ "license-header": "ߟߊ߬ߘߌ߬ߢߍ߬ߟߌ ߦߴߌ ߘߐ߫",
"imgfile": "ߞߐߕߐ߮",
"listfiles": "ߞߐߕߐ߮ ߛߙߍߘߍ",
"file-anchor-link": "ߞߐߕߐ߮",
"filehist": "ߞߐߕߐ߮ ߟߊ߫ ߘߐ߬ߝߐ",
- "filehist-help": "ߕߎ߬ߡߊ߬ߘߊ/ߕߎ߬ߡߊ ߛߐ߲߬ߞߌ߲߬ ߓߊ߫߸ ߞߊ߬ ߕߎ߬ߡߊ߬ߘߊ ߞߐߕߐ߮ ߟߎ߬ ߦߋ߫",
+ "filehist-help": "ߕߎ߬ߡߊ߬ߘߊ/ߕߎ߬ߡߊ ߛߐ߲߬ߞߌ߲߬ ߓߊ߫߸ ߞߊ߬ ߕߎ߬ߡߊ߬ߘߊ ߞߐߕߐ߮ ߟߎ߬ ߦߋ߫.",
"filehist-current": "ߞߍߛߊ߲ߞߏ",
"filehist-datetime": "ߕߎ߬ߡߊ߬ߘߊ/ߕߎ߬ߡߊ߬ߟߊ߲",
"filehist-thumb": "ߞߝߊ߬ߟߋ߲ߛߋ߲",
"filehist-dimensions": "ߛߎߡߊ߲ߘߐ",
"filehist-comment": "ߞߊ߲߬ߝߐߟߌ",
"imagelinks": "ߞߐߕߐ߮ ߟߊߓߊ߯ߙߊ",
- "linkstoimage": "ߞߐߕߐ߮ ߣߌ߲߬ {{plural:$1|ߞߐߜߍ ߟߎ߬|$1 ߞߐߜߍ ߟߎ߬}}",
+ "linkstoimage": "ߞߐߕߐ߮ ߣߌ߲߬ {{PLURAL:$1|ߞߐߜߍ ߟߎ߬|$1 ߞߐߜߍ ߟߎ߬}}:",
"nolinkstoimage": " ߞߐߜߍ߫ ߛߌ߫ ߡߊ߫ ߞߐߕߐ߮ ߣߌ߲߬ ߠߊߓߊ߯ߙߊ߫ ߡߎߣߎ߲߬",
"sharedupload-desc-here": "ߘߐ߬ߛߙߋ ߣߌ߲߬ ߦߋ߫ ߦߊ߲߬ ߠߋ߫ $1 ߖߊ߬ߕߋ߬ߘߐ߬ߛߌ߮ ߕߐ߭ ߟߎ߬ ߞߏ߬ߣߌ߲ ߘߌ߫ ߛߴߊ߬ ߟߊߓߊ߯ߙߊ߫ ߟߊ߫. ߊ߬ ߕߐ߯ ߛߓߍߟߌ ߦߙߐ $2 ߟߋ߬ ߦߋ߫ ߘߎ߰ߟߊ ߘߐ߫ ߣߌ߲߬.",
"filepage-nofile": "ߕߐ߮ ߣߌ߲߬ ߞߐߕߐ߯ ߛߎ߯ ߕߍ߫ ߦߋ߲߬",
- "upload-disallowed-here": "ߌ ߕߍߣߊ߬ ߞߐߜߍ ߣߌ߲߬ ߞߊ߲߬ߛߓߍ߫ ߟߊ߫",
+ "upload-disallowed-here": "ߌ ߕߍߣߊ߬ ߞߐߜߍ ߣߌ߲߬ ߞߊ߲߬ߛߓߍ߫ ߟߊ߫.",
"randompage": "ߓߍ߲߬ߛߋ߲߬ߡߊ߬ ߞߐߜߍ",
"statistics": "ߖߊ߬ߕߋ߬ߛߎ߬ߓߐ ߟߎ߬",
"nbytes": "$1 {{PLURAL:$1|byte|bytes}}",
+ "nmembers": "$1 {{PLURAL:$1|ߛߌ߲߬ߝߏ߲ |members}}",
"prefixindex": "ߞߐߜߍ߫ ߡߍ߲ ߠߎ߬ ߓߍ߯ ߟߊߝߟߐߣߍ߲߫...",
"listusers": "ߟߊ߬ߓߊ߰ߙߊ߬ߟߊ ߛߙߍߘߍ",
"newpages": "ߘߐߜߍ߫ ߞߎߘߊ",
"booksources-search": "ߢߌߣߌ߲ߠߌ߲",
"specialloguserlabel": "ߞߍߓߊ߮ :",
"log": "ߘߏ߲߬",
+ "logempty": "ߦߙߍߞߍߟߌ߫ ߛߌ߫ ߓߍ߲߬ߢߐ߲߰ߦߊ߬ߣߍ߲߬ ߕߍ߫ ߝߐ߰ߓߍ ߟߎ߬ ߘߐ߫",
"allpages": "ߞߐߜߍ ߟߎ߬ ߓߍ߯",
"allarticles": "ߞߐߜߍ ߟߎ߬ ߓߍ߯",
"allpagessubmit": "ߥߊ߫",
"namespace": "ߕߐ߯ ߛߓߍ ߞߣߍ",
"tooltip-invert": "ߞߏ߲߬ߘߏ ߣߌ߲߬ ߘߐߜߍ߫߸ ߞߊ߬ ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߠߎ߬ ߢߡߊߘߏ߲߰ ߞߐߜߍ ߟߎ߬ ߕߐ߯ ߞߣߍ߫ ߓߊߓߌ߬ߟߊ߬ߣߍ߲ ߘߐ߫ (ߊ߬ ߣߌ߫ ߕߐ߯ ߞߣߍ߫ ߓߟߏߘߏ߲߬ߣߍ߲ ߘߐߜߍߣߍ߲ ߠߎ߬)",
"namespace_association": "ߕߐ߯ ߓߟߏߘߏ߲߬ߣߍ߲߫ ߢߐ߲߰ߓߟߏ",
- "blanknamespace": "ߓߊߖߎߟߞߊ",
- "contributions": "{{ߟߊߓߊ߯ߙߟߊ:$1|ߞߊ߬ߘߌ߬ߛߊ߬}} ߓߟߏߡߊߜߍ߲",
+ "blanknamespace": "ߓߊߖߎ",
+ "contributions": "{{GENDER:$1|ߞߊ߬ߘߌ߬ߛߊ߬}} ߓߟߏߡߊߜߍ߲",
"contributions-title": "ߟߊ߬ߓߊ߰ߙߊ߬ߟߊ ߟߊ߫ ߓߟߏߓߌߟߊߢߐ߲߯ߞߊ߲ ߡߍ߲ ߦߋ߫$1",
"mycontris": "ߓߟߏߓߌߟߊߢߐ߲߯ߞߊ߲",
"anoncontribs": "ߓߟߏߓߌߟߊߢߐ߲߯ߞߊ߲ ߠߎ߬",
- "contribsub2": " {{ߞߊ߬ߘߌ߬ߛߊ߬:$3|$1}} ߕߊ ($2)",
+ "contribsub2": "{{GENDER:$3|$1}} ߕߊ ($2)",
"month": "ߞߵߊ߬ ߕߊ߬ ߞߊߙߏ ߡߊ߬ (ߊ߬ ߣߌ߫ ߞߊߙߏ ߞߎ߲߬ߝߟߐ ߘߐ߫)",
"year": "ߞߵߊ߬ ߕߊ߬ ߞߊߙߏ ߡߊ߬ (ߊ߬ ߣߌ߫ ߞߊߙߏ ߞߎ߲߬ߝߟߐ ߡߊ߬)",
"sp-contributions-newbies": "ߖߊ߬ߕߋ߬ߘߊ߬ ߞߎߘߊ ߟߎ߫ ߘߐߙߐ߲߫ ߠߊ߫ ߓߟߏߓߌߟߊߢߐ߲߯ߞߊ߲ ߦߌ߬ߘߊ߫ ߕߋ߲߬",
"sp-contributions-uploads": "ߟߊ߬ߦߟߍ߬ߟߌ ߟߎ߬",
"sp-contributions-talk": "ߞߎߡߊߢߐ߲߯ߦߊ",
"sp-contributions-search": "ߓߟߏߓߌߟߊߢߐ߲߯ߞߊ߲ ߘߏ߫ ߢߌߣߌ߲߫",
- "sp-contributions-username": "IP ߛߊ߲߬ߓߊ߬ߕߐ߮:ߥߟߊ߫ ߟߊ߬ߓߊ߰ߙߊ߬ߕߐ߮",
+ "sp-contributions-username": "IP ߛߊ߲߬ߓߊ߬ߕߐ߮ ߥߟߊ߫ ߟߊ߬ߓߊ߰ߙߊ߬ߕߐ߮:",
"sp-contributions-newonly": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߲ ߡߍ߲ ߣߊ߬ ߞߐߜߍ߫ ߟߊߘߊ߲ ߘߌ߫߸ ߏ߬ ߟߎ߫ ߘߐߙߐ߲߫ ߦߌ߬ߘߊ߬",
"sp-contributions-submit": "ߢߌߣߌ߲ߠߌ߲",
"whatlinkshere": "ߛߘߌ߬ߜߋ߲ ߢߎ߬ߡߊ߲߬ ߦߋ߫ ߦߊ߲߬",
"whatlinkshere-title": "ߞߐߜߍ ߡߍ߲ ߠߎ߫ ߛߘߌ߬ߣߍ߲߫ ߝߊ߲߭ ߣߌ߲߬ $1 ߡߊ߬",
"whatlinkshere-page": "ߘߐߜߍ:",
"linkshere": "ߞߐߜߍ ߟߎ߬ ߛߘߌ߬ߜߋ߲ ߡߍ߲ ߠߎ߬ ߦߋ߫ ߦߊ߲߬ <strong>$2</strong>:",
- "nolinkshere": "ߞߐߜߍ߫ ߛߌ߫ ߟߎ߫ ߛߘߌ߬ߜߋ߲߬ ߕߍ߫ ߦߋ߲߬ <strong>$2</strong>",
+ "nolinkshere": "ߞߐߜߍ߫ ߛߌ߫ ߟߎ߫ ߛߘߌ߬ߜߋ߲߬ ߕߍ߫ ߦߋ߲߬ <strong>$2</strong>.",
"isredirect": "ߞߎ߲߬ߕߋ߬ߟߋ߲߬ ߞߎߘߊ ߞߐߜߍ",
"isimage": "ߞߐߕߐ߮ ߛߘߌ߬ߜߋ߲",
"whatlinkshere-prev": "{{PLURAL:$1|ߢߝߍߕߊ ߟߎ߬|ߢߝߍߕߊ ߟߎ߬ $1}}",
"whatlinkshere-next": "{{PLURAL:$1|ߢߍߕߊ|ߢߍߕߊ $1}}",
- "whatlinkshere-links": "ߛߘߌ߬ߜߋ߲",
+ "whatlinkshere-links": "→ ߛߘߌ߬ߜߋ߲",
"whatlinkshere-hideredirs": "ߟߊ߬ߞߎ߲߬ߛߌ߲߬ߠߌ߲ ߠߎ߬ $1",
+ "whatlinkshere-hidetrans": "ߟߊ߬ߘߏ߲߬ߘߐ߬ߟߌ ߓߊ߲ߓߊ߲ߣߍ߲",
"whatlinkshere-hidelinks": "ߛߘߌ߬ߜߋ߲$1",
"whatlinkshere-hideimages": "ߞߐߕߐ߮ ߛߘߌ߬ߜߋ߲$1",
"whatlinkshere-filters": "ߢߡߊߘߏ߲߰ߣߍ߲",
"export": "ߞߐߜߍ ߟߎ߬ ߟߊߝߏ߬ߦߌ߬",
"thumbnail-more": "ߊ߬ ߟߊߞߎ߲߬ߓߦߊ߬",
"tooltip-pt-userpage": "{{GENDER:|ߌ ߟߊ߫ ߟߊ߬ߓߊ߰ߙߊ߬ߟߌ߬}} ߞߐߜߍ",
- "tooltip-pt-mytalk": "{{ߖߊ߲߬ߕߌ߮:|ߟߊ߫}} ߞߎߡߊ߫ ߞߐߜߍ",
- "tooltip-pt-preferences": "{{ߞߊ߬ߘߌ߬ߛߊ߬:|ߌ}} ߤߣߍߕߊ ߟߎ߬",
+ "tooltip-pt-mytalk": "{{GENDER:|ߟߊ߫}} ߞߎߡߊ߫ ߞߐߜߍ",
+ "tooltip-pt-preferences": "{{GENDER:|ߌ}} ߤߣߍߕߊ ߟߎ߬",
"tooltip-pt-watchlist": "ߌ ߟߊ߫ ߞߐߜߍ߫ ߡߊߦߟߍ߬ߡߊ߲߬ߕߊ ߜߋ߬ߟߎ߲߬ߣߍ߲ ߠߎ߬ ߛߙߍߘߍ",
- "tooltip-pt-mycontris": "{{ߖߊ߲߬ߕߌ߮:| ߟߊ߫}} ߓߟߏߡߊߜߍ߲ ߠߎ߬",
+ "tooltip-pt-mycontris": "{{GENDER:|ߟߊ߫}} ߓߟߏߡߊߜߍ߲ ߠߎ߬",
"tooltip-pt-login": "ߌ ߡߊߘߌߦߊߣߍ߲߫ ߦߴߌ ߜߊ߲߬ߞߎ߲߫ ߸ ߘߌߦߊߜߏߦߊ߫ ߞߏ߬ߣߌ߲߬ ߕߍ߫",
"tooltip-pt-logout": "ߌ ߜߊ߲߬ߞߎ߲߬ ߓߐ߫",
"tooltip-pt-createaccount": "ߌ ߡߊߘߌߦߊߣߍ߲߫ ߦߋ߫ ߖߊ߬ߕߋ߬ߘߊ߫ ߟߊߞߊ߬ ߞߵߌ ߜߊ߲߬ߞߎ߲߫ ߸ ߓߊ߬ߙߌ߬ ߌ ߘߌߦߊߜߏߦߊߣߍ߲߫ ߕߍ߫",
"tooltip-ca-talk": "ߘߐ߬ߞߕߌ߬ߟߌ ߞߊ߬ ߓߍ߲߬ ߞߐߜߍ ߞߣߐߘߐ ߡߊ߬",
- "tooltip-ca-edit": "ß\9eß\90ß\9cß\8d ߣß\8c߲߬ ß¡ß\8aß\9dß\8a߬ß\9fß\8b߲߬",
+ "tooltip-ca-edit": "ß\9eß\90ß\9cß\8d ߣß\8c߲߬ ß¡ß\8aߦß\9fß\8d߬ߡß\8a߲߬",
"tooltip-ca-addsection": "ߛߌ߰ߘߊ߬ ߞߎߘߊ߫ ߘߊߡߌ߬ߣߊ߬",
"tooltip-ca-viewsource": "ߞߐߜߍ ߣߌ߲߬ ߠߊߞߊ߲ߘߊߣߍ߲߫ ߠߋ߬.\nߌ ߘߌ߫ ߛߴߊ߬ ߛߎ߲ ߘߐߜߍ߫ ߟߊ߫",
"tooltip-ca-history": "ߞߐߜߍ ߣߌ߲߬ ߛߊߞߍߟߌ߫ ߕߊ߬ߡߌ߲߬ߣߍ߲ ߠߎ߫ ߘߐߜߍ߫",
"tooltip-ca-delete": "ߞߐߜߍ ߣߌ߲߬ ߖߏ߰ߛߌ߫",
"tooltip-ca-move": "ߘߐߜߍ ߣߌ߲߬ ߛߋ߲߬ߓߐ߫",
"tooltip-ca-watch": "ߞߐߜߍ ߣߌ߲߬ ߝߙߊ߬ ߌ ߟߊ߫ ߟߊߞߙߐ߬ߛߌ߬ߕߊ߬ ߛߙߍߘߍ ߟߎ߫ ߞߊ߲߬",
- "tooltip-search": " {{ߞߍߦߙߐ ߕߐ߮}} ߊ߬ ߢߌߣߌ߲߫",
+ "tooltip-search": "ߊ߬ ߢߌߣߌ߲߫ {{SITENAME}} ߘߐ߫",
"tooltip-search-go": "ߕߐ߮ ߣߌ߲߬ ߢߌߣߌ߲߫ ߞߐߜߍ߫ ߞߣߐ߫ ߣߴߊ߬ ߞߍ߫ ߘߊ߫ ߦߋ߲߬",
"tooltip-search-fulltext": "ߞߎߡߊߘߋ߲߫ ߣߌ߲߬ ߞߐߜߍ߫ ߟߎ߫ ߢߌߣߌ߲߫",
"tooltip-p-logo": "ߞߐߜߍ߫ ߓߏߟߏ߲ߘߊ ߡߊߝߍߣߍ߲߫",
"tooltip-t-whatlinkshere": "ߥߞߌ߫ ߞߐߜߍ ߓߍ߯ ߛߘߌ߬ߜߋ߲ ߠߋ߬ ߦߋ߫ ߦߊ߲߬",
"tooltip-t-recentchangeslinked": "ߡߊ߬ߦߟߍ߬ߡߊ߲߬ߠߌ߬ ߞߎߘߊ ߟߎ߬ ߞߐߜߍ߫ ߘߐ߫ ߡߍ߲ ߣߌ߫ ߞߐߜߍ ߣߌ߲߬ ߕߎ߲߰ߣߍ߲߫",
"tooltip-feed-atom": "ߞߐߜߍ ߣߌ߲߬ ߝߕߌ߫ ߓߊߟߏ",
- "tooltip-t-contributions": "{{ߞߊ߬ߘߌ߬ߛߊ߬:$1|ߟߊ߬ߓߊ߰ߙߊ߬ߟߊ}} ߟߊ߫ ߓߟߏߓߌߟߊߢߐ߲߮ߞߊ߲ ߛߙߍߘߍ",
+ "tooltip-t-contributions": "{{GENDER:$1|ߟߊ߬ߓߊ߰ߙߊ߬ߟߊ}} ߟߊ߫ ߓߟߏߓߌߟߊߢߐ߲߮ߞߊ߲ ߛߙߍߘߍ",
+ "tooltip-t-emailuser": " ߢߎߡߍߙߋ߲ߞߏ߲ߘߏ ߟߊߕߊ߯ ߟߊߓߊ߯ߙߟߊ ߣߌ߲߬ ߡߊ߬{{GENDER:$1|ߟߊߓߊ߯ߙߟߊ(ߡߏ߬ߛߏ) }}",
"tooltip-t-upload": "ߞߐߕߐ߮ ߟߎ߫ ߟߊߦߟߍ߬",
"tooltip-t-specialpages": "ߘߎ߲߬ߘߎ߬ߡߊ߬ ߞߐߜߍ߫ ߟߎ߫ ߛߙߍߘߍ",
"tooltip-t-print": " ߞߐߜߍ ߣߌ߲߬ ߜߌ߬ߙߌ߲߬ߘߌ߬ߕߊ߬ߡߊ ߛߎ߮",
"tooltip-t-permalink": "ߞߐߜߍ ߣߌ߲߬ ߡߛߊ߬ߦߌ߲߬ߠߌ߲߬ ߛߘߌ߬ߜߋ߲߬ ߓߟߏߕߍ߰ߓߊߟߌ",
"tooltip-ca-nstab-main": "ߞߐߜߍ ߞߣߐߘߐ ߘߐߜߍ߫",
"tooltip-ca-nstab-user": "ߞߐߜߍ߫ ߟߊߓߊ߯ߙߕߊ ߘߐߜߍ߫",
- "tooltip-ca-nstab-special": "ߣߌ߲߬ ߦߋ߫ ߘߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲ ߠߋ߬ ߘߌ߫߸ ߊ߬ ߕߍ߫ ߛߋ߫ ߡߊߦߟߍ߬ߡߊ߲߬ ߠߊ߫.",
+ "tooltip-ca-nstab-special": "ߣߌ߲߬ ߦߋ߫ ߘߐߜߍ߫ ߓߟߏߡߊߞߊ߬ߣߍ߲ ߠߋ߬ ߘߌ߫߸ ߊ߬ ߕߍ߫ ߛߋ߫ ߡߊߦߟߍ߬ߡߊ߲߬ ߠߊ߫",
"tooltip-ca-nstab-project": "ߖߊ߬ߕߋ߬ߘߐ߬ߛߌ߰ ߞߐߜߍ ߘߐߜߍ߫",
"tooltip-ca-nstab-image": "ߞߐߕߐ߮ ߞߐߜߍ ߟߎ߫ ߘߐߜߍ߫",
"tooltip-ca-nstab-mediawiki": "ߞߊ߲ߞߋ ߗߋߛߓߍ ߘߐߜߍ߫",
"file-nohires": "ߢߊߓߐߣߍ߲ ߛߊ߲ߘߐߕߊ߫ ߜߘߍ߫ ߕߍ߫ ߦߋ߲߬",
"show-big-image": "ߞߐߕߐ߮ ߓߊߛߎ߲",
"show-big-image-preview": "ߊ߬ ߢߍߦߋߟߌ ߢߊ߲ߞߊ߲$1",
- "show-big-image-other": "{{PLURAL:$2|ߢߊߓߐߟߌ|ߢߊߓߐߟߌ ߟߎ߬}} ߕߐ߬ߡߊ $1",
+ "show-big-image-other": "{{PLURAL:$2|ߢߊߓߐߟߌ|ߢߊߓߐߟߌ ߟߎ߬}} ߕߐ߬ߡߊ $1.",
"show-big-image-size": "$1 × $2 ߖߌ߬ߦߊ߬ߘߊ߲ߕߊ",
"metadata": "ߡߋߕߊߘߊ߯ߕߊ߫",
"metadata-fields": "Image metadata fields listed in this message will be included on image page display when the metadata table is collapsed.\nOthers will be hidden by default.\n* make\n* model\n* datetimeoriginal\n* exposuretime\n* fnumber\n* isospeedratings\n* focallength\n* artist\n* copyright\n* imagedescription\n* gpslatitude\n* gpslongitude\n* gpsaltitude",
- "namespacesall": "ߓߍ߯",
+ "namespacesall": "ß\8a߬ ß\93ß\8d߯",
"monthsall": "ߡߎ߰ߡߍ",
"imgmultipagenext": "ߞߐߜߍ ߢߍߕߊ",
"imgmultigo": "ߥߊ߫",
"imgmultigoto": "ߥߊ߫ ߞߐߜߍ ߣߌ߲߬ ߞߊ߲߬$1",
+ "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|talk]])",
"redirect-submit": "ߕߊ߯",
"redirect-lookup": "ߊ߬ ߘߐߜߍ߫",
"redirect-value": "ߡߐ߬ߟߐ߲",
"redirect-revision": "ߞߐߜߍ ߣߐ߬ߡߊ߬ߛߊ߬ߦߌ߬ ߝߙߍߕߍ",
"redirect-file": "ߞߐߕߐ߯ ߕߐ߮",
"specialpages": "ߘߎ߲߬ߘߎ߬ߡߊ߬ ߘߐߜߍ",
- "tag-filter": "[[Special:Tags|Tag]] ߢߡߊߘߏ߲߰ߣߍ߲",
- "tag-list-wrapper": "[[ߛߐ߲߬ߞߌ߲߬ߠߌ߲߬: ߓߟߏߡߊߞߊ߬ߣߍ߲|{{PLURAL:$1|ߛߐ߲߬ߞߌ߲߬ߠߌ߲|ߛߐ߲߬ߞߌ߲߬ߠߌ߲ ߠߎ߬}}]]: $2",
+ "tag-filter": "[[Special:Tags|Tag]] ߢߡߊߘߏ߲߰ߣߍ߲:",
+ "tag-list-wrapper": "[[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]: $2",
"tags-active-yes": "ߐ߲߬ߐ߲߬ߐ߲߫",
"tags-active-no": "ߍ߲߬ߍ߲ߍ߲߬",
- "tags-hitcount": "$1{{PLURAL:$1|ߦߟߍ߬ߡߊ߲߬ߠߌ|ߦߟߍ߬ߡߊ߲߬ߠߌ ߠߎ߬ }}",
- "logentry-delete-delete": "$1 {{ߞߊ߬ߘߌ߬ߛߊ߫:$2|ߖߏ߰ߛߌ߬ߣߍ߲}} ߞߐߜߍ$3",
+ "tags-hitcount": "$1 {{PLURAL:$1|ߦߟߍ߬ߡߊ߲߬ߠߌ|ߦߟߍ߬ߡߊ߲߬ߠߌ ߠߎ߬}}",
+ "logentry-delete-delete": "$1 {{GENDER:$2|ߖߏ߰ߛߌ߬ߣߍ߲}} ߞߐߜߍ$3",
"revdelete-content-hid": "ߞߣߐߘߐ ߘߐ߲߰ߣߍ߲߫ ߠߋ߬",
- "logentry-move-move": "$1 {{ߞߊ߬ߘߌ߬ߛߊ߬:$2|ߓߘߊ߫ ߞߐߜߍ}} ߓߐ߫ ߦߊ߲߬ $3 ߞߴߊ߬ ߟߊߕߊ߯ $4",
- "logentry-move-move-noredirect": "$1 {{ߞߊ߬ߘߌ߬ߛߊ߬:$1|ߓߘߴߊ߬ ߓߐ߫ ߦߋ߲߬}} ߞߐߜߍ ߣߌ߲߬ $3 ߞߊ߬ ߥߴߊ߬ ߘߌ߫ $4 ߞߵߊ߬ ߕߘߍ߬ ߊ߬ ߡߴߊ߬ ߟߊߞߎ߲߬ߛߌ߲߫",
- "logentry-newusers-create": "ߖߊ߬ߕߋ߬ߘߊ߬ ߟߊߓߊ߯ߙߕߊ $1 ߕߘߍ߬ ߦߋ߫ {{ߞߊ߬ߘߌ߬ߛߊ߬:$2|ߕߊ ߟߋ߬ ߘߌ߫}}",
+ "logentry-move-move": "$1 {{GENDER:$2|ߓߘߊ߫ ߞߐߜߍ}} ߓߐ߫ ߦߊ߲߬ $3 ߞߴߊ߬ ߟߊߕߊ߯ $4",
+ "logentry-move-move-noredirect": "$1 {{GENDER:$1|ߓߘߴߊ߬ ߓߐ߫ ߦߋ߲߬}} ߞߐߜߍ ߣߌ߲߬ $3 ߞߊ߬ ߥߴߊ߬ ߘߌ߫ $4 ߞߵߊ߬ ߕߘߍ߬ ߊ߬ ߡߴߊ߬ ߟߊߞߎ߲߬ߛߌ߲߫",
+ "logentry-newusers-create": "ߖߊ߬ߕߋ߬ߘߊ߬ ߟߊߓߊ߯ߙߕߊ $1 ߕߘߍ߬ ߦߋ߫ {{GENDER:$2|ߕߊ ߟߋ߬ ߘߌ߫}}",
"logentry-newusers-autocreate": "ߟߊߓߊ߯ߙߊߟߊ ߟߊ߫ ߖߊ߬ߕߋ߬ߘߊ $1{{GENDER:$2|ߟߊߘߊ߲߫ ߘߊ߫ }} ߞߍߒߖߘߍߦߋ߫ ߓߟߏߡߊ߬",
- "logentry-upload-upload": "$1 {{ߞߊ߬ߘߌ߬ߛߊ߫:$2|ߟߊ߬ߦߟߍ߬ߟߌ߬ߣߐ ߟߋ߬}} $3",
- "searchsuggest-search": " {{SITENAME}} ߊ߬ ߢߌߣߌ߲߫",
+ "logentry-upload-upload": "$1 {{GENDER:$2|ߟߊ߬ߦߟߍ߬ߟߌ߬ߣߐ ߟߋ߬}} $3",
+ "searchsuggest-search": "{{SITENAME}} ߊ߬ ߢߌߣߌ߲߫",
"duration-days": "$1 {{PLURAL:$1|ߟߏ߲|ߟߏ߲ ߠߎ߬}}"
}
"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-opt-sitewide": "На новоу сајта",
+ "blocklist-type": "Тип:",
+ "blocklist-type-opt-all": "Све",
+ "blocklist-type-opt-sitewide": "На нивоу сајта",
"blocklist-type-opt-partial": "Делимично",
"blocklist-rangeblocks": "Сакриј блокаде опсега",
"blocklist-timestamp": "Временска ознака",
"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": "Не",
"Muddyb",
"Fitoschido",
"Rance",
- "Vlad5250"
+ "Vlad5250",
+ "Yasen igra"
]
},
"tog-underline": "Wekea mstari viungo:",
"tog-enotifminoredits": "Pia nitumie barua pale mabadiliko ya ukurasa yanapokuwa madogo tu.",
"tog-enotifrevealaddr": "Onyesha anwani ya barua pepe yangu katika barua pepe za taarifa",
"tog-shownumberswatching": "Onyesha idadi ya watumiaji waangalizi",
- "tog-oldsig": "Sahihi iliyopo:",
+ "tog-oldsig": "Sahihi iliyopo yenu:",
"tog-fancysig": "Weka sahihi tu (bila kujiweka kiungo yenyewe)",
"tog-uselivepreview": "Tumia kihakikio cha papohapo",
"tog-forceeditsummary": "Nishtue pale ninapoingiza muhtasari mtupu wa kuhariri",
"nstab-template": "Kigezo",
"nstab-help": "Msaada",
"nstab-category": "Jamii",
+ "mainpage-nstab": "Mwanzo",
"nosuchaction": "Kitendo hiki hakipo",
"nosuchactiontext": "Haiwezikani kutenda kitendo kilichoandikwa kwenye KISARA.\nLabda ulikosea kuandika KISARA, au kiungo ulichofuata ina kasoro.\nAu labda kuna hitilafu kwenye programu inayotumika na {{SITENAME}}.",
"nosuchspecialpage": "Ukurasa maalum huu hakuna",
"minoredit": "Haya ni mabadiliko madogo",
"watchthis": "Fuatilia ukurasa huu",
"savearticle": "Hifadhi ukurasa",
+ "savechanges": "Hifadhi mabadiliko",
"preview": "Hakiki",
"showpreview": "Onyesha hakikisho la mabadiliko",
"showdiff": "Onyesha mabadiliko",
"recentchanges-label-plusminus": "Ukubwa ukurasa kubadilishwa na hii idadi ya baiti",
"recentchanges-legend-heading": "<strong>Simulizi:</strong>",
"recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (pia tazama [[Special:NewPages|orodha ya kurasa mpya]])",
+ "rcfilters-filter-editsbyself-description": "Michango yenu.",
"rcnotefrom": "Hapo chini {{PLURAL:$5|is the change|yaonekana mabadiliko}} tangu <strong>$3,$4</strong> (hadi <strong>$1</strong>tunaonyesha).",
"rclistfrom": "Onyesha mabadiliko mapya kuanzia $3 $2",
"rcshowhideminor": "$1 mabadiliko madogo",
"contributions": "Michango ya {{GENDER:$1|mtumiaji}}",
"contributions-title": "Michango ya mtumiaji $1",
"mycontris": "Michango",
+ "anoncontribs": "Michango",
"contribsub2": "Kwa {{GENDER:$3|$1}} ($2)",
"nocontribs": "Mabadiliko yanayolingana na vigezo vilivyoulizwa hayakupatikana.",
"uctop": "ya kisasa",
"whatlinkshere-hidelinks": "$1 viungo",
"whatlinkshere-hideimages": "Viungo vya faili $1",
"whatlinkshere-filters": "Machujio",
+ "whatlinkshere-submit": "Nenda",
"block": "Kumzuia mtumiaji",
"unblock": "Kuacha kumzuia mtumiaji",
"blockip": "Zuia mtumiaji",
"imgmultipagenext": "ukurasa ujao →",
"imgmultigo": "Nenda!",
"imgmultigoto": "Uende kwenye ukurasa wa $1",
+ "img-lang-go": "Enda",
"ascending_abbrev": "pand",
"descending_abbrev": "shuk",
"table_pager_next": "Ukurasa ujao",
"version-software-version": "Toleo",
"version-entrypoints-header-url": "KISARA Kioneshi Sanifu Raslimali",
"redirect-submit": "Nenda",
+ "redirect-file": "Jina la faili",
"fileduplicatesearch": "Tafuta mafaili ya nakili",
"fileduplicatesearch-summary": "Kutafuta mafaili ya nakili kwa kuzingatia thamani za reli.",
"fileduplicatesearch-filename": "Jina la faili:",
"tag-filter-submit": "Chuja",
"tags-title": "Tagi",
"tags-description-header": "Maelezo kamili ya maana",
+ "tags-active-yes": "Ndiyo",
+ "tags-active-no": "Siyo",
"tags-edit": "hariri",
"tags-hitcount": "{{PLURAL:$1|badiliko|mabadiliko}} $1",
"comparepages": "Linganisha kurasa",
"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;
}
public function execute() {
- global $wgContLang;
+ global $wgContLang, $IP;
$data = [];
- $result = Shell::command( [ 'node', __DIR__ . '/generateJsToUpperCaseList.js' ] )
+ $result = Shell::command(
+ [ 'node', $IP . '/maintenance/mediawiki.Title/generateJsToUpperCaseList.js' ]
+ )
// Node allocates lots of memory
->limits( [ 'memory' => 1024 * 1024 ] )
->execute();
}
}
- $this->output( str_replace( ' ', "\t",
+ $mappingJson = str_replace( ' ', "\t",
json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE )
- ) . "\n" );
+ ) . "\n";
+ $outputPath = '/resources/src/mediawiki.Title/phpCharToUpper.json';
+ $file = fopen( $IP . $outputPath, 'w' );
+ fwrite( $file, $mappingJson );
+
+ $this->output( count( $data ) . " differences found.\n" );
+ $this->output( "Written to $outputPath\n" );
}
}
$url = rtrim( $this->source, '?' ) . '?' . $url;
}
- $json = Http::get( $url );
+ $json = MediaWikiServices::getInstance()->getHttpRequestFactory()->get( $url );
$data = json_decode( $json, true );
if ( is_array( $data ) ) {
],
'mediawiki.content.json' => [
'styles' => 'resources/src/mediawiki.content.json.less',
+ 'targets' => [ 'desktop', 'mobile' ],
],
'mediawiki.confirmCloseWindow' => [
'scripts' => [
<?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 );
}
// All FileRepo changes should be done here by injecting services,
// there should be no need to change global variables.
- RepoGroup::setSingleton( $this->createRepoGroup() );
+ MediaWikiServices::getInstance()->disableService( 'RepoGroup' );
+ MediaWikiServices::getInstance()->redefineService( 'RepoGroup',
+ function () {
+ return $this->createRepoGroup();
+ }
+ );
$teardown[] = function () {
- RepoGroup::destroySingleton();
+ MediaWikiServices::getInstance()->resetServiceForTesting( 'RepoGroup' );
};
// Set up null lock managers
'transformVia404' => false,
'backend' => $backend
],
- []
+ [],
+ MediaWikiServices::getInstance()->getMainWANObjectCache()
);
}
/**
* 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 )
+ ) );
+ }
}
// Note, there are some obscure globals which
// could affect the results which aren't included above.
- RepoGroup::destroySingleton();
+ $this->overrideMwServices();
$context = RequestContext::getMain();
$resp = $context->getRequest()->response();
$conf = $context->getConfig();
->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();
}
'expiry' => time() + 100500,
] );
$block->insert();
- $blockinfo = [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ];
+ $userInfoTrait = TestingAccessWrapper::newFromObject(
+ $this->getMockForTrait( ApiBlockInfoTrait::class )
+ );
+ $blockinfo = [ 'blockinfo' => $userInfoTrait->getBlockInfo( $block ) ];
$expect = Status::newGood();
$expect->fatal( ApiMessage::create( 'apierror-blocked', 'blocked', $blockinfo ) );
'expiry' => time() + 100500,
] );
$block->insert();
- $blockinfo = [ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ];
+ $userInfoTrait = TestingAccessWrapper::newFromObject(
+ $this->getObjectForTrait( ApiBlockInfoTrait::class )
+ );
+ $blockinfo = [ 'blockinfo' => $userInfoTrait->getBlockInfo( $block ) ];
$expect = Status::newGood();
$expect->fatal( ApiMessage::create( 'apierror-blocked', 'blocked', $blockinfo ) );
--- /dev/null
+<?php
+
+use Wikimedia\TestingAccessWrapper;
+
+/**
+ * @covers ApiBlockInfoTrait
+ */
+class ApiBlockInfoTraitTest extends MediaWikiTestCase {
+
+ public function testGetBlockInfo() {
+ $block = new Block();
+ $mock = $this->getMockForTrait( ApiBlockInfoTrait::class );
+ $info = TestingAccessWrapper::newFromObject( $mock )->getBlockInfo( $block );
+ $subset = [
+ 'blockid' => null,
+ 'blockedby' => '',
+ 'blockedbyid' => 0,
+ 'blockreason' => '',
+ 'blockexpiry' => 'infinite',
+ 'blockpartial' => false,
+ ];
+ $this->assertArraySubset( $subset, $info );
+ }
+
+ public function testGetBlockInfoPartial() {
+ $mock = $this->getMockForTrait( ApiBlockInfoTrait::class );
+
+ $block = new Block( [
+ 'sitewide' => false,
+ ] );
+ $info = TestingAccessWrapper::newFromObject( $mock )->getBlockInfo( $block );
+ $subset = [
+ 'blockid' => null,
+ 'blockedby' => '',
+ 'blockedbyid' => 0,
+ 'blockreason' => '',
+ 'blockexpiry' => 'infinite',
+ 'blockpartial' => true,
+ ];
+ $this->assertArraySubset( $subset, $info );
+ }
+
+}
+++ /dev/null
-<?php
-
-/**
- * @group medium
- * @covers ApiQueryUserInfo
- */
-class ApiQueryUserInfoTest extends ApiTestCase {
- public function testGetBlockInfo() {
- $apiQueryUserInfo = new ApiQueryUserInfo(
- new ApiQuery( new ApiMain( $this->apiContext ), 'userinfo' ),
- 'userinfo'
- );
-
- $block = new Block();
- $info = $apiQueryUserInfo->getBlockInfo( $block );
- $subset = [
- 'blockid' => null,
- 'blockedby' => '',
- 'blockedbyid' => 0,
- 'blockreason' => '',
- 'blockexpiry' => 'infinite',
- 'blockpartial' => false,
- ];
- $this->assertArraySubset( $subset, $info );
- }
-
- public function testGetBlockInfoPartial() {
- $apiQueryUserInfo = new ApiQueryUserInfo(
- new ApiQuery( new ApiMain( $this->apiContext ), 'userinfo' ),
- 'userinfo'
- );
-
- $block = new Block( [
- 'sitewide' => false,
- ] );
- $info = $apiQueryUserInfo->getBlockInfo( $block );
- $subset = [
- 'blockid' => null,
- 'blockedby' => '',
- 'blockedbyid' => 0,
- 'blockreason' => '',
- 'blockexpiry' => 'infinite',
- 'blockpartial' => true,
- ];
- $this->assertArraySubset( $subset, $info );
- }
-}
// Test backoff
$cache = \ObjectCache::getLocalClusterInstance();
- $backoffKey = wfMemcKey( 'AuthManager', 'autocreate-failed', md5( $username ) );
+ $backoffKey = $cache->makeKey( 'AuthManager', 'autocreate-failed', md5( $username ) );
$cache->set( $backoffKey, true );
$session->clear();
$user = \User::newFromName( $username );
// Test addToDatabase throws an exception
$cache = \ObjectCache::getLocalClusterInstance();
- $backoffKey = wfMemcKey( 'AuthManager', 'autocreate-failed', md5( $username ) );
+ $backoffKey = $cache->makeKey( 'AuthManager', 'autocreate-failed', md5( $username ) );
$this->assertFalse( $cache->get( $backoffKey ), 'sanity check' );
$session->clear();
$user = $this->getMockBuilder( \User::class )
use Wikimedia\Rdbms\LBFactoryMulti;
use Wikimedia\Rdbms\LoadBalancer;
use Wikimedia\Rdbms\ChronologyProtector;
-use Wikimedia\Rdbms\DatabaseMysqli;
use Wikimedia\Rdbms\MySQLMasterPos;
use Wikimedia\Rdbms\DatabaseDomain;
* @dataProvider getLBFactoryClassProvider
*/
public function testGetLBFactoryClass( $expected, $deprecated ) {
- $mockDB = $this->getMockBuilder( DatabaseMysqli::class )
+ $mockDB = $this->getMockBuilder( IDatabase::class )
->disableOriginalConstructor()
->getMock();
$m2Pos = new MySQLMasterPos( 'db1064-bin.002400/794074907', $now );
// Master DB 1
- $mockDB1 = $this->getMockBuilder( DatabaseMysqli::class )
+ $mockDB1 = $this->getMockBuilder( IDatabase::class )
->disableOriginalConstructor()
->getMock();
$mockDB1->method( 'writesOrCallbacksPending' )->willReturn( true );
$lb1->method( 'getMasterPos' )->willReturn( $m1Pos );
$lb1->method( 'getServerName' )->with( 0 )->willReturn( 'master1' );
// Master DB 2
- $mockDB2 = $this->getMockBuilder( DatabaseMysqli::class )
+ $mockDB2 = $this->getMockBuilder( IDatabase::class )
->disableOriginalConstructor()
->getMock();
$mockDB2->method( 'writesOrCallbacksPending' )->willReturn( true );
<?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)." );
}
}
protected function getMocks() {
- $dbMock = $this->getMockBuilder( Wikimedia\Rdbms\DatabaseMysqli::class )
+ $dbMock = $this->getMockBuilder( Wikimedia\Rdbms\IDatabase::class )
->disableOriginalClone()
->disableOriginalConstructor()
->getMock();
]
] );
- $dbMock = $this->getMockBuilder( Wikimedia\Rdbms\DatabaseMysqli::class )
+ $dbMock = $this->getMockBuilder( Wikimedia\Rdbms\IDatabase::class )
->disableOriginalConstructor()
->getMock();
function testHasForeignRepoNegative() {
$this->setMwGlobals( 'wgForeignFileRepos', [] );
- RepoGroup::destroySingleton();
+ $this->overrideMwServices();
FileBackendGroup::destroySingleton();
$this->assertFalse( RepoGroup::singleton()->hasForeignRepos() );
}
function testForEachForeignRepoNone() {
$this->setMwGlobals( 'wgForeignFileRepos', [] );
- RepoGroup::destroySingleton();
+ $this->overrideMwServices();
FileBackendGroup::destroySingleton();
$fakeCallback = $this->createMock( RepoGroupTestHelper::class );
$fakeCallback->expects( $this->never() )->method( 'callback' );
'apiThumbCacheExpiry' => 86400,
'directory' => $wgUploadDirectory
] ] );
- RepoGroup::destroySingleton();
+ $this->overrideMwServices();
FileBackendGroup::destroySingleton();
}
}
* @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 {
+ /**********************************************************************************************
+ * Shared code
+ * %{
+ */
+ private $scopedCallback;
- /** @var NamespaceInfo */
- private $obj;
-
- protected function setUp() {
+ public function setUp() {
parent::setUp();
- $this->setMwGlobals( [
- 'wgContentNamespaces' => [ NS_MAIN ],
- 'wgNamespacesWithSubpages' => [
- NS_TALK => true,
- NS_USER => true,
- NS_USER_TALK => true,
- ],
- 'wgCapitalLinks' => true,
- 'wgCapitalLinkOverrides' => [],
- 'wgNonincludableNamespaces' => [],
- ] );
+ // Boo, there's still some global state in the class :(
+ global $wgHooks;
+ $hooks = $wgHooks;
+ unset( $hooks['CanonicalNamespaces'] );
+ $this->setMwGlobals( 'wgHooks', $hooks );
+
+ $this->scopedCallback =
+ ExtensionRegistry::getInstance()->setAttributeForTest( 'ExtensionNamespaces', [] );
+ }
+
+ public function tearDown() {
+ $this->scopedCallback = null;
- $this->obj = MediaWikiServices::getInstance()->getNamespaceInfo();
+ parent::tearDown();
}
/**
- * @todo Write more texts, handle $wgAllowImageMoving setting
- * @covers NamespaceInfo::isMovable
+ * TODO Make this a const once HHVM support is dropped (T192166)
*/
- public function testIsMovable() {
- $this->assertFalse( $this->obj->isMovable( NS_SPECIAL ) );
+ private static $defaultOptions = [
+ '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,
+ ],
+ 'NonincludableNamespaces' => [],
+ 'RestrictionLevels' => [ '', 'autoconfirmed', 'sysop' ],
+ ];
+
+ private function newObj( array $options = [] ) : NamespaceInfo {
+ return new NamespaceInfo( new ServiceOptions( NamespaceInfo::$constructorOptions,
+ $options, self::$defaultOptions ) );
}
- private function assertIsSubject( $ns ) {
- $this->assertTrue( $this->obj->isSubject( $ns ) );
- }
+ // %} End shared code
- private function assertIsNotSubject( $ns ) {
- $this->assertFalse( $this->obj->isSubject( $ns ) );
- }
+ /**********************************************************************************************
+ * Basic methods
+ * %{
+ */
/**
- * Please make sure to change testIsTalk() if you change the assertions below
- * @covers NamespaceInfo::isSubject
+ * @covers NamespaceInfo::__construct
+ * @dataProvider provideConstructor
+ * @param ServiceOptions $options
+ * @param string|null $expectedExceptionText
*/
- public function testIsSubject() {
- // Special namespaces
- $this->assertIsSubject( NS_MEDIA );
- $this->assertIsSubject( NS_SPECIAL );
+ public function testConstructor( ServiceOptions $options, $expectedExceptionText = null ) {
+ if ( $expectedExceptionText !== null ) {
+ $this->setExpectedException( \Wikimedia\Assert\PreconditionException::class,
+ $expectedExceptionText );
+ }
+ new NamespaceInfo( $options );
+ $this->assertTrue( true );
+ }
- // Subject pages
- $this->assertIsSubject( NS_MAIN );
- $this->assertIsSubject( NS_USER );
- $this->assertIsSubject( 100 ); # user defined
+ public function provideConstructor() {
+ return [
+ [ new ServiceOptions( NamespaceInfo::$constructorOptions, self::$defaultOptions ) ],
+ [ new ServiceOptions( [], [] ), 'Required options missing: ' ],
+ [ new ServiceOptions(
+ array_merge( NamespaceInfo::$constructorOptions, [ 'invalid' ] ),
+ self::$defaultOptions,
+ [ 'invalid' => '' ]
+ ), 'Unsupported options passed: invalid' ],
+ ];
+ }
- // Talk pages
- $this->assertIsNotSubject( NS_TALK );
- $this->assertIsNotSubject( NS_USER_TALK );
- $this->assertIsNotSubject( 101 ); # user defined
+ /**
+ * @dataProvider provideIsMovable
+ * @covers NamespaceInfo::isMovable
+ *
+ * @param bool $expected
+ * @param int $ns
+ * @param bool $allowImageMoving
+ */
+ public function testIsMovable( $expected, $ns, $allowImageMoving = true ) {
+ $obj = $this->newObj( [ 'AllowImageMoving' => $allowImageMoving ] );
+ $this->assertSame( $expected, $obj->isMovable( $ns ) );
}
- private function assertIsTalk( $ns ) {
- $this->assertTrue( $this->obj->isTalk( $ns ) );
+ public function provideIsMovable() {
+ return [
+ 'Main' => [ true, NS_MAIN ],
+ 'Talk' => [ true, NS_TALK ],
+ 'Special' => [ false, NS_SPECIAL ],
+ 'Nonexistent even namespace' => [ true, 1234 ],
+ 'Nonexistent odd namespace' => [ true, 12345 ],
+
+ 'Media with image moving' => [ false, NS_MEDIA, true ],
+ 'Media with no image moving' => [ false, NS_MEDIA, false ],
+ 'File with image moving' => [ true, NS_FILE, true ],
+ 'File with no image moving' => [ false, NS_FILE, false ],
+ ];
}
- private function assertIsNotTalk( $ns ) {
- $this->assertFalse( $this->obj->isTalk( $ns ) );
+ /**
+ * @param int $ns
+ * @param bool $expected
+ * @dataProvider provideIsSubject
+ * @covers NamespaceInfo::isSubject
+ */
+ 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 ) );
+ }
+
+ public function provideIsSubject() {
+ return [
+ // Special namespaces
+ [ NS_MEDIA, true ],
+ [ NS_SPECIAL, true ],
- // Subject pages
- $this->assertIsNotTalk( NS_MAIN );
- $this->assertIsNotTalk( NS_USER );
- $this->assertIsNotTalk( 100 ); # user defined
+ // Subject pages
+ [ NS_MAIN, true ],
+ [ NS_USER, true ],
+ [ 100, true ],
- // Talk pages
- $this->assertIsTalk( NS_TALK );
- $this->assertIsTalk( NS_USER_TALK );
- $this->assertIsTalk( 101 ); # user defined
+ // 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 ) );
}
/**
* Namespaces without a talk page (NS_MEDIA, NS_SPECIAL) are tested in
* the function testGetTalkExceptions()
* @covers NamespaceInfo::getTalk
+ * @covers NamespaceInfo::isMethodValidFor
*/
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 ) );
}
/**
* NS_MEDIA does not have talk pages. MediaWiki raise an exception for them.
* @expectedException MWException
* @covers NamespaceInfo::getTalk
+ * @covers NamespaceInfo::isMethodValidFor
*/
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::exists
+ * @dataProvider provideExists
+ * @param int $ns
+ * @param bool $expected
+ */
+ public function testExists( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->exists( $ns ) );
+ }
+
+ public function provideExists() {
+ return [
+ 'Main' => [ NS_MAIN, true ],
+ 'Talk' => [ NS_TALK, true ],
+ 'Media' => [ NS_MEDIA, true ],
+ 'Special' => [ NS_SPECIAL, true ],
+ 'Nonexistent' => [ 12345, false ],
+ 'Negative nonexistent' => [ -12345, false ],
+ ];
}
/**
* Note if we add a namespace registration system with keys like 'MAIN'
- * we should add tests here for equivilance on things like 'MAIN' == 0
+ * we should add tests here for equivalence on things like 'MAIN' == 0
* and 'MAIN' == NS_MAIN.
* @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" );
- }
-
- private function assertIsContent( $ns ) {
- $this->assertTrue( $this->obj->isContent( $ns ) );
+ public function testIsContent( $ns, $expected, $contentNamespaces = [ NS_MAIN ] ) {
+ $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
+ $this->assertSame( $expected, $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
+ * @dataProvider provideWantSignatures
+ * @covers NamespaceInfo::wantSignatures
+ *
+ * @param int $index
+ * @param bool $expected
*/
- 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
+ public function testWantSignatures( $index, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->wantSignatures( $index ) );
+ }
- $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 provideWantSignatures() {
+ return [
+ 'Main' => [ NS_MAIN, false ],
+ 'Talk' => [ NS_TALK, true ],
+ 'User' => [ NS_USER, false ],
+ 'User talk' => [ NS_USER_TALK, true ],
+ 'Special' => [ NS_SPECIAL, false ],
+ 'Media' => [ NS_MEDIA, false ],
+ 'Nonexistent talk' => [ 12345, true ],
+ 'Nonexistent subject' => [ 123456, false ],
+ 'Nonexistent negative odd' => [ -12345, false ],
+ ];
}
/**
- * Similar to testIsContent() but alters the $wgContentNamespaces
- * global variable.
- * @covers NamespaceInfo::isContent
+ * @dataProvider provideWantSignatures_ExtraSignatureNamespaces
+ * @covers NamespaceInfo::wantSignatures
+ *
+ * @param int $index
+ * @param int $expected
*/
- 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 testWantSignatures_ExtraSignatureNamespaces( $index, $expected ) {
+ $obj = $this->newObj( [ 'ExtraSignatureNamespaces' =>
+ [ NS_MAIN, NS_USER, NS_SPECIAL, NS_MEDIA, 123456, -12345 ] ] );
+ $this->assertSame( $expected, $obj->wantSignatures( $index ) );
}
- private function assertIsWatchable( $ns ) {
- $this->assertTrue( $this->obj->isWatchable( $ns ) );
- }
+ public function provideWantSignatures_ExtraSignatureNamespaces() {
+ $ret = array_map(
+ function ( $arr ) {
+ // We've added all these as extra signature namespaces, so expect true
+ return [ $arr[0], true ];
+ },
+ self::provideWantSignatures()
+ );
- private function assertIsNotWatchable( $ns ) {
- $this->assertFalse( $this->obj->isWatchable( $ns ) );
+ // Add one more that's false
+ $ret['Another nonexistent subject'] = [ 12345678, false ];
+ return $ret;
}
/**
+ * @param int $ns
+ * @param bool $expected
* @covers NamespaceInfo::isWatchable
+ * @dataProvider provideIsWatchable
*/
- 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 testIsWatchable( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->isWatchable( $ns ) );
}
- private function assertHasSubpages( $ns ) {
- $this->assertTrue( $this->obj->hasSubpages( $ns ) );
- }
+ public function provideIsWatchable() {
+ return [
+ // Specials namespaces are not watchable
+ [ NS_MEDIA, false ],
+ [ NS_SPECIAL, false ],
+
+ // Core defined namespaces are watchables
+ [ NS_MAIN, true ],
+ [ NS_TALK, true ],
- private function assertHasNotSubpages( $ns ) {
- $this->assertFalse( $this->obj->hasSubpages( $ns ) );
+ // Additional, user defined namespaces are watchables
+ [ 100, true ],
+ [ 101, true ],
+ ];
}
/**
+ * @param int $ns
+ * @param int $expected
+ * @param array|null $namespacesWithSubpages To pass to constructor
* @covers NamespaceInfo::hasSubpages
+ * @dataProvider provideHasSubpages
*/
- public function testHasSubpages() {
- global $wgNamespacesWithSubpages;
-
- // Special namespaces:
- $this->assertHasNotSubpages( NS_MEDIA );
- $this->assertHasNotSubpages( NS_SPECIAL );
-
- // Namespaces without subpages
- $this->assertHasNotSubpages( NS_MAIN );
+ public function testHasSubpages( $ns, $expected, array $namespacesWithSubpages = null ) {
+ $obj = $this->newObj( $namespacesWithSubpages
+ ? [ 'NamespacesWithSubpages' => $namespacesWithSubpages ]
+ : [] );
+ $this->assertSame( $expected, $obj->hasSubpages( $ns ) );
+ }
- $wgNamespacesWithSubpages[NS_MAIN] = true;
- $this->assertHasSubpages( NS_MAIN );
+ public function provideHasSubpages() {
+ return [
+ // Special namespaces:
+ [ NS_MEDIA, false ],
+ [ NS_SPECIAL, false ],
- $wgNamespacesWithSubpages[NS_MAIN] = false;
- $this->assertHasNotSubpages( NS_MAIN );
+ // Namespaces without subpages
+ [ NS_MAIN, false ],
+ [ NS_MAIN, true, [ NS_MAIN => true ] ],
+ [ NS_MAIN, false, [ NS_MAIN => false ] ],
- // Some namespaces with subpages
- $this->assertHasSubpages( NS_TALK );
- $this->assertHasSubpages( NS_USER );
- $this->assertHasSubpages( NS_USER_TALK );
+ // Some namespaces with subpages
+ [ NS_TALK, true ],
+ [ NS_USER, true ],
+ [ NS_USER_TALK, true ],
+ ];
}
/**
+ * @param $contentNamespaces To pass to constructor
+ * @param array $expected
+ * @dataProvider provideGetContentNamespaces
* @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() );
+ public function testGetContentNamespaces( $contentNamespaces, array $expected ) {
+ $obj = $this->newObj( [ 'ContentNamespaces' => $contentNamespaces ] );
+ $this->assertSame( $expected, $obj->getContentNamespaces() );
+ }
- # test $wgContentNamespaces === []
- $wgContentNamespaces = [];
- $this->assertEquals( [ NS_MAIN ], $this->obj->getContentNamespaces() );
+ public function provideGetContentNamespaces() {
+ return [
+ // Non-array
+ [ '', [ NS_MAIN ] ],
+ [ false, [ NS_MAIN ] ],
+ [ null, [ NS_MAIN ] ],
+ [ 5, [ NS_MAIN ] ],
- # 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'
- );
+ // Empty array
+ [ [], [ NS_MAIN ] ],
- # test other cases, return $wgcontentNamespaces as is
- $wgContentNamespaces = [ NS_MAIN ];
- $this->assertEquals(
- [ NS_MAIN ],
- $this->obj->getContentNamespaces()
- );
+ // NS_MAIN is forced to be content even if unwanted
+ [ [ NS_USER, NS_CATEGORY ], [ NS_MAIN, NS_USER, NS_CATEGORY ] ],
- $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 ) );
+ /**
+ * @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 testIsCapitalized(
+ $ns, $expected, $capitalLinks = true, array $capitalLinkOverrides = []
+ ) {
+ $obj = $this->newObj( [
+ 'CapitalLinks' => $capitalLinks,
+ 'CapitalLinkOverrides' => $capitalLinkOverrides,
+ ] );
+ $this->assertSame( $expected, $obj->isCapitalized( $ns ) );
}
- private function assertIsNotCapitalized( $ns ) {
- $this->assertFalse( $this->obj->isCapitalized( $ns ) );
+ 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 ] ],
+ ];
}
/**
- * Some namespaces are always capitalized per code definition
- * in NamespaceInfo::$alwaysCapitalizedNamespaces
- * @covers NamespaceInfo::isCapitalized
+ * @covers NamespaceInfo::hasGenderDistinction
*/
- 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'
- );
+ public function testHasGenderDistinction() {
+ $obj = $this->newObj();
- // Boths are capitalized by default
- $this->assertIsCapitalized( NS_MEDIA );
- $this->assertIsCapitalized( NS_FILE );
+ // Namespaces with gender distinctions
+ $this->assertTrue( $obj->hasGenderDistinction( NS_USER ) );
+ $this->assertTrue( $obj->hasGenderDistinction( NS_USER_TALK ) );
+
+ // Other ones, "genderless"
+ $this->assertFalse( $obj->hasGenderDistinction( NS_MEDIA ) );
+ $this->assertFalse( $obj->hasGenderDistinction( NS_SPECIAL ) );
+ $this->assertFalse( $obj->hasGenderDistinction( NS_MAIN ) );
+ $this->assertFalse( $obj->hasGenderDistinction( NS_TALK ) );
+ }
- // Always capitalized namespaces
- // @see NamespaceInfo::$alwaysCapitalizedNamespaces
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
+ /**
+ * @covers NamespaceInfo::isNonincludable
+ */
+ public function testIsNonincludable() {
+ $obj = $this->newObj( [ 'NonincludableNamespaces' => [ NS_USER ] ] );
+ $this->assertTrue( $obj->isNonincludable( NS_USER ) );
+ $this->assertFalse( $obj->isNonincludable( NS_TEMPLATE ) );
}
/**
- * Follows up for testIsCapitalizedHardcodedAssertions() but alter the
- * global $wgCapitalLink setting to have extended coverage.
+ * @dataProvider provideGetNamespaceContentModel
+ * @covers NamespaceInfo::getNamespaceContentModel
*
- * NamespaceInfo::isCapitalized() rely on two global settings:
- * $wgCapitalLinkOverrides = []; by default
- * $wgCapitalLinks = true; by default
- * This function test $wgCapitalLinks
+ * @param int $ns
+ * @param string $expected
+ */
+ public function testGetNamespaceContentModel( $ns, $expected ) {
+ $obj = $this->newObj( [ 'NamespaceContentModels' =>
+ [ NS_USER => CONTENT_MODEL_WIKITEXT, 123 => CONTENT_MODEL_JSON, 1234 => 'abcdef' ],
+ ] );
+ $this->assertSame( $expected, $obj->getNamespaceContentModel( $ns ) );
+ }
+
+ public function provideGetNamespaceContentModel() {
+ return [
+ [ NS_MAIN, null ],
+ [ NS_TALK, null ],
+ [ NS_USER, CONTENT_MODEL_WIKITEXT ],
+ [ NS_USER_TALK, null ],
+ [ NS_SPECIAL, null ],
+ [ 122, null ],
+ [ 123, CONTENT_MODEL_JSON ],
+ [ 1234, 'abcdef' ],
+ [ 1235, null ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideGetCategoryLinkType
+ * @covers NamespaceInfo::getCategoryLinkType
*
- * Global setting correctness is tested against the NS_PROJECT and
- * NS_PROJECT_TALK namespaces since they are not hardcoded nor specials
- * @covers NamespaceInfo::isCapitalized
+ * @param int $ns
+ * @param string $expected
*/
- public function testIsCapitalizedWithWgCapitalLinks() {
- $this->assertIsCapitalized( NS_PROJECT );
- $this->assertIsCapitalized( NS_PROJECT_TALK );
+ public function testGetCategoryLinkType( $ns, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->getCategoryLinkType( $ns ) );
+ }
- $this->setMwGlobals( 'wgCapitalLinks', false );
+ public function provideGetCategoryLinkType() {
+ return [
+ [ NS_MAIN, 'page' ],
+ [ NS_TALK, 'page' ],
+ [ NS_USER, 'page' ],
+ [ NS_USER_TALK, 'page' ],
+
+ [ NS_FILE, 'file' ],
+ [ NS_FILE_TALK, 'page' ],
+
+ [ NS_CATEGORY, 'subcat' ],
+ [ NS_CATEGORY_TALK, 'page' ],
+
+ [ 100, 'page' ],
+ [ 101, 'page' ],
+ ];
+ }
- // hardcoded namespaces (see above function) are still capitalized:
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
+ // %} End basic methods
- // setting is correctly applied
- $this->assertIsNotCapitalized( NS_PROJECT );
- $this->assertIsNotCapitalized( NS_PROJECT_TALK );
+ /**********************************************************************************************
+ * Canonical namespaces
+ * %{
+ */
+
+ // Default canonical namespaces
+ // %{
+ private function getDefaultNamespaces() {
+ return [ NS_MAIN => '' ] + self::$defaultOptions['CanonicalNamespaceNames'];
}
/**
- * Counter part for NamespaceInfo::testIsCapitalizedWithWgCapitalLinks() now
- * testing the $wgCapitalLinkOverrides global.
+ * @covers NamespaceInfo::getCanonicalNamespaces
+ */
+ public function testGetCanonicalNamespaces() {
+ $this->assertSame(
+ $this->getDefaultNamespaces(),
+ $this->newObj()->getCanonicalNamespaces()
+ );
+ }
+
+ /**
+ * @dataProvider provideGetCanonicalName
+ * @covers NamespaceInfo::getCanonicalName
*
- * @todo split groups of assertions in autonomous testing functions
- * @covers NamespaceInfo::isCapitalized
+ * @param int $index
+ * @param string|bool $expected
*/
- public function testIsCapitalizedWithWgCapitalLinkOverrides() {
- global $wgCapitalLinkOverrides;
+ public function testGetCanonicalName( $index, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->getCanonicalName( $index ) );
+ }
- // Test default settings
- $this->assertIsCapitalized( NS_PROJECT );
- $this->assertIsCapitalized( NS_PROJECT_TALK );
+ public function provideGetCanonicalName() {
+ return [
+ 'Main' => [ NS_MAIN, '' ],
+ 'Talk' => [ NS_TALK, 'Talk' ],
+ 'With underscore not space' => [ NS_USER_TALK, 'User_talk' ],
+ 'Special' => [ NS_SPECIAL, 'Special' ],
+ 'Nonexistent' => [ 12345, false ],
+ 'Nonexistent negative' => [ -12345, false ],
+ ];
+ }
- // hardcoded namespaces (see above function) are capitalized:
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
+ /**
+ * @dataProvider provideGetCanonicalIndex
+ * @covers NamespaceInfo::getCanonicalIndex
+ *
+ * @param string $name
+ * @param int|null $expected
+ */
+ public function testGetCanonicalIndex( $name, $expected ) {
+ $this->assertSame( $expected, $this->newObj()->getCanonicalIndex( $name ) );
+ }
- // Hardcoded namespaces remains capitalized
- $wgCapitalLinkOverrides[NS_SPECIAL] = false;
- $wgCapitalLinkOverrides[NS_USER] = false;
- $wgCapitalLinkOverrides[NS_MEDIAWIKI] = false;
+ public function provideGetCanonicalIndex() {
+ return [
+ 'Main' => [ '', NS_MAIN ],
+ 'Talk' => [ 'talk', NS_TALK ],
+ 'Not lowercase' => [ 'Talk', null ],
+ 'With underscore' => [ 'user_talk', NS_USER_TALK ],
+ 'Space is not recognized for underscore' => [ 'user talk', null ],
+ '0' => [ '0', null ],
+ ];
+ }
- $this->assertIsCapitalized( NS_SPECIAL );
- $this->assertIsCapitalized( NS_USER );
- $this->assertIsCapitalized( NS_MEDIAWIKI );
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces() {
+ $this->assertSame(
+ [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK ],
+ $this->newObj()->getValidNamespaces()
+ );
+ }
- $wgCapitalLinkOverrides[NS_PROJECT] = false;
- $this->assertIsNotCapitalized( NS_PROJECT );
+ // %} End default canonical namespaces
- $wgCapitalLinkOverrides[NS_PROJECT] = true;
- $this->assertIsCapitalized( NS_PROJECT );
+ // No canonical namespace names
+ // %{
+ /**
+ * @covers NamespaceInfo::getCanonicalNamespaces
+ */
+ public function testGetCanonicalNamespaces_NoCanonicalNamespaceNames() {
+ $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
- unset( $wgCapitalLinkOverrides[NS_PROJECT] );
- $this->assertIsCapitalized( NS_PROJECT );
+ $this->assertSame( [ NS_MAIN => '' ], $obj->getCanonicalNamespaces() );
}
/**
- * @covers NamespaceInfo::hasGenderDistinction
+ * @covers NamespaceInfo::getCanonicalName
*/
- public function testHasGenderDistinction() {
- // Namespaces with gender distinctions
- $this->assertTrue( $this->obj->hasGenderDistinction( NS_USER ) );
- $this->assertTrue( $this->obj->hasGenderDistinction( NS_USER_TALK ) );
+ public function testGetCanonicalName_NoCanonicalNamespaceNames() {
+ $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
- // 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->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
+ $this->assertFalse( $obj->getCanonicalName( NS_TALK ) );
}
/**
- * @covers NamespaceInfo::isNonincludable
+ * @covers NamespaceInfo::getCanonicalIndex
*/
- public function testIsNonincludable() {
- global $wgNonincludableNamespaces;
+ public function testGetCanonicalIndex_NoCanonicalNamespaceNames() {
+ $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
+
+ $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
+ $this->assertNull( $obj->getCanonicalIndex( 'talk' ) );
+ }
- $wgNonincludableNamespaces = [ NS_USER ];
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces_NoCanonicalNamespaceNames() {
+ $obj = $this->newObj( [ 'CanonicalNamespaceNames' => [] ] );
- $this->assertTrue( $this->obj->isNonincludable( NS_USER ) );
- $this->assertFalse( $this->obj->isNonincludable( NS_TEMPLATE ) );
+ $this->assertSame( [ NS_MAIN ], $obj->getValidNamespaces() );
}
- private function assertSameSubject( $ns1, $ns2, $msg = '' ) {
- $this->assertTrue( $this->obj->subjectEquals( $ns1, $ns2 ), $msg );
+ // %} End no canonical namespace names
+
+ // Test extension namespaces
+ // %{
+ private function setupExtensionNamespaces() {
+ $this->scopedCallback = null;
+ $this->scopedCallback = ExtensionRegistry::getInstance()->setAttributeForTest(
+ 'ExtensionNamespaces',
+ [ NS_MAIN => 'No effect', NS_TALK => 'No effect', 12345 => 'Extended' ]
+ );
}
- private function assertDifferentSubject( $ns1, $ns2, $msg = '' ) {
- $this->assertFalse( $this->obj->subjectEquals( $ns1, $ns2 ), $msg );
+ /**
+ * @covers NamespaceInfo::getCanonicalNamespaces
+ */
+ public function testGetCanonicalNamespaces_ExtensionNamespaces() {
+ $this->setupExtensionNamespaces();
+
+ $this->assertSame(
+ $this->getDefaultNamespaces() + [ 12345 => 'Extended' ],
+ $this->newObj()->getCanonicalNamespaces()
+ );
}
- public function provideGetCategoryLinkType() {
- return [
- [ NS_MAIN, 'page' ],
- [ NS_TALK, 'page' ],
- [ NS_USER, 'page' ],
- [ NS_USER_TALK, 'page' ],
+ /**
+ * @covers NamespaceInfo::getCanonicalName
+ */
+ public function testGetCanonicalName_ExtensionNamespaces() {
+ $this->setupExtensionNamespaces();
+ $obj = $this->newObj();
- [ NS_FILE, 'file' ],
- [ NS_FILE_TALK, 'page' ],
+ $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
+ $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK ) );
+ $this->assertSame( 'Extended', $obj->getCanonicalName( 12345 ) );
+ }
- [ NS_CATEGORY, 'subcat' ],
- [ NS_CATEGORY_TALK, 'page' ],
+ /**
+ * @covers NamespaceInfo::getCanonicalIndex
+ */
+ public function testGetCanonicalIndex_ExtensionNamespaces() {
+ $this->setupExtensionNamespaces();
+ $obj = $this->newObj();
- [ 100, 'page' ],
- [ 101, 'page' ],
+ $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
+ $this->assertSame( NS_TALK, $obj->getCanonicalIndex( 'talk' ) );
+ $this->assertSame( 12345, $obj->getCanonicalIndex( 'extended' ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces_ExtensionNamespaces() {
+ $this->setupExtensionNamespaces();
+
+ $this->assertSame(
+ [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 12345 ],
+ $this->newObj()->getValidNamespaces()
+ );
+ }
+
+ // %} End extension namespaces
+
+ // Hook namespaces
+ // %{
+ /**
+ * @return array Expected canonical namespaces
+ */
+ private function setupHookNamespaces() {
+ $callback =
+ function ( &$canonicalNamespaces ) {
+ $canonicalNamespaces[NS_MAIN] = 'Main';
+ unset( $canonicalNamespaces[NS_MEDIA] );
+ $canonicalNamespaces[123456] = 'Hooked';
+ };
+ $this->setTemporaryHook( 'CanonicalNamespaces', $callback );
+ $expected = $this->getDefaultNamespaces();
+ ( $callback )( $expected );
+ return $expected;
+ }
+
+ /**
+ * @covers NamespaceInfo::getCanonicalNamespaces
+ */
+ public function testGetCanonicalNamespaces_HookNamespaces() {
+ $expected = $this->setupHookNamespaces();
+
+ $this->assertSame( $expected, $this->newObj()->getCanonicalNamespaces() );
+ }
+
+ /**
+ * @covers NamespaceInfo::getCanonicalName
+ */
+ public function testGetCanonicalName_HookNamespaces() {
+ $this->setupHookNamespaces();
+ $obj = $this->newObj();
+
+ $this->assertSame( 'Main', $obj->getCanonicalName( NS_MAIN ) );
+ $this->assertFalse( $obj->getCanonicalName( NS_MEDIA ) );
+ $this->assertSame( 'Hooked', $obj->getCanonicalName( 123456 ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::getCanonicalIndex
+ */
+ public function testGetCanonicalIndex_HookNamespaces() {
+ $this->setupHookNamespaces();
+ $obj = $this->newObj();
+
+ $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( 'main' ) );
+ $this->assertNull( $obj->getCanonicalIndex( 'media' ) );
+ $this->assertSame( 123456, $obj->getCanonicalIndex( 'hooked' ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces_HookNamespaces() {
+ $this->setupHookNamespaces();
+
+ $this->assertSame(
+ [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 123456 ],
+ $this->newObj()->getValidNamespaces()
+ );
+ }
+
+ // %} End hook namespaces
+
+ // Extra namespaces
+ // %{
+ /**
+ * @return NamespaceInfo
+ */
+ private function setupExtraNamespaces() {
+ return $this->newObj( [ 'ExtraNamespaces' =>
+ [ NS_MAIN => 'No effect', NS_TALK => 'No effect', 1234567 => 'Extra' ]
+ ] );
+ }
+
+ /**
+ * @covers NamespaceInfo::getCanonicalNamespaces
+ */
+ public function testGetCanonicalNamespaces_ExtraNamespaces() {
+ $this->assertSame(
+ $this->getDefaultNamespaces() + [ 1234567 => 'Extra' ],
+ $this->setupExtraNamespaces()->getCanonicalNamespaces()
+ );
+ }
+
+ /**
+ * @covers NamespaceInfo::getCanonicalName
+ */
+ public function testGetCanonicalName_ExtraNamespaces() {
+ $obj = $this->setupExtraNamespaces();
+
+ $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
+ $this->assertSame( 'Talk', $obj->getCanonicalName( NS_TALK ) );
+ $this->assertSame( 'Extra', $obj->getCanonicalName( 1234567 ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::getCanonicalIndex
+ */
+ public function testGetCanonicalIndex_ExtraNamespaces() {
+ $obj = $this->setupExtraNamespaces();
+
+ $this->assertNull( $obj->getCanonicalIndex( 'no effect' ) );
+ $this->assertNull( $obj->getCanonicalIndex( 'no_effect' ) );
+ $this->assertSame( 1234567, $obj->getCanonicalIndex( 'extra' ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces_ExtraNamespaces() {
+ $this->assertSame(
+ [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK, 1234567 ],
+ $this->setupExtraNamespaces()->getValidNamespaces()
+ );
+ }
+
+ // %} End extra namespaces
+
+ // Canonical namespace caching
+ // %{
+ /**
+ * @covers NamespaceInfo::getCanonicalNamespaces
+ */
+ public function testGetCanonicalNamespaces_caching() {
+ $obj = $this->newObj();
+
+ // This should cache the values
+ $obj->getCanonicalNamespaces();
+
+ // Now try to alter them through nefarious means
+ $this->setupExtensionNamespaces();
+ $this->setupHookNamespaces();
+
+ // Should have no effect
+ $this->assertSame( $this->getDefaultNamespaces(), $obj->getCanonicalNamespaces() );
+ }
+
+ /**
+ * @covers NamespaceInfo::getCanonicalName
+ */
+ public function testGetCanonicalName_caching() {
+ $obj = $this->newObj();
+
+ // This should cache the values
+ $obj->getCanonicalName( NS_MAIN );
+
+ // Now try to alter them through nefarious means
+ $this->setupExtensionNamespaces();
+ $this->setupHookNamespaces();
+
+ // Should have no effect
+ $this->assertSame( '', $obj->getCanonicalName( NS_MAIN ) );
+ $this->assertSame( 'Media', $obj->getCanonicalName( NS_MEDIA ) );
+ $this->assertFalse( $obj->getCanonicalName( 12345 ) );
+ $this->assertFalse( $obj->getCanonicalName( 123456 ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::getCanonicalIndex
+ */
+ public function testGetCanonicalIndex_caching() {
+ $obj = $this->newObj();
+
+ // This should cache the values
+ $obj->getCanonicalIndex( '' );
+
+ // Now try to alter them through nefarious means
+ $this->setupExtensionNamespaces();
+ $this->setupHookNamespaces();
+
+ // Should have no effect
+ $this->assertSame( NS_MAIN, $obj->getCanonicalIndex( '' ) );
+ $this->assertSame( NS_MEDIA, $obj->getCanonicalIndex( 'media' ) );
+ $this->assertNull( $obj->getCanonicalIndex( 'extended' ) );
+ $this->assertNull( $obj->getCanonicalIndex( 'hooked' ) );
+ }
+
+ /**
+ * @covers NamespaceInfo::getValidNamespaces
+ */
+ public function testGetValidNamespaces_caching() {
+ $obj = $this->newObj();
+
+ // This should cache the values
+ $obj->getValidNamespaces();
+
+ // Now try to alter through nefarious means
+ $this->setupExtensionNamespaces();
+ $this->setupHookNamespaces();
+
+ // Should have no effect
+ $this->assertSame(
+ [ NS_MAIN, NS_TALK, NS_USER, NS_USER_TALK ],
+ $obj->getValidNamespaces()
+ );
+ }
+
+ // %} End canonical namespace caching
+
+ // Miscellaneous
+ // %{
+
+ /**
+ * @dataProvider provideGetValidNamespaces_misc
+ * @covers NamespaceInfo::getValidNamespaces
+ *
+ * @param array $namespaces List of namespace indices to return from getCanonicalNamespaces()
+ * (list is overwritten by a hook, so NS_MAIN doesn't have to be present)
+ * @param array $expected
+ */
+ public function testGetValidNamespaces_misc( array $namespaces, array $expected ) {
+ // Each namespace's name is just its index
+ $this->setTemporaryHook( 'CanonicalNamespaces',
+ function ( &$canonicalNamespaces ) use ( $namespaces ) {
+ $canonicalNamespaces = array_combine( $namespaces, $namespaces );
+ }
+ );
+ $this->assertSame( $expected, $this->newObj()->getValidNamespaces() );
+ }
+
+ public function provideGetValidNamespaces_misc() {
+ return [
+ 'Out of order (T109137)' => [ [ 1, 0 ], [ 0, 1 ] ],
+ 'Alphabetical order' => [ [ 10, 2 ], [ 2, 10 ] ],
+ 'Negative' => [ [ -1000, -500, -2, 0 ], [ 0 ] ],
];
}
+ // %} End miscellaneous
+ // %} End canonical namespaces
+
+ /**********************************************************************************************
+ * Restriction levels
+ * %{
+ */
+
/**
- * @dataProvider provideGetCategoryLinkType
- * @covers NamespaceInfo::getCategoryLinkType
+ * This mock user can only have isAllowed() called on it.
*
- * @param int $index
- * @param string $expected
+ * @param array $groups Groups for the mock user to have
+ * @return User
*/
- public function testGetCategoryLinkType( $index, $expected ) {
- $actual = $this->obj->getCategoryLinkType( $index );
- $this->assertSame( $expected, $actual, "NS $index" );
+ private function getMockUser( array $groups = [] ) : User {
+ $groups[] = '*';
+
+ $mock = $this->createMock( User::class );
+ $mock->method( 'isAllowed' )->will( $this->returnCallback(
+ function ( $action ) use ( $groups ) {
+ global $wgGroupPermissions, $wgRevokePermissions;
+ if ( $action == '' ) {
+ return true;
+ }
+ foreach ( $wgRevokePermissions as $group => $rights ) {
+ if ( !in_array( $group, $groups ) ) {
+ continue;
+ }
+ if ( isset( $rights[$action] ) && $rights[$action] ) {
+ return false;
+ }
+ }
+ foreach ( $wgGroupPermissions as $group => $rights ) {
+ if ( !in_array( $group, $groups ) ) {
+ continue;
+ }
+ if ( isset( $rights[$action] ) && $rights[$action] ) {
+ return true;
+ }
+ }
+ return false;
+ }
+ ) );
+ $mock->expects( $this->never() )->method( $this->anythingBut( 'isAllowed' ) );
+ return $mock;
}
+
+ /**
+ * @dataProvider provideGetRestrictionLevels
+ * @covers NamespaceInfo::getRestrictionLevels
+ *
+ * @param array $expected
+ * @param int $ns
+ * @param User|null $user
+ */
+ public function testGetRestrictionLevels( array $expected, $ns, User $user = null ) {
+ $this->setMwGlobals( [
+ 'wgGroupPermissions' => [
+ '*' => [ 'edit' => true ],
+ 'autoconfirmed' => [ 'editsemiprotected' => true ],
+ 'sysop' => [
+ 'editsemiprotected' => true,
+ 'editprotected' => true,
+ ],
+ 'privileged' => [ 'privileged' => true ],
+ ],
+ 'wgRevokePermissions' => [
+ 'noeditsemiprotected' => [ 'editsemiprotected' => true ],
+ ],
+ ] );
+ $obj = $this->newObj( [
+ 'NamespaceProtection' => [
+ NS_MAIN => 'autoconfirmed',
+ NS_USER => 'sysop',
+ 101 => [ 'editsemiprotected', 'privileged' ],
+ ],
+ ] );
+ $this->assertSame( $expected, $obj->getRestrictionLevels( $ns, $user ) );
+ }
+
+ public function provideGetRestrictionLevels() {
+ return [
+ 'No namespace restriction' => [ [ '', 'autoconfirmed', 'sysop' ], NS_TALK ],
+ 'Restricted to autoconfirmed' => [ [ '', 'sysop' ], NS_MAIN ],
+ 'Restricted to sysop' => [ [ '' ], NS_USER ],
+ // @todo Bug -- 'sysop' protection should be allowed in this case. Someone who's
+ // autoconfirmed and also privileged can edit this namespace, and would be blocked by
+ // the sysop protection.
+ 'Restricted to someone in two groups' => [ [ '' ], 101 ],
+
+ 'No special permissions' => [ [ '' ], NS_TALK, $this->getMockUser() ],
+ 'autoconfirmed' => [
+ [ '', 'autoconfirmed' ],
+ NS_TALK,
+ $this->getMockUser( [ 'autoconfirmed' ] )
+ ],
+ 'autoconfirmed revoked' => [
+ [ '' ],
+ NS_TALK,
+ $this->getMockUser( [ 'autoconfirmed', 'noeditsemiprotected' ] )
+ ],
+ 'sysop' => [
+ [ '', 'autoconfirmed', 'sysop' ],
+ NS_TALK,
+ $this->getMockUser( [ 'sysop' ] )
+ ],
+ 'sysop with autoconfirmed revoked (a bit silly)' => [
+ [ '', 'sysop' ],
+ NS_TALK,
+ $this->getMockUser( [ 'sysop', 'noeditsemiprotected' ] )
+ ],
+ ];
+ }
+
+ // %} End restriction levels
}
+
+/**
+ * For really cool vim folding this needs to be at the end:
+ * vim: foldmarker=%{,%} foldmethod=marker
+ */
}
/**
+ * @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'
);
<?php
+use MediaWiki\MediaWikiServices;
+
require_once dirname( __DIR__ ) . '/includes/upload/UploadFromUrlTest.php';
class UploadFromUrlTestSuite extends PHPUnit_Framework_TestSuite {
$wgStyleDirectory = "$IP/skins";
}
- RepoGroup::destroySingleton();
+ MediaWikiServices::getInstance()->resetServiceForTesting( 'RepoGroup' );
FileBackendGroup::destroySingleton();
}
$GLOBALS[$var] = $val;
}
// Restore backends
- RepoGroup::destroySingleton();
+ MediaWikiServices::getInstance()->resetServiceForTesting( 'RepoGroup' );
FileBackendGroup::destroySingleton();
parent::tearDown();
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();