MediaWiki master
LoginSignupSpecialPage.php
Go to the documentation of this file.
1<?php
24namespace MediaWiki\SpecialPage;
25
26use Exception;
27use LogicException;
28use LoginHelper;
54use StatusValue;
55use Wikimedia\ScopedCallback;
56
64
72 protected string $mReturnTo;
78 protected string $mReturnToQuery;
85 protected string $mReturnToAnchor;
86
88 protected $mPosted;
90 protected $mAction;
92 protected $mToken;
94 protected $mStickHTTPS;
96 protected $mFromHTTP;
98 protected $mEntryError = '';
100 protected $mEntryErrorType = 'error';
102 protected $mDisplay = 'page';
103
105 protected $mLoaded = false;
107 protected $mLoadedRequest = false;
111 private $reasonValidatorResult = null;
112
114 protected $securityLevel;
115
121 protected $targetUser;
122
124 protected $authForm;
125
126 abstract protected function isSignup();
127
134 abstract protected function successfulAction( $direct = false, $extraMessages = null );
135
142 abstract protected function logAuthResult( $success, UserIdentity $performer, $status = null );
143
144 protected function setRequest( array $data, $wasPosted = null ) {
145 parent::setRequest( $data, $wasPosted );
146 $this->mLoadedRequest = false;
147 }
148
152 private function loadRequestParameters() {
153 if ( $this->mLoadedRequest ) {
154 return;
155 }
156 $this->mLoadedRequest = true;
157 $request = $this->getRequest();
158
159 $this->mPosted = $request->wasPosted();
160 $this->mAction = $request->getRawVal( 'action' );
161 $this->mFromHTTP = $request->getBool( 'fromhttp', false )
162 || $request->getBool( 'wpFromhttp', false );
163 $this->mStickHTTPS = $this->getConfig()->get( MainConfigNames::ForceHTTPS )
164 || ( !$this->mFromHTTP && $request->getProtocol() === 'https' )
165 || $request->getBool( 'wpForceHttps', false );
166 $this->mReturnTo = $request->getVal( 'returnto', '' );
167 $this->mReturnToQuery = $request->getVal( 'returntoquery', '' );
168 $this->mReturnToAnchor = $request->getVal( 'returntoanchor', '' );
169 if ( $request->getRawVal( 'display' ) === 'popup' ) {
170 $this->mDisplay = 'popup';
171 }
172 }
173
179 protected function load( $subPage ) {
180 $this->loadRequestParameters();
181 if ( $this->mLoaded ) {
182 return;
183 }
184 $this->mLoaded = true;
185 $request = $this->getRequest();
186
187 $securityLevel = $this->getRequest()->getText( 'force' );
188 if (
189 $securityLevel &&
190 MediaWikiServices::getInstance()->getAuthManager()->securitySensitiveOperationStatus(
191 $securityLevel ) === AuthManager::SEC_REAUTH
192 ) {
193 $this->securityLevel = $securityLevel;
194 }
195
196 $this->loadAuth( $subPage );
197
198 $this->mToken = $request->getVal( $this->getTokenName() );
199
200 // Show an error or warning or a notice passed on from a previous page
201 $entryError = $this->msg( $request->getVal( 'error', '' ) );
202 $entryWarning = $this->msg( $request->getVal( 'warning', '' ) );
203 $entryNotice = $this->msg( $request->getVal( 'notice', '' ) );
204 // bc: provide login link as a parameter for messages where the translation
205 // was not updated
206 $loginreqlink = $this->getLinkRenderer()->makeKnownLink(
207 $this->getPageTitle(),
208 $this->msg( 'loginreqlink' )->text(),
209 [],
210 $this->getPreservedParams( [ 'reset' => true ] )
211 );
212
213 // Only show valid error or warning messages.
214 $validErrorMessages = LoginHelper::getValidErrorMessages();
215 if ( $entryError->exists()
216 && in_array( $entryError->getKey(), $validErrorMessages, true )
217 ) {
218 $this->mEntryErrorType = 'error';
219 $this->mEntryError = $entryError->rawParams( $loginreqlink )->parse();
220
221 } elseif ( $entryWarning->exists()
222 && in_array( $entryWarning->getKey(), $validErrorMessages, true )
223 ) {
224 $this->mEntryErrorType = 'warning';
225 $this->mEntryError = $entryWarning->rawParams( $loginreqlink )->parse();
226 } elseif ( $entryNotice->exists()
227 && in_array( $entryNotice->getKey(), $validErrorMessages, true )
228 ) {
229 $this->mEntryErrorType = 'notice';
230 $this->mEntryError = $entryNotice->parse();
231 }
232
233 # 1. When switching accounts, it sucks to get automatically logged out
234 # 2. Do not return to PasswordReset after a successful password change
235 # but goto Wiki start page (Main_Page) instead ( T35997 )
236 $returnToTitle = Title::newFromText( $this->mReturnTo );
237 if ( is_object( $returnToTitle )
238 && ( $returnToTitle->isSpecial( 'Userlogout' )
239 || $returnToTitle->isSpecial( 'PasswordReset' ) )
240 ) {
241 $this->mReturnTo = '';
242 $this->mReturnToQuery = '';
243 }
244 }
245
247 protected function getPreservedParams( $options = [] ) {
248 $params = parent::getPreservedParams( $options );
249
250 // Override returnto* with their property-based values, to account for the
251 // special-casing in load().
252 $this->loadRequestParameters();
253 $properties = [
254 'returnto' => 'mReturnTo',
255 'returntoquery' => 'mReturnToQuery',
256 'returntoanchor' => 'mReturnToAnchor',
257 ];
258 foreach ( $properties as $key => $prop ) {
259 $value = $this->$prop;
260 if ( $value !== '' ) {
261 $params[$key] = $value;
262 } else {
263 unset( $params[$key] );
264 }
265 }
266
267 if ( $this->getConfig()->get( MainConfigNames::SecureLogin ) && !$this->isSignup() ) {
268 $params['fromhttp'] = $this->mFromHTTP ? '1' : null;
269 }
270 if ( $this->mDisplay !== 'page' ) {
271 $params['display'] = $this->mDisplay;
272 }
273
274 return array_filter( $params, static fn ( $val ) => $val !== null );
275 }
276
277 protected function beforeExecute( $subPage ) {
278 // finish initializing the class before processing the request - T135924
279 $this->loadRequestParameters();
280 return parent::beforeExecute( $subPage );
281 }
282
286 public function execute( $subPage ) {
287 if ( $this->mPosted ) {
288 $time = microtime( true );
289 $profilingScope = new ScopedCallback( function () use ( $time ) {
290 $time = microtime( true ) - $time;
291 $stats = MediaWikiServices::getInstance()->getStatsFactory();
292 $stats->getTiming( 'auth_specialpage_executeTiming_seconds' )
293 ->setLabel( 'action', $this->authAction )
294 ->copyToStatsdAt( "timing.login.ui.{$this->authAction}" )
295 ->observe( $time * 1000 );
296 } );
297 }
298
299 $authManager = MediaWikiServices::getInstance()->getAuthManager();
300 $session = SessionManager::getGlobalSession();
301
302 // Before persisting, set the login token to avoid double writes
303 $this->getToken();
304
305 // Session data is used for various things in the authentication process, so we must make
306 // sure a session cookie or some equivalent mechanism is set.
307 $session->persist();
308 // Explicitly disable cache to ensure cookie blocks may be set (T152462).
309 // (Technically redundant with sessions persisting from this page.)
310 $this->getOutput()->disableClientCache();
311
312 $this->load( $subPage );
313
314 // Do this early, so that it affects how error pages are rendered too
315 if ( $this->mDisplay === 'popup' ) {
316 // Replace the default skin with a "micro-skin" that omits most of the interface. (T362706)
317 // In the future, we might allow normal skins to serve this mode too, if they advise that
318 // they support it by setting a skin option, so that colors and fonts could stay consistent.
319 $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
320 $this->getContext()->setSkin( $skinFactory->makeSkin( 'authentication-popup' ) );
321 }
322
323 $this->setHeaders();
324 $this->checkPermissions();
325
326 // Make sure the system configuration allows log in / sign up
327 if ( !$this->isSignup() && !$authManager->canAuthenticateNow() ) {
328 if ( !$session->canSetUser() ) {
329 throw new ErrorPageError( 'cannotloginnow-title', 'cannotloginnow-text', [
330 $session->getProvider()->describe( $this->getLanguage() )
331 ] );
332 }
333 throw new ErrorPageError( 'cannotlogin-title', 'cannotlogin-text' );
334 } elseif ( $this->isSignup() && !$authManager->canCreateAccounts() ) {
335 throw new ErrorPageError( 'cannotcreateaccount-title', 'cannotcreateaccount-text' );
336 }
337
338 /*
339 * In the case where the user is already logged in, and was redirected to
340 * the login form from a page that requires login, do not show the login
341 * page. The use case scenario for this is when a user opens a large number
342 * of tabs, is redirected to the login page on all of them, and then logs
343 * in on one, expecting all the others to work properly.
344 *
345 * However, do show the form if it was visited intentionally (no 'returnto'
346 * is present). People who often switch between several accounts have grown
347 * accustomed to this behavior.
348 *
349 * For temporary users, the form is always shown, since the UI presents
350 * temporary users as not logged in and offers to discard their temporary
351 * account by logging in.
352 *
353 * Also make an exception when force=<level> is set in the URL, which means the user must
354 * reauthenticate for security reasons.
355 */
356 if ( !$this->isSignup() && !$this->mPosted && !$this->securityLevel &&
357 ( $this->mReturnTo !== '' || $this->mReturnToQuery !== '' ) &&
358 !$this->getUser()->isTemp() && $this->getUser()->isRegistered()
359 ) {
360 $this->successfulAction();
361 return;
362 }
363
364 // If logging in and not on HTTPS, either redirect to it or offer a link.
365 if ( $this->getRequest()->getProtocol() !== 'https' ) {
366 $title = $this->getFullTitle();
367 $query = $this->getPreservedParams() + [
368 'title' => null,
369 ( $this->mEntryErrorType === 'error' ? 'error'
370 : 'warning' ) => $this->mEntryError,
371 ] + $this->getRequest()->getQueryValues();
372 $url = $title->getFullURL( $query, false, PROTO_HTTPS );
373 if ( $this->getConfig()->get( MainConfigNames::SecureLogin ) && !$this->mFromHTTP ) {
374 // Avoid infinite redirect
375 $url = wfAppendQuery( $url, 'fromhttp=1' );
376 $this->getOutput()->redirect( $url );
377 // Since we only do this redir to change proto, always vary
378 $this->getOutput()->addVaryHeader( 'X-Forwarded-Proto' );
379
380 return;
381 } else {
382 // A wiki without HTTPS login support should set $wgServer to
383 // http://somehost, in which case the secure URL generated
384 // above won't actually start with https://
385 if ( str_starts_with( $url, 'https://' ) ) {
386 $this->mSecureLoginUrl = $url;
387 }
388 }
389 }
390
391 if ( !$this->isActionAllowed( $this->authAction ) ) {
392 // FIXME how do we explain this to the user? can we handle session loss better?
393 // messages used: authpage-cannot-login, authpage-cannot-login-continue,
394 // authpage-cannot-create, authpage-cannot-create-continue
395 $this->mainLoginForm( [], 'authpage-cannot-' . $this->authAction );
396 return;
397 }
398
399 if ( $this->canBypassForm( $button_name ) ) {
400 $this->setRequest( [], true );
401 $this->getRequest()->setVal( $this->getTokenName(), $this->getToken() );
402 if ( $button_name ) {
403 $this->getRequest()->setVal( $button_name, true );
404 }
405 }
406 $performer = $this->getUser();
407 $status = $this->trySubmit();
408
409 if ( !$status || !$status->isGood() ) {
410 $this->mainLoginForm( $this->authRequests, $status ? $status->getMessage() : '', 'error' );
411 return;
412 }
413
415 $response = $status->getValue();
416
417 $returnToUrl = $this->getPageTitle( 'return' )
418 ->getFullURL( $this->getPreservedParams( [ 'withToken' => true ] ), false, PROTO_HTTPS );
419 switch ( $response->status ) {
420 case AuthenticationResponse::PASS:
421 $this->logAuthResult( true, $performer );
422 $this->proxyAccountCreation = $this->isSignup() && $this->getUser()->isNamed();
423 $this->targetUser = User::newFromName( $response->username );
424
425 if (
426 !$this->proxyAccountCreation
427 && $response->loginRequest
428 && $authManager->canAuthenticateNow()
429 ) {
430 // successful registration; log the user in instantly
431 $response2 = $authManager->beginAuthentication( [ $response->loginRequest ],
432 $returnToUrl );
433 if ( $response2->status !== AuthenticationResponse::PASS ) {
434 LoggerFactory::getInstance( 'login' )
435 ->error( 'Could not log in after account creation' );
436 $this->successfulAction( true, Status::newFatal( 'createacct-loginerror' ) );
437 break;
438 }
439 }
440
441 if ( !$this->proxyAccountCreation ) {
442 $context = RequestContext::getMain();
443 $localContext = $this->getContext();
444 if ( $context !== $localContext ) {
445 // remove AuthManagerSpecialPage context hack
446 $this->setContext( $context );
447 }
448 // Ensure that the context user is the same as the session user.
449 $this->getAuthManager()->setRequestContextUserFromSessionUser();
450 }
451
452 $this->successfulAction( true );
453 break;
454 case AuthenticationResponse::FAIL:
455 // fall through
456 case AuthenticationResponse::RESTART:
457 $this->authForm = null;
458 if ( $response->status === AuthenticationResponse::FAIL ) {
459 $action = $this->getDefaultAction( $subPage );
460 $messageType = 'error';
461 } else {
462 $action = $this->getContinueAction( $this->authAction );
463 $messageType = 'warning';
464 }
465 $this->logAuthResult( false, $performer, $response->message ? $response->message->getKey() : '-' );
466 $this->loadAuth( $subPage, $action, true );
467 $this->mainLoginForm( $this->authRequests, $response->message, $messageType );
468 break;
469 case AuthenticationResponse::REDIRECT:
470 $this->authForm = null;
471 $this->getOutput()->redirect( $response->redirectTarget );
472 break;
473 case AuthenticationResponse::UI:
474 $this->authForm = null;
475 $this->authAction = $this->isSignup() ? AuthManager::ACTION_CREATE_CONTINUE
476 : AuthManager::ACTION_LOGIN_CONTINUE;
477 $this->authRequests = $response->neededRequests;
478 $this->mainLoginForm( $response->neededRequests, $response->message, $response->messageType );
479 break;
480 default:
481 throw new LogicException( 'invalid AuthenticationResponse' );
482 }
483 }
484
498 private function canBypassForm( &$button_name ) {
499 $button_name = null;
500 if ( $this->isContinued() ) {
501 return false;
502 }
503 $fields = AuthenticationRequest::mergeFieldInfo( $this->authRequests );
504 foreach ( $fields as $fieldname => $field ) {
505 if ( !isset( $field['type'] ) ) {
506 return false;
507 }
508 if ( !empty( $field['skippable'] ) ) {
509 continue;
510 }
511 if ( $field['type'] === 'button' ) {
512 if ( $button_name !== null ) {
513 $button_name = null;
514 return false;
515 } else {
516 $button_name = $fieldname;
517 }
518 } elseif ( $field['type'] !== 'null' ) {
519 return false;
520 }
521 }
522 return true;
523 }
524
534 protected function showSuccessPage(
535 $type, $title, $msgname, $injected_html, $extraMessages
536 ) {
537 $out = $this->getOutput();
538 if ( is_string( $title ) ) {
539 wfDeprecated( __METHOD__ . ' with string title', '1.41' ); // T343849
540 $title = ( new RawMessage( '$1' ) )->rawParams( $title );
541 }
542 $out->setPageTitleMsg( $title );
543 if ( $msgname ) {
544 $out->addWikiMsg( $msgname, wfEscapeWikiText( $this->getUser()->getName() ) );
545 }
546 if ( $extraMessages ) {
547 $extraMessages = Status::wrap( $extraMessages );
548 $out->addWikiTextAsInterface(
549 $extraMessages->getWikiText( false, false, $this->getLanguage() )
550 );
551 }
552
553 $out->addHTML( $injected_html );
554
555 $helper = new LoginHelper( $this->getContext() );
556 $helper->showReturnToPage( $type, $this->mReturnTo, $this->mReturnToQuery,
557 $this->mStickHTTPS, $this->mReturnToAnchor );
558 }
559
573 protected function mainLoginForm( array $requests, $msg = '', $msgtype = 'error' ) {
574 $user = $this->getUser();
575 $out = $this->getOutput();
576
577 // FIXME how to handle empty $requests - restart, or no form, just an error message?
578 // no form would be better for no session type errors, restart is better when can* fails.
579 if ( !$requests ) {
580 $this->authAction = $this->getDefaultAction( $this->subPage );
581 $this->authForm = null;
582 $requests = MediaWikiServices::getInstance()->getAuthManager()
583 ->getAuthenticationRequests( $this->authAction, $user );
584 }
585
586 // Generic styles and scripts for both login and signup form
587 $out->addModuleStyles( [
588 'mediawiki.special.userlogin.common.styles',
589 'mediawiki.codex.messagebox.styles'
590 ] );
591 if ( $this->isSignup() ) {
592 // Additional styles and scripts for signup form
593 $out->addModules( 'mediawiki.special.createaccount' );
594 $out->addModuleStyles( [
595 'mediawiki.special.userlogin.signup.styles'
596 ] );
597 } else {
598 // Additional styles for login form
599 $out->addModuleStyles( [
600 'mediawiki.special.userlogin.login.styles'
601 ] );
602 }
603 $out->disallowUserJs(); // just in case...
604
605 $form = $this->getAuthForm( $requests, $this->authAction );
606 $form->prepareForm();
607
608 $submitStatus = Status::newGood();
609 if ( $msg && $msgtype === 'warning' ) {
610 $submitStatus->warning( $msg );
611 } elseif ( $msg && $msgtype === 'error' ) {
612 $submitStatus->fatal( $msg );
613 }
614
615 // warning header for non-standard workflows (e.g. security reauthentication)
616 if (
617 !$this->isSignup() &&
618 $this->getUser()->isRegistered() &&
619 !$this->getUser()->isTemp() &&
620 $this->authAction !== AuthManager::ACTION_LOGIN_CONTINUE
621 ) {
622 $reauthMessage = $this->securityLevel ? 'userlogin-reauth' : 'userlogin-loggedin';
623 $submitStatus->warning( $reauthMessage, $this->getUser()->getName() );
624 }
625
626 $formHtml = $form->getHTML( $submitStatus );
627
628 $out->addHTML( $this->getPageHtml( $formHtml ) );
629 }
630
637 protected function getPageHtml( $formHtml ) {
638 $loginPrompt = $this->isSignup() ? '' : Html::rawElement( 'div',
639 [ 'id' => 'userloginprompt' ], $this->msg( 'loginprompt' )->parseAsBlock() );
640 $languageLinks = $this->getConfig()->get( MainConfigNames::LoginLanguageSelector )
641 ? $this->makeLanguageSelector() : '';
642 $signupStartMsg = $this->msg( 'signupstart' );
643 $signupStart = ( $this->isSignup() && !$signupStartMsg->isDisabled() )
644 ? Html::rawElement( 'div', [ 'id' => 'signupstart' ], $signupStartMsg->parseAsBlock() ) : '';
645 if ( $languageLinks ) {
646 $languageLinks = Html::rawElement( 'div', [ 'id' => 'languagelinks' ],
647 Html::rawElement( 'p', [], $languageLinks )
648 );
649 }
650 if ( $this->getUser()->isTemp() ) {
651 $noticeHtml = $this->getNoticeHtml();
652 } else {
653 $noticeHtml = '';
654 }
655 $formBlock = Html::rawElement( 'div', [ 'id' => 'userloginForm' ], $formHtml );
656 $formAndBenefits = $formBlock;
657 if ( $this->isSignup() && $this->showExtraInformation() ) {
658 $benefitsContainerHtml = null;
659 $info = [
660 'context' => $this->getContext(),
661 'form' => $this->authForm,
662 ];
663 $options = [
664 'beforeForm' => false,
665 ];
666 $this->getHookRunner()->onSpecialCreateAccountBenefits(
667 $benefitsContainerHtml, $info, $options
668 );
669 $benefitsContainerHtml ??= $this->getBenefitsContainerHtml();
670 $formAndBenefits = $options['beforeForm']
671 ? ( $benefitsContainerHtml . $formBlock )
672 : ( $formBlock . $benefitsContainerHtml );
673 }
674
675 return $loginPrompt
676 . $languageLinks
677 . $signupStart
678 . $noticeHtml
679 . Html::rawElement( 'div', [ 'class' => 'mw-ui-container' ],
680 $formAndBenefits
681 );
682 }
683
691 protected function getBenefitsContainerHtml(): string {
692 $benefitsContainer = '';
693 $this->getOutput()->addModuleStyles( [ 'oojs-ui.styles.icons-user' ] );
694 if ( $this->isSignup() && $this->showExtraInformation() ) {
695 if ( !$this->getUser()->isTemp() ) {
696 // The following messages are used here:
697 // * createacct-benefit-icon1 createacct-benefit-head1 createacct-benefit-body1
698 // * createacct-benefit-icon2 createacct-benefit-head2 createacct-benefit-body2
699 // * createacct-benefit-icon3 createacct-benefit-head3 createacct-benefit-body3
700 $benefitCount = 3;
701 $benefitList = '';
702 for ( $benefitIdx = 1; $benefitIdx <= $benefitCount; $benefitIdx++ ) {
703 $headUnescaped = $this->msg( "createacct-benefit-head$benefitIdx" )->text();
704 $iconClass = $this->msg( "createacct-benefit-icon$benefitIdx" )->text();
705 $benefitList .= Html::rawElement( 'div', [ 'class' => "mw-number-text $iconClass" ],
706 Html::rawElement( 'span', [],
707 $this->msg( "createacct-benefit-head$benefitIdx" )->escaped()
708 )
709 . Html::rawElement( 'p', [],
710 $this->msg( "createacct-benefit-body$benefitIdx" )->params( $headUnescaped )->escaped()
711 )
712 );
713 }
714 $benefitsContainer = Html::rawElement( 'div', [ 'class' => 'mw-createacct-benefits-container' ],
715 Html::rawElement( 'div', [ 'class' => 'mw-createacct-benefits-heading' ],
716 $this->msg( 'createacct-benefit-heading' )->escaped()
717 )
718 . Html::rawElement( 'div', [ 'class' => 'mw-createacct-benefits-list' ], $benefitList )
719 );
720 } else {
721 $benefitList = '';
722 $this->getOutput()->addModuleStyles(
723 [
724 'oojs-ui.styles.icons-moderation',
725 'oojs-ui.styles.icons-interactions',
726 ]
727 );
728 $benefits = [
729 [
730 'icon' => 'oo-ui-icon-unStar',
731 'description' => $this->msg( "benefit-1-description" )->escaped()
732 ],
733 [
734 'icon' => 'oo-ui-icon-userContributions',
735 'description' => $this->msg( "benefit-2-description" )->escaped()
736 ],
737 [
738 'icon' => 'oo-ui-icon-settings',
739 'description' => $this->msg( "benefit-3-description" )->escaped()
740 ]
741 ];
742 foreach ( $benefits as $benefit ) {
743 $benefitContent = Html::rawElement( 'div', [ 'class' => 'mw-benefit-item' ],
744 Html::rawElement( 'span', [ 'class' => $benefit[ 'icon' ] ] )
745 . Html::rawElement( 'p', [], $benefit['description'] )
746 );
747
748 $benefitList .= Html::rawElement(
749 'div', [ 'class' => 'mw-benefit-item-wrapper' ], $benefitContent );
750 }
751
752 $benefitsListWrapper = Html::rawElement(
753 'div', [ 'class' => 'mw-benefit-list-wrapper' ], $benefitList );
754
755 $headingSubheadingWrapper = Html::rawElement( 'div', [ 'class' => 'mw-heading-subheading-wrapper' ],
756 Html::rawElement( 'h2', [], $this->msg( 'createacct-benefit-heading-temp-user' )->escaped() )
757 . Html::rawElement( 'p', [ 'class' => 'mw-benefit-subheading' ], $this->msg(
758 'createacct-benefit-subheading-temp-user' )->escaped() )
759 );
760
761 $benefitsContainer = Html::rawElement(
762 'div', [ 'class' => 'mw-createacct-benefits-container' ],
763 $headingSubheadingWrapper
764 . $benefitsListWrapper
765 );
766 }
767 }
768 return $benefitsContainer;
769 }
770
777 protected function getAuthForm( array $requests, $action ) {
778 // FIXME merge this with parent
779
780 if ( $this->authForm ) {
781 return $this->authForm;
782 }
783
784 $usingHTTPS = $this->getRequest()->getProtocol() === 'https';
785
786 // get basic form description from the auth logic
787 $fieldInfo = AuthenticationRequest::mergeFieldInfo( $requests );
788 // this will call onAuthChangeFormFields()
789 $formDescriptor = $this->fieldInfoToFormDescriptor( $requests, $fieldInfo, $this->authAction );
790 $this->postProcessFormDescriptor( $formDescriptor, $requests );
791
792 $context = $this->getContext();
793 if ( $context->getRequest() !== $this->getRequest() ) {
794 // We have overridden the request, need to make sure the form uses that too.
795 $context = new DerivativeContext( $this->getContext() );
796 $context->setRequest( $this->getRequest() );
797 }
798 $form = HTMLForm::factory( 'codex', $formDescriptor, $context );
799
800 $form->addHiddenField( 'authAction', $this->authAction );
801 $form->addHiddenField( 'force', $this->securityLevel );
802 $form->addHiddenField( $this->getTokenName(), $this->getToken()->toString() );
803 $config = $this->getConfig();
804 if ( $config->get( MainConfigNames::SecureLogin ) &&
805 !$config->get( MainConfigNames::ForceHTTPS ) ) {
806 // If using HTTPS coming from HTTP, then the 'fromhttp' parameter must be preserved
807 if ( !$this->isSignup() ) {
808 $form->addHiddenField( 'wpForceHttps', (int)$this->mStickHTTPS );
809 $form->addHiddenField( 'wpFromhttp', $usingHTTPS );
810 }
811 }
812
813 $form->setAction( $this->getPageTitle()->getLocalURL( $this->getPreservedParams(
814 // We have manually set authAction above, so we don't need it in the action URL.
815 [ 'reset' => true ]
816 ) ) );
817 $form->setName( 'userlogin' . ( $this->isSignup() ? '2' : '' ) );
818 if ( $this->isSignup() ) {
819 $form->setId( 'userlogin2' );
820 }
821
822 $form->suppressDefaultSubmit();
823
824 $this->authForm = $form;
825
826 return $form;
827 }
828
830 public function onAuthChangeFormFields(
831 array $requests, array $fieldInfo, array &$formDescriptor, $action
832 ) {
833 $formDescriptor = self::mergeDefaultFormDescriptor( $fieldInfo, $formDescriptor,
834 $this->getFieldDefinitions( $fieldInfo, $requests ) );
835 }
836
843 protected function showExtraInformation() {
844 return $this->authAction !== $this->getContinueAction( $this->authAction )
845 && !$this->securityLevel;
846 }
847
856 protected function getFieldDefinitions( array $fieldInfo, array $requests ) {
857 $isLoggedIn = $this->getUser()->isRegistered();
858 $continuePart = $this->isContinued() ? 'continue-' : '';
859 $anotherPart = $isLoggedIn ? 'another-' : '';
860 // @phan-suppress-next-line PhanUndeclaredMethod
861 $expiration = $this->getRequest()->getSession()->getProvider()->getRememberUserDuration();
862 $expirationDays = ceil( $expiration / ( 3600 * 24 ) );
863 $secureLoginLink = '';
864 if ( $this->mSecureLoginUrl ) {
865 $secureLoginLink = Html::rawElement( 'a', [
866 'href' => $this->mSecureLoginUrl,
867 'class' => 'mw-login-flush-right mw-secure',
868 ], Html::element( 'span', [ 'class' => 'mw-secure--icon' ] ) .
869 $this->msg( 'userlogin-signwithsecure' )->parse() );
870 }
871 $usernameHelpLink = '';
872 if ( !$this->msg( 'createacct-helpusername' )->isDisabled() ) {
873 $usernameHelpLink = Html::rawElement( 'span', [
874 'class' => 'mw-login-flush-right',
875 ], $this->msg( 'createacct-helpusername' )->parse() );
876 }
877
878 if ( $this->isSignup() ) {
879 $config = $this->getConfig();
880 $hideIf = isset( $fieldInfo['mailpassword'] ) ? [ 'hide-if' => [ '===', 'mailpassword', '1' ] ] : [];
881 $fieldDefinitions = [
882 'username' => [
883 'label-raw' => $this->msg( 'userlogin-yourname' )->escaped() . $usernameHelpLink,
884 'id' => 'wpName2',
885 'placeholder-message' => $isLoggedIn ? 'createacct-another-username-ph'
886 : 'userlogin-yourname-ph',
887 ],
888 'mailpassword' => [
889 // create account without providing password, a temporary one will be mailed
890 'type' => 'check',
891 'label-message' => 'createaccountmail',
892 'name' => 'wpCreateaccountMail',
893 'id' => 'wpCreateaccountMail',
894 ],
895 'password' => [
896 'id' => 'wpPassword2',
897 'autocomplete' => 'new-password',
898 'placeholder-message' => 'createacct-yourpassword-ph',
899 'help-message' => 'createacct-useuniquepass',
900 ] + $hideIf,
901 'domain' => [],
902 'retype' => [
903 'type' => 'password',
904 'label-message' => 'createacct-yourpasswordagain',
905 'id' => 'wpRetype',
906 'cssclass' => 'loginPassword',
907 'size' => 20,
908 'autocomplete' => 'new-password',
909 'validation-callback' => function ( $value, $alldata ) {
910 if ( empty( $alldata['mailpassword'] ) && !empty( $alldata['password'] ) ) {
911 if ( !$value ) {
912 return $this->msg( 'htmlform-required' );
913 } elseif ( $value !== $alldata['password'] ) {
914 return $this->msg( 'badretype' );
915 }
916 }
917 return true;
918 },
919 'placeholder-message' => 'createacct-yourpasswordagain-ph',
920 ] + $hideIf,
921 'email' => [
922 'type' => 'email',
923 'label-message' => $config->get( MainConfigNames::EmailConfirmToEdit )
924 ? 'createacct-emailrequired' : 'createacct-emailoptional',
925 'id' => 'wpEmail',
926 'cssclass' => 'loginText',
927 'size' => '20',
928 'maxlength' => 255,
929 'autocomplete' => 'email',
930 // FIXME will break non-standard providers
931 'required' => $config->get( MainConfigNames::EmailConfirmToEdit ),
932 'validation-callback' => function ( $value, $alldata ) {
933 // AuthManager will check most of these, but that will make the auth
934 // session fail and this won't, so nicer to do it this way
935 if ( !$value &&
936 $this->getConfig()->get( MainConfigNames::EmailConfirmToEdit )
937 ) {
938 // no point in allowing registration without email when email is
939 // required to edit
940 return $this->msg( 'noemailtitle' );
941 } elseif ( !$value && !empty( $alldata['mailpassword'] ) ) {
942 // cannot send password via email when there is no email address
943 return $this->msg( 'noemailcreate' );
944 } elseif ( $value && !Sanitizer::validateEmail( $value ) ) {
945 return $this->msg( 'invalidemailaddress' );
946 } elseif ( is_string( $value ) && strlen( $value ) > 255 ) {
947 return $this->msg( 'changeemail-maxlength' );
948 }
949 return true;
950 },
951 // The following messages are used here:
952 // * createacct-email-ph
953 // * createacct-another-email-ph
954 'placeholder-message' => 'createacct-' . $anotherPart . 'email-ph',
955 ],
956 'realname' => [
957 'type' => 'text',
958 'help-message' => $isLoggedIn ? 'createacct-another-realname-tip'
959 : 'prefs-help-realname',
960 'label-message' => 'createacct-realname',
961 'cssclass' => 'loginText',
962 'size' => 20,
963 'placeholder-message' => 'createacct-realname',
964 'id' => 'wpRealName',
965 'autocomplete' => 'name',
966 ],
967 'reason' => [
968 // comment for the user creation log
969 'type' => 'text',
970 'label-message' => 'createacct-reason',
971 'cssclass' => 'loginText',
972 'id' => 'wpReason',
973 'size' => '20',
974 'validation-callback' => function ( $value, $alldata ) {
975 // if the user sets an email address as the user creation reason, confirm that
976 // that was their intent
977 if ( $value && Sanitizer::validateEmail( $value ) ) {
978 if ( $this->reasonValidatorResult !== null ) {
979 return $this->reasonValidatorResult;
980 }
981 $this->reasonValidatorResult = true;
982 $authManager = MediaWikiServices::getInstance()->getAuthManager();
983 if ( !$authManager->getAuthenticationSessionData( 'reason-retry', false ) ) {
984 $authManager->setAuthenticationSessionData( 'reason-retry', true );
985 $this->reasonValidatorResult = $this->msg( 'createacct-reason-confirm' );
986 }
987 return $this->reasonValidatorResult;
988 }
989 return true;
990 },
991 'placeholder-message' => 'createacct-reason-ph',
992 ],
993 'createaccount' => [
994 // submit button
995 'type' => 'submit',
996 // The following messages are used here:
997 // * createacct-submit
998 // * createacct-another-submit
999 // * createacct-continue-submit
1000 // * createacct-another-continue-submit
1001 'default' => $this->msg( 'createacct-' . $anotherPart . $continuePart .
1002 'submit' )->text(),
1003 'name' => 'wpCreateaccount',
1004 'id' => 'wpCreateaccount',
1005 'weight' => 100,
1006 ],
1007 ];
1008 if ( !$this->msg( 'createacct-username-help' )->isDisabled() ) {
1009 $fieldDefinitions['username']['help-message'] = 'createacct-username-help';
1010 }
1011 } else {
1012 // When the user's password is too weak, they might be asked to provide a stronger one
1013 // as a followup step. That is a form with only two fields, 'password' and 'retype',
1014 // and they should behave more like account creation.
1015 $passwordRequest = AuthenticationRequest::getRequestByClass( $this->authRequests,
1016 PasswordAuthenticationRequest::class );
1017 $changePassword = $passwordRequest && $passwordRequest->action == AuthManager::ACTION_CHANGE;
1018 $fieldDefinitions = [
1019 'username' => (
1020 [
1021 'label-raw' => $this->msg( 'userlogin-yourname' )->escaped() . $secureLoginLink,
1022 'id' => 'wpName1',
1023 'placeholder-message' => 'userlogin-yourname-ph',
1024 ] + ( $changePassword ? [
1025 // There is no username field on the AuthManager level when changing
1026 // passwords. Fake one because password
1027 'baseField' => 'password',
1028 'nodata' => true,
1029 'readonly' => true,
1030 'cssclass' => 'mw-htmlform-hidden-field',
1031 ] : [] )
1032 ),
1033 'password' => (
1034 $changePassword ? [
1035 'autocomplete' => 'new-password',
1036 'placeholder-message' => 'createacct-yourpassword-ph',
1037 'help-message' => 'createacct-useuniquepass',
1038 ] : [
1039 'id' => 'wpPassword1',
1040 'autocomplete' => 'current-password',
1041 'placeholder-message' => 'userlogin-yourpassword-ph',
1042 ]
1043 ),
1044 'retype' => [
1045 'type' => 'password',
1046 'autocomplete' => 'new-password',
1047 'placeholder-message' => 'createacct-yourpasswordagain-ph',
1048 ],
1049 'domain' => [],
1050 'rememberMe' => [
1051 // option for saving the user token to a cookie
1052 'type' => 'check',
1053 'cssclass' => 'mw-userlogin-rememberme',
1054 'name' => 'wpRemember',
1055 'label-message' => $this->msg( 'userlogin-remembermypassword' )
1056 ->numParams( $expirationDays ),
1057 'id' => 'wpRemember',
1058 ],
1059 'loginattempt' => [
1060 // submit button
1061 'type' => 'submit',
1062 // The following messages are used here:
1063 // * pt-login-button
1064 // * pt-login-continue-button
1065 'default' => $this->msg( 'pt-login-' . $continuePart . 'button' )->text(),
1066 'id' => 'wpLoginAttempt',
1067 'weight' => 100,
1068 ],
1069 'linkcontainer' => [
1070 // help link
1071 'type' => 'info',
1072 'cssclass' => 'mw-form-related-link-container mw-userlogin-help',
1073 // 'id' => 'mw-userlogin-help', // FIXME HTMLInfoField ignores this
1074 'raw' => true,
1075 'default' => Html::element( 'a', [
1076 'href' => Skin::makeInternalOrExternalUrl( $this->msg( 'helplogin-url' )
1077 ->inContentLanguage()
1078 ->text() ),
1079 ], $this->msg( 'userlogin-helplink2' )->text() ),
1080 'weight' => 200,
1081 ],
1082 // button for ResetPasswordSecondaryAuthenticationProvider
1083 'skipReset' => [
1084 'weight' => 110,
1085 'flags' => [],
1086 ],
1087 ];
1088 }
1089
1090 // T369641: We want to ensure that this transformation to the username and/or
1091 // password fields are applied only when we have matching requests within the
1092 // authentication manager.
1093 $isUsernameOrPasswordRequest =
1094 AuthenticationRequest::getRequestByClass( $requests, UsernameAuthenticationRequest::class ) ||
1095 AuthenticationRequest::getRequestByClass( $requests, PasswordAuthenticationRequest::class );
1096
1097 if ( $isUsernameOrPasswordRequest ) {
1098 $fieldDefinitions['username'] += [
1099 'type' => 'text',
1100 'name' => 'wpName',
1101 'cssclass' => 'loginText mw-userlogin-username',
1102 'size' => 20,
1103 'autocomplete' => 'username',
1104 // 'required' => true,
1105 ];
1106 $fieldDefinitions['password'] += [
1107 'type' => 'password',
1108 // 'label-message' => 'userlogin-yourpassword', // would override the changepassword label
1109 'name' => 'wpPassword',
1110 'cssclass' => 'loginPassword mw-userlogin-password',
1111 'size' => 20,
1112 // 'required' => true,
1113 ];
1114 }
1115
1116 if ( $this->mEntryError ) {
1117 $defaultHtml = '';
1118 if ( $this->mEntryErrorType === 'error' ) {
1119 $defaultHtml = Html::errorBox( $this->mEntryError );
1120 } elseif ( $this->mEntryErrorType === 'warning' ) {
1121 $defaultHtml = Html::warningBox( $this->mEntryError );
1122 } elseif ( $this->mEntryErrorType === 'notice' ) {
1123 $defaultHtml = Html::noticeBox( $this->mEntryError, '' );
1124 }
1125 $fieldDefinitions['entryError'] = [
1126 'type' => 'info',
1127 'default' => $defaultHtml,
1128 'raw' => true,
1129 'rawrow' => true,
1130 'weight' => -100,
1131 ];
1132 }
1133 if ( !$this->showExtraInformation() ) {
1134 unset( $fieldDefinitions['linkcontainer'], $fieldDefinitions['signupend'] );
1135 }
1136 if ( $this->isSignup() && $this->showExtraInformation() ) {
1137 // blank signup footer for site customization
1138 // uses signupend-https for HTTPS requests if it's not blank, signupend otherwise
1139 $signupendMsg = $this->msg( 'signupend' );
1140 $signupendHttpsMsg = $this->msg( 'signupend-https' );
1141 if ( !$signupendMsg->isDisabled() ) {
1142 $usingHTTPS = $this->getRequest()->getProtocol() === 'https';
1143 $signupendText = ( $usingHTTPS && !$signupendHttpsMsg->isBlank() )
1144 ? $signupendHttpsMsg->parse() : $signupendMsg->parse();
1145 $fieldDefinitions['signupend'] = [
1146 'type' => 'info',
1147 'raw' => true,
1148 'default' => Html::rawElement( 'div', [ 'id' => 'signupend' ], $signupendText ),
1149 'weight' => 225,
1150 ];
1151 }
1152 }
1153 if ( !$this->isSignup() && $this->showExtraInformation() ) {
1154 $passwordReset = MediaWikiServices::getInstance()->getPasswordReset();
1155 if ( $passwordReset->isAllowed( $this->getUser() )->isGood() ) {
1156 $fieldDefinitions['passwordReset'] = [
1157 'type' => 'info',
1158 'raw' => true,
1159 'cssclass' => 'mw-form-related-link-container',
1160 'default' => $this->getLinkRenderer()->makeLink(
1161 SpecialPage::getTitleFor( 'PasswordReset' ),
1162 $this->msg( 'userlogin-resetpassword-link' )->text()
1163 ),
1164 'weight' => 230,
1165 ];
1166 }
1167
1168 // Don't show a "create account" link if the user can't.
1169 if ( $this->showCreateAccountLink() ) {
1170 // link to the other action
1171 $linkTitle = $this->getTitleFor( $this->isSignup() ? 'Userlogin' : 'CreateAccount' );
1172 $linkq = wfArrayToCgi( $this->getPreservedParams( [ 'reset' => true ] ) );
1173 $isLoggedIn = $this->getUser()->isRegistered()
1174 && !$this->getUser()->isTemp();
1175
1176 $fieldDefinitions['createOrLogin'] = [
1177 'type' => 'info',
1178 'raw' => true,
1179 'linkQuery' => $linkq,
1180 'default' => function ( $params ) use ( $isLoggedIn, $linkTitle ) {
1181 $buttonClasses = 'cdx-button cdx-button--action-progressive '
1182 . 'cdx-button--fake-button cdx-button--fake-button--enabled';
1183
1184 return Html::rawElement( 'div',
1185 // The following element IDs are used here:
1186 // mw-createaccount, mw-createaccount-cta
1187 [ 'id' => 'mw-createaccount' . ( !$isLoggedIn ? '-cta' : '' ),
1188 'class' => ( $isLoggedIn ? 'mw-form-related-link-container' : 'mw-ui-vform-field' ) ],
1189 ( $isLoggedIn ? '' : $this->msg( 'userlogin-noaccount' )->escaped() )
1190 . Html::element( 'a',
1191 [
1192 // The following element IDs are used here:
1193 // mw-createaccount-join, mw-createaccount-join-loggedin
1194 'id' => 'mw-createaccount-join' . ( $isLoggedIn ? '-loggedin' : '' ),
1195 'href' => $linkTitle->getLocalURL( $params['linkQuery'] ),
1196 'class' => [ 'mw-authentication-popup-link', $buttonClasses => !$isLoggedIn ],
1197 'target' => '_self',
1198 'tabindex' => 100,
1199 ],
1200 $this->msg(
1201 $isLoggedIn ? 'userlogin-createanother' : 'userlogin-joinproject'
1202 )->text()
1203 )
1204 );
1205 },
1206 'weight' => 235,
1207 ];
1208 }
1209 }
1210
1211 return $fieldDefinitions;
1212 }
1213
1223 protected function hasSessionCookie() {
1224 global $wgInitialSessionId;
1225
1226 return $wgInitialSessionId &&
1227 $this->getRequest()->getSession()->getId() === (string)$wgInitialSessionId;
1228 }
1229
1235 private function showCreateAccountLink() {
1236 return $this->isSignup() ||
1237 $this->getContext()->getAuthority()->isAllowed( 'createaccount' );
1238 }
1239
1240 protected function getTokenName() {
1241 return $this->isSignup() ? 'wpCreateaccountToken' : 'wpLoginToken';
1242 }
1243
1250 protected function makeLanguageSelector() {
1251 $msg = $this->msg( 'loginlanguagelinks' )->inContentLanguage();
1252 if ( $msg->isBlank() ) {
1253 return '';
1254 }
1255 $langs = explode( "\n", $msg->text() );
1256 $links = [];
1257 foreach ( $langs as $lang ) {
1258 $lang = trim( $lang, '* ' );
1259 $parts = explode( '|', $lang );
1260 if ( count( $parts ) >= 2 ) {
1261 $links[] = $this->makeLanguageSelectorLink( $parts[0], trim( $parts[1] ) );
1262 }
1263 }
1264
1265 return count( $links ) > 0 ? $this->msg( 'loginlanguagelabel' )->rawParams(
1266 $this->getLanguage()->pipeList( $links ) )->escaped() : '';
1267 }
1268
1277 protected function makeLanguageSelectorLink( $text, $lang ) {
1278 $services = MediaWikiServices::getInstance();
1279
1280 if ( $this->getLanguage()->getCode() == $lang
1281 || !$services->getLanguageNameUtils()->isValidCode( $lang )
1282 ) {
1283 // no link for currently used language
1284 // or invalid language code
1285 return htmlspecialchars( $text );
1286 }
1287
1288 $query = $this->getPreservedParams();
1289 $query['uselang'] = $lang;
1290
1291 $attr = [];
1292 $targetLanguage = $services->getLanguageFactory()->getLanguage( $lang );
1293 $attr['lang'] = $attr['hreflang'] = $targetLanguage->getHtmlCode();
1294 $attr['class'] = 'mw-authentication-popup-link';
1295 $attr['title'] = false;
1296
1297 return $this->getLinkRenderer()->makeKnownLink(
1298 $this->getPageTitle(),
1299 $text,
1300 $attr,
1301 $query
1302 );
1303 }
1304
1305 protected function getGroupName() {
1306 return 'login';
1307 }
1308
1313 protected function postProcessFormDescriptor( &$formDescriptor, $requests ) {
1314 // Pre-fill username (if not creating an account, T46775).
1315 if (
1316 isset( $formDescriptor['username'] ) &&
1317 !isset( $formDescriptor['username']['default'] ) &&
1318 !$this->isSignup()
1319 ) {
1320 $user = $this->getUser();
1321 if ( $user->isRegistered() && !$user->isTemp() ) {
1322 $formDescriptor['username']['default'] = $user->getName();
1323 } else {
1324 $formDescriptor['username']['default'] =
1325 $this->getRequest()->getSession()->suggestLoginUsername();
1326 }
1327 }
1328
1329 // don't show a submit button if there is nothing to submit (i.e. the only form content
1330 // is other submit buttons, for redirect flows)
1331 if ( !$this->needsSubmitButton( $requests ) ) {
1332 unset( $formDescriptor['createaccount'], $formDescriptor['loginattempt'] );
1333 }
1334
1335 if ( !$this->isSignup() ) {
1336 // FIXME HACK don't focus on non-empty field
1337 // maybe there should be an autofocus-if similar to hide-if?
1338 if (
1339 isset( $formDescriptor['username'] )
1340 && empty( $formDescriptor['username']['default'] )
1341 && !$this->getRequest()->getCheck( 'wpName' )
1342 ) {
1343 $formDescriptor['username']['autofocus'] = true;
1344 } elseif ( isset( $formDescriptor['password'] ) ) {
1345 $formDescriptor['password']['autofocus'] = true;
1346 }
1347 }
1348
1349 $this->addTabIndex( $formDescriptor );
1350 }
1351
1357 protected function getNoticeHtml() {
1358 $noticeContent = $this->msg( 'createacct-temp-warning', $this->getUser()->getName() )->parse();
1359 return Html::noticeBox(
1360 $noticeContent,
1361 'mw-createaccount-temp-warning',
1362 '',
1363 'mw-userLogin-icon--user-temporary'
1364 );
1365 }
1366
1367}
1368
1370class_alias( LoginSignupSpecialPage::class, 'LoginSignupSpecialPage' );
const PROTO_HTTPS
Definition Defines.php:232
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...
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
global $wgInitialSessionId
Definition Setup.php:438
Helper functions for the login form that need to be shared with other special pages (such as CentralA...
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.
AuthenticationRequest to ensure something with a username is present.
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.
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.
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...
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:209
This class is a collection of static functions that serve two purposes:
Definition Html.php:57
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:157
HTML sanitizer for MediaWiki.
Definition Sanitizer.php:46
This serves as the entry point to the MediaWiki session handling system.
The base class for all skins.
Definition Skin.php:58
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.
makeLanguageSelectorLink( $text, $lang)
Create a language selector link for a particular language Links back to this page preserving type and...
string string $mReturnToAnchor
The fragment part of the URL to return to after authentication finishes.
beforeExecute( $subPage)
Gets called before SpecialPage::execute.Return false to prevent calling execute() (since 1....
showSuccessPage( $type, $title, $msgname, $injected_html, $extraMessages)
Show the success page.
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')
getPreservedParams( $options=[])
Returns URL query parameters which should be preserved between authentication requests....
logAuthResult( $success, UserIdentity $performer, $status=null)
Logs to the authmanager-stats channel.
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,...
string string $mReturnToQuery
The query string part of the URL to return to after authentication finishes.
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 ...
successfulAction( $direct=false, $extraMessages=null)
string string $mReturnTo
The title of the page to return to after authentication finishes, or the empty string when there is n...
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...
getFieldDefinitions(array $fieldInfo, array $requests)
Create a HTMLForm descriptor for the core login fields.
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
Represents a title within MediaWiki.
Definition Title.php:78
User class for the MediaWiki software.
Definition User.php:123
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Interface for objects representing user identity.
element(SerializerNode $parent, SerializerNode $node, $contents)