3 namespace MediaWiki\RenameUser;
17 use Psr\Log\LoggerInterface;
88 private $debugPrefix =
'';
99 private $loadBalancer;
102 private $userFactory;
105 private $jobQueueGroup;
108 private $titleFactory;
114 private $updateRowsPerJob;
130 $this->hookRunner =
new HookRunner( $services->getHookContainer() );
131 $this->loadBalancer = $services->getDBLoadBalancer();
132 $this->userFactory = $services->getUserFactory();
133 $this->jobQueueGroup = $services->getJobQueueGroup();
134 $this->titleFactory = $services->getTitleFactory();
141 $this->renamer = $renamer;
142 $this->checkIfUserExists =
true;
144 if ( isset( $options[
'checkIfUserExists'] ) ) {
145 $this->checkIfUserExists = $options[
'checkIfUserExists'];
148 if ( isset( $options[
'debugPrefix'] ) ) {
149 $this->debugPrefix = $options[
'debugPrefix'];
152 if ( isset( $options[
'reason'] ) ) {
153 $this->reason = $options[
'reason'];
157 $this->tablesJob = [];
159 $this->hookRunner->onRenameUserSQL( $this );
163 if ( $this->debugPrefix ) {
164 $msg =
"{$this->debugPrefix}: $msg";
166 $this->logger->debug( $msg );
175 $contribs = $this->userFactory->newFromId( $this->uid )->getEditCount();
177 $dbw = $this->loadBalancer->getConnection(
DB_PRIMARY );
178 $atomicId = $dbw->startAtomic( __METHOD__, $dbw::ATOMIC_CANCELABLE );
180 $this->hookRunner->onRenameUserPreRename( $this->uid, $this->old, $this->
new );
183 if ( $this->checkIfUserExists && !$this->lockUserAndGetId( $this->old ) ) {
184 $this->
debug(
"User {$this->old} does not exist, bailing out" );
185 $dbw->cancelAtomic( __METHOD__, $atomicId );
192 $this->
debug(
"Starting rename of {$this->old} to {$this->new}" );
193 $dbw->newUpdateQueryBuilder()
195 ->set( [
'user_name' => $this->
new,
'user_touched' => $dbw->timestamp() ] )
196 ->where( [
'user_name' => $this->old,
'user_id' => $this->uid ] )
197 ->caller( __METHOD__ )->execute();
198 $dbw->newUpdateQueryBuilder()
200 ->set( [
'actor_name' => $this->
new ] )
201 ->where( [
'actor_name' => $this->old,
'actor_user' => $this->uid ] )
202 ->caller( __METHOD__ )->execute();
206 $user = $this->userFactory->newFromId( $this->uid );
208 $user->load( User::READ_LATEST );
212 $user->invalidateCache();
215 $dbw->newUpdateQueryBuilder()
216 ->update(
'ipblocks' )
217 ->set( [
'ipb_address' => $this->
new ] )
218 ->where( [
'ipb_user' => $this->uid,
'ipb_address' => $this->old ] )
219 ->caller( __METHOD__ )->execute();
224 $oldTitle = $this->titleFactory->makeTitle(
NS_USER, $this->old );
225 $newTitle = $this->titleFactory->makeTitle(
NS_USER, $this->
new );
226 $this->
debug(
"Updating logging table for {$this->old} to {$this->new}" );
231 $dbw->newUpdateQueryBuilder()
232 ->update(
'logging' )
233 ->set( [
'log_title' => $newTitle->getDBkey() ] )
235 'log_type' => $logTypesOnUser,
237 'log_title' => $oldTitle->getDBkey()
239 ->caller( __METHOD__ )->execute();
241 $this->
debug(
"Updating recentchanges table for {$this->old} to {$this->new}" );
242 $dbw->newUpdateQueryBuilder()
243 ->update(
'recentchanges' )
244 ->set( [
'rc_title' => $newTitle->getDBkey() ] )
247 'rc_log_type' => $logTypesOnUser,
249 'rc_title' => $oldTitle->getDBkey()
251 ->caller( __METHOD__ )->execute();
254 foreach ( $this->tables as $table => $fieldSet ) {
255 [ $nameCol, $userCol ] = $fieldSet;
256 $dbw->newUpdateQueryBuilder()
258 ->set( [ $nameCol => $this->
new ] )
259 ->where( [ $nameCol => $this->old, $userCol => $this->uid ] )
260 ->caller( __METHOD__ )->execute();
272 foreach ( $this->tablesJob as $table => $params ) {
277 $res = $dbw->newSelectQueryBuilder()
278 ->select( [ $timestampC ] )
280 ->where( [ $userTextC => $this->old, $userIDC => $this->uid ] )
281 ->orderBy( $timestampC, SelectQueryBuilder::SORT_ASC )
282 ->caller( __METHOD__ )->fetchResultSet();
285 $jobParams[
'table'] = $table;
286 $jobParams[
'column'] = $userTextC;
287 $jobParams[
'uidColumn'] = $userIDC;
288 $jobParams[
'timestampColumn'] = $timestampC;
293 $jobParams[
'minTimestamp'] =
'0';
294 $jobParams[
'maxTimestamp'] =
'0';
295 $jobParams[
'count'] = 0;
297 if ( isset( $params[
'uniqueKey'] ) ) {
298 $jobParams[
'uniqueKey'] = $params[
'uniqueKey'];
302 foreach ( $res as $row ) {
303 # Since the ORDER BY is ASC, set the min timestamp with first row
304 if ( $jobParams[
'count'] === 0 ) {
305 $jobParams[
'minTimestamp'] = $row->$timestampC;
307 # Keep updating the last timestamp, so it should be correct
308 # when the last item is added.
309 $jobParams[
'maxTimestamp'] = $row->$timestampC;
311 $jobParams[
'count']++;
312 # Once a job has $wgUpdateRowsPerJob rows, add it to the queue
313 if ( $jobParams[
'count'] >= $this->updateRowsPerJob ) {
315 $jobParams[
'minTimestamp'] =
'0';
316 $jobParams[
'maxTimestamp'] =
'0';
317 $jobParams[
'count'] = 0;
320 # If there are any job rows left, add it to the queue as one job
321 if ( $jobParams[
'count'] > 0 ) {
328 $logEntry->setPerformer( $this->renamer );
329 $logEntry->setTarget( $oldTitle );
330 $logEntry->setComment( $this->reason );
331 $logEntry->setParameters( [
332 '4::olduser' => $this->old,
333 '5::newuser' => $this->
new,
334 '6::edits' => $contribs
336 $logid = $logEntry->insert();
341 $count = count( $jobs );
343 $this->jobQueueGroup->push( $jobs );
344 $this->
debug(
"Queued $count jobs for {$this->old} to {$this->new}" );
348 $dbw->endAtomic( __METHOD__ );
351 $dbw->onTransactionCommitOrIdle(
352 function () use ( $dbw, $logEntry, $logid, $fname ) {
353 $dbw->startAtomic( $fname );
355 $user = $this->userFactory->newFromId( $this->uid );
356 $user->load( User::READ_LATEST );
358 $user->saveSettings();
359 $this->hookRunner->onRenameUserComplete( $this->uid, $this->old, $this->
new );
361 $logEntry->publish( $logid );
362 $dbw->endAtomic( $fname );
367 $this->
debug(
"Finished rename for {$this->old} to {$this->new}" );
376 private function lockUserAndGetId( $name ) {
377 return (
int)$this->loadBalancer->getConnection(
DB_PRIMARY )->newSelectQueryBuilder()
378 ->select(
'user_id' )
381 ->where( [
'user_name' => $name ] )
382 ->caller( __METHOD__ )->fetchField();
Handle enqueueing of background jobs.
Job queue task description base code.
Class for creating new log entries and inserting them into the database.
A class containing constants representing the names of configuration variables.
const UpdateRowsPerJob
Name constant for the UpdateRowsPerJob setting, for use with Config::get()
Custom job to perform updates on tables in busier environments.