MediaWiki REL1_32
SpecialRenameuser.php
Go to the documentation of this file.
1<?php
2
8 public function __construct() {
9 parent::__construct( 'Renameuser', 'renameuser' );
10 }
11
12 public function doesWrites() {
13 return true;
14 }
15
25 public function execute( $par ) {
27
28 $this->setHeaders();
29 $this->addHelpLink( 'Help:Renameuser' );
30
31 $out = $this->getOutput();
32 $out->addWikiMsg( 'renameuser-summary' );
33
34 $user = $this->getUser();
35 if ( !$user->isAllowed( 'renameuser' ) ) {
36 throw new PermissionsError( 'renameuser' );
37 }
38
39 if ( wfReadOnly() ) {
40 throw new ReadOnlyError;
41 }
42
43 if ( $user->isBlocked() ) {
44 throw new UserBlockedError( $this->getUser()->mBlock );
45 }
46
48
49 $request = $this->getRequest();
50 $showBlockLog = $request->getBool( 'submit-showBlockLog' );
51 $usernames = explode( '/', $par, 2 ); // this works as "/" is not valid in usernames
52 $oldnamePar = trim( str_replace( '_', ' ', $request->getText( 'oldusername', $usernames[0] ) ) );
53 $oldusername = Title::makeTitle( NS_USER, $oldnamePar );
54 $newnamePar = isset( $usernames[1] ) ? $usernames[1] : null;
55 $newnamePar = trim( str_replace( '_', ' ', $request->getText( 'newusername', $newnamePar ) ) );
56 // Force uppercase of newusername, otherwise wikis
57 // with wgCapitalLinks=false can create lc usernames
58 $newusername = Title::makeTitleSafe( NS_USER, $wgContLang->ucfirst( $newnamePar ) );
59 $oun = is_object( $oldusername ) ? $oldusername->getText() : '';
60 $nun = is_object( $newusername ) ? $newusername->getText() : '';
61 $token = $user->getEditToken();
62 $reason = $request->getText( 'reason' );
63
64 $move_checked = $request->getBool( 'movepages', !$request->wasPosted() );
65 $suppress_checked = $request->getCheck( 'suppressredirect' );
66
67 $warnings = [];
68 if ( $oun && $nun && !$request->getCheck( 'confirmaction' ) ) {
69 Hooks::run( 'RenameUserWarning', [ $oun, $nun, &$warnings ] );
70 }
71
72 $out->addHTML(
73 Xml::openElement( 'form', [
74 'method' => 'post',
75 'action' => $this->getPageTitle()->getLocalURL(),
76 'id' => 'renameuser'
77 ] ) .
78 Xml::openElement( 'fieldset' ) .
79 Xml::element( 'legend', null, $this->msg( 'renameuser' )->text() ) .
80 Xml::openElement( 'table', [ 'id' => 'mw-renameuser-table' ] ) .
81 "<tr>
82 <td class='mw-label'>" .
83 Xml::label( $this->msg( 'renameuserold' )->text(), 'oldusername' ) .
84 "</td>
85 <td class='mw-input'>" .
86 Xml::input( 'oldusername', 20, $oun, [ 'type' => 'text', 'tabindex' => '1' ] ) . ' ' .
87 "</td>
88 </tr>
89 <tr>
90 <td class='mw-label'>" .
91 Xml::label( $this->msg( 'renameusernew' )->text(), 'newusername' ) .
92 "</td>
93 <td class='mw-input'>" .
94 Xml::input( 'newusername', 20, $nun, [ 'type' => 'text', 'tabindex' => '2' ] ) .
95 "</td>
96 </tr>
97 <tr>
98 <td class='mw-label'>" .
99 Xml::label( $this->msg( 'renameuserreason' )->text(), 'reason' ) .
100 "</td>
101 <td class='mw-input'>" .
102 Xml::input(
103 'reason',
104 40,
105 $reason,
106 [ 'type' => 'text', 'tabindex' => '3', 'maxlength' => 255 ]
107 ) .
108 '</td>
109 </tr>'
110 );
111 if ( $user->isAllowed( 'move' ) ) {
112 $out->addHTML( "
113 <tr>
114 <td>&#160;
115 </td>
116 <td class='mw-input'>" .
117 Xml::checkLabel( $this->msg( 'renameusermove' )->text(), 'movepages', 'movepages',
118 $move_checked, [ 'tabindex' => '4' ] ) .
119 '</td>
120 </tr>'
121 );
122
123 if ( $user->isAllowed( 'suppressredirect' ) ) {
124 $out->addHTML( "
125 <tr>
126 <td>&#160;
127 </td>
128 <td class='mw-input'>" .
129 Xml::checkLabel(
130 $this->msg( 'renameusersuppress' )->text(),
131 'suppressredirect',
132 'suppressredirect',
133 $suppress_checked,
134 [ 'tabindex' => '5' ]
135 ) .
136 '</td>
137 </tr>'
138 );
139 }
140 }
141 if ( $warnings ) {
142 $warningsHtml = [];
143 foreach ( $warnings as $warning ) {
144 $warningsHtml[] = is_array( $warning ) ?
145 $this->msg( $warning[0] )->rawParams( array_slice( $warning, 1 ) )->escaped() :
146 $this->msg( $warning )->escaped();
147 }
148
149 $out->addHTML( "
150 <tr>
151 <td class='mw-label'>" . $this->msg( 'renameuserwarnings' )->escaped() . "
152 </td>
153 <td class='mw-input'>" .
154 '<ul class="error"><li>' .
155 implode( '</li><li>', $warningsHtml ) . '</li></ul>' .
156 '</td>
157 </tr>'
158 );
159 $out->addHTML( "
160 <tr>
161 <td>&#160;
162 </td>
163 <td class='mw-input'>" .
164 Xml::checkLabel(
165 $this->msg( 'renameuserconfirm' )->text(),
166 'confirmaction',
167 'confirmaction',
168 false,
169 [ 'tabindex' => '6' ]
170 ) .
171 '</td>
172 </tr>'
173 );
174 }
175 $out->addHTML( "
176 <tr>
177 <td>&#160;
178 </td>
179 <td class='mw-submit'>" .
180 Xml::submitButton(
181 $this->msg( 'renameusersubmit' )->text(),
182 [
183 'name' => 'submit',
184 'tabindex' => '7',
185 'id' => 'submit'
186 ]
187 ) .
188 ' ' .
189 Xml::submitButton(
190 $this->msg( 'renameuser-submit-blocklog' )->text(),
191 [
192 'name' => 'submit-showBlockLog',
193 'id' => 'submit-showBlockLog',
194 'tabindex' => '8'
195 ]
196 ) .
197 '</td>
198 </tr>' .
199 Xml::closeElement( 'table' ) .
200 Xml::closeElement( 'fieldset' ) .
201 Html::hidden( 'token', $token ) .
202 Xml::closeElement( 'form' ) . "\n"
203 );
204
205 // Show block log if requested
206 if ( $showBlockLog && is_object( $oldusername ) ) {
207 $this->showLogExtract( $oldusername, 'block', $out );
208
209 return;
210 }
211
212 if ( $request->getText( 'token' ) === '' ) {
213 # They probably haven't even submitted the form, so don't go further.
214 return;
215 } elseif ( $warnings ) {
216 # Let user read warnings
217 return;
218 } elseif ( !$request->wasPosted() || !$user->matchEditToken( $request->getVal( 'token' ) ) ) {
219 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>", 'renameuser-error-request' );
220
221 return;
222 } elseif ( !is_object( $oldusername ) ) {
223 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
224 [ 'renameusererrorinvalid', $request->getText( 'oldusername' ) ] );
225
226 return;
227 } elseif ( !is_object( $newusername ) ) {
228 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
229 [ 'renameusererrorinvalid', $request->getText( 'newusername' ) ] );
230
231 return;
232 } elseif ( $oldusername->getText() === $newusername->getText() ) {
233 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>", 'renameuser-error-same-user' );
234
235 return;
236 }
237
238 // Suppress username validation of old username
239 $olduser = User::newFromName( $oldusername->getText(), false );
240 $newuser = User::newFromName( $newusername->getText(), 'creatable' );
241
242 // It won't be an object if for instance "|" is supplied as a value
243 if ( !is_object( $olduser ) ) {
244 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
245 [ 'renameusererrorinvalid', $oldusername->getText() ] );
246
247 return;
248 }
249 if ( !is_object( $newuser ) || !User::isCreatableName( $newuser->getName() ) ) {
250 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
251 [ 'renameusererrorinvalid', $newusername->getText() ] );
252
253 return;
254 }
255
256 // Check for the existence of lowercase oldusername in database.
257 // Until r19631 it was possible to rename a user to a name with first character as lowercase
258 if ( $oldusername->getText() !== $wgContLang->ucfirst( $oldusername->getText() ) ) {
259 // oldusername was entered as lowercase -> check for existence in table 'user'
261 $uid = $dbr->selectField( 'user', 'user_id',
262 [ 'user_name' => $oldusername->getText() ],
263 __METHOD__ );
264 if ( $uid === false ) {
265 if ( !$wgCapitalLinks ) {
266 $uid = 0; // We are on a lowercase wiki but lowercase username does not exists
267 } else {
268 // We are on a standard uppercase wiki, use normal
269 $uid = $olduser->idForName();
270 $oldusername = Title::makeTitleSafe( NS_USER, $olduser->getName() );
271 }
272 }
273 } else {
274 // oldusername was entered as upperase -> standard procedure
275 $uid = $olduser->idForName();
276 }
277
278 if ( $uid === 0 ) {
279 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
280 [ 'renameusererrordoesnotexist', $oldusername->getText() ] );
281
282 return;
283 }
284
285 if ( $newuser->idForName() !== 0 ) {
286 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
287 [ 'renameusererrorexists', $newusername->getText() ] );
288
289 return;
290 }
291
292 // Give other affected extensions a chance to validate or abort
293 if ( !Hooks::run(
294 'RenameUserAbort',
295 [ $uid, $oldusername->getText(), $newusername->getText() ]
296 ) ) {
297 return;
298 }
299
300 // Do the heavy lifting...
301 $rename = new RenameuserSQL(
302 $oldusername->getText(),
303 $newusername->getText(),
304 $uid,
305 $this->getUser(),
306 [ 'reason' => $reason ]
307 );
308 if ( !$rename->rename() ) {
309 return;
310 }
311
312 // If this user is renaming his/herself, make sure that Title::moveTo()
313 // doesn't make a bunch of null move edits under the old name!
314 if ( $user->getId() === $uid ) {
315 $user->setName( $newusername->getText() );
316 }
317
318 // Move any user pages
319 if ( $request->getCheck( 'movepages' ) && $user->isAllowed( 'move' ) ) {
321
322 $pages = $dbr->select(
323 'page',
324 [ 'page_namespace', 'page_title' ],
325 [
326 'page_namespace' => [ NS_USER, NS_USER_TALK ],
327 $dbr->makeList( [
328 'page_title ' . $dbr->buildLike( $oldusername->getDBkey() . '/', $dbr->anyString() ),
329 'page_title = ' . $dbr->addQuotes( $oldusername->getDBkey() ),
330 ], LIST_OR ),
331 ],
332 __METHOD__
333 );
334
335 $suppressRedirect = false;
336
337 if ( $request->getCheck( 'suppressredirect' ) && $user->isAllowed( 'suppressredirect' ) ) {
338 $suppressRedirect = true;
339 }
340
341 $output = '';
343 foreach ( $pages as $row ) {
344 $oldPage = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
345 $newPage = Title::makeTitleSafe( $row->page_namespace,
346 preg_replace( '!^[^/]+!', $newusername->getDBkey(), $row->page_title ) );
347 # Do not autodelete or anything, title must not exist
348 if ( $newPage->exists() && !$oldPage->isValidMoveTarget( $newPage ) ) {
349 $link = $linkRenderer->makeKnownLink( $newPage );
350 $output .= Html::rawElement(
351 'li',
352 [ 'class' => 'mw-renameuser-pe' ],
353 $this->msg( 'renameuser-page-exists' )->rawParams( $link )->escaped()
354 );
355 } else {
356 $success = $oldPage->moveTo(
357 $newPage,
358 false,
359 $this->msg(
360 'renameuser-move-log',
361 $oldusername->getText(),
362 $newusername->getText() )->inContentLanguage()->text(),
363 !$suppressRedirect
364 );
365 if ( $success === true ) {
366 # oldPage is not known in case of redirect suppression
367 $oldLink = $linkRenderer->makeLink( $oldPage, null, [], [ 'redirect' => 'no' ] );
368
369 # newPage is always known because the move was successful
370 $newLink = $linkRenderer->makeKnownLink( $newPage );
371
372 $output .= Html::rawElement(
373 'li',
374 [ 'class' => 'mw-renameuser-pm' ],
375 $this->msg( 'renameuser-page-moved' )->rawParams( $oldLink, $newLink )->escaped()
376 );
377 } else {
378 $oldLink = $linkRenderer->makeKnownLink( $oldPage );
379 $newLink = $linkRenderer->makeLink( $newPage );
380 $output .= Html::rawElement(
381 'li', [ 'class' => 'mw-renameuser-pu' ],
382 $this->msg( 'renameuser-page-unmoved' )->rawParams( $oldLink, $newLink )->escaped()
383 );
384 }
385 }
386 }
387 if ( $output ) {
388 $out->addHTML( Html::rawElement( 'ul', [], $output ) );
389 }
390 }
391
392 // Output success message stuff :)
393 $out->wrapWikiMsg( "<div class=\"successbox\">$1</div><br style=\"clear:both\" />",
394 [ 'renameusersuccess', $oldusername->getText(), $newusername->getText() ] );
395 }
396
402 protected function showLogExtract( $username, $type, &$out ) {
403 # Show relevant lines from the logs:
404 $logPage = new LogPage( $type );
405 $out->addHTML( Xml::element( 'h2', null, $logPage->getName()->text() ) . "\n" );
406 LogEventsList::showLogExtract( $out, $type, $username->getPrefixedText() );
407 }
408
417 public function prefixSearchSubpages( $search, $limit, $offset ) {
418 if ( !class_exists( 'UserNamePrefixSearch' ) ) { // check for version 1.27
419 return [];
420 }
421 $user = User::newFromName( $search );
422 if ( !$user ) {
423 // No prefix suggestion for invalid user
424 return [];
425 }
426 // Autocomplete subpage as user list - public to allow caching
427 return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
428 }
429
430 protected function getGroupName() {
431 return 'users';
432 }
433}
This list may contain false positives That usually means there is additional text with links below the first Each row contains links to the first and second as well as the first line of the second redirect text
$wgCapitalLinks
Set this to false to avoid forcing the first letter of links to capitals.
wfReadOnly()
Check whether the wiki is in read-only mode.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
$wgContLang
Definition Setup.php:809
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Class to simplify the use of log pages.
Definition LogPage.php:33
Show an error when a user tries to do something they do not have the necessary permissions for.
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Class which performs the actual renaming of users.
Parent class for all special pages.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getOutput()
Get the OutputPage being used for this instance.
getUser()
Shortcut to get the User executing this instance.
msg( $key)
Wrapper around wfMessage that sets the current context.
getRequest()
Get the WebRequest being used for this instance.
getPageTitle( $subpage=false)
Get a self-referential title object.
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
MediaWiki Linker LinkRenderer null $linkRenderer
Special page that allows authorised users to rename user accounts.
doesWrites()
Indicates whether this special page may perform database writes.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
execute( $par)
Show the special page.
showLogExtract( $username, $type, &$out)
Show an error when the user tries to do something whilst blocked.
static search( $audience, $search, $limit, $offset=0)
Do a prefix search of user names and return a list of matching user names.
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition User.php:592
static isCreatableName( $name)
Usernames which fail to pass this function will be blocked from new account registrations,...
Definition User.php:1121
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 also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on $request
Definition hooks.txt:2880
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition hooks.txt:894
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition hooks.txt:3106
this hook is for auditing only or null if authentication failed before getting that far $username
Definition hooks.txt:815
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title e g db for database replication lag or jobqueue for job queue size converted to pseudo seconds It is possible to add more fields and they will be returned to the user in the API response after the basic globals have been set but before ordinary actions take place $output
Definition hooks.txt:2317
const LIST_OR
Definition Defines.php:46
const NS_USER_TALK
Definition Defines.php:67
const DB_REPLICA
Definition defines.php:25