Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 86 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
CleanupBlocks | |
0.00% |
0 / 83 |
|
0.00% |
0 / 2 |
272 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 80 |
|
0.00% |
0 / 1 |
240 |
1 | <?php |
2 | /** |
3 | * Cleans up user blocks with user names not matching the 'user' table |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License along |
16 | * with this program; if not, write to the Free Software Foundation, Inc., |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
18 | * http://www.gnu.org/copyleft/gpl.html |
19 | * |
20 | * @file |
21 | * @ingroup Maintenance |
22 | */ |
23 | |
24 | require_once __DIR__ . '/Maintenance.php'; |
25 | |
26 | use MediaWiki\Block\DatabaseBlock; |
27 | |
28 | /** |
29 | * Maintenance script to clean up user blocks with user names not matching the |
30 | * 'user' table. |
31 | * |
32 | * TODO: delete this script after the block_target schema migration is complete |
33 | * |
34 | * @ingroup Maintenance |
35 | */ |
36 | class CleanupBlocks extends Maintenance { |
37 | |
38 | public function __construct() { |
39 | parent::__construct(); |
40 | $this->addDescription( "Cleanup user blocks with user names not matching the 'user' table" ); |
41 | $this->setBatchSize( 1000 ); |
42 | } |
43 | |
44 | public function execute() { |
45 | $db = $this->getPrimaryDB(); |
46 | $blockQuery = DatabaseBlock::getQueryInfo(); |
47 | |
48 | $max = $db->newSelectQueryBuilder() |
49 | ->select( 'MAX(ipb_user)' ) |
50 | ->from( 'ipblocks' ) |
51 | ->caller( __METHOD__ ) |
52 | ->fetchField(); |
53 | |
54 | // Step 1: Clean up any duplicate user blocks |
55 | $batchSize = $this->getBatchSize(); |
56 | for ( $from = 1; $from <= $max; $from += $batchSize ) { |
57 | $to = min( $max, $from + $batchSize - 1 ); |
58 | $this->output( "Cleaning up duplicate ipb_user ($from-$to of $max)\n" ); |
59 | |
60 | $delete = []; |
61 | |
62 | $res = $db->newSelectQueryBuilder() |
63 | ->select( 'ipb_user' ) |
64 | ->from( 'ipblocks' ) |
65 | ->where( [ |
66 | 'ipb_user >= ' . $from, |
67 | 'ipb_user <= ' . (int)$to, |
68 | ] ) |
69 | ->groupBy( 'ipb_user' ) |
70 | ->having( 'COUNT(*) > 1' ) |
71 | ->caller( __METHOD__ ) |
72 | ->fetchResultSet(); |
73 | foreach ( $res as $row ) { |
74 | $bestBlock = null; |
75 | $res2 = $db->newSelectQueryBuilder() |
76 | ->select( $blockQuery['fields'] ) |
77 | ->tables( $blockQuery['tables'] ) |
78 | ->where( [ |
79 | 'ipb_user' => $row->ipb_user, |
80 | ] ) |
81 | ->joinConds( $blockQuery['joins'] ) |
82 | ->caller( __METHOD__ ) |
83 | ->fetchResultSet(); |
84 | foreach ( $res2 as $row2 ) { |
85 | $block = DatabaseBlock::newFromRow( $row2 ); |
86 | if ( !$bestBlock ) { |
87 | $bestBlock = $block; |
88 | continue; |
89 | } |
90 | |
91 | // Find the most-restrictive block. |
92 | $keep = null; |
93 | if ( $keep === null && $block->getExpiry() !== $bestBlock->getExpiry() ) { |
94 | // This works for infinite blocks because 'infinity' > '20141024234513' |
95 | $keep = $block->getExpiry() > $bestBlock->getExpiry(); |
96 | } |
97 | if ( $keep === null ) { |
98 | if ( $block->isCreateAccountBlocked() xor $bestBlock->isCreateAccountBlocked() ) { |
99 | $keep = $block->isCreateAccountBlocked(); |
100 | } elseif ( $block->isEmailBlocked() xor $bestBlock->isEmailBlocked() ) { |
101 | $keep = $block->isEmailBlocked(); |
102 | } elseif ( $block->isUsertalkEditAllowed() xor $bestBlock->isUsertalkEditAllowed() ) { |
103 | $keep = $block->isUsertalkEditAllowed(); |
104 | } |
105 | } |
106 | |
107 | if ( $keep ) { |
108 | $delete[] = $bestBlock->getId(); |
109 | $bestBlock = $block; |
110 | } else { |
111 | $delete[] = $block->getId(); |
112 | } |
113 | } |
114 | } |
115 | |
116 | if ( $delete ) { |
117 | $db->newDeleteQueryBuilder() |
118 | ->deleteFrom( 'ipblocks' ) |
119 | ->where( [ 'ipb_id' => $delete ] ) |
120 | ->caller( __METHOD__ )->execute(); |
121 | } |
122 | } |
123 | |
124 | // Step 2: Update the user name in any blocks where it doesn't match |
125 | for ( $from = 1; $from <= $max; $from += $batchSize ) { |
126 | $to = min( $max, $from + $batchSize - 1 ); |
127 | $this->output( "Cleaning up mismatched user name ($from-$to of $max)\n" ); |
128 | |
129 | $res = $db->newSelectQueryBuilder() |
130 | ->select( [ 'ipb_id', 'user_name' ] ) |
131 | ->tables( [ 'ipblocks', 'user' ] ) |
132 | ->where( [ |
133 | 'ipb_user = user_id', |
134 | "ipb_user >= " . $from, |
135 | "ipb_user <= " . (int)$to, |
136 | 'ipb_address != user_name', |
137 | ] ) |
138 | ->caller( __METHOD__ ) |
139 | ->fetchResultSet(); |
140 | foreach ( $res as $row ) { |
141 | $db->update( |
142 | 'ipblocks', |
143 | [ 'ipb_address' => $row->user_name ], |
144 | [ 'ipb_id' => $row->ipb_id ], |
145 | __METHOD__ |
146 | ); |
147 | } |
148 | } |
149 | |
150 | $this->output( "Done!\n" ); |
151 | } |
152 | } |
153 | |
154 | $maintClass = CleanupBlocks::class; |
155 | require_once RUN_MAINTENANCE_IF_MAIN; |