Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F5991762
renameUsersMatchingPattern.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
6 KB
Referenced Files
None
Subscribers
None
renameUsersMatchingPattern.php
View Options
<?php
namespace
MediaWiki\Extension\CentralAuth\Maintenance
;
use
MediaWiki\Extension\CentralAuth\CentralAuthDatabaseManager
;
use
MediaWiki\Extension\CentralAuth\CentralAuthServices
;
use
MediaWiki\Extension\CentralAuth\GlobalRename\GlobalRenameFactory
;
use
MediaWiki\Extension\CentralAuth\GlobalRename\GlobalRenameUserValidator
;
use
MediaWiki\Extension\CentralAuth\User\CentralAuthUser
;
use
MediaWiki\Maintenance\Maintenance
;
use
MediaWiki\User\TempUser\Pattern
;
use
MediaWiki\User\User
;
use
MediaWiki\User\UserFactory
;
use
MediaWiki\User\UserRigorOptions
;
use
Wikimedia\Rdbms\IDBAccessObject
;
use
Wikimedia\Rdbms\IExpression
;
$IP
=
getenv
(
'MW_INSTALL_PATH'
);
if
(
$IP
===
false
)
{
$IP
=
__DIR__
.
'/../../..'
;
}
require_once
"$IP/maintenance/Maintenance.php"
;
class
RenameUsersMatchingPattern
extends
Maintenance
{
private
CentralAuthDatabaseManager
$dbManager
;
private
UserFactory
$userFactory
;
private
GlobalRenameFactory
$globalRenameFactory
;
private
GlobalRenameUserValidator
$validator
;
/** @var User */
private
$performer
;
/** @var string */
private
$reason
;
/** @var bool */
private
$dryRun
;
/** @var bool */
private
$suppressRedirect
;
/** @var bool */
private
$skipPageMoves
;
public
function
__construct
()
{
parent
::
__construct
();
$this
->
addDescription
(
'Rename global users with a name matching a pattern. '
.
'This can be used to migrate to a temporary user (IP masking) configuration.'
);
$this
->
addOption
(
'from'
,
'A username pattern where $1 is '
.
'the wildcard standing in for any number of characters. All users '
.
'matching this pattern will be renamed.'
,
true
,
true
);
$this
->
addOption
(
'to'
,
'A username pattern where $1 is '
.
'the part of the username matched by $1 in --from. Users will be '
.
' renamed to this pattern.'
,
true
,
true
);
$this
->
addOption
(
'performer'
,
'Performer of the rename action'
,
false
,
true
);
$this
->
addOption
(
'reason'
,
'Reason of the rename'
,
false
,
true
);
$this
->
addOption
(
'suppress-redirect'
,
'Don
\'
t create redirects when moving pages'
);
$this
->
addOption
(
'skip-page-moves'
,
'Don
\'
t move associated user pages'
);
$this
->
addOption
(
'dry-run'
,
'Don
\'
t actually rename the '
.
'users, just report what it would do.'
);
$this
->
setBatchSize
(
1000
);
}
private
function
initServices
()
{
$services
=
$this
->
getServiceContainer
();
$this
->
dbManager
=
CentralAuthServices
::
getDatabaseManager
();
$this
->
userFactory
=
$services
->
getUserFactory
();
$this
->
globalRenameFactory
=
$services
->
get
(
'CentralAuth.GlobalRenameFactory'
);
$this
->
validator
=
$services
->
get
(
'CentralAuth.GlobalRenameUserValidator'
);
}
public
function
execute
()
{
$this
->
initServices
();
$fromPattern
=
new
Pattern
(
'from'
,
$this
->
getOption
(
'from'
)
);
$toPattern
=
new
Pattern
(
'to'
,
$this
->
getOption
(
'to'
)
);
$batchSize
=
$this
->
getBatchSize
();
if
(
$this
->
getOption
(
'performer'
)
===
null
)
{
$performer
=
User
::
newSystemUser
(
User
::
MAINTENANCE_SCRIPT_USER
,
[
'steal'
=>
true
]
);
}
else
{
$performer
=
$this
->
userFactory
->
newFromName
(
$this
->
getOption
(
'performer'
)
);
}
if
(
!
$performer
)
{
$this
->
error
(
"Unable to get performer account"
);
return
false
;
}
$this
->
performer
=
$performer
;
$this
->
reason
=
$this
->
getOption
(
'reason'
,
''
);
$this
->
suppressRedirect
=
$this
->
getOption
(
'suppress-redirect'
);
$this
->
skipPageMoves
=
$this
->
getOption
(
'skip-page-moves'
);
$this
->
dryRun
=
$this
->
getOption
(
'dry-run'
);
$dbr
=
$this
->
dbManager
->
getCentralReplicaDB
();
$batchConds
=
[];
$numRenamed
=
0
;
do
{
$res
=
$dbr
->
newSelectQueryBuilder
()
->
select
(
'gu_name'
)
->
from
(
'globaluser'
)
->
where
(
$dbr
->
expr
(
'gu_name'
,
IExpression
::
LIKE
,
$fromPattern
->
toLikeValue
(
$dbr
)
)
)
->
andWhere
(
$batchConds
)
->
orderBy
(
'gu_name'
)
->
limit
(
$batchSize
)
->
caller
(
__METHOD__
)
->
fetchResultSet
();
foreach
(
$res
as
$row
)
{
$oldName
=
$row
->
gu_name
;
$batchConds
=
[
$dbr
->
expr
(
'gu_name'
,
'>'
,
$oldName
)
];
$variablePart
=
$fromPattern
->
extract
(
$oldName
);
if
(
$variablePart
===
null
)
{
$this
->
output
(
"Username
\"
fromName
\"
matched the LIKE "
.
"but does not seem to match the pattern"
);
continue
;
}
$newName
=
$toPattern
->
generate
(
$variablePart
);
$numRenamed
+=
$this
->
renameUser
(
$oldName
,
$newName
)
?
1
:
0
;
$this
->
waitForJobs
();
$this
->
waitForReplication
();
}
}
while
(
$res
->
numRows
()
===
$batchSize
);
$this
->
output
(
"Renamed $numRenamed user(s)
\n
"
);
return
true
;
}
/**
* @param string $oldName
* @param string $newName
* @return bool True if the user was renamed
*/
private
function
renameUser
(
$oldName
,
$newName
)
{
$oldUser
=
$this
->
userFactory
->
newFromName
(
$oldName
,
UserRigorOptions
::
RIGOR_NONE
);
$newUser
=
$this
->
userFactory
->
newFromName
(
$newName
,
UserRigorOptions
::
RIGOR_CREATABLE
);
if
(
!
$oldUser
)
{
$this
->
output
(
"Unable to rename
\"
$oldName
\"
: invalid username
\n
"
);
return
false
;
}
if
(
!
$newUser
)
{
$this
->
output
(
"Unable to rename
\"
$oldName
\"
to
\"
$newName
\"
: invalid target username
\n
"
);
return
false
;
}
$status
=
$this
->
validator
->
validate
(
$oldUser
,
$newUser
);
if
(
!
$status
->
isOK
()
)
{
$this
->
output
(
"Unable to rename
\"
$oldName
\"
to
\"
$newName
\"
: "
.
$status
->
getWikiText
()
.
"
\n
"
);
return
false
;
}
$oldCaUser
=
new
CentralAuthUser
(
$oldName
,
IDBAccessObject
::
READ_LATEST
);
$data
=
[
'movepages'
=>
!
$this
->
skipPageMoves
,
'suppressredirects'
=>
$this
->
suppressRedirect
,
'reason'
=>
$this
->
reason
,
'force'
=>
true
,
];
$globalRenameUser
=
$this
->
globalRenameFactory
->
newGlobalRenameUser
(
$this
->
performer
,
$oldCaUser
,
$newName
);
if
(
$this
->
dryRun
)
{
$this
->
output
(
"Would rename
\"
$oldName
\"
to
\"
$newName
\"\n
"
);
return
true
;
}
else
{
$status
=
$globalRenameUser
->
rename
(
$data
);
if
(
$status
->
isGood
()
)
{
$this
->
output
(
"Successfully queued rename of
\"
$oldName
\"
to
\"
$newName
\"\n
"
);
return
true
;
}
else
{
$this
->
output
(
"Error renaming
\"
$oldName
\"
to
\"
$newName
\"
: "
.
$status
->
getWikiText
(
false
,
false
,
'en'
)
.
"
\n
"
);
return
false
;
}
}
}
/**
* Wait until fewer than 15 rename jobs are pending
*/
private
function
waitForJobs
()
{
while
(
true
)
{
$count
=
$this
->
dbManager
->
getCentralPrimaryDB
()->
newSelectQueryBuilder
()
->
from
(
'renameuser_status'
)
->
limit
(
15
)
->
caller
(
__METHOD__
)
->
fetchRowCount
();
if
(
$count
<
15
)
{
break
;
}
sleep
(
5
);
}
}
}
$maintClass
=
RenameUsersMatchingPattern
::
class
;
require_once
RUN_MAINTENANCE_IF_MAIN
;
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Oct 4 2026, 02:01 (4 w, 2 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
8f/f2/07bc5603cef1334b3c06c0892de5
Default Alt Text
renameUsersMatchingPattern.php (6 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment