X-Git-Url: https://git.cyclocoop.org/%27.WWW_URL.%27admin/?a=blobdiff_plain;f=includes%2Fspecials%2FSpecialBlock.php;h=299d758435f07c6fbc54b17c4c6643f6bce328d3;hb=4eb5827a1f680feb1e92d6bd52ac84c7c4c1d488;hp=91d8af96ce2a442d6d8949560ad68d23b232586d;hpb=b11b0c798d6f6a52bd56a6a82387ac35af51eeb4;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/specials/SpecialBlock.php b/includes/specials/SpecialBlock.php index 91d8af96ce..299d758435 100644 --- a/includes/specials/SpecialBlock.php +++ b/includes/specials/SpecialBlock.php @@ -40,9 +40,18 @@ class SpecialBlock extends SpecialPage { /// @var Block::TYPE_ constant protected $type; + /// @var User|String the previous block target + protected $previousTarget; + + /// @var Bool whether the previous submission of the form asked for HideUser + protected $requestedHideUser; + /// @var Bool protected $alreadyBlocked; + /// @var Array + protected $preErrors = array(); + public function __construct() { parent::__construct( 'Block', 'block' ); } @@ -70,7 +79,10 @@ class SpecialBlock extends SpecialPage { # User logs, UserRights, etc. $wgUser->getSkin()->setRelevantUser( $this->target ); } - + + list( $this->previousTarget, /*...*/ ) = Block::parseTarget( $wgRequest->getVal( 'wpPreviousTarget' ) ); + $this->requestedHideUser = $wgRequest->getBool( 'wpHideUser' ); + # bug 15810: blocked admins should have limited access here $status = self::checkUnblockSelf( $this->target ); if ( $status !== true ) { @@ -81,10 +93,9 @@ class SpecialBlock extends SpecialPage { $wgOut->addModules( 'mediawiki.special', 'mediawiki.special.block' ); $fields = self::getFormFields(); - $this->alreadyBlocked = $this->maybeAlterFormDefaults( $fields ); + $this->maybeAlterFormDefaults( $fields ); - $form = new HTMLForm( $fields ); - $form->setTitle( $this->getTitle() ); + $form = new HTMLForm( $fields, $this->getContext() ); $form->setWrapperLegend( wfMsg( 'blockip-legend' ) ); $form->setSubmitCallback( array( __CLASS__, 'processForm' ) ); @@ -94,6 +105,7 @@ class SpecialBlock extends SpecialPage { $form->setSubmitText( $t ); $this->doPreText( $form ); + $this->doHeadertext( $form ); $this->doPostText( $form ); if( $form->show() ){ @@ -120,7 +132,7 @@ class SpecialBlock extends SpecialPage { 'validation-callback' => array( __CLASS__, 'validateTargetField' ), ), 'Expiry' => array( - 'type' => 'selectorother', + 'type' => !count( self::getSuggestedDurations() ) ? 'text' : 'selectorother', 'label-message' => 'ipbexpiry', 'required' => true, 'tabindex' => '2', @@ -139,10 +151,6 @@ class SpecialBlock extends SpecialPage { ), ); - if( wfMsgForContent( 'ipboptions' ) == '-' ){ - $a['Expiry']['type'] = 'text'; - } - if( self::canBlockEmail( $wgUser ) ) { $a['DisableEmail'] = array( 'type' => 'check', @@ -187,11 +195,20 @@ class SpecialBlock extends SpecialPage { 'default' => false, ); - $a['AlreadyBlocked'] = array( + # This is basically a copy of the Target field, but the user can't change it, so we + # can see if the warnings we maybe showed to the user before still apply + $a['PreviousTarget'] = array( 'type' => 'hidden', 'default' => false, ); + # We'll turn this into a checkbox if we need to + $a['Confirm'] = array( + 'type' => 'hidden', + 'default' => '', + 'label-message' => 'ipb-confirm', + ); + return $a; } @@ -203,23 +220,45 @@ class SpecialBlock extends SpecialPage { * already blocked) */ protected function maybeAlterFormDefaults( &$fields ){ - $fields['Target']['default'] = $this->target; + global $wgRequest, $wgUser; + + # This will be overwritten by request data + $fields['Target']['default'] = (string)$this->target; + + # This won't be + $fields['PreviousTarget']['default'] = (string)$this->target; - $block = self::getBlockFromTargetAndType( $this->target, $this->type ); + $block = Block::newFromTarget( $this->target ); if( $block instanceof Block && !$block->mAuto # The block exists and isn't an autoblock && ( $this->type != Block::TYPE_RANGE # The block isn't a rangeblock - || $block->mAddress == $this->target ) # or if it is, the range is what we're about to block + || $block->getTarget() == $this->target ) # or if it is, the range is what we're about to block ) { - $fields['HardBlock']['default'] = !$block->mAnonOnly; - $fields['CreateAccount']['default'] = $block->mCreateAccount; - $fields['AutoBlock']['default'] = $block->mEnableAutoblock; - $fields['DisableEmail']['default'] = $block->mBlockEmail; - $fields['HideUser']['default'] = $block->mHideName; - $fields['DisableUTEdit']['default'] = !$block->mAllowUsertalk; + $fields['HardBlock']['default'] = $block->isHardblock(); + $fields['CreateAccount']['default'] = $block->prevents( 'createaccount' ); + $fields['AutoBlock']['default'] = $block->isAutoblocking(); + if( isset( $fields['DisableEmail'] ) ){ + $fields['DisableEmail']['default'] = $block->prevents( 'sendemail' ); + } + if( isset( $fields['HideUser'] ) ){ + $fields['HideUser']['default'] = $block->mHideName; + } + if( isset( $fields['DisableUTEdit'] ) ){ + $fields['DisableUTEdit']['default'] = $block->prevents( 'editownusertalk' ); + } $fields['Reason']['default'] = $block->mReason; - $fields['AlreadyBlocked']['default'] = true; + + if( $wgRequest->wasPosted() ){ + # Ok, so we got a POST submission asking us to reblock a user. So show the + # confirm checkbox; the user will only see it if they haven't previously + $fields['Confirm']['type'] = 'check'; + } else { + # We got a target, but it wasn't a POST request, so the user must have gone + # to a link like [[Special:Block/User]]. We don't need to show the checkbox + # as long as they go ahead and block *that* user + $fields['Confirm']['default'] = 1; + } if( $block->mExpiry == 'infinity' ) { $fields['Expiry']['default'] = 'indefinite'; @@ -227,9 +266,23 @@ class SpecialBlock extends SpecialPage { $fields['Expiry']['default'] = wfTimestamp( TS_RFC2822, $block->mExpiry ); } - return true; + $this->alreadyBlocked = true; + $this->preErrors[] = array( 'ipb-needreblock', (string)$block->getTarget() ); + } + + # We always need confirmation to do HideUser + if( $this->requestedHideUser ){ + $fields['Confirm']['type'] = 'check'; + unset( $fields['Confirm']['default'] ); + $this->preErrors[] = 'ipb-confirmhideuser'; + } + + # Or if the user is trying to block themselves + if( (string)$this->target === $wgUser->getName() ){ + $fields['Confirm']['type'] = 'check'; + unset( $fields['Confirm']['default'] ); + $this->preErrors[] = 'ipb-blockingself'; } - return false; } /** @@ -263,17 +316,25 @@ class SpecialBlock extends SpecialPage { $form->addPreText( $s ); } } + } - # Username/IP is blocked already locally - if( $this->alreadyBlocked ) { - $form->addPreText( Html::rawElement( - 'div', - array( 'class' => 'mw-ipb-needreblock', ), - wfMsgExt( - 'ipb-needreblock', - array( 'parseinline' ), - $this->target - ) ) ); + /** + * Add header text inside the form, just underneath where the errors would go + * @param $form HTMLForm + * @return void + */ + protected function doHeaderText( HTMLForm &$form ){ + global $wgRequest; + # Don't need to do anything if the form has been posted + if( !$wgRequest->wasPosted() && $this->preErrors ){ + $s = HTMLForm::formatErrors( $this->preErrors ); + if( $s ){ + $form->addHeaderText( Html::rawElement( + 'div', + array( 'class' => 'error' ), + $s + ) ); + } } } @@ -297,19 +358,18 @@ class SpecialBlock extends SpecialPage { } # Link to unblock the specified user, or to a blank unblock form - $list = SpecialPage::getTitleFor( 'Ipblocklist' ); - $query = array( 'action' => 'unblock' ); if( $this->target instanceof User ) { $message = wfMsgExt( 'ipb-unblock-addr', array( 'parseinline' ), $this->target->getName() ); - $query['ip'] = $this->target->getName(); + $list = SpecialPage::getTitleFor( 'Unblock', $this->target->getName() ); } else { $message = wfMsgExt( 'ipb-unblock', array( 'parseinline' ) ); + $list = SpecialPage::getTitleFor( 'Unblock' ); } - $links[] = $skin->linkKnown( $list, $message, array(), $query ); + $links[] = $skin->linkKnown( $list, $message, array() ); # Link to the block list $links[] = $skin->linkKnown( - SpecialPage::getTitleFor( 'Ipblocklist' ), + SpecialPage::getTitleFor( 'BlockList' ), wfMsg( 'ipb-blocklist' ) ); @@ -387,7 +447,7 @@ class SpecialBlock extends SpecialPage { if( $request instanceof WebRequest ){ $target = $request->getText( 'wpTarget', null ); } - break; + break; case 1: $target = $par; break; @@ -395,69 +455,39 @@ class SpecialBlock extends SpecialPage { if( $request instanceof WebRequest ){ $target = $request->getText( 'ip', null ); } - break; + break; case 3: # B/C @since 1.18 if( $request instanceof WebRequest ){ $target = $request->getText( 'wpBlockAddress', null ); } - break; + break; case 4: break 2; } - - $userObj = User::newFromName( $target ); - if( $userObj instanceof User ){ - return array( $userObj, Block::TYPE_USER ); - } elseif( IP::isValid( $target ) ){ - # We can still create a User if it's an IP address, but we need to turn - # off validation checking (which would exclude IP addresses) - return array( - User::newFromName( IP::sanitizeIP( $target ), false ), - Block::TYPE_IP - ); - break; - } elseif( IP::isValidBlock( $target ) ){ - # Can't create a User from an IP range - return array( Block::normaliseRange( $target ), Block::TYPE_RANGE ); + list( $target, $type ) = Block::parseTarget( $target ); + if( $type !== null ){ + return array( $target, $type ); } } return array( null, null ); } /** - * Given a target and the target's type, get a block object if possible - * @param $target String|User - * @param $type Block::TYPE_ constant - * @return Block|null - * TODO: this probably belongs in Block.php when that mess is cleared up + * HTMLForm field validation-callback for Target field. + * @since 1.18 + * @return Message */ - public static function getBlockFromTargetAndType( $target, $type ){ - if( $target instanceof User ){ - if( $type == Block::TYPE_IP ){ - return Block::newFromDB( $target->getName(), 0 ); - } elseif( $type == Block::TYPE_USER ) { - return Block::newFromDB( '', $target->getId() ); - } else { - # Should be unreachable; - return null; - } - } elseif( $type == Block::TYPE_RANGE ){ - return Block::newFromDB( '', $target ); - } else { - return null; - } - } - - public static function validateTargetField( $data, $alldata = null ) { + public static function validateTargetField( $value, $alldata = null ) { global $wgBlockCIDRLimit; - list( $target, $type ) = self::getTargetAndType( $data ); + list( $target, $type ) = self::getTargetAndType( $value ); if( $type == Block::TYPE_USER ){ # TODO: why do we not have a User->exists() method? if( !$target->getId() ){ - return wfMessage( 'nosuchusershort', $target->getName() ); + return wfMessage( 'nosuchusershort', + wfEscapeWikiText( $target->getName() ) ); } $status = self::checkUnblockSelf( $target ); @@ -465,10 +495,6 @@ class SpecialBlock extends SpecialPage { return wfMessage( 'badaccess', $status ); } - $user = $target; - $target = $user->getName(); - $userId = $user->getId(); - } elseif( $type == Block::TYPE_RANGE ){ list( $ip, $range ) = explode( '/', $target, 2 ); @@ -494,16 +520,14 @@ class SpecialBlock extends SpecialPage { return wfMessage( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv6'] ); } - $userId = 0; - } elseif( $type == Block::TYPE_IP ){ # All is well - $target = $target->getName(); - $userId = 0; } else { return wfMessage( 'badipaddress' ); } + + return true; } /** @@ -517,14 +541,45 @@ class SpecialBlock extends SpecialPage { // Handled by field validator callback // self::validateTargetField( $data['Target'] ); + # This might have been a hidden field or a checkbox, so interesting data + # can come from it + $data['Confirm'] = !in_array( $data['Confirm'], array( '', '0', null, false ), true ); + + list( $target, $type ) = self::getTargetAndType( $data['Target'] ); + if( $type == Block::TYPE_USER ){ + $user = $target; + $target = $user->getName(); + $userId = $user->getId(); + + # Give admins a heads-up before they go and block themselves. Much messier + # to do this for IPs, but it's pretty unlikely they'd ever get the 'block' + # permission anyway, although the code does allow for it + if( $target === $wgUser->getName() && + ( $data['PreviousTarget'] != $data['Target'] || !$data['Confirm'] ) ) + { + return array( 'ipb-blockingself' ); + } + + } elseif( $type == Block::TYPE_RANGE ){ + $userId = 0; + + } elseif( $type == Block::TYPE_IP ){ + $target = $target->getName(); + $userId = 0; + + } else { + # This should have been caught in the form field validation + return array( 'badipaddress' ); + } + if( ( strlen( $data['Expiry'] ) == 0) || ( strlen( $data['Expiry'] ) > 50 ) - || !Block::parseExpiryInput( $data['Expiry'] ) ) + || !self::parseExpiryInput( $data['Expiry'] ) ) { return array( 'ipb_expiry_invalid' ); } - if( !$wgBlockAllowsUTEdit ){ - $data['PreventUTEdit'] = true; + if( !isset( $data['DisableEmail'] ) ){ + $data['DisableEmail'] = false; } # If the user has done the form 'properly', they won't even have been given the @@ -545,7 +600,7 @@ class SpecialBlock extends SpecialPage { if( $type != Block::TYPE_USER ) { $data['HideUser'] = false; # IP users should not be hidden - } elseif( !in_array( $data['Expiry'], array( 'inifinite', 'infinity', 'indefinite' ) ) ) { + } elseif( !in_array( $data['Expiry'], array( 'infinite', 'infinity', 'indefinite' ) ) ) { # Bad expiry. return array( 'ipb_expiry_temp' ); @@ -553,47 +608,45 @@ class SpecialBlock extends SpecialPage { # Typically, the user should have a handful of edits. # Disallow hiding users with many edits for performance. return array( 'ipb_hide_invalid' ); + + } elseif( !$data['Confirm'] ){ + return array( 'ipb-confirmhideuser' ); } } - # Create block object. Note that for a user block, ipb_address is only for display purposes - # FIXME: Why do we need to pass fourteen optional parameters to do this!?! - $block = new Block( - $target, # IP address or User name - $userId, # User id - $wgUser->getId(), # Blocker id - $data['Reason'][0], # Reason string - wfTimestampNow(), # Block Timestamp - 0, # Is this an autoblock (no) - Block::parseExpiryInput( $data['Expiry'] ), # Expiry time - !$data['HardBlock'], # Block anon only - $data['CreateAccount'], - $data['AutoBlock'], - $data['HideUser'], - $data['DisableEmail'], - !$data['DisableUTEdit'] # *Allow* UTEdit - ); + # Create block object. + $block = new Block(); + $block->setTarget( $target ); + $block->setBlocker( $wgUser ); + $block->mReason = $data['Reason'][0]; + $block->mExpiry = self::parseExpiryInput( $data['Expiry'] ); + $block->prevents( 'createaccount', $data['CreateAccount'] ); + $block->prevents( 'editownusertalk', ( !$wgBlockAllowsUTEdit || $data['DisableUTEdit'] ) ); + $block->prevents( 'sendemail', $data['DisableEmail'] ); + $block->isHardblock( $data['HardBlock'] ); + $block->isAutoblocking( $data['AutoBlock'] ); + $block->mHideName = $data['HideUser']; if( !wfRunHooks( 'BlockIp', array( &$block, &$wgUser ) ) ) { return array( 'hookaborted' ); } # Try to insert block. Is there a conflicting block? - if( !$block->insert() ) { - + $status = $block->insert(); + if( !$status ) { # Show form unless the user is already aware of this... - if( !$data['AlreadyBlocked'] ) { - return array( array( 'ipb_already_blocked', $data['Target'] ) ); - + if( !$data['Confirm'] || ( array_key_exists( 'PreviousTarget', $data ) + && $data['PreviousTarget'] !== htmlspecialchars( $block->getTarget() ) ) ) + { + return array( array( 'ipb_already_blocked', $block->getTarget() ) ); # Otherwise, try to update the block... } else { - # This returns direct blocks before autoblocks/rangeblocks, since we should # be sure the user is blocked by now it should work for our purposes - $currentBlock = Block::newFromDB( $target, $userId ); + $currentBlock = Block::newFromTarget( $target ); if( $block->equals( $currentBlock ) ) { - return array( 'ipb_already_blocked' ); + return array( array( 'ipb_already_blocked', $block->getTarget() ) ); } # If the name was hidden and the blocking user cannot hide @@ -603,7 +656,7 @@ class SpecialBlock extends SpecialPage { } $currentBlock->delete(); - $block->insert(); + $status = $block->insert(); $logaction = 'reblock'; # Unset _deleted fields if requested @@ -616,7 +669,6 @@ class SpecialBlock extends SpecialPage { $data['HideUser'] = true; } } - } else { $logaction = 'block'; } @@ -634,8 +686,8 @@ class SpecialBlock extends SpecialPage { } # Block constructor sanitizes certain block options on insert - $data['BlockEmail'] = $block->mBlockEmail; - $data['AutoBlock'] = $block->mEnableAutoblock; + $data['BlockEmail'] = $block->prevents( 'sendemail' ); + $data['AutoBlock'] = $block->isAutoblocking(); # Prepare log parameters $logParams = array(); @@ -645,12 +697,15 @@ class SpecialBlock extends SpecialPage { # Make log entry, if the name is hidden, put it in the oversight log $log_type = $data['HideUser'] ? 'suppress' : 'block'; $log = new LogPage( $log_type ); - $log->addEntry( + $log_id = $log->addEntry( $logaction, Title::makeTitle( NS_USER, $target ), $data['Reason'][0], $logParams ); + # Relate log ID to block IDs (bug 25763) + $blockIds = array_merge( array( $status['id'] ), $status['autoIds'] ); + $log->addRelations( 'ipb_id', $blockIds, $log_id ); # Report to the user return true; @@ -658,20 +713,53 @@ class SpecialBlock extends SpecialPage { /** * Get an array of suggested block durations from MediaWiki:Ipboptions - * FIXME: this uses a rather odd syntax for the options, should it be converted + * @todo FIXME: This uses a rather odd syntax for the options, should it be converted * to the standard "**|" format? * @return Array */ - protected static function getSuggestedDurations(){ + public static function getSuggestedDurations( $lang = null ){ $a = array(); - foreach( explode( ',', wfMsgForContent( 'ipboptions' ) ) as $option ) { - if( strpos( $option, ':' ) === false ) $option = "$option:$option"; + $msg = $lang === null + ? wfMessage( 'ipboptions' )->inContentLanguage()->text() + : wfMessage( 'ipboptions' )->inLanguage( $lang )->text(); + + if( $msg == '-' ){ + return array(); + } + + foreach( explode( ',', $msg ) as $option ) { + if( strpos( $option, ':' ) === false ){ + $option = "$option:$option"; + } list( $show, $value ) = explode( ':', $option ); $a[htmlspecialchars( $show )] = htmlspecialchars( $value ); } return $a; } + /** + * Convert a submitted expiry time, which may be relative ("2 weeks", etc) or absolute + * ("24 May 2034", etc), into an absolute timestamp we can put into the database. + * @param $expiry String: whatever was typed into the form + * @return String: timestamp or "infinity" string for the DB implementation + */ + public static function parseExpiryInput( $expiry ) { + static $infinity; + if( $infinity == null ){ + $infinity = wfGetDB( DB_SLAVE )->getInfinity(); + } + if ( $expiry == 'infinite' || $expiry == 'indefinite' ) { + $expiry = $infinity; + } else { + $expiry = strtotime( $expiry ); + if ( $expiry < 0 || $expiry === false ) { + return false; + } + $expiry = wfTimestamp( TS_MW, $expiry ); + } + return $expiry; + } + /** * Can we do an email block? * @param $user User: the sysop wanting to make a block @@ -741,7 +829,7 @@ class SpecialBlock extends SpecialPage { $flags[] = 'noemail'; } - if( $data['DisableUTEdit'] && $wgBlockAllowsUTEdit ){ + if( $wgBlockAllowsUTEdit && $data['DisableUTEdit'] ){ $flags[] = 'nousertalk'; }