39use Wikimedia\ScopedCallback;
102 private $reasonValidatorResult =
null;
139 protected function setRequest( array $data, $wasPosted =
null ) {
140 parent::setRequest( $data, $wasPosted );
141 $this->mLoadedRequest =
false;
149 if ( $this->loginHelper === null ) {
158 private function loadRequestParameters() {
159 if ( $this->mLoadedRequest ) {
162 $this->mLoadedRequest =
true;
165 $this->mPosted = $request->wasPosted();
166 $this->mAction = $request->getRawVal(
'action' );
167 $this->mFromHTTP = $request->getBool(
'fromhttp',
false )
168 || $request->getBool(
'wpFromhttp',
false );
170 || ( !$this->mFromHTTP && $request->getProtocol() ===
'https' )
171 || $request->getBool(
'wpForceHttps',
false );
172 $this->mReturnTo = $request->getVal(
'returnto',
'' );
173 $this->mReturnToQuery = $request->getVal(
'returntoquery',
'' );
174 $this->mReturnToAnchor = $request->getVal(
'returntoanchor',
'' );
175 $this->mAlwaysShowLogin = $request->getBool(
'alwaysShowLogin' );
183 protected function load( $subPage ) {
184 $this->loadRequestParameters();
185 if ( $this->mLoaded ) {
188 $this->mLoaded =
true;
189 $request = $this->getRequest();
191 $securityLevel = $this->getRequest()->getText(
'force' );
195 $securityLevel ) === AuthManager::SEC_REAUTH
197 $this->securityLevel = $securityLevel;
200 $this->loadAuth( $subPage );
202 $this->mToken = $request->getVal( $this->getTokenName() );
205 $entryError = $this->msg( $request->getVal(
'error',
'' ) );
206 $entryWarning = $this->msg( $request->getVal(
'warning',
'' ) );
207 $entryNotice = $this->msg( $request->getVal(
'notice',
'' ) );
210 $loginreqlink = $this->getLinkRenderer()->makeKnownLink(
211 $this->getPageTitle(),
212 $this->msg(
'loginreqlink' )->text(),
214 $this->getPreservedParams( [
'reset' =>
true ] )
219 if ( $entryError->exists()
220 && in_array( $entryError->getKey(), $validErrorMessages,
true )
222 $this->mEntryErrorType =
'error';
223 $this->mEntryError = $entryError->rawParams( $loginreqlink )->parse();
225 } elseif ( $entryWarning->exists()
226 && in_array( $entryWarning->getKey(), $validErrorMessages,
true )
228 $this->mEntryErrorType =
'warning';
229 $this->mEntryError = $entryWarning->rawParams( $loginreqlink )->parse();
230 } elseif ( $entryNotice->exists()
231 && in_array( $entryNotice->getKey(), $validErrorMessages,
true )
233 $this->mEntryErrorType =
'notice';
234 $this->mEntryError = $entryNotice->parse();
237 # 1. When switching accounts, it sucks to get automatically logged out
238 # 2. Do not return to PasswordReset after a successful password change
239 # but goto Wiki start page (Main_Page) instead ( T35997 )
240 $returnToTitle = Title::newFromText( $this->mReturnTo );
241 if ( is_object( $returnToTitle )
242 && ( $returnToTitle->isSpecial(
'Userlogout' )
243 || $returnToTitle->isSpecial(
'PasswordReset' ) )
245 $this->mReturnTo =
'';
246 $this->mReturnToQuery =
'';
252 $params = parent::getPreservedParams( $options );
256 $this->loadRequestParameters();
258 'returnto' =>
'mReturnTo',
259 'returntoquery' =>
'mReturnToQuery',
260 'returntoanchor' =>
'mReturnToAnchor',
262 foreach ( $properties as $key => $prop ) {
263 $value = $this->$prop;
264 if ( $value !==
'' ) {
265 $params[$key] = $value;
267 unset( $params[$key] );
271 if ( $this->mAlwaysShowLogin ) {
272 $params[
'alwaysShowLogin'] =
'1';
275 $params[
'fromhttp'] = $this->mFromHTTP ?
'1' :
null;
278 $displayMode = $this->getLoginHelper()->getDisplayMode();
283 return array_filter( $params,
static fn ( $val ) => $val !==
null );
289 $this->loadRequestParameters();
290 return parent::beforeExecute( $subPage );
297 if ( $this->mPosted ) {
299 ->getTiming(
'auth_specialpage_executeTiming_seconds' )
301 $profilingScope =
new ScopedCallback(
function () use ( $timer ) {
303 ->setLabel(
'action', $this->authAction )
309 $session = $this->getRequest()->getSession();
319 $this->getOutput()->disableClientCache();
321 $this->load( $subPage );
324 if ( $this->getLoginHelper()->isDisplayModePopup() ) {
329 $this->getContext()->setSkin( $skinFactory->makeSkin(
'authentication-popup' ) );
333 $this->checkPermissions();
336 if ( !$this->isSignup() && !$authManager->canAuthenticateNow() ) {
337 if ( !$session->canSetUser() ) {
338 throw new ErrorPageError(
'cannotloginnow-title',
'cannotloginnow-text', [
339 $session->getProvider()->describe( $this->getLanguage() )
342 throw new ErrorPageError(
'cannotlogin-title',
'cannotlogin-text' );
343 } elseif ( $this->isSignup() && !$authManager->canCreateAccounts() ) {
344 throw new ErrorPageError(
'cannotcreateaccount-title',
'cannotcreateaccount-text' );
366 if ( !$this->isSignup() && !$this->mPosted && !$this->securityLevel &&
367 ( $this->mReturnTo !==
'' || $this->mReturnToQuery !==
'' ) &&
368 !$this->mAlwaysShowLogin &&
369 !$this->getUser()->isTemp() && $this->getUser()->isRegistered()
371 $this->successfulAction();
376 if ( $this->getRequest()->getProtocol() !==
'https' ) {
377 $title = $this->getFullTitle();
378 $query = $this->getPreservedParams() + [
380 ( $this->mEntryErrorType ===
'error' ?
'error'
381 :
'warning' ) => $this->mEntryError,
382 ] + $this->getRequest()->getQueryValues();
387 $this->getOutput()->redirect(
$url );
389 $this->getOutput()->addVaryHeader(
'X-Forwarded-Proto' );
396 if ( str_starts_with(
$url,
'https://' ) ) {
397 $this->mSecureLoginUrl =
$url;
402 if ( !$this->isActionAllowed( $this->authAction ) ) {
406 $this->mainLoginForm( [],
'authpage-cannot-' . $this->authAction );
410 if ( $this->canBypassForm( $button_name ) ) {
411 $this->setRequest( [],
true );
412 $this->getRequest()->setVal( $this->getTokenName(), $this->getToken() );
413 if ( $button_name ) {
414 $this->getRequest()->setVal( $button_name,
true );
417 $performer = $this->getUser();
418 $status = $this->trySubmit();
420 if ( !$status || !$status->isGood() ) {
421 $this->mainLoginForm( $this->authRequests, $status ? $status->getMessage() :
'',
'error' );
426 $response = $status->getValue();
428 $returnToUrl = $this->getPageTitle(
'return' )
429 ->getFullURL( $this->getPreservedParams( [
'withToken' =>
true ] ),
false,
PROTO_HTTPS );
430 switch ( $response->status ) {
431 case AuthenticationResponse::PASS:
432 $this->logAuthResult(
true, $performer );
433 $this->proxyAccountCreation = $this->isSignup() && $this->getUser()->isNamed();
434 $this->targetUser = User::newFromName( $response->username );
437 !$this->proxyAccountCreation
438 && $response->loginRequest
439 && $authManager->canAuthenticateNow()
442 $response2 = $authManager->beginAuthentication( [ $response->loginRequest ],
444 if ( $response2->status !== AuthenticationResponse::PASS ) {
445 LoggerFactory::getInstance(
'login' )
446 ->error(
'Could not log in after account creation' );
447 $this->successfulAction(
true, Status::newFatal(
'createacct-loginerror' ) );
452 if ( !$this->proxyAccountCreation ) {
453 $context = RequestContext::getMain();
454 $localContext = $this->getContext();
455 if ( $context !== $localContext ) {
457 $this->setContext( $context );
460 $this->getAuthManager()->setRequestContextUserFromSessionUser();
463 $this->successfulAction(
true );
465 case AuthenticationResponse::FAIL:
467 case AuthenticationResponse::RESTART:
468 $this->authForm =
null;
469 if ( $response->status === AuthenticationResponse::FAIL ) {
470 $action = $this->getDefaultAction( $subPage );
471 $messageType =
'error';
473 $action = $this->getContinueAction( $this->authAction );
474 $messageType =
'warning';
476 $this->logAuthResult(
false, $performer, $response->message ? $response->message->getKey() :
'-' );
477 $this->loadAuth( $subPage, $action,
true );
478 $this->mainLoginForm( $this->authRequests, $response->message, $messageType );
480 case AuthenticationResponse::REDIRECT:
481 $this->authForm =
null;
482 $this->getOutput()->redirect( $response->redirectTarget );
484 case AuthenticationResponse::UI:
485 $this->authForm =
null;
486 $this->authAction = $this->isSignup() ? AuthManager::ACTION_CREATE_CONTINUE
487 : AuthManager::ACTION_LOGIN_CONTINUE;
488 $this->authRequests = $response->neededRequests;
489 $this->mainLoginForm( $response->neededRequests, $response->message, $response->messageType );
492 throw new LogicException(
'invalid AuthenticationResponse' );
509 private function canBypassForm( &$button_name ) {
511 if ( $this->isContinued() ) {
514 $fields = AuthenticationRequest::mergeFieldInfo( $this->authRequests );
515 foreach ( $fields as $fieldname => $field ) {
516 if ( !isset( $field[
'type'] ) ) {
519 if ( !empty( $field[
'skippable'] ) ) {
522 if ( $field[
'type'] ===
'button' ) {
523 if ( $button_name !==
null ) {
527 $button_name = $fieldname;
529 } elseif ( $field[
'type'] !==
'null' ) {
546 $type, $title, $msgname, $injected_html, $extraMessages
548 $out = $this->getOutput();
549 $out->setPageTitleMsg( $title );
551 $out->addWikiMsg( $msgname,
wfEscapeWikiText( $this->getUser()->getName() ) );
553 if ( $extraMessages ) {
554 $extraMessages = Status::wrap( $extraMessages );
555 $out->addWikiTextAsInterface(
556 $extraMessages->getWikiText(
false,
false, $this->getLanguage() )
560 $out->addHTML( $injected_html );
563 $helper->showReturnToPage( $type, $this->mReturnTo, $this->mReturnToQuery,
564 $this->mStickHTTPS, $this->mReturnToAnchor );
580 protected function mainLoginForm( array $requests, $msg =
'', $msgtype =
'error' ) {
581 $user = $this->getUser();
582 $out = $this->getOutput();
587 $this->authAction = $this->getDefaultAction( $this->subPage );
588 $this->authForm =
null;
590 ->getAuthenticationRequests( $this->authAction, $user );
594 $out->addModuleStyles( [
595 'mediawiki.special.userlogin.common.styles',
596 'mediawiki.codex.messagebox.styles'
598 if ( $this->isSignup() ) {
600 $out->addModules(
'mediawiki.special.createaccount' );
601 $out->addModuleStyles( [
602 'mediawiki.special.userlogin.signup.styles'
606 $out->addModuleStyles( [
607 'mediawiki.special.userlogin.login.styles'
610 $out->disallowUserJs();
612 $form = $this->getAuthForm( $requests, $this->authAction );
613 $form->prepareForm();
615 $submitStatus = Status::newGood();
616 if ( $msg && $msgtype ===
'warning' ) {
617 $submitStatus->warning( $msg );
618 } elseif ( $msg && $msgtype ===
'error' ) {
619 $submitStatus->fatal( $msg );
623 $this->getOutput()->addJsConfigVars(
624 'wgErrorPageMessageKey',
625 is_string( $msg ) ? $msg : $msg->getKey()
630 if ( $this->getUser()->isNamed() && !$this->isContinued() ) {
631 if ( !$this->isSignup() && $this->securityLevel ) {
632 $submitStatus->warning(
'userlogin-reauth', $this->getUser()->getName() );
638 Html::warningBox( $this->msg(
639 $this->isSignup() ?
'createacct-loggedin' :
'userlogin-loggedin',
640 $this->getUser()->getName()
642 '<div class="cdx-field"><div class="cdx-field__control">' .
645 'class' =>
'cdx-button cdx-button--fake-button cdx-button--fake-button--enabled ' .
646 'cdx-button--action-progressive cdx-button--weight-primary mw-htmlform-submit ' .
647 ( $this->getLoginHelper()->isDisplayModePopup() ?
'mw-authentication-popup-link' :
'' ),
648 'href' => ( Title::newFromText( $this->mReturnTo ) ?: Title::newMainPage() )
649 ->createFragmentTarget( $this->mReturnToAnchor )->getLinkURL( $this->mReturnToQuery ),
652 $this->isSignup() ?
'createacct-loggedin-continue-as' :
'userlogin-loggedin-continue-as',
653 $this->getUser()->getName()
658 $this->isSignup() ?
'createacct-loggedin-heading' :
'userlogin-loggedin-heading'
661 $this->isSignup() ?
'createacct-loggedin-prompt' :
'userlogin-loggedin-prompt'
667 $formHtml = $form->getHTML( $submitStatus );
669 $out->addHTML( $this->getPageHtml( $formHtml ) );
679 $loginPrompt = $this->isSignup() || $this->getLoginHelper()->isDisplayModePopup() || $this->isContinued()
681 : Html::rawElement(
'div', [
'id' =>
'userloginprompt' ], $this->msg(
'loginprompt' )->parseAsBlock() );
683 ? $this->makeLanguageSelector() :
'';
684 $signupStartMsg = $this->msg(
'signupstart' );
685 $signupStart = ( $this->isSignup() && !$signupStartMsg->isDisabled() )
686 ? Html::rawElement(
'div', [
'id' =>
'signupstart' ], $signupStartMsg->parseAsBlock() ) :
'';
687 if ( $languageLinks ) {
688 $languageLinks = Html::rawElement(
'div', [
'id' =>
'languagelinks' ],
689 Html::rawElement(
'p', [], $languageLinks )
692 if ( $this->getUser()->isTemp() ) {
693 $noticeHtml = $this->getNoticeHtml();
697 $formBlock = Html::rawElement(
'div', [
'id' =>
'userloginForm' ], $formHtml );
698 $formAndBenefits = $formBlock;
699 if ( $this->isSignup() && $this->showExtraInformation() && !$this->getUser()->isNamed() ) {
700 $benefitsContainerHtml =
null;
702 'context' => $this->getContext(),
703 'form' => $this->authForm,
706 'beforeForm' =>
false,
708 $this->getHookRunner()->onSpecialCreateAccountBenefits(
709 $benefitsContainerHtml, $info, $options
711 $benefitsContainerHtml ??= $this->getBenefitsContainerHtml();
712 $formAndBenefits = $options[
'beforeForm']
713 ? ( $benefitsContainerHtml . $formBlock )
714 : ( $formBlock . $benefitsContainerHtml );
721 . Html::rawElement(
'div', [
'class' =>
'mw-ui-container' ],
734 $benefitsContainer =
'';
735 $this->getOutput()->addModuleStyles( [
'oojs-ui.styles.icons-user' ] );
736 if ( $this->isSignup() && $this->showExtraInformation() ) {
737 if ( !$this->getUser()->isTemp() ) {
744 for ( $benefitIdx = 1; $benefitIdx <= $benefitCount; $benefitIdx++ ) {
745 $numberUnescaped = $this->msg(
"createacct-benefit-head$benefitIdx" )->text();
746 $numberHtml = Html::rawElement(
'strong', [], $numberUnescaped );
747 $iconClass = $this->msg(
"createacct-benefit-icon$benefitIdx" )->text();
748 $benefitList .= Html::rawElement(
'div', [
'class' =>
"mw-number-text $iconClass" ],
749 Html::rawElement(
'p', [],
750 $this->msg(
"createacct-benefit-text$benefitIdx" )->params(
757 $benefitsContainer = Html::rawElement(
'div', [
'class' =>
'mw-createacct-benefits-container' ],
758 Html::element(
'div', [
'class' =>
'mw-createacct-benefits-heading' ],
759 $this->msg(
'createacct-benefit-heading' )->text()
761 . Html::rawElement(
'div', [
'class' =>
'mw-createacct-benefits-list' ], $benefitList )
765 $this->getOutput()->addModuleStyles(
767 'oojs-ui.styles.icons-moderation',
768 'oojs-ui.styles.icons-interactions',
773 'icon' =>
'oo-ui-icon-unStar',
774 'description' => $this->msg(
"benefit-1-description" )->escaped()
777 'icon' =>
'oo-ui-icon-userContributions',
778 'description' => $this->msg(
"benefit-2-description" )->escaped()
781 'icon' =>
'oo-ui-icon-settings',
782 'description' => $this->msg(
"benefit-3-description" )->escaped()
785 foreach ( $benefits as $benefit ) {
786 $benefitContent = Html::rawElement(
'div', [
'class' =>
'mw-benefit-item' ],
787 Html::rawElement(
'span', [
'class' => $benefit[
'icon' ] ] )
788 . Html::rawElement(
'p', [], $benefit[
'description'] )
791 $benefitList .= Html::rawElement(
792 'div', [
'class' =>
'mw-benefit-item-wrapper' ], $benefitContent );
795 $benefitsListWrapper = Html::rawElement(
796 'div', [
'class' =>
'mw-benefit-list-wrapper' ], $benefitList );
798 $headingSubheadingWrapper = Html::rawElement(
'div', [
'class' =>
'mw-heading-subheading-wrapper' ],
800 $this->msg(
'createacct-benefit-heading-temp-user' )->text()
802 .
Html::element(
'p', [
'class' =>
'mw-benefit-subheading' ],
803 $this->msg(
'createacct-benefit-subheading-temp-user' )->text()
807 $benefitsContainer = Html::rawElement(
808 'div', [
'class' =>
'mw-createacct-benefits-container' ],
809 $headingSubheadingWrapper
810 . $benefitsListWrapper
814 return $benefitsContainer;
826 if ( $this->authForm ) {
827 return $this->authForm;
830 $usingHTTPS = $this->getRequest()->getProtocol() ===
'https';
833 $fieldInfo = AuthenticationRequest::mergeFieldInfo( $requests );
835 $formDescriptor = $this->fieldInfoToFormDescriptor( $requests, $fieldInfo, $this->authAction );
836 $this->postProcessFormDescriptor( $formDescriptor, $requests );
838 $context = $this->getContext();
839 if ( $context->getRequest() !== $this->getRequest() ) {
842 $context->setRequest( $this->getRequest() );
844 $form = HTMLForm::factory(
'codex', $formDescriptor, $context );
846 $form->addHiddenField(
'authAction', $this->authAction );
847 $form->addHiddenField(
'force', $this->securityLevel );
848 $form->addHiddenField( $this->getTokenName(), $this->getToken()->toString() );
849 $config = $this->getConfig();
850 if ( $config->get( MainConfigNames::SecureLogin ) &&
851 !$config->get( MainConfigNames::ForceHTTPS ) ) {
853 if ( !$this->isSignup() ) {
854 $form->addHiddenField(
'wpForceHttps', (
int)$this->mStickHTTPS );
855 $form->addHiddenField(
'wpFromhttp', $usingHTTPS );
859 $form->setAction( $this->getPageTitle()->getLocalURL( $this->getPreservedParams(
863 $form->setName(
'userlogin' . ( $this->isSignup() ?
'2' :
'' ) );
864 if ( $this->isSignup() ) {
865 $form->setId(
'userlogin2' );
868 $form->suppressDefaultSubmit();
870 $this->authForm = $form;
877 array $requests, array $fieldInfo, array &$formDescriptor, $action
879 $formDescriptor = self::mergeDefaultFormDescriptor( $fieldInfo, $formDescriptor,
880 $this->getFieldDefinitions( $fieldInfo, $requests ) );
890 return $this->authAction !== $this->getContinueAction( $this->authAction )
891 && ( !$this->securityLevel || !$this->getUser()->isNamed() );
903 $isLoggedIn = $this->getUser()->isRegistered();
904 $continuePart = $this->isContinued() ?
'continue-' :
'';
905 $anotherPart = $isLoggedIn ?
'another-' :
'';
907 $expiration = $this->getRequest()->getSession()->getProvider()->getRememberUserDuration();
908 $expirationDays = ceil( $expiration / ( 3600 * 24 ) );
909 $secureLoginLink =
'';
910 if ( $this->mSecureLoginUrl ) {
911 $secureLoginLink = Html::rawElement(
'a', [
912 'href' => $this->mSecureLoginUrl,
913 'class' =>
'mw-login-flush-right mw-secure',
914 ], Html::element(
'span', [
'class' =>
'mw-secure--icon' ] ) .
915 $this->msg(
'userlogin-signwithsecure' )->parse() );
917 $usernameHelpLink =
'';
918 if ( !$this->msg(
'createacct-helpusername' )->isDisabled() ) {
919 $usernameHelpLink = Html::rawElement(
'span', [
920 'class' =>
'mw-login-flush-right',
921 ], $this->msg(
'createacct-helpusername' )->parse() );
924 if ( $this->isSignup() ) {
925 $config = $this->getConfig();
926 $hideIf = isset( $fieldInfo[
'mailpassword'] ) ? [
'hide-if' => [
'===',
'mailpassword',
'1' ] ] : [];
927 $fieldDefinitions = [
929 'label-raw' => $this->msg(
'userlogin-yourname' )->escaped() . $usernameHelpLink,
931 'placeholder-message' => $isLoggedIn ?
'createacct-another-username-ph'
932 :
'userlogin-yourname-ph',
937 'label-message' =>
'createaccountmail',
938 'name' =>
'wpCreateaccountMail',
939 'id' =>
'wpCreateaccountMail',
942 'id' =>
'wpPassword2',
943 'autocomplete' =>
'new-password',
944 'placeholder-message' =>
'createacct-yourpassword-ph',
945 'help-message' =>
'createacct-useuniquepass',
949 'type' =>
'password',
950 'label-message' =>
'createacct-yourpasswordagain',
952 'cssclass' =>
'loginPassword',
954 'autocomplete' =>
'new-password',
955 'validation-callback' =>
function ( $value, $alldata ) {
956 if ( empty( $alldata[
'mailpassword'] ) && !empty( $alldata[
'password'] ) ) {
958 return $this->msg(
'htmlform-required' );
959 } elseif ( $value !== $alldata[
'password'] ) {
960 return $this->msg(
'badretype' );
965 'placeholder-message' =>
'createacct-yourpasswordagain-ph',
969 'label-message' => $config->get( MainConfigNames::EmailConfirmToEdit )
970 ?
'createacct-emailrequired' :
'createacct-emailoptional',
972 'cssclass' =>
'loginText',
975 'autocomplete' =>
'email',
977 'required' => $config->get( MainConfigNames::EmailConfirmToEdit ),
978 'validation-callback' =>
function ( $value, $alldata ) {
982 $this->getConfig()->get( MainConfigNames::EmailConfirmToEdit )
986 return $this->msg(
'noemailtitle' );
987 } elseif ( !$value && !empty( $alldata[
'mailpassword'] ) ) {
989 return $this->msg(
'noemailcreate' );
990 } elseif ( $value && !Sanitizer::validateEmail( $value ) ) {
991 return $this->msg(
'invalidemailaddress' );
992 } elseif ( is_string( $value ) && strlen( $value ) > 255 ) {
993 return $this->msg(
'changeemail-maxlength' );
1000 'placeholder-message' =>
'createacct-' . $anotherPart .
'email-ph',
1004 'help-message' => $isLoggedIn ?
'createacct-another-realname-tip'
1005 :
'prefs-help-realname',
1006 'label-message' =>
'createacct-realname',
1007 'cssclass' =>
'loginText',
1009 'placeholder-message' =>
'createacct-realname',
1010 'id' =>
'wpRealName',
1011 'autocomplete' =>
'name',
1016 'label-message' =>
'createacct-reason',
1017 'cssclass' =>
'loginText',
1020 'validation-callback' =>
function ( $value, $alldata ) {
1023 if ( $value && Sanitizer::validateEmail( $value ) ) {
1024 if ( $this->reasonValidatorResult !==
null ) {
1025 return $this->reasonValidatorResult;
1027 $this->reasonValidatorResult =
true;
1028 $authManager = MediaWikiServices::getInstance()->getAuthManager();
1029 if ( !$authManager->getAuthenticationSessionData(
'reason-retry',
false ) ) {
1030 $authManager->setAuthenticationSessionData(
'reason-retry',
true );
1031 $this->reasonValidatorResult = $this->msg(
'createacct-reason-confirm' );
1033 return $this->reasonValidatorResult;
1037 'placeholder-message' =>
'createacct-reason-ph',
1039 'createaccount' => [
1047 'default' => $this->msg(
'createacct-' . $anotherPart . $continuePart .
1049 'name' =>
'wpCreateaccount',
1050 'id' =>
'wpCreateaccount',
1055 if ( !$this->msg(
'createacct-username-help' )->isDisabled() ) {
1056 $fieldDefinitions[
'username'][
'help-message'] =
'createacct-username-help';
1059 if ( $this->loginHelper->isDisplayModePopup() ) {
1060 $fieldDefinitions[
'redirectnotice'] = [
1062 'default' => $this->msg(
'createacct-popup-redirect-notice' )->text(),
1063 'cssclass' =>
'mw-createacct-redirect-notice',
1071 $passwordRequest = AuthenticationRequest::getRequestByClass( $this->authRequests,
1072 PasswordAuthenticationRequest::class );
1073 $changePassword = $passwordRequest && $passwordRequest->action == AuthManager::ACTION_CHANGE;
1074 $fieldDefinitions = [
1077 'label-raw' => $this->msg(
'userlogin-yourname' )->escaped() . $secureLoginLink,
1079 'placeholder-message' =>
'userlogin-yourname-ph',
1080 ] + ( $changePassword ? [
1083 'baseField' =>
'password',
1086 'cssclass' =>
'mw-htmlform-hidden-field',
1091 'autocomplete' =>
'new-password',
1092 'placeholder-message' =>
'createacct-yourpassword-ph',
1093 'help-message' =>
'createacct-useuniquepass',
1095 'id' =>
'wpPassword1',
1096 'autocomplete' =>
'current-password',
1097 'placeholder-message' =>
'userlogin-yourpassword-ph',
1101 'type' =>
'password',
1102 'autocomplete' =>
'new-password',
1103 'placeholder-message' =>
'createacct-yourpasswordagain-ph',
1109 'cssclass' =>
'mw-userlogin-rememberme',
1110 'name' =>
'wpRemember',
1111 'label-message' => $this->msg(
'userlogin-remembermypassword' )
1112 ->numParams( $expirationDays ),
1113 'id' =>
'wpRemember',
1121 'default' => $this->msg(
'pt-login-' . $continuePart .
'button' )->text(),
1122 'id' =>
'wpLoginAttempt',
1125 'linkcontainer' => [
1128 'cssclass' =>
'mw-form-related-link-container mw-userlogin-help',
1131 'default' => Html::element(
'a', [
1132 'href' => Skin::makeInternalOrExternalUrl( $this->msg(
'helplogin-url' )
1133 ->inContentLanguage()
1135 ], $this->msg(
'userlogin-helplink2' )->text() ),
1149 $isUsernameOrPasswordRequest =
1150 AuthenticationRequest::getRequestByClass( $requests, UsernameAuthenticationRequest::class ) ||
1151 AuthenticationRequest::getRequestByClass( $requests, PasswordAuthenticationRequest::class );
1153 if ( $isUsernameOrPasswordRequest ) {
1154 $fieldDefinitions[
'username'] += [
1157 'cssclass' =>
'loginText mw-userlogin-username',
1159 'autocomplete' =>
'username',
1162 $fieldDefinitions[
'password'] += [
1163 'type' =>
'password',
1165 'name' =>
'wpPassword',
1166 'cssclass' =>
'loginPassword mw-userlogin-password',
1172 if ( $this->mEntryError ) {
1174 if ( $this->mEntryErrorType ===
'error' ) {
1175 $defaultHtml = Html::errorBox( $this->mEntryError );
1176 } elseif ( $this->mEntryErrorType ===
'warning' ) {
1177 $defaultHtml = Html::warningBox( $this->mEntryError );
1178 } elseif ( $this->mEntryErrorType ===
'notice' ) {
1179 $defaultHtml = Html::noticeBox( $this->mEntryError );
1181 $fieldDefinitions[
'entryError'] = [
1183 'default' => $defaultHtml,
1189 if ( !$this->showExtraInformation() ) {
1190 unset( $fieldDefinitions[
'linkcontainer'], $fieldDefinitions[
'signupend'] );
1192 if ( $this->isSignup() && $this->showExtraInformation() ) {
1195 $signupendMsg = $this->msg(
'signupend' );
1196 $signupendHttpsMsg = $this->msg(
'signupend-https' );
1197 if ( !$signupendMsg->isDisabled() ) {
1198 $usingHTTPS = $this->getRequest()->getProtocol() ===
'https';
1199 $signupendText = ( $usingHTTPS && !$signupendHttpsMsg->isBlank() )
1200 ? $signupendHttpsMsg->parse() : $signupendMsg->parse();
1201 $fieldDefinitions[
'signupend'] = [
1204 'default' => Html::rawElement(
'div', [
'id' =>
'signupend' ], $signupendText ),
1209 if ( !$this->isSignup() && $this->showExtraInformation() ) {
1210 $passwordReset = MediaWikiServices::getInstance()->getPasswordReset();
1211 if ( $passwordReset->isEnabled()->isGood() ) {
1212 $fieldDefinitions[
'passwordReset'] = [
1215 'cssclass' =>
'mw-form-related-link-container',
1216 'default' => $this->getLinkRenderer()->makeLink(
1217 SpecialPage::getTitleFor(
'PasswordReset' ),
1218 $this->msg(
'userlogin-resetpassword-link' )->text(),
1219 [
'class' =>
'mw-authentication-popup-link' ],
1220 $this->getPreservedParams()
1227 if ( $this->showCreateAccountLink() ) {
1229 $linkTitle = SpecialPage::getTitleFor( $this->isSignup() ?
'Userlogin' :
'CreateAccount' );
1230 $linkq =
wfArrayToCgi( $this->getPreservedParams( [
'reset' =>
true ] ) );
1231 $isLoggedIn = $this->getUser()->isRegistered()
1232 && !$this->getUser()->isTemp();
1233 $popupMode = $this->getLoginHelper()->isDisplayModePopup();
1235 $fieldDefinitions[
'createOrLogin'] = [
1238 'linkQuery' => $linkq,
1239 'default' =>
function ( $params ) use ( $isLoggedIn, $linkTitle ) {
1240 $buttonClasses =
'cdx-button cdx-button--action-progressive '
1241 .
'cdx-button--fake-button cdx-button--fake-button--enabled';
1243 return Html::rawElement(
'div',
1246 [
'id' =>
'mw-createaccount' . ( !$isLoggedIn ?
'-cta' :
'' ),
1247 'class' => ( $isLoggedIn ?
'mw-form-related-link-container' :
'' ) ],
1248 ( $isLoggedIn ?
'' : $this->msg(
'userlogin-noaccount' )->escaped() )
1249 . Html::element(
'a',
1253 'id' =>
'mw-createaccount-join' . ( $isLoggedIn ?
'-loggedin' :
'' ),
1254 'href' => $linkTitle->getLocalURL( $params[
'linkQuery'] ),
1255 'class' => [
'mw-authentication-popup-link', $buttonClasses => !$isLoggedIn ],
1256 'target' =>
'_self',
1260 $isLoggedIn ?
'userlogin-createanother' :
'userlogin-joinproject'
1265 'weight' => $popupMode ? -1 : 235,
1270 return $fieldDefinitions;
1278 private function showCreateAccountLink() {
1279 return $this->isSignup() ||
1280 $this->getContext()->getAuthority()->isAllowed(
'createaccount' );
1287 return $this->isSignup() ?
'wpCreateaccountToken' :
'wpLoginToken';
1297 $msg = $this->msg(
'loginlanguagelinks' )->inContentLanguage();
1298 if ( $msg->isBlank() ) {
1301 $langs = explode(
"\n", $msg->text() );
1303 foreach ( $langs as $lang ) {
1304 $lang = trim( $lang,
'* ' );
1305 $parts = explode(
'|', $lang );
1306 if ( count( $parts ) >= 2 ) {
1307 $links[] = $this->makeLanguageSelectorLink( $parts[0], trim( $parts[1] ) );
1311 return count( $links ) > 0 ? $this->msg(
'loginlanguagelabel' )->rawParams(
1312 $this->getLanguage()->pipeList( $links ) )->escaped() :
'';
1324 $services = MediaWikiServices::getInstance();
1326 if ( $this->getLanguage()->getCode() == $lang
1327 || !$services->getLanguageNameUtils()->isValidCode( $lang )
1331 return htmlspecialchars( $text );
1334 $query = $this->getPreservedParams();
1335 $query[
'uselang'] = $lang;
1338 $targetLanguage = $services->getLanguageFactory()->getLanguage( $lang );
1339 $attr[
'lang'] = $attr[
'hreflang'] = $targetLanguage->getHtmlCode();
1340 $attr[
'class'] =
'mw-authentication-popup-link';
1341 $attr[
'title'] =
false;
1343 return $this->getLinkRenderer()->makeKnownLink(
1344 $this->getPageTitle(),
1363 isset( $formDescriptor[
'username'] ) &&
1364 !isset( $formDescriptor[
'username'][
'default'] ) &&
1367 $user = $this->getUser();
1368 if ( $user->isRegistered() && !$user->isTemp() ) {
1369 $formDescriptor[
'username'][
'default'] = $user->getName();
1371 $formDescriptor[
'username'][
'default'] =
1372 $this->getRequest()->getSession()->suggestLoginUsername();
1378 if ( !$this->needsSubmitButton( $requests ) ) {
1379 unset( $formDescriptor[
'createaccount'], $formDescriptor[
'loginattempt'] );
1382 if ( $this->getUser()->isNamed() && !$this->isContinued() ) {
1384 if ( isset( $formDescriptor[
'createaccount'] ) ) {
1385 $formDescriptor[
'createaccount'][
'flags'] = [
'progressive' ];
1387 if ( isset( $formDescriptor[
'loginattempt'] ) ) {
1388 $formDescriptor[
'loginattempt'][
'flags'] = [
'progressive' ];
1392 if ( !$this->isSignup() ) {
1396 isset( $formDescriptor[
'username'] )
1397 && empty( $formDescriptor[
'username'][
'default'] )
1398 && !$this->getRequest()->getCheck(
'wpName' )
1400 $formDescriptor[
'username'][
'autofocus'] =
true;
1401 } elseif ( isset( $formDescriptor[
'password'] ) ) {
1402 $formDescriptor[
'password'][
'autofocus'] =
true;
1406 $this->addTabIndex( $formDescriptor );
1415 $noticeContent = $this->msg(
'createacct-temp-warning', $this->getUser()->getName() )->parse();
1416 return Html::noticeBox(
1418 'mw-createaccount-temp-warning',
1420 'mw-userLogin-icon--user-temporary'
1427class_alias( LoginSignupSpecialPage::class,
'LoginSignupSpecialPage' );
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....
if(!defined('MW_SETUP_CALLBACK'))
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.
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 SecureLogin
Name constant for the SecureLogin setting, for use with Config::get()
A special page subclass for authentication-related special pages.
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.Used to preserve POST data over a...
postProcessFormDescriptor(&$formDescriptor, $requests)
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 execute.Return false to prevent calling execute() (since 1.27+)....
string null $mSecureLoginUrl
showSuccessPage( $type, $title, $msgname, $injected_html, $extraMessages)
Show the success page.
getAuthForm(array $requests, $action)
Generates a form from the given request.
getBenefitsContainerHtml()
The HTML to be shown in the "benefits to signing in / creating an account" section of the signup/logi...
bool $mAlwaysShowLogin
Value of the 'alwaysShowLogin' URL parameter.
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.
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.
load( $subPage)
Load data from request.
getContext()
Gets the context this SpecialPage is executed in.
Generic operation result class Has warning/error list, boolean status and arbitrary value.