17 private $movePageFactory;
20 private $titleFactory;
32 private $suppressRedirect;
35 private $skipPageMoves;
38 parent::__construct();
40 $this->
addDescription(
'Rename users with a name matching a pattern. ' .
41 'This can be used to migrate to a temporary user (IP masking) configuration.' );
42 $this->
addOption(
'from',
'A username pattern where $1 is ' .
43 'the wildcard standing in for any number of characters. All users ' .
44 'matching this pattern will be renamed.',
true,
true );
45 $this->
addOption(
'to',
'A username pattern where $1 is ' .
46 'the part of the username matched by $1 in --from. Users will be ' .
47 ' renamed to this pattern.',
true,
true );
48 $this->
addOption(
'performer',
'Performer of the rename action',
false,
true );
49 $this->
addOption(
'reason',
'Reason of the rename',
false,
true );
50 $this->
addOption(
'suppress-redirect',
'Don\'t create redirects when moving pages' );
51 $this->
addOption(
'skip-page-moves',
'Don\'t move associated user pages' );
52 $this->
addOption(
'dry-run',
'Don\'t actually rename the ' .
53 'users, just report what it would do.' );
57 private function initServices() {
58 $services = MediaWikiServices::getInstance();
59 if ( $services->getCentralIdLookupFactory()->getNonLocalLookup() ) {
60 $this->
fatalError(
"This script cannot be run when CentralAuth is enabled." );
62 $this->userFactory = $services->getUserFactory();
63 $this->movePageFactory = $services->getMovePageFactory();
64 $this->titleFactory = $services->getTitleFactory();
68 $this->initServices();
73 if ( $this->
getOption(
'performer' ) ===
null ) {
79 $this->
error(
"Unable to get performer account" );
82 $this->performer = $performer;
84 $this->reason = $this->
getOption(
'reason',
'' );
85 $this->dryRun = $this->
getOption(
'dry-run' );
86 $this->suppressRedirect = $this->
getOption(
'suppress-redirect' );
87 $this->skipPageMoves = $this->
getOption(
'skip-page-moves' );
95 ->select( [
'user_name' ] )
98 [
'user_name' . $fromPattern->buildLike(
$dbr ) ],
101 ->orderBy(
'user_name' )
102 ->limit( $batchSize )
103 ->caller( __METHOD__ )
106 foreach (
$res as $row ) {
107 $oldName = $row->user_name;
108 $batchConds = [
'user_name > ' .
$dbr->addQuotes( $oldName ) ];
109 $variablePart = $fromPattern->extract( $oldName );
110 if ( $variablePart ===
null ) {
111 $this->
output(
"Username \"fromName\" matched the LIKE " .
112 "but does not seem to match the pattern" );
115 $newName = $toPattern->generate( $variablePart );
116 $numRenamed += $this->renameUser( $oldName, $newName ) ? 1 : 0;
119 }
while (
$res->numRows() === $batchSize );
120 $this->
output(
"Renamed $numRenamed user(s)\n" );
129 private function renameUser( $oldName, $newName ) {
130 $id = $this->userFactory->newFromName( $oldName )->getId();
132 $this->
output(
"Cannot rename non-existent user \"$oldName\"" );
135 if ( $this->dryRun ) {
136 $this->
output(
"$oldName would be renamed to $newName\n" );
144 'reason' => $this->reason
148 if ( !$renamer->rename() ) {
149 $this->
output(
"Unable to rename $oldName" );
152 $this->
output(
"$oldName was successfully renamed to $newName.\n" );
156 if ( $this->skipPageMoves ) {
160 $this->movePageAndSubpages(
NS_USER,
'User', $oldName, $newName );
161 $this->movePageAndSubpages(
NS_USER_TALK,
'User talk', $oldName, $newName );
165 private function movePageAndSubpages( $ns, $nsName, $oldName, $newName ) {
166 $oldTitle = $this->titleFactory->makeTitleSafe( $ns, $oldName );
168 $this->
output(
"[[$nsName:$oldName]] is an invalid title, can't move it.\n" );
171 $newTitle = $this->titleFactory->makeTitleSafe( $ns, $newName );
173 $this->
output(
"[[$nsName:$newName]] is an invalid title, can't move to it.\n" );
177 $movePage = $this->movePageFactory->newMovePage( $oldTitle, $newTitle );
178 $movePage->setMaximumMovedPages( -1 );
181 'renameuser-move-log', $oldName, $newName
182 )->inContentLanguage()->text();
184 if ( $this->dryRun ) {
185 if ( $oldTitle->exists() ) {
186 $this->
output(
"Would move [[$nsName:$oldName]] to [[$nsName:$newName]].\n" );
189 if ( $oldTitle->exists() ) {
190 $status = $movePage->move(
191 $this->performer, $logMessage, !$this->suppressRedirect );
195 $status->merge( $movePage->moveSubpages(
196 $this->performer, $logMessage, !$this->suppressRedirect ) );
197 if ( !$status->isGood() ) {
198 $this->
output(
"Failed to rename user page: " .
199 $status->getWikiText(
false,
false,
'en' ) .
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
error( $err, $die=0)
Throw an error to the user.
output( $out, $channel=null)
Throw some output to the user.
waitForReplication()
Wait for replica DBs to catch up.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.