MediaWiki  1.32.0
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 
24  public function execute( $par ) {
26 
27  $this->setHeaders();
28  $this->addHelpLink( 'Help:Renameuser' );
29 
30  $out = $this->getOutput();
31  $out->addWikiMsg( 'renameuser-summary' );
32 
33  $user = $this->getUser();
34  if ( !$user->isAllowed( 'renameuser' ) ) {
35  throw new PermissionsError( 'renameuser' );
36  }
37 
38  if ( wfReadOnly() ) {
39  throw new ReadOnlyError;
40  }
41 
42  if ( $user->isBlocked() ) {
43  throw new UserBlockedError( $this->getUser()->mBlock );
44  }
45 
47 
48  $request = $this->getRequest();
49  $showBlockLog = $request->getBool( 'submit-showBlockLog' );
50  $usernames = explode( '/', $par, 2 ); // this works as "/" is not valid in usernames
51  $oldnamePar = trim( str_replace( '_', ' ', $request->getText( 'oldusername', $usernames[0] ) ) );
52  $oldusername = Title::makeTitle( NS_USER, $oldnamePar );
53  $newnamePar = isset( $usernames[1] ) ? $usernames[1] : null;
54  $newnamePar = trim( str_replace( '_', ' ', $request->getText( 'newusername', $newnamePar ) ) );
55  // Force uppercase of newusername, otherwise wikis
56  // with wgCapitalLinks=false can create lc usernames
57  $newusername = Title::makeTitleSafe( NS_USER, $wgContLang->ucfirst( $newnamePar ) );
58  $oun = is_object( $oldusername ) ? $oldusername->getText() : '';
59  $nun = is_object( $newusername ) ? $newusername->getText() : '';
60  $token = $user->getEditToken();
61  $reason = $request->getText( 'reason' );
62 
63  $move_checked = $request->getBool( 'movepages', !$request->wasPosted() );
64  $suppress_checked = $request->getCheck( 'suppressredirect' );
65 
66  $warnings = [];
67  if ( $oun && $nun && !$request->getCheck( 'confirmaction' ) ) {
68  Hooks::run( 'RenameUserWarning', [ $oun, $nun, &$warnings ] );
69  }
70 
71  $out->addHTML(
72  Xml::openElement( 'form', [
73  'method' => 'post',
74  'action' => $this->getPageTitle()->getLocalURL(),
75  'id' => 'renameuser'
76  ] ) .
77  Xml::openElement( 'fieldset' ) .
78  Xml::element( 'legend', null, $this->msg( 'renameuser' )->text() ) .
79  Xml::openElement( 'table', [ 'id' => 'mw-renameuser-table' ] ) .
80  "<tr>
81  <td class='mw-label'>" .
82  Xml::label( $this->msg( 'renameuserold' )->text(), 'oldusername' ) .
83  "</td>
84  <td class='mw-input'>" .
85  Xml::input( 'oldusername', 20, $oun, [ 'type' => 'text', 'tabindex' => '1' ] ) . ' ' .
86  "</td>
87  </tr>
88  <tr>
89  <td class='mw-label'>" .
90  Xml::label( $this->msg( 'renameusernew' )->text(), 'newusername' ) .
91  "</td>
92  <td class='mw-input'>" .
93  Xml::input( 'newusername', 20, $nun, [ 'type' => 'text', 'tabindex' => '2' ] ) .
94  "</td>
95  </tr>
96  <tr>
97  <td class='mw-label'>" .
98  Xml::label( $this->msg( 'renameuserreason' )->text(), 'reason' ) .
99  "</td>
100  <td class='mw-input'>" .
101  Xml::input(
102  'reason',
103  40,
104  $reason,
105  [ 'type' => 'text', 'tabindex' => '3', 'maxlength' => 255 ]
106  ) .
107  '</td>
108  </tr>'
109  );
110  if ( $user->isAllowed( 'move' ) ) {
111  $out->addHTML( "
112  <tr>
113  <td>&#160;
114  </td>
115  <td class='mw-input'>" .
116  Xml::checkLabel( $this->msg( 'renameusermove' )->text(), 'movepages', 'movepages',
117  $move_checked, [ 'tabindex' => '4' ] ) .
118  '</td>
119  </tr>'
120  );
121 
122  if ( $user->isAllowed( 'suppressredirect' ) ) {
123  $out->addHTML( "
124  <tr>
125  <td>&#160;
126  </td>
127  <td class='mw-input'>" .
129  $this->msg( 'renameusersuppress' )->text(),
130  'suppressredirect',
131  'suppressredirect',
132  $suppress_checked,
133  [ 'tabindex' => '5' ]
134  ) .
135  '</td>
136  </tr>'
137  );
138  }
139  }
140  if ( $warnings ) {
141  $warningsHtml = [];
142  foreach ( $warnings as $warning ) {
143  $warningsHtml[] = is_array( $warning ) ?
144  $this->msg( $warning[0] )->rawParams( array_slice( $warning, 1 ) )->escaped() :
145  $this->msg( $warning )->escaped();
146  }
147 
148  $out->addHTML( "
149  <tr>
150  <td class='mw-label'>" . $this->msg( 'renameuserwarnings' )->escaped() . "
151  </td>
152  <td class='mw-input'>" .
153  '<ul class="error"><li>' .
154  implode( '</li><li>', $warningsHtml ) . '</li></ul>' .
155  '</td>
156  </tr>'
157  );
158  $out->addHTML( "
159  <tr>
160  <td>&#160;
161  </td>
162  <td class='mw-input'>" .
164  $this->msg( 'renameuserconfirm' )->text(),
165  'confirmaction',
166  'confirmaction',
167  false,
168  [ 'tabindex' => '6' ]
169  ) .
170  '</td>
171  </tr>'
172  );
173  }
174  $out->addHTML( "
175  <tr>
176  <td>&#160;
177  </td>
178  <td class='mw-submit'>" .
180  $this->msg( 'renameusersubmit' )->text(),
181  [
182  'name' => 'submit',
183  'tabindex' => '7',
184  'id' => 'submit'
185  ]
186  ) .
187  ' ' .
189  $this->msg( 'renameuser-submit-blocklog' )->text(),
190  [
191  'name' => 'submit-showBlockLog',
192  'id' => 'submit-showBlockLog',
193  'tabindex' => '8'
194  ]
195  ) .
196  '</td>
197  </tr>' .
198  Xml::closeElement( 'table' ) .
199  Xml::closeElement( 'fieldset' ) .
200  Html::hidden( 'token', $token ) .
201  Xml::closeElement( 'form' ) . "\n"
202  );
203 
204  // Show block log if requested
205  if ( $showBlockLog && is_object( $oldusername ) ) {
206  $this->showLogExtract( $oldusername, 'block', $out );
207 
208  return;
209  }
210 
211  if ( $request->getText( 'token' ) === '' ) {
212  # They probably haven't even submitted the form, so don't go further.
213  return;
214  } elseif ( $warnings ) {
215  # Let user read warnings
216  return;
217  } elseif ( !$request->wasPosted() || !$user->matchEditToken( $request->getVal( 'token' ) ) ) {
218  $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>", 'renameuser-error-request' );
219 
220  return;
221  } elseif ( !is_object( $oldusername ) ) {
222  $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
223  [ 'renameusererrorinvalid', $request->getText( 'oldusername' ) ] );
224 
225  return;
226  } elseif ( !is_object( $newusername ) ) {
227  $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
228  [ 'renameusererrorinvalid', $request->getText( 'newusername' ) ] );
229 
230  return;
231  } elseif ( $oldusername->getText() === $newusername->getText() ) {
232  $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>", 'renameuser-error-same-user' );
233 
234  return;
235  }
236 
237  // Suppress username validation of old username
238  $olduser = User::newFromName( $oldusername->getText(), false );
239  $newuser = User::newFromName( $newusername->getText(), 'creatable' );
240 
241  // It won't be an object if for instance "|" is supplied as a value
242  if ( !is_object( $olduser ) ) {
243  $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
244  [ 'renameusererrorinvalid', $oldusername->getText() ] );
245 
246  return;
247  }
248  if ( !is_object( $newuser ) || !User::isCreatableName( $newuser->getName() ) ) {
249  $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
250  [ 'renameusererrorinvalid', $newusername->getText() ] );
251 
252  return;
253  }
254 
255  // Check for the existence of lowercase oldusername in database.
256  // Until r19631 it was possible to rename a user to a name with first character as lowercase
257  if ( $oldusername->getText() !== $wgContLang->ucfirst( $oldusername->getText() ) ) {
258  // oldusername was entered as lowercase -> check for existence in table 'user'
259  $dbr = wfGetDB( DB_REPLICA );
260  $uid = $dbr->selectField( 'user', 'user_id',
261  [ 'user_name' => $oldusername->getText() ],
262  __METHOD__ );
263  if ( $uid === false ) {
264  if ( !$wgCapitalLinks ) {
265  $uid = 0; // We are on a lowercase wiki but lowercase username does not exists
266  } else {
267  // We are on a standard uppercase wiki, use normal
268  $uid = $olduser->idForName();
269  $oldusername = Title::makeTitleSafe( NS_USER, $olduser->getName() );
270  }
271  }
272  } else {
273  // oldusername was entered as upperase -> standard procedure
274  $uid = $olduser->idForName();
275  }
276 
277  if ( $uid === 0 ) {
278  $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
279  [ 'renameusererrordoesnotexist', $oldusername->getText() ] );
280 
281  return;
282  }
283 
284  if ( $newuser->idForName() !== 0 ) {
285  $out->wrapWikiMsg( "<div class=\"errorbox\">$1</div>",
286  [ 'renameusererrorexists', $newusername->getText() ] );
287 
288  return;
289  }
290 
291  // Give other affected extensions a chance to validate or abort
292  if ( !Hooks::run(
293  'RenameUserAbort',
294  [ $uid, $oldusername->getText(), $newusername->getText() ]
295  ) ) {
296  return;
297  }
298 
299  // Do the heavy lifting...
300  $rename = new RenameuserSQL(
301  $oldusername->getText(),
302  $newusername->getText(),
303  $uid,
304  $this->getUser(),
305  [ 'reason' => $reason ]
306  );
307  if ( !$rename->rename() ) {
308  return;
309  }
310 
311  // If this user is renaming his/herself, make sure that Title::moveTo()
312  // doesn't make a bunch of null move edits under the old name!
313  if ( $user->getId() === $uid ) {
314  $user->setName( $newusername->getText() );
315  }
316 
317  // Move any user pages
318  if ( $request->getCheck( 'movepages' ) && $user->isAllowed( 'move' ) ) {
319  $dbr = wfGetDB( DB_REPLICA );
320 
321  $pages = $dbr->select(
322  'page',
323  [ 'page_namespace', 'page_title' ],
324  [
325  'page_namespace' => [ NS_USER, NS_USER_TALK ],
326  $dbr->makeList( [
327  'page_title ' . $dbr->buildLike( $oldusername->getDBkey() . '/', $dbr->anyString() ),
328  'page_title = ' . $dbr->addQuotes( $oldusername->getDBkey() ),
329  ], LIST_OR ),
330  ],
331  __METHOD__
332  );
333 
334  $suppressRedirect = false;
335 
336  if ( $request->getCheck( 'suppressredirect' ) && $user->isAllowed( 'suppressredirect' ) ) {
337  $suppressRedirect = true;
338  }
339 
340  $output = '';
341  $linkRenderer = $this->getLinkRenderer();
342  foreach ( $pages as $row ) {
343  $oldPage = Title::makeTitleSafe( $row->page_namespace, $row->page_title );
344  $newPage = Title::makeTitleSafe( $row->page_namespace,
345  preg_replace( '!^[^/]+!', $newusername->getDBkey(), $row->page_title ) );
346  # Do not autodelete or anything, title must not exist
347  if ( $newPage->exists() && !$oldPage->isValidMoveTarget( $newPage ) ) {
348  $link = $linkRenderer->makeKnownLink( $newPage );
350  'li',
351  [ 'class' => 'mw-renameuser-pe' ],
352  $this->msg( 'renameuser-page-exists' )->rawParams( $link )->escaped()
353  );
354  } else {
355  $success = $oldPage->moveTo(
356  $newPage,
357  false,
358  $this->msg(
359  'renameuser-move-log',
360  $oldusername->getText(),
361  $newusername->getText() )->inContentLanguage()->text(),
362  !$suppressRedirect
363  );
364  if ( $success === true ) {
365  # oldPage is not known in case of redirect suppression
366  $oldLink = $linkRenderer->makeLink( $oldPage, null, [], [ 'redirect' => 'no' ] );
367 
368  # newPage is always known because the move was successful
369  $newLink = $linkRenderer->makeKnownLink( $newPage );
370 
372  'li',
373  [ 'class' => 'mw-renameuser-pm' ],
374  $this->msg( 'renameuser-page-moved' )->rawParams( $oldLink, $newLink )->escaped()
375  );
376  } else {
377  $oldLink = $linkRenderer->makeKnownLink( $oldPage );
378  $newLink = $linkRenderer->makeLink( $newPage );
380  'li', [ 'class' => 'mw-renameuser-pu' ],
381  $this->msg( 'renameuser-page-unmoved' )->rawParams( $oldLink, $newLink )->escaped()
382  );
383  }
384  }
385  }
386  if ( $output ) {
387  $out->addHTML( Html::rawElement( 'ul', [], $output ) );
388  }
389  }
390 
391  // Output success message stuff :)
392  $out->wrapWikiMsg( "<div class=\"successbox\">$1</div><br style=\"clear:both\" />",
393  [ 'renameusersuccess', $oldusername->getText(), $newusername->getText() ] );
394  }
395 
401  protected function showLogExtract( $username, $type, &$out ) {
402  # Show relevant lines from the logs:
403  $logPage = new LogPage( $type );
404  $out->addHTML( Xml::element( 'h2', null, $logPage->getName()->text() ) . "\n" );
405  LogEventsList::showLogExtract( $out, $type, $username->getPrefixedText() );
406  }
407 
416  public function prefixSearchSubpages( $search, $limit, $offset ) {
417  if ( !class_exists( 'UserNamePrefixSearch' ) ) { // check for version 1.27
418  return [];
419  }
420  $user = User::newFromName( $search );
421  if ( !$user ) {
422  // No prefix suggestion for invalid user
423  return [];
424  }
425  // Autocomplete subpage as user list - public to allow caching
426  return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
427  }
428 
429  protected function getGroupName() {
430  return 'users';
431  }
432 }
ReadOnlyError
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
Definition: ReadOnlyError.php:28
SpecialPage\getPageTitle
getPageTitle( $subpage=false)
Get a self-referential title object.
Definition: SpecialPage.php:678
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:244
SpecialPage\msg
msg( $key)
Wrapper around wfMessage that sets the current context.
Definition: SpecialPage.php:796
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
UserBlockedError
Show an error when the user tries to do something whilst blocked.
Definition: UserBlockedError.php:27
SpecialPage\getOutput
getOutput()
Get the OutputPage being used for this instance.
Definition: SpecialPage.php:725
SpecialRenameuser\doesWrites
doesWrites()
Indicates whether this special page may perform database writes.
Definition: SpecialRenameuser.php:12
Xml\label
static label( $label, $id, $attribs=[])
Convenience function to build an HTML form label.
Definition: Xml.php:360
SpecialRenameuser\getGroupName
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Definition: SpecialRenameuser.php:429
UserNamePrefixSearch\search
static search( $audience, $search, $limit, $offset=0)
Do a prefix search of user names and return a list of matching user names.
Definition: UserNamePrefixSearch.php:39
wfReadOnly
wfReadOnly()
Check whether the wiki is in read-only mode.
Definition: GlobalFunctions.php:1237
User\newFromName
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
Definition: User.php:592
SpecialPage\useTransactionalTimeLimit
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition: SpecialPage.php:898
PermissionsError
Show an error when a user tries to do something they do not have the necessary permissions for.
Definition: PermissionsError.php:28
$success
$success
Definition: NoLocalSettings.php:42
Xml\openElement
static openElement( $element, $attribs=null)
This opens an XML element.
Definition: Xml.php:110
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
$dbr
$dbr
Definition: testCompression.php:50
SpecialPage\addHelpLink
addHelpLink( $to, $overrideBaseUrl=false)
Adds help link with an icon via page indicators.
Definition: SpecialPage.php:832
LIST_OR
const LIST_OR
Definition: Defines.php:46
RenameuserSQL
Class which performs the actual renaming of users.
Definition: RenameuserSQL.php:9
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2693
LogPage
Class to simplify the use of log pages.
Definition: LogPage.php:33
Xml\element
static element( $element, $attribs=null, $contents='', $allowShortTag=true)
Format an XML element with given attributes and, optionally, text content.
Definition: Xml.php:41
SpecialPage\setHeaders
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
Definition: SpecialPage.php:531
SpecialPage\getUser
getUser()
Shortcut to get the User executing this instance.
Definition: SpecialPage.php:735
Title\makeTitle
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:545
$output
$output
Definition: SyntaxHighlight.php:334
LogEventsList\showLogExtract
static showLogExtract(&$out, $types=[], $page='', $user='', $param=[])
Show log extract.
Definition: LogEventsList.php:606
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
$request
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:2675
Html\hidden
static hidden( $name, $value, array $attribs=[])
Convenience function to produce an input element with type=hidden.
Definition: Html.php:795
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:573
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:67
SpecialPage
Parent class for all special pages.
Definition: SpecialPage.php:36
SpecialPage\getRequest
getRequest()
Get the WebRequest being used for this instance.
Definition: SpecialPage.php:715
SpecialRenameuser
Special page that allows authorised users to rename user accounts.
Definition: SpecialRenameuser.php:7
SpecialPage\getLinkRenderer
getLinkRenderer()
Definition: SpecialPage.php:908
text
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
Definition: All_system_messages.txt:1267
SpecialRenameuser\prefixSearchSubpages
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
Definition: SpecialRenameuser.php:416
Xml\closeElement
static closeElement( $element)
Shortcut to close an XML element.
Definition: Xml.php:119
$wgCapitalLinks
$wgCapitalLinks
Set this to false to avoid forcing the first letter of links to capitals.
Definition: DefaultSettings.php:4030
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:210
NS_USER
const NS_USER
Definition: Defines.php:66
$link
usually copyright or history_copyright This message must be in HTML not wikitext & $link
Definition: hooks.txt:3090
SpecialRenameuser\showLogExtract
showLogExtract( $username, $type, &$out)
Definition: SpecialRenameuser.php:401
Xml\input
static input( $name, $size=false, $value=false, $attribs=[])
Convenience function to build an HTML text input field.
Definition: Xml.php:276
SpecialPage\$linkRenderer
MediaWiki Linker LinkRenderer null $linkRenderer
Definition: SpecialPage.php:66
SpecialRenameuser\execute
execute( $par)
Show the special page.
Definition: SpecialRenameuser.php:24
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
$wgContLang
$wgContLang
Definition: Setup.php:809
$username
this hook is for auditing only or null if authentication failed before getting that far $username
Definition: hooks.txt:813
User\isCreatableName
static isCreatableName( $name)
Usernames which fail to pass this function will be blocked from new account registrations,...
Definition: User.php:1121
Xml\submitButton
static submitButton( $value, $attribs=[])
Convenience function to build an HTML submit button When $wgUseMediaWikiUIEverywhere is true it will ...
Definition: Xml.php:461
SpecialRenameuser\__construct
__construct()
Definition: SpecialRenameuser.php:8
Xml\checkLabel
static checkLabel( $label, $name, $id, $checked=false, $attribs=[])
Convenience function to build an HTML checkbox with a label.
Definition: Xml.php:421
$out
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:813
$type
$type
Definition: testCompression.php:48