Use ...->stashFile()->getFileKey() instead.
* "Public domain" was removed as a wiki license option from the installer, in
favour of CC-0.
+* AuthenticationRequest::$required is now changed from REQUIRED to PRIMARY_REQUIRED
+ on requests needed by primary providers even if all primaries need them.
+ Primary providers are discouraged from returning multiple REQUIRED requests.
== Compatibility ==
case self::AS_CANNOT_USE_CUSTOM_MODEL:
case self::AS_PARSE_ERROR:
- $wgOut->addWikiText( '<div class="error">' . $status->getWikiText() . '</div>' );
+ $wgOut->addWikiText( '<div class="error">' . "\n" . $status->getWikiText() . '</div>' );
return true;
case self::AS_SUCCESS_NEW_ARTICLE:
// is if an extension hook aborted from inside ArticleSave.
// Render the status object into $this->hookError
// FIXME this sucks, we should just use the Status object throughout
- $this->hookError = '<div class="error">' . $status->getWikiText() .
+ $this->hookError = '<div class="error">' ."\n" . $status->getWikiText() .
'</div>';
return true;
}
* 3) Adds a "delimiter" element to the array, either '://', ':' or '//' (see (2)).
*
* @param string $url A URL to parse
- * @return string[] Bits of the URL in an associative array, per PHP docs
+ * @return string[]|bool Bits of the URL in an associative array, per PHP docs, false on failure
*/
function wfParseUrl( $url ) {
global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
$pageid = $this->oldTitle->getArticleID( Title::GAID_FOR_UPDATE );
$protected = $this->oldTitle->isProtected();
- // Do the actual move
+ // Do the actual move; if this fails, it will throw an MWException(!)
$nullRevision = $this->moveToInternal( $user, $this->newTitle, $reason, $createRedirect );
// Refresh the sortkey for this row. Be careful to avoid resetting
*
* @fixme This was basically directly moved from Title, it should be split into smaller functions
* @param User $user the User doing the move
- * @param Title $nt The page to move to, which should be a redirect or nonexistent
+ * @param Title $nt The page to move to, which should be a redirect or non-existent
* @param string $reason The reason for the move
* @param bool $createRedirect Whether to leave a redirect at the old title. Does not check
* if the user has the suppressredirect right
$logType = 'move';
}
+ if ( $moveOverRedirect ) {
+ $overwriteMessage = wfMessage(
+ 'delete_and_move_reason',
+ $this->oldTitle->getPrefixedText()
+ )->text();
+ $newpage = WikiPage::factory( $nt );
+ $errs = [];
+ $status = $newpage->doDeleteArticleReal(
+ $overwriteMessage,
+ /* $suppress */ false,
+ $nt->getArticleId(),
+ /* $commit */ false,
+ $errs,
+ $user
+ );
+
+ if ( !$status->isGood() ) {
+ throw new MWException( 'Failed to delete page-move revision: ' . $status );
+ }
+
+ $nt->resetArticleID( false );
+ }
+
if ( $createRedirect ) {
if ( $this->oldTitle->getNamespace() == NS_CATEGORY
&& !wfMessage( 'category-move-redirect-override' )->inContentLanguage()->isDisabled()
$newpage = WikiPage::factory( $nt );
- if ( $moveOverRedirect ) {
- $newid = $nt->getArticleID();
- $newcontent = $newpage->getContent();
-
- # Delete the old redirect. We don't save it to history since
- # by definition if we've got here it's rather uninteresting.
- # We have to remove it so that the next step doesn't trigger
- # a conflict on the unique namespace+title index...
- $dbw->delete( 'page', [ 'page_id' => $newid ], __METHOD__ );
-
- $newpage->doDeleteUpdates( $newid, $newcontent );
- }
-
# Save a null revision in the page's history notifying of the move
$nullRevision = Revision::newNullRevision( $dbw, $oldid, $comment, true, $user );
if ( !is_object( $nullRevision ) ) {
);
}
- if ( !$moveOverRedirect ) {
- WikiPage::onArticleCreate( $nt );
- }
+ WikiPage::onArticleCreate( $nt );
# Recreate the redirect, this time in the other direction.
if ( $redirectContent ) {
// Build map of extension directories to extension info
if ( self::$extensionInfo === null ) {
+ $extDir = $this->getConfig()->get( 'ExtensionDirectory' );
self::$extensionInfo = [
realpath( __DIR__ ) ?: __DIR__ => [
'path' => $IP,
'license-name' => 'GPL-2.0+',
],
realpath( "$IP/extensions" ) ?: "$IP/extensions" => null,
+ realpath( $extDir ) ?: $extDir => null,
];
$keep = [
'path' => null,
$authRes = 'Failed';
$message = $res->message;
\MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' )
- ->info( __METHOD__ . ': Authentication failed: ' . $message->plain() );
+ ->info( __METHOD__ . ': Authentication failed: '
+ . $message->inLanguage( 'en' )->plain() );
break;
default:
+ \MediaWiki\Logger\LoggerFactory::getInstance( 'authentication' )
+ ->info( __METHOD__ . ': Authentication failed due to unsupported response type: '
+ . $res->status, $this->getAuthenticationResponseLogData( $res ) );
$authRes = 'Aborted';
break;
}
public function getHelpUrls() {
return 'https://www.mediawiki.org/wiki/API:Login';
}
+
+ /**
+ * Turns an AuthenticationResponse into a hash suitable for passing to Logger
+ * @param AuthenticationResponse $response
+ * @return array
+ */
+ protected function getAuthenticationResponseLogData( AuthenticationResponse $response ) {
+ $ret = [
+ 'status' => $response->status,
+ ];
+ if ( $response->message ) {
+ $ret['message'] = $response->message->inLanguage( 'en' )->plain();
+ };
+ $reqs = [
+ 'neededRequests' => $response->neededRequests,
+ 'createRequest' => $response->createRequest,
+ 'linkRequest' => $response->linkRequest,
+ ];
+ foreach ( $reqs as $k => $v ) {
+ if ( $v ) {
+ $v = is_array( $v ) ? $v : [ $v ];
+ $reqClasses = array_unique( array_map( 'get_class', $v ) );
+ sort( $reqClasses );
+ $ret[$k] = implode( ', ', $reqClasses );
+ }
+ }
+ return $ret;
+ }
}
* @defgroup API API
*/
+use MediaWiki\Logger\LoggerFactory;
+
/**
* This is the main API class, used for both external and internal processing.
* When executed, it will create the requested formatter object,
$config->get( 'CrossSiteAJAXdomainExceptions' )
)
) ) {
- MediaWiki\Logger\LoggerFactory::getInstance( 'cors' )->warning(
+ LoggerFactory::getInstance( 'cors' )->warning(
'Non-whitelisted CORS request with session cookies', [
'origin' => $originHeader,
'cookies' => $sessionCookies,
protected function setRequestExpectations( ApiBase $module ) {
$limits = $this->getConfig()->get( 'TrxProfilerLimits' );
$trxProfiler = Profiler::instance()->getTransactionProfiler();
+ $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
if ( $this->getRequest()->hasSafeMethod() ) {
$trxProfiler->setExpectations( $limits['GET'], __METHOD__ );
} elseif ( $this->getRequest()->wasPosted() && !$module->isWriteMode() ) {
'offset' => $this->mUpload->getOffset(),
];
- $this->dieUsage( $status->getWikiText( false, false, 'en' ), 'stashfailed', 0, $extradata );
+ $this->dieStatusWithCode( $status, 'stashfailed', $extradata );
}
}
$filekey,
[ 'result' => 'Failure', 'stage' => 'assembling', 'status' => $status ]
);
- $this->dieUsage( $status->getWikiText( false, false, 'en' ), 'stashfailed' );
+ $this->dieStatusWithCode( $status, 'stashfailed' );
}
// The fully concatenated file has a new filekey. So remove
$this->dieUsage( $parsed['info'], $parsed['code'], 0, $data );
}
+ /**
+ * Like dieStatus(), but always uses $overrideCode for the error code, unless the code comes from
+ * IApiMessage.
+ *
+ * @param Status $status
+ * @param string $overrideCode Error code to use if there isn't one from IApiMessage
+ * @param array|null $moreExtraData
+ * @throws UsageException
+ */
+ public function dieStatusWithCode( $status, $overrideCode, $moreExtraData = null ) {
+ $extraData = null;
+ list( $code, $msg ) = $this->getErrorFromStatus( $status, $extraData );
+ $errors = $status->getErrorsByType( 'error' ) ?: $status->getErrorsByType( 'warning' );
+ if ( !( $errors[0]['message'] instanceof IApiMessage ) ) {
+ $code = $overrideCode;
+ }
+ if ( $moreExtraData ) {
+ $extraData += $moreExtraData;
+ }
+ $this->dieUsage( $msg, $code, 0, $extraData );
+ }
+
/**
* Select an upload module and set it to mUpload. Dies on failure. If the
* request was a status request and not a true upload, returns false;
if ( !$progress ) {
$this->dieUsage( 'No result in status data', 'missingresult' );
} elseif ( !$progress['status']->isGood() ) {
- $this->dieUsage( $progress['status']->getWikiText( false, false, 'en' ), 'stashfailed' );
+ $this->dieStatusWithCode( $progress['status'], 'stashfailed' );
}
if ( isset( $progress['status']->value['verification'] ) ) {
$this->checkVerification( $progress['status']->value['verification'] );
if ( $this->mParams['chunk'] ) {
// Chunk upload
- $this->mUpload = new UploadFromChunks();
+ $this->mUpload = new UploadFromChunks( $this->getUser() );
if ( isset( $this->mParams['filekey'] ) ) {
if ( $this->mParams['offset'] === 0 ) {
$this->dieUsage( 'Cannot supply a filekey when offset is 0', 'badparams' );
// Query them and merge results
$reqs = [];
- $allPrimaryRequired = null;
foreach ( $providers as $provider ) {
$isPrimary = $provider instanceof PrimaryAuthenticationProvider;
- $thisRequired = [];
foreach ( $provider->getAuthenticationRequests( $providerAction, $options ) as $req ) {
$id = $req->getUniqueId();
- // If it's from a Primary, mark it as "primary-required" but
- // track it for later.
+ // If a required request if from a Primary, mark it as "primary-required" instead
if ( $isPrimary ) {
if ( $req->required ) {
- $thisRequired[$id] = true;
$req->required = AuthenticationRequest::PRIMARY_REQUIRED;
}
}
- if ( !isset( $reqs[$id] ) || $req->required === AuthenticationRequest::REQUIRED ) {
+ if (
+ !isset( $reqs[$id] )
+ || $req->required === AuthenticationRequest::REQUIRED
+ || $reqs[$id] === AuthenticationRequest::OPTIONAL
+ ) {
$reqs[$id] = $req;
}
}
-
- // Track which requests are required by all primaries
- if ( $isPrimary ) {
- $allPrimaryRequired = $allPrimaryRequired === null
- ? $thisRequired
- : array_intersect_key( $allPrimaryRequired, $thisRequired );
- }
- }
- // Any requests that were required by all primaries are required.
- foreach ( (array)$allPrimaryRequired as $id => $dummy ) {
- $reqs[$id]->required = AuthenticationRequest::REQUIRED;
}
// AuthManager has its own req for some actions
const REQUIRED = 1;
/** Indicates that the request is required by a primary authentication
- * provdier, but other primary authentication providers do not require it. */
+ * provdier. Since the user can choose which primary to authenticate with,
+ * the request might or might not end up being actually required. */
const PRIMARY_REQUIRED = 2;
/** @var string|null The AuthManager::ACTION_* constant this request was
/** Provider cannot create or link to accounts */
const TYPE_NONE = 'none';
+ /**
+ * {@inheritdoc}
+ *
+ * Of the requests returned by this method, exactly one should have
+ * {@link AuthenticationRequest::$required} set to REQUIRED.
+ */
+ public function getAuthenticationRequests( $action, array $options );
+
/**
* Start an authentication flow
*
protected function getFileText( Title $title ) {
$file = wfLocalFile( $title );
if ( $file && $file->exists() ) {
- return $file->getHandler()->getEntireText( $file );
+ $handler = $file->getHandler();
+ if ( !$handler ) {
+ return null;
+ }
+ return $handler->getEntireText( $file );
}
return null;
return $this->__call( __FUNCTION__, func_get_args() );
}
- public function begin( $fname = __METHOD__ ) {
+ public function begin( $fname = __METHOD__, $mode = IDatabase::TRANSACTION_EXPLICIT ) {
return $this->__call( __FUNCTION__, func_get_args() );
}
if ( !$this->mTrxLevel && $this->getFlag( DBO_TRX )
&& $this->isTransactableQuery( $sql )
) {
- $this->begin( __METHOD__ . " ($fname)" );
+ $this->begin( __METHOD__ . " ($fname)", self::TRANSACTION_INTERNAL );
$this->mTrxAutomatic = true;
}
$useTrx = !$this->mTrxLevel;
if ( $useTrx ) {
- $this->begin( $fname );
+ $this->begin( $fname, self::TRANSACTION_INTERNAL );
}
try {
# Update any existing conflicting row(s)
throw $e;
}
if ( $useTrx ) {
- $this->commit( $fname );
+ $this->commit( $fname, self::TRANSACTION_INTERNAL );
}
return $ok;
$this->mTrxPreCommitCallbacks[] = [ $callback, wfGetCaller() ];
} else {
// If no transaction is active, then make one for this callback
- $this->begin( __METHOD__ );
+ $this->begin( __METHOD__, self::TRANSACTION_INTERNAL );
try {
call_user_func( $callback );
$this->commit( __METHOD__ );
final public function startAtomic( $fname = __METHOD__ ) {
if ( !$this->mTrxLevel ) {
- $this->begin( $fname );
+ $this->begin( $fname, self::TRANSACTION_INTERNAL );
$this->mTrxAutomatic = true;
// If DBO_TRX is set, a series of startAtomic/endAtomic pairs will result
// in all changes being in one transaction to keep requests transactional.
$this->endAtomic( $fname );
}
- final public function begin( $fname = __METHOD__ ) {
- if ( $this->mTrxLevel ) { // implicit commit
+ final public function begin( $fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT ) {
+ // Protect against mismatched atomic section, transaction nesting, and snapshot loss
+ if ( $this->mTrxLevel ) {
if ( $this->mTrxAtomicLevels ) {
- // If the current transaction was an automatic atomic one, then we definitely have
- // a problem. Same if there is any unclosed atomic level.
$levels = implode( ', ', $this->mTrxAtomicLevels );
- throw new DBUnexpectedError(
- $this,
- "Got explicit BEGIN from $fname while atomic section(s) $levels are open."
- );
+ $msg = "Got explicit BEGIN from $fname while atomic section(s) $levels are open.";
+ throw new DBUnexpectedError( $this, $msg );
} elseif ( !$this->mTrxAutomatic ) {
- // We want to warn about inadvertently nested begin/commit pairs, but not about
- // auto-committing implicit transactions that were started by query() via DBO_TRX
- throw new DBUnexpectedError(
- $this,
- "$fname: Transaction already in progress (from {$this->mTrxFname}), " .
- " performing implicit commit!"
- );
- } elseif ( $this->mTrxDoneWrites ) {
- // The transaction was automatic and has done write operations
- throw new DBUnexpectedError(
- $this,
- "$fname: Automatic transaction with writes in progress" .
- " (from {$this->mTrxFname}), performing implicit commit!\n"
- );
- }
-
- $this->runOnTransactionPreCommitCallbacks();
- $writeTime = $this->pendingWriteQueryDuration();
- $this->doCommit( $fname );
- if ( $this->mTrxDoneWrites ) {
- $this->mDoneWrites = microtime( true );
- $this->getTransactionProfiler()->transactionWritingOut(
- $this->mServer, $this->mDBname, $this->mTrxShortId, $writeTime );
+ $msg = "$fname: Explicit transaction already active (from {$this->mTrxFname}).";
+ throw new DBUnexpectedError( $this, $msg );
+ } else {
+ // @TODO: make this an exception at some point
+ $msg = "$fname: Implicit transaction already active (from {$this->mTrxFname}).";
+ wfLogDBError( $msg );
+ return; // join the main transaction set
}
-
- $this->runOnTransactionIdleCallbacks( self::TRIGGER_COMMIT );
+ } elseif ( $this->getFlag( DBO_TRX ) && $mode !== self::TRANSACTION_INTERNAL ) {
+ // @TODO: make this an exception at some point
+ wfLogDBError( "$fname: Implicit transaction expected (DBO_TRX set)." );
+ return; // let any writes be in the main transaction
}
// Avoid fatals if close() was called
$levels = implode( ', ', $this->mTrxAtomicLevels );
throw new DBUnexpectedError(
$this,
- "Got COMMIT while atomic sections $levels are still open"
+ "Got COMMIT while atomic sections $levels are still open."
);
}
} elseif ( !$this->mTrxAutomatic ) {
throw new DBUnexpectedError(
$this,
- "$fname: Flushing an explicit transaction, getting out of sync!"
+ "$fname: Flushing an explicit transaction, getting out of sync."
);
}
} else {
if ( !$this->mTrxLevel ) {
- wfWarn( "$fname: No transaction to commit, something got out of sync!" );
+ wfWarn( "$fname: No transaction to commit, something got out of sync." );
return; // nothing to do
} elseif ( $this->mTrxAutomatic ) {
- throw new DBUnexpectedError(
- $this,
- "$fname: Explicit commit of implicit transaction."
- );
+ // @TODO: make this an exception at some point
+ wfLogDBError( "$fname: Explicit commit of implicit transaction." );
+ return; // wait for the main transaction set commit round
}
}
}
final public function rollback( $fname = __METHOD__, $flush = '' ) {
- if ( $flush !== self::FLUSHING_INTERNAL && $flush !== self::FLUSHING_ALL_PEERS ) {
+ if ( $flush === self::FLUSHING_INTERNAL || $flush === self::FLUSHING_ALL_PEERS ) {
if ( !$this->mTrxLevel ) {
- wfWarn( "$fname: No transaction to rollback, something got out of sync!" );
return; // nothing to do
}
} else {
if ( !$this->mTrxLevel ) {
+ wfWarn( "$fname: No transaction to rollback, something got out of sync." );
return; // nothing to do
+ } elseif ( $this->getFlag( DBO_TRX ) ) {
+ throw new DBUnexpectedError(
+ $this,
+ "$fname: Expected mass rollback of all peer databases (DBO_TRX set)."
+ );
}
}
$this->didbegin = false;
/* If we are not in a transaction, we need to be for savepoint trickery */
if ( !$dbw->trxLevel() ) {
- $dbw->begin( "FOR SAVEPOINT" );
+ $dbw->begin( "FOR SAVEPOINT", DatabasePostgres::TRANSACTION_INTERNAL );
$this->didbegin = true;
}
}
* @param string $desiredSchema
*/
function determineCoreSchema( $desiredSchema ) {
- $this->begin( __METHOD__ );
+ $this->begin( __METHOD__, self::TRANSACTION_INTERNAL );
if ( $this->schemaExists( $desiredSchema ) ) {
if ( in_array( $desiredSchema, $this->getSchemas() ) ) {
$this->mCoreSchema = $desiredSchema;
/** @var int Callback triggered by rollback */
const TRIGGER_ROLLBACK = 3;
+ /** @var string Transaction is requested by regular caller outside of the DB layer */
+ const TRANSACTION_EXPLICIT = '';
+ /** @var string Transaction is requested interally via DBO_TRX/startAtomic() */
+ const TRANSACTION_INTERNAL = 'implicit';
+
/** @var string Transaction operation comes from service managing all DBs */
const FLUSHING_ALL_PEERS = 'flush';
/** @var string Transaction operation comes from the database class internally */
* automatically because of the DBO_TRX flag.
*
* @param string $fname
+ * @param string $mode A situationally valid IDatabase::TRANSACTION_* constant [optional]
* @throws DBError
*/
- public function begin( $fname = __METHOD__ );
+ public function begin( $fname = __METHOD__, $mode = self::TRANSACTION_EXPLICIT );
/**
* Commits a transaction previously started using begin().
return $ok;
}
+ public function changeTTL( $key, $expiry = 0 ) {
+ list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
+ try {
+ $db = $this->getDB( $serverIndex );
+ $db->update(
+ $tableName,
+ [ 'exptime' => $db->timestamp( $this->convertExpiry( $expiry ) ) ],
+ [ 'keyname' => $key, 'exptime > ' . $db->addQuotes( $db->timestamp( time() ) ) ],
+ __METHOD__
+ );
+ if ( $db->affectedRows() == 0 ) {
+ return false;
+ }
+ } catch ( DBError $e ) {
+ $this->handleWriteError( $e, $serverIndex );
+ return false;
+ }
+
+ return true;
+ }
+
/**
* @param IDatabase $db
* @param string $exptime
*/
public function tryStashFile( User $user, $isPartial = false ) {
if ( !$isPartial ) {
- $props = $this->mFileProps;
- $error = null;
- Hooks::run( 'UploadStashFile', [ $this, $user, $props, &$error ] );
+ $error = $this->runUploadStashFileHook( $user );
if ( $error ) {
- if ( !is_array( $error ) ) {
- $error = [ $error ];
- }
return call_user_func_array( 'Status::newFatal', $error );
}
}
}
}
+ /**
+ * @param User $user
+ * @return array|null Error message and parameters, null if there's no error
+ */
+ protected function runUploadStashFileHook( User $user ) {
+ $props = $this->mFileProps;
+ $error = null;
+ Hooks::run( 'UploadStashFile', [ $this, $user, $props, &$error ] );
+ if ( $error ) {
+ if ( !is_array( $error ) ) {
+ $error = [ $error ];
+ }
+ }
+ return $error;
+ }
+
/**
* If the user does not supply all necessary information in the first upload
* form submission (either by accident or by design) then we may want to
/**
* Setup local pointers to stash, repo and user (similar to UploadFromStash)
*
- * @param User|null $user Default: null
+ * @param User $user
* @param UploadStash|bool $stash Default: false
* @param FileRepo|bool $repo Default: false
*/
- public function __construct( $user = null, $stash = false, $repo = false ) {
- // user object. sometimes this won't exist, as when running from cron.
+ public function __construct( User $user, $stash = false, $repo = false ) {
$this->user = $user;
if ( $repo ) {
// Update the mTempPath and mLocalFile
// (for FileUpload or normal Stash to take over)
$tStart = microtime( true );
- $this->mLocalFile = parent::doStashFile( $this->user );
+ // This is a re-implementation of UploadBase::tryStashFile(), we can't call it because we
+ // override doStashFile() with completely different functionality in this class...
+ $error = $this->runUploadStashFileHook( $this->user );
+ if ( $error ) {
+ call_user_func_array( [ $status, 'fatal' ], $error );
+ return $status;
+ }
+ try {
+ $this->mLocalFile = parent::doStashFile( $this->user );
+ } catch ( UploadStashException $e ) {
+ $status->fatal( 'uploadstash-exception', get_class( $e ), $e->getMessage() );
+ return $status;
+ }
+
$tAmount = microtime( true ) - $tStart;
$this->mLocalFile->setLocalReference( $tmpFile ); // reuse (e.g. for getImageInfo())
wfDebugLog( 'fileconcatenate', "Stashed combined file ($i chunks) in $tAmount seconds." );
return $this->mFileProps['sha1'];
}
- /*
- * protected function verifyFile() inherited
- */
-
- /**
- * Stash the file.
- *
- * @param User $user
- * @return UploadStashFile
- */
- protected function doStashFile( User $user = null ) {
- // replace mLocalFile with an instance of UploadStashFile, which adds some methods
- // that are useful for stashed files.
- $this->mLocalFile = parent::doStashFile( $user );
-
- return $this->mLocalFile;
- }
-
/**
* Remove a temporarily kept file stashed by saveTempUploadedFile().
* @return bool Success
"alllogstext": "{{SITENAME}} üçün bütün mövcud qeydlərin birgə göstərişi.\nQeyd növü, istifadəçi adı və ya təsir edilmiş səhifəni seçməklə daha spesifik ola bilərsiniz.",
"logempty": "Jurnalda uyğun qeyd tapılmadı.",
"log-title-wildcard": "Bu mətnlə başlayan başlıqları axtar",
+ "checkbox-select": "Seçin: $1",
+ "checkbox-all": "Hamısı",
+ "checkbox-none": "Heç biri",
+ "checkbox-invert": "Çevir",
"allpages": "Bütün səhifələr",
"nextpage": "Sonrakı səhifə ($1)",
"prevpage": "Əvvəlki səhifə ($1)",
"editlink": "دەستکاری",
"viewsourcelink": "بینینی سەرچاوە",
"editsectionhint": "دەستکاریکردنی بەش: $1",
- "toc": "Ù\86اÙ\88Û\95Ú\95Û\86Ú©",
+ "toc": "Ù¾Û\8eرست",
"showtoc": "نیشانیبدە",
"hidetoc": "بیشارەوە",
"collapsible-collapse": "کۆی بکەوە",
"grant-group-high-volume": "Velkoobjemové činnosti",
"grant-group-customization": "Nastavení a přizpůsobení",
"grant-group-administration": "Provádění správcovských činností",
- "grant-group-private-information": "Zpřístupnit soukromá data o vás",
+ "grant-group-private-information": "Přístup k soukromým údajům o vás",
"grant-group-other": "Různé činnosti",
"grant-blockusers": "Blokovat a odblokovávat uživatele",
"grant-createaccount": "Zakládat účty",
"grant-highvolume": "Hromadné editace",
"grant-oversight": "Skrývat uživatele a utajovat revize",
"grant-patrol": "Patrolovat změny stránek",
+ "grant-privateinfo": "Přístup k soukromým údajům",
"grant-protect": "Zamykat a odemykat stránky",
"grant-rollback": "Vracet editace zpět",
"grant-sendemail": "Posílat e-maily ostatním uživatelům",
"uploadstash-errclear": "Soubory se nepodařilo vymazat.",
"uploadstash-refresh": "Aktualizovat seznam souborů",
"uploadstash-thumbnail": "zobrazit náhled",
+ "uploadstash-exception": "Načtený soubor se nepodařilo uložit do skrýše ($1): „$2“.",
"invalid-chunk-offset": "Neplatný posun bloku",
"img-auth-accessdenied": "Přístup odepřen",
"img-auth-nopathinfo": "Chybí PATH_INFO.\nVáš server není nastaven tak, aby tuto informaci poskytoval.\nMožná funguje pomocí CGI a img_auth na něm nemůže fungovat.\nVizte https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Image_Authorization.",
"trackingcategories-name": "Název hlášení",
"trackingcategories-desc": "Kritéria pro vložení do kategorie",
"restricted-displaytitle-ignored": "Stránky s ignorovanými zobrazovanými názvy",
- "restricted-displaytitle-ignored-desc": "Stránky obsahující příkaz <code><nowiki>{{DISPLAYTITLE}}</nowiki></code>, který se ignoruje, neboť není ekvivalentní skutečnému názvu stránky.",
+ "restricted-displaytitle-ignored-desc": "Stránka obsahuje příkaz <code><nowiki>{{DISPLAYTITLE}}</nowiki></code>, který se ignoruje, neboť není ekvivalentní skutečnému názvu stránky.",
"noindex-category-desc": "Stránka není indexována roboty, protože obsahuje kouzelné slovo <code><nowiki>__NOINDEX__</nowiki></code> a je ve jmenném prostoru, ve kterém je tento příznak dovolen.",
"index-category-desc": "Stránka obsahuje kouzelné slovo <code><nowiki>__INDEX__</nowiki></code> (a je ve jmenném prostoru, ve kterém je tento příznak dovolen), takže je indexována roboty, přestože by normálně nebyla.",
"post-expand-template-inclusion-category-desc": "Stránka je po rozbalení všech šablon větší než <code>$wgMaxArticleSize</code>, takže některé šablony rozbaleny nebyly.",
"anontalk": "Werênayış",
"navigation": "Pusula",
"and": " u",
- "qbfind": "Bıvêne",
+ "qbfind": "Bıvin",
"qbbrowse": "Çım ra viyarne",
"qbedit": "Bıvurne",
"qbpageoptions": "Ena pele",
"view-foreign": "$1 de bıvêne",
"edit": "Bıvurne",
"edit-local": "Şınasnayışê lokali bıvurne",
- "create": "Vıraze",
+ "create": "Bıvıraz",
"create-local": "Şınasnayışê lokali cı ke",
"editthispage": "Ena pele bıvurne",
"create-this-page": "Na pele bınuse",
"talkpagelinktext": "werênayış",
"specialpage": "Pela xısusiye",
"personaltools": "Hacetê şexsiy",
- "articlepage": "Pela zerreki bıvêne",
+ "articlepage": "Pera zerreki bıvin",
"talk": "Werênayış",
"views": "Asayışi",
"toolbox": "Haceti",
"imagepage": "Pera dosya bıasne",
"mediawikipage": "Pera mesaci bıasne",
"templatepage": "Pera şabloni bıasne",
- "viewhelppage": "Pela peşti bıvêne",
+ "viewhelppage": "Pera peşti bıvin",
"categorypage": "Pela kategoriya bıasne",
"viewtalkpage": "Werênayışi bıvêne",
"otherlanguages": "Zıwananê binan de",
"redirectedfrom": "($1 ra kırışı yê)",
"redirectpagesub": "Pela berdışi",
"redirectto": "Beno hetê:",
- "lastmodifiedat": "Ena pele tewr peyên roca $1, saeta $2 de biye rocane.",
+ "lastmodifiedat": "Per roca $1, sehat $2 de biye neye.",
"viewcount": "Ena pele {{PLURAL:$1|rae|$1 rey}} vêniya.",
"protectedpage": "Pela pawıtiye",
"jumpto": "Şo be:",
"confirmable-yes": "Eya",
"confirmable-no": "Nê",
"thisisdeleted": "Bıvêne ya zi $1 peyser biya?",
- "viewdeleted": "$1 bıvêne?",
+ "viewdeleted": "$1 bıvin?",
"restorelink": "{{PLURAL:$1|jew vurnayış besteriya|$1 vurnayışi besteriyaye}}",
"feedlinks": "Warikerdış:",
"feed-invalid": "Qeydey cıresnayışê beğşi nêvêreno.",
"perfcached": "Datay cı ver hazır biye. No semedê ra nıkayin niyo! tewr zaf {{PLURAL:$1|netice|$1 netice}} debêno de",
"perfcachedts": "Cêr de malumatê nımıteyi esti, demdê newe kerdışo peyın: $1. Tewr zaf {{PLURAL:$4|netice|$4 neticey cı}} debyayo de",
"querypage-no-updates": "Rocanebiyayışê na pele nıka cadayiyê.\nDayiyi tiya nıka newe nêbenê.",
- "viewsource": "Çımey bıvêne",
+ "viewsource": "Çemi bıvin",
"viewsource-title": "Cı geyrayışê $1'i bıvin",
"actionthrottled": "Kerden peysnaya",
"actionthrottledtext": "Riyê tedbirê anti-spami ra, wextê do kılmek de şıma nê fealiyeti nêşkenê zaf zêde bıkerê, şıma ki no hedi viyarna ra.\nÇend deqey ra tepeya reyna bıcerrebnên.",
"nologinlink": "Yew hesab ake",
"createaccount": "Hesab vıraze",
"gotaccount": "Hesabê şıma esto? '''$1'''.",
- "gotaccountlink": "Cı kewe",
+ "gotaccountlink": "Cıkewtış",
"userlogin-resetlink": "Melumatê cıkewtışi xo vira kerdê?",
"userlogin-resetpassword-link": "Parola xo kerda xo vira?",
"userlogin-helplink2": "Heqa qeydbiyayışi de peşti bıgêrên",
"botpasswords-label-appid": "Nameyê boti:",
"botpasswords-label-create": "Vıraze",
"botpasswords-label-update": "Rocane ke",
- "botpasswords-label-cancel": "Bıtexelne",
+ "botpasswords-label-cancel": "İbtal ke",
"botpasswords-label-delete": "Bestere",
"botpasswords-label-resetpassword": "Parola raçarne",
"botpasswords-label-grants-column": "Dayen",
"resetpass_forbidden": "parolayi nêvuryayi",
"resetpass-no-info": "şıma gani hesab akere u hona bıeşke bırese cı",
"resetpass-submit-loggedin": "Parola bıvurne",
- "resetpass-submit-cancel": "Bıtexelne",
+ "resetpass-submit-cancel": "İbtal ke",
"resetpass-wrong-oldpass": "parolayo parola maqbul niyo.\nşıma ya parolaye xo vurnayo ya zi parolayo muwaqqat waşto.",
"resetpass-recycled": "Parolaya şımaya newiye wa paroloya şımaya verêne ra ferqıne bo.",
"resetpass-temp-emailed": "E postaya rışyayê yubkoda şıma ronıştış akerdo. Ronıştışi xo temammkerdışi rê yu parolaya newi lazım a",
"hr_tip": "Xeta verardiye (teserrufın bıgureyne/bıxebetne)",
"summary": "Xulasa:",
"subject": "Mewzu:",
- "minoredit": "No yew vurnayışo werdiyo",
- "watchthis": "Ena pele seyr ke",
- "savearticle": "Pele qeyd ke",
+ "minoredit": "Vurriyayışo werdiyo",
+ "watchthis": "Seyr kı",
+ "savearticle": "Qeyd kı",
"savechanges": "Vurnayışan qeyd ke",
"publishpage": "Perer bıhesırne",
"publishchanges": "Vurnayışa vıla ke",
"accmailtext": "[[User talk:$1|$1]] parolayo ke raşt ameyo şırawiyo na adres $2.\n\nQey na hesabê newe parola, cıkewtış dıma şıma eşkeni na qısım de ''[[Special:ChangePassword|parola bıvurn]]'' bıvurni.",
"newarticle": "(Newe)",
"newarticletext": "To yew gıre tıkna be ra yew pela ke hewna çıniya.\nSeba afernayışê pele ra, qutiya metnê cêrêni bıgurene (seba melumati qaytê [$1 pela peşti] ke).\nEke be ğeletine ameya tiya, wa gocega <strong>peyser</strong>i programê xo de bıtıkne.",
- "anontalkpagetext": "----''No pel, pel o karbero hesab a nêkerdeyan o, ya zi karbero hesab akerdeyan o labele pê hesabê xo nêkewto de. No sebeb ra ma IP adres şuxulneni û ney IP adresan herkes eşkeno bıvino. Eke şıma qayil niye ina bo xo ri [[Special:CreateAccount|yew hesab bıvıraze]] veyaxut [[Special:UserLogin|hesab akere]].''",
+ "anontalkpagetext": "----''Na per, perêk kı karbero hesab a nêkerdeyan o, ya zi karbero hesab akerdeyan o labele pê hesabê xo nêkewto de. No sebeb ra ma IP adres xebetneno û ney IP adresan herkes nêşeno bıvino. Eke şıma qayil niye ina bo xorê [[Special:CreateAccount|yew hesab bıvıraze]] veya xut [[Special:UserLogin|hesab akere]].''",
"noarticletext": "Ena pele de hewna theba çıniyo.\nTı şenê zerreyê pelanê binan de [[Special:Search/{{PAGENAME}}|qandê sernameyê ena pele cı geyre]],\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} qeydan miyan de cı geyre],\nya zi [{{fullurl:{{FULLPAGENAME}}|action=edit}} ena pele vıraze]</span>.{{MediaWiki mesaca pera newi}}",
"noarticletext-nopermission": "Ena pele de hewna theba çıniyo.\nTı şenay zerreyê pelanê binan de [[Special:Search/{{PAGENAME}}|seba sernameyê na pele cı geyre]], ya zi <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} qeydan miyan de cı geyre]</span>, ema destur çıniyo ke na pele vırazê.",
"missing-revision": "Rewizyonê name dê pela da #$1 \"{{FULLPAGENAME}}\" dı çıniyo.\n\nNo normal de tarix dê pelanê besterneyan dı ena xırabin asena.\nDetayê besternayışi [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} tiya dı] aseno.",
"next-page": "Pela peyên",
"prevn-title": "$1o verên {{PLURAL:$1|netice|neticeyan}}",
"nextn-title": "$1o ke yeno {{PLURAL:$1|netice|neticey}}",
- "shown-title": "bimocne $1î {{PLURAL:$1|netice|neticeyan}} ser her pel",
+ "shown-title": "Herg per sero $1 {{PLURAL:$1|netici|netica}} bıasne",
"viewprevnext": "($1 {{int:pipe-separator}} $2) ($3) bıvênên",
"searchmenu-exists": "''Ena 'Wikipediya de ser \"[[:$1]]\" yew pel esto'''",
"searchmenu-new": "<strong>Na wiki de pela \"[[:$1]]\" vıraze!</strong> {{PLURAL:$2|0=|Sewbina pela ke şıma geyrayê cı aye bıvênê.|Yew zi neticanê cıgeyrayışê xo bıvênê.}}",
"rightslogtext": "Ena listeyê loganê ke heqqa karbaranî mucneno.",
"action-read": "ena pela wanayış",
"action-edit": "ena pela bıvurnê",
- "action-createpage": "na pele vıraze",
+ "action-createpage": "na perer bıvıraz",
"action-createtalk": "pelanê werênayışi bıvıraze",
"action-createaccount": "hesabê nê karberi bıvıraze",
"action-autocreateaccount": "nê hesabê karberiyê teberi otomatik vıraze",
"enhancedrc-history": "tarix",
"recentchanges": "Vurriyayışê peyêni",
"recentchanges-legend": "Tercihê vurnayışanê peyênan",
- "recentchanges-summary": "Ena pele de wiki sero vurriyayışanê peyênan teqib ke.",
+ "recentchanges-summary": "Wiki sero vurriyayışê peyêni asenê.",
"recentchanges-noresult": "Goreyê kriteranê kıfşkerdeyan ra qet yew vurnayış nêvêniya.",
"recentchanges-feed-description": "Ena feed dı vurnayişanê tewr peniyan teqip bık.",
"recentchanges-label-newpage": "Enê vurnayışi ra yew pela newiye vıraziye",
- "recentchanges-label-minor": "No yew vurnayışo werdiyo",
+ "recentchanges-label-minor": "Vurriyayışo werdiyo",
"recentchanges-label-bot": "Eno vurnayış terefê yew boti ra vıraziyo",
"recentchanges-label-unpatrolled": "Eno vurnayış hewna dewriya nêbiyo",
"recentchanges-label-plusminus": "Ebadê pele de bazê bayti de vayeyê cı",
"rcshowhidecategorization": "kategorizasyonê pele $1",
"rcshowhidecategorization-show": "Bıasne",
"rcshowhidecategorization-hide": "Bınımne",
- "rclinks": "Peyniya $2 rocan de $1 vurriyayışan bımocne <br />$3",
+ "rclinks": "Peyniya $2 rocan de $1 vurriyayışê <br />$3 asenê",
"diff": "ferq",
"hist": "verên",
"hide": "Bınımne",
"removedwatchtext": "Ena pela \"[[:$1]]\" biya wedariya [[Special:Watchlist|listeyê seyr-kerdışi şıma]].",
"removedwatchtext-short": "Pera $1`i listeya seyran de şıma ra wedari yê",
"watch": "Seyr ke",
- "watchthispage": "Ena pele seyr ke",
+ "watchthispage": "Seyr kı",
"unwatch": "Teqib meke",
"unwatchthispage": "temaşa kerdışê peli vındarn.",
"notanarticle": "mebhesê peli niyo",
"pagesize": "(bitî)",
"restriction-edit": "Bıvurne",
"restriction-move": "Bıkırış",
- "restriction-create": "Vıraze",
+ "restriction-create": "Bıvıraz",
"restriction-upload": "Bar ke",
"restriction-level-sysop": "tam pawiyayo",
"restriction-level-autoconfirmed": "nêm pawiyayo",
"undeleterevision-missing": "revizyonê nemeqbul u vindbiyayeyi.\nRevizyoni ya hewn a biyê ya arşiw ra veciyayê ya zi cıresayişê şımayi şaş o.",
"undelete-nodiff": "revizyonê verıni nidiya",
"undeletebtn": "Timar bike",
- "undeletelink": "bıvêne/peyser biya",
+ "undeletelink": "bıewni/peyser biya",
"undeleteviewlink": "bıvin",
"undeleteinvert": "Weçinayışi dimlaşt ke",
"undeletecomment": "Sebeb:",
"unblocked-id": "Blokê $1î wedariyayo",
"blocklist": "Karberê kılitbiyayey",
"ipblocklist": "Karberê kılitbiyayey",
- "ipblocklist-legend": "Yew karberê kılitbiyayey bıvêne",
+ "ipblocklist-legend": "Karberê kılit biyayey bıvin",
"blocklist-userblocks": "Kılitkerdışê hesaban bınımne",
"blocklist-tempblocks": "Kılitkerdışan mıweqet bınımne",
"blocklist-addressblocks": "Tenya kılitkerdışanê IPy bınımne",
"articleexists": "Ena nameyê pela database ma dı esta ya zi tı raşt nınuşt. .\nYewna name bınus.",
"cantmove-titleprotected": "şıma nêşkeni yew peli bıhewelnê tiya çunke pawıyeno",
"movetalk": "Pela werênayışiê elaqedare bere",
- "move-subpages": "Pelanê bınênan bıkırışe (heta pela $1)",
- "move-talk-subpages": "Pelanê werênayışiyê bınênan bıkırışe (heta pela $1)",
+ "move-subpages": "Peranê bınênan bıkırış (hetana $1)",
+ "move-talk-subpages": "Bın peranê peranê vatena bıkırış (hetana $1)",
"movepage-page-exists": "maddeya $1i ca ra esta u newe ra otomatikmen nênusyena.",
"movepage-page-moved": "pelê $1i kırışiya pelê $2i.",
"movepage-page-unmoved": "pelê $1i nêkırışiyeno sernameyê $2i.",
"fileduplicatesearch-result-1": "Dosyayê ''$1î'' de hem-kopya çini yo.",
"fileduplicatesearch-result-n": "Dosyayê ''$1î'' de {{PLURAL:$2|1 hem-kopya|$2 hem-kopyayî'}} esto.",
"fileduplicatesearch-noresults": "Ebe namey \"$1\" ra dosya nêdiyayê.",
- "specialpages": "Pelê xısusiyi",
+ "specialpages": "Page bağsey",
"specialpages-note-top": "Kıtabek",
"specialpages-note": "* Pelê xasê normali.\n* <span class=\"mw-specialpagerestricted\">Pelê xasê nımıtey.</span>",
"specialpages-group-maintenance": "Raporê pawıtışi",
"feedback-bugcheck": "Harika! Sadece [xırabina ke $1 ] çınyayışê cı kontrol keno.",
"feedback-bugnew": "Mı qontrol ke. Xetaya newi xeber ke",
"feedback-bugornote": "Jew mersela teferruato teknik esta şıma reca malumatê şıma hazıro se [ $1 jew xırab rapor] bıvinê.Zewbi zi, formê cerê xo rê şenê karfiyê. Vatışê xo pela da \"[ $3 $2 ]\", namey karber dê xoya piya u wasteriya karfiye.",
- "feedback-cancel": "Bıtexelne",
+ "feedback-cancel": "İbtal kı",
"feedback-close": "Biya star",
"feedback-error1": "Xeta: API ra neticey ne vıcyay",
"feedback-error2": "Xeta: Timar kerdış nebı",
"undeletehistorynoadmin": "Esta páxina foi borrada.\nO motivo do borrado consta no resumo que aparece a continuación, xunto cos detalles dos usuarios que editaron esta páxina antes da súa eliminación.\nO texto destas revisións eliminadas só está á disposición dos administradores.",
"undelete-revision": "Revisión eliminada de \"$1\" (o $4 ás $5) feita por $3:",
"undeleterevision-missing": "Revisión non válida ou inexistente. Pode que a ligazón conteña un erro ou que a revisión se restaurase ou eliminase do arquivo.",
+ "undeleterevision-duplicate-revid": "{{PLURAL:$1|Unha revisión non pode ser restaurada|$1 revisións non poden ser restauradas}} porque {{PLURAL:$1|o seu|os seus}} <code>rev_id</code> xa {{PLURAL:$1|está|están}} en uso.",
"undelete-nodiff": "Non se atopou ningunha revisión anterior.",
"undeletebtn": "Restaurar",
"undeletelink": "ver/restaurar",
"undeletedrevisions": "{{PLURAL:$1|Restaurouse $1 revisión|Restauráronse $1 revisións}}",
"undeletedrevisions-files": "Restauráronse $1 {{PLURAL:$1|revisión|revisións}} e $2 {{PLURAL:$2|ficheiro|ficheiros}}",
"undeletedfiles": "{{PLURAL:$1|Restaurouse $1 ficheiro|Restauráronse $1 ficheiros}}",
- "cannotundelete": "Houbo un erro durante a restauración:\n$1",
+ "cannotundelete": "Algunhas ou todas as restauracións fallaronː\n$1",
"undeletedpage": "'''A páxina \"$1\" foi restaurada'''\n\nComprobe o [[Special:Log/delete|rexistro de borrados]] para ver as entradas recentes no rexistro de páxinas eliminadas e restauradas.",
"undelete-header": "Consulte [[Special:Log/delete|no rexistro de borrados]] as páxinas borradas recentemente.",
"undelete-search-title": "Procurar páxinas borradas",
"sp-contributions-newbies-sub": "Contribucións dos usuarios novos",
"sp-contributions-newbies-title": "Contribucións dos usuarios novos",
"sp-contributions-blocklog": "rexistro de bloqueos",
- "sp-contributions-suppresslog": "contribucións borradas do usuario",
- "sp-contributions-deleted": "contribucións borradas do usuario",
+ "sp-contributions-suppresslog": "contribucións {{GENDER:$1|do usuario|da usuaria}} suprimidas",
+ "sp-contributions-deleted": "contribucións {{GENDER:$1|do usuario|da usuaria}} borradas",
"sp-contributions-uploads": "cargas",
"sp-contributions-logs": "rexistros",
"sp-contributions-talk": "conversa",
"datedefault": "ברירת המחדל",
"prefs-labs": "אפשרויות מעבדה",
"prefs-user-pages": "דפי משתמש",
- "prefs-personal": "פרטי המשתמש",
+ "prefs-personal": "פרטי ה{{GENDER:|משתמש|משתמשת}}",
"prefs-rc": "שינויים אחרונים",
"prefs-watchlist": "רשימת המעקב",
"prefs-editwatchlist": "עריכת רשימת המעקב",
"undeletehistorynoadmin": "Questa pagina è stata cancellata.\nIl motivo della cancellazione è mostrato qui sotto, assieme ai dettagli dell'utente che ha modificato questa pagina prima della cancellazione.\nIl testo contenuto nelle versioni cancellate è disponibile solo agli amministratori.",
"undelete-revision": "Versione cancellata della pagina $1, inserita il $4 alle $5 da $3:",
"undeleterevision-missing": "Versione errata o mancante. Il collegamento è errato oppure la versione è stata già ripristinata o eliminata dall'archivio.",
+ "undeleterevision-duplicate-revid": "{{PLURAL:$1|Una versione non può essere ripristinata|$1 versioni non possono essere ripristinate}}, poiché {{PLURAL:$1|il suo|i loro}} <code>rev_id</code> {{PLURAL:$1|è già utilizzato|sono già utilizzati}}.",
"undelete-nodiff": "Non è stata trovata nessuna versione precedente.",
"undeletebtn": "Ripristina",
"undeletelink": "visualizza/ripristina",
"sp-contributions-username": "IP-мекенжайы немесе қатысушы аты:",
"sp-contributions-toponly": "Өңдемелердің тек соңғы нұсқаларын көрсету",
"sp-contributions-newonly": "Бет бастау өңдемелерін ғана көрсету",
+ "sp-contributions-hideminor": "Шағын өңдемелерді жасыру",
"sp-contributions-submit": "Іздеу",
"whatlinkshere": "Мұнда сілтейтін беттер",
"whatlinkshere-title": "$1 дегенге сілтейтін беттер",
"acct_creation_throttle_hit": "당신의 IP 주소를 이용한 방문자가 이전에 이미 {{PLURAL:$1|계정 $1개}}를 만들어, 계정 만들기 한도를 초과하였습니다.\n따라서 지금은 이 IP 주소로는 더 이상 계정을 만들 수 없습니다.",
"emailauthenticated": "이메일 주소가 $2 $3에 인증되었습니다.",
"emailnotauthenticated": "이메일 주소를 인증하지 않았습니다.\n이메일 확인 절차를 거치지 않으면 다음 이메일 기능을 사용할 수 없습니다.",
- "noemailprefs": "이 기능을 사용하기 위해서는 사용자 환경 설정에서 이메일 주소를 설정해야 합니다.",
+ "noemailprefs": "이 기능을 사용하려면 사용자 환경 설정에서 이메일 주소를 지정하세요.",
"emailconfirmlink": "이메일 주소 확인",
"invalidemailaddress": "이메일 주소의 형식이 잘못되어 인식할 수 없습니다.\n정상적인 형식의 이메일을 입력하거나 칸을 비워 주세요.",
"cannotchangeemail": "이 위키에서는 계정의 이메일 주소를 바꿀 수 없습니다.",
"rev-showdeleted": "보이기",
"revisiondelete": "판 삭제/되살리기",
"revdelete-nooldid-title": "대상 판이 잘못되었습니다.",
- "revdelete-nooldid-text": "이 기능을 수행할 특정 판을 제시하지 않았거나 해당 판이 없습니다. 또는 현재 판을 숨기려 하고 있을 수도 있습니다.",
+ "revdelete-nooldid-text": "이 기능을 수행할 대상 판을 지정하지 않았거나 해당 판이 존재하지 않습니다. 아니면 현재 판을 숨기려 하고 있을 수도 있습니다.",
"revdelete-no-file": "해당 파일이 존재하지 않습니다.",
"revdelete-show-file-confirm": "정말 \"<nowiki>$1</nowiki>\" 파일의 삭제된 $2 $3 버전을 보시겠습니까?",
"revdelete-show-file-submit": "예",
"tags-edit-success": "바뀜이 적용되었습니다.",
"tags-edit-failure": "수정 사항이 적용될 수 없습니다: $1",
"tags-edit-nooldid-title": "대상 판이 잘못되었습니다",
- "tags-edit-nooldid-text": "이 기능을 수행할 특정 판을 제시하지 않았거나 해당 판이 없습니다.",
+ "tags-edit-nooldid-text": "이 기능을 수행할 대상 판을 지정하지 않았거나 해당 판이 존재하지 않습니다.",
"tags-edit-none-selected": "추가하거나 제거할 최소 하나 이상의 태그를 선택하세요.",
"comparepages": "문서 비교",
"compare-page1": "첫 번째 문서",
"passwordreset-emailelement": "सदस्यनाव: \n$1\n\nअस्थायी परवलीचा शब्द: \n$2",
"passwordreset-emailsentemail": "जर हा विपत्रपत्ता आपल्या खात्याशी संलग्न असेल तर, परवलीच्या शब्दाच्या पुनर्स्थापनेबाबत एक विपत्र पाठवण्यात येईल.",
"passwordreset-emailsentusername": "जर या सदस्यनावाशी संलग्न विपत्रपत्ता असेल तर, परवलीचा शब्द पुनर्स्थापनाबाबत विपत्र पाठविल्या जाईल.",
- "passwordreset-emailsent-capture": "'परवलीचा शब्द' पुनर्स्थापनेबाबत एक विपत्र पाठवण्यात आले आहे जे खाली दर्शविण्यात आले आहे.",
- "passwordreset-emailerror-capture": "'परवलीचा शब्द' पुनर्स्थापनेबाबत एक विपत्र निर्माण करण्यात आले, जे खाली दर्शविण्यात आले आहे.परंतु,{{GENDER:$2|सदस्य}}ला पाठविणे असफल झाले: $1",
"changeemail": "विपत्रपत्ता बदला किंवा हटवा",
"changeemail-header": "आपला विपत्रपत्ता बदलण्यास हे आवेदन पूर्ण करा.जर आपणास आपल्या खात्याशी संलग्न कोणताही विपत्रपत्ता हटवायचा असेल तर,आवेदन सादर करण्यापूर्वी, नविन विपत्रपत्त्यासाठी असलेली जागा कोरी ठेवा.",
- "changeemail-passwordrequired": "हे बदल नक्की करण्यासाठी आपणास आपला परवलीचा शब्द टाकावा लागेल.",
"changeemail-no-info": "हे पान थेट बघण्यासठी तुम्हाला सनोंद-प्रवेशित असावे लागेल.",
"changeemail-oldemail": "सध्याचा ईमेल पत्ता :",
"changeemail-newemail": "नवा ईमेल पत्ता:",
"undo-nochange": "असे दिसते कि हे संपादन पूर्ववत केल्या गेले आहे.",
"undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|चर्चा]])यांची आवृत्ती $1 परतवली.",
"undo-summary-username-hidden": "अज्ञात सदस्याची $1 आवृत्ती परतवा",
- "cantcreateaccounttitle": "खाते उघडू शकत नाही",
"cantcreateaccount-text": "('''$1''')या आंतरजाल अंकपत्त्याकडूनच्या खाते निर्मितीस [[User:$3|$3]]ने अटकाव केला आहे.\n\n$3ने ''$2'' कारण दिले आहे.",
"cantcreateaccount-range-text": "<strong>$1</strong>आवाक्यातील आंतरजाल अंकपत्ते,ज्यात आपल्या (<strong>$4</strong>) या अंकपत्त्याचा समावेश आहे, [[User:$3|$3]] ने त्यांच्या खाते निर्मितीस प्रतिबंध केला आहे.\n\n$3 ने <em>$2</em>कारण दिले आहे.",
"viewpagelogs": "या पानाच्या नोंदी पहा",
"undeletedrevisions": "{{PLURAL:$1|1 आवर्तन|$1 आवर्तने}} पुनर्स्थापित",
"undeletedrevisions-files": "{{PLURAL:$1|1 आवर्तन|$1 आवर्तने}}आणि {{PLURAL:$2|1 संचिका|$2 संचिका}} पुनर्स्थापित",
"undeletedfiles": "{{PLURAL:$1|1 संचिका|$1 संचिका}} पुनर्स्थापित",
- "cannotundelete": "उलटवणे फसले:$1",
+ "cannotundelete": "à¤\95ाहà¥\80 à¤\95िà¤\82वा सरà¥\8dवà¤\9a à¤\89लà¤\9fवणà¥\87 फसलà¥\87:$1",
"undeletedpage": "<strong>$1ला पुनर्स्थापित केले</strong>\n\nअलिकडिल वगळलेल्या आणि पुनर्स्थापितांच्या नोंदीकरिता [[Special:Log/delete|वगळल्याच्या नोंदी]] पहा .",
"undelete-header": "अलीकडील वगळलेल्या पानांकरिता [[Special:Log/delete|वगळलेल्या नोंदी]] पहा.",
"undelete-search-title": "वगळलेली पाने शोधा",
"sp-contributions-newbies-sub": "नवशिक्यांसाठी",
"sp-contributions-newbies-title": "नवीन खात्यांसाठी सदस्य योगदान",
"sp-contributions-blocklog": "रोध नोंदी",
- "sp-contributions-suppresslog": "सदस्य योगदानाचे दमन केले",
- "sp-contributions-deleted": "वगळलेली सदस्य संपादने",
+ "sp-contributions-suppresslog": "{{GENDER:$1|सदस्य}} योगदानाचे दमन केले",
+ "sp-contributions-deleted": "वगळलेली {{GENDER:$1|सदस्य}} संपादने",
"sp-contributions-uploads": "अपभारणे",
"sp-contributions-logs": "नोंदी",
"sp-contributions-talk": "चर्चा",
"grant-group-high-volume": "Realizar actividades em grande quantidade",
"grant-group-customization": "Personalização e preferências",
"grant-group-administration": "Executar acções administrativas",
+ "grant-group-private-information": "Aceder aos seus dados privados",
"grant-group-other": "Actividade diversa",
"grant-blockusers": "Bloquear e desbloquear utilizadores",
"grant-createaccount": "Criar contas",
"grant-group-high-volume": "Izvajanje visokoobsežnih dejavnosti",
"grant-group-customization": "Prilagoditve in nastavitve",
"grant-group-administration": "Izvajanje administrativnih dejanj",
+ "grant-group-private-information": "Dostop do zasebnih podatkov o vas",
"grant-group-other": "Druga dejavnost",
"grant-blockusers": "Blokiranje in odblokiranje uporabnikov",
"grant-createaccount": "Ustvarjanje računov",
"grant-highvolume": "Visokoobsežno urejanje",
"grant-oversight": "Skrivanje uporabnikov in zatiranje redakcij",
"grant-patrol": "Nadzor sprememb strani",
+ "grant-privateinfo": "Dostop do zasebnih podatkov",
"grant-protect": "Zaščita in odstranitev zaščite strani",
"grant-rollback": "Razveljavitev sprememb strani",
"grant-sendemail": "Pošiljanje e-pošte drugim uporabnikom",
"watchthis": "Спостерігати за цією сторінкою",
"savearticle": "Зберегти сторінку",
"savechanges": "Зберегти зміни",
- "publishpage": "Ð\9eпÑ\83блÑ\96кÑ\83вати сторінку",
- "publishchanges": "Ð\9eпÑ\83блÑ\96кÑ\83вати зміни",
+ "publishpage": "Ð\97беÑ\80егти сторінку",
+ "publishchanges": "Ð\97беÑ\80егти зміни",
"preview": "Попередній перегляд",
"showpreview": "Попередній перегляд",
"showdiff": "Показати зміни",
"shortpages": "چھوٹے صفحات",
"longpages": "طویل ترین صفحات",
"deadendpages": "مردہ صفحات",
- "protectedpages": "محفوظ شدہ صفحات",
+ "protectedpages": "محفوظ کردہ صفحات",
"protectedpages-noredirect": "رجوع مکررات چھپائیں",
"protectedpages-timestamp": "وقت کی مہر",
"protectedpages-page": "صفحہ",
"minoredit": "Bu kichik tahrir",
"watchthis": "Sahifani kuzatish",
"savearticle": "Saqla",
+ "publishpage": "Sahifani chop et",
+ "publishchanges": "Oʻzgarishlarni chop et",
"preview": "Ko‘rib chiqish",
"showpreview": "Koʻrib chiqish",
"showdiff": "Kiritilgan o‘zgarishlar",
"undo-success": "Tahrirni bekor qilish imkoniyati bor. Iltimos, solishtirish oynasini koʻrib chiqib, aynan shu oʻzgarishlarni bekor qilmoqchiligingizga ishonch hosil qiling va undan keyin «Saqla» tugmasini bosing.",
"undo-failure": "Keyingi tahrirlar bilan chalkashib ketgani sababli, ushbu tahrirni alohida oʻzini bekor qilishni iloji yoʻq.",
"undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|mun.]]) tomonidan qilingan $1-sonli tahrir qaytarildi",
- "cantcreateaccounttitle": "Ro‘yxatdan o‘tib bo‘lmadi",
"cantcreateaccount-text": "[[User:$3|$3]] ushbu IP manzil (<strong>$1</strong>) orqali ro‘yxatdan o‘tishni bloklab qo‘ygan.\n\n$3 <em>$2</em>ni sabab qilib ko‘rsatdi",
"cantcreateaccount-range-text": "[[User:$3|$3]] <strong>$1</strong> sohaga tegishli IP manzillar, shu jumladan sizning IP manzilingiz (<strong>$4</strong>), orqali ro‘yxatdan o‘tishni bloklab qo‘ygan.\n\n$3 <em>$2</em>ni sabab qilib ko‘rsatdi",
"viewpagelogs": "Ushbu sahifaga doir qaydlarni koʻrsat",
"revdelete-uname-unhid": "公开用户名",
"revdelete-restricted": "应用对管理员的限制",
"revdelete-unrestricted": "删除对管理员的限制",
- "logentry-block-block": "$1{{GENDER:$2|封禁了}}{{GENDER:$4|$3}},期限为$5 $6",
+ "logentry-block-block": "$1{{GENDER:$2|封禁了}}{{GENDER:$4|$3}},期限至$5 $6",
"logentry-block-unblock": "$1{{GENDER:$2|解封了}}{{GENDER:$4|$3}}",
"logentry-block-reblock": "$1将{{GENDER:$4|$3}}的封禁设置{{GENDER:$2|更改为}}持续时间$5 $6",
"logentry-suppress-block": "$1{{GENDER:$2|封禁了}}{{GENDER:$4|$3}},持续时间$5 $6",
"log-action-filter-upload-upload": "新上传",
"log-action-filter-upload-overwrite": "重新上传",
"authmanager-authn-not-in-progress": "身份验证尚未进行,或会话数据丢失。请从头重新开始。",
- "authmanager-authn-no-primary": "提供的证书不能被验证。",
+ "authmanager-authn-no-primary": "提供的凭据不能通过验证。",
"authmanager-authn-no-local-user": "提供的证书没有与该wiki上的任何用户相关联。",
"authmanager-authn-no-local-user-link": "提供的证书有效,但没有与该wiki上的任何用户相关联。请通过不同方式登录,或创建一个新用户,然后您将拥有一个把您之前的证书链接到对应账户的选项。",
"authmanager-authn-autocreate-failed": "所有账户的自动创建失败:$1",
require_once __DIR__ . '/cleanupTable.inc';
/**
- * Maintenance script to clean up broken page links when somebody turns on $wgCapitalLinks.
+ * Maintenance script to clean up broken page links when somebody turns
+ * on or off $wgCapitalLinks.
*
* @ingroup Maintenance
*/
class CapsCleanup extends TableCleanup {
private $user;
+ private $namespace;
public function __construct() {
parent::__construct();
}
public function execute() {
- global $wgCapitalLinks;
-
- if ( $wgCapitalLinks ) {
- $this->error( "\$wgCapitalLinks is on -- no need for caps links cleanup.", true );
- }
-
$this->user = User::newSystemUser( 'Conversion script', [ 'steal' => true ] );
$this->namespace = intval( $this->getOption( 'namespace', 0 ) );
+
+ if ( MWNamespace::isCapitalized( $this->namespace ) ) {
+ $this->output( "Will be moving pages to first letter capitalized titles" );
+ $callback = 'processRowToUppercase';
+ } else {
+ $this->output( "Will be moving pages to first letter lowercase titles" );
+ $callback = 'processRowToLowercase';
+ }
+
$this->dryrun = $this->hasOption( 'dry-run' );
$this->runTable( [
'table' => 'page',
'conds' => [ 'page_namespace' => $this->namespace ],
'index' => 'page_id',
- 'callback' => 'processRow' ] );
+ 'callback' => $callback ] );
}
- protected function processRow( $row ) {
+ protected function processRowToUppercase( $row ) {
+ global $wgContLang;
+
+ $current = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $display = $current->getPrefixedText();
+ $lower = $row->page_title;
+ $upper = $wgContLang->ucfirst( $row->page_title );
+ if ( $upper == $lower ) {
+ $this->output( "\"$display\" already uppercase.\n" );
+
+ return $this->progress( 0 );
+ }
+
+ $target = Title::makeTitle( $row->page_namespace, $upper );
+ if ( $target->exists() ) {
+ // Prefix "CapsCleanup" to bypass the conflict
+ $target = Title::newFromText( __CLASS__ . '/' . $display );
+ }
+ $ok = $this->movePage(
+ $current,
+ $target,
+ 'Converting page title to first-letter uppercase',
+ false
+ );
+ if ( $ok ) {
+ $this->progress( 1 );
+ if ( $row->page_namespace == $this->namespace ) {
+ $talk = $target->getTalkPage();
+ $row->page_namespace = $talk->getNamespace();
+ if ( $talk->exists() ) {
+ return $this->processRowToUppercase( $row );
+ }
+ }
+ }
+
+ return $this->progress( 0 );
+ }
+
+ protected function processRowToLowercase( $row ) {
global $wgContLang;
$current = Title::makeTitle( $row->page_namespace, $row->page_title );
}
$target = Title::makeTitle( $row->page_namespace, $lower );
- $targetDisplay = $target->getPrefixedText();
if ( $target->exists() ) {
+ $targetDisplay = $target->getPrefixedText();
$this->output( "\"$display\" skipped; \"$targetDisplay\" already exists\n" );
return $this->progress( 0 );
}
- if ( $this->dryrun ) {
- $this->output( "\"$display\" -> \"$targetDisplay\": DRY RUN, NOT MOVED\n" );
- $ok = true;
- } else {
- $mp = new MovePage( $current, $target );
- $status = $mp->move( $this->user, 'Converting page titles to lowercase', true );
- $ok = $status->isOK() ? 'OK' : $status->getWikiText( false, false, 'en' );
- $this->output( "\"$display\" -> \"$targetDisplay\": $ok\n" );
- }
+ $ok = $this->movePage( $current, $target, 'Converting page titles to lowercase', true );
if ( $ok === true ) {
$this->progress( 1 );
if ( $row->page_namespace == $this->namespace ) {
$talk = $target->getTalkPage();
$row->page_namespace = $talk->getNamespace();
if ( $talk->exists() ) {
- return $this->processRow( $row );
+ return $this->processRowToLowercase( $row );
}
}
}
return $this->progress( 0 );
}
+
+ /**
+ * @param Title $current
+ * @param Title $target
+ * @param string $reason
+ * @param bool $createRedirect
+ * @return bool Success
+ */
+ private function movePage( Title $current, Title $target, $reason, $createRedirect ) {
+ $display = $current->getPrefixedText();
+ $targetDisplay = $target->getPrefixedText();
+
+ if ( $this->dryrun ) {
+ $this->output( "\"$display\" -> \"$targetDisplay\": DRY RUN, NOT MOVED\n" );
+ $ok = 'OK';
+ } else {
+ $mp = new MovePage( $current, $target );
+ $status = $mp->move( $this->user, $reason, $createRedirect );
+ $ok = $status->isOK() ? 'OK' : $status->getWikiText( false, false, 'en' );
+ $this->output( "\"$display\" -> \"$targetDisplay\": $ok\n" );
+ }
+
+ return $ok === 'OK';
+ }
}
$maintClass = "CapsCleanup";
$actual = $this->manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN );
$expected = [
$rememberReq,
- $makeReq( "primary-shared", AuthenticationRequest::REQUIRED ),
+ $makeReq( "primary-shared", AuthenticationRequest::PRIMARY_REQUIRED ),
$makeReq( "required", AuthenticationRequest::PRIMARY_REQUIRED ),
$makeReq( "required2", AuthenticationRequest::PRIMARY_REQUIRED ),
$makeReq( "optional", AuthenticationRequest::OPTIONAL ),
$actual = $this->manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN );
$expected = [
$rememberReq,
- $makeReq( "primary-shared", AuthenticationRequest::REQUIRED ),
- $makeReq( "required", AuthenticationRequest::REQUIRED ),
+ $makeReq( "primary-shared", AuthenticationRequest::PRIMARY_REQUIRED ),
+ $makeReq( "required", AuthenticationRequest::PRIMARY_REQUIRED ),
$makeReq( "optional", AuthenticationRequest::OPTIONAL ),
- $makeReq( "foo", AuthenticationRequest::REQUIRED ),
+ $makeReq( "foo", AuthenticationRequest::PRIMARY_REQUIRED ),
$makeReq( "bar", AuthenticationRequest::REQUIRED ),
$makeReq( "baz", AuthenticationRequest::REQUIRED ),
];
}
}
+ /**
+ * Verify that all specified messages actually exist.
+ */
+ public function testMissingMessages() {
+ $data = self::getAllModules();
+ $validDeps = array_keys( $data['modules'] );
+ $lang = Language::factory( 'en' );
+
+ /** @var ResourceLoaderModule $module */
+ foreach ( $data['modules'] as $moduleName => $module ) {
+ foreach ( $module->getMessages() as $msgKey ) {
+ $this->assertTrue(
+ wfMessage( $msgKey )->useDatabase( false )->inLanguage( $lang )->exists(),
+ "Message '$msgKey' required by '$moduleName' must exist"
+ );
+ }
+ }
+ }
+
/**
* Verify that all dependencies of all modules are always satisfiable with the 'targets' defined
* for the involved modules.