MediaWiki master
LoginSignupSpecialPage.php
Go to the documentation of this file.
1<?php
24namespace MediaWiki\SpecialPage;
25
27use Exception;
28use FatalError;
29use LogicException;
30use LoginHelper;
52use Skin;
53use StatusValue;
54use Wikimedia\ScopedCallback;
55
62 protected $mReturnTo;
63 protected $mPosted;
64 protected $mAction;
65 protected $mLanguage;
66 protected $mVariant;
67 protected $mReturnToQuery;
68 protected $mToken;
69 protected $mStickHTTPS;
70 protected $mFromHTTP;
71 protected $mEntryError = '';
72 protected $mEntryErrorType = 'error';
73
74 protected $mLoaded = false;
75 protected $mLoadedRequest = false;
77 private $reasonValidatorResult = null;
78
80 protected $securityLevel;
81
87 protected $targetUser;
88
90 protected $authForm;
91
92 abstract protected function isSignup();
93
100 abstract protected function successfulAction( $direct = false, $extraMessages = null );
101
107 abstract protected function logAuthResult( $success, $status = null );
108
109 protected function setRequest( array $data, $wasPosted = null ) {
110 parent::setRequest( $data, $wasPosted );
111 $this->mLoadedRequest = false;
112 }
113
117 private function loadRequestParameters() {
118 if ( $this->mLoadedRequest ) {
119 return;
120 }
121 $this->mLoadedRequest = true;
122 $request = $this->getRequest();
123
124 $this->mPosted = $request->wasPosted();
125 $this->mAction = $request->getRawVal( 'action' );
126 $this->mFromHTTP = $request->getBool( 'fromhttp', false )
127 || $request->getBool( 'wpFromhttp', false );
128 $this->mStickHTTPS = $this->getConfig()->get( MainConfigNames::ForceHTTPS )
129 || ( !$this->mFromHTTP && $request->getProtocol() === 'https' )
130 || $request->getBool( 'wpForceHttps', false );
131 $this->mLanguage = $request->getText( 'uselang' );
132 $this->mVariant = $request->getText( 'variant' );
133 $this->mReturnTo = $request->getVal( 'returnto', '' );
134 $this->mReturnToQuery = $request->getVal( 'returntoquery', '' );
135 }
136
142 protected function load( $subPage ) {
143 $this->loadRequestParameters();
144 if ( $this->mLoaded ) {
145 return;
146 }
147 $this->mLoaded = true;
148 $request = $this->getRequest();
149
150 $securityLevel = $this->getRequest()->getText( 'force' );
151 if (
152 $securityLevel &&
153 MediaWikiServices::getInstance()->getAuthManager()->securitySensitiveOperationStatus(
154 $securityLevel ) === AuthManager::SEC_REAUTH
155 ) {
156 $this->securityLevel = $securityLevel;
157 }
158
159 $this->loadAuth( $subPage );
160
161 $this->mToken = $request->getVal( $this->getTokenName() );
162
163 // Show an error or warning passed on from a previous page
164 $entryError = $this->msg( $request->getVal( 'error', '' ) );
165 $entryWarning = $this->msg( $request->getVal( 'warning', '' ) );
166 // bc: provide login link as a parameter for messages where the translation
167 // was not updated
168 $loginreqlink = $this->getLinkRenderer()->makeKnownLink(
169 $this->getPageTitle(),
170 $this->msg( 'loginreqlink' )->text(),
171 [],
172 [
173 'returnto' => $this->mReturnTo,
174 'returntoquery' => $this->mReturnToQuery,
175 'uselang' => $this->mLanguage ?: null,
176 'variant' => $this->mVariant ?: null,
177 'fromhttp' => $this->getConfig()->get( MainConfigNames::SecureLogin ) &&
178 $this->mFromHTTP ? '1' : null,
179 ]
180 );
181
182 // Only show valid error or warning messages.
183 if ( $entryError->exists()
184 && in_array( $entryError->getKey(), LoginHelper::getValidErrorMessages(), true )
185 ) {
186 $this->mEntryErrorType = 'error';
187 $this->mEntryError = $entryError->rawParams( $loginreqlink )->parse();
188
189 } elseif ( $entryWarning->exists()
190 && in_array( $entryWarning->getKey(), LoginHelper::getValidErrorMessages(), true )
191 ) {
192 $this->mEntryErrorType = 'warning';
193 $this->mEntryError = $entryWarning->rawParams( $loginreqlink )->parse();
194 }
195
196 # 1. When switching accounts, it sucks to get automatically logged out
197 # 2. Do not return to PasswordReset after a successful password change
198 # but goto Wiki start page (Main_Page) instead ( T35997 )
199 $returnToTitle = Title::newFromText( $this->mReturnTo );
200 if ( is_object( $returnToTitle )
201 && ( $returnToTitle->isSpecial( 'Userlogout' )
202 || $returnToTitle->isSpecial( 'PasswordReset' ) )
203 ) {
204 $this->mReturnTo = '';
205 $this->mReturnToQuery = '';
206 }
207 }
208
209 protected function getPreservedParams( $withToken = false ) {
210 $params = parent::getPreservedParams( $withToken );
211 $params += [
212 'returnto' => $this->mReturnTo ?: null,
213 'returntoquery' => $this->mReturnToQuery ?: null,
214 ];
215 if ( $this->getConfig()->get( MainConfigNames::SecureLogin ) && !$this->isSignup() ) {
216 $params['fromhttp'] = $this->mFromHTTP ? '1' : null;
217 }
218 return $params;
219 }
220
221 protected function beforeExecute( $subPage ) {
222 // finish initializing the class before processing the request - T135924
223 $this->loadRequestParameters();
224 return parent::beforeExecute( $subPage );
225 }
226
231 public function execute( $subPage ) {
232 if ( $this->mPosted ) {
233 $time = microtime( true );
234 $profilingScope = new ScopedCallback( function () use ( $time ) {
235 $time = microtime( true ) - $time;
236 $stats = MediaWikiServices::getInstance()->getStatsFactory();
237 $stats->getTiming( 'auth_specialpage_executeTiming_seconds' )
238 ->setLabel( 'action', $this->authAction )
239 ->copyToStatsdAt( "timing.login.ui.{$this->authAction}" )
240 ->observe( $time * 1000 );
241 } );
242 }
243
244 $authManager = MediaWikiServices::getInstance()->getAuthManager();
245 $session = SessionManager::getGlobalSession();
246
247 // Session data is used for various things in the authentication process, so we must make
248 // sure a session cookie or some equivalent mechanism is set.
249 $session->persist();
250 // Explicitly disable cache to ensure cookie blocks may be set (T152462).
251 // (Technically redundant with sessions persisting from this page.)
252 $this->getOutput()->disableClientCache();
253
254 $this->load( $subPage );
255 $this->setHeaders();
256 $this->checkPermissions();
257
258 // Make sure the system configuration allows log in / sign up
259 if ( !$this->isSignup() && !$authManager->canAuthenticateNow() ) {
260 if ( !$session->canSetUser() ) {
261 throw new ErrorPageError( 'cannotloginnow-title', 'cannotloginnow-text', [
262 $session->getProvider()->describe( $this->getLanguage() )
263 ] );
264 }
265 throw new ErrorPageError( 'cannotlogin-title', 'cannotlogin-text' );
266 } elseif ( $this->isSignup() && !$authManager->canCreateAccounts() ) {
267 throw new ErrorPageError( 'cannotcreateaccount-title', 'cannotcreateaccount-text' );
268 }
269
270 /*
271 * In the case where the user is already logged in, and was redirected to
272 * the login form from a page that requires login, do not show the login
273 * page. The use case scenario for this is when a user opens a large number
274 * of tabs, is redirected to the login page on all of them, and then logs
275 * in on one, expecting all the others to work properly.
276 *
277 * However, do show the form if it was visited intentionally (no 'returnto'
278 * is present). People who often switch between several accounts have grown
279 * accustomed to this behavior.
280 *
281 * For temporary users, the form is always shown, since the UI presents
282 * temporary users as not logged in and offers to discard their temporary
283 * account by logging in.
284 *
285 * Also make an exception when force=<level> is set in the URL, which means the user must
286 * reauthenticate for security reasons.
287 */
288 if ( !$this->isSignup() && !$this->mPosted && !$this->securityLevel &&
289 ( $this->mReturnTo !== '' || $this->mReturnToQuery !== '' ) &&
290 !$this->getUser()->isTemp() && $this->getUser()->isRegistered()
291 ) {
292 $this->successfulAction();
293 return;
294 }
295
296 // If logging in and not on HTTPS, either redirect to it or offer a link.
297 if ( $this->getRequest()->getProtocol() !== 'https' ) {
298 $title = $this->getFullTitle();
299 $query = $this->getPreservedParams( false ) + [
300 'title' => null,
301 ( $this->mEntryErrorType === 'error' ? 'error'
302 : 'warning' ) => $this->mEntryError,
303 ] + $this->getRequest()->getQueryValues();
304 $url = $title->getFullURL( $query, false, PROTO_HTTPS );
305 if ( $this->getConfig()->get( MainConfigNames::SecureLogin ) && !$this->mFromHTTP ) {
306 // Avoid infinite redirect
307 $url = wfAppendQuery( $url, 'fromhttp=1' );
308 $this->getOutput()->redirect( $url );
309 // Since we only do this redir to change proto, always vary
310 $this->getOutput()->addVaryHeader( 'X-Forwarded-Proto' );
311
312 return;
313 } else {
314 // A wiki without HTTPS login support should set $wgServer to
315 // http://somehost, in which case the secure URL generated
316 // above won't actually start with https://
317 if ( str_starts_with( $url, 'https://' ) ) {
318 $this->mSecureLoginUrl = $url;
319 }
320 }
321 }
322
323 if ( !$this->isActionAllowed( $this->authAction ) ) {
324 // FIXME how do we explain this to the user? can we handle session loss better?
325 // messages used: authpage-cannot-login, authpage-cannot-login-continue,
326 // authpage-cannot-create, authpage-cannot-create-continue
327 $this->mainLoginForm( [], 'authpage-cannot-' . $this->authAction );
328 return;
329 }
330
331 if ( $this->canBypassForm( $button_name ) ) {
332 $this->setRequest( [], true );
333 $this->getRequest()->setVal( $this->getTokenName(), $this->getToken() );
334 if ( $button_name ) {
335 $this->getRequest()->setVal( $button_name, true );
336 }
337 }
338
339 $status = $this->trySubmit();
340
341 if ( !$status || !$status->isGood() ) {
342 $this->mainLoginForm( $this->authRequests, $status ? $status->getMessage() : '', 'error' );
343 return;
344 }
345
347 $response = $status->getValue();
348
349 $returnToUrl = $this->getPageTitle( 'return' )
350 ->getFullURL( $this->getPreservedParams( true ), false, PROTO_HTTPS );
351 switch ( $response->status ) {
352 case AuthenticationResponse::PASS:
353 $this->logAuthResult( true );
354 $this->proxyAccountCreation = $this->isSignup() && $this->getUser()->isNamed();
355 $this->targetUser = User::newFromName( $response->username );
356
357 if (
358 !$this->proxyAccountCreation
359 && $response->loginRequest
360 && $authManager->canAuthenticateNow()
361 ) {
362 // successful registration; log the user in instantly
363 $response2 = $authManager->beginAuthentication( [ $response->loginRequest ],
364 $returnToUrl );
365 if ( $response2->status !== AuthenticationResponse::PASS ) {
366 LoggerFactory::getInstance( 'login' )
367 ->error( 'Could not log in after account creation' );
368 $this->successfulAction( true, Status::newFatal( 'createacct-loginerror' ) );
369 break;
370 }
371 }
372
373 if ( !$this->proxyAccountCreation ) {
374 // Ensure that the context user is the same as the session user.
376 }
377
378 $this->successfulAction( true );
379 break;
380 case AuthenticationResponse::FAIL:
381 // fall through
382 case AuthenticationResponse::RESTART:
383 unset( $this->authForm );
384 if ( $response->status === AuthenticationResponse::FAIL ) {
385 $action = $this->getDefaultAction( $subPage );
386 $messageType = 'error';
387 } else {
388 $action = $this->getContinueAction( $this->authAction );
389 $messageType = 'warning';
390 }
391 $this->logAuthResult( false, $response->message ? $response->message->getKey() : '-' );
392 $this->loadAuth( $subPage, $action, true );
393 $this->mainLoginForm( $this->authRequests, $response->message, $messageType );
394 break;
395 case AuthenticationResponse::REDIRECT:
396 unset( $this->authForm );
397 $this->getOutput()->redirect( $response->redirectTarget );
398 break;
399 case AuthenticationResponse::UI:
400 unset( $this->authForm );
401 $this->authAction = $this->isSignup() ? AuthManager::ACTION_CREATE_CONTINUE
402 : AuthManager::ACTION_LOGIN_CONTINUE;
403 $this->authRequests = $response->neededRequests;
404 $this->mainLoginForm( $response->neededRequests, $response->message, $response->messageType );
405 break;
406 default:
407 throw new LogicException( 'invalid AuthenticationResponse' );
408 }
409 }
410
424 private function canBypassForm( &$button_name ) {
425 $button_name = null;
426 if ( $this->isContinued() ) {
427 return false;
428 }
429 $fields = AuthenticationRequest::mergeFieldInfo( $this->authRequests );
430 foreach ( $fields as $fieldname => $field ) {
431 if ( !isset( $field['type'] ) ) {
432 return false;
433 }
434 if ( !empty( $field['skippable'] ) ) {
435 continue;
436 }
437 if ( $field['type'] === 'button' ) {
438 if ( $button_name !== null ) {
439 $button_name = null;
440 return false;
441 } else {
442 $button_name = $fieldname;
443 }
444 } elseif ( $field['type'] !== 'null' ) {
445 return false;
446 }
447 }
448 return true;
449 }
450
460 protected function showSuccessPage(
461 $type, $title, $msgname, $injected_html, $extraMessages
462 ) {
463 $out = $this->getOutput();
464 if ( is_string( $title ) ) {
465 wfDeprecated( __METHOD__ . ' with string title', '1.41' ); // T343849
466 $title = ( new RawMessage( '$1' ) )->rawParams( $title );
467 }
468 $out->setPageTitleMsg( $title );
469 if ( $msgname ) {
470 $out->addWikiMsg( $msgname, wfEscapeWikiText( $this->getUser()->getName() ) );
471 }
472 if ( $extraMessages ) {
473 $extraMessages = Status::wrap( $extraMessages );
474 $out->addWikiTextAsInterface(
475 $extraMessages->getWikiText( false, false, $this->getLanguage() )
476 );
477 }
478
479 $out->addHTML( $injected_html );
480
481 $helper = new LoginHelper( $this->getContext() );
482 $helper->showReturnToPage( $type, $this->mReturnTo, $this->mReturnToQuery, $this->mStickHTTPS );
483 }
484
500 public function showReturnToPage(
501 $type, $returnTo = '', $returnToQuery = '', $stickHTTPS = false
502 ) {
503 $helper = new LoginHelper( $this->getContext() );
504 $helper->showReturnToPage( $type, $returnTo, $returnToQuery, $stickHTTPS );
505 }
506
511 protected function setSessionUserForCurrentRequest() {
512 // phpcs:ignore MediaWiki.Usage.ExtendClassUsage.FunctionVarUsage
513 global $wgLang;
514
515 $context = RequestContext::getMain();
516 $localContext = $this->getContext();
517 if ( $context !== $localContext ) {
518 // remove AuthManagerSpecialPage context hack
519 $this->setContext( $context );
520 }
521
522 $user = $context->getRequest()->getSession()->getUser();
523
524 StubGlobalUser::setUser( $user );
525 $context->setUser( $user );
526
527 // phpcs:ignore MediaWiki.Usage.ExtendClassUsage.FunctionVarUsage
528 $wgLang = $context->getLanguage();
529 }
530
544 protected function mainLoginForm( array $requests, $msg = '', $msgtype = 'error' ) {
545 $user = $this->getUser();
546 $out = $this->getOutput();
547
548 // FIXME how to handle empty $requests - restart, or no form, just an error message?
549 // no form would be better for no session type errors, restart is better when can* fails.
550 if ( !$requests ) {
551 $this->authAction = $this->getDefaultAction( $this->subPage );
552 $this->authForm = null;
553 $requests = MediaWikiServices::getInstance()->getAuthManager()
554 ->getAuthenticationRequests( $this->authAction, $user );
555 }
556
557 // Generic styles and scripts for both login and signup form
558 $out->addModuleStyles( [
559 'mediawiki.special.userlogin.common.styles'
560 ] );
561 if ( $this->isSignup() ) {
562 // XXX hack pending RL or JS parse() support for complex content messages T27349
563 $out->addJsConfigVars( 'wgCreateacctImgcaptchaHelp',
564 $this->msg( 'createacct-imgcaptcha-help' )->parse() );
565
566 // Additional styles and scripts for signup form
567 $out->addModules( 'mediawiki.special.createaccount' );
568 $out->addModuleStyles( [
569 'mediawiki.special.userlogin.signup.styles'
570 ] );
571 } else {
572 // Additional styles for login form
573 $out->addModuleStyles( [
574 'mediawiki.special.userlogin.login.styles'
575 ] );
576 }
577 $out->disallowUserJs(); // just in case...
578
579 $form = $this->getAuthForm( $requests, $this->authAction );
580 $form->prepareForm();
581
582 $submitStatus = Status::newGood();
583 if ( $msg && $msgtype === 'warning' ) {
584 $submitStatus->warning( $msg );
585 } elseif ( $msg && $msgtype === 'error' ) {
586 $submitStatus->fatal( $msg );
587 }
588
589 // warning header for non-standard workflows (e.g. security reauthentication)
590 if (
591 !$this->isSignup() &&
592 $this->getUser()->isRegistered() &&
593 !$this->getUser()->isTemp() &&
594 $this->authAction !== AuthManager::ACTION_LOGIN_CONTINUE
595 ) {
596 $reauthMessage = $this->securityLevel ? 'userlogin-reauth' : 'userlogin-loggedin';
597 $submitStatus->warning( $reauthMessage, $this->getUser()->getName() );
598 }
599
600 $formHtml = $form->getHTML( $submitStatus );
601
602 $out->addHTML( $this->getPageHtml( $formHtml ) );
603 }
604
611 protected function getPageHtml( $formHtml ) {
612 $loginPrompt = $this->isSignup() ? '' : Html::rawElement( 'div',
613 [ 'id' => 'userloginprompt' ], $this->msg( 'loginprompt' )->parseAsBlock() );
614 $languageLinks = $this->getConfig()->get( MainConfigNames::LoginLanguageSelector )
615 ? $this->makeLanguageSelector() : '';
616 $signupStartMsg = $this->msg( 'signupstart' );
617 $signupStart = ( $this->isSignup() && !$signupStartMsg->isDisabled() )
618 ? Html::rawElement( 'div', [ 'id' => 'signupstart' ], $signupStartMsg->parseAsBlock() ) : '';
619 if ( $languageLinks ) {
620 $languageLinks = Html::rawElement( 'div', [ 'id' => 'languagelinks' ],
621 Html::rawElement( 'p', [], $languageLinks )
622 );
623 }
624 if ( $this->getUser()->isTemp() ) {
625 $noticeHtml = $this->getNoticeHtml();
626 } else {
627 $noticeHtml = '';
628 }
629 $formBlock = Html::rawElement( 'div', [ 'id' => 'userloginForm' ], $formHtml );
630 $formAndBenefits = $formBlock;
631 if ( $this->isSignup() && $this->showExtraInformation() ) {
632 $benefitsContainerHtml = null;
633 $info = [
634 'context' => $this->getContext(),
635 'form' => $this->authForm,
636 ];
637 $options = [
638 'beforeForm' => false,
639 ];
640 $this->getHookRunner()->onSpecialCreateAccountBenefits(
641 $benefitsContainerHtml, $info, $options
642 );
643 if ( $benefitsContainerHtml === null ) {
644 $benefitsContainerHtml = $this->getBenefitsContainerHtml();
645 }
646 $formAndBenefits = $options['beforeForm']
647 ? ( $benefitsContainerHtml . $formBlock )
648 : ( $formBlock . $benefitsContainerHtml );
649 }
650
651 return Html::rawElement( 'div', [ 'class' => 'mw-ui-container' ],
652 $loginPrompt
653 . $languageLinks
654 . $signupStart
655 . $noticeHtml
656 . $formAndBenefits
657 );
658 }
659
667 protected function getBenefitsContainerHtml(): string {
668 $benefitsContainer = '';
669 $this->getOutput()->addModuleStyles( [ 'oojs-ui.styles.icons-user' ] );
670 if ( $this->isSignup() && $this->showExtraInformation() ) {
671 if ( !$this->getUser()->isTemp() ) {
672 // The following messages are used here:
673 // * createacct-benefit-icon1 createacct-benefit-head1 createacct-benefit-body1
674 // * createacct-benefit-icon2 createacct-benefit-head2 createacct-benefit-body2
675 // * createacct-benefit-icon3 createacct-benefit-head3 createacct-benefit-body3
676 $benefitCount = 3;
677 $benefitList = '';
678 for ( $benefitIdx = 1; $benefitIdx <= $benefitCount; $benefitIdx++ ) {
679 $headUnescaped = $this->msg( "createacct-benefit-head$benefitIdx" )->text();
680 $iconClass = $this->msg( "createacct-benefit-icon$benefitIdx" )->text();
681 $benefitList .= Html::rawElement( 'div', [ 'class' => "mw-number-text $iconClass" ],
682 Html::rawElement( 'h3', [],
683 $this->msg( "createacct-benefit-head$benefitIdx" )->escaped()
684 )
685 . Html::rawElement( 'p', [],
686 $this->msg( "createacct-benefit-body$benefitIdx" )->params( $headUnescaped )->escaped()
687 )
688 );
689 }
690 $benefitsContainer = Html::rawElement( 'div', [ 'class' => 'mw-createacct-benefits-container' ],
691 Html::rawElement( 'h2', [], $this->msg( 'createacct-benefit-heading' )->escaped() )
692 . Html::rawElement( 'div', [ 'class' => 'mw-createacct-benefits-list' ], $benefitList )
693 );
694 } else {
695 $benefitList = '';
696 $this->getOutput()->addModuleStyles(
697 [
698 'oojs-ui.styles.icons-moderation',
699 'oojs-ui.styles.icons-interactions',
700 ]
701 );
702 $benefits = [
703 [
704 'icon' => 'oo-ui-icon-unStar',
705 'description' => $this->msg( "benefit-1-description" )->escaped()
706 ],
707 [
708 'icon' => 'oo-ui-icon-userContributions',
709 'description' => $this->msg( "benefit-2-description" )->escaped()
710 ],
711 [
712 'icon' => 'oo-ui-icon-settings',
713 'description' => $this->msg( "benefit-3-description" )->escaped()
714 ]
715 ];
716 foreach ( $benefits as $benefit ) {
717 $benefitContent = Html::rawElement( 'div', [ 'class' => 'mw-benefit-item' ],
718 Html::rawElement( 'span', [ 'class' => $benefit[ 'icon' ] ] )
719 . Html::rawElement( 'p', [], $benefit['description'] )
720 );
721
722 $benefitList .= Html::rawElement(
723 'div', [ 'class' => 'mw-benefit-item-wrapper' ], $benefitContent );
724 }
725
726 $benefitsListWrapper = Html::rawElement(
727 'div', [ 'class' => 'mw-benefit-list-wrapper' ], $benefitList );
728
729 $headingSubheadingWrapper = Html::rawElement( 'div', [ 'class' => 'mw-heading-subheading-wrapper' ],
730 Html::rawElement( 'h2', [], $this->msg( 'createacct-benefit-heading-temp-user' )->escaped() )
731 . Html::rawElement( 'p', [ 'class' => 'mw-benefit-subheading' ], $this->msg(
732 'createacct-benefit-subheading-temp-user' )->escaped() )
733 );
734
735 $benefitsContainer = Html::rawElement(
736 'div', [ 'class' => 'mw-createacct-benefits-container' ],
737 $headingSubheadingWrapper
738 . $benefitsListWrapper
739 );
740 }
741 }
742 return $benefitsContainer;
743 }
744
751 protected function getAuthForm( array $requests, $action ) {
752 // FIXME merge this with parent
753
754 if ( isset( $this->authForm ) ) {
755 return $this->authForm;
756 }
757
758 $usingHTTPS = $this->getRequest()->getProtocol() === 'https';
759
760 // get basic form description from the auth logic
761 $fieldInfo = AuthenticationRequest::mergeFieldInfo( $requests );
762 // this will call onAuthChangeFormFields()
763 $formDescriptor = $this->fieldInfoToFormDescriptor( $requests, $fieldInfo, $this->authAction );
764 $this->postProcessFormDescriptor( $formDescriptor, $requests );
765
766 $context = $this->getContext();
767 if ( $context->getRequest() !== $this->getRequest() ) {
768 // We have overridden the request, need to make sure the form uses that too.
769 $context = new DerivativeContext( $this->getContext() );
770 $context->setRequest( $this->getRequest() );
771 }
772 $form = HTMLForm::factory( 'codex', $formDescriptor, $context );
773
774 $form->addHiddenField( 'authAction', $this->authAction );
775 if ( $this->mLanguage ) {
776 $form->addHiddenField( 'uselang', $this->mLanguage );
777 }
778 if ( $this->mVariant ) {
779 $form->addHiddenField( 'variant', $this->mVariant );
780 }
781 $form->addHiddenField( 'force', $this->securityLevel );
782 $form->addHiddenField( $this->getTokenName(), $this->getToken()->toString() );
783 $config = $this->getConfig();
784 if ( $config->get( MainConfigNames::SecureLogin ) &&
785 !$config->get( MainConfigNames::ForceHTTPS ) ) {
786 // If using HTTPS coming from HTTP, then the 'fromhttp' parameter must be preserved
787 if ( !$this->isSignup() ) {
788 $form->addHiddenField( 'wpForceHttps', (int)$this->mStickHTTPS );
789 $form->addHiddenField( 'wpFromhttp', $usingHTTPS );
790 }
791 }
792
793 // set properties of the form itself
794 $form->setAction( $this->getPageTitle()->getLocalURL( $this->getReturnToQueryStringFragment() ) );
795 $form->setName( 'userlogin' . ( $this->isSignup() ? '2' : '' ) );
796 if ( $this->isSignup() ) {
797 $form->setId( 'userlogin2' );
798 }
799
800 $form->suppressDefaultSubmit();
801
802 $this->authForm = $form;
803
804 return $form;
805 }
806
808 public function onAuthChangeFormFields(
809 array $requests, array $fieldInfo, array &$formDescriptor, $action
810 ) {
811 $formDescriptor = self::mergeDefaultFormDescriptor( $fieldInfo, $formDescriptor,
812 $this->getFieldDefinitions( $fieldInfo ) );
813 }
814
821 protected function showExtraInformation() {
822 return $this->authAction !== $this->getContinueAction( $this->authAction )
823 && !$this->securityLevel;
824 }
825
831 protected function getFieldDefinitions( array $fieldInfo ) {
832 $isLoggedIn = $this->getUser()->isRegistered();
833 $continuePart = $this->isContinued() ? 'continue-' : '';
834 $anotherPart = $isLoggedIn ? 'another-' : '';
835 // @phan-suppress-next-line PhanUndeclaredMethod
836 $expiration = $this->getRequest()->getSession()->getProvider()->getRememberUserDuration();
837 $expirationDays = ceil( $expiration / ( 3600 * 24 ) );
838 $secureLoginLink = '';
839 if ( $this->mSecureLoginUrl ) {
840 $secureLoginLink = Html::element( 'a', [
841 'href' => $this->mSecureLoginUrl,
842 'class' => 'mw-login-flush-right mw-secure',
843 ], $this->msg( 'userlogin-signwithsecure' )->text() );
844 }
845 $usernameHelpLink = '';
846 if ( !$this->msg( 'createacct-helpusername' )->isDisabled() ) {
847 $usernameHelpLink = Html::rawElement( 'span', [
848 'class' => 'mw-login-flush-right',
849 ], $this->msg( 'createacct-helpusername' )->parse() );
850 }
851
852 if ( $this->isSignup() ) {
853 $config = $this->getConfig();
854 $hideIf = isset( $fieldInfo['mailpassword'] ) ? [ 'hide-if' => [ '===', 'mailpassword', '1' ] ] : [];
855 $fieldDefinitions = [
856 'statusarea' => [
857 // Used by the mediawiki.special.createaccount module for error display.
858 // FIXME: Merge this with HTMLForm's normal status (error) area
859 'type' => 'info',
860 'raw' => true,
861 'default' => Html::element( 'div', [ 'id' => 'mw-createacct-status-area' ] ),
862 'weight' => -105,
863 ],
864 'username' => [
865 'label-raw' => $this->msg( 'userlogin-yourname' )->escaped() . $usernameHelpLink,
866 'id' => 'wpName2',
867 'placeholder-message' => $isLoggedIn ? 'createacct-another-username-ph'
868 : 'userlogin-yourname-ph',
869 ],
870 'mailpassword' => [
871 // create account without providing password, a temporary one will be mailed
872 'type' => 'check',
873 'label-message' => 'createaccountmail',
874 'name' => 'wpCreateaccountMail',
875 'id' => 'wpCreateaccountMail',
876 ],
877 'password' => [
878 'id' => 'wpPassword2',
879 'autocomplete' => 'new-password',
880 'placeholder-message' => 'createacct-yourpassword-ph',
881 'help-message' => 'createacct-useuniquepass',
882 ] + $hideIf,
883 'domain' => [],
884 'retype' => [
885 'type' => 'password',
886 'label-message' => 'createacct-yourpasswordagain',
887 'id' => 'wpRetype',
888 'cssclass' => 'loginPassword',
889 'size' => 20,
890 'autocomplete' => 'new-password',
891 'validation-callback' => function ( $value, $alldata ) {
892 if ( empty( $alldata['mailpassword'] ) && !empty( $alldata['password'] ) ) {
893 if ( !$value ) {
894 return $this->msg( 'htmlform-required' );
895 } elseif ( $value !== $alldata['password'] ) {
896 return $this->msg( 'badretype' );
897 }
898 }
899 return true;
900 },
901 'placeholder-message' => 'createacct-yourpasswordagain-ph',
902 ] + $hideIf,
903 'email' => [
904 'type' => 'email',
905 'label-message' => $config->get( MainConfigNames::EmailConfirmToEdit )
906 ? 'createacct-emailrequired' : 'createacct-emailoptional',
907 'id' => 'wpEmail',
908 'cssclass' => 'loginText',
909 'size' => '20',
910 'maxlength' => 255,
911 'autocomplete' => 'email',
912 // FIXME will break non-standard providers
913 'required' => $config->get( MainConfigNames::EmailConfirmToEdit ),
914 'validation-callback' => function ( $value, $alldata ) {
915 // AuthManager will check most of these, but that will make the auth
916 // session fail and this won't, so nicer to do it this way
917 if ( !$value &&
918 $this->getConfig()->get( MainConfigNames::EmailConfirmToEdit )
919 ) {
920 // no point in allowing registration without email when email is
921 // required to edit
922 return $this->msg( 'noemailtitle' );
923 } elseif ( !$value && !empty( $alldata['mailpassword'] ) ) {
924 // cannot send password via email when there is no email address
925 return $this->msg( 'noemailcreate' );
926 } elseif ( $value && !Sanitizer::validateEmail( $value ) ) {
927 return $this->msg( 'invalidemailaddress' );
928 } elseif ( is_string( $value ) && strlen( $value ) > 255 ) {
929 return $this->msg( 'changeemail-maxlength' );
930 }
931 return true;
932 },
933 // The following messages are used here:
934 // * createacct-email-ph
935 // * createacct-another-email-ph
936 'placeholder-message' => 'createacct-' . $anotherPart . 'email-ph',
937 ],
938 'realname' => [
939 'type' => 'text',
940 'help-message' => $isLoggedIn ? 'createacct-another-realname-tip'
941 : 'prefs-help-realname',
942 'label-message' => 'createacct-realname',
943 'cssclass' => 'loginText',
944 'size' => 20,
945 'placeholder-message' => 'createacct-realname',
946 'id' => 'wpRealName',
947 'autocomplete' => 'name',
948 ],
949 'reason' => [
950 // comment for the user creation log
951 'type' => 'text',
952 'label-message' => 'createacct-reason',
953 'cssclass' => 'loginText',
954 'id' => 'wpReason',
955 'size' => '20',
956 'validation-callback' => function ( $value, $alldata ) {
957 // if the user sets an email address as the user creation reason, confirm that
958 // that was their intent
959 if ( $value && Sanitizer::validateEmail( $value ) ) {
960 if ( $this->reasonValidatorResult !== null ) {
961 return $this->reasonValidatorResult;
962 }
963 $this->reasonValidatorResult = true;
964 $authManager = MediaWikiServices::getInstance()->getAuthManager();
965 if ( !$authManager->getAuthenticationSessionData( 'reason-retry', false ) ) {
966 $authManager->setAuthenticationSessionData( 'reason-retry', true );
967 $this->reasonValidatorResult = $this->msg( 'createacct-reason-confirm' );
968 }
969 return $this->reasonValidatorResult;
970 }
971 return true;
972 },
973 'placeholder-message' => 'createacct-reason-ph',
974 ],
975 'createaccount' => [
976 // submit button
977 'type' => 'submit',
978 // The following messages are used here:
979 // * createacct-submit
980 // * createacct-another-submit
981 // * createacct-continue-submit
982 // * createacct-another-continue-submit
983 'default' => $this->msg( 'createacct-' . $anotherPart . $continuePart .
984 'submit' )->text(),
985 'name' => 'wpCreateaccount',
986 'id' => 'wpCreateaccount',
987 'weight' => 100,
988 ],
989 ];
990 if ( !$this->msg( 'createacct-username-help' )->isDisabled() ) {
991 $fieldDefinitions['username']['help-message'] = 'createacct-username-help';
992 }
993 } else {
994 // When the user's password is too weak, they might be asked to provide a stronger one
995 // as a followup step. That is a form with only two fields, 'password' and 'retype',
996 // and they should behave more like account creation.
997 $passwordRequest = AuthenticationRequest::getRequestByClass( $this->authRequests,
998 PasswordAuthenticationRequest::class );
999 $changePassword = $passwordRequest && $passwordRequest->action == AuthManager::ACTION_CHANGE;
1000 $fieldDefinitions = [
1001 'username' => (
1002 [
1003 'label-raw' => $this->msg( 'userlogin-yourname' )->escaped() . $secureLoginLink,
1004 'id' => 'wpName1',
1005 'placeholder-message' => 'userlogin-yourname-ph',
1006 ] + ( $changePassword ? [
1007 // There is no username field on the AuthManager level when changing
1008 // passwords. Fake one because password
1009 'baseField' => 'password',
1010 'nodata' => true,
1011 'readonly' => true,
1012 'cssclass' => 'mw-htmlform-hidden-field',
1013 ] : [] )
1014 ),
1015 'password' => (
1016 $changePassword ? [
1017 'autocomplete' => 'new-password',
1018 'placeholder-message' => 'createacct-yourpassword-ph',
1019 'help-message' => 'createacct-useuniquepass',
1020 ] : [
1021 'id' => 'wpPassword1',
1022 'autocomplete' => 'current-password',
1023 'placeholder-message' => 'userlogin-yourpassword-ph',
1024 ]
1025 ),
1026 'retype' => [
1027 'type' => 'password',
1028 'autocomplete' => 'new-password',
1029 'placeholder-message' => 'createacct-yourpasswordagain-ph',
1030 ],
1031 'domain' => [],
1032 'rememberMe' => [
1033 // option for saving the user token to a cookie
1034 'type' => 'check',
1035 'cssclass' => 'mw-userlogin-rememberme',
1036 'name' => 'wpRemember',
1037 'label-message' => $this->msg( 'userlogin-remembermypassword' )
1038 ->numParams( $expirationDays ),
1039 'id' => 'wpRemember',
1040 ],
1041 'loginattempt' => [
1042 // submit button
1043 'type' => 'submit',
1044 // The following messages are used here:
1045 // * pt-login-button
1046 // * pt-login-continue-button
1047 'default' => $this->msg( 'pt-login-' . $continuePart . 'button' )->text(),
1048 'id' => 'wpLoginAttempt',
1049 'weight' => 100,
1050 ],
1051 'linkcontainer' => [
1052 // help link
1053 'type' => 'info',
1054 'cssclass' => 'mw-form-related-link-container mw-userlogin-help',
1055 // 'id' => 'mw-userlogin-help', // FIXME HTMLInfoField ignores this
1056 'raw' => true,
1057 'default' => Html::element( 'a', [
1058 'href' => Skin::makeInternalOrExternalUrl( $this->msg( 'helplogin-url' )
1059 ->inContentLanguage()
1060 ->text() ),
1061 ], $this->msg( 'userlogin-helplink2' )->text() ),
1062 'weight' => 200,
1063 ],
1064 // button for ResetPasswordSecondaryAuthenticationProvider
1065 'skipReset' => [
1066 'weight' => 110,
1067 'flags' => [],
1068 ],
1069 ];
1070 }
1071
1072 $fieldDefinitions['username'] += [
1073 'type' => 'text',
1074 'name' => 'wpName',
1075 'cssclass' => 'loginText mw-userlogin-username',
1076 'size' => 20,
1077 'autocomplete' => 'username',
1078 // 'required' => true,
1079 ];
1080 $fieldDefinitions['password'] += [
1081 'type' => 'password',
1082 // 'label-message' => 'userlogin-yourpassword', // would override the changepassword label
1083 'name' => 'wpPassword',
1084 'cssclass' => 'loginPassword mw-userlogin-password',
1085 'size' => 20,
1086 // 'required' => true,
1087 ];
1088
1089 if ( $this->mEntryError ) {
1090 $defaultHtml = '';
1091 if ( $this->mEntryErrorType === 'error' ) {
1092 $defaultHtml = Html::errorBox( $this->mEntryError );
1093 } elseif ( $this->mEntryErrorType === 'warning' ) {
1094 $defaultHtml = Html::warningBox( $this->mEntryError );
1095 }
1096 $fieldDefinitions['entryError'] = [
1097 'type' => 'info',
1098 'default' => $defaultHtml,
1099 'raw' => true,
1100 'rawrow' => true,
1101 'weight' => -100,
1102 ];
1103 }
1104 if ( !$this->showExtraInformation() ) {
1105 unset( $fieldDefinitions['linkcontainer'], $fieldDefinitions['signupend'] );
1106 }
1107 if ( $this->isSignup() && $this->showExtraInformation() ) {
1108 // blank signup footer for site customization
1109 // uses signupend-https for HTTPS requests if it's not blank, signupend otherwise
1110 $signupendMsg = $this->msg( 'signupend' );
1111 $signupendHttpsMsg = $this->msg( 'signupend-https' );
1112 if ( !$signupendMsg->isDisabled() ) {
1113 $usingHTTPS = $this->getRequest()->getProtocol() === 'https';
1114 $signupendText = ( $usingHTTPS && !$signupendHttpsMsg->isBlank() )
1115 ? $signupendHttpsMsg->parse() : $signupendMsg->parse();
1116 $fieldDefinitions['signupend'] = [
1117 'type' => 'info',
1118 'raw' => true,
1119 'default' => Html::rawElement( 'div', [ 'id' => 'signupend' ], $signupendText ),
1120 'weight' => 225,
1121 ];
1122 }
1123 }
1124 if ( !$this->isSignup() && $this->showExtraInformation() ) {
1125 $passwordReset = MediaWikiServices::getInstance()->getPasswordReset();
1126 if ( $passwordReset->isAllowed( $this->getUser() )->isGood() ) {
1127 $fieldDefinitions['passwordReset'] = [
1128 'type' => 'info',
1129 'raw' => true,
1130 'cssclass' => 'mw-form-related-link-container',
1131 'default' => $this->getLinkRenderer()->makeLink(
1132 SpecialPage::getTitleFor( 'PasswordReset' ),
1133 $this->msg( 'userlogin-resetpassword-link' )->text()
1134 ),
1135 'weight' => 230,
1136 ];
1137 }
1138
1139 // Don't show a "create account" link if the user can't.
1140 if ( $this->showCreateAccountLink() ) {
1141 // link to the other action
1142 $linkTitle = $this->getTitleFor( $this->isSignup() ? 'Userlogin' : 'CreateAccount' );
1143 $linkq = $this->getReturnToQueryStringFragment();
1144 // Pass any language selection on to the mode switch link
1145 if ( $this->mLanguage ) {
1146 $linkq .= '&uselang=' . urlencode( $this->mLanguage );
1147 }
1148 if ( $this->mVariant ) {
1149 $linkq .= '&variant=' . urlencode( $this->mVariant );
1150 }
1151 $isLoggedIn = $this->getUser()->isRegistered()
1152 && !$this->getUser()->isTemp();
1153
1154 $fieldDefinitions['createOrLogin'] = [
1155 'type' => 'info',
1156 'raw' => true,
1157 'linkQuery' => $linkq,
1158 'default' => function ( $params ) use ( $isLoggedIn, $linkTitle ) {
1159 $buttonClasses = 'cdx-button cdx-button--action-progressive '
1160 . 'cdx-button--fake-button cdx-button--fake-button--enabled';
1161
1162 return Html::rawElement( 'div',
1163 [ 'id' => 'mw-createaccount' . ( !$isLoggedIn ? '-cta' : '' ),
1164 'class' => ( $isLoggedIn ? 'mw-form-related-link-container' : 'mw-ui-vform-field' ) ],
1165 ( $isLoggedIn ? '' : $this->msg( 'userlogin-noaccount' )->escaped() )
1166 . Html::element( 'a',
1167 [
1168 'id' => 'mw-createaccount-join' . ( $isLoggedIn ? '-loggedin' : '' ),
1169 'href' => $linkTitle->getLocalURL( $params['linkQuery'] ),
1170 'class' => $isLoggedIn ? '' : $buttonClasses,
1171 'tabindex' => 100,
1172 ],
1173 $this->msg(
1174 $isLoggedIn ? 'userlogin-createanother' : 'userlogin-joinproject'
1175 )->text()
1176 )
1177 );
1178 },
1179 'weight' => 235,
1180 ];
1181 }
1182 }
1183
1184 return $fieldDefinitions;
1185 }
1186
1196 protected function hasSessionCookie() {
1197 $config = $this->getConfig();
1198 return $config->get( 'InitialSessionId' ) &&
1199 $this->getRequest()->getSession()->getId() === (string)$config->get( 'InitialSessionId' );
1200 }
1201
1207 protected function getReturnToQueryStringFragment() {
1208 $returnto = '';
1209 if ( $this->mReturnTo !== '' ) {
1210 $returnto = 'returnto=' . wfUrlencode( $this->mReturnTo );
1211 if ( $this->mReturnToQuery !== '' ) {
1212 $returnto .= '&returntoquery=' . wfUrlencode( $this->mReturnToQuery );
1213 }
1214 }
1215 return $returnto;
1216 }
1217
1223 private function showCreateAccountLink() {
1224 return $this->isSignup() ||
1225 $this->getContext()->getAuthority()->isAllowed( 'createaccount' );
1226 }
1227
1228 protected function getTokenName() {
1229 return $this->isSignup() ? 'wpCreateaccountToken' : 'wpLoginToken';
1230 }
1231
1238 protected function makeLanguageSelector() {
1239 $msg = $this->msg( 'loginlanguagelinks' )->inContentLanguage();
1240 if ( $msg->isBlank() ) {
1241 return '';
1242 }
1243 $langs = explode( "\n", $msg->text() );
1244 $links = [];
1245 foreach ( $langs as $lang ) {
1246 $lang = trim( $lang, '* ' );
1247 $parts = explode( '|', $lang );
1248 if ( count( $parts ) >= 2 ) {
1249 $links[] = $this->makeLanguageSelectorLink( $parts[0], trim( $parts[1] ) );
1250 }
1251 }
1252
1253 return count( $links ) > 0 ? $this->msg( 'loginlanguagelabel' )->rawParams(
1254 $this->getLanguage()->pipeList( $links ) )->escaped() : '';
1255 }
1256
1265 protected function makeLanguageSelectorLink( $text, $lang ) {
1266 if ( $this->getLanguage()->getCode() == $lang ) {
1267 // no link for currently used language
1268 return htmlspecialchars( $text );
1269 }
1270 $query = [ 'uselang' => $lang ];
1271 if ( $this->mVariant ) {
1272 $query['variant'] = $this->mVariant;
1273 }
1274 if ( $this->mReturnTo !== '' ) {
1275 $query['returnto'] = $this->mReturnTo;
1276 $query['returntoquery'] = $this->mReturnToQuery;
1277 }
1278
1279 $attr = [];
1280 $targetLanguage = MediaWikiServices::getInstance()->getLanguageFactory()
1281 ->getLanguage( $lang );
1282 $attr['lang'] = $attr['hreflang'] = $targetLanguage->getHtmlCode();
1283
1284 return $this->getLinkRenderer()->makeKnownLink(
1285 $this->getPageTitle(),
1286 $text,
1287 $attr,
1288 $query
1289 );
1290 }
1291
1292 protected function getGroupName() {
1293 return 'login';
1294 }
1295
1300 protected function postProcessFormDescriptor( &$formDescriptor, $requests ) {
1301 // Pre-fill username (if not creating an account, T46775).
1302 if (
1303 isset( $formDescriptor['username'] ) &&
1304 !isset( $formDescriptor['username']['default'] ) &&
1305 !$this->isSignup()
1306 ) {
1307 $user = $this->getUser();
1308 if ( $user->isRegistered() && !$user->isTemp() ) {
1309 $formDescriptor['username']['default'] = $user->getName();
1310 } else {
1311 $formDescriptor['username']['default'] =
1312 $this->getRequest()->getSession()->suggestLoginUsername();
1313 }
1314 }
1315
1316 // don't show a submit button if there is nothing to submit (i.e. the only form content
1317 // is other submit buttons, for redirect flows)
1318 if ( !$this->needsSubmitButton( $requests ) ) {
1319 unset( $formDescriptor['createaccount'], $formDescriptor['loginattempt'] );
1320 }
1321
1322 if ( !$this->isSignup() ) {
1323 // FIXME HACK don't focus on non-empty field
1324 // maybe there should be an autofocus-if similar to hide-if?
1325 if (
1326 isset( $formDescriptor['username'] )
1327 && empty( $formDescriptor['username']['default'] )
1328 && !$this->getRequest()->getCheck( 'wpName' )
1329 ) {
1330 $formDescriptor['username']['autofocus'] = true;
1331 } elseif ( isset( $formDescriptor['password'] ) ) {
1332 $formDescriptor['password']['autofocus'] = true;
1333 }
1334 }
1335
1336 $this->addTabIndex( $formDescriptor );
1337 }
1338
1344 protected function getNoticeHtml() {
1345 $noticeContent = $this->msg( 'createacct-temp-warning', $this->getUser()->getName() )->parse();
1346 return Html::noticeBox(
1347 $noticeContent,
1348 '',
1349 '',
1350 'mw-userLogin-icon--user-temporary'
1351 );
1352 }
1353
1354}
1355
1357class_alias( LoginSignupSpecialPage::class, 'LoginSignupSpecialPage' );
getUser()
getRequest()
const PROTO_HTTPS
Definition Defines.php:203
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
wfEscapeWikiText( $input)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
getContext()
if(!defined( 'MW_NO_SESSION') &&MW_ENTRY_POINT !=='cli' $wgLang
Definition Setup.php:536
array $params
The job parameters.
An error page which can definitely be safely rendered using the OutputPage.
Abort the web request with a custom HTML string that will represent the entire response.
Helper functions for the login form that need to be shared with other special pages (such as CentralA...
static getValidErrorMessages()
Returns an array of all valid error messages.
This serves as the entry point to the authentication system.
This is a value object for authentication requests.
This is a value object to hold authentication response data.
This is a value object for authentication requests with a username and password.
An IContextSource implementation which will inherit context from another source but allow individual ...
Group all the pieces relevant to the context of a request into one instance.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:206
This class is a collection of static functions that serve two purposes:
Definition Html.php:56
Variant of the Message class.
Create PSR-3 logger objects.
A class containing constants representing the names of configuration variables.
const LoginLanguageSelector
Name constant for the LoginLanguageSelector setting, for use with Config::get()
const ForceHTTPS
Name constant for the ForceHTTPS setting, for use with Config::get()
const EmailConfirmToEdit
Name constant for the EmailConfirmToEdit setting, for use with Config::get()
const SecureLogin
Name constant for the SecureLogin setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:158
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:46
This serves as the entry point to the MediaWiki session handling system.
A special page subclass for authentication-related special pages.
getContinueAction( $action)
Gets the _CONTINUE version of an action.
isContinued()
Returns true if this is not the first step of the authentication.
isActionAllowed( $action)
Checks whether AuthManager is ready to perform the action.
trySubmit()
Attempts to do an authentication step with the submitted data.
loadAuth( $subPage, $authAction=null, $reset=false)
Load or initialize $authAction, $authRequests and $subPage.
getDefaultAction( $subPage)
Get the default action for this special page if none is given via URL/POST data.
getRequest()
Get the WebRequest being used for this instance.
Holds shared logic for login and account creation pages.
setRequest(array $data, $wasPosted=null)
Override the POST data, GET data from the real request is preserved.
logAuthResult( $success, $status=null)
Logs to the authmanager-stats channel.
makeLanguageSelectorLink( $text, $lang)
Create a language selector link for a particular language Links back to this page preserving type and...
getReturnToQueryStringFragment()
Returns a string that can be appended to the URL (without encoding) to preserve the return target.
showReturnToPage( $type, $returnTo='', $returnToQuery='', $stickHTTPS=false)
Add a "return to" link or redirect to it.
showSuccessPage( $type, $title, $msgname, $injected_html, $extraMessages)
Show the success page.
getPreservedParams( $withToken=false)
Returns URL query parameters which can be used to reload the page (or leave and return) while preserv...
setSessionUserForCurrentRequest()
Replace some globals to make sure the fact that the user has just been logged in is reflected in the ...
getAuthForm(array $requests, $action)
Generates a form from the given request.
getTokenName()
Returns the name of the CSRF token (under which it should be found in the POST or GET data).
getBenefitsContainerHtml()
The HTML to be shown in the "benefits to signing in / creating an account" section of the signup/logi...
mainLoginForm(array $requests, $msg='', $msgtype='error')
bool $proxyAccountCreation
True if the user if creating an account for someone else.
showExtraInformation()
Show extra information such as password recovery information, link from login to signup,...
onAuthChangeFormFields(array $requests, array $fieldInfo, array &$formDescriptor, $action)
Change the form descriptor that determines how a field will look in the authentication form....
makeLanguageSelector()
Produce a bar of links which allow the user to select another language during login/registration but ...
getFieldDefinitions(array $fieldInfo)
Create a HTMLForm descriptor for the core login fields.
successfulAction( $direct=false, $extraMessages=null)
getNoticeHtml()
Generates the HTML for a notice box to be displayed to a temporary user.
hasSessionCookie()
Check if a session cookie is present.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
getPageHtml( $formHtml)
Add page elements which are outside the form.
User $targetUser
FIXME another flag for passing data.
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.
getConfig()
Shortcut to get main config object.
getContext()
Gets the context this SpecialPage is executed in.
setContext( $context)
Sets the context this SpecialPage is executed in.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
getName()
Get the canonical, unlocalized name of this special page without namespace.
getFullTitle()
Return the full title, including $par.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
Stub object for the global user ($wgUser) that makes it possible to change the relevant underlying ob...
Represents a title within MediaWiki.
Definition Title.php:78
internal since 1.36
Definition User.php:93
Show an error when a user tries to do something they do not have the necessary permissions for.
Show an error when the wiki is locked/read-only and the user tries to do something that requires writ...
The base class for all skins.
Definition Skin.php:58
static makeInternalOrExternalUrl( $name)
If url string starts with http, consider as external URL, else internal.
Definition Skin.php:1176
Generic operation result class Has warning/error list, boolean status and arbitrary value.
element(SerializerNode $parent, SerializerNode $node, $contents)