summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'UserMerge/includes/MergeUser.php')
-rw-r--r--UserMerge/includes/MergeUser.php215
1 files changed, 116 insertions, 99 deletions
diff --git a/UserMerge/includes/MergeUser.php b/UserMerge/includes/MergeUser.php
index 6a959c2f..07e3b7a4 100644
--- a/UserMerge/includes/MergeUser.php
+++ b/UserMerge/includes/MergeUser.php
@@ -1,10 +1,11 @@
<?php
+
+use MediaWiki\Block\DatabaseBlock;
use MediaWiki\MediaWikiServices;
use Wikimedia\Rdbms\IDatabase;
/**
* Contains the actual database backend logic for merging users
- *
*/
class MergeUser {
/**
@@ -103,19 +104,21 @@ class MergeUser {
/**
* @param IDatabase $dbw
* @return void
- * @suppress PhanTypeMismatchArgument Phan thinks that $newBlock and $oldBlock are both null when
- * Block::newFromRow is called, although the previous if/elseif returns if any of them is null.
*/
private function mergeBlocks( IDatabase $dbw ) {
$dbw->startAtomic( __METHOD__ );
// Pull blocks directly from master
+ $qi = DatabaseBlock::getQueryInfo();
$rows = $dbw->select(
- 'ipblocks',
- '*',
+ $qi['tables'],
+ array_merge( $qi['fields'], [ 'ipb_user' ] ),
[
'ipb_user' => [ $this->oldUser->getId(), $this->newUser->getId() ],
- ]
+ ],
+ __METHOD__,
+ [],
+ $qi['joins']
);
$newBlock = null;
@@ -128,15 +131,13 @@ class MergeUser {
}
}
- if ( !$newBlock && !$oldBlock ) {
- // No one is blocked, yaaay
- $dbw->endAtomic( __METHOD__ );
- return;
- } elseif ( $newBlock && !$oldBlock ) {
+ if ( !$oldBlock ) {
+ // No one is blocked or
// Only the new user is blocked, so nothing to do.
$dbw->endAtomic( __METHOD__ );
return;
- } elseif ( $oldBlock && !$newBlock ) {
+ }
+ if ( !$newBlock ) {
// Just move the old block to the new username
$dbw->update(
'ipblocks',
@@ -150,8 +151,8 @@ class MergeUser {
// Okay, let's pick the "strongest" block, and re-apply it to
// the new user.
- $oldBlockObj = Block::newFromRow( $oldBlock );
- $newBlockObj = Block::newFromRow( $newBlock );
+ $oldBlockObj = DatabaseBlock::newFromRow( $oldBlock );
+ $newBlockObj = DatabaseBlock::newFromRow( $newBlock );
$winner = $this->chooseBlock( $oldBlockObj, $newBlockObj );
if ( $winner->getId() === $newBlockObj->getId() ) {
@@ -170,11 +171,11 @@ class MergeUser {
}
/**
- * @param Block $b1
- * @param Block $b2
- * @return Block
+ * @param DatabaseBlock $b1
+ * @param DatabaseBlock $b2
+ * @return DatabaseBlock
*/
- private function chooseBlock( Block $b1, Block $b2 ) {
+ private function chooseBlock( DatabaseBlock $b1, DatabaseBlock $b2 ) {
// First, see if one is longer than the other.
if ( $b1->getExpiry() !== $b2->getExpiry() ) {
// This works for infinite blocks because:
@@ -187,12 +188,21 @@ class MergeUser {
}
// Next check what they block, in order
+ $blockProps = [];
+ foreach ( [ $b1, $b2 ] as $block ) {
+ $blockProps[] = [
+ 'block' => $block,
+ 'createaccount' => $block->isCreateAccountBlocked(),
+ 'sendemail' => $block->isEmailBlocked(),
+ 'editownusertalk' => !$block->isUsertalkEditAllowed(),
+ ];
+ }
foreach ( [ 'createaccount', 'sendemail', 'editownusertalk' ] as $action ) {
- if ( $b1->prevents( $action ) xor $b2->prevents( $action ) ) {
- if ( $b1->prevents( $action ) ) {
- return $b1;
+ if ( $blockProps[0][$action] xor $blockProps[1][$action] ) {
+ if ( $blockProps[0][$action] ) {
+ return $blockProps[0]['block'];
} else {
- return $b2;
+ return $blockProps[1]['block'];
}
}
}
@@ -210,7 +220,7 @@ class MergeUser {
}
if ( defined( 'ActorMigration::MIGRATION_STAGE_SCHEMA_COMPAT' ) ) {
- return (bool)( $stage & SCHEMA_COMPAT_WRITE_OLD );
+ return (bool)( (int)$stage & SCHEMA_COMPAT_WRITE_OLD );
} else {
return $stage < MIGRATION_NEW;
}
@@ -240,43 +250,38 @@ class MergeUser {
* @param string $fnameTrxOwner
*/
private function mergeDatabaseTables( $fnameTrxOwner ) {
- global $wgActorTableSchemaMigrationStage;
-
// Fields to update with the format:
// [
// tableName, idField, textField,
// 'batchKey' => unique field, 'options' => array(), 'db' => IDatabase
// 'actorId' => actor ID field,
+ // 'actorStage' => actor schema migration stage
// ];
// textField, batchKey, db, and options are optional
$updateFields = [
[ 'archive', 'ar_user', 'ar_user_text', 'batchKey' => 'ar_id', 'actorId' => 'ar_actor',
- 'actorStage' => $wgActorTableSchemaMigrationStage ],
+ 'actorStage' => SCHEMA_COMPAT_NEW ],
[ 'revision', 'rev_user', 'rev_user_text', 'batchKey' => 'rev_id', 'actorId' => '',
- 'actorStage' => $wgActorTableSchemaMigrationStage ],
+ 'actorStage' => SCHEMA_COMPAT_NEW ],
[ 'filearchive', 'fa_user', 'fa_user_text', 'batchKey' => 'fa_id', 'actorId' => 'fa_actor',
- 'actorStage' => $wgActorTableSchemaMigrationStage ],
+ 'actorStage' => SCHEMA_COMPAT_NEW ],
[ 'image', 'img_user', 'img_user_text', 'batchKey' => 'img_name', 'actorId' => 'img_actor',
- 'actorStage' => $wgActorTableSchemaMigrationStage ],
+ 'actorStage' => SCHEMA_COMPAT_NEW ],
[ 'oldimage', 'oi_user', 'oi_user_text', 'batchKey' => 'oi_archive_name',
- 'actorId' => 'oi_actor', 'actorStage' => $wgActorTableSchemaMigrationStage ],
+ 'actorId' => 'oi_actor', 'actorStage' => SCHEMA_COMPAT_NEW ],
[ 'recentchanges', 'rc_user', 'rc_user_text', 'batchKey' => 'rc_id', 'actorId' => 'rc_actor',
- 'actorStage' => $wgActorTableSchemaMigrationStage ],
+ 'actorStage' => SCHEMA_COMPAT_NEW ],
[ 'logging', 'log_user', 'log_user_text', 'batchKey' => 'log_id', 'actorId' => 'log_actor',
- 'actorStage' => $wgActorTableSchemaMigrationStage ],
+ 'actorStage' => SCHEMA_COMPAT_NEW ],
[ 'ipblocks', 'ipb_by', 'ipb_by_text', 'batchKey' => 'ipb_id', 'actorId' => 'ipb_by_actor',
- 'actorStage' => $wgActorTableSchemaMigrationStage ],
+ 'actorStage' => SCHEMA_COMPAT_NEW ],
[ 'watchlist', 'wl_user', 'batchKey' => 'wl_title' ],
[ 'user_groups', 'ug_user', 'options' => [ 'IGNORE' ] ],
[ 'user_properties', 'up_user', 'options' => [ 'IGNORE' ] ],
[ 'user_former_groups', 'ufg_user', 'options' => [ 'IGNORE' ] ],
+ [ 'revision_actor_temp', 'batchKey' => 'revactor_rev', 'actorId' => 'revactor_actor',
+ 'actorStage' => SCHEMA_COMPAT_NEW ],
];
- if ( $this->stageNeedsActor( $wgActorTableSchemaMigrationStage ) ) {
- $updateFields[] = [
- 'revision_actor_temp', 'batchKey' => 'revactor_rev', 'actorId' => 'revactor_actor',
- 'actorStage' => $wgActorTableSchemaMigrationStage
- ];
- }
Hooks::run( 'UserMergeAccountFields', [ &$updateFields ] );
@@ -298,16 +303,18 @@ class MergeUser {
continue;
}
- $options = isset( $fieldInfo['options'] ) ? $fieldInfo['options'] : [];
+ $options = $fieldInfo['options'] ?? [];
unset( $fieldInfo['options'] );
- $db = isset( $fieldInfo['db'] ) ? $fieldInfo['db'] : $dbw;
+ $db = $fieldInfo['db'] ?? $dbw;
unset( $fieldInfo['db'] );
$tableName = array_shift( $fieldInfo );
$idField = array_shift( $fieldInfo );
- $keyField = isset( $fieldInfo['batchKey'] ) ? $fieldInfo['batchKey'] : null;
+ $keyField = $fieldInfo['batchKey'] ?? null;
unset( $fieldInfo['batchKey'] );
- if ( isset( $fieldInfo['actorId'] ) && !$this->stageNeedsUser( $fieldInfo['actorStage'] ) ) {
+ if ( isset( $fieldInfo['actorId'] ) && isset( $fieldInfo['actorStage'] ) &&
+ !$this->stageNeedsUser( $fieldInfo['actorStage'] )
+ ) {
continue;
}
unset( $fieldInfo['actorId'], $fieldInfo['actorStage'] );
@@ -357,22 +364,22 @@ class MergeUser {
}
}
- if ( $this->stageNeedsActor( $wgActorTableSchemaMigrationStage ) &&
- $this->oldUser->getActorId()
- ) {
+ if ( $this->oldUser->getActorId() ) {
$oldActorId = $this->oldUser->getActorId();
- $newActorId = $this->newUser->getActorId( $db );
+ $newActorId = $this->newUser->getActorId( $dbw );
foreach ( $updateFields as $fieldInfo ) {
- if ( empty( $fieldInfo['actorId'] ) || !$this->stageNeedsActor( $fieldInfo['actorStage'] ) ) {
+ if ( empty( $fieldInfo['actorId'] ) || empty( $fieldInfo['actorStage'] ) ||
+ !$this->stageNeedsActor( $fieldInfo['actorStage'] )
+ ) {
continue;
}
- $options = isset( $fieldInfo['options'] ) ? $fieldInfo['options'] : [];
- $db = isset( $fieldInfo['db'] ) ? $fieldInfo['db'] : $dbw;
+ $options = $fieldInfo['options'] ?? [];
+ $db = $fieldInfo['db'] ?? $dbw;
$tableName = array_shift( $fieldInfo );
$idField = $fieldInfo['actorId'];
- $keyField = isset( $fieldInfo['batchKey'] ) ? $fieldInfo['batchKey'] : null;
+ $keyField = $fieldInfo['batchKey'] ?? null;
if ( $db->trxLevel() || $keyField === null ) {
// Can't batch/wait when in a transaction or when no batch key is given
@@ -418,7 +425,9 @@ class MergeUser {
}
}
- $dbw->delete( 'user_newtalk', [ 'user_id' => $this->oldUser->getId() ] );
+ $dbw->delete( 'user_newtalk', [ 'user_id' => $this->oldUser->getId() ], __METHOD__ );
+ $this->oldUser->clearInstanceCache();
+ $this->newUser->clearInstanceCache();
Hooks::run( 'MergeAccountFromTo', [ &$this->oldUser, &$this->newUser ] );
}
@@ -496,18 +505,17 @@ class MergeUser {
* Deletes the old user page when the target user page exists
*
* @todo This code is a duplicate of Renameuser and GlobalRename
- * @suppress PhanParamTooMany Several calls to $message, which is a variadic closure
*
* @param User $performer
* @param callable $msg Function that returns a Message object
* @return array Array of old name (string) => new name (Title) where the move failed
*/
private function movePages( User $performer, /* callable */ $msg ) {
- global $wgContLang, $wgUser;
+ $contLang = MediaWikiServices::getInstance()->getContentLanguage();
$oldusername = trim( str_replace( '_', ' ', $this->oldUser->getName() ) );
$oldusername = Title::makeTitle( NS_USER, $oldusername );
- $newusername = Title::makeTitleSafe( NS_USER, $wgContLang->ucfirst( $this->newUser->getName() ) );
+ $newusername = Title::makeTitleSafe( NS_USER, $contLang->ucfirst( $this->newUser->getName() ) );
# select all user pages and sub-pages
$dbr = wfGetDB( DB_REPLICA );
@@ -518,17 +526,14 @@ class MergeUser {
'page_namespace' => [ NS_USER, NS_USER_TALK ],
'page_title' . $dbr->buildLike( $oldusername->getDBkey() . '/', $dbr->anyString() )
. ' OR page_title = ' . $dbr->addQuotes( $oldusername->getDBkey() ),
- ]
+ ],
+ __METHOD__
);
$message = function ( /* ... */ ) use ( $msg ) {
return call_user_func_array( $msg, func_get_args() );
};
- // Need to set $wgUser to attribute log properly.
- $oldUser = $wgUser;
- $wgUser = $performer;
-
$failedMoves = [];
foreach ( $pages as $row ) {
$oldPage = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
@@ -537,46 +542,36 @@ class MergeUser {
if ( $this->newUser->getName() === 'Anonymous' ) { # delete ALL old pages
if ( $oldPage->exists() ) {
- $error = '';
- $oldPageArticle = new Article( $oldPage, 0 );
- $oldPageArticle->doDeleteArticle(
- $message( 'usermerge-autopagedelete' )->inContentLanguage()->text(),
- false, null, null, $error, true
- );
+ $this->deletePage( $message, $performer, $oldPage );
}
} elseif ( $newPage->exists()
- && !$oldPage->isValidMoveTarget( $newPage )
+ && !MediaWikiServices::getInstance()
+ ->getMovePageFactory()
+ ->newMovePage( $oldPage, $newPage )
+ ->isValidMove()
+ ->isOk()
&& $newPage->getLength() > 0
) {
# delete old pages that can't be moved
- $error = '';
- $oldPageArticle = new Article( $oldPage, 0 );
- $oldPageArticle->doDeleteArticle(
- $message( 'usermerge-autopagedelete' )->inContentLanguage()->text(),
- false, null, null, $error, true
- );
-
+ $this->deletePage( $message, $performer, $oldPage );
} else { # move content to new page
# delete target page if it exists and is blank
if ( $newPage->exists() ) {
- $error = '';
- $newPageArticle = new Article( $newPage, 0 );
- $newPageArticle->doDeleteArticle(
- $message( 'usermerge-autopagedelete' )->inContentLanguage()->text(),
- false, null, null, $error, true
- );
+ $this->deletePage( $message, $performer, $newPage );
}
# move to target location
- $errors = $oldPage->moveTo(
- $newPage,
- false,
- $message(
- 'usermerge-move-log',
- $oldusername->getText(),
- $newusername->getText() )->inContentLanguage()->text()
- );
- if ( $errors !== true ) {
+ $status = MediaWikiServices::getInstance()
+ ->getMovePageFactory()
+ ->newMovePage( $oldPage, $newPage )
+ ->move(
+ $performer,
+ $message(
+ 'usermerge-move-log',
+ $oldusername->getText(),
+ $newusername->getText() )->inContentLanguage()->text()
+ );
+ if ( !$status->isOk() ) {
$failedMoves[$oldPage->getPrefixedText()] = $newPage;
}
@@ -588,22 +583,43 @@ class MergeUser {
);
if ( !$dbr->numRows( $res ) ) {
# nothing links here, so delete unmoved page/redirect
- $error = '';
- $oldPageArticle = new Article( $oldPage, 0 );
- $oldPageArticle->doDeleteArticle(
- $message( 'usermerge-autopagedelete' )->inContentLanguage()->text(),
- false, null, null, $error, true
- );
+ $this->deletePage( $message, $performer, $oldPage );
}
}
}
- $wgUser = $oldUser;
-
return $failedMoves;
}
/**
+ * Helper to delete pages
+ *
+ * @param callable $msg
+ * @param User $user
+ * @param Title $title
+ */
+ private function deletePage( $msg, User $user, Title $title ) {
+ $wikipage = WikiPage::factory( $title );
+ $reason = $msg( 'usermerge-autopagedelete' )->inContentLanguage()->text();
+ $error = '';
+ if ( version_compare( MW_VERSION, '1.35', '<' ) ) {
+ $wikipage->doDeleteArticle( $reason, false, null, null, $error, $user, true );
+ } else {
+ $wikipage->doDeleteArticleReal(
+ $reason,
+ $user,
+ false,
+ null, // Unused
+ $error,
+ null, // Unused
+ [],
+ 'delete',
+ true
+ );
+ }
+ }
+
+ /**
* Function to delete users following a successful mergeUser call.
*
* Removes rows from the user, user_groups, user_properties
@@ -635,14 +651,15 @@ class MergeUser {
foreach ( $tablesToDelete as $table => $field ) {
// Check if a different database object was passed (Echo or Flow)
if ( is_array( $field ) ) {
- $db = isset( $field['db'] ) ? $field['db'] : $dbw;
+ $db = $field['db'] ?? $dbw;
$field = $field[0];
} else {
$db = $dbw;
}
$db->delete(
$table,
- [ $field => $this->oldUser->getId() ]
+ [ $field => $this->oldUser->getId() ],
+ __METHOD__
);
}