MediaWiki  master
SpecialConfirmEmail.php
Go to the documentation of this file.
1 <?php
24 namespace MediaWiki\Specials;
25 
26 use HTMLForm;
35 use Profiler;
36 use ReadOnlyError;
37 use UserNotLoggedIn;
38 use Wikimedia\ScopedCallback;
39 
49 
50  private UserFactory $userFactory;
51 
55  public function __construct( UserFactory $userFactory ) {
56  parent::__construct( 'Confirmemail', 'editmyprivateinfo' );
57 
58  $this->userFactory = $userFactory;
59  }
60 
61  public function doesWrites() {
62  return true;
63  }
64 
73  public function execute( $code ) {
74  // Ignore things like primary queries/connections on GET requests.
75  // It's very convenient to just allow formless link usage.
76  $trxProfiler = Profiler::instance()->getTransactionProfiler();
77 
78  $this->setHeaders();
79  $this->checkReadOnly();
80  $this->checkPermissions();
81 
82  // This could also let someone check the current email address, so
83  // require both permissions.
84  if ( !$this->getAuthority()->isAllowed( 'viewmyprivateinfo' ) ) {
85  throw new PermissionsError( 'viewmyprivateinfo' );
86  }
87 
88  if ( $code === null || $code === '' ) {
89  $this->requireNamedUser( 'confirmemail_needlogin' );
90  if ( Sanitizer::validateEmail( $this->getUser()->getEmail() ) ) {
91  $this->showRequestForm();
92  } else {
93  $this->getOutput()->addWikiMsg( 'confirmemail_noemail' );
94  }
95  } else {
96  $scope = $trxProfiler->silenceForScope( $trxProfiler::EXPECTATION_REPLICAS_ONLY );
97  $this->attemptConfirm( $code );
98  ScopedCallback::consume( $scope );
99  }
100  }
101 
105  private function showRequestForm() {
106  $user = $this->getUser();
107  $out = $this->getOutput();
108 
109  if ( !$user->isEmailConfirmed() ) {
110  $descriptor = [];
111  if ( $user->isEmailConfirmationPending() ) {
112  $descriptor += [
113  'pending' => [
114  'type' => 'info',
115  'raw' => true,
116  'default' => "<div class=\"error mw-confirmemail-pending\">\n" .
117  $this->msg( 'confirmemail_pending' )->escaped() .
118  "\n</div>",
119  ],
120  ];
121  }
122 
123  $out->addWikiMsg( 'confirmemail_text' );
124  $form = HTMLForm::factory( 'ooui', $descriptor, $this->getContext() );
125  $form
126  ->setAction( $this->getPageTitle()->getLocalURL() )
127  ->setSubmitTextMsg( 'confirmemail_send' )
128  ->setSubmitCallback( [ $this, 'submitSend' ] );
129 
130  $retval = $form->show();
131 
132  if ( $retval === true ) {
133  // should never happen, but if so, don't let the user without any message
134  $out->addWikiMsg( 'confirmemail_sent' );
135  } elseif ( $retval instanceof Status && $retval->isGood() ) {
136  $out->addWikiTextAsInterface( $retval->getValue() );
137  }
138  } else {
139  // date and time are separate parameters to facilitate localisation.
140  // $time is kept for backward compat reasons.
141  // 'emailauthenticated' is also used in SpecialPreferences.php
142  $lang = $this->getLanguage();
143  $emailAuthenticated = $user->getEmailAuthenticationTimestamp();
144  $time = $lang->userTimeAndDate( $emailAuthenticated, $user );
145  $d = $lang->userDate( $emailAuthenticated, $user );
146  $t = $lang->userTime( $emailAuthenticated, $user );
147  $out->addWikiMsg( 'emailauthenticated', $time, $d, $t );
148  }
149  }
150 
156  public function submitSend() {
157  $status = $this->getUser()->sendConfirmationMail();
158  if ( $status->isGood() ) {
159  return Status::newGood( $this->msg( 'confirmemail_sent' )->text() );
160  } else {
161  return Status::newFatal( new RawMessage(
162  $status->getWikiText( 'confirmemail_sendfailed', false, $this->getLanguage() )
163  ) );
164  }
165  }
166 
173  private function attemptConfirm( $code ) {
174  $user = $this->userFactory->newFromConfirmationCode(
175  $code,
176  UserFactory::READ_LATEST
177  );
178 
179  if ( !is_object( $user ) ) {
180  if ( User::isWellFormedConfirmationToken( $code ) ) {
181  $this->getOutput()->addWikiMsg( 'confirmemail_invalid' );
182  } else {
183  $this->getOutput()->addWikiMsg( 'confirmemail_invalid_format' );
184  }
185 
186  return;
187  }
188 
189  // rate limit email confirmations
190  if ( $user->pingLimiter( 'confirmemail' ) ) {
191  $this->getOutput()->addWikiMsg( 'actionthrottledtext' );
192 
193  return;
194  }
195 
196  $userLatest = $user->getInstanceForUpdate();
197  $userLatest->confirmEmail();
198  $userLatest->saveSettings();
199  $message = $this->getUser()->isNamed() ? 'confirmemail_loggedin' : 'confirmemail_success';
200  $this->getOutput()->addWikiMsg( $message );
201 
202  if ( !$this->getUser()->isNamed() ) {
203  $title = SpecialPage::getTitleFor( 'Userlogin' );
204  $this->getOutput()->returnToMain( true, $title );
205  }
206  }
207 }
208 
212 class_alias( SpecialConfirmEmail::class, 'SpecialConfirmEmail' );
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition: HTMLForm.php:158
static factory( $displayFormat, $descriptor, IContextSource $context, $messagePrefix='')
Construct a HTMLForm object for given display type.
Definition: HTMLForm.php:360
Variant of the Message class.
Definition: RawMessage.php:40
HTML sanitizer for MediaWiki.
Definition: Sanitizer.php:46
static validateEmail( $addr)
Does a string look like an e-mail address?
Definition: Sanitizer.php:1889
Parent class for all special pages.
Definition: SpecialPage.php:65
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
getUser()
Shortcut to get the User executing this instance.
getPageTitle( $subpage=false)
Get a self-referential title object.
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
checkReadOnly()
If the wiki is currently in readonly mode, throws a ReadOnlyError.
getContext()
Gets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
requireNamedUser( $reasonMsg='exception-nologin-text', $titleMsg='exception-nologin')
If the user is not logged in or is a temporary user, throws UserNotLoggedIn.
getOutput()
Get the OutputPage being used for this instance.
getAuthority()
Shortcut to get the Authority executing this instance.
getLanguage()
Shortcut to get user's language.
Shortcut to construct a special page which is unlisted by default.
Special page allows users to request email confirmation message, and handles processing of the confir...
doesWrites()
Indicates whether this special page may perform database writes.
submitSend()
Callback for HTMLForm send confirmation mail.
execute( $code)
Main execution point.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:58
Creates User objects.
Definition: UserFactory.php:41
internal since 1.36
Definition: User.php:98
static isWellFormedConfirmationToken(string $token)
Check if the given email confirmation token is well-formed (to detect mangling by email clients).
Definition: User.php:3108
Show an error when a user tries to do something they do not have the necessary permissions for.
Profiler base class that defines the interface and some shared functionality.
Definition: Profiler.php:37
static instance()
Definition: Profiler.php:105
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:73
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:85
Redirect a user to the login page.