MediaWiki  master
SpecialChangeEmail.php
Go to the documentation of this file.
1 <?php
24 namespace MediaWiki\Specials;
25 
26 use ErrorPageError;
27 use FormSpecialPage;
28 use HTMLForm;
34 use Sanitizer;
35 use Status;
36 use User;
37 
47  private $status;
48 
52  public function __construct( AuthManager $authManager ) {
53  parent::__construct( 'ChangeEmail', 'editmyprivateinfo' );
54 
55  $this->setAuthManager( $authManager );
56  }
57 
58  public function doesWrites() {
59  return true;
60  }
61 
65  public function isListed() {
66  return $this->getAuthManager()->allowsPropertyChange( 'emailaddress' );
67  }
68 
73  public function execute( $par ) {
74  $out = $this->getOutput();
75  $out->disallowUserJs();
76  $out->addModules( 'mediawiki.special.changeemail' );
77  parent::execute( $par );
78  }
79 
80  protected function getLoginSecurityLevel() {
81  return $this->getName();
82  }
83 
84  protected function checkExecutePermissions( User $user ) {
85  if ( !$this->getAuthManager()->allowsPropertyChange( 'emailaddress' ) ) {
86  throw new ErrorPageError( 'changeemail', 'cannotchangeemail' );
87  }
88 
89  $this->requireNamedUser( 'changeemail-no-info' );
90 
91  // This could also let someone check the current email address, so
92  // require both permissions.
93  if ( !$this->getAuthority()->isAllowed( 'viewmyprivateinfo' ) ) {
94  throw new PermissionsError( 'viewmyprivateinfo' );
95  }
96 
97  parent::checkExecutePermissions( $user );
98  }
99 
100  protected function getFormFields() {
101  $user = $this->getUser();
102 
103  return [
104  'Name' => [
105  'type' => 'info',
106  'label-message' => 'username',
107  'default' => $user->getName(),
108  ],
109  'OldEmail' => [
110  'type' => 'info',
111  'label-message' => 'changeemail-oldemail',
112  'default' => $user->getEmail() ?: $this->msg( 'changeemail-none' )->text(),
113  ],
114  'NewEmail' => [
115  'type' => 'email',
116  'label-message' => 'changeemail-newemail',
117  'autofocus' => true,
118  'maxlength' => 255,
119  'help-message' => 'changeemail-newemail-help',
120  ],
121  ];
122  }
123 
124  protected function getDisplayFormat() {
125  return 'ooui';
126  }
127 
128  protected function alterForm( HTMLForm $form ) {
129  $form->setId( 'mw-changeemail-form' );
130  $form->setTableId( 'mw-changeemail-table' );
131  $form->setSubmitTextMsg( 'changeemail-submit' );
132  $form->addHiddenFields( $this->getRequest()->getValues( 'returnto', 'returntoquery' ) );
133 
134  $form->addHeaderHtml( $this->msg( 'changeemail-header' )->parseAsBlock() );
135  $form->setSubmitID( 'change_email_submit' );
136  }
137 
138  public function onSubmit( array $data ) {
139  $this->status = $this->attemptChange( $this->getUser(), $data['NewEmail'] );
140 
141  return $this->status;
142  }
143 
144  public function onSuccess() {
145  $request = $this->getRequest();
146 
147  $returnTo = $request->getVal( 'returnto' );
148  $titleObj = $returnTo !== null ? Title::newFromText( $returnTo ) : null;
149  if ( !$titleObj instanceof Title ) {
150  $titleObj = Title::newMainPage();
151  }
152  $query = $request->getVal( 'returntoquery', '' );
153 
154  if ( $this->status->value === true ) {
155  $this->getOutput()->redirect( $titleObj->getFullUrlForRedirect( $query ) );
156  } elseif ( $this->status->value === 'eauth' ) {
157  # Notify user that a confirmation email has been sent...
158  $out = $this->getOutput();
159  $out->addHTML(
161  $out->msg( 'eauthentsent', $this->getUser()->getName() )->parse()
162  )
163  );
164  // just show the link to go back
165  $this->getOutput()->addReturnTo( $titleObj, wfCgiToArray( $query ) );
166  }
167  }
168 
175  private function attemptChange( User $user, $newAddr ) {
176  if ( $newAddr !== '' && !Sanitizer::validateEmail( $newAddr ) ) {
177  return Status::newFatal( 'invalidemailaddress' );
178  }
179 
180  $oldAddr = $user->getEmail();
181  if ( $newAddr === $oldAddr ) {
182  return Status::newFatal( 'changeemail-nochange' );
183  }
184 
185  if ( strlen( $newAddr ) > 255 ) {
186  return Status::newFatal( 'changeemail-maxlength' );
187  }
188 
189  // To prevent spam, rate limit adding a new address, but do
190  // not rate limit removing an address.
191  if ( $newAddr !== '' && $user->pingLimiter( 'changeemail' ) ) {
192  return Status::newFatal( 'actionthrottledtext' );
193  }
194 
195  $userLatest = $user->getInstanceForUpdate();
196  $status = $userLatest->setEmailWithConfirmation( $newAddr );
197  if ( !$status->isGood() ) {
198  return $status;
199  }
200 
201  LoggerFactory::getInstance( 'authentication' )->info(
202  'Changing email address for {user} from {oldemail} to {newemail}', [
203  'user' => $userLatest->getName(),
204  'oldemail' => $oldAddr,
205  'newemail' => $newAddr,
206  ]
207  );
208 
209  $this->getHookRunner()->onPrefsEmailAudit( $userLatest, $oldAddr, $newAddr );
210 
211  $userLatest->saveSettings();
212 
213  return $status;
214  }
215 
216  public function requiresUnblock() {
217  return false;
218  }
219 
220  protected function getGroupName() {
221  return 'login';
222  }
223 }
224 
228 class_alias( SpecialChangeEmail::class, 'SpecialChangeEmail' );
wfCgiToArray( $query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
An error page which can definitely be safely rendered using the OutputPage.
Special page which uses an HTMLForm to handle processing.
string null $par
The sub-page of the special page.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition: HTMLForm.php:155
addHeaderHtml( $html, $section=null)
Add HTML to the header, inside the form.
Definition: HTMLForm.php:897
setTableId( $id)
Set the id of the <table> or outermost <div> element.
Definition: HTMLForm.php:1776
setSubmitTextMsg( $msg)
Set the text for the submit button to a message.
Definition: HTMLForm.php:1637
setId( $id)
Definition: HTMLForm.php:1787
setSubmitID( $t)
Set the id for the submit button.
Definition: HTMLForm.php:1684
addHiddenFields(array $fields)
Add an array of hidden fields to the output Array values are discarded for security reasons (per WebR...
Definition: HTMLForm.php:1173
This serves as the entry point to the authentication system.
This class is a collection of static functions that serve two purposes:
Definition: Html.php:55
static warningBox( $html, $className='')
Return a warning box.
Definition: Html.php:783
PSR-3 logger instance factory.
static getInstance( $channel)
Get a named logger instance from the currently configured logger factory.
Let users change their email address.
execute( $par)
Main execution point.
doesWrites()
Indicates whether this special page may perform database writes.
onSuccess()
Do something exciting on successful processing of the form, most likely to show a confirmation messag...
getFormFields()
Get an HTMLForm descriptor array.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
alterForm(HTMLForm $form)
Play with the HTMLForm if you need to more substantially.
checkExecutePermissions(User $user)
Called from execute() to check if the given user can perform this action.
getDisplayFormat()
Get display format for the form.
onSubmit(array $data)
Process the form on submission.
requiresUnblock()
Whether this action cannot be executed by a blocked user, default to requiresPost()
getLoginSecurityLevel()
Tells if the special page does something security-sensitive and needs extra defense against a stolen ...
Represents a title within MediaWiki.
Definition: Title.php:82
static newMainPage(MessageLocalizer $localizer=null)
Create a new Title for the Main Page.
Definition: Title.php:755
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:425
Show an error when a user tries to do something they do not have the necessary permissions for.
HTML sanitizer for MediaWiki.
Definition: Sanitizer.php:42
static validateEmail( $addr)
Does a string look like an e-mail address?
Definition: Sanitizer.php:1895
getName()
Get the name of this Special Page.
getOutput()
Get the OutputPage being used for this instance.
getUser()
Shortcut to get the User executing this instance.
requireNamedUser( $reasonMsg='exception-nologin-text', $titleMsg='exception-nologin')
If the user is not logged in or is a temporary user, throws UserNotLoggedIn.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getAuthority()
Shortcut to get the Authority executing this instance.
getRequest()
Get the WebRequest being used for this instance.
setAuthManager(AuthManager $authManager)
Set the injected AuthManager from the special page constructor.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:73
isGood()
Returns whether the operation completed and didn't have any error or warnings.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:46
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:71
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:1684
pingLimiter( $action='edit', $incrBy=1)
Primitive rate limits: enforce maximum actions per time period to put a brake on flooding.
Definition: User.php:1483
getEmail()
Get the user's e-mail address.
Definition: User.php:2002
getInstanceForUpdate()
Get a new instance of this user that was loaded from the primary DB via a locking read.
Definition: User.php:3439