12require_once __DIR__ .
'/Maintenance.php';
20 private $renameUserFactory;
23 private $titleFactory;
35 private $suppressRedirect;
38 private $skipPageMoves;
41 parent::__construct();
43 $this->
addDescription(
'Rename users with a name matching a pattern. ' .
44 'This can be used to migrate to a temporary user (IP masking) configuration.' );
45 $this->
addOption(
'from',
'A username pattern where $1 is ' .
46 'the wildcard standing in for any number of characters. All users ' .
47 'matching this pattern will be renamed.',
true,
true );
48 $this->
addOption(
'to',
'A username pattern where $1 is ' .
49 'the part of the username matched by $1 in --from. Users will be ' .
50 ' renamed to this pattern.',
true,
true );
51 $this->
addOption(
'performer',
'Performer of the rename action',
false,
true );
52 $this->
addOption(
'reason',
'Reason of the rename',
false,
true );
53 $this->
addOption(
'suppress-redirect',
'Don\'t create redirects when moving pages' );
54 $this->
addOption(
'skip-page-moves',
'Don\'t move associated user pages' );
55 $this->
addOption(
'dry-run',
'Don\'t actually rename the ' .
56 'users, just report what it would do.' );
60 private function initServices() {
62 if ( $services->getCentralIdLookupFactory()->getNonLocalLookup() ) {
63 $this->
fatalError(
"This script cannot be run when CentralAuth is enabled." );
65 $this->userFactory = $services->getUserFactory();
66 $this->renameUserFactory = $services->getRenameUserFactory();
67 $this->titleFactory = $services->getTitleFactory();
72 $this->initServices();
77 if ( $this->
getOption(
'performer' ) ===
null ) {
78 $performer = User::newSystemUser( User::MAINTENANCE_SCRIPT_USER, [
'steal' =>
true ] );
83 $this->
fatalError(
"Unable to get performer account" );
85 $this->performer = $performer;
87 $this->reason = $this->
getOption(
'reason',
'' );
88 $this->dryRun = $this->
getOption(
'dry-run' );
89 $this->suppressRedirect = $this->
getOption(
'suppress-redirect' );
90 $this->skipPageMoves = $this->
getOption(
'skip-page-moves' );
97 $res = $dbr->newSelectQueryBuilder()
98 ->select( [
'user_name' ] )
100 ->where( $dbr->expr(
'user_name', IExpression::LIKE, $fromPattern->toLikeValue( $dbr ) ) )
101 ->andWhere( $batchConds )
102 ->orderBy(
'user_name' )
103 ->limit( $batchSize )
104 ->caller( __METHOD__ )
107 foreach ( $res as $row ) {
108 $oldName = $row->user_name;
109 $batchConds = [ $dbr->expr(
'user_name',
'>', $oldName ) ];
110 $variablePart = $fromPattern->extract( $oldName );
111 if ( $variablePart ===
null ) {
112 $this->
output(
"Username \"fromName\" matched the LIKE " .
113 "but does not seem to match the pattern" );
116 $newName = $toPattern->generate( $variablePart );
119 $newTitle = $this->titleFactory->makeTitleSafe(
NS_USER, $newName );
120 $newUser = $this->userFactory->newFromName( $newName );
121 if ( !$newTitle || !$newUser ) {
122 $this->
output(
"Cannot rename \"$oldName\" " .
123 "because \"$newName\" is not a valid title\n" );
126 $newName = $newTitle->getText();
129 if ( $newUser->isRegistered() ) {
130 $this->
output(
"Cannot rename \"$oldName\" " .
131 "because \"$newName\" already exists\n" );
135 $numRenamed += $this->renameUser( $oldName, $newName ) ? 1 : 0;
138 }
while ( $res->numRows() === $batchSize );
139 $this->
output(
"Renamed $numRenamed user(s)\n" );
148 private function renameUser( $oldName, $newName ) {
149 $oldUser = $this->userFactory->newFromName( $oldName );
151 $this->
output(
"Invalid user name \"$oldName\"" );
155 $id = $oldUser->getId();
157 $this->
output(
"Cannot rename non-existent user \"$oldName\"" );
161 if ( $this->dryRun ) {
162 $this->
output(
"$oldName would be renamed to $newName\n" );
164 $rename = $this->renameUserFactory->newRenameUser( $this->performer, $oldUser, $newName, $this->reason, [
165 'forceGlobalDetach' => $this->
getOption(
'force-global-detach' ),
166 'movePages' => !$this->
getOption(
'skip-page-moves' ),
167 'suppressRedirect' => $this->
getOption(
'suppress-redirect' ),
169 $status = $rename->renameGlobal();
171 if ( $status->isGood() ) {
172 $this->
output(
"$oldName was successfully renamed to $newName.\n" );
174 if ( $status->isOK() ) {
175 $this->
output(
"$oldName was renamed to $newName.\n" );
177 $this->
output(
"Unable to rename $oldName.\n" );
179 foreach ( $status->getMessages() as $msg ) {
190require_once RUN_MAINTENANCE_IF_MAIN;
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
getBatchSize()
Returns batch size.
output( $out, $channel=null)
Throw some output to the user.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
waitForReplication()
Wait for replica DB servers to catch up.
getOption( $name, $default=null)
Get an option, or return the default.
getServiceContainer()
Returns the main service container.
addDescription( $text)
Set the description text.
__construct()
Default constructor.
execute()
Do the actual work.All child classes will need to implement thisbool|null|void True for success,...