MediaWiki REL1_33
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 = $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 MovePage::move()
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
348 $movePage = new MovePage( $oldPage, $newPage );
349 $validMoveStatus = $movePage->isValidMove();
350
351 # Do not autodelete or anything, title must not exist
352 if ( $newPage->exists() && !$validMoveStatus->isOK() ) {
353 $link = $linkRenderer->makeKnownLink( $newPage );
354 $output .= Html::rawElement(
355 'li',
356 [ 'class' => 'mw-renameuser-pe' ],
357 $this->msg( 'renameuser-page-exists' )->rawParams( $link )->escaped()
358 );
359 } else {
360 $logReason = $this->msg(
361 'renameuser-move-log', $oldusername->getText(), $newusername->getText()
362 )->inContentLanguage()->text();
363
364 $moveStatus = $movePage->move( $user, $logReason, !$suppressRedirect );
365
366 if ( $moveStatus->isOK() ) {
367 # oldPage is not known in case of redirect suppression
368 $oldLink = $linkRenderer->makeLink( $oldPage, null, [], [ 'redirect' => 'no' ] );
369
370 # newPage is always known because the move was successful
371 $newLink = $linkRenderer->makeKnownLink( $newPage );
372
373 $output .= Html::rawElement(
374 'li',
375 [ 'class' => 'mw-renameuser-pm' ],
376 $this->msg( 'renameuser-page-moved' )->rawParams( $oldLink, $newLink )->escaped()
377 );
378 } else {
379 $oldLink = $linkRenderer->makeKnownLink( $oldPage );
380 $newLink = $linkRenderer->makeLink( $newPage );
381 $output .= Html::rawElement(
382 'li', [ 'class' => 'mw-renameuser-pu' ],
383 $this->msg( 'renameuser-page-unmoved' )->rawParams( $oldLink, $newLink )->escaped()
384 );
385 }
386 }
387 }
388 if ( $output ) {
389 $out->addHTML( Html::rawElement( 'ul', [], $output ) );
390 }
391 }
392
393 // Output success message stuff :)
394 $out->wrapWikiMsg( "<div class=\"successbox\">$1</div><br style=\"clear:both\" />",
395 [ 'renameusersuccess', $oldusername->getText(), $newusername->getText() ] );
396 }
397
403 protected function showLogExtract( $username, $type, &$out ) {
404 # Show relevant lines from the logs:
405 $logPage = new LogPage( $type );
406 $out->addHTML( Xml::element( 'h2', null, $logPage->getName()->text() ) . "\n" );
407 LogEventsList::showLogExtract( $out, $type, $username->getPrefixedText() );
408 }
409
418 public function prefixSearchSubpages( $search, $limit, $offset ) {
419 $user = User::newFromName( $search );
420 if ( !$user ) {
421 // No prefix suggestion for invalid user
422 return [];
423 }
424 // Autocomplete subpage as user list - public to allow caching
425 return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
426 }
427
428 protected function getGroupName() {
429 return 'users';
430 }
431}
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
and that you know you can do these things To protect your we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights These restrictions translate to certain responsibilities for you if you distribute copies of the or if you modify it For if you distribute copies of such a whether gratis or for a you must give the recipients all the rights that you have You must make sure that receive or can get the source code And you must show them these terms so they know their rights We protect your rights with two and(2) offer you this license which gives you legal permission to copy
$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:790
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Class to simplify the use of log pages.
Definition LogPage.php:33
Handles the backend logic of moving a page from one title to another.
Definition MovePage.php:32
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:585
static isCreatableName( $name)
Usernames which fail to pass this function will be blocked from new account registrations,...
Definition User.php:1117
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:2843
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not it can be in the form of< username >< more info > e g for bot passwords intended to be added to log contexts Fields it might only if the login was with a bot password 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:855
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition hooks.txt:3069
this hook is for auditing only or null if authentication failed before getting that far $username
Definition hooks.txt:782
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:2272
const LIST_OR
Definition Defines.php:55
const NS_USER_TALK
Definition Defines.php:76
const DB_REPLICA
Definition defines.php:25