MediaWiki master
WatchAction.php
Go to the documentation of this file.
1<?php
10namespace MediaWiki\Actions;
11
30use Wikimedia\Timestamp\TimestampFormat as TS;
31
37class WatchAction extends FormAction {
38
41
43 protected $expiryFormFieldName = 'expiry';
44
46 protected $watchedItem = false;
47
48 private WatchlistManager $watchlistManager;
49 private UserOptionsLookup $userOptionsLookup;
50
60 public function __construct(
61 Article $article,
63 WatchlistManager $watchlistManager,
64 WatchedItemStoreInterface $watchedItemStore,
65 UserOptionsLookup $userOptionsLookup
66 ) {
67 parent::__construct( $article, $context );
68 $this->watchlistExpiry = $this->getContext()->getConfig()->get( MainConfigNames::WatchlistExpiry );
69 if ( $this->watchlistExpiry ) {
70 // The watchedItem is only used in this action's form if $wgWatchlistExpiry is enabled.
71 $this->watchedItem = $watchedItemStore->getWatchedItem(
72 $this->getUser(),
73 $this->getTitle()
74 );
75 }
76 $this->watchlistManager = $watchlistManager;
77 $this->userOptionsLookup = $userOptionsLookup;
78 }
79
81 public function getName() {
82 return 'watch';
83 }
84
86 public function requiresUnblock() {
87 return false;
88 }
89
91 protected function getDescription() {
92 return '';
93 }
94
96 public function onSubmit( $data ) {
97 // Even though we're never unwatching here, use WatchlistManager::setWatch()
98 // because it also checks for changed expiry.
99 $result = $this->watchlistManager->setWatch(
100 true,
101 $this->getAuthority(),
102 $this->getTitle(),
103 $this->getRequest()->getVal( 'wp' . $this->expiryFormFieldName )
104 );
105
106 return Status::wrap( $result );
107 }
108
115 protected function checkCanExecute( User $user ) {
116 if ( !$user->isRegistered()
117 || ( $user->isTemp() && !$user->isAllowed( 'editmywatchlist' ) )
118 ) {
119 throw new UserNotLoggedIn( 'watchlistanontext', 'watchnologin' );
120 }
121
122 parent::checkCanExecute( $user );
123 }
124
126 public function getRestriction() {
127 return 'editmywatchlist';
128 }
129
131 protected function usesOOUI() {
132 return true;
133 }
134
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, where the default is the user's
149 // preferred expiry time (or the existing watch duration if already temporarily watched).
150 $default = $this->userOptionsLookup->getOption( $this->getUser(), 'watchstar-expiry' );
151 $expiryOptions = static::getExpiryOptions( $this->getContext(), $this->watchedItem, $default );
152 return [
153 $this->expiryFormFieldName => [
154 'type' => 'select',
155 'label-message' => 'confirm-watch-label',
156 'options' => $expiryOptions['options'],
157 'default' => $expiryOptions['default'],
158 ]
159 ];
160 }
161
174 public static function getExpiryOptions(
175 MessageLocalizer $msgLocalizer,
177 string $defaultExpiry = 'infinite'
178 ) {
179 $expiryOptions = self::getExpiryOptionsFromMessage( $msgLocalizer );
180
181 if ( !in_array( $defaultExpiry, $expiryOptions ) ) {
182 $expiryOptions = array_merge( [ $defaultExpiry => $defaultExpiry ], $expiryOptions );
183 }
184
185 if ( $watchedItem instanceof WatchedItem && $watchedItem->getExpiry() ) {
186 // If it's already being temporarily watched, add the existing expiry as an option in the dropdown.
187 $currentExpiry = $watchedItem->getExpiry( TS::ISO_8601 );
188 $daysLeft = $watchedItem->getExpiryInDaysText( $msgLocalizer, true );
189 $expiryOptions = array_merge( [ $daysLeft => $currentExpiry ], $expiryOptions );
190
191 // Always preselect the existing expiry.
192 $defaultExpiry = $currentExpiry;
193 }
194
195 return [
196 'options' => $expiryOptions,
197 'default' => $defaultExpiry,
198 ];
199 }
200
210 public static function getExpiryOptionsFromMessage(
211 MessageLocalizer $msgLocalizer, ?string $lang = null
212 ): array {
213 $expiryOptionsMsg = $msgLocalizer->msg( 'watchlist-expiry-options' );
214 $optionsText = !$lang ? $expiryOptionsMsg->text() : $expiryOptionsMsg->inLanguage( $lang )->text();
216 $optionsText
217 );
218
219 $expiryOptions = [];
220 foreach ( $options as $label => $value ) {
221 if ( strtotime( $value ) || wfIsInfinity( $value ) ) {
222 $expiryOptions[$label] = $value;
223 }
224 }
225
226 // If message options is invalid try to recover by returning
227 // english options (T267611)
228 if ( !$expiryOptions && $expiryOptionsMsg->getLanguage()->getCode() !== 'en' ) {
229 return self::getExpiryOptionsFromMessage( $msgLocalizer, 'en' );
230 }
231
232 return $expiryOptions;
233 }
234
235 protected function alterForm( HTMLForm $form ) {
236 $msg = $this->watchlistExpiry && $this->watchedItem ? 'updatewatchlist' : 'addwatch';
237 $form->setWrapperLegendMsg( $msg );
238 $submitMsg = $this->watchlistExpiry ? 'confirm-watch-button-expiry' : 'confirm-watch-button';
239 $form->setSubmitTextMsg( $submitMsg );
240 $form->setTokenSalt( 'watch' );
241 }
242
255 public function onSuccess() {
256 $submittedExpiry = $this->getContext()->getRequest()->getText( 'wp' . $this->expiryFormFieldName );
257 $this->getOutput()->addWikiMsg( $this->makeSuccessMessage( $submittedExpiry ) );
258 }
259
260 protected function makeSuccessMessage( string $submittedExpiry ): MessageValue {
261 $msgKey = $this->getTitle()->isTalkPage() ? 'addedwatchtext-talk' : 'addedwatchtext';
262 $params = [];
263 if ( $submittedExpiry ) {
264 // We can't use $this->watchedItem to get the expiry because it's not been saved at this
265 // point in the request and so its values are those from before saving.
266 $expiry = ExpiryDef::normalizeExpiry( $submittedExpiry, TS::ISO_8601 );
267
268 // If the expiry label isn't one of the predefined ones in the dropdown, calculate 'x days'.
269 $expiryDays = WatchedItem::calculateExpiryInDays( $expiry );
270 $defaultLabels = static::getExpiryOptionsFromMessage( $this->getContext() );
271 $localizedExpiry = array_search( $submittedExpiry, $defaultLabels );
272
273 // Determine which message to use, depending on whether this is a talk page or not
274 // and whether an expiry was selected.
275 $isTalk = $this->getTitle()->isTalkPage();
276 if ( wfIsInfinity( $expiry ) ) {
277 $msgKey = $isTalk ? 'addedwatchindefinitelytext-talk' : 'addedwatchindefinitelytext';
278 } elseif ( $expiryDays >= 1 ) {
279 $msgKey = $isTalk ? 'addedwatchexpirytext-talk' : 'addedwatchexpirytext';
280 $params[] = $localizedExpiry === false
281 ? $this->getContext()->msg( 'days', $expiryDays )->text()
282 : $localizedExpiry;
283 } else {
284 // Less than one day.
285 $msgKey = $isTalk ? 'addedwatchexpiryhours-talk' : 'addedwatchexpiryhours';
286 }
287 }
288 return MessageValue::new( $msgKey )->params( $this->getTitle()->getPrefixedText(), ...$params );
289 }
290
292 public function doesWrites() {
293 return true;
294 }
295}
296
298class_alias( WatchAction::class, 'WatchAction' );
wfIsInfinity( $str)
Determine input string is represents as infinity.
getContext()
Get the IContextSource in use here.
Definition Action.php:119
IContextSource null $context
IContextSource if specified; otherwise we'll use the Context from the Page.
Definition Action.php:66
getUser()
Shortcut to get the User being used for this instance.
Definition Action.php:153
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
Definition Action.php:227
getTitle()
Shortcut to get the Title object from the page.
Definition Action.php:213
getRequest()
Get the WebRequest being used for this instance.
Definition Action.php:133
getAuthority()
Shortcut to get the Authority executing this instance.
Definition Action.php:163
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:195
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:64
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:44
Provides access to user options.
User class for the MediaWiki software.
Definition User.php:110
isAllowed(string $permission, ?PermissionStatus $status=null)
Checks whether this authority has the given permission in general.
Definition User.php:2130
isTemp()
Is the user an autocreated temporary user?
Definition User.php:3388
isRegistered()
Get whether the user is registered.
Definition User.php:2071
Representation of a pair of user and title for watchlist entries.
static calculateExpiryInDays(?string $expiry)
Get the number of days remaining until the given expiry time.
getExpiry(int|TS|null $style=TS::MW)
When the watched item will expire.
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:16
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:18
Interface for objects which can provide a MediaWiki context on request.
getWatchedItem(UserIdentity $user, PageReference $target)
Get an item (may be cached)
Interface for localizing messages in MediaWiki.