Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 89
0.00% covered (danger)
0.00%
0 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialChangeEmail
0.00% covered (danger)
0.00%
0 / 88
0.00% covered (danger)
0.00%
0 / 14
812
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 doesWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isListed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getLoginSecurityLevel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 checkExecutePermissions
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 getFormFields
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
6
 getDisplayFormat
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 alterForm
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 onSubmit
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 onSuccess
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
 attemptChange
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
72
 requiresUnblock
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\Specials;
22
23use ErrorPageError;
24use MediaWiki\Auth\AuthManager;
25use MediaWiki\Html\Html;
26use MediaWiki\HTMLForm\HTMLForm;
27use MediaWiki\Logger\LoggerFactory;
28use MediaWiki\Parser\Sanitizer;
29use MediaWiki\SpecialPage\FormSpecialPage;
30use MediaWiki\Status\Status;
31use MediaWiki\Title\Title;
32use MediaWiki\User\User;
33use PermissionsError;
34
35/**
36 * Let users change their email address.
37 *
38 * @ingroup SpecialPage
39 */
40class SpecialChangeEmail extends FormSpecialPage {
41    /**
42     * @var Status
43     */
44    private $status;
45
46    public function __construct( AuthManager $authManager ) {
47        parent::__construct( 'ChangeEmail', 'editmyprivateinfo' );
48
49        $this->setAuthManager( $authManager );
50    }
51
52    public function doesWrites() {
53        return true;
54    }
55
56    /**
57     * @return bool
58     */
59    public function isListed() {
60        return $this->getAuthManager()->allowsPropertyChange( 'emailaddress' );
61    }
62
63    /**
64     * Main execution point
65     * @param string|null $par
66     */
67    public function execute( $par ) {
68        $out = $this->getOutput();
69        $out->disallowUserJs();
70        $out->addModules( 'mediawiki.special.changeemail' );
71        parent::execute( $par );
72    }
73
74    protected function getLoginSecurityLevel() {
75        return $this->getName();
76    }
77
78    protected function checkExecutePermissions( User $user ) {
79        if ( !$this->getAuthManager()->allowsPropertyChange( 'emailaddress' ) ) {
80            throw new ErrorPageError( 'changeemail', 'cannotchangeemail' );
81        }
82
83        $this->requireNamedUser( 'changeemail-no-info', 'exception-nologin', true );
84
85        // This could also let someone check the current email address, so
86        // require both permissions.
87        if ( !$this->getAuthority()->isAllowed( 'viewmyprivateinfo' ) ) {
88            throw new PermissionsError( 'viewmyprivateinfo' );
89        }
90
91        parent::checkExecutePermissions( $user );
92    }
93
94    protected function getFormFields() {
95        $user = $this->getUser();
96
97        return [
98            'Name' => [
99                'type' => 'info',
100                'label-message' => 'username',
101                'default' => $user->getName(),
102            ],
103            'OldEmail' => [
104                'type' => 'info',
105                'label-message' => 'changeemail-oldemail',
106                'default' => $user->getEmail() ?: $this->msg( 'changeemail-none' )->text(),
107            ],
108            'NewEmail' => [
109                'type' => 'email',
110                'label-message' => 'changeemail-newemail',
111                'autofocus' => true,
112                'maxlength' => 255,
113                'help-message' => 'changeemail-newemail-help',
114            ],
115        ];
116    }
117
118    protected function getDisplayFormat() {
119        return 'ooui';
120    }
121
122    protected function alterForm( HTMLForm $form ) {
123        $form->setId( 'mw-changeemail-form' );
124        $form->setTableId( 'mw-changeemail-table' );
125        $form->setSubmitTextMsg( 'changeemail-submit' );
126        $form->addHiddenFields( $this->getRequest()->getValues( 'returnto', 'returntoquery' ) );
127
128        $form->addHeaderHtml( $this->msg( 'changeemail-header' )->parseAsBlock() );
129        $form->setSubmitID( 'change_email_submit' );
130    }
131
132    public function onSubmit( array $data ) {
133        $this->status = $this->attemptChange( $this->getUser(), $data['NewEmail'] );
134
135        return $this->status;
136    }
137
138    public function onSuccess() {
139        $request = $this->getRequest();
140
141        $returnTo = $request->getVal( 'returnto' );
142        $titleObj = $returnTo !== null ? Title::newFromText( $returnTo ) : null;
143        if ( !$titleObj instanceof Title ) {
144            $titleObj = Title::newMainPage();
145        }
146        $query = $request->getVal( 'returntoquery', '' );
147
148        if ( $this->status->value === true ) {
149            $this->getOutput()->redirect( $titleObj->getFullUrlForRedirect( $query ) );
150        } elseif ( $this->status->value === 'eauth' ) {
151            # Notify user that a confirmation email has been sent...
152            $out = $this->getOutput();
153            $out->addModuleStyles( 'mediawiki.codex.messagebox.styles' );
154            $out->addHTML(
155                Html::warningBox(
156                    $out->msg( 'eauthentsent', $this->getUser()->getName() )->parse()
157                )
158            );
159            // just show the link to go back
160            $this->getOutput()->addReturnTo( $titleObj, wfCgiToArray( $query ) );
161        }
162    }
163
164    /**
165     * @param User $user
166     * @param string $newAddr
167     *
168     * @return Status
169     */
170    private function attemptChange( User $user, $newAddr ) {
171        if ( $newAddr !== '' && !Sanitizer::validateEmail( $newAddr ) ) {
172            return Status::newFatal( 'invalidemailaddress' );
173        }
174
175        $oldAddr = $user->getEmail();
176        if ( $newAddr === $oldAddr ) {
177            return Status::newFatal( 'changeemail-nochange' );
178        }
179
180        if ( strlen( $newAddr ) > 255 ) {
181            return Status::newFatal( 'changeemail-maxlength' );
182        }
183
184        // To prevent spam, rate limit adding a new address, but do
185        // not rate limit removing an address.
186        if ( $newAddr !== '' ) {
187            // Enforce permissions, user blocks, and rate limits
188            $status = $this->authorizeAction( 'changeemail' );
189            if ( !$status->isGood() ) {
190                return Status::wrap( $status );
191            }
192        }
193
194        $userLatest = $user->getInstanceForUpdate();
195        $status = $userLatest->setEmailWithConfirmation( $newAddr );
196        if ( !$status->isGood() ) {
197            return $status;
198        }
199
200        LoggerFactory::getInstance( 'authentication' )->info(
201            'Changing email address for {user} from {oldemail} to {newemail}', [
202                'user' => $userLatest->getName(),
203                'oldemail' => $oldAddr,
204                'newemail' => $newAddr,
205            ]
206        );
207
208        $this->getHookRunner()->onPrefsEmailAudit( $userLatest, $oldAddr, $newAddr );
209
210        $userLatest->saveSettings();
211
212        return $status;
213    }
214
215    public function requiresUnblock() {
216        return false;
217    }
218
219    protected function getGroupName() {
220        return 'login';
221    }
222}
223
224/** @deprecated class alias since 1.41 */
225class_alias( SpecialChangeEmail::class, 'SpecialChangeEmail' );