These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Page protection |
||
4 | * |
||
5 | * Copyright © 2005 Brion Vibber <[email protected]> |
||
6 | * https://www.mediawiki.org/ |
||
7 | * |
||
8 | * This program is free software; you can redistribute it and/or modify |
||
9 | * it under the terms of the GNU General Public License as published by |
||
10 | * the Free Software Foundation; either version 2 of the License, or |
||
11 | * (at your option) any later version. |
||
12 | * |
||
13 | * This program is distributed in the hope that it will be useful, |
||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
16 | * GNU General Public License for more details. |
||
17 | * |
||
18 | * You should have received a copy of the GNU General Public License along |
||
19 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
21 | * http://www.gnu.org/copyleft/gpl.html |
||
22 | * |
||
23 | * @file |
||
24 | */ |
||
25 | |||
26 | /** |
||
27 | * Handles the page protection UI and backend |
||
28 | */ |
||
29 | class ProtectionForm { |
||
30 | /** @var array A map of action to restriction level, from request or default */ |
||
31 | protected $mRestrictions = []; |
||
32 | |||
33 | /** @var string The custom/additional protection reason */ |
||
34 | protected $mReason = ''; |
||
35 | |||
36 | /** @var string The reason selected from the list, blank for other/additional */ |
||
37 | protected $mReasonSelection = ''; |
||
38 | |||
39 | /** @var bool True if the restrictions are cascading, from request or existing protection */ |
||
40 | protected $mCascade = false; |
||
41 | |||
42 | /** @var array Map of action to "other" expiry time. Used in preference to mExpirySelection. */ |
||
43 | protected $mExpiry = []; |
||
44 | |||
45 | /** |
||
46 | * @var array Map of action to value selected in expiry drop-down list. |
||
47 | * Will be set to 'othertime' whenever mExpiry is set. |
||
48 | */ |
||
49 | protected $mExpirySelection = []; |
||
50 | |||
51 | /** @var array Permissions errors for the protect action */ |
||
52 | protected $mPermErrors = []; |
||
53 | |||
54 | /** @var array Types (i.e. actions) for which levels can be selected */ |
||
55 | protected $mApplicableTypes = []; |
||
56 | |||
57 | /** @var array Map of action to the expiry time of the existing protection */ |
||
58 | protected $mExistingExpiry = []; |
||
59 | |||
60 | /** @var IContextSource */ |
||
61 | private $mContext; |
||
62 | |||
63 | function __construct( Article $article ) { |
||
64 | // Set instance variables. |
||
65 | $this->mArticle = $article; |
||
66 | $this->mTitle = $article->getTitle(); |
||
67 | $this->mApplicableTypes = $this->mTitle->getRestrictionTypes(); |
||
68 | $this->mContext = $article->getContext(); |
||
69 | |||
70 | // Check if the form should be disabled. |
||
71 | // If it is, the form will be available in read-only to show levels. |
||
72 | $this->mPermErrors = $this->mTitle->getUserPermissionsErrors( |
||
73 | 'protect', |
||
74 | $this->mContext->getUser(), |
||
75 | $this->mContext->getRequest()->wasPosted() ? 'secure' : 'full' // T92357 |
||
76 | ); |
||
77 | if ( wfReadOnly() ) { |
||
78 | $this->mPermErrors[] = [ 'readonlytext', wfReadOnlyReason() ]; |
||
79 | } |
||
80 | $this->disabled = $this->mPermErrors != []; |
||
81 | $this->disabledAttrib = $this->disabled |
||
82 | ? [ 'disabled' => 'disabled' ] |
||
83 | : []; |
||
84 | |||
85 | $this->loadData(); |
||
86 | } |
||
87 | |||
88 | /** |
||
89 | * Loads the current state of protection into the object. |
||
90 | */ |
||
91 | function loadData() { |
||
92 | $levels = MWNamespace::getRestrictionLevels( |
||
93 | $this->mTitle->getNamespace(), $this->mContext->getUser() |
||
94 | ); |
||
95 | $this->mCascade = $this->mTitle->areRestrictionsCascading(); |
||
96 | |||
97 | $request = $this->mContext->getRequest(); |
||
98 | $this->mReason = $request->getText( 'mwProtect-reason' ); |
||
99 | $this->mReasonSelection = $request->getText( 'wpProtectReasonSelection' ); |
||
100 | $this->mCascade = $request->getBool( 'mwProtect-cascade', $this->mCascade ); |
||
101 | |||
102 | foreach ( $this->mApplicableTypes as $action ) { |
||
103 | // @todo FIXME: This form currently requires individual selections, |
||
104 | // but the db allows multiples separated by commas. |
||
105 | |||
106 | // Pull the actual restriction from the DB |
||
107 | $this->mRestrictions[$action] = implode( '', $this->mTitle->getRestrictions( $action ) ); |
||
108 | |||
109 | if ( !$this->mRestrictions[$action] ) { |
||
110 | // No existing expiry |
||
111 | $existingExpiry = ''; |
||
112 | } else { |
||
113 | $existingExpiry = $this->mTitle->getRestrictionExpiry( $action ); |
||
114 | } |
||
115 | $this->mExistingExpiry[$action] = $existingExpiry; |
||
116 | |||
117 | $requestExpiry = $request->getText( "mwProtect-expiry-$action" ); |
||
118 | $requestExpirySelection = $request->getVal( "wpProtectExpirySelection-$action" ); |
||
119 | |||
120 | if ( $requestExpiry ) { |
||
121 | // Custom expiry takes precedence |
||
122 | $this->mExpiry[$action] = $requestExpiry; |
||
123 | $this->mExpirySelection[$action] = 'othertime'; |
||
124 | } elseif ( $requestExpirySelection ) { |
||
125 | // Expiry selected from list |
||
126 | $this->mExpiry[$action] = ''; |
||
127 | $this->mExpirySelection[$action] = $requestExpirySelection; |
||
128 | } elseif ( $existingExpiry ) { |
||
129 | // Use existing expiry in its own list item |
||
130 | $this->mExpiry[$action] = ''; |
||
131 | $this->mExpirySelection[$action] = $existingExpiry; |
||
132 | } else { |
||
133 | // Catches 'infinity' - Existing expiry is infinite, use "infinite" in drop-down |
||
134 | // Final default: infinite |
||
135 | $this->mExpiry[$action] = ''; |
||
136 | $this->mExpirySelection[$action] = 'infinite'; |
||
137 | } |
||
138 | |||
139 | $val = $request->getVal( "mwProtect-level-$action" ); |
||
140 | if ( isset( $val ) && in_array( $val, $levels ) ) { |
||
141 | $this->mRestrictions[$action] = $val; |
||
142 | } |
||
143 | } |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * Get the expiry time for a given action, by combining the relevant inputs. |
||
148 | * |
||
149 | * @param string $action |
||
150 | * |
||
151 | * @return string 14-char timestamp or "infinity", or false if the input was invalid |
||
152 | */ |
||
153 | function getExpiry( $action ) { |
||
154 | if ( $this->mExpirySelection[$action] == 'existing' ) { |
||
155 | return $this->mExistingExpiry[$action]; |
||
156 | } elseif ( $this->mExpirySelection[$action] == 'othertime' ) { |
||
157 | $value = $this->mExpiry[$action]; |
||
158 | } else { |
||
159 | $value = $this->mExpirySelection[$action]; |
||
160 | } |
||
161 | View Code Duplication | if ( wfIsInfinity( $value ) ) { |
|
162 | $time = 'infinity'; |
||
163 | } else { |
||
164 | $unix = strtotime( $value ); |
||
165 | |||
166 | if ( !$unix || $unix === -1 ) { |
||
167 | return false; |
||
168 | } |
||
169 | |||
170 | // @todo FIXME: Non-qualified absolute times are not in users specified timezone |
||
171 | // and there isn't notice about it in the ui |
||
172 | $time = wfTimestamp( TS_MW, $unix ); |
||
173 | } |
||
174 | return $time; |
||
175 | } |
||
176 | |||
177 | /** |
||
178 | * Main entry point for action=protect and action=unprotect |
||
179 | */ |
||
180 | function execute() { |
||
181 | if ( MWNamespace::getRestrictionLevels( $this->mTitle->getNamespace() ) === [ '' ] ) { |
||
182 | throw new ErrorPageError( 'protect-badnamespace-title', 'protect-badnamespace-text' ); |
||
183 | } |
||
184 | |||
185 | if ( $this->mContext->getRequest()->wasPosted() ) { |
||
186 | if ( $this->save() ) { |
||
187 | $q = $this->mArticle->isRedirect() ? 'redirect=no' : ''; |
||
188 | $this->mContext->getOutput()->redirect( $this->mTitle->getFullURL( $q ) ); |
||
189 | } |
||
190 | } else { |
||
191 | $this->show(); |
||
192 | } |
||
193 | } |
||
194 | |||
195 | /** |
||
196 | * Show the input form with optional error message |
||
197 | * |
||
198 | * @param string $err Error message or null if there's no error |
||
199 | */ |
||
200 | function show( $err = null ) { |
||
201 | $out = $this->mContext->getOutput(); |
||
202 | $out->setRobotPolicy( 'noindex,nofollow' ); |
||
203 | $out->addBacklinkSubtitle( $this->mTitle ); |
||
204 | |||
205 | if ( is_array( $err ) ) { |
||
206 | $out->wrapWikiMsg( "<p class='error'>\n$1\n</p>\n", $err ); |
||
207 | } elseif ( is_string( $err ) ) { |
||
208 | $out->addHTML( "<p class='error'>{$err}</p>\n" ); |
||
209 | } |
||
210 | |||
211 | if ( $this->mTitle->getRestrictionTypes() === [] ) { |
||
212 | // No restriction types available for the current title |
||
213 | // this might happen if an extension alters the available types |
||
214 | $out->setPageTitle( $this->mContext->msg( |
||
215 | 'protect-norestrictiontypes-title', |
||
216 | $this->mTitle->getPrefixedText() |
||
217 | ) ); |
||
218 | $out->addWikiText( $this->mContext->msg( 'protect-norestrictiontypes-text' )->plain() ); |
||
219 | |||
220 | // Show the log in case protection was possible once |
||
221 | $this->showLogExtract( $out ); |
||
222 | // return as there isn't anything else we can do |
||
223 | return; |
||
224 | } |
||
225 | |||
226 | list( $cascadeSources, /* $restrictions */ ) = $this->mTitle->getCascadeProtectionSources(); |
||
227 | if ( $cascadeSources && count( $cascadeSources ) > 0 ) { |
||
228 | $titles = ''; |
||
229 | |||
230 | foreach ( $cascadeSources as $title ) { |
||
231 | $titles .= '* [[:' . $title->getPrefixedText() . "]]\n"; |
||
232 | } |
||
233 | |||
234 | /** @todo FIXME: i18n issue, should use formatted number. */ |
||
235 | $out->wrapWikiMsg( |
||
236 | "<div id=\"mw-protect-cascadeon\">\n$1\n" . $titles . "</div>", |
||
237 | [ 'protect-cascadeon', count( $cascadeSources ) ] |
||
238 | ); |
||
239 | } |
||
240 | |||
241 | # Show an appropriate message if the user isn't allowed or able to change |
||
242 | # the protection settings at this time |
||
243 | if ( $this->disabled ) { |
||
244 | $out->setPageTitle( |
||
245 | $this->mContext->msg( 'protect-title-notallowed', |
||
246 | $this->mTitle->getPrefixedText() ) |
||
247 | ); |
||
248 | $out->addWikiText( $out->formatPermissionsErrorMessage( $this->mPermErrors, 'protect' ) ); |
||
249 | } else { |
||
250 | $out->setPageTitle( $this->mContext->msg( 'protect-title', $this->mTitle->getPrefixedText() ) ); |
||
251 | $out->addWikiMsg( 'protect-text', |
||
252 | wfEscapeWikiText( $this->mTitle->getPrefixedText() ) ); |
||
253 | } |
||
254 | |||
255 | $out->addHTML( $this->buildForm() ); |
||
256 | $this->showLogExtract( $out ); |
||
257 | } |
||
258 | |||
259 | /** |
||
260 | * Save submitted protection form |
||
261 | * |
||
262 | * @return bool Success |
||
263 | */ |
||
264 | function save() { |
||
265 | # Permission check! |
||
266 | if ( $this->disabled ) { |
||
267 | $this->show(); |
||
268 | return false; |
||
269 | } |
||
270 | |||
271 | $request = $this->mContext->getRequest(); |
||
272 | $user = $this->mContext->getUser(); |
||
273 | $out = $this->mContext->getOutput(); |
||
274 | $token = $request->getVal( 'wpEditToken' ); |
||
275 | if ( !$user->matchEditToken( $token, [ 'protect', $this->mTitle->getPrefixedDBkey() ] ) ) { |
||
276 | $this->show( [ 'sessionfailure' ] ); |
||
277 | return false; |
||
278 | } |
||
279 | |||
280 | # Create reason string. Use list and/or custom string. |
||
281 | $reasonstr = $this->mReasonSelection; |
||
282 | if ( $reasonstr != 'other' && $this->mReason != '' ) { |
||
283 | // Entry from drop down menu + additional comment |
||
284 | $reasonstr .= $this->mContext->msg( 'colon-separator' )->text() . $this->mReason; |
||
285 | } elseif ( $reasonstr == 'other' ) { |
||
286 | $reasonstr = $this->mReason; |
||
287 | } |
||
288 | $expiry = []; |
||
289 | foreach ( $this->mApplicableTypes as $action ) { |
||
290 | $expiry[$action] = $this->getExpiry( $action ); |
||
291 | if ( empty( $this->mRestrictions[$action] ) ) { |
||
292 | continue; // unprotected |
||
293 | } |
||
294 | if ( !$expiry[$action] ) { |
||
295 | $this->show( [ 'protect_expiry_invalid' ] ); |
||
296 | return false; |
||
297 | } |
||
298 | if ( $expiry[$action] < wfTimestampNow() ) { |
||
299 | $this->show( [ 'protect_expiry_old' ] ); |
||
300 | return false; |
||
301 | } |
||
302 | } |
||
303 | |||
304 | $this->mCascade = $request->getBool( 'mwProtect-cascade' ); |
||
305 | |||
306 | $status = $this->mArticle->doUpdateRestrictions( |
||
307 | $this->mRestrictions, |
||
308 | $expiry, |
||
309 | $this->mCascade, |
||
310 | $reasonstr, |
||
311 | $user |
||
312 | ); |
||
313 | |||
314 | if ( !$status->isOK() ) { |
||
315 | $this->show( $out->parseInline( $status->getWikiText() ) ); |
||
316 | return false; |
||
317 | } |
||
318 | |||
319 | /** |
||
320 | * Give extensions a change to handle added form items |
||
321 | * |
||
322 | * @since 1.19 you can (and you should) return false to abort saving; |
||
323 | * you can also return an array of message name and its parameters |
||
324 | */ |
||
325 | $errorMsg = ''; |
||
326 | if ( !Hooks::run( 'ProtectionForm::save', [ $this->mArticle, &$errorMsg, $reasonstr ] ) ) { |
||
327 | if ( $errorMsg == '' ) { |
||
328 | $errorMsg = [ 'hookaborted' ]; |
||
329 | } |
||
330 | } |
||
331 | if ( $errorMsg != '' ) { |
||
332 | $this->show( $errorMsg ); |
||
0 ignored issues
–
show
|
|||
333 | return false; |
||
334 | } |
||
335 | |||
336 | WatchAction::doWatchOrUnwatch( $request->getCheck( 'mwProtectWatch' ), $this->mTitle, $user ); |
||
337 | |||
338 | return true; |
||
339 | } |
||
340 | |||
341 | /** |
||
342 | * Build the input form |
||
343 | * |
||
344 | * @return string HTML form |
||
345 | */ |
||
346 | function buildForm() { |
||
347 | $context = $this->mContext; |
||
348 | $user = $context->getUser(); |
||
349 | $output = $context->getOutput(); |
||
350 | $lang = $context->getLanguage(); |
||
351 | $cascadingRestrictionLevels = $context->getConfig()->get( 'CascadingRestrictionLevels' ); |
||
352 | $out = ''; |
||
353 | if ( !$this->disabled ) { |
||
354 | $output->addModules( 'mediawiki.legacy.protect' ); |
||
355 | $output->addJsConfigVars( 'wgCascadeableLevels', $cascadingRestrictionLevels ); |
||
356 | $out .= Xml::openElement( 'form', [ 'method' => 'post', |
||
357 | 'action' => $this->mTitle->getLocalURL( 'action=protect' ), |
||
358 | 'id' => 'mw-Protect-Form' ] ); |
||
359 | } |
||
360 | |||
361 | $out .= Xml::openElement( 'fieldset' ) . |
||
362 | Xml::element( 'legend', null, $context->msg( 'protect-legend' )->text() ) . |
||
363 | Xml::openElement( 'table', [ 'id' => 'mwProtectSet' ] ) . |
||
364 | Xml::openElement( 'tbody' ); |
||
365 | |||
366 | $scExpiryOptions = wfMessage( 'protect-expiry-options' )->inContentLanguage()->text(); |
||
367 | $showProtectOptions = $scExpiryOptions !== '-' && !$this->disabled; |
||
368 | |||
369 | // Not all languages have V_x <-> N_x relation |
||
370 | foreach ( $this->mRestrictions as $action => $selected ) { |
||
371 | // Messages: |
||
372 | // restriction-edit, restriction-move, restriction-create, restriction-upload |
||
373 | $msg = $context->msg( 'restriction-' . $action ); |
||
374 | $out .= "<tr><td>" . |
||
375 | Xml::openElement( 'fieldset' ) . |
||
376 | Xml::element( 'legend', null, $msg->exists() ? $msg->text() : $action ) . |
||
377 | Xml::openElement( 'table', [ 'id' => "mw-protect-table-$action" ] ) . |
||
378 | "<tr><td>" . $this->buildSelector( $action, $selected ) . "</td></tr><tr><td>"; |
||
379 | |||
380 | $mProtectexpiry = Xml::label( |
||
381 | $context->msg( 'protectexpiry' )->text(), |
||
382 | "mwProtectExpirySelection-$action" |
||
383 | ); |
||
384 | $mProtectother = Xml::label( |
||
385 | $context->msg( 'protect-othertime' )->text(), |
||
386 | "mwProtect-$action-expires" |
||
387 | ); |
||
388 | |||
389 | $expiryFormOptions = new XmlSelect( |
||
390 | "wpProtectExpirySelection-$action", |
||
391 | "mwProtectExpirySelection-$action", |
||
392 | $this->mExpirySelection[$action] |
||
393 | ); |
||
394 | $expiryFormOptions->setAttribute( 'tabindex', '2' ); |
||
395 | if ( $this->disabled ) { |
||
396 | $expiryFormOptions->setAttribute( 'disabled', 'disabled' ); |
||
397 | } |
||
398 | |||
399 | if ( $this->mExistingExpiry[$action] ) { |
||
400 | if ( $this->mExistingExpiry[$action] == 'infinity' ) { |
||
401 | $existingExpiryMessage = $context->msg( 'protect-existing-expiry-infinity' ); |
||
402 | } else { |
||
403 | $timestamp = $lang->userTimeAndDate( $this->mExistingExpiry[$action], $user ); |
||
404 | $d = $lang->userDate( $this->mExistingExpiry[$action], $user ); |
||
405 | $t = $lang->userTime( $this->mExistingExpiry[$action], $user ); |
||
406 | $existingExpiryMessage = $context->msg( |
||
407 | 'protect-existing-expiry', |
||
408 | $timestamp, |
||
409 | $d, |
||
410 | $t |
||
411 | ); |
||
412 | } |
||
413 | $expiryFormOptions->addOption( $existingExpiryMessage->text(), 'existing' ); |
||
414 | } |
||
415 | |||
416 | $expiryFormOptions->addOption( |
||
417 | $context->msg( 'protect-othertime-op' )->text(), |
||
418 | 'othertime' |
||
419 | ); |
||
420 | foreach ( explode( ',', $scExpiryOptions ) as $option ) { |
||
421 | if ( strpos( $option, ":" ) === false ) { |
||
422 | $show = $value = $option; |
||
423 | } else { |
||
424 | list( $show, $value ) = explode( ":", $option ); |
||
425 | } |
||
426 | $expiryFormOptions->addOption( $show, htmlspecialchars( $value ) ); |
||
427 | } |
||
428 | # Add expiry dropdown |
||
429 | if ( $showProtectOptions && !$this->disabled ) { |
||
430 | $out .= " |
||
431 | <table><tr> |
||
432 | <td class='mw-label'> |
||
433 | {$mProtectexpiry} |
||
434 | </td> |
||
435 | <td class='mw-input'>" . |
||
436 | $expiryFormOptions->getHTML() . |
||
437 | "</td> |
||
438 | </tr></table>"; |
||
439 | } |
||
440 | # Add custom expiry field |
||
441 | $attribs = [ 'id' => "mwProtect-$action-expires" ] + $this->disabledAttrib; |
||
442 | $out .= "<table><tr> |
||
443 | <td class='mw-label'>" . |
||
444 | $mProtectother . |
||
445 | '</td> |
||
446 | <td class="mw-input">' . |
||
447 | Xml::input( "mwProtect-expiry-$action", 50, $this->mExpiry[$action], $attribs ) . |
||
448 | '</td> |
||
449 | </tr></table>'; |
||
450 | $out .= "</td></tr>" . |
||
451 | Xml::closeElement( 'table' ) . |
||
452 | Xml::closeElement( 'fieldset' ) . |
||
453 | "</td></tr>"; |
||
454 | } |
||
455 | # Give extensions a chance to add items to the form |
||
456 | Hooks::run( 'ProtectionForm::buildForm', [ $this->mArticle, &$out ] ); |
||
457 | |||
458 | $out .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' ); |
||
459 | |||
460 | // JavaScript will add another row with a value-chaining checkbox |
||
461 | if ( $this->mTitle->exists() ) { |
||
462 | $out .= Xml::openElement( 'table', [ 'id' => 'mw-protect-table2' ] ) . |
||
463 | Xml::openElement( 'tbody' ); |
||
464 | $out .= '<tr> |
||
465 | <td></td> |
||
466 | <td class="mw-input">' . |
||
467 | Xml::checkLabel( |
||
468 | $context->msg( 'protect-cascade' )->text(), |
||
469 | 'mwProtect-cascade', |
||
470 | 'mwProtect-cascade', |
||
471 | $this->mCascade, $this->disabledAttrib |
||
472 | ) . |
||
473 | "</td> |
||
474 | </tr>\n"; |
||
475 | $out .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' ); |
||
476 | } |
||
477 | |||
478 | # Add manual and custom reason field/selects as well as submit |
||
479 | if ( !$this->disabled ) { |
||
480 | $mProtectreasonother = Xml::label( |
||
481 | $context->msg( 'protectcomment' )->text(), |
||
482 | 'wpProtectReasonSelection' |
||
483 | ); |
||
484 | |||
485 | $mProtectreason = Xml::label( |
||
486 | $context->msg( 'protect-otherreason' )->text(), |
||
487 | 'mwProtect-reason' |
||
488 | ); |
||
489 | |||
490 | $reasonDropDown = Xml::listDropDown( 'wpProtectReasonSelection', |
||
491 | wfMessage( 'protect-dropdown' )->inContentLanguage()->text(), |
||
492 | wfMessage( 'protect-otherreason-op' )->inContentLanguage()->text(), |
||
493 | $this->mReasonSelection, |
||
494 | 'mwProtect-reason', 4 ); |
||
495 | |||
496 | $out .= Xml::openElement( 'table', [ 'id' => 'mw-protect-table3' ] ) . |
||
497 | Xml::openElement( 'tbody' ); |
||
498 | $out .= " |
||
499 | <tr> |
||
500 | <td class='mw-label'> |
||
501 | {$mProtectreasonother} |
||
502 | </td> |
||
503 | <td class='mw-input'> |
||
504 | {$reasonDropDown} |
||
505 | </td> |
||
506 | </tr> |
||
507 | <tr> |
||
508 | <td class='mw-label'> |
||
509 | {$mProtectreason} |
||
510 | </td> |
||
511 | <td class='mw-input'>" . |
||
512 | Xml::input( 'mwProtect-reason', 60, $this->mReason, [ 'type' => 'text', |
||
513 | 'id' => 'mwProtect-reason', 'maxlength' => 180 ] ) . |
||
514 | // Limited maxlength as the database trims at 255 bytes and other texts |
||
515 | // chosen by dropdown menus on this page are also included in this database field. |
||
516 | // The byte limit of 180 bytes is enforced in javascript |
||
517 | "</td> |
||
518 | </tr>"; |
||
519 | # Disallow watching is user is not logged in |
||
520 | if ( $user->isLoggedIn() ) { |
||
521 | $out .= " |
||
522 | <tr> |
||
523 | <td></td> |
||
524 | <td class='mw-input'>" . |
||
525 | Xml::checkLabel( $context->msg( 'watchthis' )->text(), |
||
526 | 'mwProtectWatch', 'mwProtectWatch', |
||
527 | $user->isWatched( $this->mTitle ) || $user->getOption( 'watchdefault' ) ) . |
||
528 | "</td> |
||
529 | </tr>"; |
||
530 | } |
||
531 | $out .= " |
||
532 | <tr> |
||
533 | <td></td> |
||
534 | <td class='mw-submit'>" . |
||
535 | Xml::submitButton( |
||
536 | $context->msg( 'confirm' )->text(), |
||
537 | [ 'id' => 'mw-Protect-submit' ] |
||
538 | ) . |
||
539 | "</td> |
||
540 | </tr>\n"; |
||
541 | $out .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' ); |
||
542 | } |
||
543 | $out .= Xml::closeElement( 'fieldset' ); |
||
544 | |||
545 | View Code Duplication | if ( $user->isAllowed( 'editinterface' ) ) { |
|
546 | $link = Linker::linkKnown( |
||
547 | $context->msg( 'protect-dropdown' )->inContentLanguage()->getTitle(), |
||
548 | $context->msg( 'protect-edit-reasonlist' )->escaped(), |
||
549 | [], |
||
550 | [ 'action' => 'edit' ] |
||
551 | ); |
||
552 | $out .= '<p class="mw-protect-editreasons">' . $link . '</p>'; |
||
553 | } |
||
554 | |||
555 | if ( !$this->disabled ) { |
||
556 | $out .= Html::hidden( |
||
557 | 'wpEditToken', |
||
558 | $user->getEditToken( [ 'protect', $this->mTitle->getPrefixedDBkey() ] ) |
||
559 | ); |
||
560 | $out .= Xml::closeElement( 'form' ); |
||
561 | } |
||
562 | |||
563 | return $out; |
||
564 | } |
||
565 | |||
566 | /** |
||
567 | * Build protection level selector |
||
568 | * |
||
569 | * @param string $action Action to protect |
||
570 | * @param string $selected Current protection level |
||
571 | * @return string HTML fragment |
||
572 | */ |
||
573 | function buildSelector( $action, $selected ) { |
||
574 | // If the form is disabled, display all relevant levels. Otherwise, |
||
575 | // just show the ones this user can use. |
||
576 | $levels = MWNamespace::getRestrictionLevels( $this->mTitle->getNamespace(), |
||
577 | $this->disabled ? null : $this->mContext->getUser() |
||
578 | ); |
||
579 | |||
580 | $id = 'mwProtect-level-' . $action; |
||
581 | |||
582 | $select = new XmlSelect( $id, $id, $selected ); |
||
583 | $select->setAttribute( 'size', count( $levels ) ); |
||
584 | if ( $this->disabled ) { |
||
585 | $select->setAttribute( 'disabled', 'disabled' ); |
||
586 | } |
||
587 | |||
588 | foreach ( $levels as $key ) { |
||
589 | $select->addOption( $this->getOptionLabel( $key ), $key ); |
||
590 | } |
||
591 | |||
592 | return $select->getHTML(); |
||
593 | } |
||
594 | |||
595 | /** |
||
596 | * Prepare the label for a protection selector option |
||
597 | * |
||
598 | * @param string $permission Permission required |
||
599 | * @return string |
||
600 | */ |
||
601 | private function getOptionLabel( $permission ) { |
||
602 | if ( $permission == '' ) { |
||
603 | return $this->mContext->msg( 'protect-default' )->text(); |
||
604 | } else { |
||
605 | // Messages: protect-level-autoconfirmed, protect-level-sysop |
||
606 | $msg = $this->mContext->msg( "protect-level-{$permission}" ); |
||
607 | if ( $msg->exists() ) { |
||
608 | return $msg->text(); |
||
609 | } |
||
610 | return $this->mContext->msg( 'protect-fallback', $permission )->text(); |
||
611 | } |
||
612 | } |
||
613 | |||
614 | /** |
||
615 | * Show protection long extracts for this page |
||
616 | * |
||
617 | * @param OutputPage $out |
||
618 | * @access private |
||
619 | */ |
||
620 | function showLogExtract( &$out ) { |
||
621 | # Show relevant lines from the protection log: |
||
622 | $protectLogPage = new LogPage( 'protect' ); |
||
623 | $out->addHTML( Xml::element( 'h2', null, $protectLogPage->getName()->text() ) ); |
||
624 | LogEventsList::showLogExtract( $out, 'protect', $this->mTitle ); |
||
625 | # Let extensions add other relevant log extracts |
||
626 | Hooks::run( 'ProtectionForm::showLogExtract', [ $this->mArticle, $out ] ); |
||
627 | } |
||
628 | } |
||
629 |
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.