Index: branches/snapshot-work/maintenance/archives/patch-ipb_anon_only.sql |
— | — | @@ -0,0 +1,43 @@ |
| 2 | +-- Add extra option fields to the ipblocks table, add some extra indexes, |
| 3 | +-- convert infinity values in ipb_expiry to something that sorts better, |
| 4 | +-- extend ipb_address and range fields, add a unique index for block conflict |
| 5 | +-- detection. |
| 6 | + |
| 7 | +-- Conflicts in the new unique index can be handled by creating a new |
| 8 | +-- table and inserting into it instead of doing an ALTER TABLE. |
| 9 | + |
| 10 | + |
| 11 | +DROP TABLE IF EXISTS /*$wgDBprefix*/ipblocks_newunique; |
| 12 | + |
| 13 | +CREATE TABLE /*$wgDBprefix*/ipblocks_newunique ( |
| 14 | + ipb_id int(8) NOT NULL auto_increment, |
| 15 | + ipb_address tinyblob NOT NULL default '', |
| 16 | + ipb_user int(8) unsigned NOT NULL default '0', |
| 17 | + ipb_by int(8) unsigned NOT NULL default '0', |
| 18 | + ipb_reason tinyblob NOT NULL default '', |
| 19 | + ipb_timestamp char(14) binary NOT NULL default '', |
| 20 | + ipb_auto boolean NOT NULL default 0, |
| 21 | + ipb_anon_only boolean NOT NULL default 0, |
| 22 | + ipb_create_account boolean NOT NULL default 1, |
| 23 | + ipb_expiry char(14) binary NOT NULL default '', |
| 24 | + ipb_range_start tinyblob NOT NULL default '', |
| 25 | + ipb_range_end tinyblob NOT NULL default '', |
| 26 | + |
| 27 | + PRIMARY KEY ipb_id (ipb_id), |
| 28 | + UNIQUE INDEX ipb_address_unique (ipb_address(255), ipb_user, ipb_auto), |
| 29 | + INDEX ipb_user (ipb_user), |
| 30 | + INDEX ipb_range (ipb_range_start(8), ipb_range_end(8)), |
| 31 | + INDEX ipb_timestamp (ipb_timestamp), |
| 32 | + INDEX ipb_expiry (ipb_expiry) |
| 33 | + |
| 34 | +) TYPE=InnoDB; |
| 35 | + |
| 36 | +INSERT IGNORE INTO /*$wgDBprefix*/ipblocks_newunique |
| 37 | + (ipb_id, ipb_address, ipb_user, ipb_by, ipb_reason, ipb_timestamp, ipb_auto, ipb_expiry, ipb_range_start, ipb_range_end) |
| 38 | + SELECT ipb_id, ipb_address, ipb_user, ipb_by, ipb_reason, ipb_timestamp, ipb_auto, ipb_expiry, ipb_range_start, ipb_range_end |
| 39 | + FROM /*$wgDBprefix*/ipblocks; |
| 40 | + |
| 41 | +DROP TABLE IF EXISTS /*$wgDBprefix*/ipblocks_old; |
| 42 | +RENAME TABLE /*$wgDBprefix*/ipblocks TO /*$wgDBprefix*/ipblocks_old; |
| 43 | +RENAME TABLE /*$wgDBprefix*/ipblocks_newunique TO /*$wgDBprefix*/ipblocks; |
| 44 | + |
Property changes on: branches/snapshot-work/maintenance/archives/patch-ipb_anon_only.sql |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 45 | + native |
Index: branches/snapshot-work/maintenance/updaters.inc |
— | — | @@ -56,6 +56,7 @@ |
57 | 57 | array( 'interwiki', 'iw_trans', 'patch-interwiki-trans.sql' ), |
58 | 58 | array( 'ipblocks', 'ipb_range_start', 'patch-ipb_range_start.sql' ), |
59 | 59 | array( 'site_stats', 'ss_images', 'patch-ss_images.sql' ), |
| 60 | + array( 'ipblocks', 'ipb_anon_only', 'patch-ipb_anon_only.sql' ), |
60 | 61 | ); |
61 | 62 | |
62 | 63 | function rename_table( $from, $to, $patch ) { |
Index: branches/snapshot-work/maintenance/mysql5/tables.sql |
— | — | @@ -583,8 +583,14 @@ |
584 | 584 | -- Indicates that the IP address was banned because a banned |
585 | 585 | -- user accessed a page through it. If this is 1, ipb_address |
586 | 586 | -- will be hidden, and the block identified by block ID number. |
587 | | - ipb_auto tinyint(1) NOT NULL default '0', |
| 587 | + ipb_auto boolean NOT NULL default '0', |
588 | 588 | |
| 589 | + -- If set to 1, block applies only to logged-out users |
| 590 | + ipb_anon_only boolean NOT NULL default 0, |
| 591 | + |
| 592 | + -- Block prevents account creation from matching IP addresses |
| 593 | + ipb_create_account boolean NOT NULL default 1, |
| 594 | + |
589 | 595 | -- Time at which the block will expire. |
590 | 596 | ipb_expiry char(14) binary NOT NULL default '', |
591 | 597 | |
— | — | @@ -594,9 +600,15 @@ |
595 | 601 | ipb_range_end varchar(32) NOT NULL default '', |
596 | 602 | |
597 | 603 | PRIMARY KEY ipb_id (ipb_id), |
598 | | - INDEX ipb_address (ipb_address), |
| 604 | + |
| 605 | + -- Unique index to support "user already blocked" messages |
| 606 | + -- Any new options which prevent collisions should be included |
| 607 | + UNIQUE INDEX ipb_address (ipb_address(255), ipb_user, ipb_auto, ipb_anon_only), |
| 608 | + |
599 | 609 | INDEX ipb_user (ipb_user), |
600 | | - INDEX ipb_range (ipb_range_start(8), ipb_range_end(8)) |
| 610 | + INDEX ipb_range (ipb_range_start(8), ipb_range_end(8)), |
| 611 | + INDEX ipb_timestamp (ipb_timestamp), |
| 612 | + INDEX ipb_expiry (ipb_expiry) |
601 | 613 | |
602 | 614 | ) TYPE=InnoDB, DEFAULT CHARSET=utf8; |
603 | 615 | |
— | — | @@ -1006,4 +1018,4 @@ |
1007 | 1019 | |
1008 | 1020 | UNIQUE KEY ( qci_type ) |
1009 | 1021 | |
1010 | | -) TYPE=InnoDB; |
\ No newline at end of file |
| 1022 | +) TYPE=InnoDB; |
Index: branches/snapshot-work/maintenance/tables.sql |
— | — | @@ -552,7 +552,7 @@ |
553 | 553 | ipb_id int(8) NOT NULL auto_increment, |
554 | 554 | |
555 | 555 | -- Blocked IP address in dotted-quad form or user name. |
556 | | - ipb_address varchar(40) binary NOT NULL default '', |
| 556 | + ipb_address tinyblob NOT NULL default '', |
557 | 557 | |
558 | 558 | -- Blocked user ID or 0 for IP blocks. |
559 | 559 | ipb_user int(8) unsigned NOT NULL default '0', |
— | — | @@ -570,20 +570,32 @@ |
571 | 571 | -- Indicates that the IP address was banned because a banned |
572 | 572 | -- user accessed a page through it. If this is 1, ipb_address |
573 | 573 | -- will be hidden, and the block identified by block ID number. |
574 | | - ipb_auto tinyint(1) NOT NULL default '0', |
| 574 | + ipb_auto boolean NOT NULL default 0, |
| 575 | + |
| 576 | + -- If set to 1, block applies only to logged-out users |
| 577 | + ipb_anon_only boolean NOT NULL default 0, |
| 578 | + |
| 579 | + -- Block prevents account creation from matching IP addresses |
| 580 | + ipb_create_account boolean NOT NULL default 1, |
575 | 581 | |
576 | 582 | -- Time at which the block will expire. |
577 | 583 | ipb_expiry char(14) binary NOT NULL default '', |
578 | 584 | |
579 | 585 | -- Start and end of an address range, in hexadecimal |
580 | 586 | -- Size chosen to allow IPv6 |
581 | | - ipb_range_start varchar(32) NOT NULL default '', |
582 | | - ipb_range_end varchar(32) NOT NULL default '', |
| 587 | + ipb_range_start tinyblob NOT NULL default '', |
| 588 | + ipb_range_end tinyblob NOT NULL default '', |
583 | 589 | |
584 | 590 | PRIMARY KEY ipb_id (ipb_id), |
585 | | - INDEX ipb_address (ipb_address), |
| 591 | + |
| 592 | + -- Unique index to support "user already blocked" messages |
| 593 | + -- Any new options which prevent collisions should be included |
| 594 | + UNIQUE INDEX ipb_address (ipb_address(255), ipb_user, ipb_auto, ipb_anon_only), |
| 595 | + |
586 | 596 | INDEX ipb_user (ipb_user), |
587 | | - INDEX ipb_range (ipb_range_start(8), ipb_range_end(8)) |
| 597 | + INDEX ipb_range (ipb_range_start(8), ipb_range_end(8)), |
| 598 | + INDEX ipb_timestamp (ipb_timestamp), |
| 599 | + INDEX ipb_expiry (ipb_expiry) |
588 | 600 | |
589 | 601 | ) TYPE=InnoDB; |
590 | 602 | |
— | — | @@ -913,10 +925,10 @@ |
914 | 926 | -- Cache of interwiki transclusion |
915 | 927 | -- |
916 | 928 | CREATE TABLE /*$wgDBprefix*/transcache ( |
917 | | - tc_url VARCHAR(255) NOT NULL, |
918 | | - tc_contents TEXT, |
919 | | - tc_time INT NOT NULL, |
920 | | - UNIQUE INDEX tc_url_idx(tc_url) |
| 929 | + tc_url VARCHAR(255) NOT NULL, |
| 930 | + tc_contents TEXT, |
| 931 | + tc_time INT NOT NULL, |
| 932 | + UNIQUE INDEX tc_url_idx(tc_url) |
921 | 933 | ) TYPE=InnoDB; |
922 | 934 | |
923 | 935 | CREATE TABLE /*$wgDBprefix*/logging ( |
— | — | @@ -951,14 +963,14 @@ |
952 | 964 | ) TYPE=InnoDB; |
953 | 965 | |
954 | 966 | CREATE TABLE /*$wgDBprefix*/trackbacks ( |
955 | | - tb_id integer AUTO_INCREMENT PRIMARY KEY, |
956 | | - tb_page integer REFERENCES page(page_id) ON DELETE CASCADE, |
957 | | - tb_title varchar(255) NOT NULL, |
958 | | - tb_url varchar(255) NOT NULL, |
959 | | - tb_ex text, |
960 | | - tb_name varchar(255), |
| 967 | + tb_id integer AUTO_INCREMENT PRIMARY KEY, |
| 968 | + tb_page integer REFERENCES page(page_id) ON DELETE CASCADE, |
| 969 | + tb_title varchar(255) NOT NULL, |
| 970 | + tb_url varchar(255) NOT NULL, |
| 971 | + tb_ex text, |
| 972 | + tb_name varchar(255), |
961 | 973 | |
962 | | - INDEX (tb_page) |
| 974 | + INDEX (tb_page) |
963 | 975 | ) TYPE=InnoDB; |
964 | 976 | |
965 | 977 | |
— | — | @@ -986,13 +998,15 @@ |
987 | 999 | -- Details of updates to cached special pages |
988 | 1000 | CREATE TABLE /*$wgDBprefix*/querycache_info ( |
989 | 1001 | |
990 | | - -- Special page name |
991 | | - -- Corresponds to a qc_type value |
992 | | - qci_type varchar(32) NOT NULL default '', |
| 1002 | + -- Special page name |
| 1003 | + -- Corresponds to a qc_type value |
| 1004 | + qci_type varchar(32) NOT NULL default '', |
993 | 1005 | |
994 | | - -- Timestamp of last update |
995 | | - qci_timestamp char(14) NOT NULL default '19700101000000', |
| 1006 | + -- Timestamp of last update |
| 1007 | + qci_timestamp char(14) NOT NULL default '19700101000000', |
996 | 1008 | |
997 | | - UNIQUE KEY ( qci_type ) |
| 1009 | +UNIQUE KEY ( qci_type ) |
998 | 1010 | |
999 | 1011 | ) TYPE=InnoDB; |
| 1012 | + |
| 1013 | +-- vim: sw=2 sts=2 et |
Index: branches/snapshot-work/includes/User.php |
— | — | @@ -24,6 +24,7 @@ |
25 | 25 | */ |
26 | 26 | var $mBlockedby; //!< |
27 | 27 | var $mBlockreason; //!< |
| 28 | + var $mBlock; //!< |
28 | 29 | var $mDataLoaded; //!< |
29 | 30 | var $mEmail; //!< |
30 | 31 | var $mEmailAuthenticated; //!< |
— | — | @@ -114,8 +115,6 @@ |
115 | 116 | */ |
116 | 117 | function __sleep() { |
117 | 118 | return array( |
118 | | -'mBlockedby', |
119 | | -'mBlockreason', |
120 | 119 | 'mDataLoaded', |
121 | 120 | 'mEmail', |
122 | 121 | 'mEmailAuthenticated', |
— | — | @@ -436,16 +435,17 @@ |
437 | 436 | $ip = wfGetIP(); |
438 | 437 | |
439 | 438 | # User/IP blocking |
440 | | - $block = new Block(); |
441 | | - $block->fromMaster( !$bFromSlave ); |
442 | | - if ( $block->load( $ip , $this->mId ) ) { |
| 439 | + $this->mBlock = new Block(); |
| 440 | + $this->mBlock->fromMaster( !$bFromSlave ); |
| 441 | + if ( $this->mBlock->load( $ip , $this->mId ) ) { |
443 | 442 | wfDebug( "$fname: Found block.\n" ); |
444 | | - $this->mBlockedby = $block->mBy; |
445 | | - $this->mBlockreason = $block->mReason; |
| 443 | + $this->mBlockedby = $this->mBlock->mBy; |
| 444 | + $this->mBlockreason = $this->mBlock->mReason; |
446 | 445 | if ( $this->isLoggedIn() ) { |
447 | 446 | $this->spreadBlock(); |
448 | 447 | } |
449 | 448 | } else { |
| 449 | + $this->mBlock = null; |
450 | 450 | wfDebug( "$fname: No block.\n" ); |
451 | 451 | } |
452 | 452 | |
— | — | @@ -694,6 +694,8 @@ |
695 | 695 | $user->loadFromDatabase(); |
696 | 696 | } else { |
697 | 697 | wfDebug( "User::loadFromSession() got from cache!\n" ); |
| 698 | + # Set block status to unloaded, that should be loaded every time |
| 699 | + $user->mBlockedby = -1; |
698 | 700 | } |
699 | 701 | |
700 | 702 | if ( isset( $_SESSION['wsToken'] ) ) { |
— | — | @@ -1532,13 +1534,13 @@ |
1533 | 1535 | } |
1534 | 1536 | |
1535 | 1537 | $userblock = Block::newFromDB( '', $this->mId ); |
1536 | | - if ( !$userblock->isValid() ) { |
| 1538 | + if ( !$userblock ) { |
1537 | 1539 | return; |
1538 | 1540 | } |
1539 | 1541 | |
1540 | 1542 | # Check if this IP address is already blocked |
1541 | 1543 | $ipblock = Block::newFromDB( wfGetIP() ); |
1542 | | - if ( $ipblock->isValid() ) { |
| 1544 | + if ( $ipblock ) { |
1543 | 1545 | # If the user is already blocked. Then check if the autoblock would |
1544 | 1546 | # excede the user block. If it would excede, then do nothing, else |
1545 | 1547 | # prolong block time |
— | — | @@ -1612,8 +1614,13 @@ |
1613 | 1615 | return $confstr; |
1614 | 1616 | } |
1615 | 1617 | |
| 1618 | + function isBlockedFromCreateAccount() { |
| 1619 | + $this->getBlockedStatus(); |
| 1620 | + return $this->mBlock && $this->mBlock->mCreateAccount; |
| 1621 | + } |
| 1622 | + |
1616 | 1623 | function isAllowedToCreateAccount() { |
1617 | | - return $this->isAllowed( 'createaccount' ) && !$this->isBlocked(); |
| 1624 | + return $this->isAllowed( 'createaccount' ) && !$this->isBlockedFromCreateAccount(); |
1618 | 1625 | } |
1619 | 1626 | |
1620 | 1627 | /** |
Index: branches/snapshot-work/includes/SpecialUserlogin.php |
— | — | @@ -470,6 +470,27 @@ |
471 | 471 | $wgOut->returnToMain( false ); |
472 | 472 | } |
473 | 473 | |
| 474 | + /** */ |
| 475 | + function userBlockedMessage() { |
| 476 | + global $wgOut; |
| 477 | + |
| 478 | + # Let's be nice about this, it's likely that this feature will be used |
| 479 | + # for blocking large numbers of innocent people, e.g. range blocks on |
| 480 | + # schools. Don't blame it on the user. There's a small chance that it |
| 481 | + # really is the user's fault, i.e. the username is blocked and they |
| 482 | + # haven't bothered to log out before trying to create an account to |
| 483 | + # evade it, but we'll leave that to their guilty conscience to figure |
| 484 | + # out. |
| 485 | + |
| 486 | + $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) ); |
| 487 | + $wgOut->setRobotpolicy( 'noindex,nofollow' ); |
| 488 | + $wgOut->setArticleRelated( false ); |
| 489 | + |
| 490 | + $ip = wfGetIP(); |
| 491 | + $wgOut->addWikiText( wfMsg( 'cantcreateaccounttext', $ip ) ); |
| 492 | + $wgOut->returnToMain( false ); |
| 493 | + } |
| 494 | + |
474 | 495 | /** |
475 | 496 | * @private |
476 | 497 | */ |
— | — | @@ -477,9 +498,14 @@ |
478 | 499 | global $wgUser, $wgOut, $wgAllowRealName, $wgEnableEmail; |
479 | 500 | global $wgCookiePrefix, $wgAuth, $wgLoginLanguageSelector; |
480 | 501 | |
481 | | - if ( $this->mType == 'signup' && !$wgUser->isAllowedToCreateAccount() ) { |
482 | | - $this->userNotPrivilegedMessage(); |
483 | | - return; |
| 502 | + if ( $this->mType == 'signup' ) { |
| 503 | + if ( !$wgUser->isAllowed( 'createaccount' ) ) { |
| 504 | + $this->userNotPrivilegedMessage(); |
| 505 | + return; |
| 506 | + } elseif ( $wgUser->isBlockedFromCreateAccount() ) { |
| 507 | + $this->userBlockedMessage(); |
| 508 | + return; |
| 509 | + } |
484 | 510 | } |
485 | 511 | |
486 | 512 | if ( '' == $this->mName ) { |
— | — | @@ -570,7 +596,7 @@ |
571 | 597 | function showCreateOrLoginLink( &$user ) { |
572 | 598 | if( $this->mType == 'signup' ) { |
573 | 599 | return( true ); |
574 | | - } elseif( $user->isAllowedToCreateAccount() ) { |
| 600 | + } elseif( $user->isAllowed( 'createaccount' ) ) { |
575 | 601 | return( true ); |
576 | 602 | } else { |
577 | 603 | return( false ); |
Index: branches/snapshot-work/includes/SpecialBlockip.php |
— | — | @@ -46,6 +46,15 @@ |
47 | 47 | $this->BlockReason = $wgRequest->getText( 'wpBlockReason' ); |
48 | 48 | $this->BlockExpiry = $wgRequest->getVal( 'wpBlockExpiry', wfMsg('ipbotheroption') ); |
49 | 49 | $this->BlockOther = $wgRequest->getVal( 'wpBlockOther', '' ); |
| 50 | + $this->BlockAnonOnly = $wgRequest->getBool( 'wpAnonOnly' ); |
| 51 | + |
| 52 | + # Unchecked checkboxes are not included in the form data at all, so having one |
| 53 | + # that is true by default is a bit tricky |
| 54 | + if ( $wgRequest->wasPosted() ) { |
| 55 | + $this->BlockCreateAccount = $wgRequest->getBool( 'wpCreateAccount', false ); |
| 56 | + } else { |
| 57 | + $this->BlockCreateAccount = $wgRequest->getBool( 'wpCreateAccount', true ); |
| 58 | + } |
50 | 59 | } |
51 | 60 | |
52 | 61 | function showForm( $err ) { |
— | — | @@ -64,6 +73,8 @@ |
65 | 74 | $mIpbothertime = wfMsgHtml( 'ipbotheroption' ); |
66 | 75 | $mIpbreason = wfMsgHtml( 'ipbreason' ); |
67 | 76 | $mIpbsubmit = wfMsgHtml( 'ipbsubmit' ); |
| 77 | + $mIpbanononly = wfMsgHtml( 'ipbanononly' ); |
| 78 | + $mIpbcreateaccount = wfMsgHtml( 'ipbcreateaccount' ); |
68 | 79 | $titleObj = Title::makeTitle( NS_SPECIAL, 'Blockip' ); |
69 | 80 | $action = $titleObj->escapeLocalURL( "action=submit" ); |
70 | 81 | |
— | — | @@ -77,6 +88,8 @@ |
78 | 89 | $scBlockReason = htmlspecialchars( $this->BlockReason ); |
79 | 90 | $scBlockOtherTime = htmlspecialchars( $this->BlockOther ); |
80 | 91 | $scBlockExpiryOptions = htmlspecialchars( wfMsgForContent( 'ipboptions' ) ); |
| 92 | + $anonOnlyChecked = $this->BlockAnonOnly ? 'checked' : ''; |
| 93 | + $createAccountChecked = $this->BlockCreateAccount ? 'checked' : ''; |
81 | 94 | |
82 | 95 | $showblockoptions = $scBlockExpiryOptions != '-'; |
83 | 96 | if (!$showblockoptions) |
— | — | @@ -102,7 +115,7 @@ |
103 | 116 | <tr> |
104 | 117 | <td align=\"right\">{$mIpaddress}:</td> |
105 | 118 | <td align=\"left\"> |
106 | | - <input tabindex='1' type='text' size='20' name=\"wpBlockAddress\" value=\"{$scBlockAddress}\" /> |
| 119 | + <input tabindex='1' type='text' size='40' name=\"wpBlockAddress\" value=\"{$scBlockAddress}\" /> |
107 | 120 | </td> |
108 | 121 | </tr> |
109 | 122 | <tr>"); |
— | — | @@ -133,6 +146,24 @@ |
134 | 147 | <tr> |
135 | 148 | <td> </td> |
136 | 149 | <td align=\"left\"> |
| 150 | + <label> |
| 151 | + <input type='checkbox' name='wpAnonOnly' value='1' $anonOnlyChecked /> |
| 152 | + {$mIpbanononly} |
| 153 | + </label> |
| 154 | + </td> |
| 155 | + </tr> |
| 156 | + <tr> |
| 157 | + <td> </td> |
| 158 | + <td align=\"left\"> |
| 159 | + <label> |
| 160 | + <input type='checkbox' name='wpCreateAccount' value='1' $createAccountChecked /> |
| 161 | + {$mIpbcreateaccount} |
| 162 | + </label> |
| 163 | + </td> |
| 164 | + </tr> |
| 165 | + <tr> |
| 166 | + <td style='padding-top: 1em'> </td> |
| 167 | + <td style='padding-top: 1em' align=\"left\"> |
137 | 168 | <input tabindex='4' type='submit' name=\"wpBlock\" value=\"{$mIpbsubmit}\" /> |
138 | 169 | </td> |
139 | 170 | </tr> |
— | — | @@ -188,7 +219,7 @@ |
189 | 220 | } |
190 | 221 | |
191 | 222 | if ( $expirestr == 'infinite' || $expirestr == 'indefinite' ) { |
192 | | - $expiry = ''; |
| 223 | + $expiry = Block::infinity(); |
193 | 224 | } else { |
194 | 225 | # Convert GNU-style date, on error returns -1 for PHP <5.1 and false for PHP >=5.1 |
195 | 226 | $expiry = strtotime( $expirestr ); |
— | — | @@ -199,20 +230,24 @@ |
200 | 231 | } |
201 | 232 | |
202 | 233 | $expiry = wfTimestamp( TS_MW, $expiry ); |
203 | | - |
204 | 234 | } |
205 | 235 | |
206 | 236 | # Create block |
207 | 237 | # Note: for a user block, ipb_address is only for display purposes |
208 | 238 | |
209 | | - $ban = new Block( $this->BlockAddress, $userId, $wgUser->getID(), |
210 | | - $this->BlockReason, wfTimestampNow(), 0, $expiry ); |
| 239 | + $block = new Block( $this->BlockAddress, $userId, $wgUser->getID(), |
| 240 | + $this->BlockReason, wfTimestampNow(), 0, $expiry, $this->BlockAnonOnly, |
| 241 | + $this->BlockCreateAccount ); |
211 | 242 | |
212 | | - if (wfRunHooks('BlockIp', array(&$ban, &$wgUser))) { |
| 243 | + if (wfRunHooks('BlockIp', array(&$block, &$wgUser))) { |
213 | 244 | |
214 | | - $ban->insert(); |
| 245 | + if ( !$block->insert() ) { |
| 246 | + $this->showForm( wfMsg( 'ipb_already_blocked', |
| 247 | + htmlspecialchars( $this->BlockAddress ) ) ); |
| 248 | + return; |
| 249 | + } |
215 | 250 | |
216 | | - wfRunHooks('BlockIpComplete', array($ban, $wgUser)); |
| 251 | + wfRunHooks('BlockIpComplete', array($block, $wgUser)); |
217 | 252 | |
218 | 253 | # Make log entry |
219 | 254 | $log = new LogPage( 'block' ); |
Index: branches/snapshot-work/includes/SpecialIpblocklist.php |
— | — | @@ -12,13 +12,15 @@ |
13 | 13 | global $wgUser, $wgOut, $wgRequest; |
14 | 14 | |
15 | 15 | $ip = $wgRequest->getVal( 'wpUnblockAddress', $wgRequest->getVal( 'ip' ) ); |
| 16 | + $id = $wgRequest->getVal( 'id' ); |
16 | 17 | $reason = $wgRequest->getText( 'wpUnblockReason' ); |
17 | 18 | $action = $wgRequest->getText( 'action' ); |
| 19 | + $successip = $wgRequest->getVal( 'successip' ); |
18 | 20 | |
19 | | - $ipu = new IPUnblockForm( $ip, $reason ); |
| 21 | + $ipu = new IPUnblockForm( $ip, $id, $reason ); |
20 | 22 | |
21 | 23 | if ( "success" == $action ) { |
22 | | - $ipu->showList( $wgOut->parse( wfMsg( 'unblocked', $ip ) ) ); |
| 24 | + $ipu->showList( $wgOut->parse( wfMsg( 'unblocked', $successip ) ) ); |
23 | 25 | } else if ( "submit" == $action && $wgRequest->wasPosted() && |
24 | 26 | $wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) { |
25 | 27 | if ( ! $wgUser->isAllowed('block') ) { |
— | — | @@ -39,10 +41,11 @@ |
40 | 42 | * @subpackage SpecialPage |
41 | 43 | */ |
42 | 44 | class IPUnblockForm { |
43 | | - var $ip, $reason; |
| 45 | + var $ip, $reason, $id; |
44 | 46 | |
45 | | - function IPUnblockForm( $ip, $reason ) { |
| 47 | + function IPUnblockForm( $ip, $id, $reason ) { |
46 | 48 | $this->ip = $ip; |
| 49 | + $this->id = $id; |
47 | 50 | $this->reason = $reason; |
48 | 51 | } |
49 | 52 | |
— | — | @@ -64,13 +67,27 @@ |
65 | 68 | } |
66 | 69 | $token = htmlspecialchars( $wgUser->editToken() ); |
67 | 70 | |
| 71 | + $addressPart = false; |
| 72 | + if ( $this->id ) { |
| 73 | + $block = Block::newFromID( $this->id ); |
| 74 | + if ( $block ) { |
| 75 | + $encName = htmlspecialchars( $block->getRedactedName() ); |
| 76 | + $encId = htmlspecialchars( $this->id ); |
| 77 | + $addressPart = $encName . "<input type='hidden' name=\"id\" value=\"$encId\" />"; |
| 78 | + } |
| 79 | + } |
| 80 | + if ( !$addressPart ) { |
| 81 | + $addressPart = "<input tabindex='1' type='text' size='20' " . |
| 82 | + "name=\"wpUnblockAddress\" value=\"" . htmlspecialchars( $this->ip ) . "\" />"; |
| 83 | + } |
| 84 | + |
68 | 85 | $wgOut->addHTML( " |
69 | 86 | <form id=\"unblockip\" method=\"post\" action=\"{$action}\"> |
70 | 87 | <table border='0'> |
71 | 88 | <tr> |
72 | 89 | <td align='right'>{$ipa}:</td> |
73 | 90 | <td align='left'> |
74 | | - <input tabindex='1' type='text' size='20' name=\"wpUnblockAddress\" value=\"" . htmlspecialchars( $this->ip ) . "\" /> |
| 91 | + {$addressPart} |
75 | 92 | </td> |
76 | 93 | </tr> |
77 | 94 | <tr> |
— | — | @@ -94,27 +111,46 @@ |
95 | 112 | function doSubmit() { |
96 | 113 | global $wgOut; |
97 | 114 | |
98 | | - $block = new Block(); |
99 | | - $this->ip = trim( $this->ip ); |
| 115 | + if ( $this->id ) { |
| 116 | + $block = Block::newFromID( $this->id ); |
| 117 | + if ( $block ) { |
| 118 | + $this->ip = $block->getRedactedName(); |
| 119 | + } |
| 120 | + } else { |
| 121 | + $block = new Block(); |
| 122 | + $this->ip = trim( $this->ip ); |
| 123 | + if ( substr( $this->ip, 0, 1 ) == "#" ) { |
| 124 | + $id = substr( $this->ip, 1 ); |
| 125 | + $block = Block::newFromID( $id ); |
| 126 | + } else { |
| 127 | + $block = Block::newFromDB( $this->ip ); |
| 128 | + if ( !$block ) { |
| 129 | + $block = null; |
| 130 | + } |
| 131 | + } |
| 132 | + } |
| 133 | + $success = false; |
| 134 | + if ( $block ) { |
| 135 | + # Delete block |
| 136 | + if ( $block->delete() ) { |
| 137 | + # Make log entry |
| 138 | + $log = new LogPage( 'block' ); |
| 139 | + $log->addEntry( 'unblock', Title::makeTitle( NS_USER, $this->ip ), $this->reason ); |
| 140 | + $success = true; |
| 141 | + } |
| 142 | + } |
100 | 143 | |
101 | | - if ( $this->ip{0} == "#" ) { |
102 | | - $block->mId = substr( $this->ip, 1 ); |
| 144 | + if ( $success ) { |
| 145 | + # Report to the user |
| 146 | + $titleObj = Title::makeTitle( NS_SPECIAL, "Ipblocklist" ); |
| 147 | + $success = $titleObj->getFullURL( "action=success&successip=" . urlencode( $this->ip ) ); |
| 148 | + $wgOut->redirect( $success ); |
103 | 149 | } else { |
104 | | - $block->mAddress = $this->ip; |
| 150 | + if ( !$this->ip && $this->id ) { |
| 151 | + $this->ip = '#' . $this->id; |
| 152 | + } |
| 153 | + $this->showForm( wfMsg( 'ipb_cant_unblock', htmlspecialchars( $this->id ) ) ); |
105 | 154 | } |
106 | | - |
107 | | - # Delete block (if it exists) |
108 | | - # We should probably check for errors rather than just declaring success |
109 | | - $block->delete(); |
110 | | - |
111 | | - # Make log entry |
112 | | - $log = new LogPage( 'block' ); |
113 | | - $log->addEntry( 'unblock', Title::makeTitle( NS_USER, $this->ip ), $this->reason ); |
114 | | - |
115 | | - # Report to the user |
116 | | - $titleObj = Title::makeTitle( NS_SPECIAL, "Ipblocklist" ); |
117 | | - $success = $titleObj->getFullURL( "action=success&ip=" . urlencode( $this->ip ) ); |
118 | | - $wgOut->redirect( $success ); |
119 | 155 | } |
120 | 156 | |
121 | 157 | function showList( $msg ) { |
— | — | @@ -124,29 +160,43 @@ |
125 | 161 | if ( "" != $msg ) { |
126 | 162 | $wgOut->setSubtitle( $msg ); |
127 | 163 | } |
128 | | - global $wgRequest; |
129 | | - list( $this->limit, $this->offset ) = $wgRequest->getLimitOffset(); |
130 | | - $this->counter = 0; |
131 | 164 | |
132 | | - $paging = '<p>' . wfViewPrevNext( $this->offset, $this->limit, |
133 | | - Title::makeTitle( NS_SPECIAL, 'Ipblocklist' ), |
134 | | - 'ip=' . urlencode( $this->ip ) ) . "</p>\n"; |
135 | | - $wgOut->addHTML( $paging ); |
| 165 | + // Purge expired entries on one in every 10 queries |
| 166 | + if ( !mt_rand( 0, 10 ) ) { |
| 167 | + Block::purgeExpired(); |
| 168 | + } |
136 | 169 | |
137 | | - $search = $this->searchForm(); |
138 | | - $wgOut->addHTML( $search ); |
139 | | - |
140 | | - $wgOut->addHTML( "<ul>" ); |
141 | | - if( !Block::enumBlocks( array( &$this, "addRow" ), 0 ) ) { |
142 | | - // FIXME hack to solve #bug 1487 |
143 | | - $wgOut->addHTML( '<li>'.wfMsgHtml( 'ipblocklistempty' ).'</li>' ); |
| 170 | + $conds = array(); |
| 171 | + if ( $this->ip == '' ) { |
| 172 | + // No extra conditions |
| 173 | + } elseif ( substr( $this->ip, 0, 1 ) == '#' ) { |
| 174 | + $conds['ipb_id'] = substr( $this->ip, 1 ); |
| 175 | + } elseif ( wfIP2Unsigned( $this->ip ) !== false ) { |
| 176 | + $conds['ipb_address'] = $this->ip; |
| 177 | + $conds['ipb_auto'] = 0; |
| 178 | + } else { |
| 179 | + $user = User::newFromName( $this->ip ); |
| 180 | + if ( ( $id = $user->getID() ) != 0 ) { |
| 181 | + $conds['ipb_user'] = $id; |
| 182 | + } |
144 | 183 | } |
145 | | - $wgOut->addHTML( "</ul>\n" ); |
146 | | - $wgOut->addHTML( $paging ); |
| 184 | + |
| 185 | + $pager = new IPBlocklistPager( $this, $conds ); |
| 186 | + $s = $pager->getNavigationBar() . |
| 187 | + $this->searchForm(); |
| 188 | + if ( $pager->getNumRows() ) { |
| 189 | + $s .= "<ul>" . |
| 190 | + $pager->getBody() . |
| 191 | + "</ul>"; |
| 192 | + } else { |
| 193 | + $s .= '<p>' . wfMsgHTML( 'ipblocklistempty' ) . '</p>'; |
| 194 | + } |
| 195 | + $s .= $pager->getNavigationBar(); |
| 196 | + $wgOut->addHTML( $s ); |
147 | 197 | } |
148 | 198 | |
149 | 199 | function searchForm() { |
150 | | - global $wgTitle; |
| 200 | + global $wgTitle, $wgRequest; |
151 | 201 | return |
152 | 202 | wfElement( 'form', array( |
153 | 203 | 'action' => $wgTitle->getLocalUrl() ), |
— | — | @@ -158,7 +208,7 @@ |
159 | 209 | wfElement( 'input', array( |
160 | 210 | 'type' => 'hidden', |
161 | 211 | 'name' => 'limit', |
162 | | - 'value' => $this->limit ) ). |
| 212 | + 'value' => $wgRequest->getText( 'limit' ) ) ) . |
163 | 213 | wfElement( 'input', array( |
164 | 214 | 'name' => 'ip', |
165 | 215 | 'value' => $this->ip ) ) . |
— | — | @@ -171,48 +221,26 @@ |
172 | 222 | /** |
173 | 223 | * Callback function to output a block |
174 | 224 | */ |
175 | | - function addRow( $block, $tag ) { |
176 | | - global $wgOut, $wgUser, $wgLang; |
| 225 | + function formatRow( $block ) { |
| 226 | + global $wgUser, $wgLang; |
177 | 227 | |
178 | | - if( $this->ip != '' ) { |
179 | | - if( $block->mAuto ) { |
180 | | - if( stristr( $block->mId, $this->ip ) == false ) { |
181 | | - return; |
182 | | - } |
183 | | - } else { |
184 | | - if( stristr( $block->mAddress, $this->ip ) == false ) { |
185 | | - return; |
186 | | - } |
187 | | - } |
188 | | - } |
| 228 | + wfProfileIn( __METHOD__ ); |
189 | 229 | |
190 | | - // Loading blocks is fast; displaying them is slow. |
191 | | - // Quick hack for paging. |
192 | | - $this->counter++; |
193 | | - if( $this->counter <= $this->offset ) { |
194 | | - return; |
195 | | - } |
196 | | - if( $this->counter - $this->offset > $this->limit ) { |
197 | | - return; |
198 | | - } |
199 | | - |
200 | | - $fname = 'IPUnblockForm-addRow'; |
201 | | - wfProfileIn( $fname ); |
202 | | - |
203 | 230 | static $sk=null, $msg=null; |
204 | 231 | |
205 | 232 | if( is_null( $sk ) ) |
206 | 233 | $sk = $wgUser->getSkin(); |
207 | 234 | if( is_null( $msg ) ) { |
208 | 235 | $msg = array(); |
209 | | - foreach( array( 'infiniteblock', 'expiringblock', 'contribslink', 'unblocklink' ) as $key ) { |
| 236 | + $keys = array( 'infiniteblock', 'expiringblock', 'contribslink', 'unblocklink', |
| 237 | + 'anononlyblock', 'createaccountblock' ); |
| 238 | + foreach( $keys as $key ) { |
210 | 239 | $msg[$key] = wfMsgHtml( $key ); |
211 | 240 | } |
212 | 241 | $msg['blocklistline'] = wfMsg( 'blocklistline' ); |
213 | 242 | $msg['contribslink'] = wfMsg( 'contribslink' ); |
214 | 243 | } |
215 | 244 | |
216 | | - |
217 | 245 | # Prepare links to the blocker's user and talk pages |
218 | 246 | $blocker_name = $block->getByName(); |
219 | 247 | $blocker = $sk->MakeLinkObj( Title::makeTitle( NS_USER, $blocker_name ), $blocker_name ); |
— | — | @@ -220,36 +248,102 @@ |
221 | 249 | |
222 | 250 | # Prepare links to the block target's user and contribs. pages (as applicable, don't do it for autoblocks) |
223 | 251 | if( $block->mAuto ) { |
224 | | - $target = '#' . $block->mId; # Hide the IP addresses of auto-blocks; privacy |
| 252 | + $target = $block->getRedactedName(); # Hide the IP addresses of auto-blocks; privacy |
225 | 253 | } else { |
226 | 254 | $target = $sk->makeLinkObj( Title::makeTitle( NS_USER, $block->mAddress ), $block->mAddress ); |
227 | 255 | $target .= ' (' . $sk->makeKnownLinkObj( Title::makeTitle( NS_SPECIAL, 'Contributions' ), $msg['contribslink'], 'target=' . urlencode( $block->mAddress ) ) . ')'; |
228 | 256 | } |
229 | 257 | |
230 | | - # Prep the address for the unblock link, masking autoblocks as before |
231 | | - $addr = $block->mAuto ? '#' . $block->mId : $block->mAddress; |
232 | | - |
233 | 258 | $formattedTime = $wgLang->timeanddate( $block->mTimestamp, true ); |
234 | 259 | |
235 | | - if ( $block->mExpiry === "" ) { |
236 | | - $formattedExpiry = $msg['infiniteblock']; |
| 260 | + $properties = array(); |
| 261 | + if ( $block->mExpiry === "" || $block->mExpiry === Block::infinity() ) { |
| 262 | + $properties[] = $msg['infiniteblock']; |
237 | 263 | } else { |
238 | | - $formattedExpiry = wfMsgReplaceArgs( $msg['expiringblock'], |
| 264 | + $properties[] = wfMsgReplaceArgs( $msg['expiringblock'], |
239 | 265 | array( $wgLang->timeanddate( $block->mExpiry, true ) ) ); |
240 | 266 | } |
| 267 | + if ( $block->mAnonOnly ) { |
| 268 | + $properties[] = $msg['anononlyblock']; |
| 269 | + } |
| 270 | + if ( $block->mCreateAccount ) { |
| 271 | + $properties[] = $msg['createaccountblock']; |
| 272 | + } |
| 273 | + $properties = implode( ', ', $properties ); |
241 | 274 | |
242 | | - $line = wfMsgReplaceArgs( $msg['blocklistline'], array( $formattedTime, $blocker, $target, $formattedExpiry ) ); |
| 275 | + $line = wfMsgReplaceArgs( $msg['blocklistline'], array( $formattedTime, $blocker, $target, $properties ) ); |
243 | 276 | |
244 | | - $wgOut->addHTML( "<li>{$line}" ); |
| 277 | + $s = "<li>{$line}"; |
245 | 278 | |
246 | 279 | if ( $wgUser->isAllowed('block') ) { |
247 | 280 | $titleObj = Title::makeTitle( NS_SPECIAL, "Ipblocklist" ); |
248 | | - $wgOut->addHTML( ' (' . $sk->makeKnownLinkObj($titleObj, $msg['unblocklink'], 'action=unblock&ip=' . urlencode( $addr ) ) . ')' ); |
| 281 | + $s .= ' (' . $sk->makeKnownLinkObj($titleObj, $msg['unblocklink'], 'action=unblock&id=' . urlencode( $block->mId ) ) . ')'; |
249 | 282 | } |
250 | | - $wgOut->addHTML( $sk->commentBlock( $block->mReason ) ); |
251 | | - $wgOut->addHTML( "</li>\n" ); |
252 | | - wfProfileOut( $fname ); |
| 283 | + $s .= $sk->commentBlock( $block->mReason ); |
| 284 | + $s .= "</li>\n"; |
| 285 | + wfProfileOut( __METHOD__ ); |
| 286 | + return $s; |
253 | 287 | } |
254 | 288 | } |
255 | 289 | |
| 290 | +class IPBlocklistPager extends ReverseChronologicalPager { |
| 291 | + public $mForm, $mConds; |
| 292 | + |
| 293 | + function __construct( $form, $conds = array() ) { |
| 294 | + $this->mForm = $form; |
| 295 | + $this->mConds = $conds; |
| 296 | + parent::__construct(); |
| 297 | + } |
| 298 | + |
| 299 | + function getStartBody() { |
| 300 | + wfProfileIn( __METHOD__ ); |
| 301 | + # Do a link batch query |
| 302 | + $this->mResult->seek( 0 ); |
| 303 | + $lb = new LinkBatch; |
| 304 | + |
| 305 | + /* |
| 306 | + while ( $row = $this->mResult->fetchObject() ) { |
| 307 | + $lb->addObj( Title::makeTitleSafe( NS_USER, $row->user_name ) ); |
| 308 | + $lb->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->user_name ) ); |
| 309 | + $lb->addObj( Title::makeTitleSafe( NS_USER, $row->ipb_address ) ); |
| 310 | + $lb->addObj( Title::makeTitleSafe( NS_USER_TALK, $row->ipb_address ) ); |
| 311 | + }*/ |
| 312 | + # Faster way |
| 313 | + # Usernames and titles are in fact related by a simple substitution of space -> underscore |
| 314 | + # The last few lines of Title::secureAndSplit() tell the story. |
| 315 | + while ( $row = $this->mResult->fetchObject() ) { |
| 316 | + $name = str_replace( ' ', '_', $row->user_name ); |
| 317 | + $lb->add( NS_USER, $name ); |
| 318 | + $lb->add( NS_USER_TALK, $name ); |
| 319 | + $name = str_replace( ' ', '_', $row->ipb_address ); |
| 320 | + $lb->add( NS_USER, $name ); |
| 321 | + $lb->add( NS_USER_TALK, $name ); |
| 322 | + } |
| 323 | + $lb->execute(); |
| 324 | + wfProfileOut( __METHOD__ ); |
| 325 | + return ''; |
| 326 | + } |
| 327 | + |
| 328 | + function formatRow( $row ) { |
| 329 | + $block = new Block; |
| 330 | + $block->initFromRow( $row ); |
| 331 | + return $this->mForm->formatRow( $block ); |
| 332 | + } |
| 333 | + |
| 334 | + function getQueryInfo() { |
| 335 | + $conds = $this->mConds; |
| 336 | + $conds[] = 'ipb_expiry>' . $this->mDb->addQuotes( $this->mDb->timestamp() ); |
| 337 | + $conds[] = 'ipb_by=user_id'; |
| 338 | + return array( |
| 339 | + 'tables' => array( 'ipblocks', 'user' ), |
| 340 | + 'fields' => 'ipblocks.*,user_name', |
| 341 | + 'conds' => $conds, |
| 342 | + ); |
| 343 | + } |
| 344 | + |
| 345 | + function getIndexField() { |
| 346 | + return 'ipb_timestamp'; |
| 347 | + } |
| 348 | +} |
| 349 | + |
256 | 350 | ?> |
Index: branches/snapshot-work/includes/Block.php |
— | — | @@ -9,7 +9,6 @@ |
10 | 10 | * All the functions in this class assume the object is either explicitly |
11 | 11 | * loaded or filled. It is not load-on-demand. There are no accessors. |
12 | 12 | * |
13 | | - * To use delete(), you only need to fill $mAddress |
14 | 13 | * Globals used: $wgAutoblockExpiry, $wgAntiLockFlags |
15 | 14 | * |
16 | 15 | * @todo This could be used everywhere, but it isn't. |
— | — | @@ -18,7 +17,7 @@ |
19 | 18 | class Block |
20 | 19 | { |
21 | 20 | /* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry, |
22 | | - $mRangeStart, $mRangeEnd; |
| 21 | + $mRangeStart, $mRangeEnd, $mAnonOnly; |
23 | 22 | /* private */ var $mNetworkBits, $mIntegerAddr, $mForUpdate, $mFromMaster, $mByName; |
24 | 23 | |
25 | 24 | const EB_KEEP_EXPIRED = 1; |
— | — | @@ -26,19 +25,18 @@ |
27 | 26 | const EB_RANGE_ONLY = 4; |
28 | 27 | |
29 | 28 | function Block( $address = '', $user = '', $by = 0, $reason = '', |
30 | | - $timestamp = '' , $auto = 0, $expiry = '' ) |
| 29 | + $timestamp = '' , $auto = 0, $expiry = '', $anonOnly = 0, $createAccount = 0 ) |
31 | 30 | { |
| 31 | + $this->mId = 0; |
32 | 32 | $this->mAddress = $address; |
33 | 33 | $this->mUser = $user; |
34 | 34 | $this->mBy = $by; |
35 | 35 | $this->mReason = $reason; |
36 | 36 | $this->mTimestamp = wfTimestamp(TS_MW,$timestamp); |
37 | 37 | $this->mAuto = $auto; |
38 | | - if( empty( $expiry ) ) { |
39 | | - $this->mExpiry = $expiry; |
40 | | - } else { |
41 | | - $this->mExpiry = wfTimestamp( TS_MW, $expiry ); |
42 | | - } |
| 38 | + $this->mAnonOnly = $anonOnly; |
| 39 | + $this->mCreateAccount = $createAccount; |
| 40 | + $this->mExpiry = self::decodeExpiry( $expiry ); |
43 | 41 | |
44 | 42 | $this->mForUpdate = false; |
45 | 43 | $this->mFromMaster = false; |
— | — | @@ -46,19 +44,36 @@ |
47 | 45 | $this->initialiseRange(); |
48 | 46 | } |
49 | 47 | |
50 | | - /*static*/ function newFromDB( $address, $user = 0, $killExpired = true ) |
| 48 | + static function newFromDB( $address, $user = 0, $killExpired = true ) |
51 | 49 | { |
52 | | - $ban = new Block(); |
53 | | - $ban->load( $address, $user, $killExpired ); |
54 | | - return $ban; |
| 50 | + $block = new Block(); |
| 51 | + $block->load( $address, $user, $killExpired ); |
| 52 | + if ( $block->isValid() ) { |
| 53 | + return $block; |
| 54 | + } else { |
| 55 | + return null; |
| 56 | + } |
55 | 57 | } |
56 | 58 | |
| 59 | + static function newFromID( $id ) |
| 60 | + { |
| 61 | + $dbr =& wfGetDB( DB_SLAVE ); |
| 62 | + $res = $dbr->resultObject( $dbr->select( 'ipblocks', '*', |
| 63 | + array( 'ipb_id' => $id ), __METHOD__ ) ); |
| 64 | + $block = new Block; |
| 65 | + if ( $block->loadFromResult( $res ) ) { |
| 66 | + return $block; |
| 67 | + } else { |
| 68 | + return null; |
| 69 | + } |
| 70 | + } |
| 71 | + |
57 | 72 | function clear() |
58 | 73 | { |
59 | 74 | $this->mAddress = $this->mReason = $this->mTimestamp = ''; |
60 | | - $this->mUser = $this->mBy = 0; |
| 75 | + $this->mId = $this->mAnonOnly = $this->mCreateAccount = |
| 76 | + $this->mAuto = $this->mUser = $this->mBy = 0; |
61 | 77 | $this->mByName = false; |
62 | | - |
63 | 78 | } |
64 | 79 | |
65 | 80 | /** |
— | — | @@ -70,56 +85,80 @@ |
71 | 86 | if ( $this->mForUpdate || $this->mFromMaster ) { |
72 | 87 | $db =& wfGetDB( DB_MASTER ); |
73 | 88 | if ( !$this->mForUpdate || ($wgAntiLockFlags & ALF_NO_BLOCK_LOCK) ) { |
74 | | - $options = ''; |
| 89 | + $options = array(); |
75 | 90 | } else { |
76 | | - $options = 'FOR UPDATE'; |
| 91 | + $options = array( 'FOR UPDATE' ); |
77 | 92 | } |
78 | 93 | } else { |
79 | 94 | $db =& wfGetDB( DB_SLAVE ); |
80 | | - $options = ''; |
| 95 | + $options = array(); |
81 | 96 | } |
82 | 97 | return $db; |
83 | 98 | } |
84 | 99 | |
85 | 100 | /** |
86 | 101 | * Get a ban from the DB, with either the given address or the given username |
| 102 | + * |
| 103 | + * @param string $address The IP address of the user, or blank to skip IP blocks |
| 104 | + * @param integer $user The user ID, or zero for anonymous users |
| 105 | + * @param bool $killExpired Whether to delete expired rows while loading |
| 106 | + * |
87 | 107 | */ |
88 | 108 | function load( $address = '', $user = 0, $killExpired = true ) |
89 | 109 | { |
90 | | - $fname = 'Block::load'; |
91 | 110 | wfDebug( "Block::load: '$address', '$user', $killExpired\n" ); |
92 | 111 | |
93 | | - $options = ''; |
| 112 | + $options = array(); |
94 | 113 | $db =& $this->getDBOptions( $options ); |
95 | 114 | |
96 | 115 | $ret = false; |
97 | 116 | $killed = false; |
98 | | - $ipblocks = $db->tableName( 'ipblocks' ); |
99 | 117 | |
100 | 118 | if ( 0 == $user && $address == '' ) { |
101 | 119 | # Invalid user specification, not blocked |
102 | 120 | $this->clear(); |
103 | 121 | return false; |
104 | | - } elseif ( $address == '' ) { |
105 | | - $sql = "SELECT * FROM $ipblocks WHERE ipb_user={$user} $options"; |
106 | | - } elseif ( $user == '' ) { |
107 | | - $sql = "SELECT * FROM $ipblocks WHERE ipb_address=" . $db->addQuotes( $address ) . " $options"; |
108 | | - } elseif ( $options == '' ) { |
109 | | - # If there are no options (e.g. FOR UPDATE), use a UNION |
110 | | - # so that the query can make efficient use of indices |
111 | | - $sql = "SELECT * FROM $ipblocks WHERE ipb_address='" . $db->strencode( $address ) . |
112 | | - "' UNION SELECT * FROM $ipblocks WHERE ipb_user={$user}"; |
113 | | - } else { |
114 | | - # If there are options, a UNION can not be used, use one |
115 | | - # SELECT instead. Will do a full table scan. |
116 | | - $sql = "SELECT * FROM $ipblocks WHERE (ipb_address='" . $db->strencode( $address ) . |
117 | | - "' OR ipb_user={$user}) $options"; |
118 | 122 | } |
119 | 123 | |
120 | | - $res = $db->query( $sql, $fname ); |
121 | | - if ( 0 != $db->numRows( $res ) ) { |
| 124 | + # Try user block |
| 125 | + if ( $user ) { |
| 126 | + $res = $db->resultObject( $db->select( 'ipblocks', '*', array( 'ipb_user' => $user ), |
| 127 | + __METHOD__, $options ) ); |
| 128 | + if ( $this->loadFromResult( $res, $killExpired ) ) { |
| 129 | + return true; |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + # Try IP block |
| 134 | + if ( $address ) { |
| 135 | + $conds = array( 'ipb_address' => $address ); |
| 136 | + if ( $user ) { |
| 137 | + $conds['ipb_anon_only'] = 0; |
| 138 | + } |
| 139 | + $res = $db->resultObject( $db->select( 'ipblocks', '*', $conds, __METHOD__, $options ) ); |
| 140 | + if ( $this->loadFromResult( $res, $killExpired ) ) { |
| 141 | + return true; |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + # Try range block |
| 146 | + if ( $this->loadRange( $address, $killExpired, $user == 0 ) ) { |
| 147 | + return true; |
| 148 | + } |
| 149 | + |
| 150 | + # Give up |
| 151 | + $this->clear(); |
| 152 | + return false; |
| 153 | + } |
| 154 | + |
| 155 | + /** |
| 156 | + * Fill in member variables from a result wrapper |
| 157 | + */ |
| 158 | + function loadFromResult( ResultWrapper $res, $killExpired = true ) { |
| 159 | + $ret = false; |
| 160 | + if ( 0 != $res->numRows() ) { |
122 | 161 | # Get first block |
123 | | - $row = $db->fetchObject( $res ); |
| 162 | + $row = $res->fetchObject(); |
124 | 163 | $this->initFromRow( $row ); |
125 | 164 | |
126 | 165 | if ( $killExpired ) { |
— | — | @@ -127,7 +166,7 @@ |
128 | 167 | do { |
129 | 168 | $killed = $this->deleteIfExpired(); |
130 | 169 | if ( $killed ) { |
131 | | - $row = $db->fetchObject( $res ); |
| 170 | + $row = $res->fetchObject(); |
132 | 171 | if ( $row ) { |
133 | 172 | $this->initFromRow( $row ); |
134 | 173 | } |
— | — | @@ -135,26 +174,14 @@ |
136 | 175 | } while ( $killed && $row ); |
137 | 176 | |
138 | 177 | # If there were any left after the killing finished, return true |
139 | | - if ( !$row ) { |
140 | | - $ret = false; |
141 | | - $this->clear(); |
142 | | - } else { |
| 178 | + if ( $row ) { |
143 | 179 | $ret = true; |
144 | 180 | } |
145 | 181 | } else { |
146 | 182 | $ret = true; |
147 | 183 | } |
148 | 184 | } |
149 | | - $db->freeResult( $res ); |
150 | | - |
151 | | - # No blocks found yet? Try looking for range blocks |
152 | | - if ( !$ret && $address != '' ) { |
153 | | - $ret = $this->loadRange( $address, $killExpired ); |
154 | | - } |
155 | | - if ( !$ret ) { |
156 | | - $this->clear(); |
157 | | - } |
158 | | - |
| 185 | + $res->free(); |
159 | 186 | return $ret; |
160 | 187 | } |
161 | 188 | |
— | — | @@ -162,10 +189,8 @@ |
163 | 190 | * Search the database for any range blocks matching the given address, and |
164 | 191 | * load the row if one is found. |
165 | 192 | */ |
166 | | - function loadRange( $address, $killExpired = true ) |
| 193 | + function loadRange( $address, $killExpired = true, $isAnon = true ) |
167 | 194 | { |
168 | | - $fname = 'Block::loadRange'; |
169 | | - |
170 | 195 | $iaddr = wfIP2Hex( $address ); |
171 | 196 | if ( $iaddr === false ) { |
172 | 197 | # Invalid address |
— | — | @@ -176,27 +201,19 @@ |
177 | 202 | # Blocks should not cross a /16 boundary. |
178 | 203 | $range = substr( $iaddr, 0, 4 ); |
179 | 204 | |
180 | | - $options = ''; |
| 205 | + $options = array(); |
181 | 206 | $db =& $this->getDBOptions( $options ); |
182 | | - $ipblocks = $db->tableName( 'ipblocks' ); |
183 | | - $sql = "SELECT * FROM $ipblocks WHERE ipb_range_start LIKE '$range%' ". |
184 | | - "AND ipb_range_start <= '$iaddr' AND ipb_range_end >= '$iaddr' $options"; |
185 | | - $res = $db->query( $sql, $fname ); |
186 | | - $row = $db->fetchObject( $res ); |
187 | | - |
188 | | - $success = false; |
189 | | - if ( $row ) { |
190 | | - # Found a row, initialise this object |
191 | | - $this->initFromRow( $row ); |
192 | | - |
193 | | - # Is it expired? |
194 | | - if ( !$killExpired || !$this->deleteIfExpired() ) { |
195 | | - # No, return true |
196 | | - $success = true; |
197 | | - } |
| 207 | + $conds = array( |
| 208 | + "ipb_range_start LIKE '$range%'", |
| 209 | + "ipb_range_start <= '$iaddr'", |
| 210 | + "ipb_range_end >= '$iaddr'" |
| 211 | + ); |
| 212 | + if ( !$isAnon ) { |
| 213 | + $conds['ipb_anon_only'] = 0; |
198 | 214 | } |
199 | 215 | |
200 | | - $db->freeResult( $res ); |
| 216 | + $res = $db->resultObject( $db->select( 'ipblocks', '*', $conds, __METHOD__, $options ) ); |
| 217 | + $success = $this->loadFromResult( $res, $killExpired ); |
201 | 218 | return $success; |
202 | 219 | } |
203 | 220 | |
— | — | @@ -220,10 +237,10 @@ |
221 | 238 | $this->mUser = $row->ipb_user; |
222 | 239 | $this->mBy = $row->ipb_by; |
223 | 240 | $this->mAuto = $row->ipb_auto; |
| 241 | + $this->mAnonOnly = $row->ipb_anon_only; |
| 242 | + $this->mCreateAccount = $row->ipb_create_account; |
224 | 243 | $this->mId = $row->ipb_id; |
225 | | - $this->mExpiry = $row->ipb_expiry ? |
226 | | - wfTimestamp(TS_MW,$row->ipb_expiry) : |
227 | | - $row->ipb_expiry; |
| 244 | + $this->mExpiry = self::decodeExpiry( $row->ipb_expiry ); |
228 | 245 | if ( isset( $row->user_name ) ) { |
229 | 246 | $this->mByName = $row->user_name; |
230 | 247 | } else { |
— | — | @@ -304,24 +321,27 @@ |
305 | 322 | |
306 | 323 | function delete() |
307 | 324 | { |
308 | | - $fname = 'Block::delete'; |
309 | 325 | if (wfReadOnly()) { |
310 | | - return; |
| 326 | + return false; |
311 | 327 | } |
| 328 | + if ( !$this->mId ) { |
| 329 | + throw new MWException( "Block::delete() now requires that the mId member be filled\n" ); |
| 330 | + } |
| 331 | + |
312 | 332 | $dbw =& wfGetDB( DB_MASTER ); |
313 | | - |
314 | | - if ( $this->mAddress == '' ) { |
315 | | - $condition = array( 'ipb_id' => $this->mId ); |
316 | | - } else { |
317 | | - $condition = array( 'ipb_address' => $this->mAddress ); |
318 | | - } |
319 | | - return( $dbw->delete( 'ipblocks', $condition, $fname ) > 0 ? true : false ); |
| 333 | + $dbw->delete( 'ipblocks', array( 'ipb_id' => $this->mId ), __METHOD__ ); |
| 334 | + return $dbw->affectedRows() > 0; |
320 | 335 | } |
321 | 336 | |
322 | 337 | function insert() |
323 | 338 | { |
324 | 339 | wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" ); |
325 | 340 | $dbw =& wfGetDB( DB_MASTER ); |
| 341 | + $dbw->begin(); |
| 342 | + |
| 343 | + # Don't collide with expired blocks |
| 344 | + Block::purgeExpired(); |
| 345 | + |
326 | 346 | $ipb_id = $dbw->nextSequenceValue('ipblocks_ipb_id_val'); |
327 | 347 | $dbw->insert( 'ipblocks', |
328 | 348 | array( |
— | — | @@ -332,13 +352,16 @@ |
333 | 353 | 'ipb_reason' => $this->mReason, |
334 | 354 | 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp), |
335 | 355 | 'ipb_auto' => $this->mAuto, |
336 | | - 'ipb_expiry' => $this->mExpiry ? |
337 | | - $dbw->timestamp($this->mExpiry) : |
338 | | - $this->mExpiry, |
| 356 | + 'ipb_anon_only' => $this->mAnonOnly, |
| 357 | + 'ipb_create_account' => $this->mCreateAccount, |
| 358 | + 'ipb_expiry' => self::encodeExpiry( $this->mExpiry, $dbw ), |
339 | 359 | 'ipb_range_start' => $this->mRangeStart, |
340 | 360 | 'ipb_range_end' => $this->mRangeEnd, |
341 | | - ), 'Block::insert' |
| 361 | + ), 'Block::insert', array( 'IGNORE' ) |
342 | 362 | ); |
| 363 | + $affected = $dbw->affectedRows(); |
| 364 | + $dbw->commit(); |
| 365 | + return $affected; |
343 | 366 | } |
344 | 367 | |
345 | 368 | function deleteIfExpired() |
— | — | @@ -417,13 +440,43 @@ |
418 | 441 | return wfSetVar( $this->mFromMaster, $x ); |
419 | 442 | } |
420 | 443 | |
421 | | - /* static */ function getAutoblockExpiry( $timestamp ) |
| 444 | + function getRedactedName() { |
| 445 | + if ( $this->mAuto ) { |
| 446 | + return '#' . $this->mId; |
| 447 | + } else { |
| 448 | + return $this->mAddress; |
| 449 | + } |
| 450 | + } |
| 451 | + |
| 452 | + /** |
| 453 | + * Encode expiry for DB |
| 454 | + */ |
| 455 | + static function encodeExpiry( $expiry, $db ) { |
| 456 | + if ( $expiry == '' || $expiry == Block::infinity() ) { |
| 457 | + return Block::infinity(); |
| 458 | + } else { |
| 459 | + return $db->timestamp( $expiry ); |
| 460 | + } |
| 461 | + } |
| 462 | + |
| 463 | + /** |
| 464 | + * Decode expiry which has come from the DB |
| 465 | + */ |
| 466 | + static function decodeExpiry( $expiry ) { |
| 467 | + if ( $expiry == '' || $expiry == Block::infinity() ) { |
| 468 | + return Block::infinity(); |
| 469 | + } else { |
| 470 | + return wfTimestamp( TS_MW, $expiry ); |
| 471 | + } |
| 472 | + } |
| 473 | + |
| 474 | + static function getAutoblockExpiry( $timestamp ) |
422 | 475 | { |
423 | 476 | global $wgAutoblockExpiry; |
424 | 477 | return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry ); |
425 | 478 | } |
426 | 479 | |
427 | | - /* static */ function normaliseRange( $range ) |
| 480 | + static function normaliseRange( $range ) |
428 | 481 | { |
429 | 482 | $parts = explode( '/', $range ); |
430 | 483 | if ( count( $parts ) == 2 ) { |
— | — | @@ -436,5 +489,28 @@ |
437 | 490 | return $range; |
438 | 491 | } |
439 | 492 | |
| 493 | + /** |
| 494 | + * Purge expired blocks from the ipblocks table |
| 495 | + */ |
| 496 | + static function purgeExpired() { |
| 497 | + $dbw =& wfGetDB( DB_MASTER ); |
| 498 | + $dbw->delete( 'ipblocks', array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), __METHOD__ ); |
| 499 | + } |
| 500 | + |
| 501 | + static function infinity() { |
| 502 | + # This is a special keyword for timestamps in PostgreSQL, and |
| 503 | + # works with CHAR(14) as well because "i" sorts after all numbers. |
| 504 | + return 'infinity'; |
| 505 | + |
| 506 | + /* |
| 507 | + static $infinity; |
| 508 | + if ( !isset( $infinity ) ) { |
| 509 | + $dbr =& wfGetDB( DB_SLAVE ); |
| 510 | + $infinity = $dbr->bigTimestamp(); |
| 511 | + } |
| 512 | + return $infinity; |
| 513 | + */ |
| 514 | + } |
| 515 | + |
440 | 516 | } |
441 | 517 | ?> |
Index: branches/snapshot-work/RELEASE-NOTES |
— | — | @@ -25,10 +25,8 @@ |
26 | 26 | |
27 | 27 | == Major new features == |
28 | 28 | |
29 | | -* None! |
| 29 | +* (bug 550) Allow blocks on anonymous users only. |
30 | 30 | |
31 | | - |
32 | | - |
33 | 31 | == Changes since 1.7 == |
34 | 32 | |
35 | 33 | * (bug 6562) Removed unmaintained ParserXml.php for now |
— | — | @@ -36,6 +34,13 @@ |
37 | 35 | * (bug 6586) Regression in "unblocked" subtitle |
38 | 36 | * Don't put empty-page message into view-source when page text is blank |
39 | 37 | * (bug 6587) Remove redundant "allnonarticles" message |
| 38 | +* Block improvements: Allow blocks on anonymous users only. Optionally allow |
| 39 | + or disallow account creation from blocked IP addresses. Prevent duplicate |
| 40 | + blocks. Fixed the problem of expiry and unblocking erroneously affecting |
| 41 | + multiple blocks. Fixed confusing lack of error message when a blocked user |
| 42 | + attempts to create an account. Fixed inefficiency of Special:Ipblocklist in |
| 43 | + the presence of large numbers of blocks; added indexes and implemented an |
| 44 | + indexed pager. |
40 | 45 | |
41 | 46 | == Languages updated == |
42 | 47 | |
Index: branches/snapshot-work/languages/Messages.php |
— | — | @@ -555,6 +555,10 @@ |
556 | 556 | 'nocreatetitle' => 'Page creation limited', |
557 | 557 | 'nocreatetext' => 'This site has restricted the ability to create new pages. |
558 | 558 | You can go back and edit an existing page, or [[Special:Userlogin|log in or create an account]].', |
| 559 | +'cantcreateaccounttitle' => 'Can\'t create account', |
| 560 | +'cantcreateaccounttext' => 'Account creation from this IP address (<b>$1</b>) has been blocked. |
| 561 | +This is probably due to persistent vandalism from your school or Internet service |
| 562 | +provider. ', |
559 | 563 | |
560 | 564 | # History pages |
561 | 565 | # |
— | — | @@ -1271,6 +1275,8 @@ |
1272 | 1276 | 'ipadressorusername' => 'IP Address or username', |
1273 | 1277 | 'ipbexpiry' => 'Expiry', |
1274 | 1278 | 'ipbreason' => 'Reason', |
| 1279 | +'ipbanononly' => 'Block anonymous users only', |
| 1280 | +'ipbcreateaccount' => 'Prevent account creation', |
1275 | 1281 | 'ipbsubmit' => 'Block this user', |
1276 | 1282 | 'ipbother' => 'Other time', |
1277 | 1283 | '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', |
— | — | @@ -1288,6 +1294,8 @@ |
1289 | 1295 | 'blocklistline' => "$1, $2 blocked $3 ($4)", |
1290 | 1296 | 'infiniteblock' => 'infinite', |
1291 | 1297 | 'expiringblock' => 'expires $1', |
| 1298 | +'anononlyblock' => 'anon. only', |
| 1299 | +'createaccountblock' => 'account creation blocked', |
1292 | 1300 | 'ipblocklistempty' => 'The blocklist is empty.', |
1293 | 1301 | 'blocklink' => 'block', |
1294 | 1302 | 'unblocklink' => 'unblock', |
— | — | @@ -1301,8 +1309,10 @@ |
1302 | 1310 | 'unblocklogentry' => 'unblocked $1', |
1303 | 1311 | 'range_block_disabled' => 'The sysop ability to create range blocks is disabled.', |
1304 | 1312 | 'ipb_expiry_invalid' => 'Expiry time invalid.', |
| 1313 | +'ipb_already_blocked' => '"$1" is already blocked', |
1305 | 1314 | 'ip_range_invalid' => 'Invalid IP range.', |
1306 | 1315 | 'proxyblocker' => 'Proxy blocker', |
| 1316 | +'ipb_cant_unblock' => 'Error: Block ID $1 not found. It may have been unblocked already.', |
1307 | 1317 | 'proxyblockreason' => 'Your IP address has been blocked because it is an open proxy. Please contact your Internet service provider or tech support and inform them of this serious security problem.', |
1308 | 1318 | 'proxyblocksuccess' => 'Done.', |
1309 | 1319 | 'sorbs' => 'SORBS DNSBL', |