Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 215 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
Stabilization | |
0.00% |
0 / 215 |
|
0.00% |
0 / 7 |
1332 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
doesWrites | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 43 |
|
0.00% |
0 / 1 |
132 | |||
showForm | |
0.00% |
0 / 141 |
|
0.00% |
0 / 1 |
182 | |||
buildSelector | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
30 | |||
getOptionLabel | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
disabledAttr | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | use MediaWiki\CommentStore\CommentStore; |
4 | use MediaWiki\Html\Html; |
5 | use MediaWiki\Linker\Linker; |
6 | use MediaWiki\MediaWikiServices; |
7 | use MediaWiki\SpecialPage\UnlistedSpecialPage; |
8 | use MediaWiki\Title\Title; |
9 | use MediaWiki\Xml\Xml; |
10 | |
11 | /** Assumes $wgFlaggedRevsProtection is off */ |
12 | class Stabilization extends UnlistedSpecialPage { |
13 | /** @var PageStabilityGeneralForm|null */ |
14 | private $form = null; |
15 | |
16 | public function __construct() { |
17 | parent::__construct( 'Stabilization', 'stablesettings' ); |
18 | } |
19 | |
20 | /** |
21 | * @inheritDoc |
22 | */ |
23 | public function doesWrites() { |
24 | return true; |
25 | } |
26 | |
27 | /** |
28 | * @inheritDoc |
29 | */ |
30 | public function execute( $par ) { |
31 | $out = $this->getOutput(); |
32 | $user = $this->getUser(); |
33 | $request = $this->getRequest(); |
34 | |
35 | $confirmed = $user->matchEditToken( $request->getVal( 'wpEditToken' ) ); |
36 | |
37 | # Target page |
38 | $title = Title::newFromText( $request->getVal( 'page', $par ) ); |
39 | if ( !$title ) { |
40 | $out->showErrorPage( 'notargettitle', 'notargettext' ); |
41 | return; |
42 | } |
43 | |
44 | # Let anyone view, but not submit... |
45 | if ( $request->wasPosted() ) { |
46 | $pm = MediaWikiServices::getInstance()->getPermissionManager(); |
47 | if ( !$pm->userHasRight( $user, 'stablesettings' ) ) { |
48 | throw new PermissionsError( 'stablesettings' ); |
49 | } |
50 | if ( $pm->isBlockedFrom( $user, $title, !$confirmed ) ) { |
51 | // @phan-suppress-next-line PhanTypeMismatchArgumentNullable Guaranteed via isBlockedFrom() above |
52 | throw new UserBlockedError( $user->getBlock( !$confirmed ) ); |
53 | } |
54 | $this->checkReadOnly(); |
55 | } |
56 | |
57 | # Set page title |
58 | $this->setHeaders(); |
59 | |
60 | $this->getSkin()->setRelevantTitle( $title ); |
61 | |
62 | $this->form = new PageStabilityGeneralForm( $user ); |
63 | $form = $this->form; // convenience |
64 | |
65 | $form->setTitle( $title ); |
66 | # Watch checkbox |
67 | $form->setWatchThis( $request->getCheck( 'wpWatchthis' ) ); |
68 | # Get auto-review option... |
69 | $form->setReviewThis( $request->getCheck( 'wpReviewthis' ) ); |
70 | # Reason |
71 | $form->setReasonExtra( $request->getText( 'wpReason' ) ); |
72 | $form->setReasonSelection( $request->getVal( 'wpReasonSelection' ) ); |
73 | # Expiry |
74 | $form->setExpiryCustom( $request->getText( 'mwStabilize-expiry' ) ); |
75 | $form->setExpirySelection( $request->getVal( 'wpExpirySelection' ) ); |
76 | # Default version |
77 | $form->setOverride( (int)$request->getBool( 'wpStableconfig-override' ) ); |
78 | # Get autoreview restrictions... |
79 | $form->setAutoreview( $request->getVal( 'mwProtect-level-autoreview' ) ); |
80 | $form->ready(); // params all set |
81 | |
82 | $status = $form->checkTarget(); |
83 | if ( $status === 'stabilize_page_notexists' ) { |
84 | $out->addWikiMsg( 'stabilization-notexists', $title->getPrefixedText() ); |
85 | return; |
86 | } elseif ( $status === 'stabilize_page_unreviewable' ) { |
87 | $out->addWikiMsg( 'stabilization-notcontent', $title->getPrefixedText() ); |
88 | return; |
89 | } |
90 | |
91 | # Form POST request... |
92 | if ( $request->wasPosted() && $confirmed && $form->isAllowed() ) { |
93 | $status = $form->submit(); |
94 | if ( $status === true ) { |
95 | $out->redirect( $title->getFullURL() ); |
96 | } else { |
97 | $this->showForm( $this->msg( $status )->escaped() ); |
98 | } |
99 | # Form GET request... |
100 | } else { |
101 | $form->preload(); |
102 | $this->showForm(); |
103 | } |
104 | } |
105 | |
106 | /** |
107 | * @param string|null $err |
108 | */ |
109 | private function showForm( $err = null ) { |
110 | $out = $this->getOutput(); |
111 | $user = $this->getUser(); |
112 | |
113 | $form = $this->form; // convenience |
114 | $title = $this->form->getTitle(); |
115 | $oldConfig = $form->getOldConfig(); |
116 | |
117 | $s = ''; // form HTML string |
118 | # Add any error messages |
119 | if ( $err !== '' ) { |
120 | $out->setSubtitle( $this->msg( 'formerror' ) ); |
121 | $out->addHTML( "<p class='error'>{$err}</p>\n" ); |
122 | } |
123 | # Add header text |
124 | $msg = $form->isAllowed() ? 'stabilization-text' : 'stabilization-perm'; |
125 | $s .= $this->msg( $msg, $title->getPrefixedText() )->parseAsBlock(); |
126 | # Traditionally, the list of reasons for stabilization is the same as |
127 | # for protection. In some cases, however, it might be desirable to |
128 | # use a different list for stabilization. |
129 | $defaultReasons = $this->msg( 'stabilization-dropdown' ); |
130 | if ( $defaultReasons->isDisabled() ) { |
131 | $defaultReasons = $this->msg( 'protect-dropdown' ); |
132 | } |
133 | $reasonDropdown = Xml::listDropdown( |
134 | 'wpReasonSelection', |
135 | $defaultReasons->inContentLanguage()->text(), |
136 | $this->msg( 'protect-otherreason-op' )->inContentLanguage()->escaped(), |
137 | $form->getReasonSelection(), |
138 | 'mwStabilize-reason', |
139 | 4 |
140 | ); |
141 | $scExpiryOptions = $this->msg( 'protect-expiry-options' )->inContentLanguage()->text(); |
142 | $showProtectOptions = ( $scExpiryOptions !== '-' && $form->isAllowed() ); |
143 | $dropdownOptions = []; // array of <label,value> |
144 | # Add the current expiry as a dropdown option |
145 | if ( $oldConfig['expiry'] && $oldConfig['expiry'] != 'infinity' ) { |
146 | $timestamp = $this->getLanguage()->timeanddate( $oldConfig['expiry'] ); |
147 | $d = $this->getLanguage()->date( $oldConfig['expiry'] ); |
148 | $t = $this->getLanguage()->time( $oldConfig['expiry'] ); |
149 | $dropdownOptions[] = [ |
150 | $this->msg( 'protect-existing-expiry', $timestamp, $d, $t )->text(), 'existing' ]; |
151 | } |
152 | # Add "other time" expiry dropdown option |
153 | $dropdownOptions[] = [ $this->msg( 'protect-othertime-op' )->text(), 'othertime' ]; |
154 | # Add custom expiry dropdown options (from MediaWiki message) |
155 | foreach ( explode( ',', $scExpiryOptions ) as $option ) { |
156 | $pair = explode( ':', $option, 2 ); |
157 | $show = $pair[0]; |
158 | $value = $pair[1] ?? $show; |
159 | $dropdownOptions[] = [ $show, $value ]; |
160 | } |
161 | |
162 | # Actually build the options HTML... |
163 | $expiryFormOptions = ''; |
164 | foreach ( $dropdownOptions as [ $show, $value ] ) { |
165 | $expiryFormOptions .= Html::element( 'option', |
166 | [ 'value' => $value, 'selected' => $form->getExpirySelection() === $value ], |
167 | $show |
168 | ) . "\n"; |
169 | } |
170 | |
171 | # Build up the form... |
172 | $s .= Html::openElement( 'form', [ 'name' => 'stabilization', |
173 | 'action' => $this->getPageTitle()->getLocalURL(), 'method' => 'post' ] ); |
174 | # Add stable version override and selection options |
175 | $s .= |
176 | Xml::fieldset( $this->msg( 'stabilization-def' )->text() ) . "\n" . |
177 | Xml::radioLabel( $this->msg( 'stabilization-def1' )->text(), 'wpStableconfig-override', '1', |
178 | 'default-stable', $form->getOverride() == 1, $this->disabledAttr() ) . |
179 | '<br />' . "\n" . |
180 | Xml::radioLabel( $this->msg( 'stabilization-def2' )->text(), 'wpStableconfig-override', '0', |
181 | 'default-current', $form->getOverride() == 0, $this->disabledAttr() ) . "\n" . |
182 | Html::closeElement( 'fieldset' ); |
183 | # Add autoreview restriction select |
184 | $s .= Xml::fieldset( $this->msg( 'stabilization-restrict' )->text() ) . |
185 | $this->buildSelector( $form->getAutoreview() ) . |
186 | Html::closeElement( 'fieldset' ) . |
187 | |
188 | Xml::fieldset( $this->msg( 'stabilization-leg' )->text() ) . |
189 | Html::openElement( 'table' ); |
190 | # Add expiry dropdown to form... |
191 | if ( $showProtectOptions && $form->isAllowed() ) { |
192 | $s .= " |
193 | <tr> |
194 | <td class='mw-label'>" . |
195 | Html::label( $this->msg( 'stabilization-expiry' )->text(), |
196 | 'mwStabilizeExpirySelection' ) . |
197 | "</td> |
198 | <td class='mw-input'>" . |
199 | Html::rawElement( 'select', |
200 | [ |
201 | 'id' => 'mwStabilizeExpirySelection', |
202 | 'name' => 'wpExpirySelection', |
203 | 'onchange' => 'onFRChangeExpiryDropdown()', |
204 | ] + $this->disabledAttr(), |
205 | $expiryFormOptions ) . |
206 | "</td> |
207 | </tr>"; |
208 | } |
209 | # Add custom expiry field to form... |
210 | $attribs = [ 'id' => "mwStabilizeExpiryOther", |
211 | 'size' => 50, |
212 | 'onkeyup' => 'onFRChangeExpiryField()' ] + $this->disabledAttr(); |
213 | $s .= " |
214 | <tr> |
215 | <td class='mw-label'>" . |
216 | Html::label( $this->msg( 'stabilization-othertime' )->text(), |
217 | 'mwStabilizeExpiryOther' ) . |
218 | '</td> |
219 | <td class="mw-input">' . |
220 | Html::input( 'mwStabilize-expiry', $form->getExpiryCustom(), 'text', $attribs ) . |
221 | '</td> |
222 | </tr>'; |
223 | # Add comment input and submit button |
224 | if ( $form->isAllowed() ) { |
225 | $watchAttribs = [ 'accesskey' => $this->msg( 'accesskey-watch' )->text(), |
226 | 'id' => 'wpWatchthis' ]; |
227 | $services = MediaWikiServices::getInstance(); |
228 | $userOptionsLookup = $services->getUserOptionsLookup(); |
229 | $watchlistManager = $services->getWatchlistManager(); |
230 | $watchChecked = ( $userOptionsLookup->getOption( $user, 'watchdefault' ) |
231 | || $watchlistManager->isWatched( $user, $title ) ); |
232 | |
233 | $s .= ' <tr> |
234 | <td class="mw-label">' . |
235 | Html::label( $this->msg( 'stabilization-comment' )->text(), |
236 | 'wpReasonSelection' ) . |
237 | '</td> |
238 | <td class="mw-input">' . |
239 | $reasonDropdown . |
240 | '</td> |
241 | </tr> |
242 | <tr> |
243 | <td class="mw-label">' . |
244 | Html::label( $this->msg( 'stabilization-otherreason' )->text(), 'wpReason' ) . |
245 | '</td> |
246 | <td class="mw-input">' . |
247 | Html::input( 'wpReason', $form->getReasonExtra(), 'text', [ |
248 | 'id' => 'wpReason', |
249 | 'size' => 70, |
250 | 'maxlength' => CommentStore::COMMENT_CHARACTER_LIMIT |
251 | ] ) . |
252 | '</td> |
253 | </tr> |
254 | <tr> |
255 | <td></td> |
256 | <td class="mw-input">' . |
257 | Html::check( 'wpReviewthis', $form->getReviewThis(), |
258 | [ 'id' => 'wpReviewthis' ] ) . |
259 | Html::label( $this->msg( 'stabilization-review' )->text(), 'wpReviewthis' ) . |
260 | '     ' . |
261 | Html::check( 'wpWatchthis', $watchChecked, $watchAttribs ) . |
262 | " " . |
263 | Html::label( $this->msg( 'watchthis' )->text(), 'wpWatchthis', |
264 | [ 'title' => Linker::titleAttrib( 'watch', 'withaccess' ) ] ) . |
265 | '</td> |
266 | </tr> |
267 | <tr> |
268 | <td></td> |
269 | <td class="mw-submit">' . |
270 | Html::submitButton( $this->msg( 'stabilization-submit' )->text() ) . |
271 | '</td> |
272 | </tr>' . Html::closeElement( 'table' ) . |
273 | Html::hidden( 'title', $this->getPageTitle()->getPrefixedDBkey() ) . |
274 | Html::hidden( 'page', $title->getPrefixedText() ) . |
275 | Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ); |
276 | } else { |
277 | $s .= Html::closeElement( 'table' ); |
278 | } |
279 | $s .= Html::closeElement( 'fieldset' ) . Html::closeElement( 'form' ); |
280 | |
281 | $out->addHTML( $s ); |
282 | |
283 | $log = new LogPage( 'stable' ); |
284 | $out->addHTML( Html::element( 'h2', [], |
285 | $log->getName()->setContext( $this->getContext() )->text() ) ); |
286 | LogEventsList::showLogExtract( $out, 'stable', |
287 | $title->getPrefixedText(), '', [ 'lim' => 25 ] ); |
288 | |
289 | # Add some javascript for expiry dropdowns |
290 | $out->addScript( |
291 | "<script type=\"text/javascript\"> |
292 | function onFRChangeExpiryDropdown() { |
293 | document.getElementById('mwStabilizeExpiryOther').value = ''; |
294 | } |
295 | function onFRChangeExpiryField() { |
296 | document.getElementById('mwStabilizeExpirySelection').value = 'othertime'; |
297 | } |
298 | </script>" |
299 | ); |
300 | } |
301 | |
302 | /** |
303 | * @param string $selected |
304 | * @return string HTML |
305 | */ |
306 | private function buildSelector( $selected ) { |
307 | $allowedLevels = []; |
308 | // Add a "none" level |
309 | $levels = [ '', ...FlaggedRevs::getRestrictionLevels() ]; |
310 | foreach ( $levels as $key ) { |
311 | # Don't let them choose levels they can't set, |
312 | # but *show* them all when the form is disabled. |
313 | if ( $this->form->isAllowed() |
314 | && !FlaggedRevs::userCanSetAutoreviewLevel( $this->getUser(), $key ) |
315 | ) { |
316 | continue; |
317 | } |
318 | $allowedLevels[] = $key; |
319 | } |
320 | $id = 'mwProtect-level-autoreview'; |
321 | $attribs = [ |
322 | 'id' => $id, |
323 | 'name' => $id, |
324 | 'size' => count( $allowedLevels ), |
325 | ] + $this->disabledAttr(); |
326 | |
327 | $out = ''; |
328 | foreach ( $allowedLevels as $key ) { |
329 | $out .= Html::element( 'option', |
330 | [ 'value' => $key, 'selected' => $key == $selected ], |
331 | $this->getOptionLabel( $key ) |
332 | ); |
333 | } |
334 | return Html::rawElement( 'select', $attribs, $out ); |
335 | } |
336 | |
337 | /** |
338 | * Prepare the label for a protection selector option |
339 | * |
340 | * @param string $permission Permission required |
341 | * @return string |
342 | */ |
343 | private function getOptionLabel( $permission ) { |
344 | if ( !$permission ) { |
345 | return $this->msg( 'stabilization-restrict-none' )->text(); |
346 | } |
347 | |
348 | $msg = $this->msg( "protect-level-$permission" ); |
349 | if ( $msg->isDisabled() ) { |
350 | $msg = $this->msg( 'protect-fallback', $permission ); |
351 | } |
352 | return $msg->text(); |
353 | } |
354 | |
355 | /** |
356 | * If the this form is disabled, then return the "disabled" attr array |
357 | * @return string[] |
358 | */ |
359 | private function disabledAttr() { |
360 | return $this->form->isAllowed() |
361 | ? [] |
362 | : [ 'disabled' => 'disabled' ]; |
363 | } |
364 | } |