From 0a8a3b452c76a2eb0ce46bcc3aa133d79ace074a Mon Sep 17 00:00:00 2001 From: Happy-melon Date: Sun, 13 Mar 2011 21:33:52 +0000 Subject: [PATCH] Further massive rewrite of the blocking frontend: spin out unblocking into a new SpecialUnblock.php. This leaves IPBlockList as, astonishingly enough, a list of blocks... :D --- includes/AutoLoader.php | 1 + includes/Block.php | 18 +- includes/DefaultSettings.php | 1 + includes/LogEventsList.php | 7 +- includes/SpecialPage.php | 2 +- includes/api/ApiUnblock.php | 20 +- includes/specials/SpecialBlock.php | 9 +- includes/specials/SpecialContributions.php | 9 +- includes/specials/SpecialIpblocklist.php | 210 +-------------------- includes/specials/SpecialUnblock.php | 200 ++++++++++++++++++++ languages/messages/MessagesEn.php | 2 + maintenance/language/messages.inc | 2 + 12 files changed, 247 insertions(+), 234 deletions(-) create mode 100644 includes/specials/SpecialUnblock.php diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 1e09d106e0..16393bca58 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -674,6 +674,7 @@ $wgAutoloadLocalClasses = array( 'SpecialSpecialpages' => 'includes/specials/SpecialSpecialpages.php', 'SpecialStatistics' => 'includes/specials/SpecialStatistics.php', 'SpecialTags' => 'includes/specials/SpecialTags.php', + 'SpecialUnblock' => 'includes/specials/SpecialUnblock.php', 'SpecialUnlockdb' => 'includes/specials/SpecialUnlockdb.php', 'SpecialUpload' => 'includes/specials/SpecialUpload.php', 'SpecialUserlogout' => 'includes/specials/SpecialUserlogout.php', diff --git a/includes/Block.php b/includes/Block.php index d6fd329915..b8a4f80eba 100644 --- a/includes/Block.php +++ b/includes/Block.php @@ -920,7 +920,11 @@ class Block { # FIXME: everything above here is a mess, needs much cleaning up /** - * Given a target and the target's type, get a Block object if possible + * Given a target and the target's type, get an existing Block object if possible. + * Note that passing an IP address will get an applicable rangeblock if the IP is + * not individually blocked but falls within that range + * TODO: check that that fallback handles nested rangeblocks nicely (should return + * smallest one) * @param $target String|User|Int a block target, which may be one of several types: * * A user to block, in which case $target will be a User * * An IP to block, in which case $target will be a User generated by using @@ -942,7 +946,7 @@ class Block { } } elseif( $type == Block::TYPE_RANGE ){ - return Block::newFromDB( '', $target ); + return Block::newFromDB( $target, 0 ); } elseif( $type == Block::TYPE_ID || $type == Block::TYPE_AUTO ){ return Block::newFromID( $target ); @@ -1009,4 +1013,14 @@ class Block { return array( $target, $type ); } + + public function getType(){ + list( $target, $type ) = $this->getTargetAndType(); + return $type; + } + + public function getTarget(){ + list( $target, $type ) = $this->getTargetAndType(); + return $target; + } } diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index a7abbaa7a8..b7bcbe91ca 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -4948,6 +4948,7 @@ $wgSpecialPageGroups = array( 'Listbots' => 'users', 'Userrights' => 'users', 'Block' => 'users', + 'Unblock' => 'users', 'Preferences' => 'users', 'Resetpass' => 'users', 'DeletedContributions' => 'users', diff --git a/includes/LogEventsList.php b/includes/LogEventsList.php index 112bc17822..9fd82848eb 100644 --- a/includes/LogEventsList.php +++ b/includes/LogEventsList.php @@ -424,13 +424,10 @@ class LogEventsList { } else if( self::typeAction( $row, array( 'block', 'suppress' ), array( 'block', 'reblock' ), 'block' ) ) { $revert = '(' . $this->skin->link( - SpecialPage::getTitleFor( 'Ipblocklist' ), + SpecialPage::getTitleFor( 'Unblock', $row->log_title ), $this->message['unblocklink'], array(), - array( - 'action' => 'unblock', - 'ip' => $row->log_title - ), + array(), 'known' ) . $this->message['pipe-separator'] . diff --git a/includes/SpecialPage.php b/includes/SpecialPage.php index 043f72a08a..5286adf151 100644 --- a/includes/SpecialPage.php +++ b/includes/SpecialPage.php @@ -137,8 +137,8 @@ class SpecialPage { # Users and rights 'Block' => 'SpecialBlock', + 'Unblock' => 'SpecialUnblock', 'Ipblocklist' => 'IPUnblockForm', - 'Unblock' => array( 'SpecialRedirectToSpecial', 'Unblock', 'Ipblocklist', false, array( 'uselang', 'ip', 'id' ), array( 'action' => 'unblock' ) ), 'Resetpass' => 'SpecialResetpass', 'DeletedContributions' => 'DeletedContributionsPage', 'Preferences' => 'SpecialPreferences', diff --git a/includes/api/ApiUnblock.php b/includes/api/ApiUnblock.php index b0ce803887..9a1bf336f4 100644 --- a/includes/api/ApiUnblock.php +++ b/includes/api/ApiUnblock.php @@ -72,17 +72,19 @@ class ApiUnblock extends ApiBase { } } - $id = $params['id']; - $user = $params['user']; - $reason = ( is_null( $params['reason'] ) ? '' : $params['reason'] ); - $retval = IPUnblockForm::doUnblock( $id, $user, $reason, $range ); - if ( $retval ) { - $this->dieUsageMsg( $retval ); + $data = array( + 'Target' => is_null( $params['id'] ) ? $params['user'] : "#{$params['id']}", + 'Reason' => is_null( $params['reason'] ) ? '' : $params['reason'] + ); + $block = Block::newFromTarget( $data['Target'] ); + $retval = SpecialUnblock::processUnblock( $data ); + if ( $retval !== true ) { + $this->dieUsageMsg( $retval[0] ); } - $res['id'] = intval( $id ); - $res['user'] = $user; - $res['reason'] = $reason; + $res['id'] = $block->mId; + $res['user'] = $block->getType() == Block::TYPE_AUTO ? '' : $block->getTarget(); + $res['reason'] = $params['reason']; $this->getResult()->addValue( null, $this->getModuleName(), $res ); } diff --git a/includes/specials/SpecialBlock.php b/includes/specials/SpecialBlock.php index 22c8a0049e..becb9f0a2b 100644 --- a/includes/specials/SpecialBlock.php +++ b/includes/specials/SpecialBlock.php @@ -297,15 +297,14 @@ 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( @@ -492,7 +491,7 @@ class SpecialBlock extends SpecialPage { $userId = 0; } else { # This should have been caught in the form field validation - return wfMessage( 'badipaddress' ); + return array( 'badipaddress' ); } if( ( strlen( $data['Expiry'] ) == 0) || ( strlen( $data['Expiry'] ) > 50 ) diff --git a/includes/specials/SpecialContributions.php b/includes/specials/SpecialContributions.php index fc2cfedc53..3119fe7db6 100644 --- a/includes/specials/SpecialContributions.php +++ b/includes/specials/SpecialContributions.php @@ -256,13 +256,8 @@ class SpecialContributions extends SpecialPage { wfMsgHtml( 'change-blocklink' ) ); $tools[] = $sk->linkKnown( # Unblock link - SpecialPage::getTitleFor( 'Ipblocklist' ), - wfMsgHtml( 'unblocklink' ), - array(), - array( - 'action' => 'unblock', - 'ip' => $username - ) + SpecialPage::getTitleFor( 'Unblock', $username ), + wfMsgHtml( 'unblocklink' ) ); } else { # User is not blocked $tools[] = $sk->linkKnown( # Block link diff --git a/includes/specials/SpecialIpblocklist.php b/includes/specials/SpecialIpblocklist.php index 81c4643a06..4e6301a68e 100644 --- a/includes/specials/SpecialIpblocklist.php +++ b/includes/specials/SpecialIpblocklist.php @@ -28,7 +28,7 @@ * @ingroup SpecialPage */ class IPUnblockForm extends SpecialPage { - var $ip, $reason, $id; + var $ip; var $hideuserblocks, $hidetempblocks, $hideaddressblocks; function __construct() { @@ -48,223 +48,23 @@ class IPUnblockForm extends SpecialPage { $ip = $wgRequest->getVal( 'ip', $ip ); $this->ip = trim( $wgRequest->getVal( 'wpUnblockAddress', $ip ) ); - $this->id = $wgRequest->getVal( 'id' ); - $this->reason = $wgRequest->getText( 'wpUnblockReason' ); $this->hideuserblocks = $wgRequest->getBool( 'hideuserblocks' ); $this->hidetempblocks = $wgRequest->getBool( 'hidetempblocks' ); $this->hideaddressblocks = $wgRequest->getBool( 'hideaddressblocks' ); $action = $wgRequest->getText( 'action' ); - $successip = $wgRequest->getVal( 'successip' ); if( $action == 'unblock' || $action == 'submit' && $wgRequest->wasPosted() ) { - # Check permissions - if( !$wgUser->isAllowed( 'block' ) ) { - $wgOut->permissionRequired( 'block' ); - return; - } - # Check for database lock - if( wfReadOnly() ) { - $wgOut->readOnlyPage(); - return; - } - - # bug 15810: blocked admins should have limited access here - if ( $wgUser->isBlocked() ) { - if ( $this->id ) { - # This doesn't pick up on autoblocks, but admins - # should have the ipblock-exempt permission anyway - $block = Block::newFromID( $this->id ); - $user = User::newFromName( $block->mAddress ); - } else { - $user = User::newFromName( $ip ); - } - $status = SpecialBlock::checkUnblockSelf( $user ); - if ( $status !== true ) { - throw new ErrorPageError( 'badaccess', $status ); - } - } - - if( $action == 'unblock' ){ - # Show unblock form - $this->showForm(); - } elseif( $action == 'submit' - && $wgRequest->wasPosted() - && $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) - { - # Remove blocks and redirect user to success page - $this->doSubmit(); - } - - } elseif( $action == 'success' ) { - # Inform the user of a successful unblock - # (No need to check permissions or locks here, - # if something was done, then it's too late!) - if ( substr( $successip, 0, 1) == '#' ) { - // A block ID was unblocked - $this->showList( $wgOut->parse( wfMsg( 'unblocked-id', $successip ) ) ); - } else { - // A username/IP was unblocked - $this->showList( $wgOut->parse( wfMsg( 'unblocked', $successip ) ) ); - } + # B/C @since 1.18: Unblock interface is now at Special:Unblock + $title = SpecialPage::getTitleFor( 'Unblock', $this->ip ); + $wgOut->redirect( $title->getFullUrl() ); + return; } else { # Just show the block list $this->showList( '' ); } } - /** - * Generates the unblock form - * - * @param $err String, Array or null: error message name or an array if - * there are parameters. Null indicates no error. - * @return $out string: HTML form - */ - function showForm( $err = null ) { - global $wgOut, $wgUser; - - $wgOut->addWikiMsg( 'unblockiptext' ); - - $action = $this->getTitle()->getLocalURL( 'action=submit' ); - - if ( $err !== null ) { - $wgOut->setSubtitle( wfMsg( 'formerror' ) ); - $wgOut->wrapWikiMsg( "$1\n", $err ); - } - - $addressPart = false; - if ( $this->id ) { - $block = Block::newFromID( $this->id ); - if ( $block ) { - $encName = htmlspecialchars( $block->getRedactedName() ); - $encId = $this->id; - $addressPart = $encName . Html::hidden( 'id', $encId ); - $ipa = wfMsgHtml( 'ipadressorusername' ); - } - } - if ( !$addressPart ) { - $addressPart = Xml::input( 'wpUnblockAddress', 40, $this->ip, array( 'type' => 'text', 'tabindex' => '1' ) ); - $ipa = Xml::label( wfMsg( 'ipadressorusername' ), 'wpUnblockAddress' ); - } - - $wgOut->addHTML( - Html::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'unblockip' ) ) . - Html::openElement( 'fieldset' ) . - Html::element( 'legend', null, wfMsg( 'ipb-unblock' ) ) . - Html::openElement( 'table', array( 'id' => 'mw-unblock-table' ) ). - " - - {$ipa} - - - {$addressPart} - - - - " . - Xml::label( wfMsg( 'ipbreason' ), 'wpUnblockReason' ) . - " - " . - Xml::input( 'wpUnblockReason', 40, $this->reason, array( 'type' => 'text', 'tabindex' => '2' ) ) . - " - - -   - " . - Xml::submitButton( wfMsg( 'ipusubmit' ), array( 'name' => 'wpBlock', 'tabindex' => '3' ) ) . - " - " . - Html::closeElement( 'table' ) . - Html::closeElement( 'fieldset' ) . - Html::hidden( 'wpEditToken', $wgUser->editToken() ) . - Html::closeElement( 'form' ) . "\n" - ); - - } - - const UNBLOCK_SUCCESS = 0; // Success - const UNBLOCK_NO_SUCH_ID = 1; // No such block ID - const UNBLOCK_USER_NOT_BLOCKED = 2; // IP wasn't blocked - const UNBLOCK_BLOCKED_AS_RANGE = 3; // IP is part of a range block - const UNBLOCK_UNKNOWNERR = 4; // Unknown error - - /** - * Backend code for unblocking. doSubmit() wraps around this. - * $range is only used when UNBLOCK_BLOCKED_AS_RANGE is returned, in which - * case it contains the range $ip is part of. - * @return array array(message key, parameters) on failure, empty array on success - */ - public static function doUnblock( &$id, &$ip, &$reason, &$range = null, $blocker = null ) { - if ( $id ) { - $block = Block::newFromID( $id ); - if ( !$block ) { - return array( 'ipb_cant_unblock', htmlspecialchars( $id ) ); - } - $ip = $block->getRedactedName(); - } else { - $ip = trim( $ip ); - if ( substr( $ip, 0, 1 ) == "#" ) { - $id = substr( $ip, 1 ); - $block = Block::newFromID( $id ); - if( !$block ) { - return array( 'ipb_cant_unblock', htmlspecialchars( $id ) ); - } - $ip = $block->getRedactedName(); - } else { - # FIXME: do proper sanitisation/cleanup here - $ip = str_replace( '_', ' ', $ip ); - - $block = Block::newFromDB( $ip ); - if ( !$block ) { - return array( 'ipb_cant_unblock', htmlspecialchars( $id ) ); - } - if( $block->mRangeStart != $block->mRangeEnd && !strstr( $ip, "/" ) ) { - /* If the specified IP is a single address, and the block is - * a range block, don't unblock the range. */ - $range = $block->mAddress; - return array( 'ipb_blocked_as_range', $ip, $range ); - } - } - } - // Yes, this is really necessary - $id = $block->mId; - - # If the name was hidden and the blocking user cannot hide - # names, then don't allow any block removals... - if( $blocker && $block->mHideName && !$blocker->isAllowed( 'hideuser' ) ) { - return array( 'ipb_cant_unblock', htmlspecialchars( $id ) ); - } - - # Delete block - if ( !$block->delete() ) { - return array( 'ipb_cant_unblock', htmlspecialchars( $id ) ); - } - - # Unset _deleted fields as needed - if( $block->mHideName ) { - RevisionDeleteUser::unsuppressUserName( $block->mAddress, $block->mUser ); - } - - # Make log entry - $log = new LogPage( 'block' ); - $log->addEntry( 'unblock', Title::makeTitle( NS_USER, $ip ), $reason ); - return array(); - } - - function doSubmit() { - global $wgOut, $wgUser; - - $retval = self::doUnblock( $this->id, $this->ip, $this->reason, $range, $wgUser ); - if ( count( $retval ) ) { - $this->showForm( $retval ); - return; - } - - # Report to the user - $success = $this->getTitle()->getFullURL( 'action=success&successip=' . urlencode( $this->ip ) ); - $wgOut->redirect( $success ); - } - function showList( $msg ) { global $wgOut, $wgUser; diff --git a/includes/specials/SpecialUnblock.php b/includes/specials/SpecialUnblock.php new file mode 100644 index 0000000000..585e651832 --- /dev/null +++ b/includes/specials/SpecialUnblock.php @@ -0,0 +1,200 @@ +isAllowed( 'block' ) ) { + $wgOut->permissionRequired( 'block' ); + return; + } + # Check for database lock + if( wfReadOnly() ) { + $wgOut->readOnlyPage(); + return; + } + + list( $this->target, $this->type ) = SpecialBlock::getTargetAndType( $par, $wgRequest ); + $this->block = Block::newFromTargetAndType( $this->target, $this->type ); + + # bug 15810: blocked admins should have limited access here. This won't allow sysops + # to remove autoblocks on themselves, but they should have ipblock-exempt anyway + $status = SpecialBlock::checkUnblockSelf( $this->target ); + if ( $status !== true ) { + throw new ErrorPageError( 'badaccess', $status ); + } + + $wgOut->setPageTitle( wfMsg( 'unblockip' ) ); + $wgOut->addModules( 'mediawiki.special' ); + + $form = new HTMLForm( $this->getFields() ); + $form->setTitle( $this->getTitle() ); + $form->setWrapperLegend( wfMsg( 'unblockip' ) ); + $form->setSubmitCallback( array( __CLASS__, 'processUnblock' ) ); + $form->setSubmitText( wfMsg( 'ipusubmit' ) ); + $form->addPreText( wfMsgExt( 'unblockiptext', 'parse' ) ); + + if( $form->show() ){ + switch( $this->type ){ + case Block::TYPE_USER: + case Block::TYPE_IP: + $wgOut->addWikiMsg( 'unblocked', $this->target ); + break; + case Block::TYPE_RANGE: + $wgOut->addWikiMsg( 'unblocked-range', $this->target ); + break; + case Block::TYPE_ID: + case Block::TYPE_AUTO: + $wgOut->addWikiMsg( 'unblocked-id', $this->target ); + break; + } + } + } + + protected function getFields(){ + $fields = array( + 'Target' => array( + 'type' => 'text', + 'label-message' => 'ipadressorusername', + 'tabindex' => '1', + 'size' => '45', + 'required' => true, + ), + 'Name' => array( + 'type' => 'info', + 'label-message' => 'ipadressorusername', + ), + 'Reason' => array( + 'type' => 'text', + 'label-message' => 'ipbreason', + ) + ); + + if( $this->block instanceof Block ){ + list( $target, $type ) = $this->block->getTargetAndType(); + + # Autoblocks are logged as "autoblock #123 because the IP was recently used by + # User:Foo, and we've just got any block, auto or not, that applies to a target + # the user has specified. Someone could be fishing to connect IPs to autoblocks, + # so don't show any distinction between unblocked IPs and autoblocked IPs + if( $type == Block::TYPE_AUTO && $this->type == Block::TYPE_IP ){ + $fields['Target']['default'] = $this->target; + unset( $fields['Name'] ); + + } else { + $fields['Target']['default'] = $target; + $fields['Target']['type'] = 'hidden'; + switch( $type ){ + case Block::TYPE_USER: + case Block::TYPE_IP: + global $wgUser; + $skin = $wgUser->getSkin(); + $fields['Name']['default'] = $skin->link( + $target->getUserPage(), + $target->getName() + ); + $fields['Name']['raw'] = true; + break; + + case Block::TYPE_RANGE: + $fields['Name']['default'] = $target; + break; + + case Block::TYPE_AUTO: + $fields['Name']['default'] = $this->block->getRedactedName(); + $fields['Name']['raw'] = true; + # Don't expose the real target of the autoblock + $fields['Target']['default'] = "#{$this->target}"; + break; + } + } + + } else { + $fields['Target']['default'] = $this->target; + unset( $fields['Name'] ); + } + return $fields; + } + + /** + * Process the form + * @return Array( Array(message key, parameters) ) on failure, True on success + */ + public static function processUnblock( array $data ){ + global $wgUser; + + $target = $data['Target']; + $block = Block::newFromTarget( $data['Target'] ); + + if( !$block instanceof Block ){ + return array( array( 'ipb_cant_unblock', $target ) ); + } + + # If the specified IP is a single address, and the block is a range block, don't + # unblock the whole range. + list( $target, $type ) = SpecialBlock::getTargetAndType( $target ); + if( $block->getType() == Block::TYPE_RANGE && $type == Block::TYPE_IP ) { + $range = $block->mAddress; + return array( array( 'ipb_blocked_as_range', $target, $range ) ); + } + + # If the name was hidden and the blocking user cannot hide + # names, then don't allow any block removals... + if( !$wgUser->isAllowed( 'hideuser' ) && $block->mHideName ) { + return array( 'unblock-hideuser' ); + } + + # Delete block + if ( !$block->delete() ) { + return array( 'ipb_cant_unblock', htmlspecialchars( $block->getTarget() ) ); + } + + # Unset _deleted fields as needed + if( $block->mHideName ) { + RevisionDeleteUser::unsuppressUserName( $block->mAddress, $block->mUser ); + } + + # Make log entry + $log = new LogPage( 'block' ); + $page = $block->getTarget() instanceof User + ? $block->getTarget()->getUserpage() + : Title::makeTitle( NS_USER, $block->getTarget() ); + $log->addEntry( 'unblock', $page, $data['Reason'] ); + + return true; + } +} \ No newline at end of file diff --git a/languages/messages/MessagesEn.php b/languages/messages/MessagesEn.php index c398f80ef4..28fee22b3f 100644 --- a/languages/messages/MessagesEn.php +++ b/languages/messages/MessagesEn.php @@ -3066,6 +3066,7 @@ See [[Special:IPBlockList|IP block list]] to review blocks.', 'unblockiptext' => 'Use the form below to restore write access to a previously blocked IP address or username.', 'ipusubmit' => 'Remove this block', 'unblocked' => '[[User:$1|$1]] has been unblocked', +'unblocked-range' => '$1 has been unblocked', 'unblocked-id' => 'Block $1 has been removed', 'ipblocklist' => 'Blocked IP addresses and usernames', 'ipblocklist-legend' => 'Find a blocked user', @@ -3120,6 +3121,7 @@ See the [[Special:IPBlockList|IP block list]] for the list of currently operatio $1 is already blocked. Do you want to change the settings?', 'ipb-otherblocks-header' => 'Other {{PLURAL:$1|block|blocks}}', +'unblock-hideuser' => 'You cannot unblock this user, as their username has been hidden.', 'ipb_cant_unblock' => 'Error: Block ID $1 not found. It may have been unblocked already.', 'ipb_blocked_as_range' => 'Error: The IP address $1 is not blocked directly and cannot be unblocked. diff --git a/maintenance/language/messages.inc b/maintenance/language/messages.inc index 20a76925c0..6b9819156a 100644 --- a/maintenance/language/messages.inc +++ b/maintenance/language/messages.inc @@ -2080,6 +2080,7 @@ $wgMessageStructure = array( 'unblockiptext', 'ipusubmit', 'unblocked', + 'unblocked-range', 'unblocked-id', 'ipblocklist', 'ipblocklist-legend', @@ -2127,6 +2128,7 @@ $wgMessageStructure = array( 'ipb_already_blocked', 'ipb-needreblock', 'ipb-otherblocks-header', + 'unblock-hideuser', 'ipb_cant_unblock', 'ipb_blocked_as_range', 'ip_range_invalid', -- 2.20.1