r83786 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r83785‎ | r83786 | r83787 >
Date:21:54, 12 March 2011
Author:happy-melon
Status:resolved (Comments)
Tags:
Comment:
Divert a river through the Augean Stables that is SpecialBlockip.php.
* Move to SpecialBlock.php, and rename class appropriately
* Complete refactor
* Use HTMLForm in block form. This changes most of the ids and field names on the form, but allows proper validation, nicer formatting, clears up several fixmes, and is generally Better(TM).
* Spin various parts out into static functions, several of which properly belong in the backend (but Block.php is a worse mess still)
* Invert some of the block options so that every checkbox makes the block more severe (so "check to disable email" is fine, but "check to allow usertalk edit" (default true) is inverted to "check to disable usertalk edit" (default false).
* revert r40359 (move doMassUserBlock() to core). No one seems to be using this function, which has nothing to do with the frontend UI in SpecialBlock (it might perhaps belong in Block.php); it is pretty bespoke for CheckUser, doesn't seem to have very much utility elsewhere.
Modified paths:
  • /trunk/extensions/CheckUser/CheckUser_body.php (modified) (history)
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/Block.php (modified) (history)
  • /trunk/phase3/includes/SpecialPage.php (modified) (history)
  • /trunk/phase3/includes/api/ApiBlock.php (modified) (history)
  • /trunk/phase3/includes/api/ApiUnblock.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialBlock.php (added) (history)
  • /trunk/phase3/includes/specials/SpecialBlockip.php (deleted) (history)
  • /trunk/phase3/includes/specials/SpecialIpblocklist.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /trunk/phase3/maintenance/language/messages.inc (modified) (history)
  • /trunk/phase3/resources/mediawiki.special/mediawiki.special.block.js (modified) (history)

Diff [purge]

Index: trunk/extensions/CheckUser/CheckUser_body.php
@@ -252,7 +252,7 @@
253253 $wgOut->addWikiMsg( 'checkuser-block-noreason' );
254254 return;
255255 }
256 - $safeUsers = IPBlockForm::doMassUserBlock( $users, $reason, $tag, $talkTag );
 256+ $safeUsers = self::doMassUserBlockInternal( $users, $reason, $tag, $talkTag );
257257 if ( !empty( $safeUsers ) ) {
258258 $n = count( $safeUsers );
259259 $ulist = $wgLang->listToText( $safeUsers );
@@ -262,6 +262,80 @@
263263 }
264264 }
265265
 266+ /**
 267+ * Block a list of selected users
 268+ *
 269+ * @param $users Array
 270+ * @param $reason String
 271+ * @param $tag String: replaces user pages
 272+ * @param $talkTag String: replaces user talk pages
 273+ * @return Array: list of html-safe usernames
 274+ */
 275+ public static function doMassUserBlockInternal( $users, $reason = '', $tag = '', $talkTag = '' ) {
 276+ global $wgUser;
 277+ $counter = $blockSize = 0;
 278+ $safeUsers = array();
 279+ $log = new LogPage( 'block' );
 280+ foreach( $users as $name ) {
 281+ # Enforce limits
 282+ $counter++;
 283+ $blockSize++;
 284+ # Lets not go *too* fast
 285+ if( $blockSize >= 20 ) {
 286+ $blockSize = 0;
 287+ wfWaitForSlaves( 5 );
 288+ }
 289+ $u = User::newFromName( $name, false );
 290+ // If user doesn't exist, it ought to be an IP then
 291+ if( is_null( $u ) || ( !$u->getId() && !IP::isIPAddress( $u->getName() ) ) ) {
 292+ continue;
 293+ }
 294+ $userTitle = $u->getUserPage();
 295+ $userTalkTitle = $u->getTalkPage();
 296+ $userpage = new Article( $userTitle );
 297+ $usertalk = new Article( $userTalkTitle );
 298+ $safeUsers[] = '[[' . $userTitle->getPrefixedText() . '|' . $userTitle->getText() . ']]';
 299+ $expirestr = $u->getId() ? 'indefinite' : '1 week';
 300+ $expiry = Block::parseExpiryInput( $expirestr );
 301+ $anonOnly = IP::isIPAddress( $u->getName() ) ? 1 : 0;
 302+ // Create the block
 303+ $block = new Block( $u->getName(), // victim
 304+ $u->getId(), // uid
 305+ $wgUser->getId(), // blocker
 306+ $reason, // comment
 307+ wfTimestampNow(), // block time
 308+ 0, // auto ?
 309+ $expiry, // duration
 310+ $anonOnly, // anononly?
 311+ 1, // block account creation?
 312+ 1, // autoblocking?
 313+ 0, // suppress name?
 314+ 0 // block from sending email?
 315+ );
 316+ $oldblock = Block::newFromDB( $u->getName(), $u->getId() );
 317+ if( !$oldblock ) {
 318+ $block->insert();
 319+ # Prepare log parameters
 320+ $logParams = array();
 321+ $logParams[] = $expirestr;
 322+ if( $anonOnly ) {
 323+ $logParams[] = 'anononly';
 324+ }
 325+ $logParams[] = 'nocreate';
 326+ # Add log entry
 327+ $log->addEntry( 'block', $userTitle, $reason, $logParams );
 328+ }
 329+ # Tag userpage! (check length to avoid mistakes)
 330+ if( strlen( $tag ) > 2 ) {
 331+ $userpage->doEdit( $tag, $reason, EDIT_MINOR );
 332+ }
 333+ if( strlen( $talkTag ) > 2 ) {
 334+ $usertalk->doEdit( $talkTag, $reason, EDIT_MINOR );
 335+ }
 336+ }
 337+ return $safeUsers;
 338+ }
 339+
