* $wgRateLimitLog was removed; use $wgDebugLogGroups['ratelimit'] instead.
* Deprecated API formats dbg, txt, and yaml have been removed.
* CLDRPluralRule* classes have been replaced with wikimedia/cldr-plural-rule-parser.
+* Removed $wgProfilePerHost, $wgUDPProfilerHost, $wgUDPProfilerPort,
+ $wgUDPProfilerFormatString, $wgStatsMethod, $wgAggregateStatsID, $wgStatsFormatString,
+ and $wgProfileCallTree (deprecated since 1.20).
=== New features in 1.27 ===
* $wgDataCenterId and $wgDataCenterRoles where added, which will serve as
* Added CentralIdLookup, a service that allows extensions needing a concept of
"central" users to get that without having to know about specific central
authentication extensions.
+* $wgMaxUserDBWriteDuration added to limit huge user-generated transactions.
+ Regular web request transactions that takes longer than this are aborted.
=== External library changes in 1.27 ===
==== Upgraded external libraries ====
* ResourceLoader::getLessCompiler() now takes an optional parameter of
additional LESS variables to set for the compiler.
* wfBaseConvert() marked as deprecated, use Wikimedia\base_convert() directly instead.
+* Obsolete maintenance scripts clearCacheStats.php and showCacheStats.php
+ were removed. The underlying data is sent to StatsD (see $wgStatsdServer).
== Compatibility ==
'CleanupPreferences' => __DIR__ . '/maintenance/cleanupPreferences.php',
'CleanupRemovedModules' => __DIR__ . '/maintenance/cleanupRemovedModules.php',
'CleanupSpam' => __DIR__ . '/maintenance/cleanupSpam.php',
- 'ClearCacheStats' => __DIR__ . '/maintenance/clearCacheStats.php',
'ClearInterwikiCache' => __DIR__ . '/maintenance/clearInterwikiCache.php',
'CliInstaller' => __DIR__ . '/includes/installer/CliInstaller.php',
'CloneDatabase' => __DIR__ . '/includes/db/CloneDatabase.php',
'DBQueryError' => __DIR__ . '/includes/db/DatabaseError.php',
'DBReadOnlyError' => __DIR__ . '/includes/db/DatabaseError.php',
'DBSiteStore' => __DIR__ . '/includes/site/DBSiteStore.php',
+ 'DBTransactionError' => __DIR__ . '/includes/db/DatabaseError.php',
'DBUnexpectedError' => __DIR__ . '/includes/db/DatabaseError.php',
'DataUpdate' => __DIR__ . '/includes/deferred/DataUpdate.php',
'Database' => __DIR__ . '/includes/db/Database.php',
'SevenZipStream' => __DIR__ . '/maintenance/7zip.inc',
'ShiConverter' => __DIR__ . '/languages/classes/LanguageShi.php',
'ShortPagesPage' => __DIR__ . '/includes/specials/SpecialShortpages.php',
- 'ShowCacheStats' => __DIR__ . '/maintenance/showCacheStats.php',
'ShowJobs' => __DIR__ . '/maintenance/showJobs.php',
'ShowSiteStats' => __DIR__ . '/maintenance/showSiteStats.php',
'Site' => __DIR__ . '/includes/site/Site.php',
Special:Recentchanges?action=purge&feed=atom,
but note need $wgGroupPermissions[...]['purge'] permission.
-Statistics:
- controlled by: $wgStatsMethod
- key: $wgDBname:stats:$key
- ex: wikibd:stats:request_with_session
- stores: counter for statistics (see maintenance/showCacheStats.php script)
- expiry: none (?)
- cleared by: maintenance/clearCacheStats.php script
-
User:
key: $wgDBname:user:id:$sId
ex: wikidb:user:id:51
*/
$wgProfileOnly = false;
-/**
- * If true, print a raw call tree instead of per-function report
- */
-$wgProfileCallTree = false;
-
-/**
- * Should application server host be put into profiling table
- *
- * @deprecated set $wgProfiler['perhost'] = true instead
- */
-$wgProfilePerHost = null;
-
-/**
- * Host for UDP profiler.
- *
- * The host should be running a daemon which can be obtained from MediaWiki
- * Git at:
- * https://git.wikimedia.org/tree/operations%2Fsoftware.git/master/udpprofile
- *
- * @deprecated set $wgProfiler['udphost'] instead
- */
-$wgUDPProfilerHost = null;
-
-/**
- * Port for UDP profiler.
- * @see $wgUDPProfilerHost
- *
- * @deprecated set $wgProfiler['udpport'] instead
- */
-$wgUDPProfilerPort = null;
-
-/**
- * Format string for the UDP profiler. The UDP profiler invokes sprintf() with
- * (profile id, count, cpu, cpu_sq, real, real_sq, entry name, memory) as
- * arguments. You can use sprintf's argument numbering/swapping capability to
- * repeat, re-order or omit fields.
- *
- * @see $wgStatsFormatString
- * @since 1.22
- *
- * @deprecated set $wgProfiler['udpformat'] instead
- */
-$wgUDPProfilerFormatString = null;
-
-/**
- * Destination for wfIncrStats() data...
- * 'cache' to go into the system cache, if enabled (memcached)
- * 'udp' to be sent to the UDP profiler (see $wgUDPProfilerHost)
- * false to disable
- */
-$wgStatsMethod = 'cache';
-
-/**
- * When $wgStatsMethod is 'udp', setting this to a string allows statistics to
- * be aggregated over more than one wiki. The string will be used in place of
- * the DB name in outgoing UDP packets. If this is set to false, the DB name
- * will be used.
- */
-$wgAggregateStatsID = false;
-
-/**
- * When $wgStatsMethod is 'udp', this variable specifies how stats should be
- * formatted. Its value should be a format string suitable for a sprintf()
- * invocation with (id, count, key) arguments, where 'id' is either
- * $wgAggregateStatsID or the DB name, 'count' is the value by which the metric
- * is being incremented, and 'key' is the metric name.
- *
- * @see $wgUDPProfilerFormatString
- * @see $wgAggregateStatsID
- * @since 1.22
- */
-$wgStatsFormatString = "stats/%s - %s 1 1 1 1 %s\n";
-
/**
* Destination of statsd metrics.
*
*/
$wgPopularPasswordFile = __DIR__ . '/../serialized/commonpasswords.cdb';
+/*
+ * Max time (in seconds) a user-generated transaction can spend in writes.
+ * If exceeded, the transaction is rolled back with an error instead of being committed.
+ *
+ * @var int|bool Disabled if false
+ * @since 1.27
+ */
+$wgMaxUserDBWriteDuration = false;
+
/**
* For really cool vim folding this needs to be at the end:
* vim: foldmarker=@{,@} foldmethod=marker
}
/**
- * This function takes two arrays as input, and returns a CGI-style string, e.g.
+ * This function takes one or two arrays as input, and returns a CGI-style string, e.g.
* "days=7&limit=100". Options in the first array override options in the second.
* Options set to null or false will not be output.
*
* @param array $array1 ( String|Array )
- * @param array $array2 ( String|Array )
+ * @param array|null $array2 ( String|Array )
* @param string $prefix
* @return string
*/
// Either all DBs should commit or none
ignore_user_abort( true );
- // Commit all changes and record ChronologyProtector positions
+ $config = $context->getConfig();
+
$factory = wfGetLBFactory();
+ // Check if any transaction was too big
+ $limit = $config->get( 'MaxUserDBWriteDuration' );
+ $factory->forEachLB( function ( LoadBalancer $lb ) use ( $limit ) {
+ $lb->forEachOpenConnection( function ( IDatabase $db ) use ( $limit ) {
+ $time = $db->pendingWriteQueryDuration();
+ if ( $limit > 0 && $time > $limit ) {
+ throw new DBTransactionError(
+ $db,
+ wfMessage( 'transaction-duration-limit-exceeded', $time, $limit )->plain()
+ );
+ }
+ } );
+ } );
+ // Commit all changes
$factory->commitMasterChanges();
+ // Record ChronologyProtector positions
$factory->shutdown();
wfDebug( __METHOD__ . ': all transactions committed' );
// Set a cookie to tell all CDN edge nodes to "stick" the user to the
// DC that handles this POST request (e.g. the "master" data center)
$request = $context->getRequest();
- $config = $context->getConfig();
if ( $request->wasPosted() && $factory->hasOrMadeRecentMasterChanges() ) {
$expires = time() + $config->get( 'DataCenterUpdateStickTTL' );
$request->response()->setCookie( 'UseDC', 'master', $expires, array( 'prefix' => '' ) );
list( $phpCallback ) = $callback;
$this->clearFlag( DBO_TRX ); // make each query its own transaction
call_user_func( $phpCallback );
- $this->setFlag( $autoTrx ? DBO_TRX : 0 ); // restore automatic begin()
+ if ( $autoTrx ) {
+ $this->setFlag( DBO_TRX ); // restore automatic begin()
+ } else {
+ $this->clearFlag( DBO_TRX ); // restore auto-commit
+ }
} catch ( Exception $e ) {
if ( $ePrior ) {
MWExceptionHandler::logException( $ePrior );
return $this->msg( 'readonly', 'Database is locked' );
}
}
+
+/**
+ * @ingroup Database
+ */
+class DBTransactionError extends DBExpectedError {
+}
"config-nofile": "Файл \"$1\" не удается найти. Он был удален?",
"config-extension-link": "Знаете ли вы, что ваш вики-проект поддерживает [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions расширения]?\n\nВы можете просмотреть [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category расширения по категориям] или [//www.mediawiki.org/wiki/Extension_Matrix матрицу расширений], чтобы увидеть их полный список.",
"mainpagetext": "'''Вики-движок «MediaWiki» успешно установлен.'''",
- "mainpagedocfooter": "Информацию по работе с этой вики можно найти в [//meta.wikimedia.org/wiki/Help:Contents/ru справочном руководстве].\n\n== Некоторые полезные ресурсы ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Список возможных настроек];\n* [//www.mediawiki.org/wiki/Manual:FAQ/ru Часто задаваемые вопросы и ответы по MediaWiki];\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Рассылка уведомлений о выходе новых версий MediaWiki].\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Перевод MediaWiki на свой язык]"
+ "mainpagedocfooter": "Информацию по работе с этой вики можно найти в [//meta.wikimedia.org/wiki/Help:Contents/ru справочном руководстве].\n\n== Некоторые полезные ресурсы ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Список возможных настроек];\n* [//www.mediawiki.org/wiki/Manual:FAQ/ru Часто задаваемые вопросы и ответы по MediaWiki];\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Рассылка уведомлений о выходе новых версий MediaWiki].\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Перевод MediaWiki на свой язык]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Узнайте, как бороться со спамом в вашей вики]"
}
*
* - ObjectCache::getMainWANInstance()
* Purpose: Memory cache.
- * Stored in the local data-center's main cache (uses different cache keys).
- * Delete events are broadcasted to other DCs. See WANObjectCache for details.
+ * Stored in the local data-center's main cache (keyspace different from local-cluster cache).
+ * Delete events are broadcasted to other DCs main cache. See WANObjectCache for details.
*
* - ObjectCache::getLocalServerInstance( $fallbackType )
* Purpose: Memory cache for very hot keys.
- * Stored only on the individual web server (often EmptyBagOStuff in CLI mode).
+ * Stored only on the individual web server (typically APC for web requests,
+ * and EmptyBagOStuff in CLI mode).
* Not replicated to the other servers.
*
* - ObjectCache::getLocalClusterInstance()
* Purpose: Ephemeral global storage.
* Stored centrally within the primary data-center.
* Changes are applied there first and replicated to other DCs (best-effort).
- * To retrieve the latest value (e.g. not from a slave), use BagOStuff:READ_LATEST.
+ * To retrieve the latest value (e.g. not from a slave), use BagOStuff::READ_LATEST.
* This store may be subject to LRU style evictions.
*
* - ObjectCache::getInstance( $cacheType )
* @since 1.21
* @throws MWException
*/
- public function doEditContent( Content $content, $summary, $flags = 0, $baseRevId = false,
+ public function doEditContent(
+ Content $content, $summary, $flags = 0, $baseRevId = false,
User $user = null, $serialFormat = null
) {
- global $wgUser, $wgUseAutomaticEditSummaries, $wgUseRCPatrol, $wgUseNPPatrol;
+ global $wgUser, $wgUseAutomaticEditSummaries;
// Low-level sanity check
if ( $this->mTitle->getText() === '' ) {
throw new MWException( 'Something is trying to edit an article with an empty title' );
}
-
- if ( !$content->getContentHandler()->canBeUsedOn( $this->getTitle() ) ) {
+ // Make sure the given content type is allowed for this page
+ if ( !$content->getContentHandler()->canBeUsedOn( $this->mTitle ) ) {
return Status::newFatal( 'content-not-allowed-here',
ContentHandler::getLocalizedName( $content->getModel() ),
- $this->getTitle()->getPrefixedText() );
+ $this->mTitle->getPrefixedText()
+ );
}
- $user = is_null( $user ) ? $wgUser : $user;
- $status = Status::newGood( array() );
-
// Load the data from the master database if needed.
// The caller may already loaded it from the master or even loaded it using
// SELECT FOR UPDATE, so do not override that using clear().
$this->loadPageData( 'fromdbmaster' );
+ $user = $user ?: $wgUser;
$flags = $this->checkFlags( $flags );
- // handle hook
+ // Trigger pre-save hook (using provided edit summary)
+ $hookStatus = Status::newGood( array() );
$hook_args = array( &$this, &$user, &$content, &$summary,
- $flags & EDIT_MINOR, null, null, &$flags, &$status );
-
+ $flags & EDIT_MINOR, null, null, &$flags, &$hookStatus );
+ // Check if the hook rejected the attempted save
if ( !Hooks::run( 'PageContentSave', $hook_args )
- || !ContentHandler::runLegacyHooks( 'ArticleSave', $hook_args ) ) {
-
- wfDebug( __METHOD__ . ": ArticleSave or ArticleSaveContent hook aborted save!\n" );
-
- if ( $status->isOK() ) {
- $status->fatal( 'edit-hook-aborted' );
+ || !ContentHandler::runLegacyHooks( 'ArticleSave', $hook_args )
+ ) {
+ if ( $hookStatus->isOK() ) {
+ // Hook returned false but didn't call fatal(); use generic message
+ $hookStatus->fatal( 'edit-hook-aborted' );
}
- return $status;
+ return $hookStatus;
}
- // Silently ignore EDIT_MINOR if not allowed
- $isminor = ( $flags & EDIT_MINOR ) && $user->isAllowed( 'minoredit' );
- $bot = $flags & EDIT_FORCE_BOT;
-
$old_revision = $this->getRevision(); // current revision
$old_content = $this->getContent( Revision::RAW ); // current revision's content
- $oldsize = $old_content ? $old_content->getSize() : 0;
- $oldid = $this->getLatest();
- $oldIsRedirect = $this->isRedirect();
- $oldcountable = $this->isCountable();
-
- $handler = $content->getContentHandler();
-
- // Provide autosummaries if one is not provided and autosummaries are enabled.
- if ( $wgUseAutomaticEditSummaries && $flags & EDIT_AUTOSUMMARY && $summary == '' ) {
- if ( !$old_content ) {
- $old_content = null;
- }
+ // Provide autosummaries if one is not provided and autosummaries are enabled
+ if ( $wgUseAutomaticEditSummaries && ( $flags & EDIT_AUTOSUMMARY ) && $summary == '' ) {
+ $handler = $content->getContentHandler();
$summary = $handler->getAutosummary( $old_content, $content, $flags );
}
+ // Get the pre-save transform content and final parser output
$editInfo = $this->prepareContentForEdit( $content, null, $user, $serialFormat );
- $serialized = $editInfo->pst;
-
- /**
- * @var Content $content
- */
- $content = $editInfo->pstContent;
- $newsize = $content->getSize();
-
- $dbw = wfGetDB( DB_MASTER );
- $now = wfTimestampNow();
+ $pstContent = $editInfo->pstContent; // Content object
+ $meta = array(
+ 'bot' => ( $flags & EDIT_FORCE_BOT ),
+ 'minor' => ( $flags & EDIT_MINOR ) && $user->isAllowed( 'minoredit' ),
+ 'serialized' => $editInfo->pst,
+ 'serialFormat' => $serialFormat,
+ 'baseRevId' => $baseRevId,
+ 'oldRevision' => $old_revision,
+ 'oldContent' => $old_content,
+ 'oldId' => $this->getLatest(),
+ 'oldIsRedirect' => $this->isRedirect(),
+ 'oldCountable' => $this->isCountable()
+ );
+ // Actually create the revision and create/update the page
if ( $flags & EDIT_UPDATE ) {
- // Update article, but only if changed.
- $status->value['new'] = false;
-
- if ( !$oldid ) {
- // Article gone missing
- wfDebug( __METHOD__ . ": EDIT_UPDATE specified but article doesn't exist\n" );
- $status->fatal( 'edit-gone-missing' );
-
- return $status;
- } elseif ( !$old_content ) {
- // Sanity check for bug 37225
- throw new MWException( "Could not find text for current revision {$oldid}." );
- }
+ $status = $this->doModify( $pstContent, $flags, $user, $summary, $meta );
+ } else {
+ $status = $this->doCreate( $pstContent, $flags, $user, $summary, $meta );
+ }
- $revision = new Revision( array(
- 'page' => $this->getId(),
- 'title' => $this->getTitle(), // for determining the default content model
- 'comment' => $summary,
- 'minor_edit' => $isminor,
- 'text' => $serialized,
- 'len' => $newsize,
- 'parent_id' => $oldid,
- 'user' => $user->getId(),
- 'user_text' => $user->getName(),
- 'timestamp' => $now,
- 'content_model' => $content->getModel(),
- 'content_format' => $serialFormat,
- ) ); // XXX: pass content object?!
-
- $changed = !$content->equals( $old_content );
-
- if ( $changed ) {
- $prepStatus = $content->prepareSave( $this, $flags, $oldid, $user );
- $status->merge( $prepStatus );
-
- if ( !$status->isOK() ) {
- return $status;
- }
+ // Trigger post-save hook
+ $revision = $status->value['revision']; // new revision
+ $hook_args = array( &$this, &$user, $pstContent, $summary,
+ $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId );
+ ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $hook_args );
+ Hooks::run( 'PageContentSaveComplete', $hook_args );
- $dbw->begin( __METHOD__ );
- // Get the latest page_latest value while locking it.
- // Do a CAS style check to see if it's the same as when this method
- // started. If it changed then bail out before touching the DB.
- $latestNow = $this->lockAndGetLatest();
- if ( $latestNow != $oldid ) {
- $dbw->commit( __METHOD__ );
- // Page updated or deleted in the mean time
- $status->fatal( 'edit-conflict' );
-
- return $status;
- }
+ // Promote user to any groups they meet the criteria for
+ DeferredUpdates::addCallableUpdate( function () use ( $user ) {
+ $user->addAutopromoteOnceGroups( 'onEdit' );
+ $user->addAutopromoteOnceGroups( 'onView' ); // b/c
+ } );
- // At this point we are now comitted to returning an OK
- // status unless some DB query error or other exception comes up.
- // This way callers don't have to call rollback() if $status is bad
- // unless they actually try to catch exceptions (which is rare).
+ return $status;
+ }
- $revisionId = $revision->insertOn( $dbw );
+ /**
+ * @param Content $content Pre-save transform content
+ * @param integer $flags
+ * @param User $user
+ * @param string $summary
+ * @param array $meta
+ * @return Status
+ * @throws DBUnexpectedError
+ * @throws Exception
+ * @throws FatalError
+ * @throws MWException
+ */
+ private function doModify(
+ Content $content, $flags, User $user, $summary, array $meta
+ ) {
+ global $wgUseRCPatrol;
- // Update page_latest and friends to reflect the new revision
- if ( !$this->updateRevisionOn( $dbw, $revision, null, $oldIsRedirect ) ) {
- $dbw->rollback( __METHOD__ );
- throw new MWException( "Failed to update page row to use new revision." );
- }
+ // Update article, but only if changed.
+ $status = Status::newGood( array( 'new' => false, 'revision' => null ) );
- Hooks::run( 'NewRevisionFromEditComplete',
- array( $this, $revision, $baseRevId, $user ) );
+ // Convenience variables
+ $now = wfTimestampNow();
+ $oldid = $meta['oldId'];
+ /** @var $oldContent Content|null */
+ $oldContent = $meta['oldContent'];
+ $newsize = $content->getSize();
- // Update recentchanges
- if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
- // Mark as patrolled if the user can do so
- $patrolled = $wgUseRCPatrol && !count(
- $this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
- // Add RC row to the DB
- RecentChange::notifyEdit(
- $now, $this->mTitle, $isminor, $user, $summary,
- $oldid, $this->getTimestamp(), $bot, '', $oldsize, $newsize,
- $revisionId, $patrolled
- );
- }
+ if ( !$oldid ) {
+ // Article gone missing
+ $status->fatal( 'edit-gone-missing' );
- $user->incEditCount();
+ return $status;
+ } elseif ( !$oldContent ) {
+ // Sanity check for bug 37225
+ throw new MWException( "Could not find text for current revision {$oldid}." );
+ }
- $dbw->commit( __METHOD__ );
- $this->mTimestamp = $now;
- } else {
- // Bug 32948: revision ID must be set to page {{REVISIONID}} and
- // related variables correctly
- $revision->setId( $this->getLatest() );
- }
+ // @TODO: pass content object?!
+ $revision = new Revision( array(
+ 'page' => $this->getId(),
+ 'title' => $this->mTitle, // for determining the default content model
+ 'comment' => $summary,
+ 'minor_edit' => $meta['minor'],
+ 'text' => $meta['serialized'],
+ 'len' => $newsize,
+ 'parent_id' => $oldid,
+ 'user' => $user->getId(),
+ 'user_text' => $user->getName(),
+ 'timestamp' => $now,
+ 'content_model' => $content->getModel(),
+ 'content_format' => $meta['serialFormat'],
+ ) );
- // Update links tables, site stats, etc.
- $this->doEditUpdates(
- $revision,
- $user,
- array(
- 'changed' => $changed,
- 'oldcountable' => $oldcountable,
- 'oldrevision' => $old_revision
- )
- );
-
- if ( !$changed ) {
- $status->warning( 'edit-no-change' );
- $revision = null;
- // Update page_touched, this is usually implicit in the page update
- // Other cache updates are done in onArticleEdit()
- $this->mTitle->invalidateCache( $now );
- }
- } else {
- // Create new article
- $status->value['new'] = true;
+ $changed = !$content->equals( $oldContent );
+ if ( $changed ) {
$prepStatus = $content->prepareSave( $this, $flags, $oldid, $user );
$status->merge( $prepStatus );
if ( !$status->isOK() ) {
return $status;
}
+ $dbw = wfGetDB( DB_MASTER );
$dbw->begin( __METHOD__ );
+ // Get the latest page_latest value while locking it.
+ // Do a CAS style check to see if it's the same as when this method
+ // started. If it changed then bail out before touching the DB.
+ $latestNow = $this->lockAndGetLatest();
+ if ( $latestNow != $oldid ) {
+ $dbw->commit( __METHOD__ );
+ // Page updated or deleted in the mean time
+ $status->fatal( 'edit-conflict' );
- // Add the page record unless one already exists for the title
- $newid = $this->insertOn( $dbw );
- if ( $newid === false ) {
- $dbw->commit( __METHOD__ ); // nothing inserted
- $status->fatal( 'edit-already-exists' );
-
- return $status; // nothing done
+ return $status;
}
// At this point we are now comitted to returning an OK
// This way callers don't have to call rollback() if $status is bad
// unless they actually try to catch exceptions (which is rare).
- // Save the revision text...
- $revision = new Revision( array(
- 'page' => $newid,
- 'title' => $this->getTitle(), // for determining the default content model
- 'comment' => $summary,
- 'minor_edit' => $isminor,
- 'text' => $serialized,
- 'len' => $newsize,
- 'user' => $user->getId(),
- 'user_text' => $user->getName(),
- 'timestamp' => $now,
- 'content_model' => $content->getModel(),
- 'content_format' => $serialFormat,
- ) );
+ // Save the revision text
$revisionId = $revision->insertOn( $dbw );
-
- // Bug 37225: use accessor to get the text as Revision may trim it
- $content = $revision->getContent(); // sanity; get normalized version
-
- if ( $content ) {
- $newsize = $content->getSize();
- }
-
- // Update the page record with revision data
- if ( !$this->updateRevisionOn( $dbw, $revision, 0 ) ) {
+ // Update page_latest and friends to reflect the new revision
+ if ( !$this->updateRevisionOn( $dbw, $revision, null, $meta['oldIsRedirect'] ) ) {
$dbw->rollback( __METHOD__ );
throw new MWException( "Failed to update page row to use new revision." );
}
- Hooks::run( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
+ Hooks::run( 'NewRevisionFromEditComplete',
+ array( $this, $revision, $meta['baseRevId'], $user ) );
// Update recentchanges
if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
// Mark as patrolled if the user can do so
- $patrolled = ( $wgUseRCPatrol || $wgUseNPPatrol ) && !count(
- $this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
+ $patrolled = $wgUseRCPatrol && !count(
+ $this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
// Add RC row to the DB
- RecentChange::notifyNew(
- $now, $this->mTitle, $isminor, $user, $summary, $bot,
- '', $newsize, $revisionId, $patrolled
+ RecentChange::notifyEdit(
+ $now,
+ $this->mTitle,
+ $revision->isMinor(),
+ $user,
+ $summary,
+ $oldid,
+ $this->getTimestamp(),
+ $meta['bot'],
+ '',
+ $oldContent ? $oldContent->getSize() : 0,
+ $newsize,
+ $revisionId,
+ $patrolled
);
}
$dbw->commit( __METHOD__ );
$this->mTimestamp = $now;
+ } else {
+ // Bug 32948: revision ID must be set to page {{REVISIONID}} and
+ // related variables correctly
+ $revision->setId( $this->getLatest() );
+ }
+
+ // Update links tables, site stats, etc.
+ $this->doEditUpdates(
+ $revision,
+ $user,
+ array(
+ 'changed' => $changed,
+ 'oldcountable' => $meta['oldCountable'],
+ 'oldrevision' => $meta['oldRevision']
+ )
+ );
+
+ if ( $changed ) {
+ // Return the new revision to the caller
+ $status->value['revision'] = $revision;
+ } else {
+ $status->warning( 'edit-no-change' );
+ // Update page_touched as updateRevisionOn() was not called.
+ // Other cache updates are managed in onArticleEdit() via doEditUpdates().
+ $this->mTitle->invalidateCache( $now );
+ }
+
+ return $status;
+ }
- // Update links, etc.
- $this->doEditUpdates(
- $revision,
+ /**
+ * @param Content $content Pre-save transform content
+ * @param integer $flags
+ * @param User $user
+ * @param string $summary
+ * @param array $meta
+ * @return Status
+ * @throws DBUnexpectedError
+ * @throws Exception
+ * @throws FatalError
+ * @throws MWException
+ */
+ private function doCreate(
+ Content $content, $flags, User $user, $summary, array $meta
+ ) {
+ global $wgUseRCPatrol, $wgUseNPPatrol;
+
+ $status = Status::newGood( array( 'new' => true, 'revision' => null ) );
+
+ $now = wfTimestampNow();
+ $newsize = $content->getSize();
+ $prepStatus = $content->prepareSave( $this, $flags, $meta['oldId'], $user );
+ $status->merge( $prepStatus );
+ if ( !$status->isOK() ) {
+ return $status;
+ }
+
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin( __METHOD__ );
+
+ // Add the page record unless one already exists for the title
+ $newid = $this->insertOn( $dbw );
+ if ( $newid === false ) {
+ $dbw->commit( __METHOD__ ); // nothing inserted
+ $status->fatal( 'edit-already-exists' );
+
+ return $status; // nothing done
+ }
+
+ // At this point we are now comitted to returning an OK
+ // status unless some DB query error or other exception comes up.
+ // This way callers don't have to call rollback() if $status is bad
+ // unless they actually try to catch exceptions (which is rare).
+
+ // @TODO: pass content object?!
+ $revision = new Revision( array(
+ 'page' => $newid,
+ 'title' => $this->mTitle, // for determining the default content model
+ 'comment' => $summary,
+ 'minor_edit' => $meta['minor'],
+ 'text' => $meta['serialized'],
+ 'len' => $newsize,
+ 'user' => $user->getId(),
+ 'user_text' => $user->getName(),
+ 'timestamp' => $now,
+ 'content_model' => $content->getModel(),
+ 'content_format' => $meta['serialFormat'],
+ ) );
+
+ // Save the revision text...
+ $revisionId = $revision->insertOn( $dbw );
+ // Update the page record with revision data
+ if ( !$this->updateRevisionOn( $dbw, $revision, 0 ) ) {
+ $dbw->rollback( __METHOD__ );
+ throw new MWException( "Failed to update page row to use new revision." );
+ }
+
+ Hooks::run( 'NewRevisionFromEditComplete', array( $this, $revision, false, $user ) );
+
+ // Update recentchanges
+ if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
+ // Mark as patrolled if the user can do so
+ $patrolled = ( $wgUseRCPatrol || $wgUseNPPatrol ) &&
+ !count( $this->mTitle->getUserPermissionsErrors( 'autopatrol', $user ) );
+ // Add RC row to the DB
+ RecentChange::notifyNew(
+ $now,
+ $this->mTitle,
+ $revision->isMinor(),
$user,
- array( 'created' => true, 'oldrevision' => $old_revision )
+ $summary,
+ $meta['bot'],
+ '',
+ $newsize,
+ $revisionId,
+ $patrolled
);
+ }
- $hook_args = array( &$this, &$user, $content, $summary,
- $flags & EDIT_MINOR, null, null, &$flags, $revision );
+ $user->incEditCount();
- ContentHandler::runLegacyHooks( 'ArticleInsertComplete', $hook_args );
- Hooks::run( 'PageContentInsertComplete', $hook_args );
- }
+ $dbw->commit( __METHOD__ );
+ $this->mTimestamp = $now;
- // Return the new revision (or null) to the caller
- $status->value['revision'] = $revision;
+ // Update links, etc.
+ $this->doEditUpdates( $revision, $user, array( 'created' => true ) );
$hook_args = array( &$this, &$user, $content, $summary,
- $flags & EDIT_MINOR, null, null, &$flags, $revision, &$status, $baseRevId );
-
- ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $hook_args );
- Hooks::run( 'PageContentSaveComplete', $hook_args );
+ $flags & EDIT_MINOR, null, null, &$flags, $revision );
+ ContentHandler::runLegacyHooks( 'ArticleInsertComplete', $hook_args );
+ Hooks::run( 'PageContentInsertComplete', $hook_args );
- // Promote user to any groups they meet the criteria for
- DeferredUpdates::addCallableUpdate( function () use ( $user ) {
- $user->addAutopromoteOnceGroups( 'onEdit' );
- $user->addAutopromoteOnceGroups( 'onView' ); // b/c
- } );
+ // Return the new revision to the caller
+ $status->value['revision'] = $revision;
return $status;
}
public function __construct( Profiler $collector, array $params ) {
parent::__construct( $collector, $params );
- global $wgProfilePerHost;
// Initialize per-host profiling from config, back-compat if available
if ( isset( $this->params['perHost'] ) ) {
$this->perHost = $this->params['perHost'];
- } elseif ( $wgProfilePerHost ) {
- $this->perHost = $wgProfilePerHost;
}
}
/**
* Register core modules and runs registration hooks.
- * @param Config|null $config
+ * @param Config $config [optional]
+ * @param LoggerInterface $logger [optional]
*/
public function __construct( Config $config = null, LoggerInterface $logger = null ) {
global $IP;
return $this->config;
}
+ /**
+ * @since 1.26
+ * @param LoggerInterface $logger
+ */
public function setLogger( LoggerInterface $logger ) {
$this->logger = $logger;
}
+ /**
+ * @since 1.27
+ * @return LoggerInterface
+ */
+ public function getLogger() {
+ return $this->logger;
+ }
+
/**
* @since 1.26
* @return MessageBlobStore
* of a specific loader request
*/
class ResourceLoaderContext {
- /* Protected Members */
-
protected $resourceLoader;
protected $request;
+ protected $logger;
// Module content vary
protected $skin;
protected $userObj;
protected $imageObj;
- /* Methods */
-
/**
* @param ResourceLoader $resourceLoader
* @param WebRequest $request
public function __construct( ResourceLoader $resourceLoader, WebRequest $request ) {
$this->resourceLoader = $resourceLoader;
$this->request = $request;
+ $this->logger = $resourceLoader->getLogger();
// List of modules
$modules = $request->getVal( 'modules' );
return $this->request;
}
+ /**
+ * @since 1.27
+ * @return \Psr\Log\LoggerInterface
+ */
+ public function getLogger() {
+ return $this->logger;
+ }
+
/**
* @return array
*/
$versionHash = $module->getVersionHash( $context );
if ( strlen( $versionHash ) !== 8 ) {
+ $context->getLogger()->warning(
+ "Module '{module}' produced an invalid version hash: '{version}'.",
+ array(
+ 'module' => $name,
+ 'version' => $versionHash,
+ )
+ );
// Module implementation either broken or deviated from ResourceLoader::makeHash
// Asserted by tests/phpunit/structure/ResourcesTest.
$versionHash = ResourceLoader::makeHash( $versionHash );
}
}
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ if ( $search === '' ) {
+ return array();
+ }
+ // Autocomplete subpage the same as a normal search
+ $prefixSearcher = new StringPrefixSearch;
+ $result = $prefixSearcher->search( $search, $limit, array(), $offset );
+ return $result;
+ }
+
protected function getGroupName() {
return 'pages';
}
$out->addWikiMsg( 'changecontentmodel-success-text', $this->title );
}
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ if ( $search === '' ) {
+ return array();
+ }
+ // Autocomplete subpage the same as a normal search
+ $prefixSearcher = new StringPrefixSearch;
+ $result = $prefixSearcher->search( $search, $limit, array(), $offset );
+ return $result;
+ }
+
protected function getGroupName() {
return 'pagetools';
}
return "$plink . . $user . . $time";
}
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ if ( $search === '' ) {
+ return array();
+ }
+ // Autocomplete subpage the same as a normal search, but just for files
+ $prefixSearcher = new TitlePrefixSearch;
+ $result = $prefixSearcher->search( $search, $limit, array( NS_FILE ), $offset );
+
+ return array_map( function ( Title $t ) {
+ // Remove namespace in search suggestion
+ return $t->getText();
+ }, $result );
+ }
+
protected function getGroupName() {
return 'media';
}
$out->addHTML( "</ul>\n" );
}
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ if ( $search === '' ) {
+ return array();
+ }
+ // Autocomplete subpage the same as a normal search
+ $prefixSearcher = new StringPrefixSearch;
+ $result = $prefixSearcher->search( $search, $limit, array(), $offset );
+ return $result;
+ }
+
protected function getGroupName() {
return 'pagetools';
}
return $out1 . $out2;
}
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ if ( $search === '' ) {
+ return array();
+ }
+ // Autocomplete subpage the same as a normal search
+ $prefixSearcher = new StringPrefixSearch;
+ $result = $prefixSearcher->search( $search, $limit, array(), $offset );
+ return $result;
+ }
+
protected function getGroupName() {
return 'pagetools';
}
$output->addHTML( $topOut . $out );
}
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ if ( $search === '' ) {
+ return array();
+ }
+ // Autocomplete subpage the same as a normal search
+ $prefixSearcher = new StringPrefixSearch;
+ $result = $prefixSearcher->search( $search, $limit, array(), $offset );
+ return $result;
+ }
+
protected function getGroupName() {
return 'pages';
}
return $this->rclTargetTitle;
}
+
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ if ( $search === '' ) {
+ return array();
+ }
+ // Autocomplete subpage the same as a normal search
+ $prefixSearcher = new StringPrefixSearch;
+ $result = $prefixSearcher->search( $search, $limit, array(), $offset );
+ return $result;
+ }
}
}
}
+ /**
+ * Return an array of subpages beginning with $search that this special page will accept.
+ *
+ * @param string $search Prefix to search for
+ * @param int $limit Maximum number of results to return (usually 10)
+ * @param int $offset Number of results to skip (usually 0)
+ * @return string[] Matching subpages
+ */
+ public function prefixSearchSubpages( $search, $limit, $offset ) {
+ if ( $search === '' ) {
+ return array();
+ }
+ // Autocomplete subpage the same as a normal search
+ $prefixSearcher = new StringPrefixSearch;
+ $result = $prefixSearcher->search( $search, $limit, array(), $offset );
+ return $result;
+ }
+
protected function getGroupName() {
return 'pagetools';
}
"morenotlisted": "এটি একটি অসম্পূর্ণ তালিকা।",
"mypage": " পাতা",
"mytalk": "আলোচনা",
- "anontalk": "à¦\8fà¦\87 বà§\87নামà§\80 বà§\8dযবহারà¦\95ারà§\80র à¦\86লাপà§\87র পাতা",
+ "anontalk": "à¦\86লাপ",
"navigation": "পরিভ্রমণ",
"and": " এবং",
"qbfind": "অনুসন্ধান",
"wlshowlast": "সর্বশেষ $1 ঘণ্টা $2 দিনে দেখাও",
"watchlistall2": "সমস্ত",
"watchlist-hide": "আড়াল করো",
- "wlshowtime": "সরà§\8dবশà§\87ষà¦\9fি দà§\87à¦\96াà¦\93:",
+ "wlshowtime": "পà§\8dরদরà§\8dশনà§\87র সময় à¦\95াল:",
"wlshowhideminor": "অনুল্লেখ্য সম্পাদনা",
"wlshowhidebots": "বট",
"wlshowhideliu": "নিবন্ধিত ব্যবহারকারী",
"wlshowlast": "Zeige die Änderungen der letzten $1 Stunden, $2 Tage.",
"watchlistall2": "alle",
"watchlist-hide": "Ausblenden",
- "wlshowtime": "Zeige Änderungen der letzten",
+ "wlshowtime": "Anzuzeigende Zeitperiode:",
"wlshowhideminor": "Kleine Bearbeitungen",
"wlshowhidebots": "Bots",
"wlshowhideliu": "Registrierte Benutzer",
"collapsible-collapse": "Kılm ke",
"collapsible-expand": "Hera ke",
"confirmable-confirm": "{{GENDER:$1|Şıma }} do emeli?",
- "confirmable-yes": "E",
+ "confirmable-yes": "Eya",
"confirmable-no": "Nê",
"thisisdeleted": "Bıvêne ya zi $1 peyser biya?",
"viewdeleted": "$1 bıvêne?",
"revdelete-nooldid-text": "Şıma vıraştışê nê fonksiyoni rê ya yew çımraviyarnayışo waşte diyar nêkerdo, çımraviyarnayışo diyarkerde çıniyo, ya ki şıma wazenê ke çımraviyarnayışê nıkayêni bınımnê.",
"revdelete-no-file": "Dosya diyarkerdiye çıniya.",
"revdelete-show-file-confirm": "Şıma eminê ke wazenê çımraviyarnayışê esterıtey na dosya \"<nowiki>$1</nowiki>\" $2 ra $3 de bıvênê?",
- "revdelete-show-file-submit": "E",
+ "revdelete-show-file-submit": "Eya",
"logdelete-selected": "{{PLURAL:$1|Qeydbiyayışo weçinıte|Qeydbiyayışê weçinıtey}}:",
"revdelete-confirm": "Ma rica keno testiq bike ti ena hereket keno u ti zano neticeyanê herketanê xo u ti ena hereket pê ena [[{{MediaWiki:Policy-url}}|polici]] ra keno.",
"revdelete-suppress-text": "Wedardış gani '''tenya''' nê halanê cêrênan de bıxebıtiyo:\n* Melumatê kıfırio mıhtemel\n* Melumatê şexio bêmınasıb\n*: ''adresa keyey u numreyê têlefoni, numreyê siğorta sosyale, uêb.''",
"listfiles_count": "Versiyoni",
"listfiles-show-all": "Asayışa versiyonandé verénan",
"listfiles-latestversion": "Versiyono verin",
- "listfiles-latestversion-yes": "E",
+ "listfiles-latestversion-yes": "Eya",
"listfiles-latestversion-no": "Nê",
"file-anchor-link": "Dosya",
"filehist": "Ravêrdê dosya",
"undelete-error-short": "Eka dosyayê biyereno feqet yew ğelet biya: $1",
"undelete-error-long": "hewn a kerdışê na dosyayi wexta tepiya geriyenê xeta vıraziya:\n\n$1",
"undelete-show-file-confirm": "\"<nowiki>$1</nowiki>\" şıma emin î dosyaya revizyonê no $2 $3 tarixi bıvini?",
- "undelete-show-file-submit": "E",
+ "undelete-show-file-submit": "Eya",
"namespace": "Heruna namey:",
"invert": "Weçinıtışi açarne",
"tooltip-invert": "nameyo ke nışan biyo (u nameyo elekeyın zi nışanyyayo se) vurnayışan zerrekan nımtışi re ena dore tesdiqi nışan kerê",
"pageinfo-redirectsto-info": "melumat",
"pageinfo-contentpage": "Zey jû pela zerreki hesebiyena",
"pageinfo-contentpage-yes": "Eya",
- "pageinfo-protect-cascading": "Sıtarkerdey tiya cı ra yenê war",
+ "pageinfo-protect-cascading": "Sıtarkerdeyi tiya cı ra yenê war",
"pageinfo-protect-cascading-yes": "Eya",
"pageinfo-protect-cascading-from": "Sıtarkerdey cı ra yenê war",
"pageinfo-category-info": "Şınasiya kategoriye",
"tags-description-header": "Tam arezekerdışê maneyê cı",
"tags-active-header": "Activ o?",
"tags-hitcount-header": "Vurnayîşî ke etiket biyê",
- "tags-active-yes": "E",
+ "tags-active-yes": "Eya",
"tags-active-no": "Nê",
"tags-edit": "bıvurne",
"tags-hitcount": "$1 {{PLURAL:$1|vurnayış|vurnayışi}}",
"contributions": "Συνεισφορές {{GENDER:$1|χρήστη}}",
"contributions-title": "Συνεισφορές χρήστη για {{GENDER:$1|τον|την}} $1",
"mycontris": "Συνεισφορές",
+ "anoncontribs": "Συνεισφορές",
"contribsub2": "Για {{GENDER:$3|$1}} ($2)",
"contributions-userdoesnotexist": "Ο λογαριασμός χρήστη «$1» δεν είναι εγγεγραμμένος.",
"nocontribs": "Δεν βρέθηκαν αλλαγές με αυτά τα κριτήρια.",
"databaseerror-query": "Query: $1",
"databaseerror-function": "Function: $1",
"databaseerror-error": "Error: $1",
+ "transaction-duration-limit-exceeded": "In order to avoid creating high replication lag, this transaction was aborted because the write duration ($1) exceeded the $2 second limit.\nIf you are changing many items at once, trying doing multiple smaller operations instead.",
"laggedslavemode": "<strong>Warning:</strong> Page may not contain recent updates.",
"readonly": "Database locked",
"enterlockreason": "Enter a reason for the lock, including an estimate of when the lock will be released",
"spam_blanking": "All revisions contained links to $1, blanking",
"spam_deleting": "All revisions contained links to $1, deleting",
"simpleantispam-label": "Anti-spam check.\nDo <strong>not</strong> fill this in!",
- "autochange-username": "MediaWiki automatic change",
"pageinfo-header": "-",
"pageinfo-title": "Information for \"$1\"",
"pageinfo-not-current": "Sorry, it's impossible to provide this information for old revisions.",
"deletepage": "Poista sivu",
"confirm": "Toteuta",
"excontent": "sisälsi: ”$1”",
- "excontentauthor": "sisältö oli: \"$1\", ja ainoa muokkaaja oli \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|keskustelu]])",
+ "excontentauthor": "sisältö oli: \"$1\", ja ainoa muokkaaja oli \"[[Special:Contributions/$2|$2]]\"",
"exbeforeblank": "ennen tyhjentämistä sisälsi: ”$1”",
"delete-confirm": "Poista ”$1”",
"delete-legend": "Sivun poisto",
"rcshowhidemine-hide": "Cacher",
"diff": "diff",
"hist": "hist",
+ "hide": "Cacher",
"show": "Afficher",
"minoreditletter": "m",
"newpageletter": "N",
"filehist-current": "Courant",
"filehist-datetime": "Date et heure",
"filehist-user": "Useur",
+ "filehist-dimensions": "Dimensions",
"filehist-comment": "Commentaire",
"imagelinks": "Utilisation du fichier",
"nolinkstoimage": "Aucune page uses ce fichier.",
"namespace": "Espace de noms:",
"invert": "Inverser la sélection",
"blanknamespace": "(Principal)",
- "contributions": "Changements de l'useur",
+ "contributions": "Contributions de l’{{GENDER:$1|useur|useuse}}",
"mycontris": "Contributions",
"uctop": "(actuel)",
"sp-contributions-talk": "Discuter",
"whatlinkshere": "Pages liées",
"whatlinkshere-title": "Pages qui connectent vers \"$1\"",
"whatlinkshere-page": "Page:",
+ "isimage": "lien vers le fichier",
+ "whatlinkshere-next": "{{PLURAL:$1|suivante|$1 suivantes}}",
"whatlinkshere-links": "← liens",
"whatlinkshere-hidelinks": "$1 les liens",
"whatlinkshere-filters": "Filtres",
"pageinfo-toolboxlink": "Information sur la page",
"bad_image_list": "Le format est le suivant :\n\nSeules les listes d’énumération (commençant par *) sont prises en compte. Le premier lien d’une ligne doit être celui d’une mauvaise image.\nLes autres liens sur la même ligne sont considérés comme des exceptions, par exemple des pages sur lesquelles l’image peut apparaître.",
"metadata": "Métadonnées",
+ "exif-orientation": "Orientation",
"exif-colorspace": "Espace des couleurs",
"exif-orientation-1": "Normale",
"namespacesall": "Tous",
+ "monthsall": "tous",
"signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discussion]])",
"redirect-file": "Nom du fichier",
"specialpages": "Pages spéciales",
"tog-watchlisthidebots": "הסתרת עריכות של בוטים ברשימת המעקב",
"tog-watchlisthideminor": "הסתרת עריכות משניות ברשימת המעקב",
"tog-watchlisthideliu": "הסתרת עריכות של משתמשים רשומים ברשימת המעקב",
- "tog-watchlistreloadautomatically": "×\9c×¨×¢× ×\9f ×\90ת רש×\99×\9eת ×\94×\9eעק×\91 ×\90×\95×\98×\95×\9e×\98×\99ת ×\91×\9b×\9c פע×\9d ש×\94מסנן משתנה (נדרש JavaScript)",
+ "tog-watchlistreloadautomatically": "×¨×¢× ×\95×\9f ×\90×\95×\98×\95×\9e×\98×\99 ש×\9c רש×\99×\9eת ×\94×\9eעק×\91 ×\91×\9b×\9c פע×\9d שמסנן משתנה (נדרש JavaScript)",
"tog-watchlisthideanons": "הסתרת עריכות של משתמשים אנונימיים ברשימת המעקב",
"tog-watchlisthidepatrolled": "הסתרת עריכות בדוקות ברשימת המעקב",
"tog-watchlisthidecategorization": "הסתרת הוספות והסרות של דפים מקטגוריות",
"wrongpasswordempty": "הסיסמה שהקלדתם ריקה.\nאנא נסו שוב.",
"passwordtooshort": "סיסמאות חייבות להיות באורך {{PLURAL:$1|תו אחד|$1 תווים}} לפחות.",
"passwordtoolong": "סיסמאות אינן יכולות להיות ארוכות {{PLURAL:$1|מתו אחד|מ־$1 תווים}}.",
- "passwordtoopopular": "×\9c×\90 × ×\99ת×\9f ×\9c×\94שת×\9eש ×\91סס×\9e×\90×\95ת × ×¤×\95צ×\95ת. × ×\90 ×\9c×\91×\97×\95ר ססמה ייחודית יותר.",
+ "passwordtoopopular": "×\9c×\90 × ×\99ת×\9f ×\9c×\94שת×\9eש ×\91ס×\99ס×\9e×\90×\95ת × ×¤×\95צ×\95ת. ×\99ש ×\9c×\91×\97×\95ר ס×\99סמה ייחודית יותר.",
"password-name-match": "סיסמתך חייבת להיות שונה משם המשתמש שלך.",
"password-login-forbidden": "השימוש בשם המשתמש והסיסמה האלה נאסר.",
"mailmypassword": "איפוס סיסמה",
"wlshowlast": "הצגת $1 שעות אחרונות $2 ימים אחרונים",
"watchlistall2": "הכול",
"watchlist-hide": "הסתרה",
- "wlshowtime": "×\9c×\94צ×\99×\92 ×\9e×\90×\96:",
+ "wlshowtime": "×\90×\99×\96×\95 תק×\95פ×\94 ×\9c×\94צ×\99×\92:",
"wlshowhideminor": "עריכות משניות",
"wlshowhidebots": "בוטים",
"wlshowhideliu": "משתמשים רשומים",
"search-section": "(bolken $1)",
"search-category": "(kategorien $1)",
"search-suggest": "Meinte du: «$1»",
+ "search-rewritten": "Viser resultat for $1. Søk i staden etter $2.",
"search-interwiki-caption": "Systerprosjekt",
"search-interwiki-default": "Resultat frå $1:",
"search-interwiki-more": "(meir)",
"wlshowlast": "Pokaż ostatnie $1 godzin, $2 dni",
"watchlistall2": "wszystkie",
"watchlist-hide": "Ukryj",
- "wlshowtime": "Pokaż ostatnie:",
+ "wlshowtime": "Okres czasu do wyświetlania:",
"wlshowhideminor": "drobne edycje",
"wlshowhidebots": "boty",
"wlshowhideliu": "zarejestrowanych",
"newpage": "نوی مخ",
"talkpage": "د دې مخ په اړه خبرې اترې کول",
"talkpagelinktext": "خبرې اترې",
- "specialpage": "Ú\81اÙ\86Ú¯Ú\93Û\90 پاڼÙ\87",
+ "specialpage": "Ú\81اÙ\86Ú¯Ú\93Û\8c Ù\85Ø®",
"personaltools": "شخصي اوزار",
"articlepage": "د مخ مېنځپانگه ښکاره کول",
"talk": "خبرې اترې",
"anontalkpagetext": "----''دا د يوه ورکنومي کارن چې کارن-نوم نه لري او يا خپل کارن-نوم نه کاروي، د سکالو يوه پاڼه ده. نو د يوه کس د پېژندلو پخاطر موږ د هماغه کارن د انټرنېټ شمېره يا IP پته دلته ثبتوؤ. داسې يوه IP پته د ډېرو کارنانو لخوا هم کارېدلی شي. که تاسې يو ورکنومی کارن ياست او تاسې ته دا څرگندېږي چې تاسې ته نااړونده پېغامونه او تبصرې اشاره شوي، نو د نورو بې نومو کارنانو او ستاسې ترمېنځ د ټکنتوب د مخ نيونې لپاره لطفاً [[Special:UserLogin/signup|يو گڼون جوړ کړۍ]] او يا هم [[Special:UserLogin|غونډال ته ورننوځۍ]].''",
"noarticletext": "دم مهال په دې مخ کې څه نشته.\nتاسې کولای شی چې په نورو مخونو کې [[Special:Search/{{PAGENAME}}|د دې مخ د سرليک پلټنه]] يا\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} د اړوندو يادښتونو پلټنه] وکړی.\nاو يا [{{fullurl:{{FULLPAGENAME}}|action=edit}} همدا مخ سم کړی]</span>.",
"noarticletext-nopermission": "دم مهال په دې مخ کې متن نشته.\nتاسې کولای شی چې [[Special:Search/{{PAGENAME}}|همدا سرليک په نورو مخونو کې وپلټۍ]], يا هم <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} اړونده يادښتونه وپلټۍ]</span>، خو تاسې د دې مخ د جوړولو اجازه نه لرۍ.",
- "userpage-userdoesnotexist": "د \"<nowiki>$1</nowiki>\" گڼون نه دی ثبت شوی.\nلطفاً ځان ډاډه کړۍ چې آيا تاسې په رښتيا همدا مخ جوړول که سمول غواړۍ.",
+ "userpage-userdoesnotexist": "د \"<nowiki>$1</nowiki>\" گڼون نه دی ثبت شوی.\nلطفاً ځان ډاډه کړئ چې آيا تاسې په رښتيا همدا مخ جوړول/سمول غواړئ.",
"userpage-userdoesnotexist-view": "د \"$1\" گڼون نه دی ثبت شوی.",
"blocked-notice-logextract": "دم مهال په دې کارن بنديز لگېدلی.\nد بنديز يادښت تازه مالومات په لاندې توگه دي:",
"clearyourcache": "'''يادښت:''' د غوره توبونو د خوندي کولو وروسته، خپل د کتنمل (بروزر) ساتل شوې حافظه تازه کړی.\n* '''فايرفاکس/ سفري:''' په دې کتنمل کې د ''Reload'' د ټکوهلو په وخت د ''Shift'' تڼۍ نيولې وساتی، او يا هم ''Ctrl-F5'' يا ''Ctrl-R''تڼۍ کېښکاږۍ (په Apple Mac کمپيوټر باندې ''⌘-R'' کېښکاږۍ)\n* '''گووگل کروم:''' په دې کتنمل کې د ''Ctrl-Shift-R'' تڼۍ کېښکاږۍ (د مک لپاره ''⌘-Shift-R'')\n* '''انټرنټ اېکسپلورر:''' په دې کتنمل کې د ''Refresh'' د ټکوهلو په وخت کې د ''Ctrl'' تڼۍ کېښکاږلې ونيسۍ، او يا هم د ''Ctrl-F5'' تڼۍ کېښکاږۍ\n* '''اوپرا''': په دې کتنمل کې د خپل براوزر ساتل شوې حافظه پدې توگه سپينولی شی ''Tools→Preferences''",
"exif-ycbcrpositioning-1": "منځنی",
"exif-dc-contributor": "ونډه وال",
"exif-dc-date": "نېټه (نېټې)",
- "exif-dc-publisher": "خپرونکی",
+ "exif-dc-publisher": "خپرÙ\88Ù\88Ù\86Ú©Û\8c",
"exif-dc-relation": "اړونده رسنۍ",
"exif-dc-rights": "رښتې",
"exif-dc-source": "د سرچينې رسنۍ",
"logentry-newusers-newusers": "د $1 کارن گڼون {{GENDER:$2|جوړ شو}}",
"logentry-newusers-create": "د $1 کارن گڼون {{GENDER:$2|جوړ شو}}",
"logentry-newusers-autocreate": "د $1 گڼون په اتوماتيک ډول {{GENDER:$2|جوړ شو}}",
+ "logentry-protect-unprotect": "$1 له $3 څخه ژغورنه {{GENDER:$2|ليرې کړه}}",
"logentry-rights-rights": "$1 د $3 لپاره د غړيتوب ډله له $4 څخه $5 ته {{GENDER:$2|بدله کړه}}",
"logentry-rights-rights-legacy": "$1 د $3 لپاره د غړيتوب ډله {{GENDER:$2|بدله کړه}}",
"logentry-upload-upload": "$1 $3 {{GENDER:$2|ورپورته يې کړ}}",
"databaseerror-query": "Identifies, in the list of technical details, the [[wikipedia:SQL|SQL]] statement that failed.\nParameters:\n* $1 - SQL statement (shown within a box)\n{{Identical|Query}}",
"databaseerror-function": "Identifies, in the list of technical details, the function that tried to execute the database query.\nParameters:\n* $1 - Name of function\n{{Identical|Function}}",
"databaseerror-error": "Identifies, in the list of technical details, the error message the database server returned.\nParameters:\n* $1 - Error message from the database server, probably in English\n{{Identical|Error}}",
+ "transaction-duration-limit-exceeded": "Plain text error shown when DB updates take too long. Parameters:\n* $1 - time spent in database updates\n* $2 - maximum time allowed in database updates",
"laggedslavemode": "Used as warning when getting the timestamp of the latest version, if in LaggedSlaveMode.",
"readonly": "Used as title of error message when database is locked.",
"enterlockreason": "For developers when locking the database",
"wlshowlast": "Показать за последние $1 часов $2 дней",
"watchlistall2": "все",
"watchlist-hide": "Скрыть",
- "wlshowtime": "Ð\9fоказаÑ\82Ñ\8c поÑ\81ледние:",
+ "wlshowtime": "Ð\9fеÑ\80иод вÑ\80емени длÑ\8f оÑ\82обÑ\80ажениÑ\8f:",
"wlshowhideminor": "малые правки",
"wlshowhidebots": "ботов",
"wlshowhideliu": "зарегистрированных участников",
"wlshowlast": "Prikaži zadnjih $1 ur; $2 dni",
"watchlistall2": "vse",
"watchlist-hide": "Skrij",
- "wlshowtime": "Prikaži zadnje:",
+ "wlshowtime": "Časovno obdobje za prikaz:",
"wlshowhideminor": "manjša urejanja",
"wlshowhidebots": "boti",
"wlshowhideliu": "registrirani uporabniki",
"tog-watchlisthidebots": "Приховати редагування ботів у списку спостереження",
"tog-watchlisthideminor": "Приховати незначні редагування у списку спостереження",
"tog-watchlisthideliu": "Приховати редагування зареєстрованих дописувачів у списку спостереження",
+ "tog-watchlistreloadautomatically": "Перезавантажувати список спостереження автоматично кожного разу, коли зміниться фільтр (вимагається JavaScript)",
"tog-watchlisthideanons": "Приховати редагування анонімних користувачів у списку спостереження",
"tog-watchlisthidepatrolled": "Приховати відпатрульовані редагування у списку спостереження",
"tog-watchlisthidecategorization": "Приховати категоризацію сторінок",
"morenotlisted": "Цей список неповний.",
"mypage": "Сторінка",
"mytalk": "Обговорення",
- "anontalk": "Обговорення для цієї IP-адреси",
+ "anontalk": "Обговорення",
"navigation": "Навігація",
"and": " і",
"qbfind": "Знайти",
"wrongpasswordempty": "Ви не ввели пароль. Будь ласка, спробуйте ще раз.",
"passwordtooshort": "Ваш пароль закороткий, він має містити принаймні $1 {{PLURAL:$1|символ|символи|символів}}.",
"passwordtoolong": "Пароль не може бути довшим ніж {{PLURAL:$1|1 символ|$1 символи|$1 символів}}.",
+ "passwordtoopopular": "Паролі, що часто обираються, не можуть бути використані. Будь ласка, оберіть більш унікальний пароль.",
"password-name-match": "Ваш пароль має відрізнятися від імені користувача.",
"password-login-forbidden": "Використання цього імені користувача і пароля заборонено.",
"mailmypassword": "Перевстановити пароль",
"booksources-text": "На цій сторінці наведено список посилань на сайти, де ви, можливо, знайдете додаткову інформацію про книгу. Це інтернет-магазини й системи пошуку в бібліотечних каталогах.",
"booksources-invalid-isbn": "Вказаний номер ISBN, судячи з усього, містить помилку. Будь ласка, перевірте, що при перенесенні номера з першоджерела не виникло спотворень.",
"specialloguserlabel": "Виконавець:",
- "speciallogtitlelabel": "Ціль (назва або {{ns:user}}:ім'я для користувача):",
+ "speciallogtitlelabel": "Ціль (назва сторінки або {{ns:user}}:ім'я користувача):",
"log": "Журнали",
"all-logs-page": "Усі публічні журнали",
"alllogstext": "Комбінований показ журналів {{grammar:genitive|{{SITENAME}}}}.\nВи можете відфільтрувати результати за типом журналу, іменем користувача (враховується регістр) або зазначеною сторінкою (також враховується регістр).",
"wlshowlast": "Показати зміни за останні $1 годин $2 днів",
"watchlistall2": "всі",
"watchlist-hide": "Приховати",
- "wlshowtime": "Ð\9fоказаÑ\82и оÑ\81Ñ\82аннÑ\96:",
+ "wlshowtime": "Ð\9fеÑ\80Ñ\96од Ñ\87аÑ\81Ñ\83 длÑ\8f вÑ\96добÑ\80аженнÑ\8f:",
"wlshowhideminor": "незначні редагування",
"wlshowhidebots": "ботів",
"wlshowhideliu": "зареєстрованих користувачів",
"contributions": "Внесок {{GENDER:$1|користувача|користувачки}}",
"contributions-title": "Внесок користувача $1",
"mycontris": "Внесок",
+ "anoncontribs": "Внесок",
"contribsub2": "Для {{GENDER:$3|$1}} ($2)",
"contributions-userdoesnotexist": "Обліковий запис користувача «$1» не зареєстровано.",
"nocontribs": "Редагувань, що задовольняють заданим умовам не знайдено.",
"tooltip-pt-preferences": "Ваші налаштування",
"tooltip-pt-watchlist": "Список сторінок, за змінами в яких Ви спостерігаєте",
"tooltip-pt-mycontris": "Ваш внесок",
+ "tooltip-pt-anoncontribs": "Список редагувань, зроблених з цієї IP-адреси",
"tooltip-pt-login": "Тут можна зареєструватися в системі, але це не обов'язково.",
"tooltip-pt-logout": "Вихід із системи",
"tooltip-pt-createaccount": "Пропонуємо створити обліковий запис і увійти в систему; однак, це не обов'язково",
"logentry-suppress-block": "$1 {{GENDER:$2|заблокував}} {{GENDER:$4|$3}} строком на $5 $6",
"logentry-suppress-reblock": "$1 {{GENDER:$2|змінив}} блокування для {{GENDER:$4|$3}} на період $5 $6",
"logentry-import-upload": "$1 імпортува{{GENDER:$2|в|ла}} $3 через завантаження файлів",
+ "logentry-import-upload-details": "$1 {{GENDER:$2|імпортував|імпортувала}} $3 за допомогою файлового завантаження ($4 {{PLURAL:$4|версія|версій|версії}})",
"logentry-import-interwiki": "$1 імпортува{{GENDER:$2|в|ла}} $3 з іншої вікі",
+ "logentry-import-interwiki-details": "$1 {{GENDER:$2|імпортував|імпортувала}} $3 з $5 ($4 {{PLURAL:$4|версія|версій|версії}})",
"logentry-merge-merge": "$1 {{GENDER:$2|приєднав|приєднала}} $3 до $4 (версії до $5)",
"logentry-move-move": "$1 {{GENDER:$2|перейменував|перейменувала}} сторінку з $3 на $4",
"logentry-move-move-noredirect": "$1 {{GENDER:$2|перейменував|перейменувала}} сторінку з $3 на $4 без створення перенаправлення",
"morenotlisted": "די ליסטע איז נישט פֿולשטענדיק.",
"mypage": "מיין בלאַט",
"mytalk": "שמועס",
- "anontalk": "×\93×\90ס רע×\93×\9f פ×\95×\9f ×\93×¢×\9d IP",
+ "anontalk": "ש×\9e×\95עס",
"navigation": "נאַוויגאַציע",
"and": " און",
"qbfind": "טרעף",
"wlnote": "אונטן {{PLURAL:$1|איז די לעצטע ענדערונג|זענען די לעצטע <strong>$1</strong> ענדערונגען}} אין {{PLURAL:$2|דער לעצטער שעה|די לעצטע <strong>$2</strong> שעה'ן}} ביז $3, $4.",
"wlshowlast": "ווײַזן די לעצטע $1 שעה'ן $2 טעג",
"watchlistall2": "אַלע",
+ "watchlist-hide": "באַהאַלטן",
+ "wlshowtime": "ווײַזן לעצטע:",
+ "wlshowhideminor": "מינערדיקער רעדאקטירונגען",
"watchlist-options": "אויפֿפאַסן ליסטע ברירות",
"watching": "אויפפאסענדונג…",
"unwatching": "נעמט אראפ פון אויפפאסונג ליסטע…",
"contributions": "{{GENDER:$1|באניצער}} בײַשטײַערונגען",
"contributions-title": "בײַשטײַערונגען פֿון באַניצער $1",
"mycontris": "בײַשטײַערונגען",
+ "anoncontribs": "בײַשטײַערונגען",
"contribsub2": "פֿאַר {{GENDER:$3|$1}} ($2)",
"contributions-userdoesnotexist": "באניצער קאנטע \"$1\" איז נישט איינגעשריבן.",
"nocontribs": "נישט געטראפן קיין ענדערונגען צוזאמעגעפאסט מיט די קריטעריעס.",
"movenosubpage": "דער דאָזיגער בלאַט האט נישט קיין אונטערבלעטער.",
"movereason": "אורזאַך:",
"revertmove": "צוריקדרייען",
- "delete_and_move": "אויסמעקן און באוועגן",
"delete_and_move_text": "== אויסמעקן פארלאנגט ==\nדער ציל בלאַט \"[[:$1]]\" עקזיסטירט שוין.\nצי ווילט איר אים אויסמעקן כדי צו ערמעגליכן די באוועגונג?",
"delete_and_move_confirm": "יא, מעק אויס דעם בלאט",
"delete_and_move_reason": "אויסגעמעקט כדי צו קענען באוועגן פֿון \"[[$1]]\"",
runJobs.php
Immediately complete all jobs in the job queue
- showCacheStats.php
- Show all statistics stored in the cache
-
undelete.php
Undelete all revisions of a page
+++ /dev/null
-<?php
-/**
- * Removes all statistics tracking from the cache.
- *
- * 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 Maintenance
- */
-
-require_once __DIR__ . '/Maintenance.php';
-
-/**
- * Maintenance script to remove all statistics tracking from the cache.
- *
- * @ingroup Maintenance
- */
-class ClearCacheStats extends Maintenance {
-
- public function __construct() {
- parent::__construct();
- $this->mDescription = "Remove all statistics tracking from the cache";
- }
-
- public function execute() {
- global $wgLocalDatabases, $wgMemc;
- foreach ( $wgLocalDatabases as $db ) {
- $wgMemc->delete( "$db:stats:request_with_session" );
- $wgMemc->delete( "$db:stats:request_without_session" );
- $wgMemc->delete( "$db:stats:pcache_hit" );
- $wgMemc->delete( "$db:stats:pcache_miss_expired" );
- $wgMemc->delete( "$db:stats:pcache_miss_absent" );
- $wgMemc->delete( "$db:stats:pcache_miss_stub" );
- $wgMemc->delete( "$db:stats:image_cache_hit" );
- $wgMemc->delete( "$db:stats:image_cache_miss" );
- $wgMemc->delete( "$db:stats:image_cache_update" );
- $wgMemc->delete( "$db:stats:diff_cache_hit" );
- $wgMemc->delete( "$db:stats:diff_cache_miss" );
- $wgMemc->delete( "$db:stats:diff_uncacheable" );
- $wgMemc->delete( "$db:stats:job-insert" );
- $wgMemc->delete( "$db:stats:job-pop" );
- }
- }
-}
-
-$maintClass = "ClearCacheStats";
-require_once RUN_MAINTENANCE_IF_MAIN;
+++ /dev/null
-<?php
-/**
- * Show statistics from the cache.
- *
- * 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 Maintenance
- */
-
-require_once __DIR__ . '/Maintenance.php';
-
-/**
- * Maintenance script that shows statistics from the cache.
- *
- * @ingroup Maintenance
- */
-class ShowCacheStats extends Maintenance {
-
- public function __construct() {
- $this->mDescription = "Show statistics from the cache";
- parent::__construct();
- }
-
- public function getDbType() {
- return Maintenance::DB_NONE;
- }
-
- public function execute() {
- global $wgMemc;
-
- // Can't do stats if
- if ( get_class( $wgMemc ) == 'EmptyBagOStuff' ) {
- $this->error( "You are running EmptyBagOStuff, I can not provide any statistics.", true );
- }
-
- $this->output( "\nParser cache\n" );
- $hits = intval( $wgMemc->get( wfMemcKey( 'stats', 'pcache_hit' ) ) );
- $expired = intval( $wgMemc->get( wfMemcKey( 'stats', 'pcache_miss_expired' ) ) );
- $absent = intval( $wgMemc->get( wfMemcKey( 'stats', 'pcache_miss_absent' ) ) );
- $stub = intval( $wgMemc->get( wfMemcKey( 'stats', 'pcache_miss_stub' ) ) );
- $total = $hits + $expired + $absent + $stub;
- if ( $total ) {
- $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits / $total * 100 ) );
- $this->output( sprintf(
- "expired: %-10d %6.2f%%\n",
- $expired,
- $expired / $total * 100
- ) );
- $this->output( sprintf(
- "absent: %-10d %6.2f%%\n",
- $absent,
- $absent / $total * 100
- ) );
- $this->output( sprintf( "stub threshold: %-10d %6.2f%%\n", $stub, $stub / $total * 100 ) );
- $this->output( sprintf( "total: %-10d %6.2f%%\n", $total, 100 ) );
- } else {
- $this->output( "no statistics available\n" );
- }
-
- $this->output( "\nImage cache\n" );
- $hits = intval( $wgMemc->get( wfMemcKey( 'stats', 'image_cache_hit' ) ) );
- $misses = intval( $wgMemc->get( wfMemcKey( 'stats', 'image_cache_miss' ) ) );
- $updates = intval( $wgMemc->get( wfMemcKey( 'stats', 'image_cache_update' ) ) );
- $total = $hits + $misses;
- if ( $total ) {
- $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits / $total * 100 ) );
- $this->output( sprintf(
- "misses: %-10d %6.2f%%\n",
- $misses,
- $misses / $total * 100
- ) );
- $this->output( sprintf( "updates: %-10d\n", $updates ) );
- } else {
- $this->output( "no statistics available\n" );
- }
-
- $this->output( "\nDiff cache\n" );
- $hits = intval( $wgMemc->get( wfMemcKey( 'stats', 'diff_cache_hit' ) ) );
- $misses = intval( $wgMemc->get( wfMemcKey( 'stats', 'diff_cache_miss' ) ) );
- $uncacheable = intval( $wgMemc->get( wfMemcKey( 'stats', 'diff_uncacheable' ) ) );
- $total = $hits + $misses + $uncacheable;
- if ( $total ) {
- $this->output( sprintf( "hits: %-10d %6.2f%%\n", $hits, $hits / $total * 100 ) );
- $this->output( sprintf(
- "misses: %-10d %6.2f%%\n",
- $misses,
- $misses / $total * 100
- ) );
- $this->output( sprintf(
- "uncacheable: %-10d %6.2f%%\n",
- $uncacheable,
- $uncacheable / $total * 100
- ) );
- } else {
- $this->output( "no statistics available\n" );
- }
- }
-}
-
-$maintClass = "ShowCacheStats";
-require_once RUN_MAINTENANCE_IF_MAIN;
*/
badToken: function ( type ) {
var promiseGroup = promises[ this.defaults.ajax.url ];
+
+ type = mapLegacyToken( type );
if ( promiseGroup ) {
delete promiseGroup[ type + 'Token' ];
}
}
$( function () {
- if ( mw.config.get( 'wgNamespaceNumber' ) !== mw.config.get( 'wgNamespaceIds' ).file ) {
+ if ( mw.config.get( 'wgCanonicalNamespace' ) !== 'File' ) {
return;
}
$multipageimage = $( 'table.multipageimage' );
'wgLanguageCode' => 'en',
'wgContLang' => $contLang,
'wgLang' => Language::factory( 'en' ),
- 'wgMemc' => new EmptyBagOStuff,
'wgCleanSignatures' => true,
) );
$this->setMwGlobals( array(
'wgSecretKey' => 'foo',
- 'wgMemc' => new EmptyBagOStuff(),
) );
$this->templateDir = dirname( __DIR__ ) . '/data/templates/';
$localOffset = date( 'Z' ) / 60;
$this->setMwGlobals( array(
- 'wgMemc' => new EmptyBagOStuff,
'wgContLang' => $langObj,
'wgLanguageCode' => 'en',
'wgLang' => $langObj,
);
$this->setMwGlobals( array(
- 'wgMemc' => new EmptyBagOStuff(),
'wgAuth' => new StubObject( 'wgAuth', 'AuthPlugin' ),
'wgRequest' => new FauxRequest( array() ),
'wgUser' => self::$users['sysop']->user,
$this->assertFalse( $this->db->tableExists( 'foobarbaz' ) );
$this->assertInternalType( 'int', $res->numRows() );
}
+
+ public function testTransactionIdle() {
+ $db = $this->db;
+
+ $db->setFlag( DBO_TRX );
+ $flagSet = null;
+ $db->onTransactionIdle( function() use ( $db, &$flagSet ) {
+ $flagSet = $db->getFlag( DBO_TRX );
+ } );
+ $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
+ $this->assertTrue( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
+
+ $db->clearFlag( DBO_TRX );
+ $flagSet = null;
+ $db->onTransactionIdle( function() use ( $db, &$flagSet ) {
+ $flagSet = $db->getFlag( DBO_TRX );
+ } );
+ $this->assertFalse( $flagSet, 'DBO_TRX off in callback' );
+ $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
+
+ $db->clearFlag( DBO_TRX );
+ $db->onTransactionIdle( function() use ( $db ) {
+ $db->setFlag( DBO_TRX );
+ } );
+ $this->assertFalse( $db->getFlag( DBO_TRX ), 'DBO_TRX restored to default' );
+ }
}
global $wgJobTypeConf;
parent::setUp();
- $this->setMwGlobals( 'wgMemc', new HashBagOStuff() );
-
if ( $this->getCliArg( 'use-jobqueue' ) ) {
$name = $this->getCliArg( 'use-jobqueue' );
if ( !isset( $wgJobTypeConf[$name] ) ) {
* @covers ResourceLoaderModule::validateScriptFile
*/
public function testValidateScriptFile() {
+ $this->setMwGlobals( 'wgResourceLoaderValidateJS', true );
+
$context = $this->getResourceLoaderContext();
$module = new ResourceLoaderTestModule( array(
'wgContLang' => Language::factory( 'tg' ),
'wgLanguageCode' => 'tg',
'wgDefaultLanguageVariant' => false,
- 'wgMemc' => new EmptyBagOStuff,
'wgRequest' => new FauxRequest( array() ),
'wgUser' => new User,
) );
} );
+ QUnit.test( 'badToken( legacy )', function ( assert ) {
+ QUnit.expect( 2 );
+ var api = new mw.Api( { ajax: { url: '/badTokenLegacy/api.php' } } ),
+ test = this;
+
+ this.server.respondWith( /type=csrf/, sequenceBodies( 200, { 'Content-Type': 'application/json' },
+ [
+ '{ "query": { "tokens": { "csrftoken": "badlegacy" } } }',
+ '{ "query": { "tokens": { "csrftoken": "goodlegacy" } } }'
+ ]
+ ) );
+
+ api.getToken( 'options' )
+ .then( function () {
+ api.badToken( 'options' );
+ return api.getToken( 'options' );
+ } )
+ .then( function ( token ) {
+ assert.equal( token, 'goodlegacy', 'The token' );
+ assert.equal( test.server.requests.length, 2, 'Request made' );
+ } );
+
+ } );
+
QUnit.test( 'postWithToken( tokenType, params )', function ( assert ) {
QUnit.expect( 1 );
var api = new mw.Api( { ajax: { url: '/postWithToken/api.php' } } );