Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
36.99% covered (danger)
36.99%
27 / 73
16.67% covered (danger)
16.67%
1 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialConfirmEmail
37.50% covered (danger)
37.50%
27 / 72
16.67% covered (danger)
16.67%
1 / 6
117.66
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 doesWrites
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 execute
71.43% covered (warning)
71.43%
10 / 14
0.00% covered (danger)
0.00%
0 / 1
5.58
 showRequestForm
48.39% covered (danger)
48.39%
15 / 31
0.00% covered (danger)
0.00%
0 / 1
10.95
 submitSend
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 attemptConfirm
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2/**
3 * Implements Special:Confirmemail
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @ingroup SpecialPage
22 */
23
24namespace MediaWiki\Specials;
25
26use IDBAccessObject;
27use MediaWiki\HTMLForm\HTMLForm;
28use MediaWiki\Language\RawMessage;
29use MediaWiki\Parser\Sanitizer;
30use MediaWiki\SpecialPage\SpecialPage;
31use MediaWiki\SpecialPage\UnlistedSpecialPage;
32use MediaWiki\Status\Status;
33use MediaWiki\User\User;
34use MediaWiki\User\UserFactory;
35use PermissionsError;
36use Profiler;
37use ReadOnlyError;
38use UserNotLoggedIn;
39use Wikimedia\ScopedCallback;
40
41/**
42 * Special page allows users to request email confirmation message, and handles
43 * processing of the confirmation code when the link in the email is followed
44 *
45 * @ingroup SpecialPage
46 * @author Brooke Vibber
47 * @author Rob Church <robchur@gmail.com>
48 */
49class SpecialConfirmEmail extends UnlistedSpecialPage {
50
51    private UserFactory $userFactory;
52
53    /**
54     * @param UserFactory $userFactory
55     */
56    public function __construct( UserFactory $userFactory ) {
57        parent::__construct( 'Confirmemail', 'editmyprivateinfo' );
58
59        $this->userFactory = $userFactory;
60    }
61
62    public function doesWrites() {
63        return true;
64    }
65
66    /**
67     * Main execution point
68     *
69     * @param null|string $code Confirmation code passed to the page
70     * @throws PermissionsError
71     * @throws ReadOnlyError
72     * @throws UserNotLoggedIn
73     */
74    public function execute( $code ) {
75        // Ignore things like primary queries/connections on GET requests.
76        // It's very convenient to just allow formless link usage.
77        $trxProfiler = Profiler::instance()->getTransactionProfiler();
78
79        $this->setHeaders();
80        $this->checkReadOnly();
81        $this->checkPermissions();
82
83        // This could also let someone check the current email address, so
84        // require both permissions.
85        if ( !$this->getAuthority()->isAllowed( 'viewmyprivateinfo' ) ) {
86            throw new PermissionsError( 'viewmyprivateinfo' );
87        }
88
89        if ( $code === null || $code === '' ) {
90            $this->requireNamedUser( 'confirmemail_needlogin' );
91            if ( Sanitizer::validateEmail( $this->getUser()->getEmail() ) ) {
92                $this->showRequestForm();
93            } else {
94                $this->getOutput()->addWikiMsg( 'confirmemail_noemail' );
95            }
96        } else {
97            $scope = $trxProfiler->silenceForScope( $trxProfiler::EXPECTATION_REPLICAS_ONLY );
98            $this->attemptConfirm( $code );
99            ScopedCallback::consume( $scope );
100        }
101    }
102
103    /**
104     * Show a nice form for the user to request a confirmation mail
105     */
106    private function showRequestForm() {
107        $user = $this->getUser();
108        $out = $this->getOutput();
109
110        if ( !$user->isEmailConfirmed() ) {
111            $descriptor = [];
112            if ( $user->isEmailConfirmationPending() ) {
113                $descriptor += [
114                    'pending' => [
115                        'type' => 'info',
116                        'raw' => true,
117                        'default' => "<div class=\"error mw-confirmemail-pending\">\n" .
118                            $this->msg( 'confirmemail_pending' )->escaped() .
119                            "\n</div>",
120                    ],
121                ];
122            }
123
124            $out->addWikiMsg( 'confirmemail_text' );
125            $form = HTMLForm::factory( 'ooui', $descriptor, $this->getContext() );
126            $form
127                ->setAction( $this->getPageTitle()->getLocalURL() )
128                ->setSubmitTextMsg( 'confirmemail_send' )
129                ->setSubmitCallback( [ $this, 'submitSend' ] );
130
131            $retval = $form->show();
132
133            if ( $retval === true ) {
134                // should never happen, but if so, don't let the user without any message
135                $out->addWikiMsg( 'confirmemail_sent' );
136            } elseif ( $retval instanceof Status && $retval->isGood() ) {
137                $out->addWikiTextAsInterface( $retval->getValue() );
138            }
139        } else {
140            // date and time are separate parameters to facilitate localisation.
141            // $time is kept for backward compat reasons.
142            // 'emailauthenticated' is also used in SpecialPreferences.php
143            $lang = $this->getLanguage();
144            $emailAuthenticated = $user->getEmailAuthenticationTimestamp();
145            $time = $lang->userTimeAndDate( $emailAuthenticated, $user );
146            $d = $lang->userDate( $emailAuthenticated, $user );
147            $t = $lang->userTime( $emailAuthenticated, $user );
148            $out->addWikiMsg( 'emailauthenticated', $time, $d, $t );
149        }
150    }
151
152    /**
153     * Callback for HTMLForm send confirmation mail.
154     *
155     * @return Status Status object with the result
156     */
157    public function submitSend() {
158        $status = $this->getUser()->sendConfirmationMail();
159        if ( $status->isGood() ) {
160            return Status::newGood( $this->msg( 'confirmemail_sent' )->text() );
161        } else {
162            return Status::newFatal( new RawMessage(
163                $status->getWikiText( 'confirmemail_sendfailed', false, $this->getLanguage() )
164            ) );
165        }
166    }
167
168    /**
169     * Attempt to confirm the user's email address and show success or failure
170     * as needed; if successful, take the user to log in
171     *
172     * @param string $code Confirmation code
173     */
174    private function attemptConfirm( $code ) {
175        $user = $this->userFactory->newFromConfirmationCode(
176            $code,
177            IDBAccessObject::READ_LATEST
178        );
179
180        if ( !is_object( $user ) ) {
181            if ( User::isWellFormedConfirmationToken( $code ) ) {
182                $this->getOutput()->addWikiMsg( 'confirmemail_invalid' );
183            } else {
184                $this->getOutput()->addWikiMsg( 'confirmemail_invalid_format' );
185            }
186
187            return;
188        }
189
190        // Enforce permissions, user blocks, and rate limits
191        $this->authorizeAction( 'confirmemail' )->throwErrorPageError();
192
193        $userLatest = $user->getInstanceForUpdate();
194        $userLatest->confirmEmail();
195        $userLatest->saveSettings();
196        $message = $this->getUser()->isNamed() ? 'confirmemail_loggedin' : 'confirmemail_success';
197        $this->getOutput()->addWikiMsg( $message );
198
199        if ( !$this->getUser()->isNamed() ) {
200            $title = SpecialPage::getTitleFor( 'Userlogin' );
201            $this->getOutput()->returnToMain( true, $title );
202        }
203    }
204}
205
206/** @deprecated class alias since 1.41 */
207class_alias( SpecialConfirmEmail::class, 'SpecialConfirmEmail' );