From: addshore Date: Wed, 4 May 2016 15:49:27 +0000 (+0100) Subject: add setNotificationTimestampsForUser to WatchedItemStore X-Git-Tag: 1.31.0-rc.0~6936^2 X-Git-Url: https://git.cyclocoop.org/%28%28?a=commitdiff_plain;ds=sidebyside;h=eacec0a22345c6f945908a12b8556797cfd3c52b;p=lhc%2Fweb%2Fwiklou.git add setNotificationTimestampsForUser to WatchedItemStore Bug: T134387 Change-Id: Ia6abe7687b51aabe67e8461375075692db28c9a2 --- diff --git a/includes/WatchedItemStore.php b/includes/WatchedItemStore.php index eb652ce118..f0619d69f5 100644 --- a/includes/WatchedItemStore.php +++ b/includes/WatchedItemStore.php @@ -145,16 +145,28 @@ class WatchedItemStore implements StatsdAwareInterface { } private function uncacheLinkTarget( LinkTarget $target ) { + $this->stats->increment( 'WatchedItemStore.uncacheLinkTarget' ); if ( !isset( $this->cacheIndex[$target->getNamespace()][$target->getDBkey()] ) ) { return; } - $this->stats->increment( 'WatchedItemStore.uncacheLinkTarget' ); foreach ( $this->cacheIndex[$target->getNamespace()][$target->getDBkey()] as $key ) { $this->stats->increment( 'WatchedItemStore.uncacheLinkTarget.items' ); $this->cache->delete( $key ); } } + private function uncacheUser( User $user ) { + $this->stats->increment( 'WatchedItemStore.uncacheUser' ); + foreach ( $this->cacheIndex as $ns => $dbKeyArray ) { + foreach ( $dbKeyArray as $dbKey => $userArray ) { + if ( isset( $userArray[$user->getId()] ) ) { + $this->stats->increment( 'WatchedItemStore.uncacheUser.items' ); + $this->cache->delete( $userArray[$user->getId()] ); + } + } + } + } + /** * @param User $user * @param LinkTarget $target @@ -667,6 +679,41 @@ class WatchedItemStore implements StatsdAwareInterface { return $success; } + /** + * @param User $user The user to set the timestamp for + * @param string $timestamp Set the update timestamp to this value + * @param LinkTarget[] $targets List of targets to update. Default to all targets + * + * @return bool success + */ + public function setNotificationTimestampsForUser( User $user, $timestamp, array $targets = [] ) { + // Only loggedin user can have a watchlist + if ( $user->isAnon() ) { + return false; + } + + $dbw = $this->getConnection( DB_MASTER ); + + $conds = [ 'wl_user' => $user->getId() ]; + if ( $targets ) { + $batch = new LinkBatch( $targets ); + $conds[] = $batch->constructSet( 'wl', $dbw ); + } + + $success = $dbw->update( + 'watchlist', + [ 'wl_notificationtimestamp' => $dbw->timestamp( $timestamp ) ], + $conds, + __METHOD__ + ); + + $this->reuseConnection( $dbw ); + + $this->uncacheUser( $user ); + + return $success; + } + /** * @param User $editor The editor that triggered the update. Their notification * timestamp will not be updated(they have already seen it) diff --git a/includes/api/ApiSetNotificationTimestamp.php b/includes/api/ApiSetNotificationTimestamp.php index ea52e1433a..a299e87797 100644 --- a/includes/api/ApiSetNotificationTimestamp.php +++ b/includes/api/ApiSetNotificationTimestamp.php @@ -24,6 +24,7 @@ * * @file */ +use MediaWiki\MediaWikiServices; /** * API interface for setting the wl_notificationtimestamp field @@ -98,13 +99,14 @@ class ApiSetNotificationTimestamp extends ApiBase { } } + $watchedItemStore = MediaWikiServices::getInstance()->getWatchedItemStore(); $apiResult = $this->getResult(); $result = []; if ( $params['entirewatchlist'] ) { // Entire watchlist mode: Just update the thing and return a success indicator - $dbw->update( 'watchlist', [ 'wl_notificationtimestamp' => $timestamp ], - [ 'wl_user' => $user->getId() ], - __METHOD__ + $watchedItemStore->setNotificationTimestampsForUser( + $user, + $timestamp ); $result['notificationtimestamp'] = is_null( $timestamp ) @@ -133,14 +135,15 @@ class ApiSetNotificationTimestamp extends ApiBase { if ( $pageSet->getTitles() ) { // Now process the valid titles - $lb = new LinkBatch( $pageSet->getTitles() ); - $dbw->update( 'watchlist', [ 'wl_notificationtimestamp' => $timestamp ], - [ 'wl_user' => $user->getId(), $lb->constructSet( 'wl', $dbw ) ], - __METHOD__ + $watchedItemStore->setNotificationTimestampsForUser( + $user, + $timestamp, + $pageSet->getTitles() ); // Query the results of our update $timestamps = []; + $lb = new LinkBatch( $pageSet->getTitles() ); $res = $dbw->select( 'watchlist', [ 'wl_namespace', 'wl_title', 'wl_notificationtimestamp' ], diff --git a/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php b/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php index f34af6113d..61b62aa66b 100644 --- a/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php +++ b/tests/phpunit/includes/WatchedItemStoreIntegrationTest.php @@ -106,7 +106,7 @@ class WatchedItemStoreIntegrationTest extends MediaWikiTestCase { ); } - public function testUpdateAndResetNotificationTimestamp() { + public function testUpdateResetAndSetNotificationTimestamp() { $user = $this->getUser(); $otherUser = ( new TestUser( 'WatchedItemStoreIntegrationTestUser_otherUser' ) )->getUser(); $title = Title::newFromText( 'WatchedItemStoreIntegrationTestPage' ); @@ -172,6 +172,24 @@ class WatchedItemStoreIntegrationTest extends MediaWikiTestCase { [ [ $title, '20150202020202' ] ], $initialVisitingWatchers + 1 ) ); + + // setNotificationTimestampsForUser specifying a title + $this->assertTrue( + $store->setNotificationTimestampsForUser( $user, '20200202020202', [ $title ] ) + ); + $this->assertEquals( + '20200202020202', + $store->getWatchedItem( $user, $title )->getNotificationTimestamp() + ); + + // setNotificationTimestampsForUser not specifying a title + $this->assertTrue( + $store->setNotificationTimestampsForUser( $user, '20210202020202' ) + ); + $this->assertEquals( + '20210202020202', + $store->getWatchedItem( $user, $title )->getNotificationTimestamp() + ); } public function testDuplicateAllAssociatedEntries() { diff --git a/tests/phpunit/includes/WatchedItemStoreUnitTest.php b/tests/phpunit/includes/WatchedItemStoreUnitTest.php index 6c4a6f09c6..2d2e726c83 100644 --- a/tests/phpunit/includes/WatchedItemStoreUnitTest.php +++ b/tests/phpunit/includes/WatchedItemStoreUnitTest.php @@ -2366,6 +2366,81 @@ class WatchedItemStoreUnitTest extends MediaWikiTestCase { ScopedCallback::consume( $scopedOverrideRevision ); } + public function testSetNotificationTimestampsForUser_anonUser() { + $store = $this->newWatchedItemStore( + $this->getMockLoadBalancer( $this->getMockDb() ), + $this->getMockCache() + ); + $this->assertFalse( $store->setNotificationTimestampsForUser( $this->getAnonUser(), '' ) ); + } + + public function testSetNotificationTimestampsForUser_allRows() { + $user = $this->getMockNonAnonUserWithId( 1 ); + $timestamp = '20100101010101'; + + $mockDb = $this->getMockDb(); + $mockDb->expects( $this->once() ) + ->method( 'update' ) + ->with( + 'watchlist', + [ 'wl_notificationtimestamp' => 'TS' . $timestamp . 'TS' ], + [ 'wl_user' => 1 ] + ) + ->will( $this->returnValue( true ) ); + $mockDb->expects( $this->exactly( 1 ) ) + ->method( 'timestamp' ) + ->will( $this->returnCallback( function( $value ) { + return 'TS' . $value . 'TS'; + } ) ); + + $store = $this->newWatchedItemStore( + $this->getMockLoadBalancer( $mockDb ), + $this->getMockCache() + ); + + $this->assertTrue( + $store->setNotificationTimestampsForUser( $user, $timestamp ) + ); + } + + public function testSetNotificationTimestampsForUser_specificTargets() { + $user = $this->getMockNonAnonUserWithId( 1 ); + $timestamp = '20100101010101'; + $targets = [ new TitleValue( 0, 'Foo' ), new TitleValue( 0, 'Bar' ) ]; + + $mockDb = $this->getMockDb(); + $mockDb->expects( $this->once() ) + ->method( 'update' ) + ->with( + 'watchlist', + [ 'wl_notificationtimestamp' => 'TS' . $timestamp . 'TS' ], + [ 'wl_user' => 1, 0 => 'makeWhereFrom2d return value' ] + ) + ->will( $this->returnValue( true ) ); + $mockDb->expects( $this->exactly( 1 ) ) + ->method( 'timestamp' ) + ->will( $this->returnCallback( function( $value ) { + return 'TS' . $value . 'TS'; + } ) ); + $mockDb->expects( $this->once() ) + ->method( 'makeWhereFrom2d' ) + ->with( + [ [ 'Foo' => 1, 'Bar' => 1 ] ], + $this->isType( 'string' ), + $this->isType( 'string' ) + ) + ->will( $this->returnValue( 'makeWhereFrom2d return value' ) ); + + $store = $this->newWatchedItemStore( + $this->getMockLoadBalancer( $mockDb ), + $this->getMockCache() + ); + + $this->assertTrue( + $store->setNotificationTimestampsForUser( $user, $timestamp, $targets ) + ); + } + public function testUpdateNotificationTimestamp_watchersExist() { $mockDb = $this->getMockDb(); $mockDb->expects( $this->once() )