Page MenuHomeWickedGov Phorge

GlobalRenameUserStatus.php
No OneTemporary

Size
6 KB
Referenced Files
None
Subscribers
None

GlobalRenameUserStatus.php

<?php
namespace MediaWiki\Extension\CentralAuth\GlobalRename;
use MediaWiki\Extension\CentralAuth\CentralAuthDatabaseManager;
use MediaWiki\Extension\CentralAuth\CentralAuthServices;
use MediaWiki\Extension\CentralAuth\User\CentralAuthUser;
use MediaWiki\Permissions\Authority;
use MediaWiki\WikiMap\WikiMap;
use Wikimedia\Rdbms\DBQueryError;
use Wikimedia\Rdbms\IDBAccessObject;
use Wikimedia\Rdbms\IExpression;
use Wikimedia\Rdbms\IReadableDatabase;
/**
* Status handler for CentralAuth users being renamed.
* This can work based on the new or old user name (can be constructed
* from whatever is available)
*
* @license GPL-2.0-or-later
* @author Marius Hoch < hoo@online.de >
*/
class GlobalRenameUserStatus {
private CentralAuthDatabaseManager $databaseManager;
private string $name;
/**
* @param CentralAuthDatabaseManager $databaseManager
* @param string $name Either old or new name of the user
*/
public function __construct(
CentralAuthDatabaseManager $databaseManager,
string $name
) {
$this->databaseManager = $databaseManager;
$this->name = $name;
}
/**
* Get the where clause to query rows by either old or new name
*
* @param IReadableDatabase $db
* @return IExpression
*/
private function getNameWhereClause( IReadableDatabase $db ): IExpression {
return $db->expr( 'ru_oldname', '=', $this->name )->or( 'ru_newname', '=', $this->name );
}
/**
* Get the old and new name of a user being renamed (or an empty array if
* no rename is happening).
*
* This is useful if we have a user specified name, but don't know
* whether it's the old or new name.
*
* @param string|null $wiki Only look for renames on the given wiki.
* @param int $recency IDBAccessObject flags
*
* @return string[] (oldname, newname)
*/
public function getNames( ?string $wiki = null, int $recency = IDBAccessObject::READ_NORMAL ): array {
$db = $this->databaseManager->getCentralDBFromRecency( $recency );
$where = [ $this->getNameWhereClause( $db ) ];
if ( $wiki ) {
$where['ru_wiki'] = $wiki;
}
$names = $db->newSelectQueryBuilder()
->select( [ 'ru_oldname', 'ru_newname' ] )
->from( 'renameuser_status' )
->where( $where )
->recency( $recency )
->caller( __METHOD__ )
->fetchRow();
if ( !$names ) {
return [];
}
return [
$names->ru_oldname,
$names->ru_newname
];
}
/**
* Get a user's rename status for all wikis.
* Returns an array ( wiki => status )
*
* @param int $recency IDBAccessObject flags
*
* @return string[]
*/
public function getStatuses( int $recency = IDBAccessObject::READ_NORMAL ): array {
$db = $this->databaseManager->getCentralDBFromRecency( $recency );
$res = $db->newSelectQueryBuilder()
->select( [ 'ru_wiki', 'ru_status' ] )
->from( 'renameuser_status' )
->where( [ $this->getNameWhereClause( $db ) ] )
->recency( $recency )
->caller( __METHOD__ )
->fetchResultSet();
$statuses = [];
foreach ( $res as $row ) {
$statuses[$row->ru_wiki] = $row->ru_status;
}
return $statuses;
}
/**
* Get a user's rename status for the current wiki.
*
* @param int $recency IDBAccessObject flags
*
* @return string|null Null means no rename pending for this user on the current wiki (possibly
* because it has finished already).
*/
public function getStatus( int $recency = IDBAccessObject::READ_NORMAL ): ?string {
$statuses = $this->getStatuses( $recency );
return $statuses[WikiMap::getCurrentWikiId()] ?? null;
}
/**
* Set the rename status for a certain wiki
*
* @param string $wiki
* @param string $status
*/
public function updateStatus( $wiki, $status ) {
$dbw = $this->databaseManager->getCentralPrimaryDB();
$fname = __METHOD__;
$dbw->onTransactionPreCommitOrIdle(
function () use ( $dbw, $status, $wiki, $fname ) {
$dbw->newUpdateQueryBuilder()
->update( 'renameuser_status' )
->set( [ 'ru_status' => $status ] )
->where( [ $this->getNameWhereClause( $dbw ), 'ru_wiki' => $wiki ] )
->caller( $fname )
->execute();
},
$fname
);
}
/**
* @param array $rows
*
* @return bool
*/
public function setStatuses( array $rows ): bool {
if ( !$rows ) {
return false;
}
$dbw = $this->databaseManager->getCentralPrimaryDB();
$dbw->startAtomic( __METHOD__ );
if ( $dbw->getType() === 'mysql' ) {
// If there is duplicate key error, the RDBMs will rollback the INSERT statement.
// http://dev.mysql.com/doc/refman/5.7/en/innodb-error-handling.html
try {
$dbw->newInsertQueryBuilder()
->insertInto( 'renameuser_status' )
->rows( $rows )
->caller( __METHOD__ )
->execute();
$ok = true;
} catch ( DBQueryError $e ) {
$ok = false;
}
} else {
// At least Postgres does not like continuing after errors. Only options are
// ROLLBACK or COMMIT as is. We could use SAVEPOINT here, but it's not worth it.
$keyConds = [];
foreach ( $rows as $row ) {
$keyConds[] = $dbw->expr( 'ru_wiki', '=', $row->ru_wiki )
->and( 'ru_oldname', '=', $row->ru_oldname );
}
// (a) Do a locking check for conflicting rows on the unique key
$ok = !$dbw->newSelectQueryBuilder()
->select( '1' )
->from( 'renameuser_status' )
->where( $dbw->orExpr( $keyConds ) )
->forUpdate()
->caller( __METHOD__ )
->fetchField();
// (b) Insert the new rows if no conflicts were found
if ( $ok ) {
$dbw->newInsertQueryBuilder()
->insertInto( 'renameuser_status' )
->rows( $rows )
->caller( __METHOD__ )
->execute();
}
}
$dbw->endAtomic( __METHOD__ );
return $ok;
}
/**
* Mark the process as done for a wiki (=> delete the renameuser_status row)
*
* @param string $wiki
*/
public function done( string $wiki ): void {
$dbw = $this->databaseManager->getCentralPrimaryDB();
$fname = __METHOD__;
$dbw->onTransactionPreCommitOrIdle(
function () use ( $dbw, $wiki, $fname ) {
$dbw->newDeleteQueryBuilder()
->deleteFrom( 'renameuser_status' )
->where( [ $this->getNameWhereClause( $dbw ), 'ru_wiki' => $wiki ] )
->caller( $fname )
->execute();
},
$fname
);
}
/**
* Get a list of all currently in progress renames
*
* @param Authority $performer User viewing the list, for permissions checks
* @return string[] old username => new username
*/
public static function getInProgressRenames( Authority $performer ): array {
$dbr = CentralAuthServices::getDatabaseManager()->getCentralReplicaDB();
$qb = $dbr->newSelectQueryBuilder();
$qb->select( [ 'ru_oldname', 'ru_newname' ] )
->distinct()
->from( 'renameuser_status' );
if ( !$performer->isAllowed( 'centralauth-suppress' ) ) {
$qb->join( 'globaluser', null, 'gu_name=ru_newname' );
$qb->where( [ "gu_hidden_level" => CentralAuthUser::HIDDEN_LEVEL_NONE ] );
}
$res = $qb
->caller( __METHOD__ )
->fetchResultSet();
$ret = [];
foreach ( $res as $row ) {
$ret[$row->ru_oldname] = $row->ru_newname;
}
return $ret;
}
}

File Metadata

Mime Type
text/x-php
Expires
Sat, May 16, 14:08 (1 d, 14 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
57/08/f3d8557611a7edab225110f24ad4
Default Alt Text
GlobalRenameUserStatus.php (6 KB)

Event Timeline