3 * Special page for creating/registering new user accounts.
6 class SpecialCreateAccount
extends SpecialPage
{
8 var $mUsername, $mPassword, $mRetype, $mReturnTo, $mPosted;
9 var $mCreateaccountMail, $mRemember, $mEmail, $mDomain, $mLanguage;
14 public $mDomains = array();
16 public $mUseEmail = true; # Can be switched off by AuthPlugins etc
17 public $mUseRealname = true;
18 public $mUseRemember = true;
20 public $mFormHeader = '';
21 public $mFormFields = array(
24 'label-message' => 'yourname',
33 'label-message' => 'yourpassword',
35 'id' => 'wpPassword2',
40 'label-message' => 'yourpasswordagain',
47 'label-message' => 'youremail',
53 'label-message' => 'yourrealname',
60 'label-message' => 'remembermypassword',
66 'label-message' => 'yourdomainname',
72 public function __construct(){
73 parent
::__construct( 'CreateAccount', 'createaccount' );
74 $this->mLogin
= new Login();
75 $this->mFormFields
['RealName']['label-help'] = 'prefs-help-realname';
78 public function execute( $par ){
79 global $wgUser, $wgOut;
84 # Block signup here if in readonly. Keeps user from
85 # going through the process (filling out data, etc)
86 # and being informed later.
88 $wgOut->readOnlyPage();
91 # Bail out straightaway on permissions errors
92 if ( !$this->userCanExecute( $wgUser ) ) {
93 $this->displayRestrictionError();
95 } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
96 $this->userBlockedMessage();
98 } elseif ( count( $permErrors = $this->getTitle()->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
99 $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
103 if( $this->mPosted
) {
104 $this->addNewAccount( $this->mCreateaccountMail
);
106 $this->showMainForm('');
111 * Load the member variables from the request parameters
113 protected function loadQuery(){
114 global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
115 $this->mCreateaccountMail
= $wgRequest->getCheck( 'wpCreateaccountMail' )
118 $this->mUsername
= $wgRequest->getText( 'wpName' );
119 $this->mPassword
= $wgRequest->getText( 'wpPassword' );
120 $this->mRetype
= $wgRequest->getText( 'wpRetype' );
121 $this->mDomain
= $wgRequest->getText( 'wpDomain' );
122 $this->mReturnTo
= $wgRequest->getVal( 'returnto' );
123 $this->mReturnToQuery
= $wgRequest->getVal( 'returntoquery' );
124 $this->mPosted
= $wgRequest->wasPosted();
125 $this->mCreateaccountMail
= $wgRequest->getCheck( 'wpCreateaccountMail' )
127 $this->mRemember
= $wgRequest->getCheck( 'wpRemember' );
128 $this->mLanguage
= $wgRequest->getText( 'uselang' );
130 if ( $wgRedirectOnLogin ) {
131 $this->mReturnTo
= $wgRedirectOnLogin;
132 $this->mReturnToQuery
= '';
135 if( $wgEnableEmail ) {
136 $this->mEmail
= $wgRequest->getText( 'wpEmail' );
140 if( !in_array( 'realname', $wgHiddenPrefs ) ) {
141 $this->mRealName
= $wgRequest->getText( 'wpRealName' );
143 $this->mRealName
= '';
146 if( !$wgAuth->validDomain( $this->mDomain
) ) {
147 $this->mDomain
= 'invaliddomain';
149 $wgAuth->setDomain( $this->mDomain
);
151 # When switching accounts, it sucks to get automatically logged out
152 $returnToTitle = Title
::newFromText( $this->mReturnTo
);
153 if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) {
154 $this->mReturnTo
= '';
155 $this->mReturnToQuery
= '';
160 * Create a new user account from the provided data
162 protected function addNewAccount( $byEmail=false ) {
163 global $wgUser, $wgEmailAuthentication;
165 # Do a quick check that the user actually managed to type
166 # the password in the same both times
167 if ( 0 != strcmp( $this->mPassword
, $this->mRetype
) ) {
168 return $this->showMainForm( wfMsgExt( 'badretype', 'parseinline' ) );
171 # Create the account and abort if there's a problem doing so
172 $status = $this->mLogin
->attemptCreation( $byEmail );
175 case Login
::MAIL_ERROR
:
178 case Login
::CREATE_BADDOMAIN
:
179 case Login
::CREATE_EXISTS
:
181 case Login
::CREATE_NEEDEMAIL
:
182 case Login
::CREATE_BADEMAIL
:
183 case Login
::CREATE_BADNAME
:
184 case Login
::WRONG_PLUGIN_PASS
:
186 return $this->showMainForm( wfMsgExt( $this->mLogin
->mCreateResult
, 'parseinline' ) );
188 case Login
::CREATE_SORBS
:
189 return $this->showMainForm( wfMsgExt( 'sorbs_create_account_reason', 'parseinline' ) . ' (' . wfGetIP() . ')' );
191 case Login
::CREATE_BLOCKED
:
192 return $this->userBlockedMessage();
194 case Login
::CREATE_BADPASS
:
195 global $wgMinimalPasswordLength;
196 return $this->showMainForm( wfMsgExt( $this->mLogin
->mCreateResult
, array( 'parsemag' ), $wgMinimalPasswordLength ) );
198 case Login
::THROTTLED
:
199 global $wgAccountCreationThrottle;
200 return $this->showMainForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $wgAccountCreationThrottle ) );
203 throw new MWException( "Unhandled status code $status in " . __METHOD__
);
206 # If we showed up language selection links, and one was in use, be
207 # smart (and sensible) and save that language as the user's preference
208 global $wgLoginLanguageSelector;
209 if( $wgLoginLanguageSelector && $this->mLanguage
)
210 $this->mLogin
->mUser
->setOption( 'language', $this->mLanguage
);
211 $this->mLogin
->mUser
->saveSettings();
214 if( $result == Login
::MAIL_ERROR
){
215 # FIXME: we are totally screwed if we end up here...
216 $this->showMainForm( wfMsgExt( 'mailerror', 'parseinline', $this->mLogin
->mMailResult
->getMessage() ) );
218 $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
219 $wgOut->addWikiMsg( 'accmailtext', $this->mLogin
->mUser
->getName(), $this->mLogin
->mUser
->getEmail() );
220 $wgOut->returnToMain( false );
225 # There might be a message stored from the confirmation mail
226 # send, which we can display.
227 if( $wgEmailAuthentication && $this->mLogin
->mMailResult
) {
229 if( WikiError
::isError( $this->mLogin
->mMailResult
) ) {
230 $wgOut->addWikiMsg( 'confirmemail_sendfailed', $this->mLogin
->mMailResult
->getMessage() );
232 $wgOut->addWikiMsg( 'confirmemail_oncreate' );
236 # If not logged in, assume the new account as the current
237 # one and set session cookies then show a "welcome" message
238 # or a "need cookies" message as needed
239 if( $wgUser->isAnon() ) {
240 $wgUser = $this->mLogin
->mUser
;
241 $wgUser->setCookies();
242 if( $this->hasSessionCookie() ) {
243 return $this->successfulCreation();
245 return $this->cookieRedirectCheck();
248 # Show confirmation that the account was created
250 $self = SpecialPage
::getTitleFor( 'Userlogin' );
251 $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
252 $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $this->mLogin
->mUser
->getName() ) );
253 $wgOut->returnToMain( false, $self );
260 * Run any hooks registered for logins, then
261 * display a message welcoming the user.
263 protected function successfulCreation(){
264 global $wgUser, $wgOut;
266 # Run any hooks; display injected HTML
268 wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
270 SpecialUserLogin
::displaySuccessfulLogin(
274 $this->mReturnToQuery
);
278 * Display a message indicating that account creation from their IP has
279 * been blocked by a (range)block with 'block account creation' enabled.
280 * It's likely that this feature will be used for blocking large numbers
281 * of innocent people, e.g. range blocks on schools. Don't blame it on
282 * the user. There's a small chance that it really is the user's fault,
283 * i.e. the username is blocked and they haven't bothered to log out
284 * before trying to create an account to evade it, but we'll leave that
285 * to their guilty conscience to figure out...
287 protected function userBlockedMessage() {
288 global $wgOut, $wgUser;
290 $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) );
291 $wgOut->setRobotPolicy( 'noindex,nofollow' );
292 $wgOut->setArticleRelated( false );
295 $blocker = User
::whoIs( $wgUser->mBlock
->mBy
);
296 $block_reason = $wgUser->mBlock
->mReason
;
298 if ( strval( $block_reason ) === '' ) {
299 $block_reason = wfMsgExt( 'blockednoreason', 'parseinline' );
301 $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $block_reason, $blocker );
302 $wgOut->returnToMain( false );
306 * Show the main input form, with an appropriate error message
307 * from a previous iteration, if necessary
308 * @param $msg String HTML of message received previously
309 * @param $msgtype String type of message, usually 'error'
311 protected function showMainForm( $msg, $msgtype = 'error' ) {
312 global $wgUser, $wgOut, $wgHiddenPrefs, $wgEnableEmail;
313 global $wgCookiePrefix, $wgLoginLanguageSelector;
314 global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration;
316 # Parse the error message if we got one
318 if( $msgtype == 'error' ){
319 $msg = wfMsgExt( 'loginerror', 'parseinline' ) . ' ' . $msg;
321 $msg = Html
::rawElement(
323 array( 'class' => $msgtype . 'box' ),
330 # Make sure the returnTo strings don't get lost if the
331 # user changes language, etc
333 if ( !empty( $this->mReturnTo
) ) {
334 $linkq['returnto'] = wfUrlencode( $this->mReturnTo
);
335 if ( !empty( $this->mReturnToQuery
) )
336 $linkq['returntoquery'] = wfUrlencode( $this->mReturnToQuery
);
339 # Pass any language selection on to the mode switch link
340 if( $wgLoginLanguageSelector && $this->mLanguage
)
341 $linkq['uselang'] = $this->mLanguage
;
343 $skin = $wgUser->getSkin();
345 SpecialPage
::getTitleFor( 'Userlogin' ),
346 wfMsgHtml( 'gotaccountlink' ),
349 $link = $wgUser->isLoggedIn()
351 : wfMsgWikiHtml( 'gotaccount', $link );
353 # Prepare language selection links as needed
354 $langSelector = $wgLoginLanguageSelector
357 array( 'id' => 'languagelinks' ),
358 SpecialUserLogin
::makeLanguageSelector( $this->getTitle(), $this->mReturnTo
) )
361 # Add a 'send password by email' button if available
363 if( $wgEnableEmail && $wgUser->isLoggedIn() ){
364 $buttons = Html
::element(
368 'name' => 'wpCreateaccountMail',
369 'value' => wfMsg( 'createaccountmail' ),
370 'id' => 'wpCreateaccountMail',
375 # Give authentication and captcha plugins a chance to
376 # modify the form, by hook or by using $wgAuth
377 $wgAuth->modifyUITemplate( $this, 'new' );
378 wfRunHooks( 'UserCreateForm', array( &$this ) );
380 # The most likely use of the hook is to enable domains;
381 # check that now, and add fields if necessary
382 if( $this->mDomains
){
383 $this->mFormFields
['Domain']['options'] = $this->mDomains
;
384 $this->mFormFields
['Domain']['default'] = $this->mDomain
;
386 unset( $this->mFormFields
['Domain'] );
389 # Or to switch email on or off
390 if( !$wgEnableEmail ||
!$this->mUseEmail
){
391 unset( $this->mFormFields
['Email'] );
393 if( $wgEmailConfirmToEdit ){
394 $this->mFormFields
['Email']['label-help'] = 'prefs-help-email-required';
395 $this->mFormFields
['Email']['required'] = '';
397 $this->mFormFields
['Email']['label-help'] = 'prefs-help-email';
401 # Or to play with realname
402 if( in_array( 'realname', $wgHiddenPrefs ) ||
!$this->mUseRealname
){
403 unset( $this->mFormFields
['Realname'] );
406 # Or to tweak the 'remember my password' checkbox
407 if( !($wgCookieExpiration > 0) ||
!$this->mUseRemember
){
408 # Remove it altogether
409 unset( $this->mFormFields
['Remember'] );
410 } elseif( $wgUser->getOption( 'rememberpassword' ) ||
$this->mRemember
){
411 # Or check it by default
412 # FIXME: this doesn't always work?
413 $this->mFormFields
['Remember']['checked'] = '1';
416 $form = new HTMLForm( $this->mFormFields
, '' );
417 $form->setTitle( $this->getTitle() );
418 $form->setSubmitText( wfMsg( 'createaccount' ) );
419 $form->setSubmitId( 'wpCreateaccount' );
420 $form->suppressReset();
424 . Html
::rawElement( 'p', array( 'id' => 'userloginlink' ),
429 . $form->getButtons()
431 . Xml
::hidden( 'returnto', $this->mReturnTo
)
432 . Xml
::hidden( 'returntoquery', $this->mReturnToQuery
)
435 $wgOut->setPageTitle( wfMsg( 'createaccount' ) );
436 $wgOut->setRobotPolicy( 'noindex,nofollow' );
437 $wgOut->setArticleRelated( false );
438 $wgOut->disallowUserJs(); # Stop malicious userscripts sniffing passwords
443 array( 'id' => 'loginstart' ),
444 wfMsgExt( 'loginstart', array( 'parseinline' ) )
449 array( 'id' => 'userloginForm' ),
450 $form->wrapForm( $formContents )
454 array( 'id' => 'loginend' ),
455 wfMsgExt( 'loginend', array( 'parseinline' ) )
462 * Check if a session cookie is present.
464 * This will not pick up a cookie set during _this_ request, but is meant
465 * to ensure that the client is returning the cookie which was set on a
466 * previous pass through the system.
470 protected function hasSessionCookie() {
471 global $wgDisableCookieCheck, $wgRequest;
472 return $wgDisableCookieCheck ?
true : $wgRequest->checkSessionCookie();
476 * Do a redirect back to the same page, so we can check any
477 * new session cookies.
479 protected function cookieRedirectCheck() {
482 $query = array( 'wpCookieCheck' => '1' );
483 if ( $this->mReturnTo
) $query['returnto'] = $this->mReturnTo
;
484 $check = $this->getTitle()->getFullURL( $query );
486 return $wgOut->redirect( $check );
490 * Check the cookies and show errors if they're not enabled.
491 * @param $type String action being performed
493 protected function onCookieRedirectCheck() {
494 if ( !$this->hasSessionCookie() ) {
495 return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) );
497 return SpecialUserlogin
::successfulLogin(
498 array( 'welcomecreate' ),
500 $this->mReturnToQuery
506 * Since the UserCreateForm hook was changed to pass a SpecialPage
507 * instead of a QuickTemplate derivative, old extensions might
508 * easily try calling these methods expecing them to exist. Tempting
509 * though it is to let them have the fatal error, let's at least
513 public function set(){
514 wfDeprecated( __METHOD__
);
516 public function addInputItem(){
517 wfDeprecated( __METHOD__
);