MediaWiki REL1_31
renameUserCleanup.php
Go to the documentation of this file.
1<?php
26$IP = getenv( 'MW_INSTALL_PATH' );
27if ( $IP === false ) {
28 $IP = __DIR__ . '/../../..';
29}
30require_once "$IP/maintenance/Maintenance.php";
31
33 public function __construct() {
34 parent::__construct();
35 $this->addDescription( 'Maintenance script to finish incomplete rename user,'
36 . ' in particular to reassign edits that were missed' );
37 $this->addOption( 'olduser', 'Old user name', true, true );
38 $this->addOption( 'newuser', 'New user name', true, true );
39 $this->addOption( 'olduid', 'Old user id in revision records (DANGEROUS)', false, true );
40 $this->setBatchSize( 1000 );
41
42 $this->requireExtension( 'Renameuser' );
43 }
44
45 public function execute() {
47 $this->output( "Core xx_user_text fields are no longer used, no updates should be needed.\n" );
48 return;
49 }
50
51 $this->output( "Rename User Cleanup starting...\n\n" );
52 $olduser = User::newFromName( $this->getOption( 'olduser' ) );
53 $newuser = User::newFromName( $this->getOption( 'newuser' ) );
54 $olduid = $this->getOption( 'olduid' );
55
56 $this->checkUserExistence( $olduser, $newuser );
57 $this->checkRenameLog( $olduser, $newuser );
58
59 if ( $olduid ) {
60 $this->doUpdates( $olduser, $newuser, $olduid );
61 }
62 $this->doUpdates( $olduser, $newuser, $newuser->getId() );
63 $this->doUpdates( $olduser, $newuser, 0 );
64
65 $this->output( "Done!\n" );
66 }
67
72 public function checkUserExistence( $olduser, $newuser ) {
73 if ( !$newuser->getId() ) {
74 $this->error( 'No such user: ' . $this->getOption( 'newuser' ), true );
75 }
76 if ( $olduser->getId() ) {
77 $this->output( 'WARNING!!: Old user still exists: ' . $this->getOption( 'olduser' ) . "\n" );
78 $this->output( 'We\'ll only re-attribute edits that have the new user uid (or 0) ' );
79 $this->output( 'or the uid specified by the caller, and the old user name.' );
80 $this->output( 'Proceed anyway? [N/y] ' );
81
82 $stdin = fopen( 'php://stdin', 'rt' );
83 $line = fgets( $stdin );
84 fclose( $stdin );
85
86 if ( $line[0] !== 'Y' && $line[0] !== 'y' ) {
87 $this->output( "Exiting at users request\n" );
88 }
89 }
90 }
91
96 public function checkRenameLog( $olduser, $newuser ) {
98
99 $oldTitle = Title::makeTitle( NS_USER, $olduser->getName() );
100
101 $result = $dbr->select( 'logging', '*',
102 [ 'log_type' => 'renameuser',
103 'log_action' => 'renameuser',
104 'log_namespace' => NS_USER,
105 'log_title' => $oldTitle->getDBkey(),
106 'log_params' => $newuser->getName()
107 ],
108 __METHOD__
109 );
110 if ( !$result || !$result->numRows() ) {
111 // try the old format
112 if ( class_exists( CommentStore::class ) ) {
113 $commentStore = CommentStore::getStore();
114 $commentQuery = $commentStore->getJoin( 'log_comment' );
115 } else {
116 $commentStore = null;
117 $commentQuery = [
118 'tables' => [],
119 'fields' => [ 'log_comment' => 'log_comment' ],
120 'joins' => [],
121 ];
122 }
123 $result = $dbr->select(
124 [ 'logging' ] + $commentQuery['tables'],
125 [ 'log_title', 'log_timestamp' ] + $commentQuery['fields'],
126 [
127 'log_type' => 'renameuser',
128 'log_action' => 'renameuser',
129 'log_namespace' => NS_USER,
130 'log_title' => $olduser->getName(),
131 ],
132 __METHOD__,
133 [],
134 $commentQuery['joins']
135 );
136 if ( !$result || !$result->numRows() ) {
137 $this->output( 'No log entry found for a rename of ' . $olduser->getName() .
138 ' to ' . $newuser->getName() . ', proceed anyways? [N/y] ' );
139
140 $stdin = fopen( 'php://stdin', 'rt' );
141 $line = fgets( $stdin );
142 fclose( $stdin );
143
144 if ( $line[0] !== 'Y' && $line[0] !== 'y' ) {
145 $this->output( "Exiting at user's request\n" );
146 exit( 1 );
147 }
148 } else {
149 foreach ( $result as $row ) {
150 $comment = $commentStore
151 ? $commentStore->getComment( 'log_comment', $row )->text
152 : $row->log_comment;
153 $this->output( 'Found possible log entry of the rename, please check: ' .
154 $row->log_title . ' with comment ' . $comment .
155 " on $row->log_timestamp\n" );
156 }
157 }
158 } else {
159 foreach ( $result as $row ) {
160 $this->output( 'Found log entry of the rename: ' . $olduser->getName() .
161 ' to ' . $newuser->getName() . " on $row->log_timestamp\n" );
162 }
163 }
164 if ( $result && $result->numRows() > 1 ) {
165 print 'More than one rename entry found in the log, not sure ' .
166 'what to do. Proceed anyways? [N/y] ';
167
168 $stdin = fopen( 'php://stdin', 'rt' );
169 $line = fgets( $stdin );
170 fclose( $stdin );
171
172 if ( $line[0] !== 'Y' && $line[0] !== 'y' ) {
173 $this->output( "Exiting at users request\n" );
174 exit( 1 );
175 }
176 }
177 }
178
184 public function doUpdates( $olduser, $newuser, $uid ) {
185 $this->updateTable(
186 'revision',
187 'rev_user_text',
188 'rev_user',
189 'rev_timestamp',
190 $olduser,
191 $newuser,
192 $uid
193 );
194 $this->updateTable(
195 'archive',
196 'ar_user_text',
197 'ar_user',
198 'ar_timestamp',
199 $olduser,
200 $newuser,
201 $uid
202 );
203 $this->updateTable(
204 'logging',
205 'log_user_text',
206 'log_user',
207 'log_timestamp',
208 $olduser,
209 $newuser,
210 $uid
211 );
212 $this->updateTable(
213 'image',
214 'img_user_text',
215 'img_user',
216 'img_timestamp',
217 $olduser,
218 $newuser,
219 $uid
220 );
221 $this->updateTable(
222 'oldimage',
223 'oi_user_text',
224 'oi_user',
225 'oi_timestamp',
226 $olduser,
227 $newuser,
228 $uid
229 );
230 $this->updateTable(
231 'filearchive',
232 'fa_user_text',
233 'fa_user',
234 'fa_timestamp',
235 $olduser,
236 $newuser,
237 $uid
238 );
239 }
240
250 public function updateTable( $table, $usernamefield, $useridfield,
251 $timestampfield, $olduser, $newuser, $uid
252 ) {
253 $dbw = wfGetDB( DB_MASTER );
254
255 $contribs = $dbw->selectField(
256 $table,
257 'count(*)',
258 [
259 $usernamefield => $olduser->getName(),
260 $useridfield => $uid
261 ],
262 __METHOD__
263 );
264
265 if ( $contribs === 0 ) {
266 $this->output( "No edits to be re-attributed from table $table for uid $uid\n" );
267
268 return;
269 }
270
271 $this->output( "Found $contribs edits to be re-attributed from table $table for uid $uid\n" );
272 if ( $uid !== $newuser->getId() ) {
273 $this->output( 'If you proceed, the uid field will be set to that ' .
274 'of the new user name (i.e. ' . $newuser->getId() . ") in these rows.\n" );
275 }
276
277 $this->output( 'Proceed? [N/y] ' );
278
279 $stdin = fopen( 'php://stdin', 'rt' );
280 $line = fgets( $stdin );
281 fclose( $stdin );
282
283 if ( $line[0] !== 'Y' && $line[0] !== 'y' ) {
284 $this->output( "Skipping at user's request\n" );
285 return;
286 }
287
288 $selectConds = [ $usernamefield => $olduser->getName(), $useridfield => $uid ];
289 $updateFields = [ $usernamefield => $newuser->getName(), $useridfield => $newuser->getId() ];
290
291 while ( $contribs > 0 ) {
292 $this->output( 'Doing batch of up to approximately ' . $this->mBatchSize . "\n" );
293 $this->output( 'Do this batch? [N/y] ' );
294
295 $stdin = fopen( 'php://stdin', 'rt' );
296 $line = fgets( $stdin );
297 fclose( $stdin );
298
299 if ( $line[0] !== 'Y' && $line[0] !== 'y' ) {
300 $this->output( "Skipping at user's request\n" );
301 return;
302 }
303
304 $this->beginTransaction( $dbw, __METHOD__ );
305 $result = $dbw->select(
306 $table,
307 $timestampfield,
308 $selectConds,
309 __METHOD__,
310 [
311 'ORDER BY' => $timestampfield . ' DESC',
312 'LIMIT' => $this->mBatchSize
313 ]
314 );
315
316 if ( !$result ) {
317 $this->output( "There were rows for updating but now they are gone. Skipping.\n" );
318 $this->rollbackTransaction( $dbw, __METHOD__ );
319
320 return;
321 }
322
323 $result->seek( $result->numRows() - 1 );
324 $row = $result->fetchObject();
325 $timestamp = $dbw->addQuotes( $row->$timestampfield );
326 $updateCondsWithTime = array_merge( $selectConds, [ "$timestampfield >= $timestamp" ] );
327 $success = $dbw->update(
328 $table,
329 $updateFields,
330 $updateCondsWithTime,
331 __METHOD__
332 );
333
334 if ( $success ) {
335 $rowsDone = $dbw->affectedRows();
336 $this->commitTransaction( $dbw, __METHOD__ );
337 } else {
338 $this->rollbackTransaction( $dbw, __METHOD__ );
339 $this->error( "Problem with the update, rolling back and exiting\n", true );
340 }
341
342 // $contribs = User::edits( $olduser->getId() );
343 $contribs = $dbw->selectField( $table, 'count(*)', $selectConds, __METHOD__ );
344 $this->output( "Updated $rowsDone edits; $contribs edits remaining to be re-attributed\n" );
345 }
346 }
347}
348
349$maintClass = 'RenameUserCleanup';
350require_once RUN_MAINTENANCE_IF_MAIN;
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
$line
Definition cdb.php:59
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
requireExtension( $name)
Indicate that the specified extension must be loaded before the script can run.
beginTransaction(IDatabase $dbw, $fname)
Begin a transcation on a DB.
commitTransaction(IDatabase $dbw, $fname)
Commit the transcation on a DB handle and 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.
rollbackTransaction(IDatabase $dbw, $fname)
Rollback the transcation on a DB handle.
setBatchSize( $s=0)
Set the batch size.
__construct()
Default constructor.
checkUserExistence( $olduser, $newuser)
doUpdates( $olduser, $newuser, $uid)
updateTable( $table, $usernamefield, $useridfield, $timestampfield, $olduser, $newuser, $uid)
execute()
Do the actual work.
checkRenameLog( $olduser, $newuser)
static getActorMigrationStage()
Fetch the core actor table schema migration stage.
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition User.php:591
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add in any and then calling output() to send it all. It could be easily changed to send incrementally if that becomes useful
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults error
Definition hooks.txt:2612
while(( $__line=Maintenance::readconsole()) !==false) print
Definition eval.php:64
versus $oldTitle
Definition globals.txt:16
const MIGRATION_NEW
Definition Defines.php:305
$IP
Maintenance script to clean up after incomplete user renames Sometimes user edits are left lying arou...
require_once RUN_MAINTENANCE_IF_MAIN
const DB_REPLICA
Definition defines.php:25
const DB_MASTER
Definition defines.php:29