Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / api / ApiWatchlistTrait.php
blob1ee75a906d764f650ea13bc30574b92ce1a51c6c
1 <?php
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;
15 /**
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.
23 * @ingroup API
24 * @since 1.35
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 ) ) {
40 return;
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();
48 /**
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
56 * @return array
58 protected function getWatchlistParams( array $watchOptions = [] ): array {
59 if ( !$watchOptions ) {
60 $watchOptions = [
61 'watch',
62 'unwatch',
63 'preferences',
64 'nochange',
68 $result = [
69 'watchlist' => [
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,
83 return $result;
86 /**
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(
96 string $watch,
97 PageIdentity $page,
98 User $user,
99 ?string $userOption = null,
100 ?string $expiry = null
101 ): void {
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.
113 * @return bool
115 protected function getWatchlistValue(
116 string $watchlist,
117 PageIdentity $page,
118 User $user,
119 ?string $userOption = null
120 ): bool {
121 $this->initServices();
122 $userWatching = $this->watchlistManager->isWatchedIgnoringRights( $user, $page );
124 switch ( $watchlist ) {
125 case 'watch':
126 return true;
128 case 'unwatch':
129 return false;
131 case 'preferences':
132 // If the user is already watching, don't bother checking
133 if ( $userWatching ) {
134 return true;
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 );
149 // case 'nochange':
150 default:
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,
179 PageIdentity $page,
180 UserIdentity $user
181 ): ?string {
182 $watchedItem = $store->getWatchedItem( $user, $page );
184 if ( $watchedItem ) {
185 $expiry = $watchedItem->getExpiry();
187 if ( $expiry !== null ) {
188 return ApiResult::formatExpiry( $expiry );
192 return null;
196 /** @deprecated class alias since 1.43 */
197 class_alias( ApiWatchlistTrait::class, 'ApiWatchlistTrait' );