'LonelyPagesPage' => __DIR__ . '/includes/specials/SpecialLonelypages.php',
'LongPagesPage' => __DIR__ . '/includes/specials/SpecialLongpages.php',
'MIMEsearchPage' => __DIR__ . '/includes/specials/SpecialMIMEsearch.php',
- 'MWCallableUpdate' => __DIR__ . '/includes/deferred/CallableUpdate.php',
+ 'MWCallableUpdate' => __DIR__ . '/includes/deferred/MWCallableUpdate.php',
'MWContentSerializationException' => __DIR__ . '/includes/content/ContentHandler.php',
'MWCryptHKDF' => __DIR__ . '/includes/utils/MWCryptHKDF.php',
'MWCryptHash' => __DIR__ . '/includes/utils/MWCryptHash.php',
},
"require-dev": {
"jakub-onderka/php-parallel-lint": "0.9.2",
- "justinrainbow/json-schema": "~1.3",
+ "justinrainbow/json-schema": "~1.6",
"mediawiki/mediawiki-codesniffer": "0.7.2",
"monolog/monolog": "~1.18.2",
"nikic/php-parser": "1.4.1",
$reason,
$nullRevision
];
- $dbw->onTransactionIdle( function () use ( $params, $dbw ) {
- // Keep each single hook handler atomic
- $dbw->setFlag( DBO_TRX ); // flag is automatically reset by DB layer
- Hooks::run( 'TitleMoveComplete', $params );
- } );
+ // Keep each single hook handler atomic
+ DeferredUpdates::addUpdate(
+ new AtomicSectionUpdate(
+ $dbw,
+ __METHOD__,
+ function () use ( $params ) {
+ Hooks::run( 'TitleMoveComplete', $params );
+ }
+ )
+ );
return Status::newGood();
}
return new self( $user, $title, self::DEPRECATED_USAGE_TIMESTAMP, (bool)$checkRights );
}
- /**
- * @deprecated since 1.27 Use WatchedItemStore::resetNotificationTimestamp()
- */
- public function resetNotificationTimestamp( $force = '', $oldid = 0 ) {
- wfDeprecated( __METHOD__, '1.27' );
- if ( $this->checkRights && !$this->user->isAllowed( 'editmywatchlist' ) ) {
- return;
- }
- MediaWikiServices::getInstance()->getWatchedItemStore()->resetNotificationTimestamp(
- $this->user,
- $this->getTitle(),
- $force,
- $oldid
- );
- }
-
- /**
- * @deprecated since 1.27 Use WatchedItemStore::addWatchBatch()
- */
- public static function batchAddWatch( array $items ) {
- wfDeprecated( __METHOD__, '1.27' );
- if ( !$items ) {
- return false;
- }
-
- $targets = [];
- $users = [];
- /** @var WatchedItem $watchedItem */
- foreach ( $items as $watchedItem ) {
- $user = $watchedItem->getUser();
- if ( $watchedItem->checkRights && !$user->isAllowed( 'editmywatchlist' ) ) {
- continue;
- }
- $userId = $user->getId();
- $users[$userId] = $user;
- $targets[$userId][] = $watchedItem->getTitle()->getSubjectPage();
- $targets[$userId][] = $watchedItem->getTitle()->getTalkPage();
- }
-
- $store = MediaWikiServices::getInstance()->getWatchedItemStore();
- $success = true;
- foreach ( $users as $userId => $user ) {
- $success &= $store->addWatchBatchForUser( $user, $targets[$userId] );
- }
-
- return $success;
- }
-
/**
* @deprecated since 1.27 Use User::addWatch()
* @return bool
+++ /dev/null
-<?php
-
-/**
- * Deferrable Update for closure/callback
- */
-class MWCallableUpdate implements DeferrableUpdate, DeferrableCallback {
- /** @var callable */
- private $callback;
- /** @var string */
- private $fname;
-
- /**
- * @param callable $callback
- * @param string $fname Calling method
- */
- public function __construct( callable $callback, $fname = 'unknown' ) {
- $this->callback = $callback;
- $this->fname = $fname;
- }
-
- public function doUpdate() {
- call_user_func( $this->callback );
- }
-
- public function getOrigin() {
- return $this->fname;
- }
-}
--- /dev/null
+<?php
+
+/**
+ * Deferrable Update for closure/callback
+ */
+class MWCallableUpdate implements DeferrableUpdate, DeferrableCallback {
+ /** @var callable */
+ private $callback;
+ /** @var string */
+ private $fname;
+
+ /**
+ * @param callable $callback
+ * @param string $fname Calling method
+ */
+ public function __construct( callable $callback, $fname = 'unknown' ) {
+ $this->callback = $callback;
+ $this->fname = $fname;
+ }
+
+ public function doUpdate() {
+ call_user_func( $this->callback );
+ }
+
+ public function getOrigin() {
+ return $this->fname;
+ }
+}
# Do some cache purges after final commit so that:
# a) Changes are more likely to be seen post-purge
# b) They won't cause rollback of the log publish/update above
- $that = $this;
- $dbw->onTransactionIdle( function () use (
- $that, $reupload, $wikiPage, $newPageContent, $comment, $user, $logEntry, $logId, $descId, $tags
- ) {
- # Update memcache after the commit
- $that->invalidateCache();
-
- $updateLogPage = false;
- if ( $newPageContent ) {
- # New file page; create the description page.
- # There's already a log entry, so don't make a second RC entry
- # CDN and file cache for the description page are purged by doEditContent.
- $status = $wikiPage->doEditContent(
- $newPageContent,
- $comment,
- EDIT_NEW | EDIT_SUPPRESS_RC,
- false,
- $user
- );
-
- if ( isset( $status->value['revision'] ) ) {
- // Associate new page revision id
- $logEntry->setAssociatedRevId( $status->value['revision']->getId() );
- }
- // This relies on the resetArticleID() call in WikiPage::insertOn(),
- // which is triggered on $descTitle by doEditContent() above.
- if ( isset( $status->value['revision'] ) ) {
- /** @var $rev Revision */
- $rev = $status->value['revision'];
- $updateLogPage = $rev->getPage();
- }
- } else {
- # Existing file page: invalidate description page cache
- $wikiPage->getTitle()->invalidateCache();
- $wikiPage->getTitle()->purgeSquid();
- # Allow the new file version to be patrolled from the page footer
- Article::purgePatrolFooterCache( $descId );
- }
-
- # Update associated rev id. This should be done by $logEntry->insert() earlier,
- # but setAssociatedRevId() wasn't called at that point yet...
- $logParams = $logEntry->getParameters();
- $logParams['associated_rev_id'] = $logEntry->getAssociatedRevId();
- $update = [ 'log_params' => LogEntryBase::makeParamBlob( $logParams ) ];
- if ( $updateLogPage ) {
- # Also log page, in case where we just created it above
- $update['log_page'] = $updateLogPage;
- }
- $that->getRepo()->getMasterDB()->update(
- 'logging',
- $update,
- [ 'log_id' => $logId ],
- __METHOD__
- );
- $that->getRepo()->getMasterDB()->insert(
- 'log_search',
- [
- 'ls_field' => 'associated_rev_id',
- 'ls_value' => $logEntry->getAssociatedRevId(),
- 'ls_log_id' => $logId,
- ],
- __METHOD__
- );
+ DeferredUpdates::addUpdate(
+ new AutoCommitUpdate(
+ $dbw,
+ __METHOD__,
+ function () use (
+ $reupload, $wikiPage, $newPageContent, $comment, $user,
+ $logEntry, $logId, $descId, $tags
+ ) {
+ # Update memcache after the commit
+ $this->invalidateCache();
+
+ $updateLogPage = false;
+ if ( $newPageContent ) {
+ # New file page; create the description page.
+ # There's already a log entry, so don't make a second RC entry
+ # CDN and file cache for the description page are purged by doEditContent.
+ $status = $wikiPage->doEditContent(
+ $newPageContent,
+ $comment,
+ EDIT_NEW | EDIT_SUPPRESS_RC,
+ false,
+ $user
+ );
+
+ if ( isset( $status->value['revision'] ) ) {
+ // Associate new page revision id
+ $logEntry->setAssociatedRevId( $status->value['revision']->getId() );
+ }
+ // This relies on the resetArticleID() call in WikiPage::insertOn(),
+ // which is triggered on $descTitle by doEditContent() above.
+ if ( isset( $status->value['revision'] ) ) {
+ /** @var $rev Revision */
+ $rev = $status->value['revision'];
+ $updateLogPage = $rev->getPage();
+ }
+ } else {
+ # Existing file page: invalidate description page cache
+ $wikiPage->getTitle()->invalidateCache();
+ $wikiPage->getTitle()->purgeSquid();
+ # Allow the new file version to be patrolled from the page footer
+ Article::purgePatrolFooterCache( $descId );
+ }
- # Add change tags, if any
- if ( $tags ) {
- $logEntry->setTags( $tags );
- }
+ # Update associated rev id. This should be done by $logEntry->insert() earlier,
+ # but setAssociatedRevId() wasn't called at that point yet...
+ $logParams = $logEntry->getParameters();
+ $logParams['associated_rev_id'] = $logEntry->getAssociatedRevId();
+ $update = [ 'log_params' => LogEntryBase::makeParamBlob( $logParams ) ];
+ if ( $updateLogPage ) {
+ # Also log page, in case where we just created it above
+ $update['log_page'] = $updateLogPage;
+ }
+ $this->getRepo()->getMasterDB()->update(
+ 'logging',
+ $update,
+ [ 'log_id' => $logId ],
+ __METHOD__
+ );
+ $this->getRepo()->getMasterDB()->insert(
+ 'log_search',
+ [
+ 'ls_field' => 'associated_rev_id',
+ 'ls_value' => $logEntry->getAssociatedRevId(),
+ 'ls_log_id' => $logId,
+ ],
+ __METHOD__
+ );
+
+ # Add change tags, if any
+ if ( $tags ) {
+ $logEntry->setTags( $tags );
+ }
- # Uploads can be patrolled
- $logEntry->setIsPatrollable( true );
+ # Uploads can be patrolled
+ $logEntry->setIsPatrollable( true );
- # Now that the log entry is up-to-date, make an RC entry.
- $logEntry->publish( $logId );
+ # Now that the log entry is up-to-date, make an RC entry.
+ $logEntry->publish( $logId );
- # Run hook for other updates (typically more cache purging)
- Hooks::run( 'FileUpload', [ $that, $reupload, !$newPageContent ] );
+ # Run hook for other updates (typically more cache purging)
+ Hooks::run( 'FileUpload', [ $this, $reupload, !$newPageContent ] );
- if ( $reupload ) {
- # Delete old thumbnails
- $that->purgeThumbnails();
- # Remove the old file from the CDN cache
- DeferredUpdates::addUpdate(
- new CdnCacheUpdate( [ $that->getUrl() ] ),
- DeferredUpdates::PRESEND
- );
- } else {
- # Update backlink pages pointing to this title if created
- LinksUpdate::queueRecursiveJobsForTable( $that->getTitle(), 'imagelinks' );
- }
- } );
+ if ( $reupload ) {
+ # Delete old thumbnails
+ $this->purgeThumbnails();
+ # Remove the old file from the CDN cache
+ DeferredUpdates::addUpdate(
+ new CdnCacheUpdate( [ $this->getUrl() ] ),
+ DeferredUpdates::PRESEND
+ );
+ } else {
+ # Update backlink pages pointing to this title if created
+ LinksUpdate::queueRecursiveJobsForTable( $this->getTitle(), 'imagelinks' );
+ }
+ }
+ )
+ );
if ( !$reupload ) {
# This is a new file, so update the image count
* Get a MediaHandler for a given MIME type from the instance cache
*
* @param string $type
- * @return MediaHandler
+ * @return MediaHandler|bool
*/
static function getHandler( $type ) {
global $wgMediaHandlers;
}
// Do secondary updates once the main changes have been committed...
- $that = $this;
- $dbw->onTransactionIdle(
- function () use (
- $dbw, &$that, $revision, &$user, $content, $summary, &$flags,
- $changed, $meta, &$status
- ) {
- // Do per-page updates in a transaction
- $dbw->setFlag( DBO_TRX );
- // Update links tables, site stats, etc.
- $that->doEditUpdates(
- $revision,
- $user,
- [
- 'changed' => $changed,
- 'oldcountable' => $meta['oldCountable'],
- 'oldrevision' => $meta['oldRevision']
- ]
- );
- // Trigger post-save hook
- $params = [ &$that, &$user, $content, $summary, $flags & EDIT_MINOR,
- null, null, &$flags, $revision, &$status, $meta['baseRevId'] ];
- ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $params );
- Hooks::run( 'PageContentSaveComplete', $params );
- }
+ DeferredUpdates::addUpdate(
+ new AtomicSectionUpdate(
+ $dbw,
+ __METHOD__,
+ function () use (
+ $revision, &$user, $content, $summary, &$flags,
+ $changed, $meta, &$status
+ ) {
+ // Update links tables, site stats, etc.
+ $this->doEditUpdates(
+ $revision,
+ $user,
+ [
+ 'changed' => $changed,
+ 'oldcountable' => $meta['oldCountable'],
+ 'oldrevision' => $meta['oldRevision']
+ ]
+ );
+ // Trigger post-save hook
+ $params = [ &$this, &$user, $content, $summary, $flags & EDIT_MINOR,
+ null, null, &$flags, $revision, &$status, $meta['baseRevId'] ];
+ ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $params );
+ Hooks::run( 'PageContentSaveComplete', $params );
+ }
+ )
);
return $status;
$status->value['revision'] = $revision;
// Do secondary updates once the main changes have been committed...
- $that = $this;
- $dbw->onTransactionIdle(
- function () use (
- &$that, $dbw, $revision, &$user, $content, $summary, &$flags, $meta, &$status
- ) {
- // Do per-page updates in a transaction
- $dbw->setFlag( DBO_TRX );
- // Update links, etc.
- $that->doEditUpdates( $revision, $user, [ 'created' => true ] );
- // Trigger post-create hook
- $params = [ &$that, &$user, $content, $summary,
- $flags & EDIT_MINOR, null, null, &$flags, $revision ];
- ContentHandler::runLegacyHooks( 'ArticleInsertComplete', $params );
- Hooks::run( 'PageContentInsertComplete', $params );
- // Trigger post-save hook
- $params = array_merge( $params, [ &$status, $meta['baseRevId'] ] );
- ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $params );
- Hooks::run( 'PageContentSaveComplete', $params );
+ DeferredUpdates::addUpdate(
+ new AtomicSectionUpdate(
+ $dbw,
+ __METHOD__,
+ function () use (
+ $revision, &$user, $content, $summary, &$flags, $meta, &$status
+ ) {
+ // Update links, etc.
+ $this->doEditUpdates( $revision, $user, [ 'created' => true ] );
+ // Trigger post-create hook
+ $params = [ &$this, &$user, $content, $summary,
+ $flags & EDIT_MINOR, null, null, &$flags, $revision ];
+ ContentHandler::runLegacyHooks( 'ArticleInsertComplete', $params );
+ Hooks::run( 'PageContentInsertComplete', $params );
+ // Trigger post-save hook
+ $params = array_merge( $params, [ &$status, $meta['baseRevId'] ] );
+ ContentHandler::runLegacyHooks( 'ArticleSaveComplete', $params );
+ Hooks::run( 'PageContentSaveComplete', $params );
- }
+ }
+ )
);
return $status;
} else {
return false;
}
- switch ( $config['driver'] ) {
- case 'RaggettInternalHHVM':
- self::$instance = new MediaWiki\Tidy\RaggettInternalHHVM( $config );
- break;
- case 'RaggettInternalPHP':
- self::$instance = new MediaWiki\Tidy\RaggettInternalPHP( $config );
- break;
- case 'RaggettExternal':
- self::$instance = new MediaWiki\Tidy\RaggettExternal( $config );
- break;
- case 'Html5Depurate':
- self::$instance = new MediaWiki\Tidy\Html5Depurate( $config );
- break;
- case 'Html5Internal':
- self::$instance = new MediaWiki\Tidy\Html5Internal( $config );
- break;
- case 'disabled':
- return false;
- default:
- throw new MWException( "Invalid tidy driver: \"{$config['driver']}\"" );
- }
+ self::$instance = self::factory( $config );
}
return self::$instance;
}
+ /**
+ * Create a new Tidy driver object from configuration.
+ * @see $wgTidyConfig
+ * @param array $config
+ * @return TidyDriverBase
+ */
+ public static function factory( array $config ) {
+ switch ( $config['driver'] ) {
+ case 'RaggettInternalHHVM':
+ $instance = new MediaWiki\Tidy\RaggettInternalHHVM( $config );
+ break;
+ case 'RaggettInternalPHP':
+ $instance = new MediaWiki\Tidy\RaggettInternalPHP( $config );
+ break;
+ case 'RaggettExternal':
+ $instance = new MediaWiki\Tidy\RaggettExternal( $config );
+ break;
+ case 'Html5Depurate':
+ $instance = new MediaWiki\Tidy\Html5Depurate( $config );
+ break;
+ case 'Html5Internal':
+ $instance = new MediaWiki\Tidy\Html5Internal( $config );
+ break;
+ case 'disabled':
+ return false;
+ default:
+ throw new MWException( "Invalid tidy driver: \"{$config['driver']}\"" );
+ }
+ return $instance;
+ }
+
/**
* Set the driver to be used. This is for testing.
* @param TidyDriverBase|false|null $instance
]
);
- // Clear caches
- $that = $this;
- $dbw->onTransactionIdle( function() use ( $that, $visibilityChangeMap ) {
- $that->doPostCommitUpdates( $visibilityChangeMap );
- } );
+ // Clear caches after commit
+ DeferredUpdates::addCallableUpdate(
+ function () use ( $visibilityChangeMap ) {
+ $this->doPostCommitUpdates( $visibilityChangeMap );
+ },
+ DeferredUpdates::PRESEND
+ );
$dbw->endAtomic( __METHOD__ );
<?php
+use MediaWiki\MediaWikiServices;
/**
* @author Addshore
WatchedItem::fromUserTitle( $user, $title )->getNotificationTimestamp()
);
- WatchedItem::fromUserTitle( $user, $title )->resetNotificationTimestamp();
+ MediaWikiServices::getInstance()->getWatchedItemStore()->resetNotificationTimestamp(
+ $user, $title
+ );
$this->assertNull( WatchedItem::fromUserTitle( $user, $title )->getNotificationTimestamp() );
}
$user = $this->getUser();
$title = Title::newFromText( 'WatchedItemIntegrationTestPage' );
WatchedItem::fromUserTitle( $user, $title )->addWatch();
- WatchedItem::fromUserTitle( $user, $title )->resetNotificationTimestamp();
+ MediaWikiServices::getInstance()->getWatchedItemStore()->resetNotificationTimestamp(
+ $user, $title
+ );
$this->assertEquals(
null,
$this->assertEquals( $timestamp, $item->getNotificationTimestamp() );
}
- /**
- * @dataProvider provideUserTitleTimestamp
- */
- public function testResetNotificationTimestamp( $user, $linkTarget, $timestamp ) {
- $force = 'XXX';
- $oldid = 999;
-
- $store = $this->getMockWatchedItemStore();
- $store->expects( $this->once() )
- ->method( 'resetNotificationTimestamp' )
- ->with( $user, $this->isInstanceOf( Title::class ), $force, $oldid )
- ->will( $this->returnCallback(
- function ( $user, Title $title, $force, $oldid ) use ( $linkTarget ) {
- /** @var LinkTarget $linkTarget */
- $this->assertInstanceOf( 'Title', $title );
- $this->assertSame( $linkTarget->getDBkey(), $title->getDBkey() );
- $this->assertSame( $linkTarget->getFragment(), $title->getFragment() );
- $this->assertSame( $linkTarget->getNamespace(), $title->getNamespace() );
- $this->assertSame( $linkTarget->getText(), $title->getText() );
-
- return true;
- }
- ) );
- $this->setService( 'WatchedItemStore', $store );
-
- $item = new WatchedItem( $user, $linkTarget, $timestamp );
- $item->resetNotificationTimestamp( $force, $oldid );
- }
-
public function testAddWatch() {
$title = Title::newFromText( 'SomeTitle' );
$timestamp = null;
WatchedItem::duplicateEntries( $oldTitle, $newTitle );
}
- public function testBatchAddWatch() {
- $itemOne = new WatchedItem( $this->getMockUser( 1 ), new TitleValue( 0, 'Title1' ), null );
- $itemTwo = new WatchedItem(
- $this->getMockUser( 3 ),
- Title::newFromText( 'Title2' ),
- '20150101010101'
- );
-
- $store = $this->getMockWatchedItemStore();
- $store->expects( $this->exactly( 2 ) )
- ->method( 'addWatchBatchForUser' );
- $store->expects( $this->at( 0 ) )
- ->method( 'addWatchBatchForUser' )
- ->with(
- $itemOne->getUser(),
- [
- $itemOne->getTitle()->getSubjectPage(),
- $itemOne->getTitle()->getTalkPage(),
- ]
- );
- $store->expects( $this->at( 1 ) )
- ->method( 'addWatchBatchForUser' )
- ->with(
- $itemTwo->getUser(),
- [
- $itemTwo->getTitle()->getSubjectPage(),
- $itemTwo->getTitle()->getTalkPage(),
- ]
- );
- $this->setService( 'WatchedItemStore', $store );
-
- WatchedItem::batchAddWatch( [ $itemOne, $itemTwo ] );
- }
-
}