MediaWiki master
WatchAction.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\Actions;
24
43
49class WatchAction extends FormAction {
50
53
55 protected $expiryFormFieldName = 'expiry';
56
58 protected $watchedItem = false;
59
60 private WatchlistManager $watchlistManager;
61 private UserOptionsLookup $userOptionsLookup;
62
72 public function __construct(
73 Article $article,
75 WatchlistManager $watchlistManager,
76 WatchedItemStoreInterface $watchedItemStore,
77 UserOptionsLookup $userOptionsLookup
78 ) {
79 parent::__construct( $article, $context );
80 $this->watchlistExpiry = $this->getContext()->getConfig()->get( MainConfigNames::WatchlistExpiry );
81 if ( $this->watchlistExpiry ) {
82 // The watchedItem is only used in this action's form if $wgWatchlistExpiry is enabled.
83 $this->watchedItem = $watchedItemStore->getWatchedItem(
84 $this->getUser(),
85 $this->getTitle()
86 );
87 }
88 $this->watchlistManager = $watchlistManager;
89 $this->userOptionsLookup = $userOptionsLookup;
90 }
91
93 public function getName() {
94 return 'watch';
95 }
96
98 public function requiresUnblock() {
99 return false;
100 }
101
103 protected function getDescription() {
104 return '';
105 }
106
108 public function onSubmit( $data ) {
109 // Even though we're never unwatching here, use WatchlistManager::setWatch()
110 // because it also checks for changed expiry.
111 $result = $this->watchlistManager->setWatch(
112 true,
113 $this->getAuthority(),
114 $this->getTitle(),
115 $this->getRequest()->getVal( 'wp' . $this->expiryFormFieldName )
116 );
117
118 return Status::wrap( $result );
119 }
120
127 protected function checkCanExecute( User $user ) {
128 if ( !$user->isRegistered()
129 || ( $user->isTemp() && !$user->isAllowed( 'editmywatchlist' ) )
130 ) {
131 throw new UserNotLoggedIn( 'watchlistanontext', 'watchnologin' );
132 }
133
134 parent::checkCanExecute( $user );
135 }
136
138 public function getRestriction() {
139 return 'editmywatchlist';
140 }
141
143 protected function usesOOUI() {
144 return true;
145 }
146
148 protected function getFormFields() {
149 // If watchlist expiry is not enabled, return a simple confirmation message.
150 if ( !$this->watchlistExpiry ) {
151 return [
152 'intro' => [
153 'type' => 'info',
154 'raw' => true,
155 'default' => $this->msg( 'confirm-watch-top' )->parse(),
156 ],
157 ];
158 }
159
160 // Otherwise, use a select-list of expiries, where the default is the user's
161 // preferred expiry time (or the existing watch duration if already temporarily watched).
162 $default = $this->userOptionsLookup->getOption( $this->getUser(), 'watchstar-expiry' );
163 $expiryOptions = static::getExpiryOptions( $this->getContext(), $this->watchedItem, $default );
164 return [
165 $this->expiryFormFieldName => [
166 'type' => 'select',
167 'label-message' => 'confirm-watch-label',
168 'options' => $expiryOptions['options'],
169 'default' => $expiryOptions['default'],
170 ]
171 ];
172 }
173
186 public static function getExpiryOptions(
187 MessageLocalizer $msgLocalizer,
189 string $defaultExpiry = 'infinite'
190 ) {
191 $expiryOptions = self::getExpiryOptionsFromMessage( $msgLocalizer );
192
193 if ( !in_array( $defaultExpiry, $expiryOptions ) ) {
194 $expiryOptions = array_merge( [ $defaultExpiry => $defaultExpiry ], $expiryOptions );
195 }
196
197 if ( $watchedItem instanceof WatchedItem && $watchedItem->getExpiry() ) {
198 // If it's already being temporarily watched, add the existing expiry as an option in the dropdown.
199 $currentExpiry = $watchedItem->getExpiry( TS_ISO_8601 );
200 $daysLeft = $watchedItem->getExpiryInDaysText( $msgLocalizer, true );
201 $expiryOptions = array_merge( [ $daysLeft => $currentExpiry ], $expiryOptions );
202
203 // Always preselect the existing expiry.
204 $defaultExpiry = $currentExpiry;
205 }
206
207 return [
208 'options' => $expiryOptions,
209 'default' => $defaultExpiry,
210 ];
211 }
212
222 public static function getExpiryOptionsFromMessage(
223 MessageLocalizer $msgLocalizer, ?string $lang = null
224 ): array {
225 $expiryOptionsMsg = $msgLocalizer->msg( 'watchlist-expiry-options' );
226 $optionsText = !$lang ? $expiryOptionsMsg->text() : $expiryOptionsMsg->inLanguage( $lang )->text();
228 $optionsText
229 );
230
231 $expiryOptions = [];
232 foreach ( $options as $label => $value ) {
233 if ( strtotime( $value ) || wfIsInfinity( $value ) ) {
234 $expiryOptions[$label] = $value;
235 }
236 }
237
238 // If message options is invalid try to recover by returning
239 // english options (T267611)
240 if ( !$expiryOptions && $expiryOptionsMsg->getLanguage()->getCode() !== 'en' ) {
241 return self::getExpiryOptionsFromMessage( $msgLocalizer, 'en' );
242 }
243
244 return $expiryOptions;
245 }
246
247 protected function alterForm( HTMLForm $form ) {
248 $msg = $this->watchlistExpiry && $this->watchedItem ? 'updatewatchlist' : 'addwatch';
249 $form->setWrapperLegendMsg( $msg );
250 $submitMsg = $this->watchlistExpiry ? 'confirm-watch-button-expiry' : 'confirm-watch-button';
251 $form->setSubmitTextMsg( $submitMsg );
252 $form->setTokenSalt( 'watch' );
253 }
254
267 public function onSuccess() {
268 $submittedExpiry = $this->getContext()->getRequest()->getText( 'wp' . $this->expiryFormFieldName );
269 $this->getOutput()->addWikiMsg( $this->makeSuccessMessage( $submittedExpiry ) );
270 }
271
272 protected function makeSuccessMessage( string $submittedExpiry ): MessageValue {
273 $msgKey = $this->getTitle()->isTalkPage() ? 'addedwatchtext-talk' : 'addedwatchtext';
274 $params = [];
275 if ( $submittedExpiry ) {
276 // We can't use $this->watchedItem to get the expiry because it's not been saved at this
277 // point in the request and so its values are those from before saving.
278 $expiry = ExpiryDef::normalizeExpiry( $submittedExpiry, TS_ISO_8601 );
279
280 // If the expiry label isn't one of the predefined ones in the dropdown, calculate 'x days'.
281 $expiryDays = WatchedItem::calculateExpiryInDays( $expiry );
282 $defaultLabels = static::getExpiryOptionsFromMessage( $this->getContext() );
283 $localizedExpiry = array_search( $submittedExpiry, $defaultLabels );
284
285 // Determine which message to use, depending on whether this is a talk page or not
286 // and whether an expiry was selected.
287 $isTalk = $this->getTitle()->isTalkPage();
288 if ( wfIsInfinity( $expiry ) ) {
289 $msgKey = $isTalk ? 'addedwatchindefinitelytext-talk' : 'addedwatchindefinitelytext';
290 } elseif ( $expiryDays >= 1 ) {
291 $msgKey = $isTalk ? 'addedwatchexpirytext-talk' : 'addedwatchexpirytext';
292 $params[] = $localizedExpiry === false
293 ? $this->getContext()->msg( 'days', $expiryDays )->text()
294 : $localizedExpiry;
295 } else {
296 // Less than one day.
297 $msgKey = $isTalk ? 'addedwatchexpiryhours-talk' : 'addedwatchexpiryhours';
298 }
299 }
300 return MessageValue::new( $msgKey )->params( $this->getTitle()->getPrefixedText(), ...$params );
301 }
302
304 public function doesWrites() {
305 return true;
306 }
307}
308
310class_alias( WatchAction::class, 'WatchAction' );
wfIsInfinity( $str)
Determine input string is represents as infinity.
getContext()
Get the IContextSource in use here.
Definition Action.php:132
IContextSource null $context
IContextSource if specified; otherwise we'll use the Context from the Page.
Definition Action.php:79
getUser()
Shortcut to get the User being used for this instance.
Definition Action.php:166
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition Action.php:240
getTitle()
Shortcut to get the Title object from the page.
Definition Action.php:226
getRequest()
Get the WebRequest being used for this instance.
Definition Action.php:146
getAuthority()
Shortcut to get the Authority executing this instance.
Definition Action.php:176
An action which shows a form and does something based on the input from the form.
Page addition to a user's watchlist.
getName()
Return the name of the action this object responds to.1.17string Lowercase name
getRestriction()
Get the permission required to perform this action.Often, but not always, the same as the action name...
makeSuccessMessage(string $submittedExpiry)
onSubmit( $data)
Process the form on POST submission.If you don't want to do anything with the form,...
static getExpiryOptionsFromMessage(MessageLocalizer $msgLocalizer, ?string $lang=null)
Parse expiry options message.
onSuccess()
Show one of 8 possible success messages.
getFormFields()
Get an HTMLForm descriptor array.to override array
static getExpiryOptions(MessageLocalizer $msgLocalizer, $watchedItem, string $defaultExpiry='infinite')
Get options and default for a watchlist expiry select list.
usesOOUI()
Whether the form should use OOUI.to override bool
bool $watchlistExpiry
The value of the $wgWatchlistExpiry configuration variable.
getDescription()
Returns the description that goes below the <h1> element.1.17 to override string HTML
requiresUnblock()
Whether this action can still be executed by a blocked user.Implementations of this methods must alwa...
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
false WatchedItem $watchedItem
__construct(Article $article, IContextSource $context, WatchlistManager $watchlistManager, WatchedItemStoreInterface $watchedItemStore, UserOptionsLookup $userOptionsLookup)
Only public since 1.21.
Show an error when a user tries to do something they do not have the necessary permissions for.
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Show an error when the user tries to do something whilst blocked.
Redirect a user to the login page or account creation page.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:209
setWrapperLegendMsg( $msg)
Prompt the whole form to be wrapped in a "<fieldset>", with this message as its "<legend>" element.
setTokenSalt( $salt)
Set the salt for the edit token.
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
A class containing constants representing the names of configuration variables.
const WatchlistExpiry
Name constant for the WatchlistExpiry setting, for use with Config::get()
Legacy class representing an editable page and handling UI for some page actions.
Definition Article.php:76
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
Provides access to user options.
User class for the MediaWiki software.
Definition User.php:123
isAllowed(string $permission, ?PermissionStatus $status=null)
Checks whether this authority has the given permission in general.
Definition User.php:2190
isTemp()
Is the user an autocreated temporary user?
Definition User.php:3444
isRegistered()
Get whether the user is registered.
Definition User.php:2133
Representation of a pair of user and title for watchlist entries.
getExpiry(?int $style=TS_MW)
When the watched item will expire.
static calculateExpiryInDays(?string $expiry)
Get the number of days remaining until the given expiry time.
getExpiryInDaysText(MessageLocalizer $msgLocalizer, $isDropdownOption=false)
Get days remaining until a watched item expires as a text.
Class for generating HTML <select> or <datalist> elements.
Definition XmlSelect.php:30
static parseOptionsMessage(string $msg)
Parse labels and values out of a comma- and colon-separated list of options, such as is used for expi...
Value object representing a message for i18n.
static new(string $key, array $params=[])
Static constructor for easier chaining of ->params() methods.
Type definition for expiry timestamps.
Definition ExpiryDef.php:17
static normalizeExpiry(?string $expiry=null, ?int $style=null)
Normalize a user-inputted expiry in ConvertibleTimestamp.
Definition ExpiryDef.php:94
Interface for objects which can provide a MediaWiki context on request.
getWatchedItem(UserIdentity $user, $target)
Get an item (may be cached)
Interface for localizing messages in MediaWiki.