Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 80 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
FixStuckGlobalRename | |
0.00% |
0 / 74 |
|
0.00% |
0 / 2 |
132 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 66 |
|
0.00% |
0 / 1 |
110 |
1 | <?php |
2 | |
3 | use MediaWiki\Extension\CentralAuth\CentralAuthServices; |
4 | use MediaWiki\Extension\CentralAuth\GlobalRename\LocalRenameJob\LocalRenameUserJob; |
5 | use MediaWiki\Extension\CentralAuth\User\CentralAuthUser; |
6 | use MediaWiki\MediaWikiServices; |
7 | use MediaWiki\Title\Title; |
8 | use MediaWiki\User\User; |
9 | use MediaWiki\WikiMap\WikiMap; |
10 | use Wikimedia\Rdbms\SelectQueryBuilder; |
11 | |
12 | $IP = getenv( 'MW_INSTALL_PATH' ); |
13 | if ( $IP === false ) { |
14 | $IP = __DIR__ . '/../../..'; |
15 | } |
16 | require_once "$IP/maintenance/Maintenance.php"; |
17 | |
18 | /** |
19 | * Script to run global renames that are stuck in the status table with "queued" or "in progress" |
20 | * but failed for whatever reason |
21 | */ |
22 | class FixStuckGlobalRename extends Maintenance { |
23 | public function __construct() { |
24 | parent::__construct(); |
25 | $this->requireExtension( 'CentralAuth' ); |
26 | $this->addArg( 'oldname', 'Old name' ); |
27 | $this->addArg( 'newname', 'New name' ); |
28 | $this->addOption( 'logwiki', 'Wiki where the log entry exists', true, true ); |
29 | $this->addOption( 'ignorestatus', 'Ignore rename status. Don\'t do this when the rename ' |
30 | . 'jobs might still be running.' ); |
31 | $this->addDescription( 'Unstuck global rename on a single wiki' ); |
32 | } |
33 | |
34 | public function execute() { |
35 | global $wgLocalDatabases; |
36 | $userNameUtils = MediaWikiServices::getInstance()->getUserNameUtils(); |
37 | $databaseManager = CentralAuthServices::getDatabaseManager(); |
38 | |
39 | $oldName = $userNameUtils->getCanonical( $this->getArg( 0 ) ); |
40 | $newName = $userNameUtils->getCanonical( $this->getArg( 1 ) ); |
41 | if ( $oldName === false || $newName === false ) { |
42 | $this->fatalError( 'Invalid name' ); |
43 | } |
44 | |
45 | $logTitle = Title::newFromText( 'Special:CentralAuth' )->getSubpage( $newName ); |
46 | $ca = new CentralAuthUser( $newName ); |
47 | if ( !$ca->renameInProgressOn( WikiMap::getCurrentWikiId() ) ) { |
48 | $this->fatalError( "{$ca->getName()} does not have a rename in progress on this wiki." ); |
49 | } |
50 | |
51 | $dbr = $databaseManager->getLocalDB( DB_REPLICA, $this->getOption( 'logwiki' ) ); |
52 | $queryData = DatabaseLogEntry::getSelectQueryData(); |
53 | $row = $dbr->newSelectQueryBuilder() |
54 | ->tables( $queryData['tables'] ) |
55 | ->select( $queryData['fields'] ) |
56 | ->where( $queryData['conds'] ) |
57 | ->andWhere( [ |
58 | 'log_type' => 'gblrename', |
59 | 'log_action' => 'rename', |
60 | 'log_namespace' => NS_SPECIAL, |
61 | 'log_title' => $logTitle->getDBkey(), |
62 | ] ) |
63 | ->options( $queryData['options'] ) |
64 | ->orderBy( 'log_timestamp', SelectQueryBuilder::SORT_DESC ) |
65 | ->joinConds( $queryData['join_conds'] ) |
66 | ->caller( __METHOD__ ) |
67 | ->fetchRow(); |
68 | |
69 | // try to guess the options if the log record does not contain them |
70 | $movepages = true; |
71 | $suppressredirects = false; |
72 | if ( $row ) { |
73 | $logEntry = DatabaseLogEntry::newFromRow( $row ); |
74 | $renamer = $logEntry->getPerformerIdentity()->getName(); |
75 | $comment = $logEntry->getComment(); |
76 | $logParams = $logEntry->getParameters(); |
77 | if ( isset( $logParams['movepages'] ) ) { |
78 | $movepages = $logParams['movepages']; |
79 | } |
80 | if ( isset( $logParams['suppressredirects'] ) ) { |
81 | $suppressredirects = $logParams['suppressredirects']; |
82 | } |
83 | $this->output( "Using $renamer as the renamer.\n" ); |
84 | } else { |
85 | $this->output( "Could not find log entry, falling back to system account\n" ); |
86 | // Go through User::newSystemUser() to ensure the user exists (T344632). |
87 | // The username is reserved in onUserGetReservedNames() hook. |
88 | $renamer = User::newSystemUser( 'Global rename script', [ 'steal' => true ] )->getName(); |
89 | $comment = ''; |
90 | } |
91 | |
92 | $params = [ |
93 | 'from' => $oldName, |
94 | 'to' => $newName, |
95 | 'renamer' => $renamer, |
96 | 'movepages' => $movepages, |
97 | 'suppressredirects' => $suppressredirects, |
98 | 'reason' => $comment, |
99 | 'ignorestatus' => $this->getOption( 'ignorestatus', false ), |
100 | // No way to recover localuser attachment details, faking it. |
101 | // $wgLocalDatabases will contain wikis where the user does not have an account. |
102 | // That's fine, LocalRenameUserJob will only use the one that matches WikiMap::getCurrentWikiId(). |
103 | // FIXME the real attachment info should be saved & restored instead, see T215107 |
104 | 'reattach' => array_fill_keys( $wgLocalDatabases, [ |
105 | 'attachedMethod' => 'admin', |
106 | 'attachedTimestamp' => wfTimestamp( TS_MW ), |
107 | ] ), |
108 | ]; |
109 | foreach ( $params as $key => $value ) { |
110 | if ( $key === 'reattach' ) { |
111 | continue; |
112 | } |
113 | $this->output( "$key: $value\n" ); |
114 | } |
115 | $title = Title::newFromText( 'Global rename job' ); |
116 | $job = new LocalRenameUserJob( $title, $params ); |
117 | $this->output( "\nStarting to run job...\n" ); |
118 | $status = $job->run(); |
119 | $job->teardown( $status ); |
120 | $this->output( $status ? "Done!\n" : "Failed!\n" ); |
121 | } |
122 | } |
123 | |
124 | $maintClass = FixStuckGlobalRename::class; |
125 | require_once RUN_MAINTENANCE_IF_MAIN; |