00001 <?php
00025 class ProtectionForm {
00027 var $mRestrictions = array();
00028
00030 var $mReason = '';
00031
00033 var $mReasonSelection = '';
00034
00036 var $mCascade = false;
00037
00039 var $mExpiry = array();
00040
00045 var $mExpirySelection = array();
00046
00048 var $mPermErrors = array();
00049
00051 var $mApplicableTypes = array();
00052
00054 var $mExistingExpiry = array();
00055
00056 function __construct( Article $article ) {
00057 global $wgUser;
00058
00059 $this->mArticle = $article;
00060 $this->mTitle = $article->mTitle;
00061 $this->mApplicableTypes = $this->mTitle->getRestrictionTypes();
00062
00063
00064
00065 $this->mPermErrors = $this->mTitle->getUserPermissionsErrors('protect',$wgUser);
00066 $this->disabled = wfReadOnly() || $this->mPermErrors != array();
00067 $this->disabledAttrib = $this->disabled
00068 ? array( 'disabled' => 'disabled' )
00069 : array();
00070
00071 $this->loadData();
00072 }
00073
00074
00075 function loadData() {
00076 global $wgRequest, $wgUser;
00077 global $wgRestrictionLevels;
00078
00079 $this->mCascade = $this->mTitle->areRestrictionsCascading();
00080
00081 $this->mReason = $wgRequest->getText( 'mwProtect-reason' );
00082 $this->mReasonSelection = $wgRequest->getText( 'wpProtectReasonSelection' );
00083 $this->mCascade = $wgRequest->getBool( 'mwProtect-cascade', $this->mCascade );
00084
00085 foreach( $this->mApplicableTypes as $action ) {
00086
00087
00088
00089
00090 $this->mRestrictions[$action] = implode( '', $this->mTitle->getRestrictions( $action ) );
00091
00092 if ( !$this->mRestrictions[$action] ) {
00093
00094 $existingExpiry = '';
00095 } else {
00096 $existingExpiry = $this->mTitle->getRestrictionExpiry( $action );
00097 }
00098 $this->mExistingExpiry[$action] = $existingExpiry;
00099
00100 $requestExpiry = $wgRequest->getText( "mwProtect-expiry-$action" );
00101 $requestExpirySelection = $wgRequest->getVal( "wpProtectExpirySelection-$action" );
00102
00103 if ( $requestExpiry ) {
00104
00105 $this->mExpiry[$action] = $requestExpiry;
00106 $this->mExpirySelection[$action] = 'othertime';
00107 } elseif ( $requestExpirySelection ) {
00108
00109 $this->mExpiry[$action] = '';
00110 $this->mExpirySelection[$action] = $requestExpirySelection;
00111 } elseif ( $existingExpiry == 'infinity' ) {
00112
00113 $this->mExpiry[$action] = '';
00114 $this->mExpirySelection[$action] = 'infinite';
00115 } elseif ( $existingExpiry ) {
00116
00117 $this->mExpiry[$action] = '';
00118 $this->mExpirySelection[$action] = $existingExpiry;
00119 } else {
00120
00121 $this->mExpiry[$action] = '';
00122 $this->mExpirySelection[$action] = 'infinite';
00123 }
00124
00125 $val = $wgRequest->getVal( "mwProtect-level-$action" );
00126 if( isset( $val ) && in_array( $val, $wgRestrictionLevels ) ) {
00127
00128 if( $val == 'sysop' ) {
00129
00130 if( !$wgUser->isAllowed('protect') && !$wgUser->isAllowed('editprotected') )
00131 continue;
00132 } else {
00133 if( !$wgUser->isAllowed($val) )
00134 continue;
00135 }
00136 $this->mRestrictions[$action] = $val;
00137 }
00138 }
00139 }
00140
00145 function getExpiry( $action ) {
00146 if ( $this->mExpirySelection[$action] == 'existing' ) {
00147 return $this->mExistingExpiry[$action];
00148 } elseif ( $this->mExpirySelection[$action] == 'othertime' ) {
00149 $value = $this->mExpiry[$action];
00150 } else {
00151 $value = $this->mExpirySelection[$action];
00152 }
00153 if ( $value == 'infinite' || $value == 'indefinite' || $value == 'infinity' ) {
00154 $time = Block::infinity();
00155 } else {
00156 $unix = strtotime( $value );
00157
00158 if ( !$unix || $unix === -1 ) {
00159 return false;
00160 }
00161
00162
00163
00164 $time = wfTimestamp( TS_MW, $unix );
00165 }
00166 return $time;
00167 }
00168
00169 function execute() {
00170 global $wgRequest, $wgOut;
00171 if( $wgRequest->wasPosted() ) {
00172 if( $this->save() ) {
00173 $q = $this->mArticle->isRedirect() ? 'redirect=no' : '';
00174 $wgOut->redirect( $this->mTitle->getFullUrl( $q ) );
00175 }
00176 } else {
00177 $this->show();
00178 }
00179 }
00180
00181 function show( $err = null ) {
00182 global $wgOut, $wgUser;
00183
00184 $wgOut->setRobotPolicy( 'noindex,nofollow' );
00185
00186 if( is_null( $this->mTitle ) ||
00187 $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
00188 $wgOut->showFatalError( wfMsg( 'badarticleerror' ) );
00189 return;
00190 }
00191
00192 list( $cascadeSources, ) = $this->mTitle->getCascadeProtectionSources();
00193
00194 if ( $err != "" ) {
00195 $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
00196 $wgOut->addHTML( "<p class='error'>{$err}</p>\n" );
00197 }
00198
00199 if ( $cascadeSources && count($cascadeSources) > 0 ) {
00200 $titles = '';
00201
00202 foreach ( $cascadeSources as $title ) {
00203 $titles .= '* [[:' . $title->getPrefixedText() . "]]\n";
00204 }
00205
00206 $wgOut->wrapWikiMsg( "<div id=\"mw-protect-cascadeon\">\n$1\n" . $titles . "</div>", array( 'protect-cascadeon', count($cascadeSources) ) );
00207 }
00208
00209 $sk = $wgUser->getSkin();
00210 $titleLink = $sk->link( $this->mTitle );
00211 $wgOut->setPageTitle( wfMsg( 'protect-title', $this->mTitle->getPrefixedText() ) );
00212 $wgOut->setSubtitle( wfMsg( 'protect-backlink', $titleLink ) );
00213
00214 # Show an appropriate message if the user isn't allowed or able to change
00215 # the protection settings at this time
00216 if( $this->disabled ) {
00217 if( wfReadOnly() ) {
00218 $wgOut->readOnlyPage();
00219 } elseif( $this->mPermErrors ) {
00220 $wgOut->addWikiText( $wgOut->formatPermissionsErrorMessage( $this->mPermErrors ) );
00221 }
00222 } else {
00223 $wgOut->addWikiMsg( 'protect-text', $this->mTitle->getPrefixedText() );
00224 }
00225
00226 $wgOut->addHTML( $this->buildForm() );
00227
00228 $this->showLogExtract( $wgOut );
00229 }
00230
00231 function save() {
00232 global $wgRequest, $wgUser;
00233 # Permission check!
00234 if ( $this->disabled ) {
00235 $this->show();
00236 return false;
00237 }
00238
00239 $token = $wgRequest->getVal( 'wpEditToken' );
00240 if ( !$wgUser->matchEditToken( $token ) ) {
00241 $this->show( wfMsg( 'sessionfailure' ) );
00242 return false;
00243 }
00244
00245 # Create reason string. Use list and/or custom string.
00246 $reasonstr = $this->mReasonSelection;
00247 if ( $reasonstr != 'other' && $this->mReason != '' ) {
00248
00249 $reasonstr .= wfMsgForContent( 'colon-separator' ) . $this->mReason;
00250 } elseif ( $reasonstr == 'other' ) {
00251 $reasonstr = $this->mReason;
00252 }
00253 $expiry = array();
00254 foreach( $this->mApplicableTypes as $action ) {
00255 $expiry[$action] = $this->getExpiry( $action );
00256 if( empty($this->mRestrictions[$action]) )
00257 continue;
00258 if ( !$expiry[$action] ) {
00259 $this->show( wfMsg( 'protect_expiry_invalid' ) );
00260 return false;
00261 }
00262 if ( $expiry[$action] < wfTimestampNow() ) {
00263 $this->show( wfMsg( 'protect_expiry_old' ) );
00264 return false;
00265 }
00266 }
00267
00268 # They shouldn't be able to do this anyway, but just to make sure, ensure that cascading restrictions aren't being applied
00269 # to a semi-protected page.
00270 global $wgGroupPermissions;
00271
00272 $edit_restriction = isset( $this->mRestrictions['edit'] ) ? $this->mRestrictions['edit'] : '';
00273 $this->mCascade = $wgRequest->getBool( 'mwProtect-cascade' );
00274 if ($this->mCascade && ($edit_restriction != 'protect') &&
00275 !(isset($wgGroupPermissions[$edit_restriction]['protect']) && $wgGroupPermissions[$edit_restriction]['protect'] ) )
00276 $this->mCascade = false;
00277
00278 if ($this->mTitle->exists()) {
00279 $ok = $this->mArticle->updateRestrictions( $this->mRestrictions, $reasonstr, $this->mCascade, $expiry );
00280 } else {
00281 $ok = $this->mTitle->updateTitleProtection( $this->mRestrictions['create'], $reasonstr, $expiry['create'] );
00282 }
00283
00284 if( !$ok ) {
00285 throw new FatalError( "Unknown error at restriction save time." );
00286 }
00287
00288 $errorMsg = '';
00289 # Give extensions a change to handle added form items
00290 if( !wfRunHooks( 'ProtectionForm::save', array($this->mArticle,&$errorMsg) ) ) {
00291 throw new FatalError( "Unknown hook error at restriction save time." );
00292 }
00293 if( $errorMsg != '' ) {
00294 $this->show( $errorMsg );
00295 return false;
00296 }
00297
00298 if( $wgRequest->getCheck( 'mwProtectWatch' ) && $wgUser->isLoggedIn() ) {
00299 $this->mArticle->doWatch();
00300 } elseif( $this->mTitle->userIsWatching() ) {
00301 $this->mArticle->doUnwatch();
00302 }
00303 return $ok;
00304 }
00305
00311 function buildForm() {
00312 global $wgUser, $wgLang;
00313
00314 $mProtectreasonother = Xml::label( wfMsg( 'protectcomment' ), 'wpProtectReasonSelection' );
00315 $mProtectreason = Xml::label( wfMsg( 'protect-otherreason' ), 'mwProtect-reason' );
00316
00317 $out = '';
00318 if( !$this->disabled ) {
00319 $out .= $this->buildScript();
00320 $out .= Xml::openElement( 'form', array( 'method' => 'post',
00321 'action' => $this->mTitle->getLocalUrl( 'action=protect' ),
00322 'id' => 'mw-Protect-Form', 'onsubmit' => 'ProtectionForm.enableUnchainedInputs(true)' ) );
00323 $out .= Xml::hidden( 'wpEditToken',$wgUser->editToken() );
00324 }
00325
00326 $out .= Xml::openElement( 'fieldset' ) .
00327 Xml::element( 'legend', null, wfMsg( 'protect-legend' ) ) .
00328 Xml::openElement( 'table', array( 'id' => 'mwProtectSet' ) ) .
00329 Xml::openElement( 'tbody' );
00330
00331 foreach( $this->mRestrictions as $action => $selected ) {
00332
00333 $msg = wfMsg( 'restriction-' . $action );
00334 if( wfEmptyMsg( 'restriction-' . $action, $msg ) ) {
00335 $msg = $action;
00336 }
00337 $out .= "<tr><td>".
00338 Xml::openElement( 'fieldset' ) .
00339 Xml::element( 'legend', null, $msg ) .
00340 Xml::openElement( 'table', array( 'id' => "mw-protect-table-$action" ) ) .
00341 "<tr><td>" . $this->buildSelector( $action, $selected ) . "</td></tr><tr><td>";
00342
00343 $reasonDropDown = Xml::listDropDown( 'wpProtectReasonSelection',
00344 wfMsgForContent( 'protect-dropdown' ),
00345 wfMsgForContent( 'protect-otherreason-op' ),
00346 $this->mReasonSelection,
00347 'mwProtect-reason', 4 );
00348 $scExpiryOptions = wfMsgForContent( 'protect-expiry-options' );
00349
00350 $showProtectOptions = ($scExpiryOptions !== '-' && !$this->disabled);
00351
00352 $mProtectexpiry = Xml::label( wfMsg( 'protectexpiry' ), "mwProtectExpirySelection-$action" );
00353 $mProtectother = Xml::label( wfMsg( 'protect-othertime' ), "mwProtect-$action-expires" );
00354
00355 $expiryFormOptions = '';
00356 if ( $this->mExistingExpiry[$action] && $this->mExistingExpiry[$action] != 'infinity' ) {
00357 $timestamp = $wgLang->timeanddate( $this->mExistingExpiry[$action] );
00358 $d = $wgLang->date( $this->mExistingExpiry[$action] );
00359 $t = $wgLang->time( $this->mExistingExpiry[$action] );
00360 $expiryFormOptions .=
00361 Xml::option(
00362 wfMsg( 'protect-existing-expiry', $timestamp, $d, $t ),
00363 'existing',
00364 $this->mExpirySelection[$action] == 'existing'
00365 ) . "\n";
00366 }
00367
00368 $expiryFormOptions .= Xml::option( wfMsg( 'protect-othertime-op' ), "othertime" ) . "\n";
00369 foreach( explode(',', $scExpiryOptions) as $option ) {
00370 if ( strpos($option, ":") === false ) {
00371 $show = $value = $option;
00372 } else {
00373 list($show, $value) = explode(":", $option);
00374 }
00375 $show = htmlspecialchars($show);
00376 $value = htmlspecialchars($value);
00377 $expiryFormOptions .= Xml::option( $show, $value, $this->mExpirySelection[$action] === $value ) . "\n";
00378 }
00379 # Add expiry dropdown
00380 if( $showProtectOptions && !$this->disabled ) {
00381 $out .= "
00382 <table><tr>
00383 <td class='mw-label'>
00384 {$mProtectexpiry}
00385 </td>
00386 <td class='mw-input'>" .
00387 Xml::tags( 'select',
00388 array(
00389 'id' => "mwProtectExpirySelection-$action",
00390 'name' => "wpProtectExpirySelection-$action",
00391 'onchange' => "ProtectionForm.updateExpiryList(this)",
00392 'tabindex' => '2' ) + $this->disabledAttrib,
00393 $expiryFormOptions ) .
00394 "</td>
00395 </tr></table>";
00396 }
00397 # Add custom expiry field
00398 $attribs = array( 'id' => "mwProtect-$action-expires",
00399 'onkeyup' => 'ProtectionForm.updateExpiry(this)',
00400 'onchange' => 'ProtectionForm.updateExpiry(this)' ) + $this->disabledAttrib;
00401 $out .= "<table><tr>
00402 <td class='mw-label'>" .
00403 $mProtectother .
00404 '</td>
00405 <td class="mw-input">' .
00406 Xml::input( "mwProtect-expiry-$action", 50, $this->mExpiry[$action], $attribs ) .
00407 '</td>
00408 </tr></table>';
00409 $out .= "</td></tr>" .
00410 Xml::closeElement( 'table' ) .
00411 Xml::closeElement( 'fieldset' ) .
00412 "</td></tr>";
00413 }
00414 # Give extensions a chance to add items to the form
00415 wfRunHooks( 'ProtectionForm::buildForm', array($this->mArticle,&$out) );
00416
00417 $out .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' );
00418
00419
00420 if( $this->mTitle->exists() ) {
00421 $out .= Xml::openElement( 'table', array( 'id' => 'mw-protect-table2' ) ) .
00422 Xml::openElement( 'tbody' );
00423 $out .= '<tr>
00424 <td></td>
00425 <td class="mw-input">' .
00426 Xml::checkLabel( wfMsg( 'protect-cascade' ), 'mwProtect-cascade', 'mwProtect-cascade',
00427 $this->mCascade, $this->disabledAttrib ) .
00428 "</td>
00429 </tr>\n";
00430 $out .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' );
00431 }
00432
00433 # Add manual and custom reason field/selects as well as submit
00434 if( !$this->disabled ) {
00435 $out .= Xml::openElement( 'table', array( 'id' => 'mw-protect-table3' ) ) .
00436 Xml::openElement( 'tbody' );
00437 $out .= "
00438 <tr>
00439 <td class='mw-label'>
00440 {$mProtectreasonother}
00441 </td>
00442 <td class='mw-input'>
00443 {$reasonDropDown}
00444 </td>
00445 </tr>
00446 <tr>
00447 <td class='mw-label'>
00448 {$mProtectreason}
00449 </td>
00450 <td class='mw-input'>" .
00451 Xml::input( 'mwProtect-reason', 60, $this->mReason, array( 'type' => 'text',
00452 'id' => 'mwProtect-reason', 'maxlength' => 255 ) ) .
00453 "</td>
00454 </tr>";
00455 # Disallow watching is user is not logged in
00456 if( $wgUser->isLoggedIn() ) {
00457 $out .= "
00458 <tr>
00459 <td></td>
00460 <td class='mw-input'>" .
00461 Xml::checkLabel( wfMsg( 'watchthis' ),
00462 'mwProtectWatch', 'mwProtectWatch',
00463 $this->mTitle->userIsWatching() || $wgUser->getOption( 'watchdefault' ) ) .
00464 "</td>
00465 </tr>";
00466 }
00467 $out .= "
00468 <tr>
00469 <td></td>
00470 <td class='mw-submit'>" .
00471 Xml::submitButton( wfMsg( 'confirm' ), array( 'id' => 'mw-Protect-submit' ) ) .
00472 "</td>
00473 </tr>\n";
00474 $out .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' );
00475 }
00476 $out .= Xml::closeElement( 'fieldset' );
00477
00478 if ( $wgUser->isAllowed( 'editinterface' ) ) {
00479 $title = Title::makeTitle( NS_MEDIAWIKI, 'Protect-dropdown' );
00480 $link = $wgUser->getSkin()->link(
00481 $title,
00482 wfMsgHtml( 'protect-edit-reasonlist' ),
00483 array(),
00484 array( 'action' => 'edit' )
00485 );
00486 $out .= '<p class="mw-protect-editreasons">' . $link . '</p>';
00487 }
00488
00489 if ( !$this->disabled ) {
00490 $out .= Xml::closeElement( 'form' ) .
00491 $this->buildCleanupScript();
00492 }
00493
00494 return $out;
00495 }
00496
00497 function buildSelector( $action, $selected ) {
00498 global $wgRestrictionLevels, $wgUser;
00499
00500 $levels = array();
00501 foreach( $wgRestrictionLevels as $key ) {
00502
00503 if( $key == 'sysop' ) {
00504
00505 if( !$wgUser->isAllowed('protect') && !$wgUser->isAllowed('editprotected') && !$this->disabled )
00506 continue;
00507 } else {
00508 if( !$wgUser->isAllowed($key) && !$this->disabled )
00509 continue;
00510 }
00511 $levels[] = $key;
00512 }
00513
00514 $id = 'mwProtect-level-' . $action;
00515 $attribs = array(
00516 'id' => $id,
00517 'name' => $id,
00518 'size' => count( $levels ),
00519 'onchange' => 'ProtectionForm.updateLevels(this)',
00520 ) + $this->disabledAttrib;
00521
00522 $out = Xml::openElement( 'select', $attribs );
00523 foreach( $levels as $key ) {
00524 $out .= Xml::option( $this->getOptionLabel( $key ), $key, $key == $selected );
00525 }
00526 $out .= Xml::closeElement( 'select' );
00527 return $out;
00528 }
00529
00536 private function getOptionLabel( $permission ) {
00537 if( $permission == '' ) {
00538 return wfMsg( 'protect-default' );
00539 } else {
00540 $key = "protect-level-{$permission}";
00541 $msg = wfMsg( $key );
00542 if( wfEmptyMsg( $key, $msg ) )
00543 $msg = wfMsg( 'protect-fallback', $permission );
00544 return $msg;
00545 }
00546 }
00547
00548 function buildScript() {
00549 global $wgStylePath, $wgStyleVersion;
00550 return Xml::tags( 'script', array(
00551 'type' => 'text/javascript',
00552 'src' => $wgStylePath . "/common/protect.js?$wgStyleVersion.1" ), '' );
00553 }
00554
00555 function buildCleanupScript() {
00556 global $wgRestrictionLevels, $wgGroupPermissions;
00557 $script = 'var wgCascadeableLevels=';
00558 $CascadeableLevels = array();
00559 foreach( $wgRestrictionLevels as $key ) {
00560 if ( (isset($wgGroupPermissions[$key]['protect']) && $wgGroupPermissions[$key]['protect']) || $key == 'protect' ) {
00561 $CascadeableLevels[] = "'" . Xml::escapeJsString( $key ) . "'";
00562 }
00563 }
00564 $script .= "[" . implode(',',$CascadeableLevels) . "];\n";
00565 $options = (object)array(
00566 'tableId' => 'mwProtectSet',
00567 'labelText' => wfMsg( 'protect-unchain-permissions' ),
00568 'numTypes' => count($this->mApplicableTypes),
00569 'existingMatch' => 1 == count( array_unique( $this->mExistingExpiry ) ),
00570 );
00571 $encOptions = Xml::encodeJsVar( $options );
00572
00573 $script .= "ProtectionForm.init($encOptions)";
00574 return Xml::tags( 'script', array( 'type' => 'text/javascript' ), $script );
00575 }
00576
00581 function showLogExtract( &$out ) {
00582 # Show relevant lines from the protection log:
00583 $out->addHTML( Xml::element( 'h2', null, LogPage::logName( 'protect' ) ) );
00584 LogEventsList::showLogExtract( $out, 'protect', $this->mTitle->getPrefixedText() );
00585 # Let extensions add other relevant log extracts
00586 wfRunHooks( 'ProtectionForm::showLogExtract', array($this->mArticle,$out) );
00587 }
00588 }