266340 // Give a "no matches found for X" message.
267341 // If $checkLast, then mention the last edit by this user or IP.
268342 protected function noMatchesMessage( $userName, $checkLast = true ) {
Index: trunk/phase3/resources/mediawiki.special/mediawiki.special.block.js
@@ -3,9 +3,11 @@
44 jQuery( function( $ ) {
55
66 var DO_INSTANT = true,
7 - $blockTarget = $( '#mw-bi-target' ), $anonOnlyRow = $( '#wpAnonOnlyRow' ),
8 - $enableAutoblockRow = $( '#wpEnableAutoblockRow' ),
9 - $hideUser = $( '#wpEnableHideUser' ), $watchUser = $( '#wpEnableWatchUser' );
 7+ $blockTarget = $( '#mw-bi-target' ),
 8+ $anonOnlyRow = $( '#mw-input-wpHardblock' ).closest( 'tr' ),
 9+ $enableAutoblockRow = $( '#mw-input-wpAutoBlock' ).closest( 'tr' ),
 10+ $hideUser = $( '#mw-input-wpHideUser' ).closest( 'tr' ),
 11+ $watchUser = $( '#mw-input-wpWatch' ).closest( 'tr' );
1012
1113 var updateBlockOptions = function( instant ) {
1214 if ( !$blockTarget.length ) {
Index: trunk/phase3/maintenance/language/messages.inc
@@ -2054,7 +2054,7 @@
20552055 'ipbreason',
20562056 'ipbreasonotherlist',
20572057 'ipbreason-dropdown',
2058 - 'ipbanononly',
 2058+ 'ipb-hardblock',
20592059 'ipbcreateaccount',
20602060 'ipbemailban',
20612061 'ipbenableautoblock',
@@ -2065,7 +2065,7 @@
20662066 'ipbotherreason',
20672067 'ipbhidename',
20682068 'ipbwatchuser',
2069 - 'ipballowusertalk',
 2069+ 'ipb-disableusertalk',
20702070 'ipb-change-block',
20712071 'badipaddress',
20722072 'blockipsuccesssub',
Index: trunk/phase3/includes/Block.php
@@ -12,6 +12,7 @@
1313 * Globals used: $wgAutoblockExpiry, $wgAntiLockFlags
1414 *
1515 * @todo This could be used everywhere, but it isn't.
 16+ * FIXME: this whole class is a cesspit, needs a complete rewrite
1617 */
1718 class Block {
1819 /* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry,
@@ -23,6 +24,10 @@
2425 const EB_FOR_UPDATE = 2;
2526 const EB_RANGE_ONLY = 4;
2627
 28+ const TYPE_USER = 1;
 29+ const TYPE_IP = 2;
 30+ const TYPE_RANGE = 3;
 31+
2732 function __construct( $address = '', $user = 0, $by = 0, $reason = '',
2833 $timestamp = 0, $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0, $enableAutoblock = 0,
2934 $hideName = 0, $blockEmail = 0, $allowUsertalk = 0, $byName = false )
@@ -63,7 +68,6 @@
6469 public static function newFromDB( $address, $user = 0, $killExpired = true ) {
6570 $block = new Block;
6671 $block->load( $address, $user, $killExpired );
67 -
6872 if ( $block->isValid() ) {
6973 return $block;
7074 } else {
Index: trunk/phase3/includes/SpecialPage.php
@@ -136,7 +136,7 @@
137137 'CreateAccount' => array( 'SpecialRedirectToSpecial', 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) ),
138138
139139 # Users and rights
140 - 'Blockip' => 'IPBlockForm',
 140+ 'Block' => 'SpecialBlock',
141141 'Ipblocklist' => 'IPUnblockForm',
142142 'Unblock' => array( 'SpecialRedirectToSpecial', 'Unblock', 'Ipblocklist', false, array( 'uselang', 'ip', 'id' ), array( 'action' => 'unblock' ) ),
143143 'Resetpass' => 'SpecialResetpass',
Index: trunk/phase3/includes/api/ApiUnblock.php
@@ -66,7 +66,7 @@
6767 }
6868 # bug 15810: blocked admins should have limited access here
6969 if ( $wgUser->isBlocked() ) {
70 - $status = IPBlockForm::checkUnblockSelf( $params['user'] );
 70+ $status = SpecialBlock::checkUnblockSelf( $params['user'] );
7171 if ( $status !== true ) {
7272 $this->dieUsageMsg( array( $status ) );
7373 }
Index: trunk/phase3/includes/api/ApiBlock.php
@@ -62,7 +62,7 @@
6363 }
6464 # bug 15810: blocked admins should have limited access here
6565 if ( $wgUser->isBlocked() ) {
66 - $status = IPBlockForm::checkUnblockSelf( $params['user'] );
 66+ $status = SpecialBlock::checkUnblockSelf( $params['user'] );
6767 if ( $status !== true ) {
6868 $this->dieUsageMsg( array( $status ) );
6969 }
@@ -70,35 +70,48 @@
7171 if ( $params['hidename'] && !$wgUser->isAllowed( 'hideuser' ) ) {
7272 $this->dieUsageMsg( array( 'canthide' ) );
7373 }
74 - if ( $params['noemail'] && !IPBlockForm::canBlockEmail( $wgUser ) ) {
 74+ if ( $params['noemail'] && !SpecialBlock::canBlockEmail( $wgUser ) ) {
7575 $this->dieUsageMsg( array( 'cantblock-email' ) );
7676 }
7777
78 - $form = new IPBlockForm( '' );
79 - $form->BlockAddress = $params['user'];
80 - $form->BlockReason = ( is_null( $params['reason'] ) ? '' : $params['reason'] );
81 - $form->BlockReasonList = 'other';
82 - $form->BlockExpiry = ( $params['expiry'] == 'never' ? 'infinite' : $params['expiry'] );
83 - $form->BlockOther = '';
84 - $form->BlockAnonOnly = $params['anononly'];
85 - $form->BlockCreateAccount = $params['nocreate'];
86 - $form->BlockEnableAutoblock = $params['autoblock'];
87 - $form->BlockEmail = $params['noemail'];
88 - $form->BlockHideName = $params['hidename'];
89 - $form->BlockAllowUsertalk = $params['allowusertalk'] && $wgBlockAllowsUTEdit;
90 - $form->BlockReblock = $params['reblock'];
91 - $form->BlockWatchUser = $params['watchuser'];
 78+ $data = array(
 79+ 'Target' => $params['user'],
 80+ 'Reason' => array(
 81+ is_null( $params['reason'] ) ? '' : $params['reason'],
 82+ 'other',
 83+ is_null( $params['reason'] ) ? '' : $params['reason']
 84+ ),
 85+ 'Expiry' => $params['expiry'] == 'never' ? 'infinite' : $params['expiry'],
 86+ 'HardBlock' => !$params['anononly'],
 87+ 'CreateAccount' => $params['nocreate'],
 88+ 'AutoBlock' => $params['autoblock'],
 89+ 'DisableEmail' => $params['noemail'],
 90+ 'HideUser' => $params['hidename'],
 91+ 'DisableUTEdit' => $params['allowusertalk'],
 92+ 'AlreadyBlocked' => $params['reblock'],
 93+ 'Watch' => $params['watchuser'],
 94+ );
9295
93 - $userID = $expiry = null;
94 - $retval = $form->doBlock( $userID, $expiry );
95 - if ( count( $retval ) ) {
 96+ $retval = SpecialBlock::processForm( $data );
 97+ if ( $retval !== true ) {
9698 // We don't care about multiple errors, just report one of them
9799 $this->dieUsageMsg( $retval );
98100 }
99101
 102+ list( $target, $type ) = SpecialBlock::getTargetAndType( $params['user'] );
100103 $res['user'] = $params['user'];
101 - $res['userID'] = intval( $userID );
102 - $res['expiry'] = ( $expiry == Block::infinity() ? 'infinite' : wfTimestamp( TS_ISO_8601, $expiry ) );
 104+ $res['userID'] = $target instanceof User ? $target->getId() : 0;
 105+
 106+ $block = SpecialBlock::getBlockFromTargetAndType( $target, $type );
 107+ if( $block instanceof Block ){
 108+ $res['expiry'] = $block->mExpiry == Block::infinity()
 109+ ? 'infinite'
 110+ : wfTimestamp( TS_ISO_8601, $block->mExpiry );
 111+ } else {
 112+ # should be unreachable
 113+ $res['expiry'] = '';
 114+ }
 115+
103116 $res['reason'] = $params['reason'];
104117 if ( $params['anononly'] ) {
105118 $res['anononly'] = '';
Index: trunk/phase3/includes/specials/SpecialBlockip.php
@@ -1,846 +0,0 @@
2 -<?php
3 -/**
4 - * Implements Special:Blockip
5 - *
6 - * This program is free software; you can redistribute it and/or modify
7 - * it under the terms of the GNU General Public License as published by
8 - * the Free Software Foundation; either version 2 of the License, or
9 - * (at your option) any later version.
10 - *
11 - * This program is distributed in the hope that it will be useful,
12 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 - * GNU General Public License for more details.
15 - *
16 - * You should have received a copy of the GNU General Public License along
17 - * with this program; if not, write to the Free Software Foundation, Inc.,
18 - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 - * http://www.gnu.org/copyleft/gpl.html
20 - *
21 - * @file
22 - * @ingroup SpecialPage
23 - */
24 -
25 -/**
26 - * A special page that allows users with 'block' right to block users from
27 - * editing pages and other actions
28 - *
29 - * @ingroup SpecialPage
30 - */
31 -class IPBlockForm extends SpecialPage {
32 - var $BlockAddress, $BlockExpiry, $BlockReason, $BlockReasonList, $BlockOther, $BlockAnonOnly, $BlockCreateAccount,
33 - $BlockEnableAutoblock, $BlockEmail, $BlockHideName, $BlockAllowUsertalk, $BlockReblock, $BlockWatchUser;
34 - // The maximum number of edits a user can have and still be hidden
35 - const HIDEUSER_CONTRIBLIMIT = 1000;
36 -
37 - public function __construct() {
38 - parent::__construct( 'Blockip', 'block' );
39 - }
40 -
41 - public function execute( $par ) {
42 - global $wgUser, $wgOut, $wgRequest;
43 -
44 - # Can't block when the database is locked
45 - if( wfReadOnly() ) {
46 - $wgOut->readOnlyPage();
47 - return;
48 - }
49 - # Permission check
50 - if( !$this->userCanExecute( $wgUser ) ) {
51 - $wgOut->permissionRequired( 'block' );
52 - return;
53 - }
54 -
55 - $this->setup( $par );
56 -
57 - # bug 15810: blocked admins should have limited access here
58 - if ( $wgUser->isBlocked() ) {
59 - $status = IPBlockForm::checkUnblockSelf( $this->BlockAddress );
60 - if ( $status !== true ) {
61 - throw new ErrorPageError( 'badaccess', $status );
62 - }
63 - }
64 -
65 - $action = $wgRequest->getVal( 'action' );
66 - if( 'success' == $action ) {
67 - $this->showSuccess();
68 - } elseif( $wgRequest->wasPosted() && 'submit' == $action &&
69 - $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
70 - $this->doSubmit();
71 - } else {
72 - $this->showForm( '' );
73 - }
74 - }
75 -
76 - private function setup( $par ) {
77 - global $wgRequest, $wgUser, $wgBlockAllowsUTEdit;
78 -
79 - $this->BlockAddress = $wgRequest->getVal( 'wpBlockAddress', $wgRequest->getVal( 'ip', $par ) );
80 - $this->BlockAddress = strtr( $this->BlockAddress, '_', ' ' );
81 - $this->BlockReason = $wgRequest->getText( 'wpBlockReason' );
82 - $this->BlockReasonList = $wgRequest->getText( 'wpBlockReasonList' );
83 - $this->BlockExpiry = $wgRequest->getVal( 'wpBlockExpiry', wfMsg( 'ipbotheroption' ) );
84 - $this->BlockOther = $wgRequest->getVal( 'wpBlockOther', '' );
85 -
86 - # Unchecked checkboxes are not included in the form data at all, so having one
87 - # that is true by default is a bit tricky
88 - $byDefault = !$wgRequest->wasPosted();
89 - $this->BlockAnonOnly = $wgRequest->getBool( 'wpAnonOnly', $byDefault );
90 - $this->BlockCreateAccount = $wgRequest->getBool( 'wpCreateAccount', $byDefault );
91 - $this->BlockEnableAutoblock = $wgRequest->getBool( 'wpEnableAutoblock', $byDefault );
92 - $this->BlockEmail = false;
93 - if( self::canBlockEmail( $wgUser ) ) {
94 - $this->BlockEmail = $wgRequest->getBool( 'wpEmailBan', false );
95 - }
96 - $this->BlockWatchUser = $wgRequest->getBool( 'wpWatchUser', false ) && $wgUser->isLoggedIn();
97 - # Re-check user's rights to hide names, very serious, defaults to null
98 - if( $wgUser->isAllowed( 'hideuser' ) ) {
99 - $this->BlockHideName = $wgRequest->getBool( 'wpHideName', null );
100 - } else {
101 - $this->BlockHideName = false;
102 - }
103 - $this->BlockAllowUsertalk = ( $wgRequest->getBool( 'wpAllowUsertalk', $byDefault ) && $wgBlockAllowsUTEdit );
104 - $this->BlockReblock = $wgRequest->getBool( 'wpChangeBlock', false );
105 -
106 - $this->wasPosted = $wgRequest->wasPosted();
107 - }
108 -
109 - public function showForm( $err ) {
110 - global $wgOut, $wgUser;
111 -
112 - $wgOut->setPageTitle( wfMsg( 'blockip-title' ) );
113 - $wgOut->addWikiMsg( 'blockiptext' );
114 -
115 - $mIpaddress = Xml::label( wfMsg( 'ipadressorusername' ), 'mw-bi-target' );
116 - $mIpbexpiry = Xml::label( wfMsg( 'ipbexpiry' ), 'wpBlockExpiry' );
117 - $mIpbother = Xml::label( wfMsg( 'ipbother' ), 'mw-bi-other' );
118 - $mIpbreasonother = Xml::label( wfMsg( 'ipbreason' ), 'wpBlockReasonList' );
119 - $mIpbreason = Xml::label( wfMsg( 'ipbotherreason' ), 'mw-bi-reason' );
120 -
121 - $titleObj = SpecialPage::getTitleFor( 'Blockip' );
122 - $user = User::newFromName( $this->BlockAddress );
123 - if ( is_object( $user ) || User::isIP( $this->BlockAddress ) ) {
124 - $wgUser->getSkin()->setRelevantUser( is_object($user) ? $user : User::newFromName( $this->BlockAddress, false ) );
125 - }
126 -
127 - $alreadyBlocked = false;
128 - $otherBlockedMsgs = array();
129 - if( $err && $err[0] != 'ipb_already_blocked' ) {
130 - $key = array_shift( $err );
131 - $msg = wfMsgExt( $key, 'parsemag', $err );
132 - $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
133 - $wgOut->addHTML( Xml::tags( 'p', array( 'class' => 'error' ), $msg ) );
134 - } elseif( $this->BlockAddress !== null ) {
135 - # Get other blocks, i.e. from GlobalBlocking or TorBlock extension
136 - wfRunHooks( 'OtherBlockLogLink', array( &$otherBlockedMsgs, $this->BlockAddress ) );
137 -
138 - $userId = is_object( $user ) ? $user->getId() : 0;
139 - $currentBlock = Block::newFromDB( $this->BlockAddress, $userId );
140 - if( !is_null( $currentBlock ) && !$currentBlock->mAuto && # The block exists and isn't an autoblock
141 - ( $currentBlock->mRangeStart == $currentBlock->mRangeEnd || # The block isn't a rangeblock
142 - # or if it is, the range is what we're about to block
143 - ( $currentBlock->mAddress == $this->BlockAddress ) )
144 - ) {
145 - $alreadyBlocked = true;
146 - # Set the block form settings to the existing block
147 - if( !$this->wasPosted ) {
148 - $this->BlockAnonOnly = $currentBlock->mAnonOnly;
149 - $this->BlockCreateAccount = $currentBlock->mCreateAccount;
150 - $this->BlockEnableAutoblock = $currentBlock->mEnableAutoblock;
151 - $this->BlockEmail = $currentBlock->mBlockEmail;
152 - $this->BlockHideName = $currentBlock->mHideName;
153 - $this->BlockAllowUsertalk = $currentBlock->mAllowUsertalk;
154 - if( $currentBlock->mExpiry == 'infinity' ) {
155 - $this->BlockOther = 'indefinite';
156 - } else {
157 - $this->BlockOther = wfTimestamp( TS_ISO_8601, $currentBlock->mExpiry );
158 - }
159 - $this->BlockReason = $currentBlock->mReason;
160 - }
161 - }
162 - }
163 -
164 - # Show other blocks from extensions, i.e. GlockBlocking and TorBlock
165 - if( count( $otherBlockedMsgs ) ) {
166 - $wgOut->addHTML(
167 - Html::rawElement( 'h2', array(), wfMsgExt( 'ipb-otherblocks-header', 'parseinline', count( $otherBlockedMsgs ) ) ) . "\n"
168 - );
169 - $list = '';
170 - foreach( $otherBlockedMsgs as $link ) {
171 - $list .= Html::rawElement( 'li', array(), $link ) . "\n";
172 - }
173 - $wgOut->addHTML( Html::rawElement( 'ul', array( 'class' => 'mw-blockip-alreadyblocked' ), $list ) . "\n" );
174 - }
175 -
176 - # Username/IP is blocked already locally
177 - if( $alreadyBlocked ) {
178 - $wgOut->wrapWikiMsg( "<div class='mw-ipb-needreblock'>\n$1\n</div>", array( 'ipb-needreblock', $this->BlockAddress ) );
179 - }
180 -
181 - $scBlockExpiryOptions = wfMsgForContent( 'ipboptions' );
182 -
183 - $showblockoptions = $scBlockExpiryOptions != '-';
184 - if( !$showblockoptions ) $mIpbother = $mIpbexpiry;
185 -
186 - $blockExpiryFormOptions = Xml::option( wfMsg( 'ipbotheroption' ), 'other' );
187 - foreach( explode( ',', $scBlockExpiryOptions ) as $option ) {
188 - if( strpos( $option, ':' ) === false ) $option = "$option:$option";
189 - list( $show, $value ) = explode( ':', $option );
190 - $show = htmlspecialchars( $show );
191 - $value = htmlspecialchars( $value );
192 - $blockExpiryFormOptions .= Xml::option( $show, $value, $this->BlockExpiry === $value ) . "\n";
193 - }
194 -
195 - $reasonDropDown = Xml::listDropDown( 'wpBlockReasonList',
196 - wfMsgForContent( 'ipbreason-dropdown' ),
197 - wfMsgForContent( 'ipbreasonotherlist' ), $this->BlockReasonList, 'wpBlockDropDown', 4 );
198 -
199 - # FIXME: this should actually use HTMLForm, not just some of its JavaScript
200 - $wgOut->addModules( array( 'mediawiki.special.block', 'mediawiki.htmlform' ) );
201 -
202 - $wgOut->addHTML(
203 - Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( 'action=submit' ), 'id' => 'blockip' ) ) .
204 - Xml::openElement( 'fieldset' ) .
205 - Xml::element( 'legend', null, wfMsg( 'blockip-legend' ) ) .
206 - Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-blockip-table' ) ) .
207 - "<tr>
208 - <td class='mw-label'>
209 - {$mIpaddress}
210 - </td>
211 - <td class='mw-input'>" .
212 - Html::input( 'wpBlockAddress', $this->BlockAddress, 'text', array(
213 - 'tabindex' => '1',
214 - 'id' => 'mw-bi-target',
215 - 'size' => '45',
216 - 'required' => ''
217 - ) + ( $this->BlockAddress ? array() : array( 'autofocus' ) ) ). "
218 - </td>
219 - </tr>
220 - <tr>
221 - <td class='mw-label'>
222 - {$mIpbexpiry}
223 - </td>
224 - <td class='mw-input'>"
225 - );
226 - if( $showblockoptions ) {
227 - $wgOut->addHTML(
228 - Xml::tags( 'select',
229 - array(
230 - 'id' => 'wpBlockExpiry',
231 - 'name' => 'wpBlockExpiry',
232 - 'class' => 'mw-htmlform-select-or-other', # FIXME: actually make this use HTMLForm
233 - 'tabindex' => '2' ),
234 - $blockExpiryFormOptions
235 - ) . "<br/>\n"
236 - );
237 - }
238 - $wgOut->addHTML(
239 - Xml::input(
240 - 'wpBlockOther',
241 - 45,
242 - $this->BlockOther,
243 - array(
244 - 'tabindex' => '3',
245 - 'id' => 'wpBlockExpiry-other'
246 - )
247 - ) . "
248 - </td>
249 - </tr>
250 - <tr>
251 - <td class='mw-label'>
252 - {$mIpbreasonother}
253 - </td>
254 - <td class='mw-input'>
255 - {$reasonDropDown}
256 - </td>
257 - </tr>
258 - <tr id=\"wpBlockReason\">
259 - <td class='mw-label'>
260 - {$mIpbreason}
261 - </td>
262 - <td class='mw-input'>" .
263 - Html::input( 'wpBlockReason', $this->BlockReason, 'text', array(
264 - 'tabindex' => '5',
265 - 'id' => 'mw-bi-reason',
266 - 'maxlength' => '200',
267 - 'size' => '45'
268 - ) + ( $this->BlockAddress ? array( 'autofocus' ) : array() ) ) . "
269 - </td>
270 - </tr>
271 - <tr id='wpCreateAccountRow'>
272 - <td>&#160;</td>
273 - <td class='mw-input'>" .
274 - Xml::checkLabel( wfMsg( 'ipbcreateaccount' ),
275 - 'wpCreateAccount', 'wpCreateAccount', $this->BlockCreateAccount,
276 - array( 'tabindex' => '7' ) ) . "
277 - </td>
278 - </tr>"
279 - );
280 -
281 - if( self::canBlockEmail( $wgUser ) ) {
282 - $wgOut->addHTML("
283 - <tr id='wpEnableEmailBan'>
284 - <td>&#160;</td>
285 - <td class='mw-input'>" .
286 - Xml::checkLabel( wfMsg( 'ipbemailban' ),
287 - 'wpEmailBan', 'wpEmailBan', $this->BlockEmail,
288 - array( 'tabindex' => '9' ) ) . "
289 - </td>
290 - </tr>"
291 - );
292 - }
293 -
294 - # Can we explicitly disallow the use of user_talk?
295 - global $wgBlockAllowsUTEdit;
296 - if( $wgBlockAllowsUTEdit ){
297 - $wgOut->addHTML("
298 - <tr id='wpAllowUsertalkRow'>
299 - <td>&#160;</td>
300 - <td class='mw-input'>" .
301 - Xml::checkLabel( wfMsg( 'ipballowusertalk' ),
302 - 'wpAllowUsertalk', 'wpAllowUsertalk', $this->BlockAllowUsertalk,
303 - array( 'tabindex' => '12' ) ) . "
304 - </td>
305 - </tr>"
306 - );
307 - }
308 -
309 - $wgOut->addHTML( "
310 - <tr id='wpEnableAutoblockRow'>
311 - <td>&#160;</td>
312 - <td class='mw-input'>" .
313 - Xml::checkLabel( wfMsg( 'ipbenableautoblock' ),
314 - 'wpEnableAutoblock', 'wpEnableAutoblock', $this->BlockEnableAutoblock,
315 - array( 'tabindex' => '8' ) ) . "
316 - </td>
317 - </tr>"
318 - );
319 -
320 - // Allow some users to hide name from block log, blocklist and listusers
321 - if( $wgUser->isAllowed( 'hideuser' ) ) {
322 - $wgOut->addHTML("
323 - <tr id='wpEnableHideUser'>
324 - <td>&#160;</td>
325 - <td class='mw-input'><strong>" .
326 - Xml::checkLabel( wfMsg( 'ipbhidename' ),
327 - 'wpHideName', 'wpHideName', $this->BlockHideName,
328 - array( 'tabindex' => '10' )
329 - ) . "
330 - </strong></td>
331 - </tr>"
332 - );
333 - }
334 -
335 - # Watchlist their user page? (Only if user is logged in)
336 - if( $wgUser->isLoggedIn() ) {
337 - $wgOut->addHTML("
338 - <tr id='wpEnableWatchUser'>
339 - <td>&#160;</td>
340 - <td class='mw-input'>" .
341 - Xml::checkLabel( wfMsg( 'ipbwatchuser' ),
342 - 'wpWatchUser', 'wpWatchUser', $this->BlockWatchUser,
343 - array( 'tabindex' => '11' ) ) . "
344 - </td>
345 - </tr>"
346 - );
347 - }
348 -
349 - $wgOut->addHTML("
350 - <tr id='wpAnonOnlyRow'>
351 - <td>&#160;</td>
352 - <td class='mw-input'>" .
353 - Xml::checkLabel( wfMsg( 'ipbanononly' ),
354 - 'wpAnonOnly', 'wpAnonOnly', $this->BlockAnonOnly,
355 - array( 'tabindex' => '6' ) ) . "
356 - </td>
357 - </tr>
358 - <tr>
359 - <td style='padding-top: 1em'>&#160;</td>
360 - <td class='mw-submit' style='padding-top: 1em'>" .
361 - Xml::submitButton( wfMsg( $alreadyBlocked ? 'ipb-change-block' : 'ipbsubmit' ),
362 - array( 'name' => 'wpBlock', 'tabindex' => '13' )
363 - + $wgUser->getSkin()->tooltipAndAccessKeyAttribs( 'blockip-block' ) ). "
364 - </td>
365 - </tr>" .
366 - Xml::closeElement( 'table' ) .
367 - Html::hidden( 'wpEditToken', $wgUser->editToken() ) .
368 - ( $alreadyBlocked ? Html::hidden( 'wpChangeBlock', 1 ) : "" ) .
369 - Xml::closeElement( 'fieldset' ) .
370 - Xml::closeElement( 'form' )
371 - );
372 -
373 - $wgOut->addHTML( $this->getConvenienceLinks() );
374 -
375 - if( is_object( $user ) ) {
376 - $this->showLogFragment( $wgOut, $user->getUserPage() );
377 - } elseif( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $this->BlockAddress ) ) {
378 - $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
379 - } elseif( preg_match( '/^\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}/', $this->BlockAddress ) ) {
380 - $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
381 - }
382 - }
383 -
384 - /**
385 - * Can we do an email block?
386 - * @param $user User: the sysop wanting to make a block
387 - * @return Boolean
388 - */
389 - public static function canBlockEmail( $user ) {
390 - global $wgEnableUserEmail, $wgSysopEmailBans;
391 - return ( $wgEnableUserEmail && $wgSysopEmailBans && $user->isAllowed( 'blockemail' ) );
392 - }
393 -
394 - /**
395 - * bug 15810: blocked admins should not be able to block/unblock
396 - * others, and probably shouldn't be able to unblock themselves
397 - * either.
398 - * @param $user User|Int|String
399 - */
400 - public static function checkUnblockSelf( $user ) {
401 - global $wgUser;
402 - if ( is_int( $user ) ) {
403 - $user = User::newFromId( $user );
404 - } elseif ( is_string( $user ) ) {
405 - $user = User::newFromName( $user );
406 - }
407 - if( $user instanceof User && $user->getId() == $wgUser->getId() ) {
408 - # User is trying to unblock themselves
409 - if ( $wgUser->isAllowed( 'unblockself' ) ) {
410 - return true;
411 - } else {
412 - return 'ipbnounblockself';
413 - }
414 - } else {
415 - # User is trying to block/unblock someone else
416 - return 'ipbblocked';
417 - }
418 - }
419 -
420 - /**
421 - * Backend block code.
422 - * $userID and $expiry will be filled accordingly
423 - * @return array(message key, arguments) on failure, empty array on success
424 - */
425 - function doBlock( &$userId = null, &$expiry = null ) {
426 - global $wgUser, $wgBlockAllowsUTEdit, $wgBlockCIDRLimit;
427 -
428 - $userId = 0;
429 - # Expand valid IPv6 addresses, usernames are left as is
430 - $this->BlockAddress = IP::sanitizeIP( $this->BlockAddress );
431 - # isIPv4() and IPv6() are used for final validation
432 - $rxIP4 = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
433 - $rxIP6 = '\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}';
434 - $rxIP = "($rxIP4|$rxIP6)";
435 -
436 - # Check for invalid specifications
437 - if( !preg_match( "/^$rxIP$/", $this->BlockAddress ) ) {
438 - $matches = array();
439 - if( preg_match( "/^($rxIP4)\\/(\\d{1,2})$/", $this->BlockAddress, $matches ) ) {
440 - # IPv4
441 - if( $wgBlockCIDRLimit['IPv4'] != 32 ){
442 - if( !IP::isIPv4( $this->BlockAddress ) || $matches[2] > 32 ) {
443 - return array( 'ip_range_invalid' );
444 - } elseif ( $matches[2] < $wgBlockCIDRLimit['IPv4'] ) {
445 - return array( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv4'] );
446 - }
447 - $this->BlockAddress = Block::normaliseRange( $this->BlockAddress );
448 - } else {
449 - # Range block illegal
450 - return array( 'range_block_disabled' );
451 - }
452 - } elseif( preg_match( "/^($rxIP6)\\/(\\d{1,3})$/", $this->BlockAddress, $matches ) ) {
453 - # IPv6
454 - if( $wgBlockCIDRLimit['IPv6'] != 128 ) {
455 - if( !IP::isIPv6( $this->BlockAddress ) || $matches[2] > 128 ) {
456 - return array( 'ip_range_invalid' );
457 - } elseif( $matches[2] < $wgBlockCIDRLimit['IPv6'] ) {
458 - return array( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv6'] );
459 - }
460 - $this->BlockAddress = Block::normaliseRange( $this->BlockAddress );
461 - } else {
462 - # Range block illegal
463 - return array('range_block_disabled');
464 - }
465 - } else {
466 - # Username block
467 - $user = User::newFromName( $this->BlockAddress );
468 - if( $user instanceof User && $user->getId() ) {
469 - # Use canonical name
470 - $userId = $user->getId();
471 - $this->BlockAddress = $user->getName();
472 - } else {
473 - return array( 'nosuchusershort', htmlspecialchars( $user ? $user->getName() : $this->BlockAddress ) );
474 - }
475 - }
476 - }
477 -
478 - if( $wgUser->isBlocked() && ( $wgUser->getId() !== $userId ) ) {
479 - return array( 'cant-block-while-blocked' );
480 - }
481 -
482 - $reasonstr = $this->BlockReasonList;
483 - if( $reasonstr != 'other' && $this->BlockReason != '' ) {
484 - // Entry from drop down menu + additional comment
485 - $reasonstr .= wfMsgForContent( 'colon-separator' ) . $this->BlockReason;
486 - } elseif( $reasonstr == 'other' ) {
487 - $reasonstr = $this->BlockReason;
488 - }
489 -
490 - $expirestr = $this->BlockExpiry;
491 - if( $expirestr == 'other' )
492 - $expirestr = $this->BlockOther;
493 -
494 - if( ( strlen( $expirestr ) == 0) || ( strlen( $expirestr ) > 50 ) ) {
495 - return array( 'ipb_expiry_invalid' );
496 - }
497 -
498 - if( false === ( $expiry = Block::parseExpiryInput( $expirestr ) ) ) {
499 - // Bad expiry.
500 - return array( 'ipb_expiry_invalid' );
501 - }
502 -
503 - if( $this->BlockHideName ) {
504 - // Recheck params here...
505 - if( !$userId || !$wgUser->isAllowed('hideuser') ) {
506 - $this->BlockHideName = false; // IP users should not be hidden
507 - } elseif( $expiry !== 'infinity' ) {
508 - // Bad expiry.
509 - return array( 'ipb_expiry_temp' );
510 - } elseif( User::edits( $userId ) > self::HIDEUSER_CONTRIBLIMIT ) {
511 - // Typically, the user should have a handful of edits.
512 - // Disallow hiding users with many edits for performance.
513 - return array( 'ipb_hide_invalid' );
514 - }
515 - }
516 -
517 - # Create block object
518 - # Note: for a user block, ipb_address is only for display purposes
519 - $block = new Block( $this->BlockAddress, $userId, $wgUser->getId(),
520 - $reasonstr, wfTimestampNow(), 0, $expiry, $this->BlockAnonOnly,
521 - $this->BlockCreateAccount, $this->BlockEnableAutoblock, $this->BlockHideName,
522 - $this->BlockEmail,
523 - isset( $this->BlockAllowUsertalk ) ? $this->BlockAllowUsertalk : $wgBlockAllowsUTEdit
524 - );
525 -
526 - # Should this be privately logged?
527 - $suppressLog = (bool)$this->BlockHideName;
528 - if( wfRunHooks( 'BlockIp', array( &$block, &$wgUser ) ) ) {
529 - # Try to insert block. Is there a conflicting block?
530 - if( !$block->insert() ) {
531 - # Show form unless the user is already aware of this...
532 - if( !$this->BlockReblock ) {
533 - return array( 'ipb_already_blocked' );
534 - # Otherwise, try to update the block...
535 - } else {
536 - # This returns direct blocks before autoblocks/rangeblocks, since we should
537 - # be sure the user is blocked by now it should work for our purposes
538 - $currentBlock = Block::newFromDB( $this->BlockAddress, $userId );
539 - if( $block->equals( $currentBlock ) ) {
540 - return array( 'ipb_already_blocked' );
541 - }
542 - # If the name was hidden and the blocking user cannot hide
543 - # names, then don't allow any block changes...
544 - if( $currentBlock->mHideName && !$wgUser->isAllowed( 'hideuser' ) ) {
545 - return array( 'cant-see-hidden-user' );
546 - }
547 - $currentBlock->delete();
548 - $block->insert();
549 - # If hiding/unhiding a name, this should go in the private logs
550 - $suppressLog = $suppressLog || (bool)$currentBlock->mHideName;
551 - $log_action = 'reblock';
552 - # Unset _deleted fields if requested
553 - if( $currentBlock->mHideName && !$this->BlockHideName ) {
554 - RevisionDeleteUser::unsuppressUserName( $this->BlockAddress, $userId );
555 - }
556 - }
557 - } else {
558 - $log_action = 'block';
559 - }
560 - wfRunHooks( 'BlockIpComplete', array( $block, $wgUser ) );
561 -
562 - # Set *_deleted fields if requested
563 - if( $this->BlockHideName ) {
564 - RevisionDeleteUser::suppressUserName( $this->BlockAddress, $userId );
565 - }
566 -
567 - # Only show watch link when this is no range block
568 - if( $this->BlockWatchUser && $block->mRangeStart == $block->mRangeEnd ) {
569 - $wgUser->addWatch( Title::makeTitle( NS_USER, $this->BlockAddress ) );
570 - }
571 -
572 - # Block constructor sanitizes certain block options on insert
573 - $this->BlockEmail = $block->mBlockEmail;
574 - $this->BlockEnableAutoblock = $block->mEnableAutoblock;
575 -
576 - # Prepare log parameters
577 - $logParams = array();
578 - $logParams[] = $expirestr;
579 - $logParams[] = $this->blockLogFlags();
580 -
581 - # Make log entry, if the name is hidden, put it in the oversight log
582 - $log_type = $suppressLog ? 'suppress' : 'block';
583 - $log = new LogPage( $log_type );
584 - $log->addEntry( $log_action, Title::makeTitle( NS_USER, $this->BlockAddress ),
585 - $reasonstr, $logParams );
586 -
587 - # Report to the user
588 - return array();
589 - } else {
590 - return array( 'hookaborted' );
591 - }
592 - }
593 -
594 - # @deprecated since 1.18
595 - public static function suppressUserName( $name, $userId, $dbw = null ) {
596 - return RevisionDeleteUser::suppressUserName( $name, $userId, $dbw );
597 - }
598 -
599 - # @deprecated since 1.18
600 - public static function unsuppressUserName( $name, $userId, $dbw = null ) {
601 - return RevisionDeleteUser::unsuppressUserName( $name, $userId, $dbw );
602 - }
603 -
604 - /**
605 - * UI entry point for blocking
606 - * Wraps around doBlock()
607 - */
608 - public function doSubmit() {
609 - global $wgOut;
610 - $retval = $this->doBlock();
611 - if( empty( $retval ) ) {
612 - $titleObj = SpecialPage::getTitleFor( 'Blockip' );
613 - $wgOut->redirect( $titleObj->getFullURL( 'action=success&ip=' .
614 - urlencode( $this->BlockAddress ) ) );
615 - return;
616 - }
617 - $this->showForm( $retval );
618 - }
619 -
620 - public function showSuccess() {
621 - global $wgOut;
622 -
623 - $wgOut->setPageTitle( wfMsg( 'blockip-title' ) );
624 - $wgOut->setSubtitle( wfMsg( 'blockipsuccesssub' ) );
625 - $text = wfMsgExt( 'blockipsuccesstext', array( 'parse' ), $this->BlockAddress );
626 - $wgOut->addHTML( $text );
627 - }
628 -
629 - private function showLogFragment( $out, $title ) {
630 - global $wgUser;
631 -
632 - // Used to support GENDER in 'blocklog-showlog' and 'blocklog-showsuppresslog'
633 - $userBlocked = $title->getText();
634 -
635 - LogEventsList::showLogExtract(
636 - $out,
637 - 'block',
638 - $title->getPrefixedText(),
639 - '',
640 - array(
641 - 'lim' => 10,
642 - 'msgKey' => array(
643 - 'blocklog-showlog',
644 - $userBlocked
645 - ),
646 - 'showIfEmpty' => false
647 - )
648 - );
649 -
650 - // Add suppression block entries if allowed
651 - if( $wgUser->isAllowed( 'suppressionlog' ) ) {
652 - LogEventsList::showLogExtract( $out, 'suppress', $title->getPrefixedText(), '',
653 - array(
654 - 'lim' => 10,
655 - 'conds' => array(
656 - 'log_action' => array(
657 - 'block',
658 - 'reblock',
659 - 'unblock'
660 - )
661 - ),
662 - 'msgKey' => array(
663 - 'blocklog-showsuppresslog',
664 - $userBlocked
665 - ),
666 - 'showIfEmpty' => false
667 - )
668 - );
669 - }
670 - }
671 -
672 - /**
673 - * Return a comma-delimited list of "flags" to be passed to the log
674 - * reader for this block, to provide more information in the logs
675 - *
676 - * @return array
677 - */
678 - private function blockLogFlags() {
679 - global $wgBlockAllowsUTEdit;
680 - $flags = array();
681 - if( $this->BlockAnonOnly && IP::isIPAddress( $this->BlockAddress ) )
682 - // when blocking a user the option 'anononly' is not available/has no effect -> do not write this into log
683 - $flags[] = 'anononly';
684 - if( $this->BlockCreateAccount )
685 - $flags[] = 'nocreate';
686 - if( !$this->BlockEnableAutoblock && !IP::isIPAddress( $this->BlockAddress ) )
687 - // Same as anononly, this is not displayed when blocking an IP address
688 - $flags[] = 'noautoblock';
689 - if( $this->BlockEmail )
690 - $flags[] = 'noemail';
691 - if( !$this->BlockAllowUsertalk && $wgBlockAllowsUTEdit )
692 - $flags[] = 'nousertalk';
693 - if( $this->BlockHideName )
694 - $flags[] = 'hiddenname';
695 - return implode( ',', $flags );
696 - }
697 -
698 - /**
699 - * Builds unblock and block list links
700 - *
701 - * @return string
702 - */
703 - private function getConvenienceLinks() {
704 - global $wgUser, $wgLang;
705 - $skin = $wgUser->getSkin();
706 - if( $this->BlockAddress )
707 - $links[] = $this->getContribsLink( $skin );
708 - $links[] = $this->getUnblockLink( $skin );
709 - $links[] = $this->getBlockListLink( $skin );
710 - if ( $wgUser->isAllowed( 'editinterface' ) ) {
711 - $title = Title::makeTitle( NS_MEDIAWIKI, 'Ipbreason-dropdown' );
712 - $links[] = $skin->link(
713 - $title,
714 - wfMsgHtml( 'ipb-edit-dropdown' ),
715 - array(),
716 - array( 'action' => 'edit' )
717 - );
718 - }
719 - return '<p class="mw-ipb-conveniencelinks">' . $wgLang->pipeList( $links ) . '</p>';
720 - }
721 -
722 - /**
723 - * Build a convenient link to a user or IP's contribs
724 - * form
725 - *
726 - * @param $skin Skin to use
727 - * @return string
728 - */
729 - private function getContribsLink( $skin ) {
730 - $contribsPage = SpecialPage::getTitleFor( 'Contributions', $this->BlockAddress );
731 - return $skin->link( $contribsPage, wfMsgExt( 'ipb-blocklist-contribs', 'escape', $this->BlockAddress ) );
732 - }
733 -
734 - /**
735 - * Build a convenient link to unblock the given username or IP
736 - * address, if available; otherwise link to a blank unblock
737 - * form
738 - *
739 - * @param $skin Skin to use
740 - * @return string
741 - */
742 - private function getUnblockLink( $skin ) {
743 - $list = SpecialPage::getTitleFor( 'Ipblocklist' );
744 - $query = array( 'action' => 'unblock' );
745 -
746 - if( $this->BlockAddress ) {
747 - $addr = strtr( $this->BlockAddress, '_', ' ' );
748 - $message = wfMsg( 'ipb-unblock-addr', $addr );
749 - $query['ip'] = $this->BlockAddress;
750 - } else {
751 - $message = wfMsg( 'ipb-unblock' );
752 - }
753 - return $skin->linkKnown(
754 - $list,
755 - htmlspecialchars( $message ),
756 - array(),
757 - $query
758 - );
759 - }
760 -
761 - /**
762 - * Build a convenience link to the block list
763 - *
764 - * @param $skin Skin to use
765 - * @return string
766 - */
767 - private function getBlockListLink( $skin ) {
768 - return $skin->linkKnown(
769 - SpecialPage::getTitleFor( 'Ipblocklist' ),
770 - wfMsg( 'ipb-blocklist' )
771 - );
772 - }
773 -
774 - /**
775 - * Block a list of selected users
776 - *
777 - * @param $users Array
778 - * @param $reason String
779 - * @param $tag String: replaces user pages
780 - * @param $talkTag String: replaces user talk pages
781 - * @return Array: list of html-safe usernames
782 - */
783 - public static function doMassUserBlock( $users, $reason = '', $tag = '', $talkTag = '' ) {
784 - global $wgUser;
785 - $counter = $blockSize = 0;
786 - $safeUsers = array();
787 - $log = new LogPage( 'block' );
788 - foreach( $users as $name ) {
789 - # Enforce limits
790 - $counter++;
791 - $blockSize++;
792 - # Lets not go *too* fast
793 - if( $blockSize >= 20 ) {
794 - $blockSize = 0;
795 - wfWaitForSlaves( 5 );
796 - }
797 - $u = User::newFromName( $name, false );
798 - // If user doesn't exist, it ought to be an IP then
799 - if( is_null( $u ) || ( !$u->getId() && !IP::isIPAddress( $u->getName() ) ) ) {
800 - continue;
801 - }
802 - $userTitle = $u->getUserPage();
803 - $userTalkTitle = $u->getTalkPage();
804 - $userpage = new Article( $userTitle );
805 - $usertalk = new Article( $userTalkTitle );
806 - $safeUsers[] = '[[' . $userTitle->getPrefixedText() . '|' . $userTitle->getText() . ']]';
807 - $expirestr = $u->getId() ? 'indefinite' : '1 week';
808 - $expiry = Block::parseExpiryInput( $expirestr );
809 - $anonOnly = IP::isIPAddress( $u->getName() ) ? 1 : 0;
810 - // Create the block
811 - $block = new Block( $u->getName(), // victim
812 - $u->getId(), // uid
813 - $wgUser->getId(), // blocker
814 - $reason, // comment
815 - wfTimestampNow(), // block time
816 - 0, // auto ?
817 - $expiry, // duration
818 - $anonOnly, // anononly?
819 - 1, // block account creation?
820 - 1, // autoblocking?
821 - 0, // suppress name?
822 - 0 // block from sending email?
823 - );
824 - $oldblock = Block::newFromDB( $u->getName(), $u->getId() );
825 - if( !$oldblock ) {
826 - $block->insert();
827 - # Prepare log parameters
828 - $logParams = array();
829 - $logParams[] = $expirestr;
830 - if( $anonOnly ) {
831 - $logParams[] = 'anononly';
832 - }
833 - $logParams[] = 'nocreate';
834 - # Add log entry
835 - $log->addEntry( 'block', $userTitle, $reason, $logParams );
836 - }
837 - # Tag userpage! (check length to avoid mistakes)
838 - if( strlen( $tag ) > 2 ) {
839 - $userpage->doEdit( $tag, $reason, EDIT_MINOR );
840 - }
841 - if( strlen( $talkTag ) > 2 ) {
842 - $usertalk->doEdit( $talkTag, $reason, EDIT_MINOR );
843 - }
844 - }
845 - return $safeUsers;
846 - }
847 -}
Index: trunk/phase3/includes/specials/SpecialIpblocklist.php
@@ -79,7 +79,7 @@
8080 } else {
8181 $user = User::newFromName( $ip );
8282 }
83 - $status = IPBlockForm::checkUnblockSelf( $user );
 83+ $status = SpecialBlock::checkUnblockSelf( $user );
8484 if ( $status !== true ) {
8585 throw new ErrorPageError( 'badaccess', $status );
8686 }
Index: trunk/phase3/includes/specials/SpecialBlock.php
@@ -0,0 +1,749 @@
 2+<?php
 3+/**
 4+ * Implements Special:Block
 5+ *
 6+ * This program is free software; you can redistribute it and/or modify
 7+ * it under the terms of the GNU General Public License as published by
 8+ * the Free Software Foundation; either version 2 of the License, or
 9+ * (at your option) any later version.
 10+ *
 11+ * This program is distributed in the hope that it will be useful,
 12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 14+ * GNU General Public License for more details.
 15+ *
 16+ * You should have received a copy of the GNU General Public License along
 17+ * with this program; if not, write to the Free Software Foundation, Inc.,
 18+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 19+ * http://www.gnu.org/copyleft/gpl.html
 20+ *
 21+ * @file
 22+ * @ingroup SpecialPage
 23+ */
 24+
 25+/**
 26+ * A special page that allows users with 'block' right to block users from
 27+ * editing pages and other actions
 28+ *
 29+ * @ingroup SpecialPage
 30+ */
 31+class SpecialBlock extends SpecialPage {
 32+
 33+ # The maximum number of edits a user can have and still be hidden
 34+ # TODO: config setting?
 35+ const HIDEUSER_CONTRIBLIMIT = 1000;
 36+
 37+ # @var User user to be blocked, as passed either by parameter (url?wpTarget=Foo)
 38+ # or as subpage (Special:Block/Foo)
 39+ protected $target;
 40+
 41+ # @var Block::TYPE_ constant
 42+ protected $type;
 43+
 44+ # @var Bool
 45+ protected $alreadyBlocked;
 46+
 47+ public function __construct() {
 48+ parent::__construct( 'Block', 'block' );
 49+ }
 50+
 51+ public function execute( $par ) {
 52+ global $wgUser, $wgOut, $wgRequest;
 53+
 54+ # Can't block when the database is locked
 55+ if( wfReadOnly() ) {
 56+ $wgOut->readOnlyPage();
 57+ return;
 58+ }
 59+ # Permission check
 60+ if( !$this->userCanExecute( $wgUser ) ) {
 61+ $wgOut->permissionRequired( 'block' );
 62+ return;
 63+ }
 64+
 65+ # Extract variables from the request. Try not to get into a situation where we
 66+ # need to extract *every* variable from the form just for processing here, but
 67+ # there are legitimate uses for some variables
 68+ list( $this->target, $this->type ) = self::getTargetAndType( $par, $wgRequest );
 69+ if ( $this->target instanceof User ) {
 70+ # Set the 'relevant user' in the skin, so it displays links like Contributions,
 71+ # User logs, UserRights, etc.
 72+ $wgUser->getSkin()->setRelevantUser( $this->target );
 73+ }
 74+
 75+ # bug 15810: blocked admins should have limited access here
 76+ $status = self::checkUnblockSelf( $this->target );
 77+ if ( $status !== true ) {
 78+ throw new ErrorPageError( 'badaccess', $status );
 79+ }
 80+
 81+ $wgOut->setPageTitle( wfMsg( 'blockip-title' ) );
 82+ $wgOut->addModules( 'mediawiki.special.block' );
 83+
 84+ $fields = self::getFormFields();
 85+ $this->alreadyBlocked = $this->maybeAlterFormDefaults( $fields );
 86+
 87+ $form = new HTMLForm( $fields );
 88+ $form->setTitle( $this->getTitle() );
 89+ $form->setWrapperLegend( wfMsg( 'blockip-legend' ) );
 90+ $form->setSubmitCallback( array( __CLASS__, 'processForm' ) );
 91+
 92+ $t = $this->alreadyBlocked
 93+ ? wfMsg( 'ipb-change-block' )
 94+ : wfMsg( 'ipbsubmit' );
 95+ $form->setSubmitText( $t );
 96+
 97+ $this->doPreText( $form );
 98+ $this->doPostText( $form );
 99+
 100+ if( $form->show() ){
 101+ $wgOut->setPageTitle( wfMsg( 'blockipsuccesssub' ) );
 102+ $wgOut->addHTML( wfMsgExt( 'blockipsuccesstext', array( 'parse' ), $this->target ) );
 103+ }
 104+ }
 105+
 106+ /**
 107+ * Get the HTMLForm descriptor array for the block form
 108+ * @return Array
 109+ */
 110+ protected static function getFormFields(){
 111+ global $wgUser, $wgBlockAllowsUTEdit;
 112+
 113+ $a = array(
 114+ 'Target' => array(
 115+ 'type' => 'text',
 116+ 'label-message' => 'ipadressorusername',
 117+ 'tabindex' => '1',
 118+ 'id' => 'mw-bi-target',
 119+ 'size' => '45',
 120+ 'required' => true,
 121+ ),
 122+ 'Expiry' => array(
 123+ 'type' => 'selectorother',
 124+ 'label-message' => 'ipbexpiry',
 125+ 'required' => true,
 126+ 'tabindex' => '2',
 127+ 'options' => self::getSuggestedDurations(),
 128+ 'other' => wfMsg( 'ipbother' ),
 129+ ),
 130+ 'Reason' => array(
 131+ 'type' => 'selectandother',
 132+ 'label-message' => 'ipbreason',
 133+ 'options-message' => 'ipbreason-dropdown',
 134+ ),
 135+ 'CreateAccount' => array(
 136+ 'type' => 'check',
 137+ 'label-message' => 'ipbcreateaccount',
 138+ 'default' => true,
 139+ ),
 140+ );
 141+
 142+ if( wfMsgForContent( 'ipboptions' ) == '-' ){
 143+ $a['Expiry']['type'] = 'text';
 144+ }
 145+
 146+ if( self::canBlockEmail( $wgUser ) ) {
 147+ $a['DisableEmail'] = array(
 148+ 'type' => 'check',
 149+ 'label-message' => 'ipbemailban',
 150+ );
 151+ }
 152+
 153+ if( $wgBlockAllowsUTEdit ){
 154+ $a['DisableUTEdit'] = array(
 155+ 'type' => 'check',
 156+ 'label-message' => 'ipb-disableusertalk',
 157+ 'default' => false,
 158+ );
 159+ }
 160+
 161+ $a['AutoBlock'] = array(
 162+ 'type' => 'check',
 163+ 'label-message' => 'ipbenableautoblock',
 164+ 'default' => true,
 165+ );
 166+
 167+ # Allow some users to hide name from block log, blocklist and listusers
 168+ if( $wgUser->isAllowed( 'hideuser' ) ) {
 169+ $a['HideUser'] = array(
 170+ 'type' => 'check',
 171+ 'label-message' => 'ipbhidename',
 172+ 'cssclass' => 'mw-block-hideuser',
 173+ );
 174+ }
 175+
 176+ # Watchlist their user page? (Only if user is logged in)
 177+ if( $wgUser->isLoggedIn() ) {
 178+ $a['Watch'] = array(
 179+ 'type' => 'check',
 180+ 'label-message' => 'ipbwatchuser',
 181+ );
 182+ }
 183+
 184+ $a['HardBlock'] = array(
 185+ 'type' => 'check',
 186+ 'label-message' => 'ipb-hardblock',
 187+ 'default' => false,
 188+ );
 189+
 190+ $a['AlreadyBlocked'] = array(
 191+ 'type' => 'hidden',
 192+ 'default' => false,
 193+ );
 194+
 195+ return $a;
 196+ }
 197+
 198+ /**
 199+ * If the user has already been blocked with similar settings, load that block
 200+ * and change the defaults for the form fields to match the existing settings.
 201+ * @param &$fields Array HTMLForm descriptor array
 202+ * @return Bool whether fields were altered (that is, whether the target is
 203+ * already blocked)
 204+ */
 205+ protected function maybeAlterFormDefaults( &$fields ){
 206+ $fields['Target']['default'] = $this->target;
 207+
 208+ $block = self::getBlockFromTargetAndType( $this->target, $this->type );
 209+
 210+ if( $block instanceof Block && !$block->mAuto # The block exists and isn't an autoblock
 211+ && ( $this->type != Block::TYPE_RANGE # The block isn't a rangeblock
 212+ || $block->mAddress == $this->target ) # or if it is, the range is what we're about to block
 213+ )
 214+ {
 215+ $fields['HardBlock']['default'] = !$block->mAnonOnly;
 216+ $fields['CreateAccount']['default'] = $block->mCreateAccount;
 217+ $fields['AutoBlock']['default'] = $block->mEnableAutoblock;
 218+ $fields['DisableEmail']['default'] = $block->mBlockEmail;
 219+ $fields['HideUser']['default'] = $block->mHideName;
 220+ $fields['DisableUTEdit']['default'] = !$block->mAllowUsertalk;
 221+ $fields['Reason']['default'] = $block->mReason;
 222+ $fields['AlreadyBlocked']['default'] = true;
 223+
 224+ if( $block->mExpiry == 'infinity' ) {
 225+ $fields['Expiry']['default'] = 'indefinite';
 226+ } else {
 227+ $fields['Expiry']['default'] = wfTimestamp( TS_RFC2822, $block->mExpiry );
 228+ }
 229+
 230+ return true;
 231+ }
 232+ return false;
 233+ }
 234+
 235+ /**
 236+ * Add header elements like block log entries, etc.
 237+ * @param $form HTMLForm
 238+ * @return void
 239+ */
 240+ protected function doPreText( HTMLForm &$form ){
 241+ $form->addPreText( wfMsgExt( 'blockiptext', 'parse' ) );
 242+
 243+ $otherBlockMessages = array();
 244+ if( $this->target !== null ) {
 245+ # Get other blocks, i.e. from GlobalBlocking or TorBlock extension
 246+ wfRunHooks( 'OtherBlockLogLink', array( &$otherBlockMessages, $this->target ) );
 247+
 248+ if( count( $otherBlockMessages ) ) {
 249+ $s = Html::rawElement(
 250+ 'h2',
 251+ array(),
 252+ wfMsgExt( 'ipb-otherblocks-header', 'parseinline', count( $otherBlockMessages ) )
 253+ ) . "\n";
 254+ $list = '';
 255+ foreach( $otherBlockMessages as $link ) {
 256+ $list .= Html::rawElement( 'li', array(), $link ) . "\n";
 257+ }
 258+ $s .= Html::rawElement(
 259+ 'ul',
 260+ array( 'class' => 'mw-blockip-alreadyblocked' ),
 261+ $list
 262+ ) . "\n";
 263+ $form->addPreText( $s );
 264+ }
 265+ }
 266+
 267+ # Username/IP is blocked already locally
 268+ if( $this->alreadyBlocked ) {
 269+ $form->addPreText( Html::rawElement(
 270+ 'div',
 271+ array( 'class' => 'mw-ipb-needreblock', ),
 272+ wfMsgExt(
 273+ 'ipb-needreblock',
 274+ array( 'parseinline' ),
 275+ $this->target
 276+ ) ) );
 277+ }
 278+ }
 279+
 280+ /**
 281+ * Add footer elements to the form
 282+ * @param $form HTMLForm
 283+ * @return void
 284+ */
 285+ protected function doPostText( HTMLForm &$form ){
 286+ global $wgUser, $wgLang;
 287+
 288+ $skin = $wgUser->getSkin();
 289+
 290+ # Link to the user's contributions, if applicable
 291+ if( $this->target instanceof User ){
 292+ $contribsPage = SpecialPage::getTitleFor( 'Contributions', $this->target->getName() );
 293+ $links[] = $skin->link(
 294+ $contribsPage,
 295+ wfMsgExt( 'ipb-blocklist-contribs', 'escape', $this->target->getName() )
 296+ );
 297+ }
 298+
 299+ # Link to unblock the specified user, or to a blank unblock form
 300+ $list = SpecialPage::getTitleFor( 'Ipblocklist' );
 301+ $query = array( 'action' => 'unblock' );
 302+ if( $this->target instanceof User ) {
 303+ $message = wfMsgExt( 'ipb-unblock-addr', array( 'parseinline' ), $this->target->getName() );
 304+ $query['ip'] = $this->target->getName();
 305+ } else {
 306+ $message = wfMsgExt( 'ipb-unblock', array( 'parseinline' ) );
 307+ }
 308+ $links[] = $skin->linkKnown( $list, $message, array(), $query );
 309+
 310+ # Link to the block list
 311+ $links[] = $skin->linkKnown(
 312+ SpecialPage::getTitleFor( 'Ipblocklist' ),
 313+ wfMsg( 'ipb-blocklist' )
 314+ );
 315+
 316+ # Link to edit the block dropdown reasons, if applicable
 317+ if ( $wgUser->isAllowed( 'editinterface' ) ) {
 318+ $links[] = $skin->link(
 319+ Title::makeTitle( NS_MEDIAWIKI, 'Ipbreason-dropdown' ),
 320+ wfMsgHtml( 'ipb-edit-dropdown' ),
 321+ array(),
 322+ array( 'action' => 'edit' )
 323+ );
 324+ }
 325+
 326+ $form->addPostText( Html::rawElement(
 327+ 'p',
 328+ array( 'class' => 'mw-ipb-conveniencelinks' ),
 329+ $wgLang->pipeList( $links )
 330+ ) );
 331+
 332+ if( $this->target instanceof User ){
 333+ # Get relevant extracts from the block and suppression logs, if possible
 334+ $userpage = $this->target->getUserPage();
 335+ $out = '';
 336+
 337+ LogEventsList::showLogExtract(
 338+ $out,
 339+ 'block',
 340+ $userpage->getPrefixedText(),
 341+ '',
 342+ array(
 343+ 'lim' => 10,
 344+ 'msgKey' => array( 'blocklog-showlog', $userpage->getText() ),
 345+ 'showIfEmpty' => false
 346+ )
 347+ );
 348+ $form->addPostText( $out );
 349+
 350+ # Add suppression block entries if allowed
 351+ if( $wgUser->isAllowed( 'suppressionlog' ) ) {
 352+ LogEventsList::showLogExtract(
 353+ $out,
 354+ 'suppress',
 355+ $userpage->getPrefixedText(),
 356+ '',
 357+ array(
 358+ 'lim' => 10,
 359+ 'conds' => array( 'log_action' => array( 'block', 'reblock', 'unblock' ) ),
 360+ 'msgKey' => array( 'blocklog-showsuppresslog', $userpage->getText() ),
 361+ 'showIfEmpty' => false
 362+ )
 363+ );
 364+ $form->addPostText( $out );
 365+ }
 366+ }
 367+ }
 368+
 369+ /**
 370+ * Determine the target of the block, and the type of target
 371+ * TODO: should be in Block.php?
 372+ * @param $par String subpage parameter passed to setup, or data value from
 373+ * the HTMLForm
 374+ * @param $request WebRequest optionally try and get data from a request too
 375+ * @return void
 376+ */
 377+ public static function getTargetAndType( $par, WebRequest $request = null ){
 378+ $i = 0;
 379+ $target = null;
 380+ while( true ){
 381+ switch( $i++ ){
 382+ case 0:
 383+ # The HTMLForm will check wpTarget first and only if it doesn't get
 384+ # a value use the default, which will be generated from the options
 385+ # below; so this has to have a higher precedence here than $par, or
 386+ # we could end up with different values in $this->target and the HTMLForm!
 387+ if( $request instanceof WebRequest ){
 388+ $target = $request->getText( 'wpTarget', null );
 389+ }
 390+ break;
 391+ case 1:
 392+ $target = $par;
 393+ break;
 394+ case 2:
 395+ if( $request instanceof WebRequest ){
 396+ $target = $request->getText( 'ip', null );
 397+ }
 398+ break;
 399+ case 3:
 400+ # B/C @since 1.18
 401+ if( $request instanceof WebRequest ){
 402+ $target = $request->getText( 'wpBlockAddress', null );
 403+ }
 404+ break;
 405+ case 4:
 406+ break 2;
 407+ }
 408+
 409+ $userObj = User::newFromName( $target );
 410+ if( $userObj instanceof User ){
 411+ return array( $userObj, Block::TYPE_USER );
 412+ } elseif( IP::isValid( $target ) ){
 413+ # We can still create a User if it's an IP address, but we need to turn
 414+ # off validation checking (which would exclude IP addresses)
 415+ return array(
 416+ User::newFromName( IP::sanitizeIP( $target ), false ),
 417+ Block::TYPE_IP
 418+ );
 419+ break;
 420+ } elseif( IP::isValidBlock( $target ) ){
 421+ # Can't create a User from an IP range
 422+ return array( Block::normaliseRange( $target ), Block::TYPE_RANGE );
 423+ }
 424+ }
 425+ return array( null, null );
 426+ }
 427+
 428+ /**
 429+ * Given a target and the target's type, get a block object if possible
 430+ * @param $target String|User
 431+ * @param $type Block::TYPE_ constant
 432+ * @return Block|null
 433+ * TODO: this probably belongs in Block.php when that mess is cleared up
 434+ */
 435+ public static function getBlockFromTargetAndType( $target, $type ){
 436+ if( $target instanceof User ){
 437+ if( $type == Block::TYPE_IP ){
 438+ return Block::newFromDB( $target->getName(), 0 );
 439+ } elseif( $type == Block::TYPE_USER ) {
 440+ return Block::newFromDB( '', $target->getId() );
 441+ } else {
 442+ # Should be unreachable;
 443+ return null;
 444+ }
 445+ } elseif( $type == Block::TYPE_RANGE ){
 446+ return Block::newFromDB( '', $target );
 447+ } else {
 448+ return null;
 449+ }
 450+ }
 451+
 452+ /**
 453+ * Given the form data, actually implement a block
 454+ * @param $data Array
 455+ * @return Bool|String
 456+ */
 457+ public static function processForm( array $data ){
 458+ global $wgUser, $wgBlockAllowsUTEdit, $wgBlockCIDRLimit;
 459+
 460+ list( $target, $type ) = self::getTargetAndType( $data['Target'] );
 461+
 462+ if( $type == Block::TYPE_USER ){
 463+ # TODO: why do we not have a User->exists() method?
 464+ if( !$target->getId() ){
 465+ return array( array( 'nosuchusershort', $target->getName() ) );
 466+ }
 467+
 468+ $status = self::checkUnblockSelf( $target );
 469+ if ( $status !== true ) {
 470+ return array( array( 'badaccess', $status ) );
 471+ }
 472+
 473+ $user = $target;
 474+ $target = $user->getName();
 475+ $userId = $user->getId();
 476+
 477+ } elseif( $type == Block::TYPE_RANGE ){
 478+ list( $ip, $range ) = explode( '/', $target, 2 );
 479+
 480+ if( ( IP::isIPv4( $ip ) && $wgBlockCIDRLimit['IPv4'] == 32 )
 481+ || ( IP::isIPv6( $ip ) && $wgBlockCIDRLimit['IPV6'] == 128 ) )
 482+ {
 483+ # Range block effectively disabled
 484+ return array( 'range_block_disabled' );
 485+ }
 486+
 487+ if( ( IP::isIPv4( $ip ) && $range > 32 )
 488+ || ( IP::isIPv6( $ip ) && $range > 128 ) )
 489+ {
 490+ # Dodgy range
 491+ return array( 'ip_range_invalid' );
 492+ }
 493+
 494+ if( IP::isIPv4( $ip ) && $range < $wgBlockCIDRLimit['IPv4'] ) {
 495+ return array( array( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv4'] ) );
 496+ }
 497+
 498+ if( IP::isIPv6( $ip ) && $range < $wgBlockCIDRLimit['IPv6'] ) {
 499+ return array( array( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv6'] ) );
 500+ }
 501+
 502+ $userId = 0;
 503+
 504+ } elseif( $type == Block::TYPE_IP ){
 505+ # All is well
 506+ $target = $target->getName();
 507+ $userId = 0;
 508+
 509+ } else {
 510+ return array( 'badipaddress' );
 511+ }
 512+
 513+ if( ( strlen( $data['Expiry'] ) == 0) || ( strlen( $data['Expiry'] ) > 50 )
 514+ || !Block::parseExpiryInput( $data['Expiry'] ) )
 515+ {
 516+ return array( 'ipb_expiry_invalid' );
 517+ }
 518+
 519+ if( !$wgBlockAllowsUTEdit ){
 520+ $data['PreventUTEdit'] = true;
 521+ }
 522+
 523+ # If the user has done the form 'properly', they won't even have been given the
 524+ # option to suppress-block unless they have the 'hideuser' permission
 525+ if( !isset( $data['HideUser'] ) ){
 526+ $data['HideUser'] = false;
 527+ }
 528+ if( $data['HideUser'] ) {
 529+ if( !$wgUser->isAllowed('hideuser') ){
 530+ # this codepath is unreachable except by a malicious user spoofing forms,
 531+ # or by race conditions (user has oversight and sysop, loads block form,
 532+ # and is de-oversighted before submission); so need to fail completely
 533+ # rather than just silently disable hiding
 534+ return array( 'badaccess-group0' );
 535+ }
 536+
 537+ # Recheck params here...
 538+ if( $type != Block::TYPE_USER ) {
 539+ $data['HideUser'] = false; # IP users should not be hidden
 540+
 541+ } elseif( !in_array( $data['Expiry'], array( 'inifinite', 'infinity', 'indefinite' ) ) ) {
 542+ # Bad expiry.
 543+ return array( 'ipb_expiry_temp' );
 544+
 545+ } elseif( $user->getEditCount() > self::HIDEUSER_CONTRIBLIMIT ) {
 546+ # Typically, the user should have a handful of edits.
 547+ # Disallow hiding users with many edits for performance.
 548+ return array( 'ipb_hide_invalid' );
 549+ }
 550+ }
 551+
 552+ # Create block object. Note that for a user block, ipb_address is only for display purposes
 553+ # FIXME: Why do we need to pass fourteen optional parameters to do this!?!
 554+ $block = new Block(
 555+ $target, # IP address or User name
 556+ $userId, # User id
 557+ $wgUser->getId(), # Blocker id
 558+ $data['Reason'][0], # Reason string
 559+ wfTimestampNow(), # Block Timestamp
 560+ 0, # Is this an autoblock (no)
 561+ Block::parseExpiryInput( $data['Expiry'] ), # Expiry time
 562+ !$data['HardBlock'], # Block anon only
 563+ $data['CreateAccount'],
 564+ $data['AutoBlock'],
 565+ $data['HideUser'],
 566+ $data['DisableEmail'],
 567+ !$data['DisableUTEdit'] # *Allow* UTEdit
 568+ );
 569+
 570+ if( !wfRunHooks( 'BlockIp', array( &$block, &$wgUser ) ) ) {
 571+ return array( 'hookaborted' );
 572+ }
 573+
 574+ # Try to insert block. Is there a conflicting block?
 575+ if( !$block->insert() ) {
 576+
 577+ # Show form unless the user is already aware of this...
 578+ if( !$data['AlreadyBlocked'] ) {
 579+ return array( array( 'ipb_already_blocked', $data['Target'] ) );
 580+
 581+ # Otherwise, try to update the block...
 582+ } else {
 583+
 584+ # This returns direct blocks before autoblocks/rangeblocks, since we should
 585+ # be sure the user is blocked by now it should work for our purposes
 586+ $currentBlock = Block::newFromDB( $target, $userId );
 587+
 588+ if( $block->equals( $currentBlock ) ) {
 589+ return array( 'ipb_already_blocked' );
 590+ }
 591+
 592+ # If the name was hidden and the blocking user cannot hide
 593+ # names, then don't allow any block changes...
 594+ if( $currentBlock->mHideName && !$wgUser->isAllowed( 'hideuser' ) ) {
 595+ return array( 'cant-see-hidden-user' );
 596+ }
 597+
 598+ $currentBlock->delete();
 599+ $block->insert();
 600+ $logaction = 'reblock';
 601+
 602+ # Unset _deleted fields if requested
 603+ if( $currentBlock->mHideName && !$data['HideUser'] ) {
 604+ RevisionDeleteUser::unsuppressUserName( $target, $userId );
 605+ }
 606+
 607+ # If hiding/unhiding a name, this should go in the private logs
 608+ if( (bool)$currentBlock->mHideName ){
 609+ $data['HideUser'] = true;
 610+ }
 611+ }
 612+
 613+ } else {
 614+ $logaction = 'block';
 615+ }
 616+
 617+ wfRunHooks( 'BlockIpComplete', array( $block, $wgUser ) );
 618+
 619+ # Set *_deleted fields if requested
 620+ if( $data['HideUser'] ) {
 621+ RevisionDeleteUser::suppressUserName( $target, $userId );
 622+ }
 623+
 624+ # Can't watch a rangeblock
 625+ if( $type != Block::TYPE_RANGE && $data['Watch'] ) {
 626+ $wgUser->addWatch( Title::makeTitle( NS_USER, $target ) );
 627+ }
 628+
 629+ # Block constructor sanitizes certain block options on insert
 630+ $data['BlockEmail'] = $block->mBlockEmail;
 631+ $data['AutoBlock'] = $block->mEnableAutoblock;
 632+
 633+ # Prepare log parameters
 634+ $logParams = array();
 635+ $logParams[] = $data['Expiry'];
 636+ $logParams[] = self::blockLogFlags( $data, $type );
 637+
 638+ # Make log entry, if the name is hidden, put it in the oversight log
 639+ $log_type = $data['HideUser'] ? 'suppress' : 'block';
 640+ $log = new LogPage( $log_type );
 641+ $log->addEntry(
 642+ $logaction,
 643+ Title::makeTitle( NS_USER, $target ),
 644+ $data['Reason'][0],
 645+ $logParams
 646+ );
 647+
 648+ # Report to the user
 649+ return true;
 650+ }
 651+
 652+ /**
 653+ * Get an array of suggested block durations from MediaWiki:Ipboptions
 654+ * FIXME: this uses a rather odd syntax for the options, should it be converted
 655+ * to the standard "**<duration>|<displayname>" format?
 656+ * @return Array
 657+ */
 658+ protected static function getSuggestedDurations(){
 659+ $a = array();
 660+ foreach( explode( ',', wfMsgForContent( 'ipboptions' ) ) as $option ) {
 661+ if( strpos( $option, ':' ) === false ) $option = "$option:$option";
 662+ list( $show, $value ) = explode( ':', $option );
 663+ $a[htmlspecialchars( $show )] = htmlspecialchars( $value );
 664+ }
 665+ return $a;
 666+ }
 667+
 668+ /**
 669+ * Can we do an email block?
 670+ * @param $user User: the sysop wanting to make a block
 671+ * @return Boolean
 672+ */
 673+ public static function canBlockEmail( $user ) {
 674+ global $wgEnableUserEmail, $wgSysopEmailBans;
 675+ return ( $wgEnableUserEmail && $wgSysopEmailBans && $user->isAllowed( 'blockemail' ) );
 676+ }
 677+
 678+ /**
 679+ * bug 15810: blocked admins should not be able to block/unblock
 680+ * others, and probably shouldn't be able to unblock themselves
 681+ * either.
 682+ * @param $user User|Int|String
 683+ */
 684+ public static function checkUnblockSelf( $user ) {
 685+ global $wgUser;
 686+ if ( is_int( $user ) ) {
 687+ $user = User::newFromId( $user );
 688+ } elseif ( is_string( $user ) ) {
 689+ $user = User::newFromName( $user );
 690+ }
 691+ if( $wgUser->isBlocked() ){
 692+ if( $user instanceof User && $user->getId() == $wgUser->getId() ) {
 693+ # User is trying to unblock themselves
 694+ if ( $wgUser->isAllowed( 'unblockself' ) ) {
 695+ return true;
 696+ } else {
 697+ return 'ipbnounblockself';
 698+ }
 699+ } else {
 700+ # User is trying to block/unblock someone else
 701+ return 'ipbblocked';
 702+ }
 703+ } else {
 704+ return true;
 705+ }
 706+ }
 707+
 708+ /**
 709+ * Return a comma-delimited list of "flags" to be passed to the log
 710+ * reader for this block, to provide more information in the logs
 711+ * @param $data Array from HTMLForm data
 712+ * @param $type Block::TYPE_ constant
 713+ * @return array
 714+ */
 715+ protected static function blockLogFlags( array $data, $type ) {
 716+ global $wgBlockAllowsUTEdit;
 717+ $flags = array();
 718+
 719+ # when blocking a user the option 'anononly' is not available/has no effect -> do not write this into log
 720+ if( !$data['HardBlock'] && $type != Block::TYPE_USER ){
 721+ $flags[] = 'anononly';
 722+ }
 723+
 724+ if( $data['CreateAccount'] ){
 725+ $flags[] = 'nocreate';
 726+ }
 727+
 728+ # Same as anononly, this is not displayed when blocking an IP address
 729+ if( !$data['AutoBlock'] && $type != Block::TYPE_IP ){
 730+ $flags[] = 'noautoblock';
 731+ }
 732+
 733+ if( $data['DisableEmail'] ){
 734+ $flags[] = 'noemail';
 735+ }
 736+
 737+ if( $data['DisableUTEdit'] && $wgBlockAllowsUTEdit ){
 738+ $flags[] = 'nousertalk';
 739+ }
 740+
 741+ if( $data['HideUser'] ){
 742+ $flags[] = 'hiddenname';
 743+ }
 744+
 745+ return implode( ',', $flags );
 746+ }
 747+}
 748+
 749+# BC @since 1.18
 750+class IPBlockForm extends SpecialBlock {}
Property changes on: trunk/phase3/includes/specials/SpecialBlock.php
___________________________________________________________________
Added: svn:eol-style
1751 + native
Index: trunk/phase3/includes/AutoLoader.php
@@ -601,7 +601,7 @@
602602 'SpecialEmailUser' => 'includes/specials/SpecialEmailuser.php',
603603 'FewestrevisionsPage' => 'includes/specials/SpecialFewestrevisions.php',
604604 'FileDuplicateSearchPage' => 'includes/specials/SpecialFileDuplicateSearch.php',
605 - 'IPBlockForm' => 'includes/specials/SpecialBlockip.php',
 605+ 'IPBlockForm' => 'includes/specials/SpecialBlock.php',
606606 'IPBlocklistPager' => 'includes/specials/SpecialIpblocklist.php',
607607 'IPUnblockForm' => 'includes/specials/SpecialIpblocklist.php',
608608 'ImportReporter' => 'includes/specials/SpecialImport.php',
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -438,7 +438,7 @@
439439 'Version' => array( 'Version' ),
440440 'Allmessages' => array( 'AllMessages' ),
441441 'Log' => array( 'Log', 'Logs' ),
442 - 'Blockip' => array( 'Block', 'BlockIP', 'BlockUser' ),
 442+ 'Block' => array( 'Block', 'BlockIP', 'BlockUser' ),
443443 'Undelete' => array( 'Undelete' ),
444444 'Import' => array( 'Import' ),
445445 'Lockdb' => array( 'LockDB' ),
@@ -3039,18 +3039,18 @@
30403040 ** Intimidating behaviour/harassment
30413041 ** Abusing multiple accounts
30423042 ** Unacceptable username',
3043 -'ipbanononly' => 'Block anonymous users only',
30443043 'ipbcreateaccount' => 'Prevent account creation',
30453044 'ipbemailban' => 'Prevent user from sending e-mail',
30463045 'ipbenableautoblock' => 'Automatically block the last IP address used by this user, and any subsequent IP addresses they try to edit from',
30473046 'ipbsubmit' => 'Block this user',
30483047 'ipbother' => 'Other time:',
3049 -'ipboptions' => '2 hours:2 hours,1 day:1 day,3 days:3 days,1 week:1 week,2 weeks:2 weeks,1 month:1 month,3 months:3 months,6 months:6 months,1 year:1 year,infinite:infinite',
 3048+'ipboptions' => '2 hours:2 hours,1 day:1 day,3 days:3 days,1 week:1 week,2 weeks:2 weeks,1 month:1 month,3 months:3 months,6 months:6 months,1 year:1 year,indefinite:infinite',
30503049 'ipbotheroption' => 'other',
30513050 'ipbotherreason' => 'Other/additional reason:',
30523051 'ipbhidename' => 'Hide username from edits and lists',
30533052 'ipbwatchuser' => "Watch this user's user and talk pages",
3054 -'ipballowusertalk' => 'Allow this user to edit own talk page while blocked',
 3053+'ipb-disableusertalk' => 'Prevent this user from editing their own talk page while blocked',
 3054+'ipb-hardblock' => 'Prevent logged-in users from editing from this IP address',
30553055 'ipb-change-block' => 'Re-block the user with these settings',
30563056 'badipaddress' => 'Invalid IP address',
30573057 'blockipsuccesssub' => 'Block succeeded',
Index: trunk/phase3/RELEASE-NOTES
@@ -55,6 +55,13 @@
5656 Use $wgBlockCIDRLimit = array( 'IPv4' => 43, 'IPv6' => 128 ) to achieve the same
5757 functionality as $wgSysopRangeBans; you can use the BlockIp hook to replicate
5858 $wgSysopUserBans functionality.
 59+* The options on the block form have been standardised such that checking a box
 60+ makes the block 'more serious'; so while "check to prevent account creation" and
 61+ "check to enable autoblock" remain the same, "check to allow user-talk edit" is
 62+ reversed to "check to *disable* user-talk edit", and "check to block anon-only"
 63+ becomes "check to block logged-in users too". The default settings remain the same.
 64+* Most of the field names on the Special:Block form have been changed, which will
 65+ probably break screen-scraping bots.
5966
6067 === New features in 1.18 ===
6168 * Added a special page, disabled by default, that allows users with the

Follow-up revisions

RevisionCommit summaryAuthorDate
r83790Follow-up r83786, try to keep the svn history of SpecialBlockip. This rev wi...happy-melon22:48, 12 March 2011
r83795Follow-up r83794, r83792: restore new SpecialBlock.php code from r83786. Thi...happy-melon22:53, 12 March 2011
r83796Follow-up r83786: update references in SpecialPage::getTitleFor() and friends...happy-melon23:22, 12 March 2011
r83810* Moved Target field validation into a field validation callback to show erro...nikerabbit09:57, 13 March 2011
r83824Fix totally-broken r83810.happy-melon14:41, 13 March 2011
r84028Fix notice from TWN, reported in r83786 CRhappy-melon15:56, 15 March 2011
r84195Fix typo in r83786happy-melon19:04, 17 March 2011
r84269Follow-up r83909, r83825: update special page aliases for all languages.happy-melon21:42, 18 March 2011
r113792(bug 32827) "[Regression] Block log for IP ranges not shown on Special:Block"aaron01:42, 14 March 2012

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r40359Move doMassUserBlock() to coreaaron04:17, 3 September 2008

Comments

#Comment by Bawolff (talk | contribs)   15:23, 15 March 2011

Present on trunk currently, appears starting with this revision:
Notice: Undefined index: DisableUTEdit in /var/www/w/phase3/includes/specials/SpecialBlock.php on line 566

Notice: Undefined index: DisableUTEdit in /var/www/w/phase3/includes/specials/SpecialBlock.php on line 736


As an aside I'd like to say a general Yay! for reworking this scary code :)

#Comment by Happy-melon (talk | contribs)   15:57, 15 March 2011

Fixed in r84028.

Thanks!

#Comment by Platonides (talk | contribs)   17:00, 15 March 2011

Did you really intend to add 'inifinite' as an alias?

Breaks TitlePermissionTest::testUserBlock (infinite changes to indefinite)

#Comment by Happy-melon (talk | contribs)   19:04, 17 March 2011

Nope :D Fixed in r84195.

#Comment by Duplicatebug (talk | contribs)   20:20, 18 March 2011

Same as r83909#c15140: Please update the other Messages*.php-Files, because there are no local aliases for Special:Block in all languages unequal to en. Or it is bad to reuse the local aliases? Thanks.

#Comment by Bawolff (talk | contribs)   20:29, 18 March 2011

Special:blockip is still an alias of special:block on my test wiki (in non-en content language - tested sr). I don't know much about how special page aliases work, but presumably the en one's work on all languages or something.

#Comment by Duplicatebug (talk | contribs)   20:36, 18 March 2011

Yes Special:Blockip works. With local aliases I mean the translated aliases, like "Sperren" for german (de). That alias is not working, because all the message files are missing the change from 'Blockip' to 'Block'.

#Comment by MaxSem (talk | contribs)   16:01, 5 May 2011

Causes bug 28797.

#Comment by Happy-melon (talk | contribs)   13:07, 7 May 2011

Exposes bug 28797, more like. Fixed in r87627.

#Comment by Aaron Schulz (talk | contribs)   17:02, 16 June 2011

Should have used svn move for the specialblockip rename (for easier diffs).

#Comment by Krinkle (talk | contribs)   17:36, 16 June 2011

Not just diffing, what about svn history ?

#Comment by Aaron Schulz (talk | contribs)   17:37, 16 June 2011

Looks like some follow-ups handled this.

#Comment by Aaron Schulz (talk | contribs)   17:35, 16 June 2011

+ # Should be unreachable;

Can this just throw an exception?

#Comment by Aaron Schulz (talk | contribs)   17:47, 16 June 2011

Perhaps checkUnblockSelf() could be optimized a tad to check $wgUser->isBlocked() before the newFromName/newFromId stuff.

#Comment by Krinkle (talk | contribs)   22:12, 13 October 2011
-		'ipballowusertalk',
+		'ipb-disableusertalk',
..
-		$form->BlockAllowUsertalk = $params['allowusertalk'] && $wgBlockAllowsUTEdit;
+			'DisableUTEdit' => $params['allowusertalk'],
bug 31679
#Comment by MaxSem (talk | contribs)   19:38, 8 March 2012

Causes bug 32827: Block log for IP ranges not shown on Special:Block.

Status & tagging log