MediaWiki REL1_28
LoginSignupSpecialPage.php
Go to the documentation of this file.
1<?php
30
37 protected $mReturnTo;
38 protected $mPosted;
39 protected $mAction;
40 protected $mLanguage;
41 protected $mReturnToQuery;
42 protected $mToken;
43 protected $mStickHTTPS;
44 protected $mFromHTTP;
45 protected $mEntryError = '';
46 protected $mEntryErrorType = 'error';
47
48 protected $mLoaded = false;
49 protected $mLoadedRequest = false;
51
53 protected $securityLevel;
54
59 protected $targetUser;
60
62 protected $authForm;
63
65 protected $fakeTemplate;
66
67 abstract protected function isSignup();
68
75 abstract protected function successfulAction( $direct = false, $extraMessages = null );
76
82 abstract protected function logAuthResult( $success, $status = null );
83
84 public function __construct( $name ) {
86 parent::__construct( $name );
87
88 // Override UseMediaWikiEverywhere to true, to force login and create form to use mw ui
90 }
91
92 protected function setRequest( array $data, $wasPosted = null ) {
93 parent::setRequest( $data, $wasPosted );
94 $this->mLoadedRequest = false;
95 }
96
101 private function loadRequestParameters( $subPage ) {
102 if ( $this->mLoadedRequest ) {
103 return;
104 }
105 $this->mLoadedRequest = true;
106 $request = $this->getRequest();
107
108 $this->mPosted = $request->wasPosted();
109 $this->mIsReturn = $subPage === 'return';
110 $this->mAction = $request->getVal( 'action' );
111 $this->mFromHTTP = $request->getBool( 'fromhttp', false )
112 || $request->getBool( 'wpFromhttp', false );
113 $this->mStickHTTPS = ( !$this->mFromHTTP && $request->getProtocol() === 'https' )
114 || $request->getBool( 'wpForceHttps', false );
115 $this->mLanguage = $request->getText( 'uselang' );
116 $this->mReturnTo = $request->getVal( 'returnto', '' );
117 $this->mReturnToQuery = $request->getVal( 'returntoquery', '' );
118 }
119
125 protected function load( $subPage ) {
127
129 if ( $this->mLoaded ) {
130 return;
131 }
132 $this->mLoaded = true;
133 $request = $this->getRequest();
134
135 $securityLevel = $this->getRequest()->getText( 'force' );
136 if (
137 $securityLevel && AuthManager::singleton()->securitySensitiveOperationStatus(
138 $securityLevel ) === AuthManager::SEC_REAUTH
139 ) {
140 $this->securityLevel = $securityLevel;
141 }
142
143 $this->loadAuth( $subPage );
144
145 $this->mToken = $request->getVal( $this->getTokenName() );
146
147 // Show an error or warning passed on from a previous page
148 $entryError = $this->msg( $request->getVal( 'error', '' ) );
149 $entryWarning = $this->msg( $request->getVal( 'warning', '' ) );
150 // bc: provide login link as a parameter for messages where the translation
151 // was not updated
152 $loginreqlink = Linker::linkKnown(
153 $this->getPageTitle(),
154 $this->msg( 'loginreqlink' )->escaped(),
155 [],
156 [
157 'returnto' => $this->mReturnTo,
158 'returntoquery' => $this->mReturnToQuery,
159 'uselang' => $this->mLanguage,
160 'fromhttp' => $wgSecureLogin && $this->mFromHTTP ? '1' : null,
161 ]
162 );
163
164 // Only show valid error or warning messages.
165 if ( $entryError->exists()
166 && in_array( $entryError->getKey(), LoginHelper::getValidErrorMessages(), true )
167 ) {
168 $this->mEntryErrorType = 'error';
169 $this->mEntryError = $entryError->rawParams( $loginreqlink )->parse();
170
171 } elseif ( $entryWarning->exists()
172 && in_array( $entryWarning->getKey(), LoginHelper::getValidErrorMessages(), true )
173 ) {
174 $this->mEntryErrorType = 'warning';
175 $this->mEntryError = $entryWarning->rawParams( $loginreqlink )->parse();
176 }
177
178 # 1. When switching accounts, it sucks to get automatically logged out
179 # 2. Do not return to PasswordReset after a successful password change
180 # but goto Wiki start page (Main_Page) instead ( bug 33997 )
181 $returnToTitle = Title::newFromText( $this->mReturnTo );
182 if ( is_object( $returnToTitle )
183 && ( $returnToTitle->isSpecial( 'Userlogout' )
184 || $returnToTitle->isSpecial( 'PasswordReset' ) )
185 ) {
186 $this->mReturnTo = '';
187 $this->mReturnToQuery = '';
188 }
189 }
190
191 protected function getPreservedParams( $withToken = false ) {
193
194 $params = parent::getPreservedParams( $withToken );
195 $params += [
196 'returnto' => $this->mReturnTo ?: null,
197 'returntoquery' => $this->mReturnToQuery ?: null,
198 ];
199 if ( $wgSecureLogin && !$this->isSignup() ) {
200 $params['fromhttp'] = $this->mFromHTTP ? '1' : null;
201 }
202 return $params;
203 }
204
205 protected function beforeExecute( $subPage ) {
206 // finish initializing the class before processing the request - T135924
208 return parent::beforeExecute( $subPage );
209 }
210
214 public function execute( $subPage ) {
215 $authManager = AuthManager::singleton();
216 $session = SessionManager::getGlobalSession();
217
218 // Session data is used for various things in the authentication process, so we must make
219 // sure a session cookie or some equivalent mechanism is set.
220 $session->persist();
221
222 $this->load( $subPage );
223 $this->setHeaders();
224 $this->checkPermissions();
225
226 // Make sure the system configuration allows log in / sign up
227 if ( !$this->isSignup() && !$authManager->canAuthenticateNow() ) {
228 if ( !$session->canSetUser() ) {
229 throw new ErrorPageError( 'cannotloginnow-title', 'cannotloginnow-text', [
230 $session->getProvider()->describe( RequestContext::getMain()->getLanguage() )
231 ] );
232 }
233 throw new ErrorPageError( 'cannotlogin-title', 'cannotlogin-text' );
234 } elseif ( $this->isSignup() && !$authManager->canCreateAccounts() ) {
235 throw new ErrorPageError( 'cannotcreateaccount-title', 'cannotcreateaccount-text' );
236 }
237
238 /*
239 * In the case where the user is already logged in, and was redirected to
240 * the login form from a page that requires login, do not show the login
241 * page. The use case scenario for this is when a user opens a large number
242 * of tabs, is redirected to the login page on all of them, and then logs
243 * in on one, expecting all the others to work properly.
244 *
245 * However, do show the form if it was visited intentionally (no 'returnto'
246 * is present). People who often switch between several accounts have grown
247 * accustomed to this behavior.
248 *
249 * Also make an exception when force=<level> is set in the URL, which means the user must
250 * reauthenticate for security reasons.
251 */
252 if ( !$this->isSignup() && !$this->mPosted && !$this->securityLevel &&
253 ( $this->mReturnTo !== '' || $this->mReturnToQuery !== '' ) &&
254 $this->getUser()->isLoggedIn()
255 ) {
256 $this->successfulAction();
257 }
258
259 // If logging in and not on HTTPS, either redirect to it or offer a link.
261 if ( $this->getRequest()->getProtocol() !== 'https' ) {
262 $title = $this->getFullTitle();
263 $query = $this->getPreservedParams( false ) + [
264 'title' => null,
265 ( $this->mEntryErrorType === 'error' ? 'error'
266 : 'warning' ) => $this->mEntryError,
267 ] + $this->getRequest()->getQueryValues();
268 $url = $title->getFullURL( $query, false, PROTO_HTTPS );
269 if ( $wgSecureLogin && !$this->mFromHTTP &&
270 wfCanIPUseHTTPS( $this->getRequest()->getIP() )
271 ) {
272 // Avoid infinite redirect
273 $url = wfAppendQuery( $url, 'fromhttp=1' );
274 $this->getOutput()->redirect( $url );
275 // Since we only do this redir to change proto, always vary
276 $this->getOutput()->addVaryHeader( 'X-Forwarded-Proto' );
277
278 return;
279 } else {
280 // A wiki without HTTPS login support should set $wgServer to
281 // http://somehost, in which case the secure URL generated
282 // above won't actually start with https://
283 if ( substr( $url, 0, 8 ) === 'https://' ) {
284 $this->mSecureLoginUrl = $url;
285 }
286 }
287 }
288
289 if ( !$this->isActionAllowed( $this->authAction ) ) {
290 // FIXME how do we explain this to the user? can we handle session loss better?
291 // messages used: authpage-cannot-login, authpage-cannot-login-continue,
292 // authpage-cannot-create, authpage-cannot-create-continue
293 $this->mainLoginForm( [], 'authpage-cannot-' . $this->authAction );
294 return;
295 }
296
297 if ( $this->canBypassForm( $button_name ) ) {
298 $this->setRequest( [], true );
299 $this->getRequest()->setVal( $this->getTokenName(), $this->getToken() );
300 if ( $button_name ) {
301 $this->getRequest()->setVal( $button_name, true );
302 }
303 }
304
305 $status = $this->trySubmit();
306
307 if ( !$status || !$status->isGood() ) {
308 $this->mainLoginForm( $this->authRequests, $status ? $status->getMessage() : '', 'error' );
309 return;
310 }
311
313 $response = $status->getValue();
314
315 $returnToUrl = $this->getPageTitle( 'return' )
316 ->getFullURL( $this->getPreservedParams( true ), false, PROTO_HTTPS );
317 switch ( $response->status ) {
318 case AuthenticationResponse::PASS:
319 $this->logAuthResult( true );
320 $this->proxyAccountCreation = $this->isSignup() && !$this->getUser()->isAnon();
321 $this->targetUser = User::newFromName( $response->username );
322
323 if (
324 !$this->proxyAccountCreation
325 && $response->loginRequest
326 && $authManager->canAuthenticateNow()
327 ) {
328 // successful registration; log the user in instantly
329 $response2 = $authManager->beginAuthentication( [ $response->loginRequest ],
330 $returnToUrl );
331 if ( $response2->status !== AuthenticationResponse::PASS ) {
332 LoggerFactory::getInstance( 'login' )
333 ->error( 'Could not log in after account creation' );
334 $this->successfulAction( true, Status::newFatal( 'createacct-loginerror' ) );
335 break;
336 }
337 }
338
339 if ( !$this->proxyAccountCreation ) {
340 // Ensure that the context user is the same as the session user.
342 }
343
344 $this->successfulAction( true );
345 break;
346 case AuthenticationResponse::FAIL:
347 // fall through
348 case AuthenticationResponse::RESTART:
349 unset( $this->authForm );
350 if ( $response->status === AuthenticationResponse::FAIL ) {
351 $action = $this->getDefaultAction( $subPage );
352 $messageType = 'error';
353 } else {
354 $action = $this->getContinueAction( $this->authAction );
355 $messageType = 'warning';
356 }
357 $this->logAuthResult( false, $response->message ? $response->message->getKey() : '-' );
358 $this->loadAuth( $subPage, $action, true );
359 $this->mainLoginForm( $this->authRequests, $response->message, $messageType );
360 break;
361 case AuthenticationResponse::REDIRECT:
362 unset( $this->authForm );
363 $this->getOutput()->redirect( $response->redirectTarget );
364 break;
365 case AuthenticationResponse::UI:
366 unset( $this->authForm );
367 $this->authAction = $this->isSignup() ? AuthManager::ACTION_CREATE_CONTINUE
368 : AuthManager::ACTION_LOGIN_CONTINUE;
369 $this->authRequests = $response->neededRequests;
370 $this->mainLoginForm( $response->neededRequests, $response->message, $response->messageType );
371 break;
372 default:
373 throw new LogicException( 'invalid AuthenticationResponse' );
374 }
375 }
376
390 private function canBypassForm( &$button_name ) {
391 $button_name = null;
392 if ( $this->isContinued() ) {
393 return false;
394 }
395 $fields = AuthenticationRequest::mergeFieldInfo( $this->authRequests );
396 foreach ( $fields as $fieldname => $field ) {
397 if ( !isset( $field['type'] ) ) {
398 return false;
399 }
400 if ( !empty( $field['skippable'] ) ) {
401 continue;
402 }
403 if ( $field['type'] === 'button' ) {
404 if ( $button_name !== null ) {
405 $button_name = null;
406 return false;
407 } else {
408 $button_name = $fieldname;
409 }
410 } elseif ( $field['type'] !== 'null' ) {
411 return false;
412 }
413 }
414 return true;
415 }
416
426 protected function showSuccessPage(
427 $type, $title, $msgname, $injected_html, $extraMessages
428 ) {
429 $out = $this->getOutput();
430 $out->setPageTitle( $title );
431 if ( $msgname ) {
432 $out->addWikiMsg( $msgname, wfEscapeWikiText( $this->getUser()->getName() ) );
433 }
434 if ( $extraMessages ) {
435 $extraMessages = Status::wrap( $extraMessages );
436 $out->addWikiText( $extraMessages->getWikiText() );
437 }
438
439 $out->addHTML( $injected_html );
440
441 $helper = new LoginHelper( $this->getContext() );
442 $helper->showReturnToPage( $type, $this->mReturnTo, $this->mReturnToQuery, $this->mStickHTTPS );
443 }
444
460 public function showReturnToPage(
461 $type, $returnTo = '', $returnToQuery = '', $stickHTTPS = false
462 ) {
463 $helper = new LoginHelper( $this->getContext() );
464 $helper->showReturnToPage( $type, $returnTo, $returnToQuery, $stickHTTPS );
465 }
466
472 protected function setSessionUserForCurrentRequest() {
474
476 $localContext = $this->getContext();
477 if ( $context !== $localContext ) {
478 // remove AuthManagerSpecialPage context hack
479 $this->setContext( $context );
480 }
481
482 $user = $context->getRequest()->getSession()->getUser();
483
484 $wgUser = $user;
485 $context->setUser( $user );
486
487 $code = $this->getRequest()->getVal( 'uselang', $user->getOption( 'language' ) );
488 $userLang = Language::factory( $code );
489 $wgLang = $userLang;
490 $context->setLanguage( $userLang );
491 }
492
507 protected function mainLoginForm( array $requests, $msg = '', $msgtype = 'error' ) {
508 $titleObj = $this->getPageTitle();
509 $user = $this->getUser();
510 $out = $this->getOutput();
511
512 // FIXME how to handle empty $requests - restart, or no form, just an error message?
513 // no form would be better for no session type errors, restart is better when can* fails.
514 if ( !$requests ) {
515 $this->authAction = $this->getDefaultAction( $this->subPage );
516 $this->authForm = null;
517 $requests = AuthManager::singleton()->getAuthenticationRequests( $this->authAction, $user );
518 }
519
520 // Generic styles and scripts for both login and signup form
521 $out->addModuleStyles( [
522 'mediawiki.ui',
523 'mediawiki.ui.button',
524 'mediawiki.ui.checkbox',
525 'mediawiki.ui.input',
526 'mediawiki.special.userlogin.common.styles'
527 ] );
528 if ( $this->isSignup() ) {
529 // XXX hack pending RL or JS parse() support for complex content messages T27349
530 $out->addJsConfigVars( 'wgCreateacctImgcaptchaHelp',
531 $this->msg( 'createacct-imgcaptcha-help' )->parse() );
532
533 // Additional styles and scripts for signup form
534 $out->addModules( [
535 'mediawiki.special.userlogin.signup.js'
536 ] );
537 $out->addModuleStyles( [
538 'mediawiki.special.userlogin.signup.styles'
539 ] );
540 } else {
541 // Additional styles for login form
542 $out->addModuleStyles( [
543 'mediawiki.special.userlogin.login.styles'
544 ] );
545 }
546 $out->disallowUserJs(); // just in case...
547
548 $form = $this->getAuthForm( $requests, $this->authAction, $msg, $msgtype );
549 $form->prepareForm();
550
551 $submitStatus = Status::newGood();
552 if ( $msg && $msgtype === 'warning' ) {
553 $submitStatus->warning( $msg );
554 } elseif ( $msg && $msgtype === 'error' ) {
555 $submitStatus->fatal( $msg );
556 }
557
558 // warning header for non-standard workflows (e.g. security reauthentication)
559 if ( !$this->isSignup() && $this->getUser()->isLoggedIn() ) {
560 $reauthMessage = $this->securityLevel ? 'userlogin-reauth' : 'userlogin-loggedin';
561 $submitStatus->warning( $reauthMessage, $this->getUser()->getName() );
562 }
563
564 $formHtml = $form->getHTML( $submitStatus );
565
566 $out->addHTML( $this->getPageHtml( $formHtml ) );
567 }
568
575 protected function getPageHtml( $formHtml ) {
577
578 $loginPrompt = $this->isSignup() ? '' : Html::rawElement( 'div',
579 [ 'id' => 'userloginprompt' ], $this->msg( 'loginprompt' )->parseAsBlock() );
580 $languageLinks = $wgLoginLanguageSelector ? $this->makeLanguageSelector() : '';
581 $signupStartMsg = $this->msg( 'signupstart' );
582 $signupStart = ( $this->isSignup() && !$signupStartMsg->isDisabled() )
583 ? Html::rawElement( 'div', [ 'id' => 'signupstart' ], $signupStartMsg->parseAsBlock() ) : '';
584 if ( $languageLinks ) {
585 $languageLinks = Html::rawElement( 'div', [ 'id' => 'languagelinks' ],
586 Html::rawElement( 'p', [], $languageLinks )
587 );
588 }
589
590 $benefitsContainer = '';
591 if ( $this->isSignup() && $this->showExtraInformation() ) {
592 // messages used:
593 // createacct-benefit-icon1 createacct-benefit-head1 createacct-benefit-body1
594 // createacct-benefit-icon2 createacct-benefit-head2 createacct-benefit-body2
595 // createacct-benefit-icon3 createacct-benefit-head3 createacct-benefit-body3
596 $benefitCount = 3;
597 $benefitList = '';
598 for ( $benefitIdx = 1; $benefitIdx <= $benefitCount; $benefitIdx++ ) {
599 $headUnescaped = $this->msg( "createacct-benefit-head$benefitIdx" )->text();
600 $iconClass = $this->msg( "createacct-benefit-icon$benefitIdx" )->escaped();
601 $benefitList .= Html::rawElement( 'div', [ 'class' => "mw-number-text $iconClass" ],
602 Html::rawElement( 'h3', [],
603 $this->msg( "createacct-benefit-head$benefitIdx" )->escaped()
604 )
605 . Html::rawElement( 'p', [],
606 $this->msg( "createacct-benefit-body$benefitIdx" )->params( $headUnescaped )->escaped()
607 )
608 );
609 }
610 $benefitsContainer = Html::rawElement( 'div', [ 'class' => 'mw-createacct-benefits-container' ],
611 Html::rawElement( 'h2', [], $this->msg( 'createacct-benefit-heading' )->escaped() )
612 . Html::rawElement( 'div', [ 'class' => 'mw-createacct-benefits-list' ],
613 $benefitList
614 )
615 );
616 }
617
618 $html = Html::rawElement( 'div', [ 'class' => 'mw-ui-container' ],
619 $loginPrompt
620 . $languageLinks
621 . $signupStart
622 . Html::rawElement( 'div', [ 'id' => 'userloginForm' ],
623 $formHtml
624 )
625 . $benefitsContainer
626 );
627
628 return $html;
629 }
630
639 protected function getAuthForm( array $requests, $action, $msg = '', $msgType = 'error' ) {
641 // FIXME merge this with parent
642
643 if ( isset( $this->authForm ) ) {
644 return $this->authForm;
645 }
646
647 $usingHTTPS = $this->getRequest()->getProtocol() === 'https';
648
649 // get basic form description from the auth logic
650 $fieldInfo = AuthenticationRequest::mergeFieldInfo( $requests );
651 $fakeTemplate = $this->getFakeTemplate( $msg, $msgType );
652 $this->fakeTemplate = $fakeTemplate; // FIXME there should be a saner way to pass this to the hook
653 // this will call onAuthChangeFormFields()
654 $formDescriptor = static::fieldInfoToFormDescriptor( $requests, $fieldInfo, $this->authAction );
655 $this->postProcessFormDescriptor( $formDescriptor, $requests );
656
657 $context = $this->getContext();
658 if ( $context->getRequest() !== $this->getRequest() ) {
659 // We have overridden the request, need to make sure the form uses that too.
660 $context = new DerivativeContext( $this->getContext() );
661 $context->setRequest( $this->getRequest() );
662 }
663 $form = HTMLForm::factory( 'vform', $formDescriptor, $context );
664
665 $form->addHiddenField( 'authAction', $this->authAction );
667 $form->addHiddenField( 'uselang', $this->mLanguage );
668 }
669 $form->addHiddenField( 'force', $this->securityLevel );
670 $form->addHiddenField( $this->getTokenName(), $this->getToken()->toString() );
671 if ( $wgSecureLogin ) {
672 // If using HTTPS coming from HTTP, then the 'fromhttp' parameter must be preserved
673 if ( !$this->isSignup() ) {
674 $form->addHiddenField( 'wpForceHttps', (int)$this->mStickHTTPS );
675 $form->addHiddenField( 'wpFromhttp', $usingHTTPS );
676 }
677 }
678
679 // set properties of the form itself
680 $form->setAction( $this->getPageTitle()->getLocalURL( $this->getReturnToQueryStringFragment() ) );
681 $form->setName( 'userlogin' . ( $this->isSignup() ? '2' : '' ) );
682 if ( $this->isSignup() ) {
683 $form->setId( 'userlogin2' );
684 }
685
686 $form->suppressDefaultSubmit();
687
688 $this->authForm = $form;
689
690 return $form;
691 }
692
699 protected function getFakeTemplate( $msg, $msgType ) {
702
703 // make a best effort to get the value of fields which used to be fixed in the old login
704 // template but now might or might not exist depending on what providers are used
705 $request = $this->getRequest();
706 $data = (object) [
707 'mUsername' => $request->getText( 'wpName' ),
708 'mPassword' => $request->getText( 'wpPassword' ),
709 'mRetype' => $request->getText( 'wpRetype' ),
710 'mEmail' => $request->getText( 'wpEmail' ),
711 'mRealName' => $request->getText( 'wpRealName' ),
712 'mDomain' => $request->getText( 'wpDomain' ),
713 'mReason' => $request->getText( 'wpReason' ),
714 'mRemember' => $request->getCheck( 'wpRemember' ),
715 ];
716
717 // Preserves a bunch of logic from the old code that was rewritten in getAuthForm().
718 // There is no code reuse to make this easier to remove .
719 // If an extension tries to change any of these values, they are out of luck - we only
720 // actually use the domain/usedomain/domainnames, extraInput and extrafields keys.
721
722 $titleObj = $this->getPageTitle();
723 $user = $this->getUser();
725
726 // Pre-fill username (if not creating an account, bug 44775).
727 if ( $data->mUsername == '' && $this->isSignup() ) {
728 if ( $user->isLoggedIn() ) {
729 $data->mUsername = $user->getName();
730 } else {
731 $data->mUsername = $this->getRequest()->getSession()->suggestLoginUsername();
732 }
733 }
734
735 if ( $this->isSignup() ) {
736 // Must match number of benefits defined in messages
737 $template->set( 'benefitCount', 3 );
738
739 $q = 'action=submitlogin&type=signup';
740 $linkq = 'type=login';
741 } else {
742 $q = 'action=submitlogin&type=login';
743 $linkq = 'type=signup';
744 }
745
746 if ( $this->mReturnTo !== '' ) {
747 $returnto = '&returnto=' . wfUrlencode( $this->mReturnTo );
748 if ( $this->mReturnToQuery !== '' ) {
749 $returnto .= '&returntoquery=' .
750 wfUrlencode( $this->mReturnToQuery );
751 }
752 $q .= $returnto;
753 $linkq .= $returnto;
754 }
755
756 # Don't show a "create account" link if the user can't.
757 if ( $this->showCreateAccountLink() ) {
758 # Pass any language selection on to the mode switch link
759 if ( $wgLoginLanguageSelector && $this->mLanguage ) {
760 $linkq .= '&uselang=' . $this->mLanguage;
761 }
762 // Supply URL, login template creates the button.
763 $template->set( 'createOrLoginHref', $titleObj->getLocalURL( $linkq ) );
764 } else {
765 $template->set( 'link', '' );
766 }
767
768 $resetLink = $this->isSignup()
769 ? null
770 : is_array( $wgPasswordResetRoutes )
771 && in_array( true, array_values( $wgPasswordResetRoutes ), true );
772
773 $template->set( 'header', '' );
774 $template->set( 'formheader', '' );
775 $template->set( 'skin', $this->getSkin() );
776
777 $template->set( 'name', $data->mUsername );
778 $template->set( 'password', $data->mPassword );
779 $template->set( 'retype', $data->mRetype );
780 $template->set( 'createemailset', false ); // no easy way to get that from AuthManager
781 $template->set( 'email', $data->mEmail );
782 $template->set( 'realname', $data->mRealName );
783 $template->set( 'domain', $data->mDomain );
784 $template->set( 'reason', $data->mReason );
785 $template->set( 'remember', $data->mRemember );
786
787 $template->set( 'action', $titleObj->getLocalURL( $q ) );
788 $template->set( 'message', $msg );
789 $template->set( 'messagetype', $msgType );
790 $template->set( 'createemail', $wgEnableEmail && $user->isLoggedIn() );
791 $template->set( 'userealname', !in_array( 'realname', $wgHiddenPrefs, true ) );
792 $template->set( 'useemail', $wgEnableEmail );
793 $template->set( 'emailrequired', $wgEmailConfirmToEdit );
794 $template->set( 'emailothers', $wgEnableUserEmail );
795 $template->set( 'canreset', $wgAuth->allowPasswordChange() );
796 $template->set( 'resetlink', $resetLink );
797 $template->set( 'canremember', $request->getSession()->getProvider()
798 ->getRememberUserDuration() !== null );
799 $template->set( 'usereason', $user->isLoggedIn() );
800 $template->set( 'cansecurelogin', ( $wgSecureLogin ) );
801 $template->set( 'stickhttps', (int)$this->mStickHTTPS );
802 $template->set( 'loggedin', $user->isLoggedIn() );
803 $template->set( 'loggedinuser', $user->getName() );
804 $template->set( 'token', $this->getToken()->toString() );
805
806 $action = $this->isSignup() ? 'signup' : 'login';
807 $wgAuth->modifyUITemplate( $template, $action );
808
809 $oldTemplate = $template;
810 $hookName = $this->isSignup() ? 'UserCreateForm' : 'UserLoginForm';
811 Hooks::run( $hookName, [ &$template ] );
812 if ( $oldTemplate !== $template ) {
813 wfDeprecated( "reference in $hookName hook", '1.27' );
814 }
815
816 return $template;
817
818 }
819
820 public function onAuthChangeFormFields(
821 array $requests, array $fieldInfo, array &$formDescriptor, $action
822 ) {
823 $coreFieldDescriptors = $this->getFieldDefinitions( $this->fakeTemplate );
824 $specialFields = array_merge( [ 'extraInput' ],
825 array_keys( $this->fakeTemplate->getExtraInputDefinitions() ) );
826
827 // keep the ordering from getCoreFieldDescriptors() where there is no explicit weight
828 foreach ( $coreFieldDescriptors as $fieldName => $coreField ) {
829 $requestField = isset( $formDescriptor[$fieldName] ) ?
830 $formDescriptor[$fieldName] : [];
831
832 // remove everything that is not in the fieldinfo, is not marked as a supplemental field
833 // to something in the fieldinfo, is not B/C for the pre-AuthManager templates,
834 // and is not an info field or a submit button
835 if (
836 !isset( $fieldInfo[$fieldName] )
837 && (
838 !isset( $coreField['baseField'] )
839 || !isset( $fieldInfo[$coreField['baseField']] )
840 )
841 && !in_array( $fieldName, $specialFields, true )
842 && (
843 !isset( $coreField['type'] )
844 || !in_array( $coreField['type'], [ 'submit', 'info' ], true )
845 )
846 ) {
847 $coreFieldDescriptors[$fieldName] = null;
848 continue;
849 }
850
851 // core message labels should always take priority
852 if (
853 isset( $coreField['label'] )
854 || isset( $coreField['label-message'] )
855 || isset( $coreField['label-raw'] )
856 ) {
857 unset( $requestField['label'], $requestField['label-message'], $coreField['label-raw'] );
858 }
859
860 $coreFieldDescriptors[$fieldName] += $requestField;
861 }
862
863 $formDescriptor = array_filter( $coreFieldDescriptors + $formDescriptor );
864 return true;
865 }
866
873 protected function showExtraInformation() {
874 return $this->authAction !== $this->getContinueAction( $this->authAction )
876 }
877
883 protected function getFieldDefinitions( $template ) {
885
886 $isLoggedIn = $this->getUser()->isLoggedIn();
887 $continuePart = $this->isContinued() ? 'continue-' : '';
888 $anotherPart = $isLoggedIn ? 'another-' : '';
889 $expiration = $this->getRequest()->getSession()->getProvider()->getRememberUserDuration();
890 $expirationDays = ceil( $expiration / ( 3600 * 24 ) );
891 $secureLoginLink = '';
892 if ( $this->mSecureLoginUrl ) {
893 $secureLoginLink = Html::element( 'a', [
894 'href' => $this->mSecureLoginUrl,
895 'class' => 'mw-ui-flush-right mw-secure',
896 ], $this->msg( 'userlogin-signwithsecure' )->text() );
897 }
898 $usernameHelpLink = '';
899 if ( !$this->msg( 'createacct-helpusername' )->isDisabled() ) {
900 $usernameHelpLink = Html::rawElement( 'span', [
901 'class' => 'mw-ui-flush-right',
902 ], $this->msg( 'createacct-helpusername' )->parse() );
903 }
904
905 if ( $this->isSignup() ) {
906 $fieldDefinitions = [
907 'statusarea' => [
908 // used by the mediawiki.special.userlogin.signup.js module for error display
909 // FIXME merge this with HTMLForm's normal status (error) area
910 'type' => 'info',
911 'raw' => true,
912 'default' => Html::element( 'div', [ 'id' => 'mw-createacct-status-area' ] ),
913 'weight' => -105,
914 ],
915 'username' => [
916 'label-raw' => $this->msg( 'userlogin-yourname' )->escaped() . $usernameHelpLink,
917 'id' => 'wpName2',
918 'placeholder-message' => $isLoggedIn ? 'createacct-another-username-ph'
919 : 'userlogin-yourname-ph',
920 ],
921 'mailpassword' => [
922 // create account without providing password, a temporary one will be mailed
923 'type' => 'check',
924 'label-message' => 'createaccountmail',
925 'name' => 'wpCreateaccountMail',
926 'id' => 'wpCreateaccountMail',
927 ],
928 'password' => [
929 'id' => 'wpPassword2',
930 'placeholder-message' => 'createacct-yourpassword-ph',
931 'hide-if' => [ '===', 'wpCreateaccountMail', '1' ],
932 ],
933 'domain' => [],
934 'retype' => [
935 'baseField' => 'password',
936 'type' => 'password',
937 'label-message' => 'createacct-yourpasswordagain',
938 'id' => 'wpRetype',
939 'cssclass' => 'loginPassword',
940 'size' => 20,
941 'validation-callback' => function ( $value, $alldata ) {
942 if ( empty( $alldata['mailpassword'] ) && !empty( $alldata['password'] ) ) {
943 if ( !$value ) {
944 return $this->msg( 'htmlform-required' );
945 } elseif ( $value !== $alldata['password'] ) {
946 return $this->msg( 'badretype' );
947 }
948 }
949 return true;
950 },
951 'hide-if' => [ '===', 'wpCreateaccountMail', '1' ],
952 'placeholder-message' => 'createacct-yourpasswordagain-ph',
953 ],
954 'email' => [
955 'type' => 'email',
956 'label-message' => $wgEmailConfirmToEdit ? 'createacct-emailrequired'
957 : 'createacct-emailoptional',
958 'id' => 'wpEmail',
959 'cssclass' => 'loginText',
960 'size' => '20',
961 // FIXME will break non-standard providers
962 'required' => $wgEmailConfirmToEdit,
963 'validation-callback' => function ( $value, $alldata ) {
965
966 // AuthManager will check most of these, but that will make the auth
967 // session fail and this won't, so nicer to do it this way
968 if ( !$value && $wgEmailConfirmToEdit ) {
969 // no point in allowing registration without email when email is
970 // required to edit
971 return $this->msg( 'noemailtitle' );
972 } elseif ( !$value && !empty( $alldata['mailpassword'] ) ) {
973 // cannot send password via email when there is no email address
974 return $this->msg( 'noemailcreate' );
975 } elseif ( $value && !Sanitizer::validateEmail( $value ) ) {
976 return $this->msg( 'invalidemailaddress' );
977 }
978 return true;
979 },
980 'placeholder-message' => 'createacct-' . $anotherPart . 'email-ph',
981 ],
982 'realname' => [
983 'type' => 'text',
984 'help-message' => $isLoggedIn ? 'createacct-another-realname-tip'
985 : 'prefs-help-realname',
986 'label-message' => 'createacct-realname',
987 'cssclass' => 'loginText',
988 'size' => 20,
989 'id' => 'wpRealName',
990 ],
991 'reason' => [
992 // comment for the user creation log
993 'type' => 'text',
994 'label-message' => 'createacct-reason',
995 'cssclass' => 'loginText',
996 'id' => 'wpReason',
997 'size' => '20',
998 'placeholder-message' => 'createacct-reason-ph',
999 ],
1000 'extrainput' => [], // placeholder for fields coming from the template
1001 'createaccount' => [
1002 // submit button
1003 'type' => 'submit',
1004 'default' => $this->msg( 'createacct-' . $anotherPart . $continuePart .
1005 'submit' )->text(),
1006 'name' => 'wpCreateaccount',
1007 'id' => 'wpCreateaccount',
1008 'weight' => 100,
1009 ],
1010 ];
1011 } else {
1012 $fieldDefinitions = [
1013 'username' => [
1014 'label-raw' => $this->msg( 'userlogin-yourname' )->escaped() . $secureLoginLink,
1015 'id' => 'wpName1',
1016 'placeholder-message' => 'userlogin-yourname-ph',
1017 ],
1018 'password' => [
1019 'id' => 'wpPassword1',
1020 'placeholder-message' => 'userlogin-yourpassword-ph',
1021 ],
1022 'domain' => [],
1023 'extrainput' => [],
1024 'rememberMe' => [
1025 // option for saving the user token to a cookie
1026 'type' => 'check',
1027 'name' => 'wpRemember',
1028 'label-message' => $this->msg( 'userlogin-remembermypassword' )
1029 ->numParams( $expirationDays ),
1030 'id' => 'wpRemember',
1031 ],
1032 'loginattempt' => [
1033 // submit button
1034 'type' => 'submit',
1035 'default' => $this->msg( 'pt-login-' . $continuePart . 'button' )->text(),
1036 'id' => 'wpLoginAttempt',
1037 'weight' => 100,
1038 ],
1039 'linkcontainer' => [
1040 // help link
1041 'type' => 'info',
1042 'cssclass' => 'mw-form-related-link-container mw-userlogin-help',
1043 // 'id' => 'mw-userlogin-help', // FIXME HTMLInfoField ignores this
1044 'raw' => true,
1045 'default' => Html::element( 'a', [
1046 'href' => Skin::makeInternalOrExternalUrl( wfMessage( 'helplogin-url' )
1047 ->inContentLanguage()
1048 ->text() ),
1049 ], $this->msg( 'userlogin-helplink2' )->text() ),
1050 'weight' => 200,
1051 ],
1052 // button for ResetPasswordSecondaryAuthenticationProvider
1053 'skipReset' => [
1054 'weight' => 110,
1055 'flags' => [],
1056 ],
1057 ];
1058 }
1059
1060 $fieldDefinitions['username'] += [
1061 'type' => 'text',
1062 'name' => 'wpName',
1063 'cssclass' => 'loginText',
1064 'size' => 20,
1065 // 'required' => true,
1066 ];
1067 $fieldDefinitions['password'] += [
1068 'type' => 'password',
1069 // 'label-message' => 'userlogin-yourpassword', // would override the changepassword label
1070 'name' => 'wpPassword',
1071 'cssclass' => 'loginPassword',
1072 'size' => 20,
1073 // 'required' => true,
1074 ];
1075
1076 if ( $template->get( 'header' ) || $template->get( 'formheader' ) ) {
1077 // B/C for old extensions that haven't been converted to AuthManager (or have been
1078 // but somebody is using the old version) and still use templates via the
1079 // UserCreateForm/UserLoginForm hook.
1080 // 'header' used by ConfirmEdit, CondfirmAccount, Persona, WikimediaIncubator, SemanticSignup
1081 // 'formheader' used by MobileFrontend
1082 $fieldDefinitions['header'] = [
1083 'type' => 'info',
1084 'raw' => true,
1085 'default' => $template->get( 'header' ) ?: $template->get( 'formheader' ),
1086 'weight' => - 110,
1087 ];
1088 }
1089 if ( $this->mEntryError ) {
1090 $fieldDefinitions['entryError'] = [
1091 'type' => 'info',
1092 'default' => Html::rawElement( 'div', [ 'class' => $this->mEntryErrorType . 'box', ],
1093 $this->mEntryError ),
1094 'raw' => true,
1095 'rawrow' => true,
1096 'weight' => -100,
1097 ];
1098 }
1099 if ( !$this->showExtraInformation() ) {
1100 unset( $fieldDefinitions['linkcontainer'], $fieldDefinitions['signupend'] );
1101 }
1102 if ( $this->isSignup() && $this->showExtraInformation() ) {
1103 // blank signup footer for site customization
1104 // uses signupend-https for HTTPS requests if it's not blank, signupend otherwise
1105 $signupendMsg = $this->msg( 'signupend' );
1106 $signupendHttpsMsg = $this->msg( 'signupend-https' );
1107 if ( !$signupendMsg->isDisabled() ) {
1108 $usingHTTPS = $this->getRequest()->getProtocol() === 'https';
1109 $signupendText = ( $usingHTTPS && !$signupendHttpsMsg->isBlank() )
1110 ? $signupendHttpsMsg ->parse() : $signupendMsg->parse();
1111 $fieldDefinitions['signupend'] = [
1112 'type' => 'info',
1113 'raw' => true,
1114 'default' => Html::rawElement( 'div', [ 'id' => 'signupend' ], $signupendText ),
1115 'weight' => 225,
1116 ];
1117 }
1118 }
1119 if ( !$this->isSignup() && $this->showExtraInformation() ) {
1120 $passwordReset = new PasswordReset( $this->getConfig(), AuthManager::singleton() );
1121 if ( $passwordReset->isAllowed( $this->getUser() )->isGood() ) {
1122 $fieldDefinitions['passwordReset'] = [
1123 'type' => 'info',
1124 'raw' => true,
1125 'cssclass' => 'mw-form-related-link-container',
1126 'default' => Linker::link(
1127 SpecialPage::getTitleFor( 'PasswordReset' ),
1128 $this->msg( 'userlogin-resetpassword-link' )->escaped()
1129 ),
1130 'weight' => 230,
1131 ];
1132 }
1133
1134 // Don't show a "create account" link if the user can't.
1135 if ( $this->showCreateAccountLink() ) {
1136 // link to the other action
1137 $linkTitle = $this->getTitleFor( $this->isSignup() ? 'Userlogin' :'CreateAccount' );
1138 $linkq = $this->getReturnToQueryStringFragment();
1139 // Pass any language selection on to the mode switch link
1140 if ( $wgLoginLanguageSelector && $this->mLanguage ) {
1141 $linkq .= '&uselang=' . $this->mLanguage;
1142 }
1143 $loggedIn = $this->getUser()->isLoggedIn();
1144
1145 $fieldDefinitions['createOrLogin'] = [
1146 'type' => 'info',
1147 'raw' => true,
1148 'linkQuery' => $linkq,
1149 'default' => function ( $params ) use ( $loggedIn, $linkTitle ) {
1150 return Html::rawElement( 'div',
1151 [ 'id' => 'mw-createaccount' . ( !$loggedIn ? '-cta' : '' ),
1152 'class' => ( $loggedIn ? 'mw-form-related-link-container' : 'mw-ui-vform-field' ) ],
1153 ( $loggedIn ? '' : $this->msg( 'userlogin-noaccount' )->escaped() )
1154 . Html::element( 'a',
1155 [
1156 'id' => 'mw-createaccount-join' . ( $loggedIn ? '-loggedin' : '' ),
1157 'href' => $linkTitle->getLocalURL( $params['linkQuery'] ),
1158 'class' => ( $loggedIn ? '' : 'mw-ui-button' ),
1159 'tabindex' => 100,
1160 ],
1161 $this->msg(
1162 $loggedIn ? 'userlogin-createanother' : 'userlogin-joinproject'
1163 )->escaped()
1164 )
1165 );
1166 },
1167 'weight' => 235,
1168 ];
1169 }
1170 }
1171
1172 $fieldDefinitions = $this->getBCFieldDefinitions( $fieldDefinitions, $template );
1173 $fieldDefinitions = array_filter( $fieldDefinitions );
1174
1175 return $fieldDefinitions;
1176 }
1177
1184 protected function getBCFieldDefinitions( $fieldDefinitions, $template ) {
1185 if ( $template->get( 'usedomain', false ) ) {
1186 // TODO probably should be translated to the new domain notation in AuthManager
1187 $fieldDefinitions['domain'] = [
1188 'type' => 'select',
1189 'label-message' => 'yourdomainname',
1190 'options' => array_combine( $template->get( 'domainnames', [] ),
1191 $template->get( 'domainnames', [] ) ),
1192 'default' => $template->get( 'domain', '' ),
1193 'name' => 'wpDomain',
1194 // FIXME id => 'mw-user-domain-section' on the parent div
1195 ];
1196 }
1197
1198 // poor man's associative array_splice
1199 $extraInputPos = array_search( 'extrainput', array_keys( $fieldDefinitions ), true );
1200 $fieldDefinitions = array_slice( $fieldDefinitions, 0, $extraInputPos, true )
1201 + $template->getExtraInputDefinitions()
1202 + array_slice( $fieldDefinitions, $extraInputPos + 1, null, true );
1203
1204 return $fieldDefinitions;
1205 }
1206
1216 protected function hasSessionCookie() {
1218
1219 return $wgDisableCookieCheck || (
1221 $this->getRequest()->getSession()->getId() === (string)$wgInitialSessionId
1222 );
1223 }
1224
1229 protected function getReturnToQueryStringFragment() {
1230 $returnto = '';
1231 if ( $this->mReturnTo !== '' ) {
1232 $returnto = 'returnto=' . wfUrlencode( $this->mReturnTo );
1233 if ( $this->mReturnToQuery !== '' ) {
1234 $returnto .= '&returntoquery=' . wfUrlencode( $this->mReturnToQuery );
1235 }
1236 }
1237 return $returnto;
1238 }
1239
1245 private function showCreateAccountLink() {
1246 if ( $this->isSignup() ) {
1247 return true;
1248 } elseif ( $this->getUser()->isAllowed( 'createaccount' ) ) {
1249 return true;
1250 } else {
1251 return false;
1252 }
1253 }
1254
1255 protected function getTokenName() {
1256 return $this->isSignup() ? 'wpCreateaccountToken' : 'wpLoginToken';
1257 }
1258
1265 protected function makeLanguageSelector() {
1266 $msg = $this->msg( 'loginlanguagelinks' )->inContentLanguage();
1267 if ( $msg->isBlank() ) {
1268 return '';
1269 }
1270 $langs = explode( "\n", $msg->text() );
1271 $links = [];
1272 foreach ( $langs as $lang ) {
1273 $lang = trim( $lang, '* ' );
1274 $parts = explode( '|', $lang );
1275 if ( count( $parts ) >= 2 ) {
1276 $links[] = $this->makeLanguageSelectorLink( $parts[0], trim( $parts[1] ) );
1277 }
1278 }
1279
1280 return count( $links ) > 0 ? $this->msg( 'loginlanguagelabel' )->rawParams(
1281 $this->getLanguage()->pipeList( $links ) )->escaped() : '';
1282 }
1283
1292 protected function makeLanguageSelectorLink( $text, $lang ) {
1293 if ( $this->getLanguage()->getCode() == $lang ) {
1294 // no link for currently used language
1295 return htmlspecialchars( $text );
1296 }
1297 $query = [ 'uselang' => $lang ];
1298 if ( $this->mReturnTo !== '' ) {
1299 $query['returnto'] = $this->mReturnTo;
1300 $query['returntoquery'] = $this->mReturnToQuery;
1301 }
1302
1303 $attr = [];
1304 $targetLanguage = Language::factory( $lang );
1305 $attr['lang'] = $attr['hreflang'] = $targetLanguage->getHtmlCode();
1306
1307 return Linker::linkKnown(
1308 $this->getPageTitle(),
1309 htmlspecialchars( $text ),
1310 $attr,
1311 $query
1312 );
1313 }
1314
1315 protected function getGroupName() {
1316 return 'login';
1317 }
1318
1322 protected function postProcessFormDescriptor( &$formDescriptor, $requests ) {
1323 // Pre-fill username (if not creating an account, T46775).
1324 if (
1325 isset( $formDescriptor['username'] ) &&
1326 !isset( $formDescriptor['username']['default'] ) &&
1327 !$this->isSignup()
1328 ) {
1329 $user = $this->getUser();
1330 if ( $user->isLoggedIn() ) {
1331 $formDescriptor['username']['default'] = $user->getName();
1332 } else {
1333 $formDescriptor['username']['default'] =
1334 $this->getRequest()->getSession()->suggestLoginUsername();
1335 }
1336 }
1337
1338 // don't show a submit button if there is nothing to submit (i.e. the only form content
1339 // is other submit buttons, for redirect flows)
1340 if ( !$this->needsSubmitButton( $requests ) ) {
1341 unset( $formDescriptor['createaccount'], $formDescriptor['loginattempt'] );
1342 }
1343
1344 if ( !$this->isSignup() ) {
1345 // FIXME HACK don't focus on non-empty field
1346 // maybe there should be an autofocus-if similar to hide-if?
1347 if (
1348 isset( $formDescriptor['username'] )
1349 && empty( $formDescriptor['username']['default'] )
1350 && !$this->getRequest()->getCheck( 'wpName' )
1351 ) {
1352 $formDescriptor['username']['autofocus'] = true;
1353 } elseif ( isset( $formDescriptor['password'] ) ) {
1354 $formDescriptor['password']['autofocus'] = true;
1355 }
1356 }
1357
1358 $this->addTabIndex( $formDescriptor );
1359 }
1360}
1361
1369 public function execute() {
1370 throw new LogicException( 'not used' );
1371 }
1372
1377 public function addInputItem( $name, $value, $type, $msg, $helptext = false ) {
1378 // use the same indexes as UserCreateForm just in case someone adds an item manually
1379 $this->data['extrainput'][] = [
1380 'name' => $name,
1381 'value' => $value,
1382 'type' => $type,
1383 'msg' => $msg,
1384 'helptext' => $helptext,
1385 ];
1386 }
1387
1392 public function getExtraInputDefinitions() {
1393 $definitions = [];
1394
1395 foreach ( $this->get( 'extrainput', [] ) as $field ) {
1396 $definition = [
1397 'type' => $field['type'] === 'checkbox' ? 'check' : $field['type'],
1398 'name' => $field['name'],
1399 'value' => $field['value'],
1400 'id' => $field['name'],
1401 ];
1402 if ( $field['msg'] ) {
1403 $definition['label-message'] = $this->getMsg( $field['msg'] );
1404 }
1405 if ( $field['helptext'] ) {
1406 $definition['help'] = $this->msgWiki( $field['helptext'] );
1407 }
1408
1409 // the array key doesn't matter much when name is defined explicitly but
1410 // let's try and follow HTMLForm conventions
1411 $name = preg_replace( '/^wp(?=[A-Z])/', '', $field['name'] );
1412 $definitions[$name] = $definition;
1413 }
1414
1415 if ( $this->haveData( 'extrafields' ) ) {
1416 $definitions['extrafields'] = [
1417 'type' => 'info',
1418 'raw' => true,
1419 'default' => $this->get( 'extrafields' ),
1420 ];
1421 }
1422
1423 return $definitions;
1424 }
1425}
1426
1432class LoginForm extends SpecialPage {
1433 const SUCCESS = 0;
1434 const NO_NAME = 1;
1435 const ILLEGAL = 2;
1437 const NOT_EXISTS = 4;
1438 const WRONG_PASS = 5;
1439 const EMPTY_PASS = 6;
1440 const RESET_PASS = 7;
1441 const ABORTED = 8;
1443 const THROTTLED = 10;
1444 const USER_BLOCKED = 11;
1445 const NEED_TOKEN = 12;
1446 const WRONG_TOKEN = 13;
1447 const USER_MIGRATED = 14;
1448
1449 public static $statusCodes = [
1450 self::SUCCESS => 'success',
1451 self::NO_NAME => 'no_name',
1452 self::ILLEGAL => 'illegal',
1453 self::WRONG_PLUGIN_PASS => 'wrong_plugin_pass',
1454 self::NOT_EXISTS => 'not_exists',
1455 self::WRONG_PASS => 'wrong_pass',
1456 self::EMPTY_PASS => 'empty_pass',
1457 self::RESET_PASS => 'reset_pass',
1458 self::ABORTED => 'aborted',
1459 self::CREATE_BLOCKED => 'create_blocked',
1460 self::THROTTLED => 'throttled',
1461 self::USER_BLOCKED => 'user_blocked',
1462 self::NEED_TOKEN => 'need_token',
1463 self::WRONG_TOKEN => 'wrong_token',
1464 self::USER_MIGRATED => 'user_migrated',
1465 ];
1466
1470 public function __construct( $request = null ) {
1471 wfDeprecated( 'LoginForm', '1.27' );
1472 parent::__construct();
1473 }
1474
1478 public static function getValidErrorMessages() {
1480 }
1481
1485 public static function incrementLoginThrottle( $username ) {
1486 wfDeprecated( __METHOD__, "1.27" );
1488 $username = User::getCanonicalName( $username, 'usable' ) ?: $username;
1489 $throttler = new Throttler();
1490 return $throttler->increase( $username, $wgRequest->getIP(), __METHOD__ );
1491 }
1492
1496 public static function incLoginThrottle( $username ) {
1497 wfDeprecated( __METHOD__, "1.27" );
1498 $res = self::incrementLoginThrottle( $username );
1499 return is_array( $res ) ? true : 0;
1500 }
1501
1505 public static function clearLoginThrottle( $username ) {
1506 wfDeprecated( __METHOD__, "1.27" );
1508 $username = User::getCanonicalName( $username, 'usable' ) ?: $username;
1509 $throttler = new Throttler();
1510 return $throttler->clear( $username, $wgRequest->getIP() );
1511 }
1512
1516 public static function getLoginToken() {
1517 wfDeprecated( __METHOD__, '1.27' );
1519 return $wgRequest->getSession()->getToken( '', 'login' )->toString();
1520 }
1521
1525 public static function setLoginToken() {
1526 wfDeprecated( __METHOD__, '1.27' );
1527 }
1528
1532 public static function clearLoginToken() {
1533 wfDeprecated( __METHOD__, '1.27' );
1535 $wgRequest->getSession()->resetToken( 'login' );
1536 }
1537
1541 public static function getCreateaccountToken() {
1542 wfDeprecated( __METHOD__, '1.27' );
1544 return $wgRequest->getSession()->getToken( '', 'createaccount' )->toString();
1545 }
1546
1550 public static function setCreateaccountToken() {
1551 wfDeprecated( __METHOD__, '1.27' );
1552 }
1553
1557 public static function clearCreateaccountToken() {
1558 wfDeprecated( __METHOD__, '1.27' );
1560 $wgRequest->getSession()->resetToken( 'createaccount' );
1561 }
1562}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
$wgDisableCookieCheck
By default, MediaWiki checks if the client supports cookies during the login process,...
$wgHiddenPrefs
An array of preferences to not show for the user.
$wgLoginLanguageSelector
Show a bar of language selection links in the user login and user registration forms; edit the "login...
$wgEnableUserEmail
Set to true to enable user-to-user e-mail.
$wgPasswordResetRoutes
Whether to allow password resets ("enter some identifying data, and we'll send an email with a tempor...
$wgEnableEmail
Set to true to enable the e-mail basic features: Password reminders, etc.
$wgSecureLogin
This is to let user authenticate using https when they come from http.
$wgEmailConfirmToEdit
Should editors be required to have a validated e-mail address before being allowed to edit?
$wgAuth $wgAuth
Authentication plugin.
$wgUseMediaWikiUIEverywhere
Temporary variable that applies MediaWiki UI wherever it can be supported.
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
wfCanIPUseHTTPS( $ip)
Determine whether the client at a given source IP is likely to be able to access the wiki via HTTPS.
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
$wgUser
Definition Setup.php:806
$wgInitialSessionId
Definition Setup.php:742
if(! $wgDBerrorLogTZ) $wgRequest
Definition Setup.php:664
A special page subclass for authentication-related special pages.
getContinueAction( $action)
Gets the _CONTINUE version of an action.
isActionAllowed( $action)
Checks whether AuthManager is ready to perform the action.
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.
needsSubmitButton(array $requests)
Returns true if the form built from the given AuthenticationRequests needs a submit button.
string $subPage
Subpage of the special page.
isContinued()
Returns true if this is not the first step of the authentication.
getRequest()
Get the WebRequest being used for this instance.
trySubmit()
Attempts to do an authentication step with the submitted data.
getToken()
Returns the CSRF token.
addTabIndex(&$formDescriptor)
Adds a sequential tabindex starting from 1 to all form elements.
New base template for a skin's template extended from QuickTemplate this class features helper method...
msgWiki( $str)
An ugly, ugly hack.
getMsg( $name)
Get a Message object with its context set.
An IContextSource implementation which will inherit context from another source but allow individual ...
An error page which can definitely be safely rendered using the OutputPage.
B/C class to try handling login/signup template modifications even though login/signup does not actua...
addInputItem( $name, $value, $type, $msg, $helptext=false)
Extensions (AntiSpoof and TitleBlacklist) call this in response to UserCreateForm hook to add checkbo...
getExtraInputDefinitions()
Turns addInputItem-style field definitions into HTMLForm field definitions.
execute()
Main function, used by classes that subclass QuickTemplate to show the actual HTML output.
Object handling generic submission, CSRF protection, layout and other logic for UI forms.
Definition HTMLForm.php:128
static factory( $displayFormat)
Construct a HTMLForm object for given display type.
Definition HTMLForm.php:275
static link( $target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition Linker.php:203
static linkKnown( $target, $html=null, $customAttribs=[], $query=[], $options=[ 'known'])
Identical to link(), except $options defaults to 'known'.
Definition Linker.php:255
LoginForm as a special page has been replaced by SpecialUserLogin and SpecialCreateAccount,...
static incrementLoginThrottle( $username)
static incLoginThrottle( $username)
__construct( $request=null)
static clearLoginThrottle( $username)
static clearCreateaccountToken()
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.
Holds shared logic for login and account creation pages.
mainLoginForm(array $requests, $msg='', $msgtype='error')
canBypassForm(&$button_name)
Determine if the login form can be bypassed.
getPreservedParams( $withToken=false)
Returns URL query parameters which can be used to reload the page (or leave and return) while preserv...
logAuthResult( $success, $status=null)
Logs to the authmanager-stats channel.
onAuthChangeFormFields(array $requests, array $fieldInfo, array &$formDescriptor, $action)
Change the form descriptor that determines how a field will look in the authentication form.
loadRequestParameters( $subPage)
Load basic request parameters for this Special page.
setSessionUserForCurrentRequest()
Replace some globals to make sure the fact that the user has just been logged in is reflected in the ...
getFakeTemplate( $msg, $msgType)
Temporary B/C method to handle extensions using the UserLoginForm/UserCreateForm hooks.
showSuccessPage( $type, $title, $msgname, $injected_html, $extraMessages)
Show the success page.
getReturnToQueryStringFragment()
Returns a string that can be appended to the URL (without encoding) to preserve the return target.
User $targetUser
FIXME another flag for passing data.
successfulAction( $direct=false, $extraMessages=null)
getBCFieldDefinitions( $fieldDefinitions, $template)
Adds fields provided via the deprecated UserLoginForm / UserCreateForm hooks.
showExtraInformation()
Show extra information such as password recovery information, link from login to signup,...
getPageHtml( $formHtml)
Add page elements which are outside the form.
hasSessionCookie()
Check if a session cookie is present.
getAuthForm(array $requests, $action, $msg='', $msgType='error')
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).
makeLanguageSelectorLink( $text, $lang)
Create a language selector link for a particular language Links back to this page preserving type and...
bool $proxyAccountCreation
True if the user if creating an account for someone else.
showCreateAccountLink()
Whether the login/create account form should display a link to the other form (in addition to whateve...
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
beforeExecute( $subPage)
Gets called before.
getFieldDefinitions( $template)
Create a HTMLForm descriptor for the core login fields.
postProcessFormDescriptor(&$formDescriptor, $requests)
setRequest(array $data, $wasPosted=null)
Override the POST data, GET data from the real request is preserved.
showReturnToPage( $type, $returnTo='', $returnToQuery='', $stickHTTPS=false)
Add a "return to" link or redirect to it.
makeLanguageSelector()
Produce a bar of links which allow the user to select another language during login/registration but ...
load( $subPage)
Load data from request.
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.
PSR-3 logger instance factory.
This serves as the entry point to the MediaWiki session handling system.
Helper class for the password reset functionality shared by the web UI and the API.
static getMain()
Static methods.
static makeInternalOrExternalUrl( $name)
If url string starts with http, consider as external URL, else internal.
Definition Skin.php:1098
Parent class for all special pages.
setContext( $context)
Sets the context this SpecialPage is executed in.
getName()
Get the name of this Special Page.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
getOutput()
Get the OutputPage being used for this instance.
getUser()
Shortcut to get the User executing this instance.
getSkin()
Shortcut to get the skin being used for this instance.
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
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,...
getContext()
Gets the context this SpecialPage is executed in.
getConfig()
Shortcut to get main config object.
getPageTitle( $subpage=false)
Get a self-referential title object.
getLanguage()
Shortcut to get user's language.
msg()
Wrapper around wfMessage that sets the current context.
getFullTitle()
Return the full title, including $par.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:48
$res
Definition database.txt:21
when a variable name is used in a it is silently declared as a new local masking the global
Definition design.txt:95
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add text
Definition design.txt:18
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as $wgLang
Definition design.txt:56
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
globals will be eliminated from MediaWiki replaced by an application object which would be passed to constructors Whether that would be an convenient solution remains to be but certainly PHP makes such object oriented programming models easier than they were in previous versions For the time being MediaWiki programmers will have to work in an environment with some global context At the time of globals were initialised on startup by MediaWiki of these were configuration which are documented in DefaultSettings php There is no comprehensive documentation for the remaining however some of the most important ones are listed below They are typically initialised either in index php or in Setup php For a description of the see design txt $wgTitle Title object created from the request URL $wgOut OutputPage object for HTTP response $wgUser User object for the user associated with the current request $wgLang Language object selected by user preferences $wgContLang Language object associated with the wiki being viewed $wgParser Parser object Parser extensions register their hooks here $wgRequest WebRequest object
Definition globals.txt:64
const PROTO_HTTPS
Definition Defines.php:224
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
Definition hooks.txt:1049
the array() calling protocol came about after MediaWiki 1.4rc1.
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
Definition hooks.txt:249
namespace are movable Hooks may change this value to override the return value of MWNamespace::isMovable(). 'NewDifferenceEngine' do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached one of or reset my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect as a string mapping parameter names to values & $type
Definition hooks.txt:2568
This code would result in ircNotify being run twice when an article is and once for brion Hooks can return three possible true was required This is the default since MediaWiki *some string
Definition hooks.txt:183
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping $template
Definition hooks.txt:853
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:986
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt;div ...>$1&lt;/div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return true
Definition hooks.txt:1950
namespace are movable Hooks may change this value to override the return value of MWNamespace::isMovable(). 'NewDifferenceEngine' do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached one of or reset my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect & $returnTo
Definition hooks.txt:2566
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition hooks.txt:2685
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition hooks.txt:886
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses & $html
Definition hooks.txt:1957
Allows to change the fields on the form that will be generated are created Can be used to omit specific feeds from being outputted You must not use this hook to add use OutputPage::addFeedLink() instead. & $feedLinks hooks can tweak the array to change how login etc forms should look $requests
Definition hooks.txt:306
namespace are movable Hooks may change this value to override the return value of MWNamespace::isMovable(). 'NewDifferenceEngine' do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached one of or reset my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect as a string & $returnToQuery
Definition hooks.txt:2567
this hook is for auditing only or null if authentication failed before getting that far $username
Definition hooks.txt:807
this hook is for auditing only $response
Definition hooks.txt:805
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:304
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition hooks.txt:1595
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
Definition hooks.txt:887
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
$context
Definition load.php:50
MediaWiki has optional support for a high distributed memory object caching system For general information on but for a larger site with heavy load
Definition memcached.txt:6
This document describes the state of Postgres support in and is fairly well maintained The main code is very well while extensions are very hit and miss it is probably the most supported database after MySQL Much of the work in making MediaWiki database agnostic came about through the work of creating Postgres as and are nearing end of but without copying over all the usage comments General notes on the but these can almost always be programmed around *Although Postgres has a true BOOLEAN boolean columns are always mapped to as the code does not always treat the column as a and VARBINARY columns should simply be TEXT The only exception is when VARBINARY is used to store true binary data
Definition postgres.txt:43
$params
if(!isset( $args[0])) $lang