MediaWiki
REL1_24
|
00001 <?php 00029 class ProtectionForm { 00031 protected $mRestrictions = array(); 00032 00034 protected $mReason = ''; 00035 00037 protected $mReasonSelection = ''; 00038 00040 protected $mCascade = false; 00041 00043 protected $mExpiry = array(); 00044 00049 protected $mExpirySelection = array(); 00050 00052 protected $mPermErrors = array(); 00053 00055 protected $mApplicableTypes = array(); 00056 00058 protected $mExistingExpiry = array(); 00059 00061 private $mContext; 00062 00063 function __construct( Article $article ) { 00064 // Set instance variables. 00065 $this->mArticle = $article; 00066 $this->mTitle = $article->getTitle(); 00067 $this->mApplicableTypes = $this->mTitle->getRestrictionTypes(); 00068 $this->mContext = $article->getContext(); 00069 00070 // Check if the form should be disabled. 00071 // If it is, the form will be available in read-only to show levels. 00072 $this->mPermErrors = $this->mTitle->getUserPermissionsErrors( 00073 'protect', $this->mContext->getUser() 00074 ); 00075 if ( wfReadOnly() ) { 00076 $this->mPermErrors[] = array( 'readonlytext', wfReadOnlyReason() ); 00077 } 00078 $this->disabled = $this->mPermErrors != array(); 00079 $this->disabledAttrib = $this->disabled 00080 ? array( 'disabled' => 'disabled' ) 00081 : array(); 00082 00083 $this->loadData(); 00084 } 00085 00089 function loadData() { 00090 $levels = MWNamespace::getRestrictionLevels( 00091 $this->mTitle->getNamespace(), $this->mContext->getUser() 00092 ); 00093 $this->mCascade = $this->mTitle->areRestrictionsCascading(); 00094 00095 $request = $this->mContext->getRequest(); 00096 $this->mReason = $request->getText( 'mwProtect-reason' ); 00097 $this->mReasonSelection = $request->getText( 'wpProtectReasonSelection' ); 00098 $this->mCascade = $request->getBool( 'mwProtect-cascade', $this->mCascade ); 00099 00100 foreach ( $this->mApplicableTypes as $action ) { 00101 // @todo FIXME: This form currently requires individual selections, 00102 // but the db allows multiples separated by commas. 00103 00104 // Pull the actual restriction from the DB 00105 $this->mRestrictions[$action] = implode( '', $this->mTitle->getRestrictions( $action ) ); 00106 00107 if ( !$this->mRestrictions[$action] ) { 00108 // No existing expiry 00109 $existingExpiry = ''; 00110 } else { 00111 $existingExpiry = $this->mTitle->getRestrictionExpiry( $action ); 00112 } 00113 $this->mExistingExpiry[$action] = $existingExpiry; 00114 00115 $requestExpiry = $request->getText( "mwProtect-expiry-$action" ); 00116 $requestExpirySelection = $request->getVal( "wpProtectExpirySelection-$action" ); 00117 00118 if ( $requestExpiry ) { 00119 // Custom expiry takes precedence 00120 $this->mExpiry[$action] = $requestExpiry; 00121 $this->mExpirySelection[$action] = 'othertime'; 00122 } elseif ( $requestExpirySelection ) { 00123 // Expiry selected from list 00124 $this->mExpiry[$action] = ''; 00125 $this->mExpirySelection[$action] = $requestExpirySelection; 00126 } elseif ( $existingExpiry ) { 00127 // Use existing expiry in its own list item 00128 $this->mExpiry[$action] = ''; 00129 $this->mExpirySelection[$action] = $existingExpiry; 00130 } else { 00131 // Catches 'infinity' - Existing expiry is infinite, use "infinite" in drop-down 00132 // Final default: infinite 00133 $this->mExpiry[$action] = ''; 00134 $this->mExpirySelection[$action] = 'infinite'; 00135 } 00136 00137 $val = $request->getVal( "mwProtect-level-$action" ); 00138 if ( isset( $val ) && in_array( $val, $levels ) ) { 00139 $this->mRestrictions[$action] = $val; 00140 } 00141 } 00142 } 00143 00151 function getExpiry( $action ) { 00152 if ( $this->mExpirySelection[$action] == 'existing' ) { 00153 return $this->mExistingExpiry[$action]; 00154 } elseif ( $this->mExpirySelection[$action] == 'othertime' ) { 00155 $value = $this->mExpiry[$action]; 00156 } else { 00157 $value = $this->mExpirySelection[$action]; 00158 } 00159 if ( $value == 'infinite' || $value == 'indefinite' || $value == 'infinity' ) { 00160 $time = wfGetDB( DB_SLAVE )->getInfinity(); 00161 } else { 00162 $unix = strtotime( $value ); 00163 00164 if ( !$unix || $unix === -1 ) { 00165 return false; 00166 } 00167 00168 // @todo FIXME: Non-qualified absolute times are not in users specified timezone 00169 // and there isn't notice about it in the ui 00170 $time = wfTimestamp( TS_MW, $unix ); 00171 } 00172 return $time; 00173 } 00174 00178 function execute() { 00179 if ( MWNamespace::getRestrictionLevels( $this->mTitle->getNamespace() ) === array( '' ) ) { 00180 throw new ErrorPageError( 'protect-badnamespace-title', 'protect-badnamespace-text' ); 00181 } 00182 00183 if ( $this->mContext->getRequest()->wasPosted() ) { 00184 if ( $this->save() ) { 00185 $q = $this->mArticle->isRedirect() ? 'redirect=no' : ''; 00186 $this->mContext->getOutput()->redirect( $this->mTitle->getFullURL( $q ) ); 00187 } 00188 } else { 00189 $this->show(); 00190 } 00191 } 00192 00198 function show( $err = null ) { 00199 $out = $this->mContext->getOutput(); 00200 $out->setRobotPolicy( 'noindex,nofollow' ); 00201 $out->addBacklinkSubtitle( $this->mTitle ); 00202 00203 if ( is_array( $err ) ) { 00204 $out->wrapWikiMsg( "<p class='error'>\n$1\n</p>\n", $err ); 00205 } elseif ( is_string( $err ) ) { 00206 $out->addHTML( "<p class='error'>{$err}</p>\n" ); 00207 } 00208 00209 if ( $this->mTitle->getRestrictionTypes() === array() ) { 00210 // No restriction types available for the current title 00211 // this might happen if an extension alters the available types 00212 $out->setPageTitle( wfMessage( 00213 'protect-norestrictiontypes-title', 00214 $this->mTitle->getPrefixedText() 00215 ) ); 00216 $out->addWikiText( wfMessage( 'protect-norestrictiontypes-text' )->text() ); 00217 00218 // Show the log in case protection was possible once 00219 $this->showLogExtract( $out ); 00220 // return as there isn't anything else we can do 00221 return; 00222 } 00223 00224 list( $cascadeSources, /* $restrictions */ ) = $this->mTitle->getCascadeProtectionSources(); 00225 if ( $cascadeSources && count( $cascadeSources ) > 0 ) { 00226 $titles = ''; 00227 00228 foreach ( $cascadeSources as $title ) { 00229 $titles .= '* [[:' . $title->getPrefixedText() . "]]\n"; 00230 } 00231 00233 $out->wrapWikiMsg( 00234 "<div id=\"mw-protect-cascadeon\">\n$1\n" . $titles . "</div>", 00235 array( 'protect-cascadeon', count( $cascadeSources ) ) 00236 ); 00237 } 00238 00239 # Show an appropriate message if the user isn't allowed or able to change 00240 # the protection settings at this time 00241 if ( $this->disabled ) { 00242 $out->setPageTitle( 00243 wfMessage( 'protect-title-notallowed', 00244 $this->mTitle->getPrefixedText() ) 00245 ); 00246 $out->addWikiText( $out->formatPermissionsErrorMessage( $this->mPermErrors, 'protect' ) ); 00247 } else { 00248 $out->setPageTitle( wfMessage( 'protect-title', $this->mTitle->getPrefixedText() ) ); 00249 $out->addWikiMsg( 'protect-text', 00250 wfEscapeWikiText( $this->mTitle->getPrefixedText() ) ); 00251 } 00252 00253 $out->addHTML( $this->buildForm() ); 00254 $this->showLogExtract( $out ); 00255 } 00256 00262 function save() { 00263 # Permission check! 00264 if ( $this->disabled ) { 00265 $this->show(); 00266 return false; 00267 } 00268 00269 $request = $this->mContext->getRequest(); 00270 $user = $this->mContext->getUser(); 00271 $out = $this->mContext->getOutput(); 00272 $token = $request->getVal( 'wpEditToken' ); 00273 if ( !$user->matchEditToken( $token, array( 'protect', $this->mTitle->getPrefixedDBkey() ) ) ) { 00274 $this->show( array( 'sessionfailure' ) ); 00275 return false; 00276 } 00277 00278 # Create reason string. Use list and/or custom string. 00279 $reasonstr = $this->mReasonSelection; 00280 if ( $reasonstr != 'other' && $this->mReason != '' ) { 00281 // Entry from drop down menu + additional comment 00282 $reasonstr .= wfMessage( 'colon-separator' )->text() . $this->mReason; 00283 } elseif ( $reasonstr == 'other' ) { 00284 $reasonstr = $this->mReason; 00285 } 00286 $expiry = array(); 00287 foreach ( $this->mApplicableTypes as $action ) { 00288 $expiry[$action] = $this->getExpiry( $action ); 00289 if ( empty( $this->mRestrictions[$action] ) ) { 00290 continue; // unprotected 00291 } 00292 if ( !$expiry[$action] ) { 00293 $this->show( array( 'protect_expiry_invalid' ) ); 00294 return false; 00295 } 00296 if ( $expiry[$action] < wfTimestampNow() ) { 00297 $this->show( array( 'protect_expiry_old' ) ); 00298 return false; 00299 } 00300 } 00301 00302 $this->mCascade = $request->getBool( 'mwProtect-cascade' ); 00303 00304 $status = $this->mArticle->doUpdateRestrictions( 00305 $this->mRestrictions, 00306 $expiry, 00307 $this->mCascade, 00308 $reasonstr, 00309 $user 00310 ); 00311 00312 if ( !$status->isOK() ) { 00313 $this->show( $out->parseInline( $status->getWikiText() ) ); 00314 return false; 00315 } 00316 00323 $errorMsg = ''; 00324 if ( !wfRunHooks( 'ProtectionForm::save', array( $this->mArticle, &$errorMsg, $reasonstr ) ) ) { 00325 if ( $errorMsg == '' ) { 00326 $errorMsg = array( 'hookaborted' ); 00327 } 00328 } 00329 if ( $errorMsg != '' ) { 00330 $this->show( $errorMsg ); 00331 return false; 00332 } 00333 00334 WatchAction::doWatchOrUnwatch( $request->getCheck( 'mwProtectWatch' ), $this->mTitle, $user ); 00335 00336 return true; 00337 } 00338 00344 function buildForm() { 00345 $user = $this->mContext->getUser(); 00346 $output = $this->mContext->getOutput(); 00347 $lang = $this->mContext->getLanguage(); 00348 $cascadingRestrictionLevels = $this->mContext->getConfig()->get( 'CascadingRestrictionLevels' ); 00349 $out = ''; 00350 if ( !$this->disabled ) { 00351 $output->addModules( 'mediawiki.legacy.protect' ); 00352 $output->addJsConfigVars( 'wgCascadeableLevels', $cascadingRestrictionLevels ); 00353 $out .= Xml::openElement( 'form', array( 'method' => 'post', 00354 'action' => $this->mTitle->getLocalURL( 'action=protect' ), 00355 'id' => 'mw-Protect-Form' ) ); 00356 } 00357 00358 $out .= Xml::openElement( 'fieldset' ) . 00359 Xml::element( 'legend', null, wfMessage( 'protect-legend' )->text() ) . 00360 Xml::openElement( 'table', array( 'id' => 'mwProtectSet' ) ) . 00361 Xml::openElement( 'tbody' ); 00362 00363 $scExpiryOptions = wfMessage( 'protect-expiry-options' )->inContentLanguage()->text(); 00364 $showProtectOptions = $scExpiryOptions !== '-' && !$this->disabled; 00365 00366 // Not all languages have V_x <-> N_x relation 00367 foreach ( $this->mRestrictions as $action => $selected ) { 00368 // Messages: 00369 // restriction-edit, restriction-move, restriction-create, restriction-upload 00370 $msg = wfMessage( 'restriction-' . $action ); 00371 $out .= "<tr><td>" . 00372 Xml::openElement( 'fieldset' ) . 00373 Xml::element( 'legend', null, $msg->exists() ? $msg->text() : $action ) . 00374 Xml::openElement( 'table', array( 'id' => "mw-protect-table-$action" ) ) . 00375 "<tr><td>" . $this->buildSelector( $action, $selected ) . "</td></tr><tr><td>"; 00376 00377 $mProtectexpiry = Xml::label( 00378 wfMessage( 'protectexpiry' )->text(), 00379 "mwProtectExpirySelection-$action" 00380 ); 00381 $mProtectother = Xml::label( 00382 wfMessage( 'protect-othertime' )->text(), 00383 "mwProtect-$action-expires" 00384 ); 00385 00386 $expiryFormOptions = ''; 00387 if ( $this->mExistingExpiry[$action] ) { 00388 if ( $this->mExistingExpiry[$action] == 'infinity' ) { 00389 $existingExpiryMessage = wfMessage( 'protect-existing-expiry-infinity' ); 00390 } else { 00391 $timestamp = $lang->timeanddate( $this->mExistingExpiry[$action], true ); 00392 $d = $lang->date( $this->mExistingExpiry[$action], true ); 00393 $t = $lang->time( $this->mExistingExpiry[$action], true ); 00394 $existingExpiryMessage = wfMessage( 'protect-existing-expiry', $timestamp, $d, $t ); 00395 } 00396 $expiryFormOptions .= 00397 Xml::option( 00398 $existingExpiryMessage->text(), 00399 'existing', 00400 $this->mExpirySelection[$action] == 'existing' 00401 ) . "\n"; 00402 } 00403 00404 $expiryFormOptions .= Xml::option( 00405 wfMessage( 'protect-othertime-op' )->text(), 00406 "othertime" 00407 ) . "\n"; 00408 foreach ( explode( ',', $scExpiryOptions ) as $option ) { 00409 if ( strpos( $option, ":" ) === false ) { 00410 $show = $value = $option; 00411 } else { 00412 list( $show, $value ) = explode( ":", $option ); 00413 } 00414 $show = htmlspecialchars( $show ); 00415 $value = htmlspecialchars( $value ); 00416 $expiryFormOptions .= Xml::option( 00417 $show, 00418 $value, 00419 $this->mExpirySelection[$action] === $value 00420 ) . "\n"; 00421 } 00422 # Add expiry dropdown 00423 if ( $showProtectOptions && !$this->disabled ) { 00424 $out .= " 00425 <table><tr> 00426 <td class='mw-label'> 00427 {$mProtectexpiry} 00428 </td> 00429 <td class='mw-input'>" . 00430 Xml::tags( 'select', 00431 array( 00432 'id' => "mwProtectExpirySelection-$action", 00433 'name' => "wpProtectExpirySelection-$action", 00434 'tabindex' => '2' ) + $this->disabledAttrib, 00435 $expiryFormOptions ) . 00436 "</td> 00437 </tr></table>"; 00438 } 00439 # Add custom expiry field 00440 $attribs = array( 'id' => "mwProtect-$action-expires" ) + $this->disabledAttrib; 00441 $out .= "<table><tr> 00442 <td class='mw-label'>" . 00443 $mProtectother . 00444 '</td> 00445 <td class="mw-input">' . 00446 Xml::input( "mwProtect-expiry-$action", 50, $this->mExpiry[$action], $attribs ) . 00447 '</td> 00448 </tr></table>'; 00449 $out .= "</td></tr>" . 00450 Xml::closeElement( 'table' ) . 00451 Xml::closeElement( 'fieldset' ) . 00452 "</td></tr>"; 00453 } 00454 # Give extensions a chance to add items to the form 00455 wfRunHooks( 'ProtectionForm::buildForm', array( $this->mArticle, &$out ) ); 00456 00457 $out .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' ); 00458 00459 // JavaScript will add another row with a value-chaining checkbox 00460 if ( $this->mTitle->exists() ) { 00461 $out .= Xml::openElement( 'table', array( 'id' => 'mw-protect-table2' ) ) . 00462 Xml::openElement( 'tbody' ); 00463 $out .= '<tr> 00464 <td></td> 00465 <td class="mw-input">' . 00466 Xml::checkLabel( 00467 wfMessage( 'protect-cascade' )->text(), 00468 'mwProtect-cascade', 00469 'mwProtect-cascade', 00470 $this->mCascade, $this->disabledAttrib 00471 ) . 00472 "</td> 00473 </tr>\n"; 00474 $out .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' ); 00475 } 00476 00477 # Add manual and custom reason field/selects as well as submit 00478 if ( !$this->disabled ) { 00479 $mProtectreasonother = Xml::label( 00480 wfMessage( 'protectcomment' )->text(), 00481 'wpProtectReasonSelection' 00482 ); 00483 00484 $mProtectreason = Xml::label( 00485 wfMessage( 'protect-otherreason' )->text(), 00486 'mwProtect-reason' 00487 ); 00488 00489 $reasonDropDown = Xml::listDropDown( 'wpProtectReasonSelection', 00490 wfMessage( 'protect-dropdown' )->inContentLanguage()->text(), 00491 wfMessage( 'protect-otherreason-op' )->inContentLanguage()->text(), 00492 $this->mReasonSelection, 00493 'mwProtect-reason', 4 ); 00494 00495 $out .= Xml::openElement( 'table', array( 'id' => 'mw-protect-table3' ) ) . 00496 Xml::openElement( 'tbody' ); 00497 $out .= " 00498 <tr> 00499 <td class='mw-label'> 00500 {$mProtectreasonother} 00501 </td> 00502 <td class='mw-input'> 00503 {$reasonDropDown} 00504 </td> 00505 </tr> 00506 <tr> 00507 <td class='mw-label'> 00508 {$mProtectreason} 00509 </td> 00510 <td class='mw-input'>" . 00511 Xml::input( 'mwProtect-reason', 60, $this->mReason, array( 'type' => 'text', 00512 'id' => 'mwProtect-reason', 'maxlength' => 180 ) ) . 00513 // Limited maxlength as the database trims at 255 bytes and other texts 00514 // chosen by dropdown menus on this page are also included in this database field. 00515 // The byte limit of 180 bytes is enforced in javascript 00516 "</td> 00517 </tr>"; 00518 # Disallow watching is user is not logged in 00519 if ( $user->isLoggedIn() ) { 00520 $out .= " 00521 <tr> 00522 <td></td> 00523 <td class='mw-input'>" . 00524 Xml::checkLabel( wfMessage( 'watchthis' )->text(), 00525 'mwProtectWatch', 'mwProtectWatch', 00526 $user->isWatched( $this->mTitle ) || $user->getOption( 'watchdefault' ) ) . 00527 "</td> 00528 </tr>"; 00529 } 00530 $out .= " 00531 <tr> 00532 <td></td> 00533 <td class='mw-submit'>" . 00534 Xml::submitButton( 00535 wfMessage( 'confirm' )->text(), 00536 array( 'id' => 'mw-Protect-submit' ) 00537 ) . 00538 "</td> 00539 </tr>\n"; 00540 $out .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' ); 00541 } 00542 $out .= Xml::closeElement( 'fieldset' ); 00543 00544 if ( $user->isAllowed( 'editinterface' ) ) { 00545 $title = Title::makeTitle( NS_MEDIAWIKI, 'Protect-dropdown' ); 00546 $link = Linker::link( 00547 $title, 00548 wfMessage( 'protect-edit-reasonlist' )->escaped(), 00549 array(), 00550 array( 'action' => 'edit' ) 00551 ); 00552 $out .= '<p class="mw-protect-editreasons">' . $link . '</p>'; 00553 } 00554 00555 if ( !$this->disabled ) { 00556 $out .= Html::hidden( 00557 'wpEditToken', 00558 $user->getEditToken( array( 'protect', $this->mTitle->getPrefixedDBkey() ) ) 00559 ); 00560 $out .= Xml::closeElement( 'form' ); 00561 } 00562 00563 return $out; 00564 } 00565 00573 function buildSelector( $action, $selected ) { 00574 // If the form is disabled, display all relevant levels. Otherwise, 00575 // just show the ones this user can use. 00576 $levels = MWNamespace::getRestrictionLevels( $this->mTitle->getNamespace(), 00577 $this->disabled ? null : $this->mContext->getUser() 00578 ); 00579 00580 $id = 'mwProtect-level-' . $action; 00581 $attribs = array( 00582 'id' => $id, 00583 'name' => $id, 00584 'size' => count( $levels ), 00585 ) + $this->disabledAttrib; 00586 00587 $out = Xml::openElement( 'select', $attribs ); 00588 foreach ( $levels as $key ) { 00589 $out .= Xml::option( $this->getOptionLabel( $key ), $key, $key == $selected ); 00590 } 00591 $out .= Xml::closeElement( 'select' ); 00592 return $out; 00593 } 00594 00601 private function getOptionLabel( $permission ) { 00602 if ( $permission == '' ) { 00603 return wfMessage( 'protect-default' )->text(); 00604 } else { 00605 // Messages: protect-level-autoconfirmed, protect-level-sysop 00606 $msg = wfMessage( "protect-level-{$permission}" ); 00607 if ( $msg->exists() ) { 00608 return $msg->text(); 00609 } 00610 return wfMessage( 'protect-fallback', $permission )->text(); 00611 } 00612 } 00613 00620 function showLogExtract( &$out ) { 00621 # Show relevant lines from the protection log: 00622 $protectLogPage = new LogPage( 'protect' ); 00623 $out->addHTML( Xml::element( 'h2', null, $protectLogPage->getName()->text() ) ); 00624 LogEventsList::showLogExtract( $out, 'protect', $this->mTitle ); 00625 # Let extensions add other relevant log extracts 00626 wfRunHooks( 'ProtectionForm::showLogExtract', array( $this->mArticle, $out ) ); 00627 } 00628 }