Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 97 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
DeleteEmptyAccounts | |
0.00% |
0 / 91 |
|
0.00% |
0 / 3 |
506 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 40 |
|
0.00% |
0 / 1 |
72 | |||
process | |
0.00% |
0 / 35 |
|
0.00% |
0 / 1 |
182 |
1 | <?php |
2 | |
3 | $IP = getenv( 'MW_INSTALL_PATH' ); |
4 | if ( $IP === false ) { |
5 | $IP = __DIR__ . '/../../..'; |
6 | } |
7 | require_once "$IP/maintenance/Maintenance.php"; |
8 | |
9 | use MediaWiki\Extension\CentralAuth\CentralAuthServices; |
10 | use MediaWiki\Extension\CentralAuth\User\CentralAuthUser; |
11 | use MediaWiki\User\User; |
12 | |
13 | class DeleteEmptyAccounts extends Maintenance { |
14 | |
15 | /** @var bool */ |
16 | protected $fix; |
17 | |
18 | /** @var bool */ |
19 | protected $safe; |
20 | |
21 | /** @var bool */ |
22 | protected $migrate; |
23 | |
24 | /** @var bool */ |
25 | protected $suppressRC; |
26 | |
27 | public function __construct() { |
28 | parent::__construct(); |
29 | $this->requireExtension( 'CentralAuth' ); |
30 | $this->addDescription( 'Delete all global accounts with no attached local accounts, ' . |
31 | 'then attempt to migrate a local account' ); |
32 | $this->fix = false; |
33 | $this->safe = false; |
34 | $this->migrate = false; |
35 | $this->suppressRC = false; |
36 | |
37 | $this->addOption( 'fix', 'Actually update the database. Otherwise, ' . |
38 | 'only prints what would be done.', false, false ); |
39 | $this->addOption( 'migrate', 'Migrate a local account; the winner is picked using ' . |
40 | 'CentralAuthUser::attemptAutoMigration defaults', false, false ); |
41 | $this->addOption( 'safe-migrate', 'Migrate a local account, only if all accounts ' . |
42 | 'can be attached', false, false ); |
43 | $this->addOption( 'suppressrc', 'Do not send entries to RC feed', false, false ); |
44 | $this->setBatchSize( 500 ); |
45 | } |
46 | |
47 | public function execute() { |
48 | // phpcs:ignore MediaWiki.Usage.DeprecatedGlobalVariables.Deprecated$wgUser |
49 | global $wgUser; |
50 | |
51 | $original = $wgUser; |
52 | |
53 | $user = User::newFromName( User::MAINTENANCE_SCRIPT_USER ); |
54 | $wgUser = $user; |
55 | RequestContext::getMain()->setUser( $user ); |
56 | |
57 | $dbr = CentralAuthServices::getDatabaseManager()->getCentralReplicaDB(); |
58 | |
59 | if ( $this->getOption( 'fix', false ) !== false ) { |
60 | $this->fix = true; |
61 | } |
62 | if ( $this->getOption( 'safe-migrate', false ) !== false ) { |
63 | $this->safe = true; |
64 | $this->migrate = true; |
65 | } |
66 | if ( $this->getOption( 'migrate', false ) !== false ) { |
67 | $this->migrate = true; |
68 | } |
69 | if ( $this->getOption( 'suppressrc', false ) !== false ) { |
70 | $this->suppressRC = true; |
71 | } |
72 | |
73 | $end = $dbr->newSelectQueryBuilder() |
74 | ->select( 'MAX(gu_id)' ) |
75 | ->from( 'globaluser' ) |
76 | ->caller( __METHOD__ ) |
77 | ->fetchField(); |
78 | |
79 | for ( $cur = 0; $cur <= $end; $cur += $this->mBatchSize ) { |
80 | $this->output( "PROGRESS: $cur / $end\n" ); |
81 | $result = $dbr->newSelectQueryBuilder() |
82 | ->select( 'gu_name' ) |
83 | ->from( 'globaluser' ) |
84 | ->leftJoin( 'localuser', null, 'gu_name=lu_name' ) |
85 | ->where( [ |
86 | 'lu_name' => null, |
87 | $dbr->expr( 'gu_id', '>=', $cur ), |
88 | $dbr->expr( 'gu_id', '<', $cur + $this->mBatchSize ), |
89 | ] ) |
90 | ->orderBy( 'gu_id' ) |
91 | ->caller( __METHOD__ ) |
92 | ->fetchResultSet(); |
93 | |
94 | foreach ( $result as $row ) { |
95 | $this->process( $row->gu_name, $user ); |
96 | } |
97 | if ( $this->fix ) { |
98 | CentralAuthServices::getDatabaseManager()->waitForReplication(); |
99 | } |
100 | } |
101 | |
102 | $this->output( "done.\n" ); |
103 | |
104 | // Restore old $wgUser value |
105 | $wgUser = $original; |
106 | } |
107 | |
108 | /** |
109 | * @param string $username |
110 | * @param User $deleter |
111 | */ |
112 | private function process( $username, User $deleter ) { |
113 | $central = new CentralAuthUser( $username, IDBAccessObject::READ_LATEST ); |
114 | if ( !$central->exists() ) { |
115 | $this->output( |
116 | "ERROR: [$username] Central account does not exist. So how'd we find it?\n" |
117 | ); |
118 | return; |
119 | } |
120 | |
121 | try { |
122 | $unattached = $central->queryUnattached(); |
123 | } catch ( Exception $e ) { |
124 | // This might happen due to localnames inconsistencies (T69350) |
125 | $this->output( "ERROR: [$username] Fetching unattached accounts failed.\n" ); |
126 | return; |
127 | } |
128 | |
129 | foreach ( $unattached as $wiki => $local ) { |
130 | if ( $local['email'] === '' && $local['password'] === '' ) { |
131 | $this->output( "SKIP: [$username] Account on $wiki has no password or email\n" ); |
132 | return; |
133 | } |
134 | } |
135 | |
136 | if ( $this->fix ) { |
137 | $reason = wfMessage( 'centralauth-delete-empty-account' )->inContentLanguage()->text(); |
138 | $status = $central->adminDelete( $reason, $deleter ); |
139 | if ( !$status->isGood() ) { |
140 | $msg = $status->getErrors()[0]['message']; |
141 | if ( $msg instanceof Message ) { |
142 | $msg = $msg->getKey(); |
143 | } |
144 | $this->output( "ERROR: [$username] Delete failed ($msg)\n" ); |
145 | return; |
146 | } |
147 | $this->output( "DELETE: [$username] Deleted\n" ); |
148 | } else { |
149 | $this->output( "DELETE: [$username] Would delete\n" ); |
150 | } |
151 | |
152 | if ( count( $unattached ) !== 0 && $this->migrate ) { |
153 | if ( $this->fix ) { |
154 | $central = CentralAuthUser::newUnattached( $username, true ); |
155 | if ( $central->storeAndMigrate( [], !$this->suppressRC, $this->safe ) ) { |
156 | $unattachedAfter = count( $central->queryUnattached() ); |
157 | $this->output( |
158 | "MIGRATE: [$username] Success; $unattachedAfter left unattached\n" |
159 | ); |
160 | } else { |
161 | $this->output( "MIGRATE: [$username] Fail\n" ); |
162 | } |
163 | } else { |
164 | $this->output( "MIGRATE: [$username] Would attempt\n" ); |
165 | } |
166 | } |
167 | } |
168 | } |
169 | |
170 | $maintClass = DeleteEmptyAccounts::class; |
171 | require_once RUN_MAINTENANCE_IF_MAIN; |