]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/specials/SpecialBlockip.php
MediaWiki 1.16.1-scripts
[autoinstallsdev/mediawiki.git] / includes / specials / SpecialBlockip.php
1 <?php
2 /**
3  * Constructor for Special:Blockip page
4  *
5  * @file
6  * @ingroup SpecialPage
7  */
8
9 /**
10  * Constructor
11  */
12 function wfSpecialBlockip( $par ) {
13         global $wgUser, $wgOut, $wgRequest;
14
15         # Can't block when the database is locked
16         if( wfReadOnly() ) {
17                 $wgOut->readOnlyPage();
18                 return;
19         }
20         # Permission check
21         if( !$wgUser->isAllowed( 'block' ) ) {
22                 $wgOut->permissionRequired( 'block' );
23                 return;
24         }
25
26         $ipb = new IPBlockForm( $par );
27
28         $action = $wgRequest->getVal( 'action' );
29         if( 'success' == $action ) {
30                 $ipb->showSuccess();
31         } elseif( $wgRequest->wasPosted() && 'submit' == $action &&
32                 $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) {
33                 $ipb->doSubmit();
34         } else {
35                 $ipb->showForm( '' );
36         }
37 }
38
39 /**
40  * Form object for the Special:Blockip page.
41  *
42  * @ingroup SpecialPage
43  */
44 class IPBlockForm {
45         var $BlockAddress, $BlockExpiry, $BlockReason;
46         // The maximum number of edits a user can have and still be hidden
47         const HIDEUSER_CONTRIBLIMIT = 1000;
48
49         public function __construct( $par ) {
50                 global $wgRequest, $wgUser, $wgBlockAllowsUTEdit;
51
52                 $this->BlockAddress = $wgRequest->getVal( 'wpBlockAddress', $wgRequest->getVal( 'ip', $par ) );
53                 $this->BlockAddress = strtr( $this->BlockAddress, '_', ' ' );
54                 $this->BlockReason = $wgRequest->getText( 'wpBlockReason' );
55                 $this->BlockReasonList = $wgRequest->getText( 'wpBlockReasonList' );
56                 $this->BlockExpiry = $wgRequest->getVal( 'wpBlockExpiry', wfMsg( 'ipbotheroption' ) );
57                 $this->BlockOther = $wgRequest->getVal( 'wpBlockOther', '' );
58
59                 # Unchecked checkboxes are not included in the form data at all, so having one
60                 # that is true by default is a bit tricky
61                 $byDefault = !$wgRequest->wasPosted();
62                 $this->BlockAnonOnly = $wgRequest->getBool( 'wpAnonOnly', $byDefault );
63                 $this->BlockCreateAccount = $wgRequest->getBool( 'wpCreateAccount', $byDefault );
64                 $this->BlockEnableAutoblock = $wgRequest->getBool( 'wpEnableAutoblock', $byDefault );
65                 $this->BlockEmail = false;
66                 if( self::canBlockEmail( $wgUser ) ) {
67                         $this->BlockEmail = $wgRequest->getBool( 'wpEmailBan', false );
68                 }
69                 $this->BlockWatchUser = $wgRequest->getBool( 'wpWatchUser', false ) && $wgUser->isLoggedIn();
70                 # Re-check user's rights to hide names, very serious, defaults to null
71                 if( $wgUser->isAllowed( 'hideuser' ) ) {
72                         $this->BlockHideName = $wgRequest->getBool( 'wpHideName', null );
73                 } else {
74                         $this->BlockHideName = false;
75                 }
76                 $this->BlockAllowUsertalk = ( $wgRequest->getBool( 'wpAllowUsertalk', $byDefault ) && $wgBlockAllowsUTEdit );
77                 $this->BlockReblock = $wgRequest->getBool( 'wpChangeBlock', false );
78                 
79                 $this->wasPosted = $wgRequest->wasPosted();
80         }
81
82         public function showForm( $err ) {
83                 global $wgOut, $wgUser, $wgSysopUserBans;
84
85                 $wgOut->setPageTitle( wfMsg( 'blockip-title' ) );
86                 $wgOut->addWikiMsg( 'blockiptext' );
87
88                 if( $wgSysopUserBans ) {
89                         $mIpaddress = Xml::label( wfMsg( 'ipadressorusername' ), 'mw-bi-target' );
90                 } else {
91                         $mIpaddress = Xml::label( wfMsg( 'ipaddress' ), 'mw-bi-target' );
92                 }
93                 $mIpbexpiry = Xml::label( wfMsg( 'ipbexpiry' ), 'wpBlockExpiry' );
94                 $mIpbother = Xml::label( wfMsg( 'ipbother' ), 'mw-bi-other' );
95                 $mIpbreasonother = Xml::label( wfMsg( 'ipbreason' ), 'wpBlockReasonList' );
96                 $mIpbreason = Xml::label( wfMsg( 'ipbotherreason' ), 'mw-bi-reason' );
97
98                 $titleObj = SpecialPage::getTitleFor( 'Blockip' );
99                 $user = User::newFromName( $this->BlockAddress );
100
101                 $alreadyBlocked = false;
102                 $otherBlockedMsgs = array();
103                 if( $err && $err[0] != 'ipb_already_blocked' ) {
104                         $key = array_shift( $err );
105                         $msg = wfMsgReal( $key, $err );
106                         $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) );
107                         $wgOut->addHTML( Xml::tags( 'p', array( 'class' => 'error' ), $msg ) );
108                 } elseif( $this->BlockAddress ) {
109                         # Get other blocks, i.e. from GlobalBlocking or TorBlock extension
110                         wfRunHooks( 'OtherBlockLogLink', array( &$otherBlockedMsgs, $this->BlockAddress ) );
111
112                         $userId = is_object( $user ) ? $user->getId() : 0;
113                         $currentBlock = Block::newFromDB( $this->BlockAddress, $userId );
114                         if( !is_null( $currentBlock ) && !$currentBlock->mAuto && # The block exists and isn't an autoblock
115                                 ( $currentBlock->mRangeStart == $currentBlock->mRangeEnd || # The block isn't a rangeblock
116                                 # or if it is, the range is what we're about to block
117                                 ( $currentBlock->mAddress == $this->BlockAddress ) )
118                         ) {
119                                 $alreadyBlocked = true;
120                                 # Set the block form settings to the existing block
121                                 if( !$this->wasPosted ) {
122                                         $this->BlockAnonOnly = $currentBlock->mAnonOnly;
123                                         $this->BlockCreateAccount = $currentBlock->mCreateAccount;
124                                         $this->BlockEnableAutoblock = $currentBlock->mEnableAutoblock;
125                                         $this->BlockEmail = $currentBlock->mBlockEmail;
126                                         $this->BlockHideName = $currentBlock->mHideName;
127                                         $this->BlockAllowUsertalk = $currentBlock->mAllowUsertalk;
128                                         if( $currentBlock->mExpiry == 'infinity' ) {
129                                                 $this->BlockOther = 'indefinite';
130                                         } else {
131                                                 $this->BlockOther = wfTimestamp( TS_ISO_8601, $currentBlock->mExpiry );
132                                         }
133                                         $this->BlockReason = $currentBlock->mReason;
134                                 }
135                         }
136                 }
137
138                 # Show other blocks from extensions, i.e. GlockBlocking and TorBlock
139                 if( count( $otherBlockedMsgs ) ) {
140                         $wgOut->addHTML(
141                                 Html::rawElement( 'h2', array(), wfMsgExt( 'ipb-otherblocks-header', 'parseinline',  count( $otherBlockedMsgs ) ) ) . "\n"
142                         );
143                         $list = '';
144                         foreach( $otherBlockedMsgs as $link ) {
145                                 $list .= Html::rawElement( 'li', array(), $link ) . "\n";
146                         }
147                         $wgOut->addHTML( Html::rawElement( 'ul', array( 'class' => 'mw-blockip-alreadyblocked' ), $list ) . "\n" );
148                 }
149
150                 # Username/IP is blocked already locally
151                 if( $alreadyBlocked ) {
152                         $wgOut->addWikiMsg( 'ipb-needreblock', $this->BlockAddress );
153                 }
154
155                 $scBlockExpiryOptions = wfMsgForContent( 'ipboptions' );
156
157                 $showblockoptions = $scBlockExpiryOptions != '-';
158                 if( !$showblockoptions ) $mIpbother = $mIpbexpiry;
159
160                 $blockExpiryFormOptions = Xml::option( wfMsg( 'ipbotheroption' ), 'other' );
161                 foreach( explode( ',', $scBlockExpiryOptions ) as $option ) {
162                         if( strpos( $option, ':' ) === false ) $option = "$option:$option";
163                         list( $show, $value ) = explode( ':', $option );
164                         $show = htmlspecialchars( $show );
165                         $value = htmlspecialchars( $value );
166                         $blockExpiryFormOptions .= Xml::option( $show, $value, $this->BlockExpiry === $value ? true : false ) . "\n";
167                 }
168
169                 $reasonDropDown = Xml::listDropDown( 'wpBlockReasonList',
170                         wfMsgForContent( 'ipbreason-dropdown' ),
171                         wfMsgForContent( 'ipbreasonotherlist' ), $this->BlockReasonList, 'wpBlockDropDown', 4 );
172
173                 global $wgStylePath, $wgStyleVersion;
174                 $wgOut->addHTML(
175                         Xml::tags( 'script', array( 'type' => 'text/javascript', 'src' => "$wgStylePath/common/block.js?$wgStyleVersion" ), '' ) .
176                         Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( 'action=submit' ), 'id' => 'blockip' ) ) .
177                         Xml::openElement( 'fieldset' ) .
178                         Xml::element( 'legend', null, wfMsg( 'blockip-legend' ) ) .
179                         Xml::openElement( 'table', array( 'border' => '0', 'id' => 'mw-blockip-table' ) ) .
180                         "<tr>
181                                 <td class='mw-label'>
182                                         {$mIpaddress}
183                                 </td>
184                                 <td class='mw-input'>" .
185                                         Html::input( 'wpBlockAddress', $this->BlockAddress, 'text', array(
186                                                 'tabindex' => '1',
187                                                 'id' => 'mw-bi-target',
188                                                 'onchange' => 'updateBlockOptions()',
189                                                 'size' => '45',
190                                                 'required' => ''
191                                         ) + ( $this->BlockAddress ? array() : array( 'autofocus' ) ) ). "
192                                 </td>
193                         </tr>
194                         <tr>"
195                 );
196                 if( $showblockoptions ) {
197                         $wgOut->addHTML("
198                                 <td class='mw-label'>
199                                         {$mIpbexpiry}
200                                 </td>
201                                 <td class='mw-input'>" .
202                                         Xml::tags( 'select',
203                                                 array(
204                                                         'id' => 'wpBlockExpiry',
205                                                         'name' => 'wpBlockExpiry',
206                                                         'onchange' => 'considerChangingExpiryFocus()',
207                                                         'tabindex' => '2' ),
208                                                 $blockExpiryFormOptions ) .
209                                 "</td>"
210                         );
211                 }
212                 $wgOut->addHTML("
213                         </tr>
214                         <tr id='wpBlockOther'>
215                                 <td class='mw-label'>
216                                         {$mIpbother}
217                                 </td>
218                                 <td class='mw-input'>" .
219                                         Xml::input( 'wpBlockOther', 45, $this->BlockOther,
220                                                 array( 'tabindex' => '3', 'id' => 'mw-bi-other' ) ) . "
221                                 </td>
222                         </tr>
223                         <tr>
224                                 <td class='mw-label'>
225                                         {$mIpbreasonother}
226                                 </td>
227                                 <td class='mw-input'>
228                                         {$reasonDropDown}
229                                 </td>
230                         </tr>
231                         <tr id=\"wpBlockReason\">
232                                 <td class='mw-label'>
233                                         {$mIpbreason}
234                                 </td>
235                                 <td class='mw-input'>" .
236                                 Html::input( 'wpBlockReason', $this->BlockReason, 'text', array(
237                                         'tabindex' => '5',
238                                         'id' => 'mw-bi-reason',
239                                         'maxlength' => '200',
240                                         'size' => '45'
241                                 ) + ( $this->BlockAddress ? array( 'autofocus' ) : array() ) ) . "
242                                 </td>
243                         </tr>
244                         <tr id='wpAnonOnlyRow'>
245                                 <td>&nbsp;</td>
246                                 <td class='mw-input'>" .
247                                 Xml::checkLabel( wfMsg( 'ipbanononly' ),
248                                                 'wpAnonOnly', 'wpAnonOnly', $this->BlockAnonOnly,
249                                                 array( 'tabindex' => '6' ) ) . "
250                                 </td>
251                         </tr>
252                         <tr id='wpCreateAccountRow'>
253                                 <td>&nbsp;</td>
254                                 <td class='mw-input'>" .
255                                         Xml::checkLabel( wfMsg( 'ipbcreateaccount' ),
256                                                 'wpCreateAccount', 'wpCreateAccount', $this->BlockCreateAccount,
257                                                 array( 'tabindex' => '7' ) ) . "
258                                 </td>
259                         </tr>
260                         <tr id='wpEnableAutoblockRow'>
261                                 <td>&nbsp;</td>
262                                 <td class='mw-input'>" .
263                                         Xml::checkLabel( wfMsg( 'ipbenableautoblock' ),
264                                                 'wpEnableAutoblock', 'wpEnableAutoblock', $this->BlockEnableAutoblock,
265                                                 array( 'tabindex' => '8' ) ) . "
266                                 </td>
267                         </tr>"
268                 );
269
270                 if( self::canBlockEmail( $wgUser ) ) {
271                         $wgOut->addHTML("
272                                 <tr id='wpEnableEmailBan'>
273                                         <td>&nbsp;</td>
274                                         <td class='mw-input'>" .
275                                                 Xml::checkLabel( wfMsg( 'ipbemailban' ),
276                                                         'wpEmailBan', 'wpEmailBan', $this->BlockEmail,
277                                                         array( 'tabindex' => '9' ) ) . "
278                                         </td>
279                                 </tr>"
280                         );
281                 }
282
283                 // Allow some users to hide name from block log, blocklist and listusers
284                 if( $wgUser->isAllowed( 'hideuser' ) ) {
285                         $wgOut->addHTML("
286                                 <tr id='wpEnableHideUser'>
287                                         <td>&nbsp;</td>
288                                         <td class='mw-input'><strong>" .
289                                                 Xml::checkLabel( wfMsg( 'ipbhidename' ),
290                                                         'wpHideName', 'wpHideName', $this->BlockHideName,
291                                                         array( 'tabindex' => '10' )
292                                                 ) . "
293                                         </strong></td>
294                                 </tr>"
295                         );
296                 }
297
298                 # Watchlist their user page? (Only if user is logged in)
299                 if( $wgUser->isLoggedIn() ) {
300                         $wgOut->addHTML("
301                         <tr id='wpEnableWatchUser'>
302                                 <td>&nbsp;</td>
303                                 <td class='mw-input'>" .
304                                         Xml::checkLabel( wfMsg( 'ipbwatchuser' ),
305                                                 'wpWatchUser', 'wpWatchUser', $this->BlockWatchUser,
306                                                 array( 'tabindex' => '11' ) ) . "
307                                 </td>
308                         </tr>"
309                         );
310                 }
311
312                 # Can we explicitly disallow the use of user_talk?
313                 global $wgBlockAllowsUTEdit;
314                 if( $wgBlockAllowsUTEdit ){
315                         $wgOut->addHTML("
316                                 <tr id='wpAllowUsertalkRow'>
317                                         <td>&nbsp;</td>
318                                         <td class='mw-input'>" .
319                                                 Xml::checkLabel( wfMsg( 'ipballowusertalk' ),
320                                                         'wpAllowUsertalk', 'wpAllowUsertalk', $this->BlockAllowUsertalk,
321                                                         array( 'tabindex' => '12' ) ) . "
322                                         </td>
323                                 </tr>"
324                         );
325                 }
326
327                 $wgOut->addHTML("
328                         <tr>
329                                 <td style='padding-top: 1em'>&nbsp;</td>
330                                 <td  class='mw-submit' style='padding-top: 1em'>" .
331                                         Xml::submitButton( wfMsg( $alreadyBlocked ? 'ipb-change-block' : 'ipbsubmit' ),
332                                                 array( 'name' => 'wpBlock', 'tabindex' => '13', 'accesskey' => 's' ) ) . "
333                                 </td>
334                         </tr>" .
335                         Xml::closeElement( 'table' ) .
336                         Xml::hidden( 'wpEditToken', $wgUser->editToken() ) .
337                         ( $alreadyBlocked ? Xml::hidden( 'wpChangeBlock', 1 ) : "" ) .
338                         Xml::closeElement( 'fieldset' ) .
339                         Xml::closeElement( 'form' ) .
340                         Xml::tags( 'script', array( 'type' => 'text/javascript' ), 'updateBlockOptions()' ) . "\n"
341                 );
342
343                 $wgOut->addHTML( $this->getConvenienceLinks() );
344
345                 if( is_object( $user ) ) {
346                         $this->showLogFragment( $wgOut, $user->getUserPage() );
347                 } elseif( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $this->BlockAddress ) ) {
348                         $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
349                 } 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 ) ) {
350                         $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) );
351                 }
352         }
353
354         /**
355          * Can we do an email block?
356          * @param User $user The sysop wanting to make a block
357          * @return boolean
358          */
359         public static function canBlockEmail( $user ) {
360                 global $wgEnableUserEmail, $wgSysopEmailBans;
361                 return ( $wgEnableUserEmail && $wgSysopEmailBans && $user->isAllowed( 'blockemail' ) );
362         }
363
364         /**
365          * Backend block code.
366          * $userID and $expiry will be filled accordingly
367          * @return array(message key, arguments) on failure, empty array on success
368          */
369         function doBlock( &$userId = null, &$expiry = null ) {
370                 global $wgUser, $wgSysopUserBans, $wgSysopRangeBans, $wgBlockAllowsUTEdit, $wgBlockCIDRLimit;
371
372                 $userId = 0;
373                 # Expand valid IPv6 addresses, usernames are left as is
374                 $this->BlockAddress = IP::sanitizeIP( $this->BlockAddress );
375                 # isIPv4() and IPv6() are used for final validation
376                 $rxIP4 = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';
377                 $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}';
378                 $rxIP = "($rxIP4|$rxIP6)";
379
380                 # Check for invalid specifications
381                 if( !preg_match( "/^$rxIP$/", $this->BlockAddress ) ) {
382                         $matches = array();
383                         if( preg_match( "/^($rxIP4)\\/(\\d{1,2})$/", $this->BlockAddress, $matches ) ) {
384                                 # IPv4
385                                 if( $wgSysopRangeBans ) {
386                                         if( !IP::isIPv4( $this->BlockAddress ) || $matches[2] > 32 ) {
387                                                 return array( 'ip_range_invalid' );
388                                         } elseif ( $matches[2] < $wgBlockCIDRLimit['IPv4'] ) {
389                                                 return array( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv4'] );
390                                         }
391                                         $this->BlockAddress = Block::normaliseRange( $this->BlockAddress );
392                                 } else {
393                                         # Range block illegal
394                                         return array( 'range_block_disabled' );
395                                 }
396                         } elseif( preg_match( "/^($rxIP6)\\/(\\d{1,3})$/", $this->BlockAddress, $matches ) ) {
397                                 # IPv6
398                                 if( $wgSysopRangeBans ) {
399                                         if( !IP::isIPv6( $this->BlockAddress ) || $matches[2] > 128 ) {
400                                                 return array( 'ip_range_invalid' );
401                                         } elseif( $matches[2] < $wgBlockCIDRLimit['IPv6'] ) {
402                                                 return array( 'ip_range_toolarge', $wgBlockCIDRLimit['IPv6'] );
403                                         }
404                                         $this->BlockAddress = Block::normaliseRange( $this->BlockAddress );
405                                 } else {
406                                         # Range block illegal
407                                         return array('range_block_disabled');
408                                 }
409                         } else {
410                                 # Username block
411                                 if( $wgSysopUserBans ) {
412                                         $user = User::newFromName( $this->BlockAddress );
413                                         if( !is_null( $user ) && $user->getId() ) {
414                                                 # Use canonical name
415                                                 $userId = $user->getId();
416                                                 $this->BlockAddress = $user->getName();
417                                         } else {
418                                                 return array( 'nosuchusershort', htmlspecialchars( $user ? $user->getName() : $this->BlockAddress ) );
419                                         }
420                                 } else {
421                                         return array( 'badipaddress' );
422                                 }
423                         }
424                 }
425
426                 if( $wgUser->isBlocked() && ( $wgUser->getId() !== $userId ) ) {
427                         return array( 'cant-block-while-blocked' );
428                 }
429
430                 $reasonstr = $this->BlockReasonList;
431                 if( $reasonstr != 'other' && $this->BlockReason != '' ) {
432                         // Entry from drop down menu + additional comment
433                         $reasonstr .= wfMsgForContent( 'colon-separator' ) . $this->BlockReason;
434                 } elseif( $reasonstr == 'other' ) {
435                         $reasonstr = $this->BlockReason;
436                 }
437
438                 $expirestr = $this->BlockExpiry;
439                 if( $expirestr == 'other' )
440                         $expirestr = $this->BlockOther;
441
442                 if( ( strlen( $expirestr ) == 0) || ( strlen( $expirestr ) > 50 ) ) {
443                         return array( 'ipb_expiry_invalid' );
444                 }
445                 
446                 if( false === ( $expiry = Block::parseExpiryInput( $expirestr ) ) ) {
447                         // Bad expiry.
448                         return array( 'ipb_expiry_invalid' );
449                 }
450
451                 if( $this->BlockHideName ) {
452                         // Recheck params here...
453                         if( !$userId || !$wgUser->isAllowed('hideuser') ) {
454                                 $this->BlockHideName = false; // IP users should not be hidden
455                         } elseif( $expiry !== 'infinity' ) {
456                                 // Bad expiry.
457                                 return array( 'ipb_expiry_temp' );
458                         } elseif( User::edits( $userId ) > self::HIDEUSER_CONTRIBLIMIT ) {
459                                 // Typically, the user should have a handful of edits.
460                                 // Disallow hiding users with many edits for performance.
461                                 return array( 'ipb_hide_invalid' );
462                         }
463                 }
464
465                 # Create block object
466                 # Note: for a user block, ipb_address is only for display purposes
467                 $block = new Block( $this->BlockAddress, $userId, $wgUser->getId(),
468                         $reasonstr, wfTimestampNow(), 0, $expiry, $this->BlockAnonOnly,
469                         $this->BlockCreateAccount, $this->BlockEnableAutoblock, $this->BlockHideName,
470                         $this->BlockEmail,
471                         isset( $this->BlockAllowUsertalk ) ? $this->BlockAllowUsertalk : $wgBlockAllowsUTEdit
472                 );
473
474                 # Should this be privately logged?
475                 $suppressLog = (bool)$this->BlockHideName;
476                 if( wfRunHooks( 'BlockIp', array( &$block, &$wgUser ) ) ) {
477                         # Try to insert block. Is there a conflicting block?
478                         if( !$block->insert() ) {
479                                 # Show form unless the user is already aware of this...
480                                 if( !$this->BlockReblock ) {
481                                         return array( 'ipb_already_blocked' );
482                                 # Otherwise, try to update the block...
483                                 } else {
484                                         # This returns direct blocks before autoblocks/rangeblocks, since we should
485                                         # be sure the user is blocked by now it should work for our purposes
486                                         $currentBlock = Block::newFromDB( $this->BlockAddress, $userId );
487                                         if( $block->equals( $currentBlock ) ) {
488                                                 return array( 'ipb_already_blocked' );
489                                         }
490                                         # If the name was hidden and the blocking user cannot hide
491                                         # names, then don't allow any block changes...
492                                         if( $currentBlock->mHideName && !$wgUser->isAllowed( 'hideuser' ) ) {
493                                                 return array( 'cant-see-hidden-user' );
494                                         }
495                                         $currentBlock->delete();
496                                         $block->insert();
497                                         # If hiding/unhiding a name, this should go in the private logs
498                                         $suppressLog = $suppressLog || (bool)$currentBlock->mHideName;
499                                         $log_action = 'reblock';
500                                         # Unset _deleted fields if requested
501                                         if( $currentBlock->mHideName && !$this->BlockHideName ) {
502                                                 self::unsuppressUserName( $this->BlockAddress, $userId );
503                                         }
504                                 }
505                         } else {
506                                 $log_action = 'block';
507                         }
508                         wfRunHooks( 'BlockIpComplete', array( $block, $wgUser ) );
509
510                         # Set *_deleted fields if requested
511                         if( $this->BlockHideName ) {
512                                 self::suppressUserName( $this->BlockAddress, $userId );
513                         }
514
515                         # Only show watch link when this is no range block
516                         if( $this->BlockWatchUser && $block->mRangeStart == $block->mRangeEnd ) {
517                                 $wgUser->addWatch( Title::makeTitle( NS_USER, $this->BlockAddress ) );
518                         }
519
520                         # Block constructor sanitizes certain block options on insert
521                         $this->BlockEmail = $block->mBlockEmail;
522                         $this->BlockEnableAutoblock = $block->mEnableAutoblock;
523
524                         # Prepare log parameters
525                         $logParams = array();
526                         $logParams[] = $expirestr;
527                         $logParams[] = $this->blockLogFlags();
528
529                         # Make log entry, if the name is hidden, put it in the oversight log
530                         $log_type = $suppressLog ? 'suppress' : 'block';
531                         $log = new LogPage( $log_type );
532                         $log->addEntry( $log_action, Title::makeTitle( NS_USER, $this->BlockAddress ),
533                                 $reasonstr, $logParams );
534
535                         # Report to the user
536                         return array();
537                 } else {
538                         return array( 'hookaborted' );
539                 }
540         }
541
542         public static function suppressUserName( $name, $userId, $dbw = null ) {
543                 $op = '|'; // bitwise OR
544                 return self::setUsernameBitfields( $name, $userId, $op, $dbw );
545         }
546
547         public static function unsuppressUserName( $name, $userId, $dbw = null ) {
548                 $op = '&'; // bitwise AND
549                 return self::setUsernameBitfields( $name, $userId, $op, $dbw );
550         }
551
552         private static function setUsernameBitfields( $name, $userId, $op, $dbw ) {
553                 if( $op !== '|' && $op !== '&' ) return false; // sanity check
554                 if( !$dbw )
555                         $dbw = wfGetDB( DB_MASTER );
556                 $delUser = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
557                 $delAction = LogPage::DELETED_ACTION | Revision::DELETED_RESTRICTED;
558                 # Normalize user name
559                 $userTitle = Title::makeTitleSafe( NS_USER, $name );
560                 $userDbKey = $userTitle->getDBkey();
561                 # To suppress, we OR the current bitfields with Revision::DELETED_USER
562                 # to put a 1 in the username *_deleted bit. To unsuppress we AND the
563                 # current bitfields with the inverse of Revision::DELETED_USER. The
564                 # username bit is made to 0 (x & 0 = 0), while others are unchanged (x & 1 = x).
565                 # The same goes for the sysop-restricted *_deleted bit.
566                 if( $op == '&' ) {
567                         $delUser = "~{$delUser}";
568                         $delAction = "~{$delAction}";
569                 }
570                 # Hide name from live edits
571                 $dbw->update( 'revision', array( "rev_deleted = rev_deleted $op $delUser" ),
572                         array( 'rev_user' => $userId ), __METHOD__ );
573                 # Hide name from deleted edits
574                 $dbw->update( 'archive', array( "ar_deleted = ar_deleted $op $delUser" ),
575                         array( 'ar_user_text' => $name ), __METHOD__ );
576                 # Hide name from logs
577                 $dbw->update( 'logging', array( "log_deleted = log_deleted $op $delUser" ),
578                         array( 'log_user' => $userId, "log_type != 'suppress'" ), __METHOD__ );
579                 $dbw->update( 'logging', array( "log_deleted = log_deleted $op $delAction" ),
580                         array( 'log_namespace' => NS_USER, 'log_title' => $userDbKey,
581                                 "log_type != 'suppress'" ), __METHOD__ );
582                 # Hide name from RC
583                 $dbw->update( 'recentchanges', array( "rc_deleted = rc_deleted $op $delUser" ),
584                         array( 'rc_user_text' => $name ), __METHOD__ );
585                 $dbw->update( 'recentchanges', array( "rc_deleted = rc_deleted $op $delAction" ),
586                         array( 'rc_namespace' => NS_USER, 'rc_title' => $userDbKey, 'rc_logid > 0' ), __METHOD__ );
587                 # Hide name from live images
588                 $dbw->update( 'oldimage', array( "oi_deleted = oi_deleted $op $delUser" ),
589                         array( 'oi_user_text' => $name ), __METHOD__ );
590                 # Hide name from deleted images
591                 # WMF - schema change pending
592                 # $dbw->update( 'filearchive', array( "fa_deleted = fa_deleted $op $delUser" ),
593                 #       array( 'fa_user_text' => $name ), __METHOD__ );
594                 # Done!
595                 return true;
596         }
597
598         /**
599          * UI entry point for blocking
600          * Wraps around doBlock()
601          */
602         public function doSubmit() {
603                 global $wgOut;
604                 $retval = $this->doBlock();
605                 if( empty( $retval ) ) {
606                         $titleObj = SpecialPage::getTitleFor( 'Blockip' );
607                         $wgOut->redirect( $titleObj->getFullURL( 'action=success&ip=' .
608                                 urlencode( $this->BlockAddress ) ) );
609                         return;
610                 }
611                 $this->showForm( $retval );
612         }
613
614         public function showSuccess() {
615                 global $wgOut;
616
617                 $wgOut->setPageTitle( wfMsg( 'blockip-title' ) );
618                 $wgOut->setSubtitle( wfMsg( 'blockipsuccesssub' ) );
619                 $text = wfMsgExt( 'blockipsuccesstext', array( 'parse' ), $this->BlockAddress );
620                 $wgOut->addHTML( $text );
621         }
622
623         private function showLogFragment( $out, $title ) {
624                 global $wgUser;
625
626                 // Used to support GENDER in 'blocklog-showlog' and 'blocklog-showsuppresslog'
627                 $userBlocked = $title->getText();
628
629                 LogEventsList::showLogExtract(
630                         $out,
631                         'block',
632                         $title->getPrefixedText(),
633                         '',
634                         array(
635                                 'lim' => 10,
636                                 'msgKey' => array(
637                                         'blocklog-showlog',
638                                         $userBlocked
639                                 ),
640                                 'showIfEmpty' => false
641                         )
642                 );
643
644                 // Add suppression block entries if allowed
645                 if( $wgUser->isAllowed( 'hideuser' ) ) {
646                         LogEventsList::showLogExtract( $out, 'suppress', $title->getPrefixedText(), '',
647                                 array(
648                                         'lim' => 10,
649                                         'conds' => array(
650                                                 'log_action' => array(
651                                                         'block',
652                                                         'reblock',
653                                                         'unblock'
654                                                 )
655                                         ),
656                                         'msgKey' => array(
657                                                 'blocklog-showsuppresslog',
658                                                 $userBlocked
659                                         ),
660                                         'showIfEmpty' => false
661                                 )
662                         );
663                 }
664         }
665
666         /**
667          * Return a comma-delimited list of "flags" to be passed to the log
668          * reader for this block, to provide more information in the logs
669          *
670          * @return array
671          */
672         private function blockLogFlags() {
673                 global $wgBlockAllowsUTEdit;
674                 $flags = array();
675                 if( $this->BlockAnonOnly && IP::isIPAddress( $this->BlockAddress ) )
676                         // when blocking a user the option 'anononly' is not available/has no effect -> do not write this into log
677                         $flags[] = 'anononly';
678                 if( $this->BlockCreateAccount )
679                         $flags[] = 'nocreate';
680                 if( !$this->BlockEnableAutoblock && !IP::isIPAddress( $this->BlockAddress ) )
681                         // Same as anononly, this is not displayed when blocking an IP address
682                         $flags[] = 'noautoblock';
683                 if( $this->BlockEmail )
684                         $flags[] = 'noemail';
685                 if( !$this->BlockAllowUsertalk && $wgBlockAllowsUTEdit )
686                         $flags[] = 'nousertalk';
687                 if( $this->BlockHideName )
688                         $flags[] = 'hiddenname';
689                 return implode( ',', $flags );
690         }
691
692         /**
693          * Builds unblock and block list links
694          *
695          * @return string
696          */
697         private function getConvenienceLinks() {
698                 global $wgUser, $wgLang;
699                 $skin = $wgUser->getSkin();
700                 if( $this->BlockAddress )
701                         $links[] = $this->getContribsLink( $skin );
702                 $links[] = $this->getUnblockLink( $skin );
703                 $links[] = $this->getBlockListLink( $skin );
704                 if ( $wgUser->isAllowed( 'editinterface' ) ) {
705                         $title = Title::makeTitle( NS_MEDIAWIKI, 'Ipbreason-dropdown' );
706                         $links[] = $skin->link(
707                                 $title,
708                                 wfMsgHtml( 'ipb-edit-dropdown' ),
709                                 array(),
710                                 array( 'action' => 'edit' )
711                         );
712                 }
713                 return '<p class="mw-ipb-conveniencelinks">' . $wgLang->pipeList( $links ) . '</p>';
714         }
715
716         /**
717          * Build a convenient link to a user or IP's contribs
718          * form
719          *
720          * @param $skin Skin to use
721          * @return string
722          */
723         private function getContribsLink( $skin ) {
724                 $contribsPage = SpecialPage::getTitleFor( 'Contributions', $this->BlockAddress );
725                 return $skin->link( $contribsPage, wfMsgExt( 'ipb-blocklist-contribs', 'escape', $this->BlockAddress ) );
726         }
727
728         /**
729          * Build a convenient link to unblock the given username or IP
730          * address, if available; otherwise link to a blank unblock
731          * form
732          *
733          * @param $skin Skin to use
734          * @return string
735          */
736         private function getUnblockLink( $skin ) {
737                 $list = SpecialPage::getTitleFor( 'Ipblocklist' );
738                 $query = array( 'action' => 'unblock' );
739
740                 if( $this->BlockAddress ) {
741                         $addr = strtr( $this->BlockAddress, '_', ' ' );
742                         $message = wfMsg( 'ipb-unblock-addr', $addr );
743                         $query['ip'] = $this->BlockAddress;
744                 } else {
745                         $message = wfMsg( 'ipb-unblock' );
746                 }
747                 return $skin->linkKnown(
748                         $list,
749                         htmlspecialchars( $message ),
750                         array(),
751                         $query
752                 );
753         }
754
755         /**
756          * Build a convenience link to the block list
757          *
758          * @param $skin Skin to use
759          * @return string
760          */
761         private function getBlockListLink( $skin ) {
762                 $list = SpecialPage::getTitleFor( 'Ipblocklist' );
763                 $query = array();
764
765                 if( $this->BlockAddress ) {
766                         $addr = strtr( $this->BlockAddress, '_', ' ' );
767                         $message = wfMsg( 'ipb-blocklist-addr', $addr );
768                         $query['ip'] = $this->BlockAddress;
769                 } else {
770                         $message = wfMsg( 'ipb-blocklist' );
771                 }
772
773                 return $skin->linkKnown(
774                         $list,
775                         htmlspecialchars( $message ),
776                         array(),
777                         $query
778                 );
779         }
780
781         /**
782          * Block a list of selected users
783          * @param array $users
784          * @param string $reason
785          * @param string $tag replaces user pages
786          * @param string $talkTag replaces user talk pages
787          * @returns array, list of html-safe usernames
788          */
789         public static function doMassUserBlock( $users, $reason = '', $tag = '', $talkTag = '' ) {
790                 global $wgUser;
791                 $counter = $blockSize = 0;
792                 $safeUsers = array();
793                 $log = new LogPage( 'block' );
794                 foreach( $users as $name ) {
795                         # Enforce limits
796                         $counter++;
797                         $blockSize++;
798                         # Lets not go *too* fast
799                         if( $blockSize >= 20 ) {
800                                 $blockSize = 0;
801                                 wfWaitForSlaves( 5 );
802                         }
803                         $u = User::newFromName( $name, false );
804                         // If user doesn't exist, it ought to be an IP then
805                         if( is_null( $u ) || ( !$u->getId() && !IP::isIPAddress( $u->getName() ) ) ) {
806                                 continue;
807                         }
808                         $userTitle = $u->getUserPage();
809                         $userTalkTitle = $u->getTalkPage();
810                         $userpage = new Article( $userTitle );
811                         $usertalk = new Article( $userTalkTitle );
812                         $safeUsers[] = '[[' . $userTitle->getPrefixedText() . '|' . $userTitle->getText() . ']]';
813                         $expirestr = $u->getId() ? 'indefinite' : '1 week';
814                         $expiry = Block::parseExpiryInput( $expirestr );
815                         $anonOnly = IP::isIPAddress( $u->getName() ) ? 1 : 0;
816                         // Create the block
817                         $block = new Block( $u->getName(), // victim
818                                 $u->getId(), // uid
819                                 $wgUser->getId(), // blocker
820                                 $reason, // comment
821                                 wfTimestampNow(), // block time
822                                 0, // auto ?
823                                 $expiry, // duration
824                                 $anonOnly, // anononly?
825                                 1, // block account creation?
826                                 1, // autoblocking?
827                                 0, // suppress name?
828                                 0 // block from sending email?
829                         );
830                         $oldblock = Block::newFromDB( $u->getName(), $u->getId() );
831                         if( !$oldblock ) {
832                                 $block->insert();
833                                 # Prepare log parameters
834                                 $logParams = array();
835                                 $logParams[] = $expirestr;
836                                 if( $anonOnly ) {
837                                         $logParams[] = 'anononly';
838                                 }
839                                 $logParams[] = 'nocreate';
840                                 # Add log entry
841                                 $log->addEntry( 'block', $userTitle, $reason, $logParams );
842                         }
843                         # Tag userpage! (check length to avoid mistakes)
844                         if( strlen( $tag ) > 2 ) {
845                                 $userpage->doEdit( $tag, $reason, EDIT_MINOR );
846                         }
847                         if( strlen( $talkTag ) > 2 ) {
848                                 $usertalk->doEdit( $talkTag, $reason, EDIT_MINOR );
849                         }
850                 }
851                 return $safeUsers;
852         }
853 }