Index: trunk/extensions/CheckUser/CheckUser_body.php |
— | — | @@ -46,6 +46,8 @@ |
47 | 47 | $reason = $wgRequest->getText( 'reason' ); |
48 | 48 | $checktype = $wgRequest->getVal( 'checktype' ); |
49 | 49 | $period = $wgRequest->getInt( 'period' ); |
| 50 | + $users = $wgRequest->getArray( 'users' ); |
| 51 | + $tag = $wgRequest->getBool('usetag') ? trim( $wgRequest->getVal( 'tag' ) ) : ""; |
50 | 52 | |
51 | 53 | # An IPv4? |
52 | 54 | if( preg_match( '#^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(/\d{1,2}|)$#', $user ) ) { |
— | — | @@ -77,20 +79,21 @@ |
78 | 80 | } |
79 | 81 | |
80 | 82 | $this->doForm( $user, $reason, $checktype, $ip, $xff, $name, $period ); |
81 | | - |
82 | | - if( !$wgRequest->wasPosted() ) |
83 | | - return; |
84 | | - |
85 | | - if( $checktype=='subuserips' ) { |
86 | | - $this->doUserIPsRequest( $name, $reason, $period ); |
87 | | - } else if( $xff && $checktype=='subipedits' ) { |
88 | | - $this->doIPEditsRequest( $xff, true, $reason, $period ); |
89 | | - } else if( $checktype=='subipedits' ) { |
90 | | - $this->doIPEditsRequest( $ip, false, $reason, $period ); |
91 | | - } else if( $xff && $checktype=='subipusers' ) { |
92 | | - $this->doIPUsersRequest( $xff, true, $reason, $period ); |
93 | | - } else if( $checktype=='subipusers' ) { |
94 | | - $this->doIPUsersRequest( $ip, false, $reason, $period ); |
| 83 | + # Perform one of the various submit operations... |
| 84 | + if( $wgRequest->wasPosted() ) { |
| 85 | + if( $wgRequest->getVal('action') === 'block' ) { |
| 86 | + $this->doMassUserBlock( $users, $reason, $tag ); |
| 87 | + } else if( $checktype=='subuserips' ) { |
| 88 | + $this->doUserIPsRequest( $name, $reason, $period ); |
| 89 | + } else if( $xff && $checktype=='subipedits' ) { |
| 90 | + $this->doIPEditsRequest( $xff, true, $reason, $period ); |
| 91 | + } else if( $checktype=='subipedits' ) { |
| 92 | + $this->doIPEditsRequest( $ip, false, $reason, $period ); |
| 93 | + } else if( $xff && $checktype=='subipusers' ) { |
| 94 | + $this->doIPUsersRequest( $xff, true, $reason, $period, $tag ); |
| 95 | + } else if( $checktype=='subipusers' ) { |
| 96 | + $this->doIPUsersRequest( $ip, false, $reason, $period, $tag ); |
| 97 | + } |
95 | 98 | } |
96 | 99 | } |
97 | 100 | |
— | — | @@ -103,7 +106,6 @@ |
104 | 107 | |
105 | 108 | protected function doForm( $user, $reason, $checktype, $ip, $xff, $name, $period ) { |
106 | 109 | global $wgOut, $wgTitle; |
107 | | - |
108 | 110 | $action = $wgTitle->escapeLocalUrl(); |
109 | 111 | # Fill in requested type if it makes sense |
110 | 112 | $encipusers=0; $encipedits=0; $encuserips=0; |
— | — | @@ -120,8 +122,7 @@ |
121 | 123 | $encuserips = 1; |
122 | 124 | # Compile our nice form |
123 | 125 | # User box length should fit things like "2001:0db8:85a3:08d3:1319:8a2e:0370:7344/100/xff" |
124 | | - $wgOut->addWikiText( |
125 | | - wfMsg( 'checkuser-summary' ) . |
| 126 | + $wgOut->addWikiText( wfMsg( 'checkuser-summary' ) . |
126 | 127 | "\n\n[[" . $this->getLogSubpageTitle()->getPrefixedText() . '|' . wfMsg( 'checkuser-showlog' ) . ']]' |
127 | 128 | ); |
128 | 129 | $form = "<form name='checkuserform' id='checkuserform' action='$action' method='post'>"; |
— | — | @@ -140,7 +141,7 @@ |
141 | 142 | $form .= " ".Xml::label( wfMsgHtml("checkuser-users"), 'subipusers' )."</td>"; |
142 | 143 | $form .= "</tr></table></td>"; |
143 | 144 | $form .= "</tr><tr>"; |
144 | | - $form .= "<td>".wfMsgHtml( "checkuser-reason" ).":</td>"; |
| 145 | + $form .= "<td>".wfMsgHtml( "checkuser-reason" )."</td>"; |
145 | 146 | $form .= "<td>".Xml::input( 'reason', 46, $reason, array( 'maxlength' => '150', 'id' => 'checkreason' ) ); |
146 | 147 | $form .= " ".Xml::submitButton( wfMsgHtml('checkuser-check'), |
147 | 148 | array('id' => 'checkusersubmit','name' => 'checkusersubmit') )."</td>"; |
— | — | @@ -163,6 +164,86 @@ |
164 | 165 | $s .= Xml::closeElement('select')."\n"; |
165 | 166 | return $s; |
166 | 167 | } |
| 168 | + |
| 169 | + /** |
| 170 | + * Block a list of selected users |
| 171 | + * @param array $users |
| 172 | + * @param string $reason |
| 173 | + * @param string $tag |
| 174 | + */ |
| 175 | + protected function doMassUserBlock( $users, $reason = '', $tag = '' ) { |
| 176 | + global $wgOut, $wgUser, $wgCheckUserMaxBlocks; |
| 177 | + if( empty($users) || $wgUser->isBlocked(false) ) { |
| 178 | + $wgOut->addWikiText( wfMsgExt('checkuser-block-failure',array('parsemag')) ); |
| 179 | + return; |
| 180 | + } |
| 181 | + $counter = $blockSize = 0; |
| 182 | + $safeUsers = array(); |
| 183 | + $log = new LogPage( 'block' ); |
| 184 | + foreach( $users as $name ) { |
| 185 | + # Enforce limits |
| 186 | + $counter++; |
| 187 | + $blockSize++; |
| 188 | + if( $counter > $wgCheckUserMaxBlocks ) { |
| 189 | + break; |
| 190 | + } |
| 191 | + # Lets not go *too* fast |
| 192 | + if( $blockSize >= 20 ) { |
| 193 | + $blockSize = 0; |
| 194 | + wfWaitForSlaves( 5 ); |
| 195 | + } |
| 196 | + $u = User::newFromName( $name, false ); |
| 197 | + // If user doesn't exist, it ought to be an IP then |
| 198 | + if( is_null($u) || (!$u->getId() && !IP::isIPAddress( $u->getName() )) ) { |
| 199 | + continue; |
| 200 | + } |
| 201 | + $usertitle = Title::makeTitle( NS_USER, $u->getName() ); |
| 202 | + $userpage = new Article( $usertitle ); |
| 203 | + $safeUsers[] = '[[' . $usertitle->getPrefixedText() . '|' . $usertitle->getText() . ']]'; |
| 204 | + $expirestr = $u->getId() ? 'infinity' : '1 week'; |
| 205 | + $anonOnly = IP::isIPAddress( $u->getName() ) ? 1 : 0; |
| 206 | + // Create the block |
| 207 | + $block = new Block( $u->getName(), // victim |
| 208 | + $u->getId(), // uid |
| 209 | + $wgUser->getId(), // blocker |
| 210 | + $reason, // comment |
| 211 | + wfTimestampNow(), // block time |
| 212 | + 0, // auto ? |
| 213 | + strtotime( $expirestr ), // duration |
| 214 | + $anonOnly, // anononly? |
| 215 | + 1, // block account creation? |
| 216 | + 1, // autoblocking? |
| 217 | + 0, // suppress name? |
| 218 | + 0 // block from sending email? |
| 219 | + ); |
| 220 | + // Kill old blocks, but leave range blocks |
| 221 | + $oldblock = Block::newFromDB( $u->getName() ); |
| 222 | + if( $oldblock && $oldblock->mAddress == $u->getName() && $block->mRangeStart == $block->mRangeEnd ) { |
| 223 | + $oldblock->delete(); |
| 224 | + } |
| 225 | + $block->insert(); |
| 226 | + # Prepare log parameters |
| 227 | + $logParams = array(); |
| 228 | + $logParams[] = $expirestr; |
| 229 | + if( $anonOnly ) { |
| 230 | + $logParams[] = 'anononly'; |
| 231 | + } |
| 232 | + $logParams[] = 'nocreate'; |
| 233 | + # Add log entry |
| 234 | + $log->addEntry( 'block', $usertitle, $reason, $logParams ); |
| 235 | + # Tag userpage! (check length to avoid mistakes) |
| 236 | + if( strlen($tag) > 2 ) { |
| 237 | + $userpage->doEdit( $tag, $reason, EDIT_MINOR ); |
| 238 | + } |
| 239 | + } |
| 240 | + if( !empty($safeUsers) ) { |
| 241 | + $n = count($safeUsers); |
| 242 | + $ulist = implode(', ',$safeUsers); |
| 243 | + $wgOut->addWikiText( wfMsgExt('checkuser-block-success',array('parsemag'),$ulist,$n) ); |
| 244 | + } else { |
| 245 | + $wgOut->addWikiText( wfMsgExt('checkuser-block-failure',array('parsemag')) ); |
| 246 | + } |
| 247 | + } |
167 | 248 | |
168 | 249 | /** |
169 | 250 | * @param string $ip |
— | — | @@ -170,7 +251,7 @@ |
171 | 252 | * @param string $reason |
172 | 253 | * Shows all edits in Recent Changes by this IP (or range) and who made them |
173 | 254 | */ |
174 | | - protected function doIPEditsRequest( $ip, $xfor = false, $reason = '', $period ) { |
| 255 | + protected function doIPEditsRequest( $ip, $xfor = false, $reason = '', $period = 0 ) { |
175 | 256 | global $wgUser, $wgOut, $wgLang, $wgTitle, $wgDBname; |
176 | 257 | $fname = 'CheckUser::doIPEditsRequest'; |
177 | 258 | # Invalid IPs are passed in as a blank string |
— | — | @@ -408,27 +489,27 @@ |
409 | 490 | * Outputs usernames, latest and earliest found edit date, and count |
410 | 491 | * List unique IPs used for each user in time order, list corresponding user agent |
411 | 492 | */ |
412 | | - protected function doIPUsersRequest( $ip, $xfor = false, $reason = '', $period ) { |
| 493 | + protected function doIPUsersRequest( $ip, $xfor = false, $reason = '', $period = 0, $tag = '' ) { |
413 | 494 | global $wgUser, $wgOut, $wgLang, $wgTitle, $wgDBname; |
414 | 495 | $fname = 'CheckUser::doIPUsersRequest'; |
415 | 496 | |
416 | | - #invalid IPs are passed in as a blank string |
417 | | - if(!$ip) { |
| 497 | + # Invalid IPs are passed in as a blank string |
| 498 | + if( !$ip ) { |
418 | 499 | $s = wfMsgHtml('badipaddress'); |
419 | 500 | $wgOut->addHTML( $s ); |
420 | 501 | return; |
421 | 502 | } |
422 | 503 | |
423 | 504 | $logType = 'ipusers'; |
424 | | - if ( $xfor ) { |
| 505 | + if( $xfor ) { |
425 | 506 | $logType .= '-xff'; |
426 | 507 | } |
| 508 | + # Log the check... |
427 | 509 | if( !$this->addLogEntry( $logType, 'ip', $ip, $reason ) ) { |
428 | 510 | $wgOut->addHTML( '<p>'.wfMsgHtml('checkuser-log-fail').'</p>' ); |
429 | 511 | } |
430 | 512 | |
431 | 513 | $dbr = wfGetDB( DB_SLAVE ); |
432 | | - |
433 | 514 | $ip_conds = $dbr->makeList( $this->getIpConds( $dbr, $ip, $xfor ), LIST_AND ); |
434 | 515 | $time_conds = $this->getTimeConds( $period ); |
435 | 516 | $cu_changes = $dbr->tableName( 'cu_changes' ); |
— | — | @@ -441,7 +522,7 @@ |
442 | 523 | __METHOD__, |
443 | 524 | array( 'USE INDEX' => $index ) ); |
444 | 525 | } |
445 | | - |
| 526 | + // Are there too many edits? |
446 | 527 | if( isset($rangecount) && $rangecount > 5000 ) { |
447 | 528 | $use_index = $dbr->useIndexClause( $index ); |
448 | 529 | $sql = "SELECT cuc_ip_hex, COUNT(*) AS count, |
— | — | @@ -456,13 +537,7 @@ |
457 | 538 | # Convert the IP hexes into normal form |
458 | 539 | if( strpos($row->cuc_ip_hex,'v6-') !==false ) { |
459 | 540 | $ip = substr( $row->cuc_ip_hex, 3 ); |
460 | | - // Seperate into 8 octets |
461 | | - $ip_oct = substr( $ip, 0, 4 ); |
462 | | - for ($n=1; $n < 8; $n++) { |
463 | | - $ip_oct .= ':' . substr($ip, 4*$n, 4); |
464 | | - } |
465 | | - // NO leading zeroes |
466 | | - $ip = preg_replace( '/(^|:)0+' . RE_IPV6_WORD . '/', '$1$2', $ip_oct ); |
| 541 | + $ip = IP::HextoOctet( $ip ); |
467 | 542 | } else { |
468 | 543 | $ip = long2ip( wfBaseConvert($row->cuc_ip_hex, 16, 10, 8) ); |
469 | 544 | } |
— | — | @@ -528,12 +603,16 @@ |
529 | 604 | |
530 | 605 | $logs = SpecialPage::getTitleFor( 'Log' ); |
531 | 606 | $blocklist = SpecialPage::getTitleFor( 'Ipblocklist' ); |
532 | | - $s = '<ul>'; |
| 607 | + |
| 608 | + $action = $wgTitle->escapeLocalUrl( 'action=block' ); |
| 609 | + $s = "<form name='checkuserblock' id='checkuserblock' action='$action' method='post'>"; |
| 610 | + $s .= '<ul>'; |
533 | 611 | foreach( $users_edits as $name => $count ) { |
534 | 612 | $s .= '<li>'; |
| 613 | + $s .= Xml::check( 'users[]', false, array( 'value' => $name ) ) . ' '; |
535 | 614 | $s .= $this->sk->userLink( -1 , $name ) . $this->sk->userToolLinks( -1 , $name ); |
536 | | - $s .= ' (<a href="' . $wgTitle->escapeLocalURL( 'user='.urlencode($name).'&reason='.urlencode($reason) ) . |
537 | | - '">' . wfMsgHtml('checkuser-check') . '</a>)'; |
| 615 | + $s .= ' (<a href="' . $wgTitle->escapeLocalURL( 'user='.urlencode($name) . |
| 616 | + '&reason='.urlencode($reason) ) . '">' . wfMsgHtml('checkuser-check') . '</a>)'; |
538 | 617 | if( $users_first[$name] == $users_last[$name] ) { |
539 | 618 | $s .= ' (' . $wgLang->timeanddate( $users_first[$name], true ) . ') '; |
540 | 619 | } else { |
— | — | @@ -618,7 +697,20 @@ |
619 | 698 | $s .= '</ol>'; |
620 | 699 | $s .= '</li>'; |
621 | 700 | } |
622 | | - $s .= '</ul>'; |
| 701 | + $s .= "</ul>\n"; |
| 702 | + if( $wgUser->isAllowed('block') && !$wgUser->isBlocked() ) { |
| 703 | + $s.= "<fieldset>\n"; |
| 704 | + $s .= "<legend>" . wfMsgHtml('checkuser-massblock') . "</legend>\n"; |
| 705 | + $s .= "<p>" . wfMsgExt('checkuser-massblock-text',array('parseinline')) . "</p>\n"; |
| 706 | + $s .= "<p>" . Xml::checkLabel( wfMsgHtml( "checkuser-blocktag" ), 'usetag', 'usetag') . ' '; |
| 707 | + $s .= Xml::input( 'tag', 46, $tag, array( 'maxlength' => '150', 'id' => 'blocktag' ) ) . "</p>\n"; |
| 708 | + $s .= "<p>" . wfMsgHtml( "checkuser-reason" ) . ' '; |
| 709 | + $s .= Xml::input( 'reason', 46, $reason, array( 'maxlength' => '150', 'id' => 'blockreason' ) ); |
| 710 | + $s .= ' ' . Xml::submitButton( wfMsgHtml('checkuser-massblock-commit'), |
| 711 | + array('id' => 'checkuserblocksubmit','name' => 'checkuserblock') ) . "</p>\n"; |
| 712 | + $s .= "</fieldset>\n"; |
| 713 | + } |
| 714 | + $s .= '</form>'; |
623 | 715 | } |
624 | 716 | |
625 | 717 | $wgOut->addHTML( $s ); |
— | — | @@ -700,7 +792,7 @@ |
701 | 793 | * Get all IPs used by a user |
702 | 794 | * Shows first and last date and number of edits |
703 | 795 | */ |
704 | | - protected function doUserIPsRequest( $user , $reason = '', $period ) { |
| 796 | + protected function doUserIPsRequest( $user , $reason = '', $period = 0 ) { |
705 | 797 | global $wgOut, $wgTitle, $wgLang, $wgUser, $wgDBname; |
706 | 798 | $fname = 'CheckUser::doUserIPsRequest'; |
707 | 799 | |
Index: trunk/extensions/CheckUser/CheckUser.i18n.php |
— | — | @@ -19,7 +19,7 @@ |
20 | 20 | 'group-checkuser-member' => 'check user', |
21 | 21 | 'right-checkuser' => "Check user's IP addresses and other information", |
22 | 22 | 'grouppage-checkuser' => '{{ns:project}}:Check user', |
23 | | - 'checkuser-reason' => 'Reason', |
| 23 | + 'checkuser-reason' => 'Reason:', |
24 | 24 | 'checkuser-showlog' => 'Show log', |
25 | 25 | 'checkuser-log' => 'CheckUser log', |
26 | 26 | 'checkuser-query' => 'Query recent changes', |
— | — | @@ -40,6 +40,13 @@ |
41 | 41 | 'checkuser-nolog' => 'No log file found.', |
42 | 42 | 'checkuser-blocked' => 'Blocked', |
43 | 43 | 'checkuser-wasblocked' => 'Previously blocked', |
| 44 | + 'checkuser-massblock' => 'Block selected users', |
| 45 | + 'checkuser-massblock-text' => 'Selected accounts will be blocked indefinetly, with autoblocing enabled and account creation disabled. |
| 46 | + IP addresses will be blocked for 1 week for IP users only and with account creation disabled.', |
| 47 | + 'checkuser-blocktag' => 'Replace user pages with:', |
| 48 | + 'checkuser-massblock-commit' => 'Block selected users', |
| 49 | + 'checkuser-block-success' => '\'\'\'The {{PLURAL:$2|user|users}} $1 {{PLURAL:$2|is|are}} now blocked.\'\'\'', |
| 50 | + 'checkuser-block-failure' => '\'\'\'No users blocked.\'\'\'', |
44 | 51 | 'checkuser-accounts' => '$1 new {{PLURAL:$1|account|accounts}}', |
45 | 52 | 'checkuser-too-many' => 'Too many results, please narrow down the CIDR. |
46 | 53 | Here are the IPs used (5000 max, sorted by address):', |
Index: trunk/extensions/CheckUser/CheckUser.php |
— | — | @@ -29,7 +29,10 @@ |
30 | 30 | # How long to keep CU data? |
31 | 31 | $wgCUDMaxAge = 3 * 30 * 24 * 3600; |
32 | 32 | |
33 | | -#Recent changes data hook |
| 33 | +# Mass block limits |
| 34 | +$wgCheckUserMaxBlocks = 200; |
| 35 | + |
| 36 | +# Recent changes data hook |
34 | 37 | global $wgHooks; |
35 | 38 | $wgHooks['RecentChange_save'][] = 'efUpdateCheckUserData'; |
36 | 39 | $wgHooks['EmailUser'][] = 'efUpdateCheckUserEmailData'; |