3 namespace MediaWiki\Api
;
5 use MediaWiki\MediaWikiServices
;
6 use MediaWiki\Page\PageIdentity
;
7 use MediaWiki\User\Options\UserOptionsLookup
;
8 use MediaWiki\User\User
;
9 use MediaWiki\User\UserIdentity
;
10 use MediaWiki\Watchlist\WatchedItemStoreInterface
;
11 use MediaWiki\Watchlist\WatchlistManager
;
12 use Wikimedia\ParamValidator\ParamValidator
;
13 use Wikimedia\ParamValidator\TypeDef\ExpiryDef
;
16 * An ApiWatchlistTrait adds class properties and convenience methods for APIs that allow you to
17 * watch a page. This should ONLY be used in API modules that extend ApiBase.
18 * Also, it should not be used in ApiWatch, which has its own special handling.
20 * Note the class-level properties watchlistExpiryEnabled and watchlistMaxDuration must still be
21 * set in the API module's constructor.
26 trait ApiWatchlistTrait
{
28 /** @var bool Whether watchlist expiries are enabled. */
29 private $watchlistExpiryEnabled;
31 /** @var string Relative maximum expiry. */
32 private $watchlistMaxDuration;
34 private WatchlistManager
$watchlistManager;
35 private UserOptionsLookup
$userOptionsLookup;
37 private function initServices() {
38 // @phan-suppress-next-line PhanRedundantCondition Phan trusts the type hints too much
39 if ( isset( $this->watchlistManager
) && isset( $this->userOptionsLookup
) ) {
42 // This trait is used outside of core and therefor fallback to global state - T263904
43 $services = MediaWikiServices
::getInstance();
44 $this->watchlistManager ??
= $services->getWatchlistManager();
45 $this->userOptionsLookup ??
= $services->getUserOptionsLookup();
49 * Get additional allow params specific to watchlisting.
50 * This should be merged in with the result of self::getAllowedParams().
52 * This purposefully does not include the deprecated 'watch' and 'unwatch'
53 * parameters that some APIs still accept.
55 * @param string[] $watchOptions
58 protected function getWatchlistParams( array $watchOptions = [] ): array {
59 if ( !$watchOptions ) {
70 ParamValidator
::PARAM_DEFAULT
=> 'preferences',
71 ParamValidator
::PARAM_TYPE
=> $watchOptions,
75 if ( $this->watchlistExpiryEnabled
) {
76 $result['watchlistexpiry'] = [
77 ParamValidator
::PARAM_TYPE
=> 'expiry',
78 ExpiryDef
::PARAM_MAX
=> $this->watchlistMaxDuration
,
79 ExpiryDef
::PARAM_USE_MAX
=> true,
87 * Set a watch (or unwatch) based the based on a watchlist parameter.
88 * @param string $watch Valid values: 'watch', 'unwatch', 'preferences', 'nochange'
89 * @param PageIdentity $page The page to change
90 * @param User $user The user to set watch/unwatch for
91 * @param string|null $userOption The user option to consider when $watch=preferences
92 * @param string|null $expiry Optional expiry timestamp in any format acceptable to wfTimestamp(),
93 * null will not create expiries, or leave them unchanged should they already exist.
95 protected function setWatch(
99 ?
string $userOption = null,
100 ?
string $expiry = null
102 $value = $this->getWatchlistValue( $watch, $page, $user, $userOption );
103 $this->watchlistManager
->setWatch( $value, $user, $page, $expiry );
107 * Return true if we're to watch the page, false if not.
108 * @param string $watchlist Valid values: 'watch', 'unwatch', 'preferences', 'nochange'
109 * @param PageIdentity $page The page under consideration
110 * @param User $user The user get the value for.
111 * @param string|null $userOption The user option to consider when $watchlist=preferences.
112 * If not set will use watchdefault always and watchcreations if $page doesn't exist.
115 protected function getWatchlistValue(
119 ?
string $userOption = null
121 $this->initServices();
122 $userWatching = $this->watchlistManager
->isWatchedIgnoringRights( $user, $page );
124 switch ( $watchlist ) {
132 // If the user is already watching, don't bother checking
133 if ( $userWatching ) {
136 // If the user is a bot, act as 'nochange' to avoid big watchlists on single users
137 if ( $user->isBot() ) {
138 return $userWatching;
140 // If no user option was passed, use watchdefault and watchcreations
141 if ( $userOption === null ) {
142 return $this->userOptionsLookup
->getBoolOption( $user, 'watchdefault' ) ||
143 ( $this->userOptionsLookup
->getBoolOption( $user, 'watchcreations' ) && !$page->exists() );
146 // Watch the article based on the user preference
147 return $this->userOptionsLookup
->getBoolOption( $user, $userOption );
151 return $userWatching;
156 * Get formatted expiry from the given parameters, or null if no expiry was provided.
157 * @param array $params Request parameters passed to the API.
158 * @return string|null
160 protected function getExpiryFromParams( array $params ): ?
string {
161 $watchlistExpiry = null;
162 if ( $this->watchlistExpiryEnabled
&& isset( $params['watchlistexpiry'] ) ) {
163 $watchlistExpiry = ApiResult
::formatExpiry( $params['watchlistexpiry'] );
166 return $watchlistExpiry;
170 * Get existing expiry from the database.
172 * @param WatchedItemStoreInterface $store
173 * @param PageIdentity $page
174 * @param UserIdentity $user The user to get the expiry for.
175 * @return string|null
177 protected function getWatchlistExpiry(
178 WatchedItemStoreInterface
$store,
182 $watchedItem = $store->getWatchedItem( $user, $page );
184 if ( $watchedItem ) {
185 $expiry = $watchedItem->getExpiry();
187 if ( $expiry !== null ) {
188 return ApiResult
::formatExpiry( $expiry );
196 /** @deprecated class alias since 1.43 */
197 class_alias( ApiWatchlistTrait
::class, 'ApiWatchlistTrait' );