MediaWiki  1.27.1
LoginSignupSpecialPage.php
Go to the documentation of this file.
1 <?php
31 
38  protected $mReturnTo;
39  protected $mPosted;
40  protected $mAction;
41  protected $mLanguage;
42  protected $mReturnToQuery;
43  protected $mToken;
44  protected $mStickHTTPS;
45  protected $mFromHTTP;
46  protected $mEntryError = '';
47  protected $mEntryErrorType = 'error';
48 
49  protected $mLoaded = false;
50  protected $mLoadedRequest = false;
51  protected $mSecureLoginUrl;
52 
54  protected $securityLevel;
55 
60  protected $targetUser;
61 
63  protected $authForm;
64 
66  protected $fakeTemplate;
67 
68  abstract protected function isSignup();
69 
76  abstract protected function successfulAction( $direct = false, $extraMessages = null );
77 
83  abstract protected function logAuthResult( $success, $status = null );
84 
85  public function __construct( $name ) {
87  parent::__construct( $name );
88 
89  // Override UseMediaWikiEverywhere to true, to force login and create form to use mw ui
90  $wgUseMediaWikiUIEverywhere = true;
91  }
92 
93  protected function setRequest( array $data, $wasPosted = null ) {
94  parent::setRequest( $data, $wasPosted );
95  $this->mLoadedRequest = false;
96  }
97 
102  private function loadRequestParameters( $subPage ) {
103  if ( $this->mLoadedRequest ) {
104  return;
105  }
106  $this->mLoadedRequest = true;
107  $request = $this->getRequest();
108 
109  $this->mPosted = $request->wasPosted();
110  $this->mIsReturn = $subPage === 'return';
111  $this->mAction = $request->getVal( 'action' );
112  $this->mFromHTTP = $request->getBool( 'fromhttp', false )
113  || $request->getBool( 'wpFromhttp', false );
114  $this->mStickHTTPS = ( !$this->mFromHTTP && $request->getProtocol() === 'https' )
115  || $request->getBool( 'wpForceHttps', false );
116  $this->mLanguage = $request->getText( 'uselang' );
117  $this->mReturnTo = $request->getVal( 'returnto', '' );
118  $this->mReturnToQuery = $request->getVal( 'returntoquery', '' );
119  }
120 
126  protected function load( $subPage ) {
127  global $wgSecureLogin;
128 
130  if ( $this->mLoaded ) {
131  return;
132  }
133  $this->mLoaded = true;
134  $request = $this->getRequest();
135 
136  $securityLevel = $this->getRequest()->getText( 'force' );
137  if (
138  $securityLevel && AuthManager::singleton()->securitySensitiveOperationStatus(
139  $securityLevel ) === AuthManager::SEC_REAUTH
140  ) {
141  $this->securityLevel = $securityLevel;
142  }
143 
144  $this->loadAuth( $subPage );
145 
146  $this->mToken = $request->getVal( $this->getTokenName() );
147 
148  // Show an error or warning passed on from a previous page
149  $entryError = $this->msg( $request->getVal( 'error', '' ) );
150  $entryWarning = $this->msg( $request->getVal( 'warning', '' ) );
151  // bc: provide login link as a parameter for messages where the translation
152  // was not updated
153  $loginreqlink = Linker::linkKnown(
154  $this->getPageTitle(),
155  $this->msg( 'loginreqlink' )->escaped(),
156  [],
157  [
158  'returnto' => $this->mReturnTo,
159  'returntoquery' => $this->mReturnToQuery,
160  'uselang' => $this->mLanguage,
161  'fromhttp' => $wgSecureLogin && $this->mFromHTTP ? '1' : null,
162  ]
163  );
164 
165  // Only show valid error or warning messages.
166  if ( $entryError->exists()
167  && in_array( $entryError->getKey(), LoginHelper::getValidErrorMessages(), true )
168  ) {
169  $this->mEntryErrorType = 'error';
170  $this->mEntryError = $entryError->rawParams( $loginreqlink )->parse();
171 
172  } elseif ( $entryWarning->exists()
173  && in_array( $entryWarning->getKey(), LoginHelper::getValidErrorMessages(), true )
174  ) {
175  $this->mEntryErrorType = 'warning';
176  $this->mEntryError = $entryWarning->rawParams( $loginreqlink )->parse();
177  }
178 
179  # 1. When switching accounts, it sucks to get automatically logged out
180  # 2. Do not return to PasswordReset after a successful password change
181  # but goto Wiki start page (Main_Page) instead ( bug 33997 )
182  $returnToTitle = Title::newFromText( $this->mReturnTo );
183  if ( is_object( $returnToTitle )
184  && ( $returnToTitle->isSpecial( 'Userlogout' )
185  || $returnToTitle->isSpecial( 'PasswordReset' ) )
186  ) {
187  $this->mReturnTo = '';
188  $this->mReturnToQuery = '';
189  }
190  }
191 
192  protected function getPreservedParams( $withToken = false ) {
193  global $wgSecureLogin;
194 
195  $params = parent::getPreservedParams( $withToken );
196  $params += [
197  'returnto' => $this->mReturnTo ?: null,
198  'returntoquery' => $this->mReturnToQuery ?: null,
199  ];
200  if ( $wgSecureLogin && !$this->isSignup() ) {
201  $params['fromhttp'] = $this->mFromHTTP ? '1' : null;
202  }
203  return $params;
204  }
205 
206  protected function beforeExecute( $subPage ) {
207  // finish initializing the class before processing the request - T135924
209  return parent::beforeExecute( $subPage );
210  }
211 
215  public function execute( $subPage ) {
216  $authManager = AuthManager::singleton();
217  $session = SessionManager::getGlobalSession();
218 
219  // Session data is used for various things in the authentication process, so we must make
220  // sure a session cookie or some equivalent mechanism is set.
221  $session->persist();
222 
223  $this->load( $subPage );
224  $this->setHeaders();
225  $this->checkPermissions();
226 
227  // Make sure it's possible to log in
228  if ( !$this->isSignup() && !$session->canSetUser() ) {
229  throw new ErrorPageError( 'cannotloginnow-title', 'cannotloginnow-text', [
230  $session->getProvider()->describe( RequestContext::getMain()->getLanguage() )
231  ] );
232  }
233 
234  /*
235  * In the case where the user is already logged in, and was redirected to
236  * the login form from a page that requires login, do not show the login
237  * page. The use case scenario for this is when a user opens a large number
238  * of tabs, is redirected to the login page on all of them, and then logs
239  * in on one, expecting all the others to work properly.
240  *
241  * However, do show the form if it was visited intentionally (no 'returnto'
242  * is present). People who often switch between several accounts have grown
243  * accustomed to this behavior.
244  *
245  * Also make an exception when force=<level> is set in the URL, which means the user must
246  * reauthenticate for security reasons.
247  */
248  if ( !$this->isSignup() && !$this->mPosted && !$this->securityLevel &&
249  ( $this->mReturnTo !== '' || $this->mReturnToQuery !== '' ) &&
250  $this->getUser()->isLoggedIn()
251  ) {
252  $this->successfulAction();
253  }
254 
255  // If logging in and not on HTTPS, either redirect to it or offer a link.
256  global $wgSecureLogin;
257  if ( $this->getRequest()->getProtocol() !== 'https' ) {
258  $title = $this->getFullTitle();
259  $query = $this->getPreservedParams( false ) + [
260  'title' => null,
261  ( $this->mEntryErrorType === 'error' ? 'error'
262  : 'warning' ) => $this->mEntryError,
263  ] + $this->getRequest()->getQueryValues();
264  $url = $title->getFullURL( $query, false, PROTO_HTTPS );
265  if ( $wgSecureLogin && !$this->mFromHTTP &&
266  wfCanIPUseHTTPS( $this->getRequest()->getIP() )
267  ) {
268  // Avoid infinite redirect
269  $url = wfAppendQuery( $url, 'fromhttp=1' );
270  $this->getOutput()->redirect( $url );
271  // Since we only do this redir to change proto, always vary
272  $this->getOutput()->addVaryHeader( 'X-Forwarded-Proto' );
273 
274  return;
275  } else {
276  // A wiki without HTTPS login support should set $wgServer to
277  // http://somehost, in which case the secure URL generated
278  // above won't actually start with https://
279  if ( substr( $url, 0, 8 ) === 'https://' ) {
280  $this->mSecureLoginUrl = $url;
281  }
282  }
283  }
284 
285  if ( !$this->isActionAllowed( $this->authAction ) ) {
286  // FIXME how do we explain this to the user? can we handle session loss better?
287  // messages used: authpage-cannot-login, authpage-cannot-login-continue,
288  // authpage-cannot-create, authpage-cannot-create-continue
289  $this->mainLoginForm( [], 'authpage-cannot-' . $this->authAction );
290  return;
291  }
292 
293  $status = $this->trySubmit();
294 
295  if ( !$status || !$status->isGood() ) {
296  $this->mainLoginForm( $this->authRequests, $status ? $status->getMessage() : '', 'error' );
297  return;
298  }
299 
301  $response = $status->getValue();
302 
303  $returnToUrl = $this->getPageTitle( 'return' )
304  ->getFullURL( $this->getPreservedParams( true ), false, PROTO_HTTPS );
305  switch ( $response->status ) {
306  case AuthenticationResponse::PASS:
307  $this->logAuthResult( true );
308  $this->proxyAccountCreation = $this->isSignup() && !$this->getUser()->isAnon();
309  $this->targetUser = User::newFromName( $response->username );
310 
311  if (
312  !$this->proxyAccountCreation
313  && $response->loginRequest
314  && $authManager->canAuthenticateNow()
315  ) {
316  // successful registration; log the user in instantly
317  $response2 = $authManager->beginAuthentication( [ $response->loginRequest ],
318  $returnToUrl );
319  if ( $response2->status !== AuthenticationResponse::PASS ) {
320  LoggerFactory::getInstance( 'login' )
321  ->error( 'Could not log in after account creation' );
322  $this->successfulAction( true, Status::newFatal( 'createacct-loginerror' ) );
323  break;
324  }
325  }
326 
327  if ( !$this->proxyAccountCreation ) {
328  // Ensure that the context user is the same as the session user.
330  }
331 
332  $this->successfulAction( true );
333  break;
334  case AuthenticationResponse::FAIL:
335  // fall through
336  case AuthenticationResponse::RESTART:
337  unset( $this->authForm );
338  if ( $response->status === AuthenticationResponse::FAIL ) {
339  $action = $this->getDefaultAction( $subPage );
340  $messageType = 'error';
341  } else {
342  $action = $this->getContinueAction( $this->authAction );
343  $messageType = 'warning';
344  }
345  $this->logAuthResult( false, $response->message ? $response->message->getKey() : '-' );
346  $this->loadAuth( $subPage, $action, true );
347  $this->mainLoginForm( $this->authRequests, $response->message, $messageType );
348  break;
349  case AuthenticationResponse::REDIRECT:
350  unset( $this->authForm );
351  $this->getOutput()->redirect( $response->redirectTarget );
352  break;
353  case AuthenticationResponse::UI:
354  unset( $this->authForm );
355  $this->authAction = $this->isSignup() ? AuthManager::ACTION_CREATE_CONTINUE
356  : AuthManager::ACTION_LOGIN_CONTINUE;
357  $this->authRequests = $response->neededRequests;
358  $this->mainLoginForm( $response->neededRequests, $response->message, 'warning' );
359  break;
360  default:
361  throw new LogicException( 'invalid AuthenticationResponse' );
362  }
363  }
364 
374  protected function showSuccessPage(
375  $type, $title, $msgname, $injected_html, $extraMessages
376  ) {
377  $out = $this->getOutput();
378  $out->setPageTitle( $title );
379  if ( $msgname ) {
380  $out->addWikiMsg( $msgname, wfEscapeWikiText( $this->getUser()->getName() ) );
381  }
382  if ( $extraMessages ) {
383  $extraMessages = Status::wrap( $extraMessages );
384  $out->addWikiText( $extraMessages->getWikiText() );
385  }
386 
387  $out->addHTML( $injected_html );
388 
389  $helper = new LoginHelper( $this->getContext() );
390  $helper->showReturnToPage( $type, $this->mReturnTo, $this->mReturnToQuery, $this->mStickHTTPS );
391  }
392 
408  public function showReturnToPage(
409  $type, $returnTo = '', $returnToQuery = '', $stickHTTPS = false
410  ) {
411  $helper = new LoginHelper( $this->getContext() );
412  $helper->showReturnToPage( $type, $returnTo, $returnToQuery, $stickHTTPS );
413  }
414 
420  protected function setSessionUserForCurrentRequest() {
422 
424  $localContext = $this->getContext();
425  if ( $context !== $localContext ) {
426  // remove AuthManagerSpecialPage context hack
427  $this->setContext( $context );
428  }
429 
430  $user = $context->getRequest()->getSession()->getUser();
431 
432  $wgUser = $user;
433  $context->setUser( $user );
434 
435  $code = $this->getRequest()->getVal( 'uselang', $user->getOption( 'language' ) );
436  $userLang = Language::factory( $code );
437  $wgLang = $userLang;
438  $context->setLanguage( $userLang );
439  }
440 
455  protected function mainLoginForm( array $requests, $msg = '', $msgtype = 'error' ) {
456  $titleObj = $this->getPageTitle();
457  $user = $this->getUser();
458  $out = $this->getOutput();
459 
460  // FIXME how to handle empty $requests - restart, or no form, just an error message?
461  // no form would be better for no session type errors, restart is better when can* fails.
462  if ( !$requests ) {
463  $this->authAction = $this->getDefaultAction( $this->subPage );
464  $this->authForm = null;
465  $requests = AuthManager::singleton()->getAuthenticationRequests( $this->authAction, $user );
466  }
467 
468  // Generic styles and scripts for both login and signup form
469  $out->addModuleStyles( [
470  'mediawiki.ui',
471  'mediawiki.ui.button',
472  'mediawiki.ui.checkbox',
473  'mediawiki.ui.input',
474  'mediawiki.special.userlogin.common.styles'
475  ] );
476  if ( $this->isSignup() ) {
477  // XXX hack pending RL or JS parse() support for complex content messages T27349
478  $out->addJsConfigVars( 'wgCreateacctImgcaptchaHelp',
479  $this->msg( 'createacct-imgcaptcha-help' )->parse() );
480 
481  // Additional styles and scripts for signup form
482  $out->addModules( [
483  'mediawiki.special.userlogin.signup.js'
484  ] );
485  $out->addModuleStyles( [
486  'mediawiki.special.userlogin.signup.styles'
487  ] );
488  } else {
489  // Additional styles for login form
490  $out->addModuleStyles( [
491  'mediawiki.special.userlogin.login.styles'
492  ] );
493  }
494  $out->disallowUserJs(); // just in case...
495 
496  $form = $this->getAuthForm( $requests, $this->authAction, $msg, $msgtype );
497  $form->prepareForm();
498  $formHtml = $form->getHTML( $msg ? Status::newFatal( $msg ) : false );
499 
500  $out->addHTML( $this->getPageHtml( $formHtml ) );
501  }
502 
509  protected function getPageHtml( $formHtml ) {
511 
512  $loginPrompt = $this->isSignup() ? '' : Html::rawElement( 'div',
513  [ 'id' => 'userloginprompt' ], $this->msg( 'loginprompt' )->parseAsBlock() );
514  $languageLinks = $wgLoginLanguageSelector ? $this->makeLanguageSelector() : '';
515  $signupStartMsg = $this->msg( 'signupstart' );
516  $signupStart = ( $this->isSignup() && !$signupStartMsg->isDisabled() )
517  ? Html::rawElement( 'div', [ 'id' => 'signupstart' ], $signupStartMsg->parseAsBlock() ) : '';
518  if ( $languageLinks ) {
519  $languageLinks = Html::rawElement( 'div', [ 'id' => 'languagelinks' ],
520  Html::rawElement( 'p', [], $languageLinks )
521  );
522  }
523 
524  $benefitsContainer = '';
525  if ( $this->isSignup() && $this->showExtraInformation() ) {
526  // messages used:
527  // createacct-benefit-icon1 createacct-benefit-head1 createacct-benefit-body1
528  // createacct-benefit-icon2 createacct-benefit-head2 createacct-benefit-body2
529  // createacct-benefit-icon3 createacct-benefit-head3 createacct-benefit-body3
530  $benefitCount = 3;
531  $benefitList = '';
532  for ( $benefitIdx = 1; $benefitIdx <= $benefitCount; $benefitIdx++ ) {
533  $headUnescaped = $this->msg( "createacct-benefit-head$benefitIdx" )->text();
534  $iconClass = $this->msg( "createacct-benefit-icon$benefitIdx" )->escaped();
535  $benefitList .= Html::rawElement( 'div', [ 'class' => "mw-number-text $iconClass" ],
536  Html::rawElement( 'h3', [],
537  $this->msg( "createacct-benefit-head$benefitIdx" )->escaped()
538  )
539  . Html::rawElement( 'p', [],
540  $this->msg( "createacct-benefit-body$benefitIdx" )->params( $headUnescaped )->escaped()
541  )
542  );
543  }
544  $benefitsContainer = Html::rawElement( 'div', [ 'class' => 'mw-createacct-benefits-container' ],
545  Html::rawElement( 'h2', [], $this->msg( 'createacct-benefit-heading' )->escaped() )
546  . Html::rawElement( 'div', [ 'class' => 'mw-createacct-benefits-list' ],
547  $benefitList
548  )
549  );
550  }
551 
552  $html = Html::rawElement( 'div', [ 'class' => 'mw-ui-container' ],
553  $loginPrompt
554  . $languageLinks
555  . $signupStart
556  . Html::rawElement( 'div', [ 'id' => 'userloginForm' ],
557  $formHtml
558  )
559  . $benefitsContainer
560  );
561 
562  return $html;
563  }
564 
573  protected function getAuthForm( array $requests, $action, $msg = '', $msgType = 'error' ) {
574  global $wgSecureLogin, $wgLoginLanguageSelector;
575  // FIXME merge this with parent
576 
577  if ( isset( $this->authForm ) ) {
578  return $this->authForm;
579  }
580 
581  $usingHTTPS = $this->getRequest()->getProtocol() === 'https';
582 
583  // get basic form description from the auth logic
584  $fieldInfo = AuthenticationRequest::mergeFieldInfo( $requests );
585  $fakeTemplate = $this->getFakeTemplate( $msg, $msgType );
586  $this->fakeTemplate = $fakeTemplate; // FIXME there should be a saner way to pass this to the hook
587  // this will call onAuthChangeFormFields()
588  $formDescriptor = static::fieldInfoToFormDescriptor( $requests, $fieldInfo, $this->authAction );
589  $this->postProcessFormDescriptor( $formDescriptor );
590 
591  $context = $this->getContext();
592  if ( $context->getRequest() !== $this->getRequest() ) {
593  // We have overridden the request, need to make sure the form uses that too.
594  $context = new DerivativeContext( $this->getContext() );
595  $context->setRequest( $this->getRequest() );
596  }
597  $form = HTMLForm::factory( 'vform', $formDescriptor, $context );
598 
599  $form->addHiddenField( 'authAction', $this->authAction );
600  if ( $wgLoginLanguageSelector ) {
601  $form->addHiddenField( 'uselang', $this->mLanguage );
602  }
603  $form->addHiddenField( 'force', $this->securityLevel );
604  $form->addHiddenField( $this->getTokenName(), $this->getToken()->toString() );
605  if ( $wgSecureLogin ) {
606  // If using HTTPS coming from HTTP, then the 'fromhttp' parameter must be preserved
607  if ( !$this->isSignup() ) {
608  $form->addHiddenField( 'wpForceHttps', (int)$this->mStickHTTPS );
609  $form->addHiddenField( 'wpFromhttp', $usingHTTPS );
610  }
611  }
612 
613  // set properties of the form itself
614  $form->setAction( $this->getPageTitle()->getLocalURL( $this->getReturnToQueryStringFragment() ) );
615  $form->setName( 'userlogin' . ( $this->isSignup() ? '2' : '' ) );
616  if ( $this->isSignup() ) {
617  $form->setId( 'userlogin2' );
618  }
619 
620  // add pre/post text
621  // header used by ConfirmEdit, CondfirmAccount, Persona, WikimediaIncubator, SemanticSignup
622  // should be above the error message but HTMLForm doesn't support that
623  $form->addHeaderText( $fakeTemplate->get( 'header' ) );
624 
625  // FIXME the old form used this for error/warning messages which does not play well with
626  // HTMLForm (maybe it could with a subclass?); for now only display it for signups
627  // (where the JS username validation needs it) and alway empty
628  if ( $this->isSignup() ) {
629  // used by the mediawiki.special.userlogin.signup.js module
630  $statusAreaAttribs = [ 'id' => 'mw-createacct-status-area' ];
631  // $statusAreaAttribs += $msg ? [ 'class' => "{$msgType}box" ] : [ 'style' => 'display: none;' ];
632  $form->addHeaderText( Html::element( 'div', $statusAreaAttribs ) );
633  }
634 
635  // header used by MobileFrontend
636  $form->addHeaderText( $fakeTemplate->get( 'formheader' ) );
637 
638  // blank signup footer for site customization
639  if ( $this->isSignup() && $this->showExtraInformation() ) {
640  // Use signupend-https for HTTPS requests if it's not blank, signupend otherwise
641  $signupendMsg = $this->msg( 'signupend' );
642  $signupendHttpsMsg = $this->msg( 'signupend-https' );
643  if ( !$signupendMsg->isDisabled() ) {
644  $signupendText = ( $usingHTTPS && !$signupendHttpsMsg->isBlank() )
645  ? $signupendHttpsMsg ->parse() : $signupendMsg->parse();
646  $form->addPostText( Html::rawElement( 'div', [ 'id' => 'signupend' ], $signupendText ) );
647  }
648  }
649 
650  // warning header for non-standard workflows (e.g. security reauthentication)
651  if ( !$this->isSignup() && $this->getUser()->isLoggedIn() ) {
652  $reauthMessage = $this->securityLevel ? 'userlogin-reauth' : 'userlogin-loggedin';
653  $form->addHeaderText( Html::rawElement( 'div', [ 'class' => 'warningbox' ],
654  $this->msg( $reauthMessage )->params( $this->getUser()->getName() )->parse() ) );
655  }
656 
657  if ( !$this->isSignup() && $this->showExtraInformation() ) {
658  $passwordReset = new PasswordReset( $this->getConfig(), AuthManager::singleton() );
659  if ( $passwordReset->isAllowed( $this->getUser() ) ) {
660  $form->addFooterText( Html::rawElement(
661  'div',
662  [ 'class' => 'mw-ui-vform-field mw-form-related-link-container' ],
663  Linker::link(
664  SpecialPage::getTitleFor( 'PasswordReset' ),
665  $this->msg( 'userlogin-resetpassword-link' )->escaped()
666  )
667  ) );
668  }
669 
670  // Don't show a "create account" link if the user can't.
671  if ( $this->showCreateAccountLink() ) {
672  // link to the other action
673  $linkTitle = $this->getTitleFor( $this->isSignup() ? 'Userlogin' :'CreateAccount' );
674  $linkq = $this->getReturnToQueryStringFragment();
675  // Pass any language selection on to the mode switch link
676  if ( $wgLoginLanguageSelector && $this->mLanguage ) {
677  $linkq .= '&uselang=' . $this->mLanguage;
678  }
679  $createOrLoginHref = $linkTitle->getLocalURL( $linkq );
680 
681  if ( $this->getUser()->isLoggedIn() ) {
682  $createOrLoginHtml = Html::rawElement( 'div',
683  [ 'class' => 'mw-ui-vform-field' ],
684  Html::element( 'a',
685  [
686  'id' => 'mw-createaccount-join',
687  'href' => $createOrLoginHref,
688  // put right after all auth inputs in the tab order
689  'tabindex' => 100,
690  ],
691  $this->msg( 'userlogin-createanother' )->escaped()
692  )
693  );
694  } else {
695  $createOrLoginHtml = Html::rawElement( 'div',
696  [ 'id' => 'mw-createaccount-cta',
697  'class' => 'mw-ui-vform-field' ],
698  $this->msg( 'userlogin-noaccount' )->escaped()
699  . Html::element( 'a',
700  [
701  'id' => 'mw-createaccount-join',
702  'href' => $createOrLoginHref,
703  'class' => 'mw-ui-button',
704  'tabindex' => 100,
705  ],
706  $this->msg( 'userlogin-joinproject' )->escaped()
707  )
708  );
709  }
710  $form->addFooterText( $createOrLoginHtml );
711  }
712  }
713 
714  $form->suppressDefaultSubmit();
715 
716  $this->authForm = $form;
717 
718  return $form;
719  }
720 
727  protected function getFakeTemplate( $msg, $msgType ) {
728  global $wgAuth, $wgEnableEmail, $wgHiddenPrefs, $wgEmailConfirmToEdit, $wgEnableUserEmail,
729  $wgSecureLogin, $wgLoginLanguageSelector, $wgPasswordResetRoutes;
730 
731  // make a best effort to get the value of fields which used to be fixed in the old login
732  // template but now might or might not exist depending on what providers are used
733  $request = $this->getRequest();
734  $data = (object) [
735  'mUsername' => $request->getText( 'wpName' ),
736  'mPassword' => $request->getText( 'wpPassword' ),
737  'mRetype' => $request->getText( 'wpRetype' ),
738  'mEmail' => $request->getText( 'wpEmail' ),
739  'mRealName' => $request->getText( 'wpRealName' ),
740  'mDomain' => $request->getText( 'wpDomain' ),
741  'mReason' => $request->getText( 'wpReason' ),
742  'mRemember' => $request->getCheck( 'wpRemember' ),
743  ];
744 
745  // Preserves a bunch of logic from the old code that was rewritten in getAuthForm().
746  // There is no code reuse to make this easier to remove .
747  // If an extension tries to change any of these values, they are out of luck - we only
748  // actually use the domain/usedomain/domainnames, extraInput and extrafields keys.
749 
750  $titleObj = $this->getPageTitle();
751  $user = $this->getUser();
752  $template = new FakeAuthTemplate();
753 
754  // Pre-fill username (if not creating an account, bug 44775).
755  if ( $data->mUsername == '' && $this->isSignup() ) {
756  if ( $user->isLoggedIn() ) {
757  $data->mUsername = $user->getName();
758  } else {
759  $data->mUsername = $this->getRequest()->getSession()->suggestLoginUsername();
760  }
761  }
762 
763  if ( $this->isSignup() ) {
764  // Must match number of benefits defined in messages
765  $template->set( 'benefitCount', 3 );
766 
767  $q = 'action=submitlogin&type=signup';
768  $linkq = 'type=login';
769  } else {
770  $q = 'action=submitlogin&type=login';
771  $linkq = 'type=signup';
772  }
773 
774  if ( $this->mReturnTo !== '' ) {
775  $returnto = '&returnto=' . wfUrlencode( $this->mReturnTo );
776  if ( $this->mReturnToQuery !== '' ) {
777  $returnto .= '&returntoquery=' .
778  wfUrlencode( $this->mReturnToQuery );
779  }
780  $q .= $returnto;
781  $linkq .= $returnto;
782  }
783 
784  # Don't show a "create account" link if the user can't.
785  if ( $this->showCreateAccountLink() ) {
786  # Pass any language selection on to the mode switch link
787  if ( $wgLoginLanguageSelector && $this->mLanguage ) {
788  $linkq .= '&uselang=' . $this->mLanguage;
789  }
790  // Supply URL, login template creates the button.
791  $template->set( 'createOrLoginHref', $titleObj->getLocalURL( $linkq ) );
792  } else {
793  $template->set( 'link', '' );
794  }
795 
796  $resetLink = $this->isSignup()
797  ? null
798  : is_array( $wgPasswordResetRoutes )
799  && in_array( true, array_values( $wgPasswordResetRoutes ), true );
800 
801  $template->set( 'header', '' );
802  $template->set( 'formheader', '' );
803  $template->set( 'skin', $this->getSkin() );
804 
805  $template->set( 'name', $data->mUsername );
806  $template->set( 'password', $data->mPassword );
807  $template->set( 'retype', $data->mRetype );
808  $template->set( 'createemailset', false ); // no easy way to get that from AuthManager
809  $template->set( 'email', $data->mEmail );
810  $template->set( 'realname', $data->mRealName );
811  $template->set( 'domain', $data->mDomain );
812  $template->set( 'reason', $data->mReason );
813  $template->set( 'remember', $data->mRemember );
814 
815  $template->set( 'action', $titleObj->getLocalURL( $q ) );
816  $template->set( 'message', $msg );
817  $template->set( 'messagetype', $msgType );
818  $template->set( 'createemail', $wgEnableEmail && $user->isLoggedIn() );
819  $template->set( 'userealname', !in_array( 'realname', $wgHiddenPrefs, true ) );
820  $template->set( 'useemail', $wgEnableEmail );
821  $template->set( 'emailrequired', $wgEmailConfirmToEdit );
822  $template->set( 'emailothers', $wgEnableUserEmail );
823  $template->set( 'canreset', $wgAuth->allowPasswordChange() );
824  $template->set( 'resetlink', $resetLink );
825  $template->set( 'canremember', $request->getSession()->getProvider()
826  ->getRememberUserDuration() !== null );
827  $template->set( 'usereason', $user->isLoggedIn() );
828  $template->set( 'cansecurelogin', ( $wgSecureLogin ) );
829  $template->set( 'stickhttps', (int)$this->mStickHTTPS );
830  $template->set( 'loggedin', $user->isLoggedIn() );
831  $template->set( 'loggedinuser', $user->getName() );
832  $template->set( 'token', $this->getToken()->toString() );
833 
834  $action = $this->isSignup() ? 'signup' : 'login';
835  $wgAuth->modifyUITemplate( $template, $action );
836 
837  $oldTemplate = $template;
838  $hookName = $this->isSignup() ? 'UserCreateForm' : 'UserLoginForm';
839  Hooks::run( $hookName, [ &$template ] );
840  if ( $oldTemplate !== $template ) {
841  wfDeprecated( "reference in $hookName hook", '1.27' );
842  }
843 
844  return $template;
845 
846  }
847 
848  public function onAuthChangeFormFields(
849  array $requests, array $fieldInfo, array &$formDescriptor, $action
850  ) {
851  $coreFieldDescriptors = $this->getFieldDefinitions( $this->fakeTemplate );
852  $specialFields = array_merge( [ 'extraInput', 'linkcontainer', 'entryError' ],
853  array_keys( $this->fakeTemplate->getExtraInputDefinitions() ) );
854 
855  // keep the ordering from getCoreFieldDescriptors() where there is no explicit weight
856  foreach ( $coreFieldDescriptors as $fieldName => $coreField ) {
857  $requestField = isset( $formDescriptor[$fieldName] ) ?
858  $formDescriptor[$fieldName] : [];
859 
860  // remove everything that is not in the fieldinfo, is not marked as a supplemental field
861  // to something in the fieldinfo, and is not a generic or B/C field or a submit button
862  if (
863  !isset( $fieldInfo[$fieldName] )
864  && (
865  !isset( $coreField['baseField'] )
866  || !isset( $fieldInfo[$coreField['baseField']] )
867  ) && !in_array( $fieldName, $specialFields, true )
868  && ( !isset( $coreField['type'] ) || $coreField['type'] !== 'submit' )
869  ) {
870  $coreFieldDescriptors[$fieldName] = null;
871  continue;
872  }
873 
874  // core message labels should always take priority
875  if (
876  isset( $coreField['label'] )
877  || isset( $coreField['label-message'] )
878  || isset( $coreField['label-raw'] )
879  ) {
880  unset( $requestField['label'], $requestField['label-message'], $coreField['label-raw'] );
881  }
882 
883  $coreFieldDescriptors[$fieldName] += $requestField;
884  }
885 
886  $formDescriptor = array_filter( $coreFieldDescriptors + $formDescriptor );
887  return true;
888  }
889 
896  protected function showExtraInformation() {
897  return $this->authAction !== $this->getContinueAction( $this->authAction )
899  }
900 
906  protected function getFieldDefinitions( $template ) {
907  global $wgEmailConfirmToEdit;
908 
909  $isLoggedIn = $this->getUser()->isLoggedIn();
910  $continuePart = $this->isContinued() ? 'continue-' : '';
911  $anotherPart = $isLoggedIn ? 'another-' : '';
912  $expiration = $this->getRequest()->getSession()->getProvider()
913  ->getRememberUserDuration();
914  $expirationDays = ceil( $expiration / ( 3600 * 24 ) );
915  $secureLoginLink = '';
916  if ( $this->mSecureLoginUrl ) {
917  $secureLoginLink = Html::element( 'a', [
918  'href' => $this->mSecureLoginUrl,
919  'class' => 'mw-ui-flush-right mw-secure',
920  ], $this->msg( 'userlogin-signwithsecure' )->text() );
921  }
922 
923  if ( $this->isSignup() ) {
924  $fieldDefinitions = [
925  'username' => [
926  'label-message' => 'userlogin-yourname',
927  // FIXME help-message does not match old formatting
928  'help-message' => 'createacct-helpusername',
929  'id' => 'wpName2',
930  'placeholder-message' => $isLoggedIn ? 'createacct-another-username-ph'
931  : 'userlogin-yourname-ph',
932  ],
933  'mailpassword' => [
934  // create account without providing password, a temporary one will be mailed
935  'type' => 'check',
936  'label-message' => 'createaccountmail',
937  'name' => 'wpCreateaccountMail',
938  'id' => 'wpCreateaccountMail',
939  ],
940  'password' => [
941  'id' => 'wpPassword2',
942  'placeholder-message' => 'createacct-yourpassword-ph',
943  'hide-if' => [ '===', 'wpCreateaccountMail', '1' ],
944  ],
945  'domain' => [],
946  'retype' => [
947  'baseField' => 'password',
948  'type' => 'password',
949  'label-message' => 'createacct-yourpasswordagain',
950  'id' => 'wpRetype',
951  'cssclass' => 'loginPassword',
952  'size' => 20,
953  'validation-callback' => function ( $value, $alldata ) {
954  if ( empty( $alldata['mailpassword'] ) && !empty( $alldata['password'] ) ) {
955  if ( !$value ) {
956  return $this->msg( 'htmlform-required' );
957  } elseif ( $value !== $alldata['password'] ) {
958  return $this->msg( 'badretype' );
959  }
960  }
961  return true;
962  },
963  'hide-if' => [ '===', 'wpCreateaccountMail', '1' ],
964  'placeholder-message' => 'createacct-yourpasswordagain-ph',
965  ],
966  'email' => [
967  'type' => 'email',
968  'label-message' => $wgEmailConfirmToEdit ? 'createacct-emailrequired'
969  : 'createacct-emailoptional',
970  'id' => 'wpEmail',
971  'cssclass' => 'loginText',
972  'size' => '20',
973  // FIXME will break non-standard providers
974  'required' => $wgEmailConfirmToEdit,
975  'validation-callback' => function ( $value, $alldata ) {
976  global $wgEmailConfirmToEdit;
977 
978  // AuthManager will check most of these, but that will make the auth
979  // session fail and this won't, so nicer to do it this way
980  if ( !$value && $wgEmailConfirmToEdit ) {
981  // no point in allowing registration without email when email is
982  // required to edit
983  return $this->msg( 'noemailtitle' );
984  } elseif ( !$value && !empty( $alldata['mailpassword'] ) ) {
985  // cannot send password via email when there is no email address
986  return $this->msg( 'noemailcreate' );
987  } elseif ( $value && !Sanitizer::validateEmail( $value ) ) {
988  return $this->msg( 'invalidemailaddress' );
989  }
990  return true;
991  },
992  'placeholder-message' => 'createacct-' . $anotherPart . 'email-ph',
993  ],
994  'realname' => [
995  'type' => 'text',
996  'help-message' => $isLoggedIn ? 'createacct-another-realname-tip'
997  : 'prefs-help-realname',
998  'label-message' => 'createacct-realname',
999  'cssclass' => 'loginText',
1000  'size' => 20,
1001  'id' => 'wpRealName',
1002  ],
1003  'reason' => [
1004  // comment for the user creation log
1005  'type' => 'text',
1006  'label-message' => 'createacct-reason',
1007  'cssclass' => 'loginText',
1008  'id' => 'wpReason',
1009  'size' => '20',
1010  'placeholder-message' => 'createacct-reason-ph',
1011  ],
1012  'extrainput' => [], // placeholder for fields coming from the template
1013  'createaccount' => [
1014  // submit button
1015  'type' => 'submit',
1016  'default' => $this->msg( 'createacct-' . $anotherPart . $continuePart .
1017  'submit' )->text(),
1018  'name' => 'wpCreateaccount',
1019  'id' => 'wpCreateaccount',
1020  'weight' => 100,
1021  ],
1022  ];
1023  } else {
1024  $fieldDefinitions = [
1025  'username' => [
1026  'label-raw' => $this->msg( 'userlogin-yourname' )->escaped() . $secureLoginLink,
1027  'id' => 'wpName1',
1028  'placeholder-message' => 'userlogin-yourname-ph',
1029  ],
1030  'password' => [
1031  'id' => 'wpPassword1',
1032  'placeholder-message' => 'userlogin-yourpassword-ph',
1033  ],
1034  'domain' => [],
1035  'extrainput' => [],
1036  'rememberMe' => [
1037  // option for saving the user token to a cookie
1038  'type' => 'check',
1039  'name' => 'wpRemember',
1040  'label-message' => $this->msg( 'userlogin-remembermypassword' )
1041  ->numParams( $expirationDays ),
1042  'id' => 'wpRemember',
1043  ],
1044  'loginattempt' => [
1045  // submit button
1046  'type' => 'submit',
1047  'default' => $this->msg( 'pt-login-' . $continuePart . 'button' )->text(),
1048  'id' => 'wpLoginAttempt',
1049  'weight' => 100,
1050  ],
1051  'linkcontainer' => [
1052  // help link
1053  'type' => 'info',
1054  'cssclass' => 'mw-form-related-link-container mw-userlogin-help',
1055  // 'id' => 'mw-userlogin-help', // FIXME HTMLInfoField ignores this
1056  'raw' => true,
1057  'default' => Html::element( 'a', [
1058  'href' => Skin::makeInternalOrExternalUrl( wfMessage( 'helplogin-url' )
1059  ->inContentLanguage()
1060  ->text() ),
1061  ], $this->msg( 'userlogin-helplink2' )->text() ),
1062  'weight' => 200,
1063  ],
1064  // button for ResetPasswordSecondaryAuthenticationProvider
1065  'skipReset' => [
1066  'weight' => 110,
1067  'flags' => [],
1068  ],
1069  ];
1070  }
1071  $fieldDefinitions['username'] += [
1072  'type' => 'text',
1073  'name' => 'wpName',
1074  'cssclass' => 'loginText',
1075  'size' => 20,
1076  // 'required' => true,
1077  ];
1078  $fieldDefinitions['password'] += [
1079  'type' => 'password',
1080  // 'label-message' => 'userlogin-yourpassword', // would override the changepassword label
1081  'name' => 'wpPassword',
1082  'cssclass' => 'loginPassword',
1083  'size' => 20,
1084  // 'required' => true,
1085  ];
1086 
1087  if ( $this->mEntryError ) {
1088  $fieldDefinitions['entryError'] = [
1089  'type' => 'info',
1090  'default' => Html::rawElement( 'div', [ 'class' => $this->mEntryErrorType . 'box', ],
1091  $this->mEntryError ),
1092  'raw' => true,
1093  'rawrow' => true,
1094  'weight' => -100,
1095  ];
1096  }
1097 
1098  if ( !$this->showExtraInformation() ) {
1099  unset( $fieldDefinitions['linkcontainer'] );
1100  }
1101 
1102  $fieldDefinitions = $this->getBCFieldDefinitions( $fieldDefinitions, $template );
1103  $fieldDefinitions = array_filter( $fieldDefinitions );
1104 
1105  return $fieldDefinitions;
1106  }
1107 
1114  protected function getBCFieldDefinitions( $fieldDefinitions, $template ) {
1115  if ( $template->get( 'usedomain', false ) ) {
1116  // TODO probably should be translated to the new domain notation in AuthManager
1117  $fieldDefinitions['domain'] = [
1118  'type' => 'select',
1119  'label-message' => 'yourdomainname',
1120  'options' => array_combine( $template->get( 'domainnames', [] ),
1121  $template->get( 'domainnames', [] ) ),
1122  'default' => $template->get( 'domain', '' ),
1123  'name' => 'wpDomain',
1124  // FIXME id => 'mw-user-domain-section' on the parent div
1125  ];
1126  }
1127 
1128  // poor man's associative array_splice
1129  $extraInputPos = array_search( 'extrainput', array_keys( $fieldDefinitions ), true );
1130  $fieldDefinitions = array_slice( $fieldDefinitions, 0, $extraInputPos, true )
1131  + $template->getExtraInputDefinitions()
1132  + array_slice( $fieldDefinitions, $extraInputPos + 1, null, true );
1133 
1134  return $fieldDefinitions;
1135  }
1136 
1146  protected function hasSessionCookie() {
1147  global $wgDisableCookieCheck, $wgInitialSessionId;
1148 
1149  return $wgDisableCookieCheck || (
1150  $wgInitialSessionId &&
1151  $this->getRequest()->getSession()->getId() === (string)$wgInitialSessionId
1152  );
1153  }
1154 
1159  protected function getReturnToQueryStringFragment() {
1160  $returnto = '';
1161  if ( $this->mReturnTo !== '' ) {
1162  $returnto = 'returnto=' . wfUrlencode( $this->mReturnTo );
1163  if ( $this->mReturnToQuery !== '' ) {
1164  $returnto .= '&returntoquery=' . wfUrlencode( $this->mReturnToQuery );
1165  }
1166  }
1167  return $returnto;
1168  }
1169 
1175  private function showCreateAccountLink() {
1176  if ( $this->isSignup() ) {
1177  return true;
1178  } elseif ( $this->getUser()->isAllowed( 'createaccount' ) ) {
1179  return true;
1180  } else {
1181  return false;
1182  }
1183  }
1184 
1185  protected function getTokenName() {
1186  return $this->isSignup() ? 'wpCreateaccountToken' : 'wpLoginToken';
1187  }
1188 
1195  protected function makeLanguageSelector() {
1196  $msg = $this->msg( 'loginlanguagelinks' )->inContentLanguage();
1197  if ( $msg->isBlank() ) {
1198  return '';
1199  }
1200  $langs = explode( "\n", $msg->text() );
1201  $links = [];
1202  foreach ( $langs as $lang ) {
1203  $lang = trim( $lang, '* ' );
1204  $parts = explode( '|', $lang );
1205  if ( count( $parts ) >= 2 ) {
1206  $links[] = $this->makeLanguageSelectorLink( $parts[0], trim( $parts[1] ) );
1207  }
1208  }
1209 
1210  return count( $links ) > 0 ? $this->msg( 'loginlanguagelabel' )->rawParams(
1211  $this->getLanguage()->pipeList( $links ) )->escaped() : '';
1212  }
1213 
1222  protected function makeLanguageSelectorLink( $text, $lang ) {
1223  if ( $this->getLanguage()->getCode() == $lang ) {
1224  // no link for currently used language
1225  return htmlspecialchars( $text );
1226  }
1227  $query = [ 'uselang' => $lang ];
1228  if ( $this->mReturnTo !== '' ) {
1229  $query['returnto'] = $this->mReturnTo;
1230  $query['returntoquery'] = $this->mReturnToQuery;
1231  }
1232 
1233  $attr = [];
1234  $targetLanguage = Language::factory( $lang );
1235  $attr['lang'] = $attr['hreflang'] = $targetLanguage->getHtmlCode();
1236 
1237  return Linker::linkKnown(
1238  $this->getPageTitle(),
1239  htmlspecialchars( $text ),
1240  $attr,
1241  $query
1242  );
1243  }
1244 
1245  protected function getGroupName() {
1246  return 'login';
1247  }
1248 
1252  protected function postProcessFormDescriptor( &$formDescriptor ) {
1253  // Pre-fill username (if not creating an account, T46775).
1254  if (
1255  isset( $formDescriptor['username'] ) &&
1256  !isset( $formDescriptor['username']['default'] ) &&
1257  !$this->isSignup()
1258  ) {
1259  $user = $this->getUser();
1260  if ( $user->isLoggedIn() ) {
1261  $formDescriptor['username']['default'] = $user->getName();
1262  } else {
1263  $formDescriptor['username']['default'] =
1264  $this->getRequest()->getSession()->suggestLoginUsername();
1265  }
1266  }
1267 
1268  // don't show a submit button if there is nothing to submit (i.e. the only form content
1269  // is other submit buttons, for redirect flows)
1270  if ( !$this->needsSubmitButton( $formDescriptor ) ) {
1271  unset( $formDescriptor['createaccount'], $formDescriptor['loginattempt'] );
1272  }
1273 
1274  if ( !$this->isSignup() ) {
1275  // FIXME HACK don't focus on non-empty field
1276  // maybe there should be an autofocus-if similar to hide-if?
1277  if (
1278  isset( $formDescriptor['username'] )
1279  && empty( $formDescriptor['username']['default'] )
1280  && !$this->getRequest()->getCheck( 'wpName' )
1281  ) {
1282  $formDescriptor['username']['autofocus'] = true;
1283  } elseif ( isset( $formDescriptor['password'] ) ) {
1284  $formDescriptor['password']['autofocus'] = true;
1285  }
1286  }
1287 
1288  $this->addTabIndex( $formDescriptor );
1289  }
1290 }
1291 
1299  public function execute() {
1300  throw new LogicException( 'not used' );
1301  }
1302 
1307  public function addInputItem( $name, $value, $type, $msg, $helptext = false ) {
1308  // use the same indexes as UserCreateForm just in case someone adds an item manually
1309  $this->data['extrainput'][] = [
1310  'name' => $name,
1311  'value' => $value,
1312  'type' => $type,
1313  'msg' => $msg,
1314  'helptext' => $helptext,
1315  ];
1316  }
1317 
1322  public function getExtraInputDefinitions() {
1323  $definitions = [];
1324 
1325  foreach ( $this->get( 'extrainput', [] ) as $field ) {
1326  $definition = [
1327  'type' => $field['type'] === 'checkbox' ? 'check' : $field['type'],
1328  'name' => $field['name'],
1329  'value' => $field['value'],
1330  'id' => $field['name'],
1331  ];
1332  if ( $field['msg'] ) {
1333  $definition['label-message'] = $this->getMsg( $field['msg'] );
1334  }
1335  if ( $field['helptext'] ) {
1336  $definition['help'] = $this->msgWiki( $field['helptext'] );
1337  }
1338 
1339  // the array key doesn't matter much when name is defined explicitly but
1340  // let's try and follow HTMLForm conventions
1341  $name = preg_replace( '/^wp(?=[A-Z])/', '', $field['name'] );
1342  $definitions[$name] = $definition;
1343  }
1344 
1345  if ( $this->haveData( 'extrafields' ) ) {
1346  $definitions['extrafields'] = [
1347  'type' => 'info',
1348  'raw' => true,
1349  'default' => $this->get( 'extrafields' ),
1350  ];
1351  }
1352 
1353  return $definitions;
1354  }
1355 }
1356 
1362 class LoginForm extends SpecialPage {
1363  const SUCCESS = 0;
1364  const NO_NAME = 1;
1365  const ILLEGAL = 2;
1367  const NOT_EXISTS = 4;
1368  const WRONG_PASS = 5;
1369  const EMPTY_PASS = 6;
1370  const RESET_PASS = 7;
1371  const ABORTED = 8;
1372  const CREATE_BLOCKED = 9;
1373  const THROTTLED = 10;
1374  const USER_BLOCKED = 11;
1375  const NEED_TOKEN = 12;
1376  const WRONG_TOKEN = 13;
1377  const USER_MIGRATED = 14;
1378 
1379  public static $statusCodes = [
1380  self::SUCCESS => 'success',
1381  self::NO_NAME => 'no_name',
1382  self::ILLEGAL => 'illegal',
1383  self::WRONG_PLUGIN_PASS => 'wrong_plugin_pass',
1384  self::NOT_EXISTS => 'not_exists',
1385  self::WRONG_PASS => 'wrong_pass',
1386  self::EMPTY_PASS => 'empty_pass',
1387  self::RESET_PASS => 'reset_pass',
1388  self::ABORTED => 'aborted',
1389  self::CREATE_BLOCKED => 'create_blocked',
1390  self::THROTTLED => 'throttled',
1391  self::USER_BLOCKED => 'user_blocked',
1392  self::NEED_TOKEN => 'need_token',
1393  self::WRONG_TOKEN => 'wrong_token',
1394  self::USER_MIGRATED => 'user_migrated',
1395  ];
1396 
1400  public function __construct( $request = null ) {
1401  wfDeprecated( 'LoginForm', '1.27' );
1402  parent::__construct();
1403  }
1404 
1408  public static function getValidErrorMessages() {
1410  }
1411 
1415  public static function incrementLoginThrottle( $username ) {
1416  wfDeprecated( __METHOD__, "1.27" );
1419  $throttler = new Throttler();
1420  return $throttler->increase( $username, $wgRequest->getIP(), __METHOD__ );
1421  }
1422 
1426  public static function incLoginThrottle( $username ) {
1427  wfDeprecated( __METHOD__, "1.27" );
1428  $res = self::incrementLoginThrottle( $username );
1429  return is_array( $res ) ? true : 0;
1430  }
1431 
1435  public static function clearLoginThrottle( $username ) {
1436  wfDeprecated( __METHOD__, "1.27" );
1439  $throttler = new Throttler();
1440  return $throttler->clear( $username, $wgRequest->getIP() );
1441  }
1442 
1446  public static function getLoginToken() {
1447  wfDeprecated( __METHOD__, '1.27' );
1449  return $wgRequest->getSession()->getToken( '', 'login' )->toString();
1450  }
1451 
1455  public static function setLoginToken() {
1456  wfDeprecated( __METHOD__, '1.27' );
1457  }
1458 
1462  public static function clearLoginToken() {
1463  wfDeprecated( __METHOD__, '1.27' );
1465  $wgRequest->getSession()->resetToken( 'login' );
1466  }
1467 
1471  public static function getCreateaccountToken() {
1472  wfDeprecated( __METHOD__, '1.27' );
1474  return $wgRequest->getSession()->getToken( '', 'createaccount' )->toString();
1475  }
1476 
1480  public static function setCreateaccountToken() {
1481  wfDeprecated( __METHOD__, '1.27' );
1482  }
1483 
1487  public static function clearCreateaccountToken() {
1488  wfDeprecated( __METHOD__, '1.27' );
1490  $wgRequest->getSession()->resetToken( 'createaccount' );
1491  }
1492 }
static newFromName($name, $validate= 'valid')
Static factory method for creation from username.
Definition: User.php:568
$wgInitialSessionId
Definition: Setup.php:730
setSessionUserForCurrentRequest()
Replace some globals to make sure the fact that the user has just been logged in is reflected in the ...
and how to run hooks for an and one after Each event has a preferably in CamelCase For ArticleDelete hook A clump of code and data that should be run when an event happens This can be either a function and a chunk of data
Definition: hooks.txt:6
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:1798
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:762
the array() calling protocol came about after MediaWiki 1.4rc1.
getAuthForm(array $requests, $action, $msg= '', $msgType= 'error')
Generates a form from the given request.
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:1418
wfCanIPUseHTTPS($ip)
Determine whether the client at a given source IP is likely to be able to access the wiki via HTTPS...
static getCreateaccountToken()
static wrap($sv)
Succinct helper method to wrap a StatusValue.
Definition: Status.php:79
static linkKnown($target, $html=null, $customAttribs=[], $query=[], $options=[ 'known', 'noclasses'])
Identical to link(), except $options defaults to 'known'.
Definition: Linker.php:264
A special page subclass for authentication-related special pages.
$context
Definition: load.php:44
getContext()
Gets the context this SpecialPage is executed in.
static setCreateaccountToken()
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:2338
$success
makeLanguageSelectorLink($text, $lang)
Create a language selector link for a particular language Links back to this page preserving type and...
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
static getTitleFor($name, $subpage=false, $fragment= '')
Get a localised Title object for a specified special page name.
Definition: SpecialPage.php:75
static getCanonicalName($name, $validate= 'valid')
Given unvalidated user input, return a canonical username, or false if the username is invalid...
Definition: User.php:1050
getFakeTemplate($msg, $msgType)
Temporary B/C method to handle extensions using the UserLoginForm/UserCreateForm hooks.
makeLanguageSelector()
Produce a bar of links which allow the user to select another language during login/registration but ...
needsSubmitButton($formDescriptor)
Returns true if the form has fields which take values.
static rawElement($element, $attribs=[], $contents= '')
Returns an HTML element in a string.
Definition: Html.php:210
if(!isset($args[0])) $lang
An IContextSource implementation which will inherit context from another source but allow individual ...
static factory($displayFormat)
Construct a HTMLForm object for given display type.
Definition: HTMLForm.php:264
hasSessionCookie()
Check if a session cookie is present.
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:177
New base template for a skin's template extended from QuickTemplate this class features helper method...
$value
getToken()
Returns the CSRF token.
mainLoginForm(array $requests, $msg= '', $msgtype= 'error')
msg()
Wrapper around wfMessage that sets the current context.
User $targetUser
FIXME another flag for passing data.
get($name, $default=null)
Gets the template data requested.
getOutput()
Get the OutputPage being used for this instance.
wfUrlencode($s)
We want some things to be included as literal characters in our title URLs for prettiness, which urlencode encodes by default.
static newFromText($text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:277
this hook is for auditing only $response
Definition: hooks.txt:762
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
Holds shared logic for login and account creation pages.
static newFatal($message)
Factory function for fatal errors.
Definition: Status.php:89
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:25
static getValidErrorMessages()
showExtraInformation()
Show extra information such as password recovery information, link from login to signup, CTA etc? Such information should only be shown on the "landing page", ie.
$wgEnableEmail
Set to true to enable the e-mail basic features: Password reminders, etc.
showSuccessPage($type, $title, $msgname, $injected_html, $extraMessages)
Show the success page.
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 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:762
static incLoginThrottle($username)
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:1798
$wgLoginLanguageSelector
Show a bar of language selection links in the user login and user registration forms; edit the "login...
isActionAllowed($action)
Checks whether AuthManager is ready to perform the action.
Parent class for all special pages.
Definition: SpecialPage.php:36
const PROTO_HTTPS
Definition: Defines.php:262
isContinued()
Returns true if this is not the first step of the authentication.
wfEscapeWikiText($text)
Escapes the given text so that it may be output using addWikiText() without any linking, formatting, etc.
getContinueAction($action)
Gets the _CONTINUE version of an action.
static getMain()
Static methods.
getMsg($name)
Get a Message object with its context set.
logAuthResult($success, $status=null)
Logs to the authmanager-stats channel.
addTabIndex(&$formDescriptor)
Adds a sequential tabindex starting from 1 to all form elements.
getExtraInputDefinitions()
Turns addInputItem-style field definitions into HTMLForm field definitions.
wfAppendQuery($url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
An error page which can definitely be safely rendered using the OutputPage.
loadRequestParameters($subPage)
Load basic request parameters for this Special page.
static clearCreateaccountToken()
$res
Definition: database.txt:21
getBCFieldDefinitions($fieldDefinitions, $template)
Adds fields provided via the deprecated UserLoginForm / UserCreateForm hooks.
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 after processing 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 unsetoffset-wrap String Wrap the message in html(usually something like"&lt
B/C class to try handling login/signup template modifications even though login/signup does not actua...
static incrementLoginThrottle($username)
$params
getSkin()
Shortcut to get the skin being used for this instance.
wfDeprecated($function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes! ...
$wgUseMediaWikiUIEverywhere
Temporary variable that applies MediaWiki UI wherever it can be supported.
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:312
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:912
static run($event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
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:12
$wgEnableUserEmail
Set to true to enable user-to-user e-mail.
Helper functions for the login form that need to be shared with other special pages (such as CentralA...
Definition: LoginHelper.php:8
getFieldDefinitions($template)
Create a HTMLForm descriptor for the core login fields.
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
Definition: distributors.txt:9
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:762
loadAuth($subPage, $authAction=null, $reset=false)
Load or initialize $authAction, $authRequests and $subPage.
Helper class for the password reset functionality shared by the web UI and the API.
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:242
static link($target, $html=null, $customAttribs=[], $query=[], $options=[])
This function returns an HTML link to the given target.
Definition: Linker.php:195
onAuthChangeFormFields(array $requests, array $fieldInfo, array &$formDescriptor, $action)
setRequest(array $data, $wasPosted=null)
showReturnToPage($type, $returnTo= '', $returnToQuery= '', $stickHTTPS=false)
Add a "return to" link or redirect to it.
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:35
this hook is for auditing only or null if authentication failed before getting that far $username
Definition: hooks.txt:762
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2418
getName()
Get the name of this Special Page.
bool $proxyAccountCreation
True if the user if creating an account for someone else.
getPageHtml($formHtml)
Add page elements which are outside the form.
getDefaultAction($subPage)
Get the default action for this special page, if none is given via URL/POST data. ...
getUser()
Shortcut to get the User executing this instance.
getConfig()
Shortcut to get main config object.
this hook is for auditing only WRONG_PASS
Definition: hooks.txt:1946
static getValidErrorMessages()
Returns an array of all valid error messages.
Definition: LoginHelper.php:36
getLanguage()
Shortcut to get user's language.
load($subPage)
Load data from request.
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:1004
MediaWiki Logger LoggerFactory implements a PSR[0] compatible message logging system Named Psr Log LoggerInterface instances can be obtained from the MediaWiki Logger LoggerFactory::getInstance() static method.MediaWiki\Logger\LoggerFactory expects a class implementing the MediaWiki\Logger\Spi interface to act as a factory for new Psr\Log\LoggerInterface instances.The"Spi"in MediaWiki\Logger\Spi stands for"service provider interface".An SPI is an API intended to be implemented or extended by a third party.This software design pattern is intended to enable framework extension and replaceable components.It is specifically used in the MediaWiki\Logger\LoggerFactory service to allow alternate PSR-3 logging implementations to be easily integrated with MediaWiki.The service provider interface allows the backend logging library to be implemented in multiple ways.The $wgMWLoggerDefaultSpi global provides the classname of the default MediaWiki\Logger\Spi implementation to be loaded at runtime.This can either be the name of a class implementing the MediaWiki\Logger\Spi with a zero argument const ructor or a callable that will return an MediaWiki\Logger\Spi instance.Alternately the MediaWiki\Logger\LoggerFactory MediaWiki Logger LoggerFactory
Definition: logger.txt:5
static makeInternalOrExternalUrl($name)
If url string starts with http, consider as external URL, else internal.
Definition: Skin.php:1097
getFullTitle()
Return the full title, including $par.
static validateEmail($addr)
Does a string look like an e-mail address?
Definition: Sanitizer.php:1903
__construct($request=null)
checkPermissions()
Checks if userCanExecute, and if not throws a PermissionsError.
static clearLoginThrottle($username)
postProcessFormDescriptor(&$formDescriptor)
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:2338
static factory($code)
Get a cached or new language object for a given language code.
Definition: Language.php:179
showCreateAccountLink()
Whether the login/create account form should display a link to the other form (in addition to whateve...
string $subPage
Subpage of the special page.
getPreservedParams($withToken=false)
static element($element, $attribs=[], $contents= '')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:230
successfulAction($direct=false, $extraMessages=null)
setContext($context)
Sets the context this SpecialPage is executed in.
if(is_null($wgLocalTZoffset)) if(!$wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:657
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:2338
getReturnToQueryStringFragment()
Returns a string that can be appended to the URL (without encoding) to preserve the return target...
addInputItem($name, $value, $type, $msg, $helptext=false)
Extensions (AntiSpoof and TitleBlacklist) call this in response to UserCreateForm hook to add checkbo...
getPageTitle($subpage=false)
Get a self-referential title object.
$wgUser
Definition: Setup.php:794
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:310
trySubmit()
Attempts to do an authentication step with the submitted data.