Merge "mw.Feedback: If the message is posted remotely, link the title correctly"
[lhc/web/wiklou.git] / includes / watcheditem / WatchedItemStore.php
index 60d8b76..d6d9ff0 100644 (file)
@@ -7,11 +7,11 @@ use MediaWiki\MediaWikiServices;
 use Wikimedia\Assert\Assert;
 use Wikimedia\ScopedCallback;
 use Wikimedia\Rdbms\LoadBalancer;
-use Wikimedia\Rdbms\DBUnexpectedError;
 
 /**
  * Storage layer class for WatchedItems.
- * Database interaction.
+ * Database interaction & caching
+ * TODO caching should be factored out into a CachingWatchedItemStore class
  *
  * Uses database because this uses User::isAnon
  *
@@ -20,10 +20,7 @@ use Wikimedia\Rdbms\DBUnexpectedError;
  * @author Addshore
  * @since 1.27
  */
-class WatchedItemStore implements StatsdAwareInterface {
-
-       const SORT_DESC = 'DESC';
-       const SORT_ASC = 'ASC';
+class WatchedItemStore implements WatchedItemStoreInterface, StatsdAwareInterface {
 
        /**
         * @var LoadBalancer
@@ -77,10 +74,13 @@ class WatchedItemStore implements StatsdAwareInterface {
                $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' ];
        }
 
+       /**
+        * @param StatsdDataFactoryInterface $stats
+        */
        public function setStatsdDataFactory( StatsdDataFactoryInterface $stats ) {
                $this->stats = $stats;
        }
@@ -216,11 +216,35 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * Count the number of individual items that are watched by the user.
-        * If a subject and corresponding talk page are watched this will return 2.
+        * Queues a job that will clear the users watchlist using the Job Queue.
         *
-        * @param User $user
+        * @since 1.31
         *
+        * @param User $user
+        */
+       public function clearUserWatchedItemsUsingJobQueue( User $user ) {
+               $job = ClearUserWatchlistJob::newForUser( $user, $this->getMaxId() );
+               // TODO inject me.
+               JobQueueGroup::singleton()->push( $job );
+       }
+
+       /**
+        * @since 1.31
+        * @return int The maximum current wl_id
+        */
+       public function getMaxId() {
+               $dbr = $this->getConnectionRef( DB_REPLICA );
+               return (int)$dbr->selectField(
+                       'watchlist',
+                       'MAX(wl_id)',
+                       '',
+                       __METHOD__
+               );
+       }
+
+       /**
+        * @since 1.31
+        * @param User $user
         * @return int
         */
        public function countWatchedItems( User $user ) {
@@ -238,8 +262,8 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
+        * @since 1.27
         * @param LinkTarget $target
-        *
         * @return int
         */
        public function countWatchers( LinkTarget $target ) {
@@ -258,14 +282,10 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * Number of page watchers who also visited a "recent" edit
-        *
+        * @since 1.27
         * @param LinkTarget $target
-        * @param mixed $threshold timestamp accepted by wfTimestamp
-        *
+        * @param string|int $threshold
         * @return int
-        * @throws DBUnexpectedError
-        * @throws MWException
         */
        public function countVisitingWatchers( LinkTarget $target, $threshold ) {
                $dbr = $this->getConnectionRef( DB_REPLICA );
@@ -286,13 +306,10 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
+        * @since 1.27
         * @param LinkTarget[] $targets
-        * @param array $options Allowed keys:
-        *        'minimumWatchers' => int
-        *
-        * @return array multi dimensional like $return[$namespaceId][$titleString] = int $watchers
-        *         All targets will be present in the result. 0 either means no watchers or the number
-        *         of watchers was below the minimumWatchers option if passed.
+        * @param array $options
+        * @return array
         */
        public function countWatchersMultiple( array $targets, array $options = [] ) {
                $dbOptions = [ 'GROUP BY' => [ 'wl_namespace', 'wl_title' ] ];
@@ -325,19 +342,10 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * Number of watchers of each page who have visited recent edits to that page
-        *
-        * @param array $targetsWithVisitThresholds array of pairs (LinkTarget $target, mixed $threshold),
-        *        $threshold is:
-        *        - a timestamp of the recent edit if $target exists (format accepted by wfTimestamp)
-        *        - null if $target doesn't exist
+        * @since 1.27
+        * @param array $targetsWithVisitThresholds
         * @param int|null $minimumWatchers
-        * @return array multi-dimensional like $return[$namespaceId][$titleString] = $watchers,
-        *         where $watchers is an int:
-        *         - if the page exists, number of users watching who have visited the page recently
-        *         - if the page doesn't exist, number of users that have the page on their watchlist
-        *         - 0 means there are no visiting watchers or their number is below the minimumWatchers
-        *         option (if passed).
+        * @return array
         */
        public function countVisitingWatchersMultiple(
                array $targetsWithVisitThresholds,
@@ -417,12 +425,10 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * Get an item (may be cached)
-        *
+        * @since 1.27
         * @param User $user
         * @param LinkTarget $target
-        *
-        * @return WatchedItem|false
+        * @return bool
         */
        public function getWatchedItem( User $user, LinkTarget $target ) {
                if ( $user->isAnon() ) {
@@ -439,12 +445,10 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * Loads an item from the db
-        *
+        * @since 1.27
         * @param User $user
         * @param LinkTarget $target
-        *
-        * @return WatchedItem|false
+        * @return bool
         */
        public function loadWatchedItem( User $user, LinkTarget $target ) {
                // Only loggedin user can have a watchlist
@@ -475,12 +479,9 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
+        * @since 1.27
         * @param User $user
-        * @param array $options Allowed keys:
-        *        'forWrite' => bool defaults to false
-        *        'sort' => string optional sorting by namespace ID and title
-        *                     one of the self::SORT_* constants
-        *
+        * @param array $options
         * @return WatchedItem[]
         */
        public function getWatchedItemsForUser( User $user, array $options = [] ) {
@@ -522,11 +523,9 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * Must be called separately for Subject & Talk namespaces
-        *
+        * @since 1.27
         * @param User $user
         * @param LinkTarget $target
-        *
         * @return bool
         */
        public function isWatched( User $user, LinkTarget $target ) {
@@ -534,13 +533,10 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
+        * @since 1.27
         * @param User $user
         * @param LinkTarget[] $targets
-        *
-        * @return array multi-dimensional like $return[$namespaceId][$titleString] = $timestamp,
-        *         where $timestamp is:
-        *         - string|null value of wl_notificationtimestamp,
-        *         - false if $target is not watched by $user.
+        * @return array
         */
        public function getNotificationTimestampsBatch( User $user, array $targets ) {
                $timestamps = [];
@@ -589,8 +585,7 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * Must be called separately for Subject & Talk namespaces
-        *
+        * @since 1.27
         * @param User $user
         * @param LinkTarget $target
         */
@@ -599,10 +594,10 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
+        * @since 1.27
         * @param User $user
         * @param LinkTarget[] $targets
-        *
-        * @return bool success
+        * @return bool
         */
        public function addWatchBatchForUser( User $user, array $targets ) {
                if ( $this->readOnlyMode->isReadOnly() ) {
@@ -651,15 +646,10 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * Removes the an entry for the User watching the LinkTarget
-        * Must be called separately for Subject & Talk namespaces
-        *
+        * @since 1.27
         * @param User $user
         * @param LinkTarget $target
-        *
-        * @return bool success
-        * @throws DBUnexpectedError
-        * @throws MWException
+        * @return bool
         */
        public function removeWatch( User $user, LinkTarget $target ) {
                // Only logged in user can have a watchlist
@@ -683,11 +673,11 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * @param User $user The user to set the timestamp for
-        * @param string|null $timestamp Set the update timestamp to this value
-        * @param LinkTarget[] $targets List of targets to update. Default to all targets
-        *
-        * @return bool success
+        * @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
@@ -720,12 +710,11 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * @param User $editor The editor that triggered the update. Their notification
-        *  timestamp will not be updated(they have already seen it)
-        * @param LinkTarget $target The target to update timestamps for
-        * @param string $timestamp Set the update timestamp to this value
-        *
-        * @return int[] Array of user IDs the timestamp has been updated for
+        * @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 );
@@ -781,16 +770,12 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * Reset the notification timestamp of this entry
-        *
+        * @since 1.27
         * @param User $user
         * @param Title $title
-        * @param string $force Whether to force the write query to be executed even if the
-        *    page is not watched or the notification timestamp is already NULL.
-        *    'force' in order to force
-        * @param int $oldid The revision id being viewed. If not given or 0, latest revision is assumed.
-        *
-        * @return bool success
+        * @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
@@ -879,11 +864,10 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
+        * @since 1.27
         * @param User $user
-        * @param int $unreadLimit
-        *
-        * @return int|bool The number of unread notifications
-        *                  true if greater than or equal to $unreadLimit
+        * @param int|null $unreadLimit
+        * @return int|bool
         */
        public function countUnreadNotifications( User $user, $unreadLimit = null ) {
                $queryOptions = [];
@@ -916,11 +900,7 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * Check if the given title already is watched by the user, and if so
-        * add a watch for the new title.
-        *
-        * To be used for page renames and such.
-        *
+        * @since 1.27
         * @param LinkTarget $oldTarget
         * @param LinkTarget $newTarget
         */
@@ -933,12 +913,7 @@ class WatchedItemStore implements StatsdAwareInterface {
        }
 
        /**
-        * Check if the given title already is watched by the user, and if so
-        * add a watch for the new title.
-        *
-        * To be used for page renames and such.
-        * This must be called separately for Subject and Talk pages
-        *
+        * @since 1.27
         * @param LinkTarget $oldTarget
         * @param LinkTarget $newTarget
         */