mediawiki-extensions-Centra.../maintenance/deleteEmptyAccounts.php
Umherirrender 24c5c7a346 Use expression builder to avoid some raw sql
Change-Id: Ide3e3b75029e0dc79e6ca6c43d5c3ecb332fc772
2024-04-21 14:14:19 +02:00

172 行
4.8 KiB
PHP

<?php
$IP = getenv( 'MW_INSTALL_PATH' );
if ( $IP === false ) {
$IP = __DIR__ . '/../../..';
}
require_once "$IP/maintenance/Maintenance.php";
use MediaWiki\Extension\CentralAuth\CentralAuthServices;
use MediaWiki\Extension\CentralAuth\User\CentralAuthUser;
use MediaWiki\User\User;
class DeleteEmptyAccounts extends Maintenance {
/** @var bool */
protected $fix;
/** @var bool */
protected $safe;
/** @var bool */
protected $migrate;
/** @var bool */
protected $suppressRC;
public function __construct() {
parent::__construct();
$this->requireExtension( 'CentralAuth' );
$this->addDescription( 'Delete all global accounts with no attached local accounts, ' .
'then attempt to migrate a local account' );
$this->fix = false;
$this->safe = false;
$this->migrate = false;
$this->suppressRC = false;
$this->addOption( 'fix', 'Actually update the database. Otherwise, ' .
'only prints what would be done.', false, false );
$this->addOption( 'migrate', 'Migrate a local account; the winner is picked using ' .
'CentralAuthUser::attemptAutoMigration defaults', false, false );
$this->addOption( 'safe-migrate', 'Migrate a local account, only if all accounts ' .
'can be attached', false, false );
$this->addOption( 'suppressrc', 'Do not send entries to RC feed', false, false );
$this->setBatchSize( 500 );
}
public function execute() {
// phpcs:ignore MediaWiki.Usage.DeprecatedGlobalVariables.Deprecated$wgUser
global $wgUser;
$original = $wgUser;
$user = User::newFromName( User::MAINTENANCE_SCRIPT_USER );
$wgUser = $user;
RequestContext::getMain()->setUser( $user );
$dbr = CentralAuthServices::getDatabaseManager()->getCentralReplicaDB();
if ( $this->getOption( 'fix', false ) !== false ) {
$this->fix = true;
}
if ( $this->getOption( 'safe-migrate', false ) !== false ) {
$this->safe = true;
$this->migrate = true;
}
if ( $this->getOption( 'migrate', false ) !== false ) {
$this->migrate = true;
}
if ( $this->getOption( 'suppressrc', false ) !== false ) {
$this->suppressRC = true;
}
$end = $dbr->newSelectQueryBuilder()
->select( 'MAX(gu_id)' )
->from( 'globaluser' )
->caller( __METHOD__ )
->fetchField();
for ( $cur = 0; $cur <= $end; $cur += $this->mBatchSize ) {
$this->output( "PROGRESS: $cur / $end\n" );
$result = $dbr->newSelectQueryBuilder()
->select( 'gu_name' )
->from( 'globaluser' )
->leftJoin( 'localuser', null, 'gu_name=lu_name' )
->where( [
'lu_name' => null,
$dbr->expr( 'gu_id', '>=', $cur ),
$dbr->expr( 'gu_id', '<', $cur + $this->mBatchSize ),
] )
->orderBy( 'gu_id' )
->caller( __METHOD__ )
->fetchResultSet();
foreach ( $result as $row ) {
$this->process( $row->gu_name, $user );
}
if ( $this->fix ) {
CentralAuthServices::getDatabaseManager()->waitForReplication();
}
}
$this->output( "done.\n" );
// Restore old $wgUser value
$wgUser = $original;
}
/**
* @param string $username
* @param User $deleter
*/
private function process( $username, User $deleter ) {
$central = new CentralAuthUser( $username, IDBAccessObject::READ_LATEST );
if ( !$central->exists() ) {
$this->output(
"ERROR: [$username] Central account does not exist. So how'd we find it?\n"
);
return;
}
try {
$unattached = $central->queryUnattached();
} catch ( Exception $e ) {
// This might happen due to localnames inconsistencies (T69350)
$this->output( "ERROR: [$username] Fetching unattached accounts failed.\n" );
return;
}
foreach ( $unattached as $wiki => $local ) {
if ( $local['email'] === '' && $local['password'] === '' ) {
$this->output( "SKIP: [$username] Account on $wiki has no password or email\n" );
return;
}
}
if ( $this->fix ) {
$reason = wfMessage( 'centralauth-delete-empty-account' )->inContentLanguage()->text();
$status = $central->adminDelete( $reason, $deleter );
if ( !$status->isGood() ) {
$msg = $status->getErrors()[0]['message'];
if ( $msg instanceof Message ) {
$msg = $msg->getKey();
}
$this->output( "ERROR: [$username] Delete failed ($msg)\n" );
return;
}
$this->output( "DELETE: [$username] Deleted\n" );
} else {
$this->output( "DELETE: [$username] Would delete\n" );
}
if ( count( $unattached ) !== 0 && $this->migrate ) {
if ( $this->fix ) {
$central = CentralAuthUser::newUnattached( $username, true );
if ( $central->storeAndMigrate( [], !$this->suppressRC, $this->safe ) ) {
$unattachedAfter = count( $central->queryUnattached() );
$this->output(
"MIGRATE: [$username] Success; $unattachedAfter left unattached\n"
);
} else {
$this->output( "MIGRATE: [$username] Fail\n" );
}
} else {
$this->output( "MIGRATE: [$username] Would attempt\n" );
}
}
}
}
$maintClass = DeleteEmptyAccounts::class;
require_once RUN_MAINTENANCE_IF_MAIN;