X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fwatcheditem%2FWatchedItemStore.php;h=d6d9ff0eb932e4ab2c21c1b35adef31427c3d75e;hb=1de7117197761961736ea43b237599569eee93a2;hp=f29bd479c14f2498cd3e9bb9227966e0166b3dfd;hpb=5405fd880e914e7d460a9148477688770aeb7d1a;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/watcheditem/WatchedItemStore.php b/includes/watcheditem/WatchedItemStore.php index f29bd479c1..6b0c2aab11 100644 --- a/includes/watcheditem/WatchedItemStore.php +++ b/includes/watcheditem/WatchedItemStore.php @@ -13,10 +13,6 @@ use Wikimedia\Rdbms\LoadBalancer; * Database interaction & caching * TODO caching should be factored out into a CachingWatchedItemStore class * - * Uses database because this uses User::isAnon - * - * @group Database - * * @author Addshore * @since 1.27 */ @@ -55,6 +51,11 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac */ private $revisionGetTimestampFromIdCallback; + /** + * @var int + */ + private $updateRowsPerQuery; + /** * @var StatsdDataFactoryInterface */ @@ -64,20 +65,28 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac * @param LoadBalancer $loadBalancer * @param HashBagOStuff $cache * @param ReadOnlyMode $readOnlyMode + * @param int $updateRowsPerQuery */ public function __construct( LoadBalancer $loadBalancer, HashBagOStuff $cache, - ReadOnlyMode $readOnlyMode + ReadOnlyMode $readOnlyMode, + $updateRowsPerQuery ) { $this->loadBalancer = $loadBalancer; $this->cache = $cache; $this->readOnlyMode = $readOnlyMode; $this->stats = new NullStatsdDataFactory(); - $this->deferredUpdatesAddCallableUpdateCallback = [ 'DeferredUpdates', 'addCallableUpdate' ]; - $this->revisionGetTimestampFromIdCallback = [ 'Revision', 'getTimestampFromId' ]; + $this->deferredUpdatesAddCallableUpdateCallback = + [ DeferredUpdates::class, 'addCallableUpdate' ]; + $this->revisionGetTimestampFromIdCallback = + [ Revision::class, 'getTimestampFromId' ]; + $this->updateRowsPerQuery = $updateRowsPerQuery; } + /** + * @param StatsdDataFactoryInterface $stats + */ public function setStatsdDataFactory( StatsdDataFactoryInterface $stats ) { $this->stats = $stats; } @@ -212,6 +221,56 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac return $this->loadBalancer->getConnectionRef( $dbIndex, [ 'watchlist' ] ); } + /** + * Deletes ALL watched items for the given user when under + * $updateRowsPerQuery entries exist. + * + * @since 1.30 + * + * @param User $user + * + * @return bool true on success, false when too many items are watched + */ + public function clearUserWatchedItems( User $user ) { + if ( $this->countWatchedItems( $user ) > $this->updateRowsPerQuery ) { + return false; + } + + $dbw = $this->loadBalancer->getConnectionRef( DB_MASTER ); + $dbw->delete( + 'watchlist', + [ 'wl_user' => $user->getId() ], + __METHOD__ + ); + $this->uncacheAllItemsForUser( $user ); + + return true; + } + + private function uncacheAllItemsForUser( User $user ) { + $userId = $user->getId(); + foreach ( $this->cacheIndex as $ns => $dbKeyIndex ) { + foreach ( $dbKeyIndex as $dbKey => $userIndex ) { + if ( array_key_exists( $userId, $userIndex ) ) { + $this->cache->delete( $userIndex[$userId] ); + unset( $this->cacheIndex[$ns][$dbKey][$userId] ); + } + } + } + + // Cleanup empty cache keys + foreach ( $this->cacheIndex as $ns => $dbKeyIndex ) { + foreach ( $dbKeyIndex as $dbKey => $userIndex ) { + if ( empty( $this->cacheIndex[$ns][$dbKey] ) ) { + unset( $this->cacheIndex[$ns][$dbKey] ); + } + } + if ( empty( $this->cacheIndex[$ns] ) ) { + unset( $this->cacheIndex[$ns] ); + } + } + } + /** * Queues a job that will clear the users watchlist using the Job Queue. * @@ -241,6 +300,8 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.31 + * @param User $user + * @return int */ public function countWatchedItems( User $user ) { $dbr = $this->getConnectionRef( DB_REPLICA ); @@ -258,6 +319,8 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param LinkTarget $target + * @return int */ public function countWatchers( LinkTarget $target ) { $dbr = $this->getConnectionRef( DB_REPLICA ); @@ -276,6 +339,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param LinkTarget $target + * @param string|int $threshold + * @return int */ public function countVisitingWatchers( LinkTarget $target, $threshold ) { $dbr = $this->getConnectionRef( DB_REPLICA ); @@ -297,6 +363,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param LinkTarget[] $targets + * @param array $options + * @return array */ public function countWatchersMultiple( array $targets, array $options = [] ) { $dbOptions = [ 'GROUP BY' => [ 'wl_namespace', 'wl_title' ] ]; @@ -330,11 +399,19 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param array $targetsWithVisitThresholds + * @param int|null $minimumWatchers + * @return array */ public function countVisitingWatchersMultiple( array $targetsWithVisitThresholds, $minimumWatchers = null ) { + if ( $targetsWithVisitThresholds === [] ) { + // No titles requested => no results returned + return []; + } + $dbr = $this->getConnectionRef( DB_REPLICA ); $conds = $this->getVisitingWatchersCondition( $dbr, $targetsWithVisitThresholds ); @@ -410,6 +487,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param User $user + * @param LinkTarget $target + * @return bool */ public function getWatchedItem( User $user, LinkTarget $target ) { if ( $user->isAnon() ) { @@ -427,6 +507,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param User $user + * @param LinkTarget $target + * @return WatchedItem|bool */ public function loadWatchedItem( User $user, LinkTarget $target ) { // Only loggedin user can have a watchlist @@ -458,6 +541,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param User $user + * @param array $options + * @return WatchedItem[] */ public function getWatchedItemsForUser( User $user, array $options = [] ) { $options += [ 'forWrite' => false ]; @@ -499,6 +585,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param User $user + * @param LinkTarget $target + * @return bool */ public function isWatched( User $user, LinkTarget $target ) { return (bool)$this->getWatchedItem( $user, $target ); @@ -506,6 +595,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param User $user + * @param LinkTarget[] $targets + * @return array */ public function getNotificationTimestampsBatch( User $user, array $targets ) { $timestamps = []; @@ -555,6 +647,8 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param User $user + * @param LinkTarget $target */ public function addWatch( User $user, LinkTarget $target ) { $this->addWatchBatchForUser( $user, [ $target ] ); @@ -562,6 +656,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param User $user + * @param LinkTarget[] $targets + * @return bool */ public function addWatchBatchForUser( User $user, array $targets ) { if ( $this->readOnlyMode->isReadOnly() ) { @@ -601,7 +698,7 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac } // Update process cache to ensure skin doesn't claim that the current // page is unwatched in the response of action=watch itself (T28292). - // This would otherwise be re-queried from a slave by isWatched(). + // This would otherwise be re-queried from a replica by isWatched(). foreach ( $items as $item ) { $this->cache( $item ); } @@ -611,6 +708,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param User $user + * @param LinkTarget $target + * @return bool */ public function removeWatch( User $user, LinkTarget $target ) { // Only logged in user can have a watchlist @@ -635,6 +735,10 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param User $user + * @param string|int $timestamp + * @param LinkTarget[] $targets + * @return bool */ public function setNotificationTimestampsForUser( User $user, $timestamp, array $targets = [] ) { // Only loggedin user can have a watchlist @@ -666,8 +770,34 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac return $success; } + public function resetAllNotificationTimestampsForUser( User $user ) { + // Only loggedin user can have a watchlist + if ( $user->isAnon() ) { + return; + } + + // If the page is watched by the user (or may be watched), update the timestamp + $job = new ClearWatchlistNotificationsJob( + $user->getUserPage(), + [ 'userId' => $user->getId(), 'casTime' => time() ] + ); + + // Try to run this post-send + // Calls DeferredUpdates::addCallableUpdate in normal operation + call_user_func( + $this->deferredUpdatesAddCallableUpdateCallback, + function () use ( $job ) { + $job->run(); + } + ); + } + /** * @since 1.27 + * @param User $editor + * @param LinkTarget $target + * @param string|int $timestamp + * @return int[] */ public function updateNotificationTimestamp( User $editor, LinkTarget $target, $timestamp ) { $dbw = $this->getConnectionRef( DB_MASTER ); @@ -724,6 +854,11 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param User $user + * @param Title $title + * @param string $force + * @param int $oldid + * @return bool */ public function resetNotificationTimestamp( User $user, Title $title, $force = '', $oldid = 0 ) { // Only loggedin user can have a watchlist @@ -813,6 +948,9 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param User $user + * @param int|null $unreadLimit + * @return int|bool */ public function countUnreadNotifications( User $user, $unreadLimit = null ) { $queryOptions = []; @@ -846,6 +984,8 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param LinkTarget $oldTarget + * @param LinkTarget $newTarget */ public function duplicateAllAssociatedEntries( LinkTarget $oldTarget, LinkTarget $newTarget ) { $oldTarget = Title::newFromLinkTarget( $oldTarget ); @@ -857,6 +997,8 @@ class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterfac /** * @since 1.27 + * @param LinkTarget $oldTarget + * @param LinkTarget $newTarget */ public function duplicateEntry( LinkTarget $oldTarget, LinkTarget $newTarget ) { $dbw = $this->getConnectionRef( DB_MASTER );