MediaWiki 1.44.0
WatchAction.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\Actions;
24
42
48class WatchAction extends FormAction {
49
52
54 protected $expiryFormFieldName = 'expiry';
55
57 protected $watchedItem = false;
58
59 private WatchlistManager $watchlistManager;
60
69 public function __construct(
70 Article $article,
72 WatchlistManager $watchlistManager,
73 WatchedItemStore $watchedItemStore
74 ) {
75 parent::__construct( $article, $context );
76 $this->watchlistExpiry = $this->getContext()->getConfig()->get( MainConfigNames::WatchlistExpiry );
77 if ( $this->watchlistExpiry ) {
78 // The watchedItem is only used in this action's form if $wgWatchlistExpiry is enabled.
79 $this->watchedItem = $watchedItemStore->getWatchedItem(
80 $this->getUser(),
81 $this->getTitle()
82 );
83 }
84 $this->watchlistManager = $watchlistManager;
85 }
86
87 public function getName() {
88 return 'watch';
89 }
90
91 public function requiresUnblock() {
92 return false;
93 }
94
95 protected function getDescription() {
96 return '';
97 }
98
99 public function onSubmit( $data ) {
100 // Even though we're never unwatching here, use WatchlistManager::setWatch()
101 // because it also checks for changed expiry.
102 $result = $this->watchlistManager->setWatch(
103 true,
104 $this->getAuthority(),
105 $this->getTitle(),
106 $this->getRequest()->getVal( 'wp' . $this->expiryFormFieldName )
107 );
108
109 return Status::wrap( $result );
110 }
111
118 protected function checkCanExecute( User $user ) {
119 if ( !$user->isRegistered()
120 || ( $user->isTemp() && !$user->isAllowed( 'editmywatchlist' ) )
121 ) {
122 throw new UserNotLoggedIn( 'watchlistanontext', 'watchnologin' );
123 }
124
125 parent::checkCanExecute( $user );
126 }
127
128 public function getRestriction() {
129 return 'editmywatchlist';
130 }
131
132 protected function usesOOUI() {
133 return true;
134 }
135
136 protected function getFormFields() {
137 // If watchlist expiry is not enabled, return a simple confirmation message.
138 if ( !$this->watchlistExpiry ) {
139 return [
140 'intro' => [
141 'type' => 'info',
142 'raw' => true,
143 'default' => $this->msg( 'confirm-watch-top' )->parse(),
144 ],
145 ];
146 }
147
148 // Otherwise, use a select-list of expiries.
149 $expiryOptions = static::getExpiryOptions( $this->getContext(), $this->watchedItem );
150 return [
151 $this->expiryFormFieldName => [
152 'type' => 'select',
153 'label-message' => 'confirm-watch-label',
154 'options' => $expiryOptions['options'],
155 'default' => $expiryOptions['default'],
156 ]
157 ];
158 }
159
172 public static function getExpiryOptions( MessageLocalizer $msgLocalizer, $watchedItem ) {
173 $expiryOptions = self::getExpiryOptionsFromMessage( $msgLocalizer );
174 $default = in_array( 'infinite', $expiryOptions )
175 ? 'infinite'
176 : current( $expiryOptions );
177 if ( $watchedItem instanceof WatchedItem && $watchedItem->getExpiry() ) {
178 // If it's already being temporarily watched,
179 // add the existing expiry as the default option in the dropdown.
180 $default = $watchedItem->getExpiry( TS_ISO_8601 );
181 $daysLeft = $watchedItem->getExpiryInDaysText( $msgLocalizer, true );
182 $expiryOptions = array_merge( [ $daysLeft => $default ], $expiryOptions );
183 }
184 return [
185 'options' => $expiryOptions,
186 'default' => $default,
187 ];
188 }
189
198 private static function getExpiryOptionsFromMessage(
199 MessageLocalizer $msgLocalizer, ?string $lang = null
200 ): array {
201 $expiryOptionsMsg = $msgLocalizer->msg( 'watchlist-expiry-options' );
202 $optionsText = !$lang ? $expiryOptionsMsg->text() : $expiryOptionsMsg->inLanguage( $lang )->text();
204 $optionsText
205 );
206
207 $expiryOptions = [];
208 foreach ( $options as $label => $value ) {
209 if ( strtotime( $value ) || wfIsInfinity( $value ) ) {
210 $expiryOptions[$label] = $value;
211 }
212 }
213
214 // If message options is invalid try to recover by returning
215 // english options (T267611)
216 if ( !$expiryOptions && $expiryOptionsMsg->getLanguage()->getCode() !== 'en' ) {
217 return self::getExpiryOptionsFromMessage( $msgLocalizer, 'en' );
218 }
219
220 return $expiryOptions;
221 }
222
223 protected function alterForm( HTMLForm $form ) {
224 $msg = $this->watchlistExpiry && $this->watchedItem ? 'updatewatchlist' : 'addwatch';
225 $form->setWrapperLegendMsg( $msg );
226 $submitMsg = $this->watchlistExpiry ? 'confirm-watch-button-expiry' : 'confirm-watch-button';
227 $form->setSubmitTextMsg( $submitMsg );
228 $form->setTokenSalt( 'watch' );
229 }
230
243 public function onSuccess() {
244 $submittedExpiry = $this->getContext()->getRequest()->getText( 'wp' . $this->expiryFormFieldName );
245 $this->getOutput()->addWikiMsg( $this->makeSuccessMessage( $submittedExpiry ) );
246 }
247
248 protected function makeSuccessMessage( string $submittedExpiry ): MessageValue {
249 $msgKey = $this->getTitle()->isTalkPage() ? 'addedwatchtext-talk' : 'addedwatchtext';
250 $params = [];
251 if ( $submittedExpiry ) {
252 // We can't use $this->watcheditem to get the expiry because it's not been saved at this
253 // point in the request and so its values are those from before saving.
254 $expiry = ExpiryDef::normalizeExpiry( $submittedExpiry, TS_ISO_8601 );
255
256 // If the expiry label isn't one of the predefined ones in the dropdown, calculate 'x days'.
257 $expiryDays = WatchedItem::calculateExpiryInDays( $expiry );
258 $defaultLabels = static::getExpiryOptions( $this->getContext(), false )['options'];
259 $localizedExpiry = array_search( $submittedExpiry, $defaultLabels );
260
261 // Determine which message to use, depending on whether this is a talk page or not
262 // and whether an expiry was selected.
263 $isTalk = $this->getTitle()->isTalkPage();
264 if ( wfIsInfinity( $expiry ) ) {
265 $msgKey = $isTalk ? 'addedwatchindefinitelytext-talk' : 'addedwatchindefinitelytext';
266 } elseif ( $expiryDays >= 1 ) {
267 $msgKey = $isTalk ? 'addedwatchexpirytext-talk' : 'addedwatchexpirytext';
268 $params[] = $localizedExpiry === false
269 ? $this->getContext()->msg( 'days', $expiryDays )->text()
270 : $localizedExpiry;
271 } else {
272 $msgKey = $isTalk ? 'addedwatchexpiryhours-talk' : 'addedwatchexpiryhours';
273 }
274 }
275 return MessageValue::new( $msgKey )->params( $this->getTitle()->getPrefixedText(), ...$params );
276 }
277
278 public function doesWrites() {
279 return true;
280 }
281}
282
284class_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.
getRestriction()
Get the permission required to perform this action.
makeSuccessMessage(string $submittedExpiry)
static getExpiryOptions(MessageLocalizer $msgLocalizer, $watchedItem)
Get options and default for a watchlist expiry select list.
onSubmit( $data)
Process the form on POST submission.
__construct(Article $article, IContextSource $context, WatchlistManager $watchlistManager, WatchedItemStore $watchedItemStore)
Only public since 1.21.
onSuccess()
Show one of 8 possible success messages.
getFormFields()
Get an HTMLForm descriptor array.
usesOOUI()
Whether the form should use OOUI.
bool $watchlistExpiry
The value of the $wgWatchlistExpiry configuration variable.
getDescription()
Returns the description that goes below the <h1> element.
requiresUnblock()
Whether this action can still be executed by a blocked user.
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
false WatchedItem $watchedItem
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
User class for the MediaWiki software.
Definition User.php:121
isAllowed(string $permission, ?PermissionStatus $status=null)
Checks whether this authority has the given permission in general.
Definition User.php:2217
isTemp()
Is the user an autocreated temporary user?
Definition User.php:3420
isRegistered()
Get whether the user is registered.
Definition User.php:2160
Storage layer class for WatchedItems.
getWatchedItem(UserIdentity $user, $target)
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.
Interface for localizing messages in MediaWiki.