MarkAHershberger has uploaded a new change for review.
https://gerrit.wikimedia.org/r/80690
Change subject: Abstract out some code for use in BlockandNuke
......................................................................
Abstract out some code for use in BlockandNuke
Change-Id: Iaa4abe7fa3ed22d98bb97aeb53923bf5409e1ecb
---
A UserMerger.php
1 file changed, 469 insertions(+), 0 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/UserMerge
refs/changes/90/80690/1
diff --git a/UserMerger.php b/UserMerger.php
new file mode 100644
index 0000000..474267c
--- /dev/null
+++ b/UserMerger.php
@@ -0,0 +1,469 @@
+<?php
+
+class UserMerger {
+ protected $page;
+
+ public function __construct( $page = null ) {
+ $this->page = $page;
+ }
+
+ public function merge( $oldUser, $newUser, $deleteUserPage = true ) {
+ $this->mergeEditcount( $newUser, $oldUser );
+ $this->mergeUser( $newUser, $oldUser );
+ if ( $deleteUserPage ) {
+ $this->movePages( $newUser, $oldUser );
+ $this->deleteUser( $oldUser );
+ }
+ }
+
+ /**
+ * Function to delete users following a successful mergeUser call
+ *
+ * Removes user entries from the user table and the user_groups table
+ *
+ * @param $user User
+ *
+ * @return bool Always returns true - throws exceptions on failure.
+ */
+ public function deleteUser( $user ) {
+ $olduserID = $user->getId();
+ $olduser_text = $user->getName();
+
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->delete(
+ 'user_groups',
+ array( 'ug_user' => $olduserID )
+ );
+ $dbw->delete(
+ 'user',
+ array( 'user_id' => $olduserID )
+ );
+
+ $log = new LogPage( 'usermerge' );
+ if( $this->page ) {
+ $this->page->getOutput()->addHTML(
+ wfMessage( 'usermerge-userdeleted',
$olduser_text, $olduserID )->escaped() .
+ Html::element( 'br' ) . "\n"
+ );
+
+ $log->addEntry( 'deleteuser',
$this->page->getUser()->getUserPage(), '', array( $olduser_text, $olduserID ) );
+ } else {
+ $log->addEntry( 'deleteuser', Title::newFromText(
$olduser_text, NS_USER ), '', array( $olduser_text, $olduserID ) );
+ }
+
+ wfRunHooks( 'DeleteAccount', array( &$user ) );
+
+ $users = $dbw->selectField(
+ 'user',
+ 'COUNT(*)',
+ array()
+ );
+ $dbw->update( 'site_stats',
+ array( 'ss_users' => $users ),
+ array( 'ss_row_id' => 1 )
+ );
+ return true;
+ }
+
+ /**
+ * Deduplicate watchlist entries
+ * which old (merge-from) and new (merge-to) users are watching
+ *
+ * @param $oldUser User
+ * @param $newUser User
+ *
+ * @return bool
+ */
+ public function deduplicateWatchlistEntries( $oldUser, $newUser ) {
+
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->begin( __METHOD__ );
+
+ $res = $dbw->select(
+ array(
+ 'w1' => 'watchlist',
+ 'w2' => 'watchlist'
+ ),
+ array(
+ 'w2.wl_namespace',
+ 'w2.wl_title'
+ ),
+ array(
+ 'w1.wl_user' => $newUser->getID(),
+ 'w2.wl_user' => $oldUser->getID()
+ ),
+ __METHOD__,
+ array( 'FOR UPDATE' ),
+ array(
+ 'w2' => array(
+ 'INNER JOIN',
+ array(
+ 'w1.wl_namespace =
w2.wl_namespace',
+ 'w1.wl_title = w2.wl_title'
+ ),
+ )
+ )
+ );
+
+ # Construct an array to delete all watched pages of the old user
+ # which the new user already watches
+ $conds = array();
+
+ foreach ( $res as $result ) {
+ $conds[] = $dbw->makeList(
+ array(
+ 'wl_user' => $oldUser->getID(),
+ 'wl_namespace' => $result->wl_namespace,
+ 'wl_title' => $result->wl_title
+ ),
+ LIST_AND
+ );
+ }
+
+ if ( empty( $conds ) ) {
+ $dbw->commit( __METHOD__ );
+ return true;
+ }
+
+ # Perform a multi-row delete
+
+ # requires
+ # MediaWiki database function with fixed
https://bugzilla.wikimedia.org/50078
+ # i.e. MediaWiki core after
505dbb331e16a03d87cb4511ee86df12ea295c40 (20130625)
+ $dbw->delete(
+ 'watchlist',
+ $dbw->makeList( $conds, LIST_OR ),
+ __METHOD__
+ );
+
+ $dbw->commit( __METHOD__ );
+
+ return true;
+ }
+
+
+ /**
+ * Function to merge database references from one user to another user
+ *
+ * Merges database references from one user ID or username to another
user ID or username
+ * to preserve referential integrity.
+ *
+ * @param $newUser User to merge from and delete
+ * @param $oldUser User to merge into
+ *
+ * @return bool Always returns true - throws exceptions on failure.
+ */
+ public function mergeUser( $newUser, $oldUser, $block = false,
$banningUser = null ) {
+ $newuser_text = $newUser->getName();
+ $newuserID = $newUser->idForName();
+ $olduser_text = $oldUser->getName();
+ $olduserID = $oldUser->idForName();
+
+ if( $block == "block" ) {
+ if( $banningUser === null ) {
+ $banningUser = $oldUser;
+ }
+ // Make sure we block the IP first.
+ $blk = new Block($newUser->getName(), $newUser->getId(),
+ $banningUser->getID(), wfMsg('block-message'),
+ wfTimestamp(), 0, Block::infinity(), 0, 1, 0,
0, 1);
+ $blk->isAutoBlocking( true );
+ if($blk->insert()) {
+ $log = new LogPage('block');
+ $log->addEntry('block', Title::makeTitle(
NS_USER, $oldUser->getName() ),
+ 'Blocked through UserMerger',
array('infinite', $oldUser->getName(), 'nocreate'));
+ }
+ }
+
+ // Fields to update with the format:
+ // array( tableName, idField, textField )
+ $updateFields = array(
+ array( 'archive', 'ar_user', 'ar_user_text' ),
+ array( 'revision', 'rev_user', 'rev_user_text' ),
+ array( 'filearchive', 'fa_user', 'fa_user_text' ),
+ array( 'image', 'img_user', 'img_user_text' ),
+ array( 'oldimage', 'oi_user', 'oi_user_text' ),
+ array( 'recentchanges', 'rc_user', 'rc_user_text' ),
+ array( 'logging', 'log_user' ),
+ array( 'ipblocks', 'ipb_user', 'ipb_address' ),
+ array( 'ipblocks', 'ipb_by', 'ipb_by_text' ),
+ array( 'watchlist', 'wl_user' ),
+ );
+
+ $dbw = wfGetDB( DB_MASTER );
+ $out = null;
+ if( $this->page ) {
+ $out = $this->page->getOutput();
+ }
+
+ $this->deduplicateWatchlistEntries( $oldUser, $newUser );
+
+ foreach ( $updateFields as $fieldInfo ) {
+ $tableName = array_shift( $fieldInfo );
+ $idField = array_shift( $fieldInfo );
+
+ $dbw->update(
+ $tableName,
+ array( $idField => $newuserID ) +
array_fill_keys( $fieldInfo, $newuser_text ),
+ array( $idField => $olduserID ),
+ __METHOD__,
+ array( 'IGNORE' )
+ );
+
+ if( $out ) {
+ $out->addHTML(
+ wfMessage(
+ 'usermerge-updating',
+ $tableName,
+ $olduserID,
+ $newuserID
+ )->escaped() .
+ Html::element( 'br' ) . "\n"
+ );
+
+ foreach ( $fieldInfo as $textField ) {
+ $out->addHTML(
+ wfMessage(
+ 'usermerge-updating',
+ $tableName,
+ $olduser_text,
+ $newuser_text
+ )->escaped() .
+ Html::element( 'br' ) . "\n"
+ );
+ }
+ }
+ }
+
+ $dbw->delete( 'user_newtalk', array( 'user_id' => $olduserID )
);
+
+ if( $out ) {
+ $out->addHTML(
+ Html::element( 'hr' ) . "\n" .
+ wfMessage( 'usermerge-success', $olduser_text,
$olduserID, $newuser_text, $newuserID )->escaped() .
+ Html::element( 'br' ) . "\n"
+ );
+
+ $log = new LogPage( 'usermerge' );
+ $log->addEntry(
+ 'mergeuser',
+ $this->page->getUser()->getUserPage(),
+ '',
+ array( $olduser_text, $olduserID,
$newuser_text, $newuserID )
+ );
+ }
+ wfRunHooks( 'MergeAccountFromTo', array( &$oldUser, &$newUser )
);
+
+ return true;
+ }
+
+
+ /**
+ * Function to add edit count
+ *
+ * Adds edit count of both users
+ *
+ * @param $newUser user to merge references TO
+ * @param $oldUser user to remove references FROM
+ *
+ * @return bool Always returns true - throws exceptions on failure.
+ *
+ * @author Matthew April <[email protected]>
+ */
+ public function mergeEditcount( $newUser, $oldUser ) {
+ $dbw = wfGetDB( DB_MASTER );
+
+ $newuserID = $newUser->getId();
+ $olduserID = $oldUser->getId();
+
+ $olduserEdits = $dbw->selectField(
+ 'user',
+ 'user_editcount',
+ array( 'user_id' => $olduserID ),
+ __METHOD__
+ );
+ if ( $olduserEdits === false ) {
+ $olduserEdits = 0;
+ }
+
+ $newuserEdits = $dbw->selectField(
+ 'user',
+ 'user_editcount',
+ array( 'user_id' => $newuserID ),
+ __METHOD__
+ );
+ if ( $newuserEdits === false ) {
+ $newuserEdits = 0;
+ }
+
+ $totalEdits = $olduserEdits + $newuserEdits;
+
+ # don't run querys if neither user has any edits
+ if ( $totalEdits > 0 ) {
+ # update new user with total edits
+ $dbw->update( 'user',
+ array( 'user_editcount' => $totalEdits ),
+ array( 'user_id' => $newuserID ),
+ __METHOD__
+ );
+
+ # clear old users edits
+ $dbw->update( 'user',
+ array( 'user_editcount' => 0 ),
+ array( 'user_id' => $olduserID ),
+ __METHOD__
+ );
+ }
+
+ if( $this->page ) {
+ $this->page->getOutput()->addHTML(
+ wfMessage(
+ 'usermerge-editcount-merge-success',
+ $olduserEdits, $olduserID,
$newuserEdits, $newuserID, $totalEdits
+ )->escaped() .
+ Html::element( 'br' ) . "\n"
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Function to merge user pages
+ *
+ * Deletes all pages when merging to Anon
+ * Moves user page when the target user page does not exist or is empty
+ * Deletes redirect if nothing links to old page
+ * Deletes the old user page when the target user page exists
+ *
+ * @param $newuser_text string Username to merge pages TO
+ * @param $olduser_text string Username of user to remove pages FROM
+ *
+ * @return bool True on completion
+ *
+ * @author Matthew April <[email protected]>
+ */
+ public function movePages( $newUser, $oldUser ) {
+ $newuser_text = $newUser->getName();
+ $olduser_text = $oldUser->getName();
+ global $wgContLang;
+
+ $oldusername = trim( str_replace( '_', ' ', $olduser_text ) );
+ $oldusername = Title::makeTitle( NS_USER, $oldusername );
+ $newusername = Title::makeTitleSafe( NS_USER,
$wgContLang->ucfirst( $newuser_text ) );
+
+ # select all user pages and sub-pages
+ $dbr = wfGetDB( DB_SLAVE );
+ $pages = $dbr->select( 'page',
+ array( 'page_namespace', 'page_title' ),
+ array(
+ 'page_namespace' => array( NS_USER,
NS_USER_TALK ),
+ $dbr->makeList( array(
+ 'page_title' => $dbr->buildLike(
$oldusername->getDBkey() . '/', $dbr->anyString() ),
+ 'page_title' => $oldusername->getDBkey()
+ ),
+ LIST_OR
+ )
+ )
+ );
+
+ $output = '';
+
+ foreach ( $pages as $row ) {
+
+ $oldPage = Title::makeTitleSafe( $row->page_namespace,
$row->page_title );
+ $newPage = Title::makeTitleSafe( $row->page_namespace,
+ preg_replace( '!^[^/]+!',
$newusername->getDBkey(), $row->page_title ) );
+
+ if ( $newuser_text === "Anonymous" ) { # delete ALL old
pages
+ if ( $oldPage->exists() ) {
+ $oldPageArticle = new Article(
$oldPage, 0 );
+ $oldPageArticle->doDeleteArticle(
wfMessage( 'usermerge-autopagedelete' )->inContentLanguage()->text() );
+
+ $oldLink = Linker::linkKnown( $oldPage
);
+ $output .= Html::rawElement( 'li',
+ array( 'class' =>
'mw-renameuser-pe' ),
+ wfMessage(
'usermerge-page-deleted' )->rawParams( $oldLink )->escaped()
+ );
+
+ }
+ } elseif ( $newPage->exists()
+ && !$oldPage->isValidMoveTarget( $newPage )
+ && $newPage->getLength() > 0 ) { # delete old
pages that can't be moved
+
+ $oldPageArticle = new Article( $oldPage, 0 );
+ $oldPageArticle->doDeleteArticle( wfMessage(
'usermerge-autopagedelete' )->text() );
+
+ $link = Linker::linkKnown( $oldPage );
+ $output .= Html::rawElement( 'li',
+ array( 'class' => 'mw-renameuser-pe' ),
+ wfMessage( 'usermerge-page-deleted'
)->rawParams( $link )->escaped()
+ );
+
+ } else { # move content to new page
+ # delete target page if it exists and is blank
+ if ( $newPage->exists() ) {
+ $newPageArticle = new Article(
$newPage, 0 );
+ $newPageArticle->doDeleteArticle(
wfMessage( 'usermerge-autopagedelete' )->inContentLanguage()->text() );
+ }
+
+ # move to target location
+ $success = $oldPage->moveTo(
+ $newPage,
+ false,
+ wfMessage(
+ 'usermerge-move-log',
+ $oldusername->getText(),
+ $newusername->getText()
)->inContentLanguage()->text()
+ );
+
+ if ( $success === true ) {
+ $oldLink = Linker::linkKnown(
+ $oldPage,
+ null,
+ array(),
+ array( 'redirect' => 'no' )
+ );
+ $newLink = Linker::linkKnown( $newPage
);
+ $output .= Html::rawElement( 'li',
+ array( 'class' =>
'mw-renameuser-pm' ),
+ wfMessage(
'usermerge-page-moved' )->rawParams( $oldLink, $newLink )->escaped()
+ );
+ } else {
+ $oldLink = Linker::linkKnown( $oldPage
);
+ $newLink = Linker::linkKnown( $newPage
);
+ $output .= Html::rawElement( 'li',
+ array( 'class' =>
'mw-renameuser-pu' ),
+ wfMessage(
'usermerge-page-unmoved' )->rawParams( $oldLink, $newLink )->escaped()
+ );
+ }
+
+ # check if any pages link here
+ $res = $dbr->selectField( 'pagelinks',
+ 'pl_title',
+ array( 'pl_title' => $olduser_text ),
+ __METHOD__
+ );
+ if ( !$dbr->numRows( $res ) ) {
+ # nothing links here, so delete unmoved
page/redirect
+ $oldPageArticle = new Article(
$oldPage, 0 );
+ $oldPageArticle->doDeleteArticle(
wfMessage( 'usermerge-autopagedelete' )->inContentLanguage()->text() );
+ }
+ }
+
+ }
+
+ if ( $output && $this->page ) {
+ $this->page->getOutput()->addHTML(
+ Html::rawElement( 'ul',
+ array(),
+ $output
+ )
+ );
+ } elseif( $output ) {
+ var_dump($output );
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
--
To view, visit https://gerrit.wikimedia.org/r/80690
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Iaa4abe7fa3ed22d98bb97aeb53923bf5409e1ecb
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/UserMerge
Gerrit-Branch: master
Gerrit-Owner: MarkAHershberger <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits