MediaWiki  1.34.0
renameUserCleanup.php
Go to the documentation of this file.
1 <?php
26 $IP = getenv( 'MW_INSTALL_PATH' );
27 if ( $IP === false ) {
28  $IP = __DIR__ . '/../../..';
29 }
30 require_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 ) {
97  $dbr = wfGetDB( DB_REPLICA );
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::class;
350 require_once RUN_MAINTENANCE_IF_MAIN;
RUN_MAINTENANCE_IF_MAIN
const RUN_MAINTENANCE_IF_MAIN
Definition: Maintenance.php:39
$maintClass
$maintClass
Definition: renameUserCleanup.php:349
RenameUserCleanup
Definition: renameUserCleanup.php:32
Maintenance\addDescription
addDescription( $text)
Set the description text.
Definition: Maintenance.php:348
RenameUserCleanup\execute
execute()
Do the actual work.
Definition: renameUserCleanup.php:45
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:515
Maintenance
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: Maintenance.php:82
$success
$success
Definition: NoLocalSettings.php:42
$dbr
$dbr
Definition: testCompression.php:50
Maintenance\rollbackTransaction
rollbackTransaction(IDatabase $dbw, $fname)
Rollback the transcation on a DB handle.
Definition: Maintenance.php:1461
Maintenance\beginTransaction
beginTransaction(IDatabase $dbw, $fname)
Begin a transcation on a DB.
Definition: Maintenance.php:1426
RenameUserCleanup\checkRenameLog
checkRenameLog( $olduser, $newuser)
Definition: renameUserCleanup.php:96
RenameuserSQL\actorMigrationWriteOld
static actorMigrationWriteOld()
Indicate whether we should still write old user fields.
Definition: RenameuserSQL.php:405
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2575
Maintenance\addOption
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
Definition: Maintenance.php:267
Maintenance\requireExtension
requireExtension( $name)
Indicate that the specified extension must be loaded before the script can run.
Definition: Maintenance.php:638
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:586
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
DB_MASTER
const DB_MASTER
Definition: defines.php:26
$line
$line
Definition: cdb.php:59
RenameUserCleanup\updateTable
updateTable( $table, $usernamefield, $useridfield, $timestampfield, $olduser, $newuser, $uid)
Definition: renameUserCleanup.php:250
Maintenance\commitTransaction
commitTransaction(IDatabase $dbw, $fname)
Commit the transcation on a DB handle and wait for replica DBs to catch up.
Definition: Maintenance.php:1441
RenameUserCleanup\__construct
__construct()
Default constructor.
Definition: renameUserCleanup.php:33
Maintenance\getOption
getOption( $name, $default=null)
Get an option, or return the default.
Definition: Maintenance.php:302
$IP
$IP
Maintenance script to clean up after incomplete user renames Sometimes user edits are left lying arou...
Definition: renameUserCleanup.php:26
NS_USER
const NS_USER
Definition: Defines.php:62
Maintenance\error
error( $err, $die=0)
Throw an error to the user.
Definition: Maintenance.php:481
Maintenance\output
output( $out, $channel=null)
Throw some output to the user.
Definition: Maintenance.php:453
RenameUserCleanup\doUpdates
doUpdates( $olduser, $newuser, $uid)
Definition: renameUserCleanup.php:184
CommentStore\getStore
static getStore()
Definition: CommentStore.php:139
RenameUserCleanup\checkUserExistence
checkUserExistence( $olduser, $newuser)
Definition: renameUserCleanup.php:72
Maintenance\setBatchSize
setBatchSize( $s=0)
Set the batch size.
Definition: Maintenance.php:394