MediaWiki REL1_34
SpecialRenameuser.php
Go to the documentation of this file.
1<?php
2
4
10 public function __construct() {
11 parent::__construct( 'Renameuser', 'renameuser' );
12 }
13
14 public function doesWrites() {
15 return true;
16 }
17
27 public function execute( $par ) {
28 global $wgCapitalLinks;
29 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
30
31 $this->setHeaders();
32 $this->addHelpLink( 'Help:Renameuser' );
33
34 $out = $this->getOutput();
35 $out->addWikiMsg( 'renameuser-summary' );
36
37 $user = $this->getUser();
38 if ( !$user->isAllowed( 'renameuser' ) ) {
39 throw new PermissionsError( 'renameuser' );
40 }
41
42 if ( wfReadOnly() ) {
43 throw new ReadOnlyError;
44 }
45
46 if ( $user->isBlocked() ) {
47 throw new UserBlockedError( $this->getUser()->mBlock );
48 }
49
51
52 $request = $this->getRequest();
53 $showBlockLog = $request->getBool( 'submit-showBlockLog' );
54 $usernames = explode( '/', $par, 2 ); // this works as "/" is not valid in usernames
55 $oldnamePar = trim( str_replace( '_', ' ', $request->getText( 'oldusername', $usernames[0] ) ) );
56 $oldusername = Title::makeTitle( NS_USER, $oldnamePar );
57 $newnamePar = $usernames[1] ?? null;
58 $newnamePar = trim( str_replace( '_', ' ', $request->getText( 'newusername', $newnamePar ) ) );
59 // Force uppercase of newusername, otherwise wikis
60 // with wgCapitalLinks=false can create lc usernames
61 $newusername = Title::makeTitleSafe( NS_USER, $contLang->ucfirst( $newnamePar ) );
62 $oun = is_object( $oldusername ) ? $oldusername->getText() : '';
63 $nun = is_object( $newusername ) ? $newusername->getText() : '';
64 $token = $user->getEditToken();
65 $reason = $request->getText( 'reason' );
66
67 $move_checked = $request->getBool( 'movepages', !$request->wasPosted() );
68 $suppress_checked = $request->getCheck( 'suppressredirect' );
69
70 $warnings = [];
71 if ( $oun && $nun && !$request->getCheck( 'confirmaction' ) ) {
72 Hooks::run( 'RenameUserWarning', [ $oun, $nun, &$warnings ] );
73 }
74
75 $out->addHTML(
76 Xml::openElement( 'form', [
77 'method' => 'post',
78 'action' => $this->getPageTitle()->getLocalURL(),
79 'id' => 'renameuser'
80 ] ) .
81 Xml::openElement( 'fieldset' ) .
82 Xml::element( 'legend', null, $this->msg( 'renameuser' )->text() ) .
83 Xml::openElement( 'table', [ 'id' => 'mw-renameuser-table' ] ) .
84 "<tr>
85 <td class='mw-label'>" .
86 Xml::label( $this->msg( 'renameuserold' )->text(), 'oldusername' ) .
87 "</td>
88 <td class='mw-input'>" .
89 Xml::input( 'oldusername', 20, $oun, [ 'type' => 'text', 'tabindex' => '1' ] ) . ' ' .
90 "</td>
91 </tr>
92 <tr>
93 <td class='mw-label'>" .
94 Xml::label( $this->msg( 'renameusernew' )->text(), 'newusername' ) .
95 "</td>
96 <td class='mw-input'>" .
97 Xml::input( 'newusername', 20, $nun, [ 'type' => 'text', 'tabindex' => '2' ] ) .
98 "</td>
99 </tr>
100 <tr>
101 <td class='mw-label'>" .
102 Xml::label( $this->msg( 'renameuserreason' )->text(), 'reason' ) .
103 "</td>
104 <td class='mw-input'>" .
105 Xml::input(
106 'reason',
107 40,
108 $reason,
109 [ 'type' => 'text', 'tabindex' => '3', 'maxlength' => 255 ]
110 ) .
111 '</td>
112 </tr>'
113 );
114 if ( $user->isAllowed( 'move' ) ) {
115 $out->addHTML( "
116 <tr>
117 <td>&#160;
118 </td>
119 <td class='mw-input'>" .
120 Xml::checkLabel( $this->msg( 'renameusermove' )->text(), 'movepages', 'movepages',
121 $move_checked, [ 'tabindex' => '4' ] ) .
122 '</td>
123 </tr>'
124 );
125
126 if ( $user->isAllowed( 'suppressredirect' ) ) {
127 $out->addHTML( "
128 <tr>
129 <td>&#160;
130 </td>
131 <td class='mw-input'>" .
132 Xml::checkLabel(
133 $this->msg( 'renameusersuppress' )->text(),
134 'suppressredirect',
135 'suppressredirect',
136 $suppress_checked,
137 [ 'tabindex' => '5' ]
138 ) .
139 '</td>
140 </tr>'
141 );
142 }
143 }
144 if ( $warnings ) {
145 $warningsHtml = [];
146 foreach ( $warnings as $warning ) {
147 $warningsHtml[] = is_array( $warning ) ?
148 $this->msg( $warning[0] )->rawParams( array_slice( $warning, 1 ) )->escaped() :
149 $this->msg( $warning )->escaped();
150 }
151
152 $out->addHTML( "
153 <tr>
154 <td class='mw-label'>" . $this->msg( 'renameuserwarnings' )->escaped() . "
155 </td>
156 <td class='mw-input'>" .
157 '<ul class="error"><li>' .
158 implode( '</li><li>', $warningsHtml ) . '</li></ul>' .
159 '</td>
160 </tr>'
161 );
162 $out->addHTML( "
163 <tr>
164 <td>&#160;
165 </td>
166 <td class='mw-input'>" .
167 Xml::checkLabel(
168 $this->msg( 'renameuserconfirm' )->text(),
169 'confirmaction',
170 'confirmaction',
171 false,
172 [ 'tabindex' => '6' ]
173 ) .
174 '</td>
175 </tr>'
176 );
177 }
178 $out->addHTML( "
179 <tr>
180 <td>&#160;
181 </td>
182 <td class='mw-submit'>" .
183 Xml::submitButton(
184 $this->msg( 'renameusersubmit' )->text(),
185 [
186 'name' => 'submit',
187 'tabindex' => '7',
188 'id' => 'submit'
189 ]
190 ) .
191 ' ' .
192 Xml::submitButton(
193 $this->msg( 'renameuser-submit-blocklog' )->text(),
194 [
195 'name' => 'submit-showBlockLog',
196 'id' => 'submit-showBlockLog',
197 'tabindex' => '8'
198 ]
199 ) .
200 '</td>
201 </tr>' .
202 Xml::closeElement( 'table' ) .
203 Xml::closeElement( 'fieldset' ) .
204 Html::hidden( 'token', $token ) .
205 Xml::closeElement( 'form' ) . "\n"
206 );
207
208 // Show block log if requested
209 if ( $showBlockLog && is_object( $oldusername ) ) {
210 $this->showLogExtract( $oldusername, 'block', $out );
211
212 return;
213 }
214
215 if ( $request->getText( 'token' ) === '' ) {
216 # They probably haven't even submitted the form, so don't go further.
217 return;
218 } elseif ( $warnings ) {
219 # Let user read warnings
220 return;
221 } elseif ( !$request->wasPosted() || !$user->matchEditToken( $request->getVal( 'token' ) ) ) {
222 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>", 'renameuser-error-request' );
223
224 return;
225 } elseif ( !is_object( $oldusername ) ) {
226 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
227 [ 'renameusererrorinvalid', $request->getText( 'oldusername' ) ] );
228
229 return;
230 } elseif ( !is_object( $newusername ) ) {
231 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
232 [ 'renameusererrorinvalid', $request->getText( 'newusername' ) ] );
233
234 return;
235 } elseif ( $oldusername->getText() === $newusername->getText() ) {
236 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>", 'renameuser-error-same-user' );
237
238 return;
239 }
240
241 // Suppress username validation of old username
242 $olduser = User::newFromName( $oldusername->getText(), false );
243 $newuser = User::newFromName( $newusername->getText(), 'creatable' );
244
245 // It won't be an object if for instance "|" is supplied as a value
246 if ( !is_object( $olduser ) ) {
247 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
248 [ 'renameusererrorinvalid', $oldusername->getText() ] );
249
250 return;
251 }
252 if ( !is_object( $newuser ) || !User::isCreatableName( $newuser->getName() ) ) {
253 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
254 [ 'renameusererrorinvalid', $newusername->getText() ] );
255
256 return;
257 }
258
259 // Check for the existence of lowercase oldusername in database.
260 // Until r19631 it was possible to rename a user to a name with first character as lowercase
261 if ( $oldusername->getText() !== $contLang->ucfirst( $oldusername->getText() ) ) {
262 // oldusername was entered as lowercase -> check for existence in table 'user'
264 $uid = $dbr->selectField( 'user', 'user_id',
265 [ 'user_name' => $oldusername->getText() ],
266 __METHOD__ );
267 if ( $uid === false ) {
268 if ( !$wgCapitalLinks ) {
269 $uid = 0; // We are on a lowercase wiki but lowercase username does not exists
270 } else {
271 // We are on a standard uppercase wiki, use normal
272 $uid = $olduser->idForName();
273 $oldusername = Title::makeTitleSafe( NS_USER, $olduser->getName() );
274 }
275 }
276 } else {
277 // oldusername was entered as upperase -> standard procedure
278 $uid = $olduser->idForName();
279 }
280
281 if ( $uid === 0 ) {
282 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
283 [ 'renameusererrordoesnotexist', $oldusername->getText() ] );
284
285 return;
286 }
287
288 if ( $newuser->idForName() !== 0 ) {
289 $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
290 [ 'renameusererrorexists', $newusername->getText() ] );
291
292 return;
293 }
294
295 // Give other affected extensions a chance to validate or abort
296 if ( !Hooks::run(
297 'RenameUserAbort',
298 [ $uid, $oldusername->getText(), $newusername->getText() ]
299 ) ) {
300 return;
301 }
302
303 // Do the heavy lifting...
304 $rename = new RenameuserSQL(
305 $oldusername->getText(),
306 $newusername->getText(),
307 $uid,
308 $this->getUser(),
309 [ 'reason' => $reason ]
310 );
311 if ( !$rename->rename() ) {
312 return;
313 }
314
315 // If this user is renaming his/herself, make sure that MovePage::move()
316 // doesn't make a bunch of null move edits under the old name!
317 if ( $user->getId() === $uid ) {
318 $user->setName( $newusername->getText() );
319 }
320
321 // Move any user pages
322 if ( $request->getCheck( 'movepages' ) && $user->isAllowed( 'move' ) ) {
324
325 $pages = $dbr->select(
326 'page',
327 [ 'page_namespace', 'page_title' ],
328 [
329 'page_namespace' => [ NS_USER, NS_USER_TALK ],
330 $dbr->makeList( [
331 'page_title ' . $dbr->buildLike( $oldusername->getDBkey() . '/', $dbr->anyString() ),
332 'page_title = ' . $dbr->addQuotes( $oldusername->getDBkey() ),
333 ], LIST_OR ),
334 ],
335 __METHOD__
336 );
337
338 $suppressRedirect = false;
339
340 if ( $request->getCheck( 'suppressredirect' ) && $user->isAllowed( 'suppressredirect' ) ) {
341 $suppressRedirect = true;
342 }
343
344 $output = '';
346 foreach ( $pages as $row ) {
347 $oldPage = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
348 $newPage = Title::makeTitleSafe( $row->page_namespace,
349 preg_replace( '!^[^/]+!', $newusername->getDBkey(), $row->page_title ) );
350
351 $movePage = new MovePage( $oldPage, $newPage );
352 $validMoveStatus = $movePage->isValidMove();
353
354 # Do not autodelete or anything, title must not exist
355 if ( $newPage->exists() && !$validMoveStatus->isOK() ) {
356 $link = $linkRenderer->makeKnownLink( $newPage );
357 $output .= Html::rawElement(
358 'li',
359 [ 'class' => 'mw-renameuser-pe' ],
360 $this->msg( 'renameuser-page-exists' )->rawParams( $link )->escaped()
361 );
362 } else {
363 $logReason = $this->msg(
364 'renameuser-move-log', $oldusername->getText(), $newusername->getText()
365 )->inContentLanguage()->text();
366
367 $moveStatus = $movePage->move( $user, $logReason, !$suppressRedirect );
368
369 if ( $moveStatus->isOK() ) {
370 # oldPage is not known in case of redirect suppression
371 $oldLink = $linkRenderer->makeLink( $oldPage, null, [], [ 'redirect' => 'no' ] );
372
373 # newPage is always known because the move was successful
374 $newLink = $linkRenderer->makeKnownLink( $newPage );
375
376 $output .= Html::rawElement(
377 'li',
378 [ 'class' => 'mw-renameuser-pm' ],
379 $this->msg( 'renameuser-page-moved' )->rawParams( $oldLink, $newLink )->escaped()
380 );
381 } else {
382 $oldLink = $linkRenderer->makeKnownLink( $oldPage );
383 $newLink = $linkRenderer->makeLink( $newPage );
384 $output .= Html::rawElement(
385 'li', [ 'class' => 'mw-renameuser-pu' ],
386 $this->msg( 'renameuser-page-unmoved' )->rawParams( $oldLink, $newLink )->escaped()
387 );
388 }
389 }
390 }
391 if ( $output ) {
392 $out->addHTML( Html::rawElement( 'ul', [], $output ) );
393 }
394 }
395
396 // Output success message stuff :)
397 $out->wrapWikiMsg( "<div class=\"successbox\">$1</div><br style=\"clear:both\" />",
398 [ 'renameusersuccess', $oldusername->getText(), $newusername->getText() ] );
399 }
400
406 protected function showLogExtract( $username, $type, &$out ) {
407 # Show relevant lines from the logs:
408 $logPage = new LogPage( $type );
409 $out->addHTML( Xml::element( 'h2', null, $logPage->getName()->text() ) . "\n" );
410 LogEventsList::showLogExtract( $out, $type, $username->getPrefixedText() );
411 }
412
421 public function prefixSearchSubpages( $search, $limit, $offset ) {
422 $user = User::newFromName( $search );
423 if ( !$user ) {
424 // No prefix suggestion for invalid user
425 return [];
426 }
427 // Autocomplete subpage as user list - public to allow caching
428 return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
429 }
430
431 protected function getGroupName() {
432 return 'users';
433 }
434}
$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.
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Class to simplify the use of log pages.
Definition LogPage.php:33
MediaWikiServices is the service locator for the application scope of MediaWiki.
Handles the backend logic of moving a page from one title to another.
Definition MovePage.php:36
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,... $params)
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.
const NS_USER
Definition Defines.php:71
const LIST_OR
Definition Defines.php:51
const NS_USER_TALK
Definition Defines.php:72
const DB_REPLICA
Definition defines.php:25