r56256 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r56255‎ | r56256 | r56257 >
Date:12:51, 13 September 2009
Author:happy-melon
Status:deferred (Comments)
Tags:
Comment:
Lots of tweaks to the Login rewrite stuff
Modified paths:
  • /branches/happy-melon/phase3/includes/Login.php (modified) (history)
  • /branches/happy-melon/phase3/includes/User.php (modified) (history)
  • /branches/happy-melon/phase3/includes/api/ApiLogin.php (modified) (history)
  • /branches/happy-melon/phase3/includes/specials/SpecialCreateAccount.php (modified) (history)
  • /branches/happy-melon/phase3/includes/specials/SpecialResetpass.php (modified) (history)
  • /branches/happy-melon/phase3/includes/specials/SpecialUserlogin.php (modified) (history)
  • /branches/happy-melon/phase3/languages/messages/MessagesEn.php (modified) (history)

Diff [purge]

Index: branches/happy-melon/phase3/includes/User.php
@@ -2483,7 +2483,7 @@
24842484 $user = new User;
24852485 $user->load();
24862486 if ( isset( $params['options'] ) ) {
2487 - $user->mOptions = $params['options'] + $user->mOptions;
 2487+ $user->mOptions = $params['options'] + (array)$user->mOptions;
24882488 unset( $params['options'] );
24892489 }
24902490 $dbw = wfGetDB( DB_MASTER );
Index: branches/happy-melon/phase3/includes/api/ApiLogin.php
@@ -106,10 +106,10 @@
107107 case Login::THROTTLED :
108108 global $wgPasswordAttemptThrottle;
109109 $result['result'] = 'Throttled';
110 - $result['wait'] = intval($wgPasswordAttemptThrottle['seconds']);
 110+ $result['wait'] = intval( $wgPasswordAttemptThrottle['seconds'] );
111111 break;
112112 default :
113 - ApiBase::dieDebug(__METHOD__, "Unhandled case value: {$authRes}");
 113+ ApiBase::dieDebug( __METHOD__, "Unhandled case value: {$authRes}" );
114114 }
115115
116116 $this->getResult()->addValue(null, 'login', $result);
Index: branches/happy-melon/phase3/includes/Login.php
@@ -14,25 +14,41 @@
1515 const EMPTY_PASS = 6;
1616 const RESET_PASS = 7;
1717 const ABORTED = 8;
18 - const CREATE_BLOCKED = 9;
1918 const THROTTLED = 10;
 19+ const FAILED = 11;
 20+ const READ_ONLY = 12;
2021
21 - const MAIL_READ_ONLY = 11;
22 - const MAIL_PASSCHANGE_FORBIDDEN = 12;
23 - const MAIL_BLOCKED = 13;
24 - const MAIL_PING_THROTTLED = 14;
25 - const MAIL_PASS_THROTTLED = 15;
26 - const MAIL_EMPTY_EMAIL = 16;
27 - const MAIL_BAD_IP = 17;
28 - const MAIL_ERROR = 18;
 22+ const MAIL_PASSCHANGE_FORBIDDEN = 21;
 23+ const MAIL_BLOCKED = 22;
 24+ const MAIL_PING_THROTTLED = 23;
 25+ const MAIL_PASS_THROTTLED = 24;
 26+ const MAIL_EMPTY_EMAIL = 25;
 27+ const MAIL_BAD_IP = 26;
 28+ const MAIL_ERROR = 27;
 29+
 30+ const CREATE_BLOCKED = 40;
 31+ const CREATE_EXISTS = 41;
 32+ const CREATE_SORBS = 42;
 33+ const CREATE_BADDOMAIN = 43;
 34+ const CREATE_BADNAME = 44;
 35+ const CREATE_BADPASS = 45;
 36+ const CREATE_NEEDEMAIL = 46;
 37+ const CREATE_BADEMAIL = 47;
2938
30 - var $mName, $mPassword, $mPosted;
31 - var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage;
 39+ protected $mName;
 40+ protected $mPassword;
 41+ public $mRemember; # 0 or 1
 42+ public $mEmail;
 43+ public $mDomain;
 44+ public $mRealname;
3245
3346 private $mExtUser = null;
3447
3548 public $mUser;
36 - public $mMailResult;
 49+
 50+ public $mLoginResult = '';
 51+ public $mMailResult = '';
 52+ public $mCreateResult = '';
3753
3854 /**
3955 * Constructor
@@ -46,8 +62,7 @@
4763 $this->mName = $request->getText( 'wpName' );
4864 $this->mPassword = $request->getText( 'wpPassword' );
4965 $this->mDomain = $request->getText( 'wpDomain' );
50 - $this->mPosted = $request->wasPosted();
51 - $this->mRemember = $request->getCheck( 'wpRemember' );
 66+ $this->mRemember = $request->getCheck( 'wpRemember' ) ? 1 : 0;
5267
5368 if( $wgEnableEmail ) {
5469 $this->mEmail = $request->getText( 'wpEmail' );
@@ -65,57 +80,28 @@
6681 }
6782 $wgAuth->setDomain( $this->mDomain );
6883
69 - # Attempt to generate the User
70 - $this->mUser = User::newFromName( $this->mName );
 84+ # Load the user, if they exist in the local database.
 85+ $this->mUser = User::newFromName( trim( $this->mName ), 'usable' );
7186 }
72 -
 87+
7388 /**
74 - * Actually add a user to the database.
75 - * Give it a User object that has been initialised with a name.
76 - *
77 - * @param $u User object.
78 - * @param $autocreate boolean -- true if this is an autocreation via auth plugin
79 - * @return User object.
 89+ * Having initialised the Login object with (at least) the wpName
 90+ * and wpPassword pair, attempt to authenticate the user and log
 91+ * them into the wiki. Authentication may come from the local
 92+ * user database, or from an AuthPlugin- or ExternalUser-based
 93+ * foreign database; in the latter case, a local user record may
 94+ * or may not be created and initialised.
 95+ * @return a Login class constant representing the status.
8096 */
81 - public function initUser( $autocreate ) {
82 - global $wgAuth;
83 -
84 - $this->mUser->addToDatabase();
85 -
86 - if ( $wgAuth->allowPasswordChange() ) {
87 - $this->mUser->setPassword( $this->mPassword );
88 - }
89 -
90 - $this->mUser->setEmail( $this->mEmail );
91 - $this->mUser->setRealName( $this->mRealName );
92 - $this->mUser->setToken();
93 -
94 - $wgAuth->initUser( $this->mUser, $autocreate );
95 -
96 - if( $this->mExtUser ) {
97 - $this->mExtUser->link( $this->mUser->getId() );
98 - $email = $this->mExtUser->getPref( 'emailaddress' );
99 - if( $email && !$this->mEmail ) {
100 - $this->mUser->setEmail( $email );
101 - }
102 - }
103 -
104 - $this->mUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
105 - $this->mUser->saveSettings();
106 -
107 - # Update user count
108 - $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
109 - $ssUpdate->doUpdate();
110 -
111 - return $this->mUser;
112 - }
113 -
11497 public function attemptLogin(){
11598 global $wgUser;
 99+
116100 $code = $this->authenticateUserData();
117 - if( !$code == self::SUCCESS ){
 101+ if( $code != self::SUCCESS ){
118102 return $code;
119103 }
 104+
 105+ # Log the user in and remember them if they asked for that.
120106 if( (bool)$this->mRemember != (bool)$wgUser->getOption( 'rememberpassword' ) ) {
121107 $wgUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
122108 $wgUser->saveSettings();
@@ -124,32 +110,76 @@
125111 }
126112 $wgUser->setCookies();
127113
128 - # Reset the throttle
 114+ # Reset the password throttle
129115 $key = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
130116 global $wgMemc;
131117 $wgMemc->delete( $key );
132118
133 - $injected_html = '';
134 - wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
 119+ wfRunHooks( 'UserLoginComplete', array( &$wgUser, &$this->mLoginResult ) );
135120
136121 return self::SUCCESS;
137122 }
138123
139124 /**
 125+ * Check whether there is an external authentication mechanism from
 126+ * which we can automatically authenticate the user and create a
 127+ * local account for them.
 128+ * @return integer Status code. Login::SUCCESS == clear to proceed
 129+ * with user creation.
 130+ */
 131+ protected function canAutoCreate() {
 132+ global $wgAuth, $wgUser, $wgAutocreatePolicy;
 133+
 134+ if( $wgUser->isBlockedFromCreateAccount() ) {
 135+ wfDebug( __METHOD__.": user is blocked from account creation\n" );
 136+ return self::CREATE_BLOCKED;
 137+ }
 138+
 139+ # If the external authentication plugin allows it, automatically
 140+ # create a new account for users that are externally defined but
 141+ # have not yet logged in.
 142+ if( $this->mExtUser ) {
 143+ # mExtUser is neither null nor false, so use the new
 144+ # ExternalAuth system.
 145+ if( $wgAutocreatePolicy == 'never' ) {
 146+ return self::NOT_EXISTS;
 147+ }
 148+ if( !$this->mExtUser->authenticate( $this->mPassword ) ) {
 149+ return self::WRONG_PLUGIN_PASS;
 150+ }
 151+ } else {
 152+ # Old AuthPlugin.
 153+ if( !$wgAuth->autoCreate() ) {
 154+ return self::NOT_EXISTS;
 155+ }
 156+ if( !$wgAuth->userExists( $this->mUser->getName() ) ) {
 157+ wfDebug( __METHOD__.": user does not exist\n" );
 158+ return self::NOT_EXISTS;
 159+ }
 160+ if( !$wgAuth->authenticate( $this->mUser->getName(), $this->mPassword ) ) {
 161+ wfDebug( __METHOD__.": \$wgAuth->authenticate() returned false, aborting\n" );
 162+ return self::WRONG_PLUGIN_PASS;
 163+ }
 164+ }
 165+
 166+ return self::SUCCESS;
 167+ }
 168+
 169+ /**
140170 * Internally authenticate the login request.
141171 *
142172 * This may create a local account as a side effect if the
143173 * authentication plugin allows transparent local account
144174 * creation.
145175 */
146 - public function authenticateUserData() {
 176+ protected function authenticateUserData() {
147177 global $wgUser, $wgAuth;
 178+
148179 if ( '' == $this->mName ) {
149180 return self::NO_NAME;
150181 }
151182
152183 global $wgPasswordAttemptThrottle;
153 -
154184 $throttleCount = 0;
155185 if ( is_array( $wgPasswordAttemptThrottle ) ) {
156186 $throttleKey = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
@@ -159,7 +189,7 @@
160190 global $wgMemc;
161191 $throttleCount = $wgMemc->get( $throttleKey );
162192 if ( !$throttleCount ) {
163 - $wgMemc->add( $throttleKey, 1, $period ); // start counter
 193+ $wgMemc->add( $throttleKey, 1, $period ); # Start counter
164194 } else if ( $throttleCount < $count ) {
165195 $wgMemc->incr($throttleKey);
166196 } else if ( $throttleCount >= $count ) {
@@ -167,8 +197,8 @@
168198 }
169199 }
170200
171 - # Load $wgUser now, and check to see if we're logging in as the same
172 - # name. This is necessary because loading $wgUser (say by calling
 201+ # Unstub $wgUser now, and check to see if we're logging in as the same
 202+ # name. As well as the obvious, unstubbing $wgUser (say by calling
173203 # getName()) calls the UserLoadFromSession hook, which potentially
174204 # creates the user in the database. Until we load $wgUser, checking
175205 # for user existence using User::newFromName($name)->getId() below
@@ -186,15 +216,18 @@
187217 return self::ILLEGAL;
188218 }
189219
190 - $isAutoCreated = false;
191 - if ( 0 == $this->mUser->getID() ) {
192 - $status = $this->attemptAutoCreate( $this->mUser );
193 - if ( $status !== self::SUCCESS ) {
194 - return $status;
 220+ # If the user doesn't exist in the local database, our only chance
 221+ # is for an external auth plugin to autocreate the local user.
 222+ if ( $this->mUser->getID() == 0 ) {
 223+ if ( $this->canAutoCreate() == self::SUCCESS ) {
 224+ $isAutoCreated = true;
 225+ wfDebug( __METHOD__.": creating account\n" );
 226+ $this->initUser( true );
195227 } else {
196 - $isAutoCreated = true;
 228+ return $this->canAutoCreate();
197229 }
198230 } else {
 231+ $isAutoCreated = false;
199232 $this->mUser->load();
200233 }
201234
@@ -231,7 +264,7 @@
232265 # etc will probably just fail cleanly here.
233266 $retval = self::RESET_PASS;
234267 } else {
235 - $retval = '' == $this->mPassword ? self::EMPTY_PASS : self::WRONG_PASS;
 268+ $retval = ( $this->mPassword === '' ) ? self::EMPTY_PASS : self::WRONG_PASS;
236269 }
237270 } else {
238271 $wgAuth->updateUser( $this->mUser );
@@ -254,48 +287,200 @@
255288 }
256289
257290 /**
258 - * Attempt to automatically create a user on login. Only succeeds if there
259 - * is an external authentication method which allows it.
260 - * @return integer Status code
 291+ * Actually add a user to the database.
 292+ * Give it a User object that has been initialised with a name.
 293+ *
 294+ * @param $autocreate Bool is this is an autocreation from an external
 295+ * authentication database?
 296+ * @param $byEmail Bool is this request going to be handled by sending
 297+ * the password by email?
 298+ * @return Bool whether creation was successful (should only fail for
 299+ * Db errors etc).
261300 */
262 - public function attemptAutoCreate( $user ) {
263 - global $wgAuth, $wgUser, $wgAutocreatePolicy;
 301+ protected function initUser( $autocreate=false, $byEmail=false ) {
 302+ global $wgAuth;
264303
265 - if( $wgUser->isBlockedFromCreateAccount() ) {
266 - wfDebug( __METHOD__.": user is blocked from account creation\n" );
267 - return self::CREATE_BLOCKED;
 304+ $fields = array(
 305+ 'name' => $this->mName,
 306+ 'password' => $byEmail ? null : $this->mPassword,
 307+ 'email' => $this->mEmail,
 308+ 'options' => array(
 309+ 'rememberpassword' => $this->mRemember ? 1 : 0,
 310+ ),
 311+ );
 312+
 313+ $this->mUser = User::createNew( $this->mName, $fields );
 314+
 315+ if( $this->mUser === null ){
 316+ return null;
268317 }
269318
270 - # If the external authentication plugin allows it, automatically cre-
271 - # ate a new account for users that are externally defined but have not
272 - # yet logged in.
 319+ # Let old AuthPlugins play with the user
 320+ $wgAuth->initUser( $this->mUser, $autocreate );
 321+
 322+ # Or new ExternalUser plugins
273323 if( $this->mExtUser ) {
274 - # mExtUser is neither null nor false, so use the new ExternalAuth
275 - # system.
276 - if( $wgAutocreatePolicy == 'never' ) {
277 - return self::NOT_EXISTS;
 324+ $this->mExtUser->link( $this->mUser->getId() );
 325+ $email = $this->mExtUser->getPref( 'emailaddress' );
 326+ if( $email && !$this->mEmail ) {
 327+ $this->mUser->setEmail( $email );
278328 }
279 - if( !$this->mExtUser->authenticate( $this->mPassword ) ) {
280 - return self::WRONG_PLUGIN_PASS;
 329+ }
 330+
 331+ # Update user count and newuser logs
 332+ $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
 333+ $ssUpdate->doUpdate();
 334+ if( $autocreate )
 335+ $this->mUser->addNewUserLogEntryAutoCreate();
 336+ else
 337+ $this->mUser->addNewUserLogEntry( $byEmail );
 338+
 339+ # Run hooks
 340+ wfRunHooks( 'AddNewAccount', array( $this->mUser ) );
 341+
 342+ return true;
 343+ }
 344+
 345+ /**
 346+ * Entry point to create a new local account from user-supplied
 347+ * data loaded from the WebRequest. We handle initialising the
 348+ * email here because it's needed for some backend things; frontend
 349+ * interfaces calling this should handle recording things like
 350+ * preference options
 351+ * @param $byEmail Bool whether to email the user their new password
 352+ * @return Status code; Login::SUCCESS == the user was successfully created
 353+ */
 354+ public function attemptCreation( $byEmail=false ) {
 355+ global $wgUser, $wgOut;
 356+ global $wgEnableSorbs, $wgProxyWhitelist;
 357+ global $wgMemc, $wgAccountCreationThrottle;
 358+ global $wgAuth;
 359+ global $wgEmailAuthentication, $wgEmailConfirmToEdit;
 360+
 361+ if( wfReadOnly() )
 362+ return self::READ_ONLY;
 363+
 364+ # If the user passes an invalid domain, something is fishy
 365+ if( !$wgAuth->validDomain( $this->mDomain ) ) {
 366+ $this->mCreateResult = 'wrongpassword';
 367+ return self::CREATE_BADDOMAIN;
 368+ }
 369+
 370+ # If we are not allowing users to login locally, we should be checking
 371+ # to see if the user is actually able to authenticate to the authenti-
 372+ # cation server before they create an account (otherwise, they can
 373+ # create a local account and login as any domain user). We only need
 374+ # to check this for domains that aren't local.
 375+ if( !in_array( $this->mDomain, array( 'local', '' ) )
 376+ && !$wgAuth->canCreateAccounts()
 377+ && ( !$wgAuth->userExists( $this->mUsername )
 378+ || !$wgAuth->authenticate( $this->mUsername, $this->mPassword )
 379+ ) )
 380+ {
 381+ $this->mCreateResult = 'wrongpassword';
 382+ return self::WRONG_PLUGIN_PASS;
 383+ }
 384+
 385+ $ip = wfGetIP();
 386+ if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
 387+ $wgUser->inSorbsBlacklist( $ip ) )
 388+ {
 389+ $this->mCreateResult = 'sorbs_create_account_reason';
 390+ return self::CREATE_SORBS;
 391+ }
 392+
 393+ # Now create a dummy user ($user) and check if it is valid
 394+ $name = trim( $this->mName );
 395+ $user = User::newFromName( $name, 'creatable' );
 396+ if ( is_null( $user ) ) {
 397+ $this->mCreateResult = 'noname';
 398+ return self::CREATE_BADNAME;
 399+ }
 400+
 401+ if ( $this->mUser->idForName() != 0 ) {
 402+ $this->mCreateResult = 'userexists';
 403+ return self::CREATE_EXISTS;
 404+ }
 405+
 406+ # Check that the password is acceptable, if we're actually
 407+ # going to use it
 408+ if( !$byEmail ){
 409+ $valid = $this->mUser->isValidPassword( $this->mPassword );
 410+ if ( $valid !== true ) {
 411+ $this->mCreateResult = $valid;
 412+ return self::CREATE_BADPASS;
281413 }
282 - } else {
283 - # Old AuthPlugin.
284 - if( !$wgAuth->autoCreate() ) {
285 - return self::NOT_EXISTS;
 414+ }
 415+
 416+ # if you need a confirmed email address to edit, then obviously you
 417+ # need an email address. Equally if we're going to send the password to it.
 418+ if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) || $byEmail ) {
 419+ $this->mCreateResult = 'noemailcreate';
 420+ return self::CREATE_NEEDEMAIL;
 421+ }
 422+
 423+ if( !empty( $this->mEmail ) && !User::isValidEmailAddr( $this->mEmail ) ) {
 424+ $this->mCreateResult = 'invalidemailaddress';
 425+ return self::CREATE_BADEMAIL;
 426+ }
 427+
 428+ # Set some additional data so the AbortNewAccount hook can be used for
 429+ # more than just username validation
 430+ $this->mUser->setEmail( $this->mEmail );
 431+ $this->mUser->setRealName( $this->mRealName );
 432+
 433+ if( !wfRunHooks( 'AbortNewAccount', array( $this->mUser, &$this->mCreateResult ) ) ) {
 434+ # Hook point to add extra creation throttles and blocks
 435+ wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
 436+ return self::ABORTED;
 437+ }
 438+
 439+ if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
 440+ $key = wfMemcKey( 'acctcreate', 'ip', $ip );
 441+ $value = $wgMemc->get( $key );
 442+ if ( !$value ) {
 443+ $wgMemc->set( $key, 0, 86400 );
286444 }
287 - if( !$wgAuth->userExists( $user->getName() ) ) {
288 - wfDebug( __METHOD__.": user does not exist\n" );
289 - return self::NOT_EXISTS;
 445+ if ( $value >= $wgAccountCreationThrottle ) {
 446+ return self::THROTTLED;
290447 }
291 - if( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) {
292 - wfDebug( __METHOD__.": \$wgAuth->authenticate() returned false, aborting\n" );
293 - return self::WRONG_PLUGIN_PASS;
 448+ $wgMemc->incr( $key );
 449+ }
 450+
 451+ # Since we're creating a new local user, give the external
 452+ # database a chance to synchronise.
 453+ if( !$wgAuth->addUser( $this->mUser, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
 454+ $this->mCreateResult = 'externaldberror';
 455+ return self::ABORTED;
 456+ }
 457+
 458+ $result = $this->initUser( false, $byEmail );
 459+ if( $result === null )
 460+ # It's unlikely we'd get here without some exception
 461+ # being thrown, but it's probably possible...
 462+ return self::FAILED;
 463+
 464+
 465+ # Send out an email message if needed
 466+ if( $byEmail ){
 467+ $this->mailPassword( 'createaccount-title', 'createaccount-text' );
 468+ if( WikiError::isError( $this->mMailResult ) ){
 469+ # FIXME: If the password email hasn't gone out,
 470+ # then the account is inaccessible :(
 471+ return self::MAIL_ERROR;
 472+ } else {
 473+ return self::SUCCESS;
294474 }
 475+ } else {
 476+ if( $wgEmailAuthentication && User::isValidEmailAddr( $this->mUser->getEmail() ) )
 477+ {
 478+ $this->mMailResult = $this->mUser->sendConfirmationMail();
 479+ return WikiError::isError( $this->mMailResult )
 480+ ? self::MAIL_ERROR
 481+ : self::SUCCESS;
 482+ }
295483 }
296 -
297 - wfDebug( __METHOD__.": creating account\n" );
298 - $this->initUser( true );
299 - return self::SUCCESS;
 484+ return true;
300485 }
301486
302487 /**
@@ -308,8 +493,11 @@
309494 global $wgUser, $wgOut, $wgAuth, $wgServer, $wgScript, $wgNewPasswordExpiry;
310495
311496 if( wfReadOnly() )
312 - return self::MAIL_READ_ONLY;
 497+ return self::READ_ONLY;
313498
 499+ # If we let the email go out, it will take users to a form where
 500+ # they are forced to change their password, so don't let us go
 501+ # there if we don't want passwords changed.
314502 if( !$wgAuth->allowPasswordChange() )
315503 return self::MAIL_PASSCHANGE_FORBIDDEN;
316504
@@ -319,23 +507,22 @@
320508 return self::MAIL_BLOCKED;
321509
322510 # Check for hooks
323 - $error = null;
324 - if ( ! wfRunHooks( 'UserLoginMailPassword', array( $this->mName, &$error ) ) )
325 - return $error;
 511+ if( !wfRunHooks( 'UserLoginMailPassword', array( $this->mName, &$this->mMailResult ) ) )
 512+ return self::ABORTED;
326513
327514 # Check against the rate limiter
328515 if( $wgUser->pingLimiter( 'mailpassword' ) )
329516 return self::MAIL_PING_THROTTLED;
330517
331518 # Check for a valid name
332 - if ( '' == $this->mName )
 519+ if ($this->mName === '' )
333520 return self::NO_NAME;
334521 $this->mUser = User::newFromName( $this->mName );
335522 if( is_null( $this->mUser ) )
336523 return self::NO_NAME;
337524
338525 # And that the resulting user actually exists
339 - if ( 0 == $this->mUser->getId() )
 526+ if ( $this->mUser->getId() === 0 )
340527 return self::NOT_EXISTS;
341528
342529 # Check against password throttle
@@ -343,7 +530,7 @@
344531 return self::MAIL_PASS_THROTTLED;
345532
346533 # User doesn't have email address set
347 - if ( '' == $this->mUser->getEmail() )
 534+ if ( $this->mUser->getEmail() === '' )
348535 return self::MAIL_EMPTY_EMAIL;
349536
350537 # Don't send to people who are acting fishily by hiding their IP
@@ -363,7 +550,9 @@
364551 $this->mMailResult = $this->mUser->sendMail( wfMsg( $title ), $message );
365552
366553 if( WikiError::isError( $this->mMailResult ) ) {
367 - return self::MAIL_ERROR;
 554+ #XXX
 555+ var_dump($message);
 556+ return self::SUCCESS;
368557 } else {
369558 return self::SUCCESS;
370559 }
Index: branches/happy-melon/phase3/includes/specials/SpecialUserlogin.php
@@ -51,8 +51,6 @@
5252
5353 function execute( $par ) {
5454 global $wgRequest;
55 - $this->loadQuery();
56 - $this->mLogin = new Login();
5755
5856 # Redirect out for account creation, for B/C
5957 $type = ( $par == 'signup' ) ? $par : $wgRequest->getText( 'type' );
@@ -61,11 +59,14 @@
6260 $sp->execute( $par );
6361 return;
6462 }
 63+
 64+ $this->loadQuery();
 65+ $this->mLogin = new Login();
6566
66 - if ( !is_null( $this->mCookieCheck ) ) {
 67+ if ( $wgRequest->getCheck( 'wpCookieCheck' ) ) {
6768 $this->onCookieRedirectCheck();
6869 return;
69 - } else if( $this->mPosted ) {
 70+ } else if( $wgRequest->wasPosted() ) {
7071 if ( $this->mMailmypassword ) {
7172 return $this->showMailPage();
7273 } else {
@@ -90,24 +91,21 @@
9192
9293 $this->mReturnTo = $wgRequest->getVal( 'returnto' );
9394 $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
94 - $this->mCookieCheck = $wgRequest->getVal( 'wpCookieCheck' );
9595
9696 $this->mMailmypassword = $wgRequest->getCheck( 'wpMailmypassword' )
9797 && $wgEnableEmail;
9898 $this->mRemember = $wgRequest->getCheck( 'wpRemember' );
9999 $this->mSkipCookieCheck = $wgRequest->getCheck( 'wpSkipCookieCheck' );
100 - $this->mPosted = $wgRequest->wasPosted();
101100
 101+ if( !$wgAuth->validDomain( $this->mDomain ) ) {
 102+ $this->mDomain = 'invaliddomain';
 103+ }
 104+ $wgAuth->setDomain( $this->mDomain );
 105+
102106 if ( $wgRedirectOnLogin ) {
103107 $this->mReturnTo = $wgRedirectOnLogin;
104108 $this->mReturnToQuery = '';
105109 }
106 -
107 - if( !$wgAuth->validDomain( $this->mDomain ) ) {
108 - $this->mDomain = 'invaliddomain';
109 - }
110 - $wgAuth->setDomain( $this->mDomain );
111 -
112110 # When switching accounts, it sucks to get automatically logged out
113111 $returnToTitle = Title::newFromText( $this->mReturnTo );
114112 if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) {
@@ -270,7 +268,6 @@
271269 wfMsgExt( 'loginend', array( 'parseinline' ) )
272270 )
273271 );
274 -
275272 }
276273
277274 /**
@@ -284,7 +281,7 @@
285282 */
286283 protected function hasSessionCookie() {
287284 global $wgDisableCookieCheck, $wgRequest;
288 - return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
 285+ return $wgDisableCookieCheck || $wgRequest->checkSessionCookie();
289286 }
290287
291288 /**
@@ -306,10 +303,14 @@
307304 * @param $type String action being performed
308305 */
309306 protected function onCookieRedirectCheck() {
310 - if ( !$this->hasSessionCookie() ) {
 307+ if ( $this->hasSessionCookie() ) {
 308+ return self::successfulLogin(
 309+ 'loginsuccess',
 310+ $this->mReturnTo,
 311+ $this->mReturnToQuery
 312+ );
 313+ } else {
311314 return $this->mainLoginForm( wfMsgExt( 'nocookieslogin', array( 'parseinline' ) ) );
312 - } else {
313 - return self::successfulLogin( 'loginsuccess', $this->mReturnTo, $this->mReturnToQuery );
314315 }
315316 }
316317
@@ -365,50 +366,55 @@
366367
367368 /**
368369 * Display a "login successful" page.
369 - * @param $msgname String message key to display
 370+ * @param $message String message key of main message to display
370371 * @param $html String HTML to optionally add
371372 * @param $returnto Title to returnto
372373 * @param $returntoQuery String query string for returnto link
373374 */
374 - public static function displaySuccessfulLogin( $msgname, $injected_html='', $returnto=false, $returntoQuery=false ) {
 375+ public static function displaySuccessfulLogin( $message, $html='', $returnTo=false, $returnToQuery=false ) {
375376 global $wgOut, $wgUser;
376 -
 377+
377378 $wgOut->setPageTitle( wfMsg( 'loginsuccesstitle' ) );
378379 $wgOut->setRobotPolicy( 'noindex,nofollow' );
379380 $wgOut->setArticleRelated( false );
380 - $wgOut->addWikiMsg( $msgname, $wgUser->getName() );
381 - $wgOut->addHTML( $injected_html );
 381+ $wgOut->addWikiMsg( $message, $wgUser->getName() );
 382+ $wgOut->addHTML( $html );
382383
383 - if ( $returnto ) {
384 - $wgOut->returnToMain( null, $returnto, $this->mReturnToQuery );
 384+ if ( $returnTo ) {
 385+ $wgOut->returnToMain( null, $returnTo, $returnToQuery );
385386 } else {
386387 $wgOut->returnToMain( null );
387388 }
388389 }
389390
390391 /**
391 - * Run any hooks registered for logins, then HTTP redirect to
 392+ * Display any messages generated by hooks, or HTTP redirect to
392393 * $this->mReturnTo (or Main Page if that's undefined). Formerly we had a
393 - * nice message here, but that's really not as useful as just being sent to
 394+ * nice message here, but that's not as useful as just being sent to
394395 * wherever you logged in from. It should be clear that the action was
395396 * successful, given the lack of error messages plus the appearance of your
396397 * name in the upper right.
 398+ *
 399+ * Remember that this function can be accessed from a variety of
 400+ * places, such as Special:ResetPass, or Special:CreateAccount.
 401+ * @param $message String message key of a message to display if
 402+ * we don't redirect
 403+ * @param $returnTo String title of page to redirect to
 404+ * @param $returnToQuery String query string to add to the redirect.
 405+ * @param $html String empty string to go straight
 406+ * to the redirect, or valid HTML to add underneath the text.
397407 */
398 - public static function successfulLogin( $message, $returnTo='', $returnToQuery='' ) {
 408+ public static function successfulLogin( $message, $returnTo='', $returnToQuery='', $html='' ) {
399409 global $wgUser, $wgOut;
400410
401 - # Run any hooks; display injected HTML if any, else redirect
402 - $injected_html = '';
403 - wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
404 -
405 - if( $injected_html !== '' ) {
406 - SpecialUserLogin::displaySuccessfulLogin( $message, $injected_html );
407 - } else {
 411+ if( $html === '' ) {
408412 $titleObj = Title::newFromText( $returnTo );
409413 if ( !$titleObj instanceof Title ) {
410414 $titleObj = Title::newMainPage();
411415 }
412416 $wgOut->redirect( $titleObj->getFullURL( $returnToQuery ) );
 417+ } else {
 418+ SpecialUserLogin::displaySuccessfulLogin( $message, $html, $returnTo, $returnToQuery );
413419 }
414420 }
415421
@@ -424,7 +430,11 @@
425431 global $wgLang, $wgRequest;
426432 $code = $wgRequest->getVal( 'uselang', $wgUser->getOption( 'language' ) );
427433 $wgLang = Language::factory( $code );
428 - return self::successfulLogin( 'loginsuccess', $this->mReturnTo, $this->mReturnToQuery );
 434+ return self::successfulLogin(
 435+ 'loginsuccess',
 436+ $this->mReturnTo,
 437+ $this->mReturnToQuery,
 438+ $this->mLogin->mLoginResult );
429439 } else {
430440 # Do a redirect check to ensure that the cookies are
431441 # being retained by the user's browser.
@@ -453,7 +463,12 @@
454464 $this->mainLoginForm( wfMsg( 'wrongpasswordempty' ) );
455465 break;
456466 case Login::RESET_PASS:
457 - $this->resetLoginForm( wfMsg( 'resetpass_announce' ) );
 467+ # 'Shell out' to Special:ResetPass to get the user to
 468+ # set a new permanent password from a temporary one.
 469+ $reset = new SpecialResetpass();
 470+ $reset->mHeaderMsg = 'resetpass_announce';
 471+ $reset->mHeaderMsgType = 'success';
 472+ $reset->execute( null );
458473 break;
459474 case Login::CREATE_BLOCKED:
460475 $this->userBlockedMessage();
@@ -467,28 +482,15 @@
468483 }
469484
470485 /**
471 - * 'Shell out' to Special:ResetPass to get the user to
472 - * set a new permanent password from a temporary one.
473 - * @param $error String message
474 - */
475 - function resetLoginForm( $error ) {
476 - global $wgOut;
477 - $wgOut->addHTML( Xml::element('p', array( 'class' => 'error' ), $error ) );
478 - $reset = new SpecialResetpass();
479 - $reset->execute( null );
480 - }
481 -
482 - /**
483486 * Attempt to send the user a password-reset mail, and display
484487 * the results (good, bad or ugly).
485 - * @return unknown_type
486488 */
487489 protected function showMailPage(){
488490 global $wgOut;
489491 $result = $this->mLogin->mailPassword();
490492
491493 switch( $result ){
492 - case Login::MAIL_READ_ONLY :
 494+ case Login::READ_ONLY :
493495 $wgOut->readOnlyPage();
494496 return;
495497 case Login::MAIL_PASSCHANGE_FORBIDDEN:
Index: branches/happy-melon/phase3/includes/specials/SpecialCreateAccount.php
@@ -8,6 +8,8 @@
99 var $mUsername, $mPassword, $mRetype, $mReturnTo, $mPosted;
1010 var $mCreateaccountMail, $mRemember, $mEmail, $mDomain, $mLanguage;
1111 var $mReturnToQuery;
 12+
 13+ protected $mLogin;
1214
1315 public $mDomains = array();
1416
@@ -94,17 +96,12 @@
9597 $this->userBlockedMessage();
9698 return;
9799 } elseif ( count( $permErrors = $this->getTitle()->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
98 - var_dump('error');
99100 $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
100101 return;
101102 }
102103
103104 if( $this->mPosted ) {
104 - if ( $this->mCreateaccountMail ) {
105 - return $this->addNewAccountMailPassword();
106 - } else {
107 - return $this->addNewAccount();
108 - }
 105+ $this->addNewAccount( $this->mCreateaccountMail );
109106 } else {
110107 $this->showMainForm('');
111108 }
@@ -160,223 +157,103 @@
161158 }
162159
163160 /**
164 - * Add a new account, and mail its password to the user
 161+ * Create a new user account from the provided data
165162 */
166 - protected function addNewAccountMailPassword() {
167 - global $wgOut;
168 -
169 - if( !$this->mEmail ) {
170 - $this->showMainForm( wfMsg( 'noemail', htmlspecialchars( $this->mUsername ) ) );
171 - return;
 163+ protected function addNewAccount( $byEmail=false ) {
 164+ global $wgUser, $wgEmailAuthentication;
 165+
 166+ # Do a quick check that the user actually managed to type
 167+ # the password in the same both times
 168+ if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
 169+ return $this->showMainForm( wfMsg( 'badretype' ) );
172170 }
173 -
174 - if( !$this->addNewaccountInternal() ) {
175 - return;
176 - }
177 -
178 - # Wipe the initial password
179 - $this->mLogin->mUser->setPassword( null );
180 - $this->mLogin->mUser->saveSettings();
181171
182 - # And mail them a temporary one
183 - $result = $this->mLogin->mailPassword( 'createaccount-title', 'createaccount-text' );
184 -
185 - wfRunHooks( 'AddNewAccount', array( $this->mLogin->mUser, true ) );
186 - $this->mLogin->mUser->addNewUserLogEntry();
187 -
188 - $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
189 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
190 - $wgOut->setArticleRelated( false );
191 -
192 - if( $result != Login::SUCCESS ) {
193 - if( $result == Login::MAIL_ERROR ){
194 - $this->showMainForm( wfMsg( 'mailerror', $this->mLogin->mMailResult->getMessage() ) );
195 - } else {
196 - $this->showMainForm( wfMsg( 'mailerror' ) );
197 - }
198 - } else {
199 - $wgOut->addWikiMsg( 'accmailtext', $this->mLogin->mUser->getName(), $this->mLogin->mUser->getEmail() );
200 - $wgOut->returnToMain( false );
 172+ # Create the account and abort if there's a problem doing so
 173+ $status = $this->mLogin->attemptCreation( $byEmail );
 174+ switch( $status ){
 175+ case Login::SUCCESS:
 176+ case Login::MAIL_ERROR:
 177+ break;
 178+
 179+ case Login::CREATE_BADDOMAIN:
 180+ case Login::CREATE_EXISTS:
 181+ case Login::NO_NAME:
 182+ case Login::CREATE_NEEDEMAIL:
 183+ case Login::CREATE_BADEMAIL:
 184+ case Login::CREATE_BADNAME:
 185+ case Login::WRONG_PLUGIN_PASS:
 186+ case Login::ABORTED:
 187+ return $this->showMainForm( wfMsg( $this->mLogin->mCreateResult ) );
 188+
 189+ case Login::CREATE_SORBS:
 190+ return $this->showMainForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . wfGetIP() . ')' );
 191+
 192+ case Login::CREATE_BLOCKED:
 193+ return $this->userBlockedMessage();
 194+
 195+ case Login::CREATE_BADPASS:
 196+ global $wgMinimalPasswordLength;
 197+ return $this->showMainForm( wfMsgExt( $this->mLogin->mCreateResult, array( 'parsemag' ), $wgMinimalPasswordLength ) );
 198+
 199+ case Login::THROTTLED:
 200+ global $wgAccountCreationThrottle;
 201+ return $this->showMainForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $wgAccountCreationThrottle ) );
 202+
 203+ default:
 204+ throw new MWException( "Unhandled status code $status in " . __METHOD__ );
201205 }
202 - }
203206
204 - /**
205 - * Create a new user account from the provided data
206 - */
207 - protected function addNewAccount() {
208 - global $wgUser, $wgEmailAuthentication;
209 -
210 - # Create the account and abort if there's a problem doing so
211 - if( !$this->addNewAccountInternal() )
212 - return;
213 - $user = $this->mLogin->mUser;
214 -
215207 # If we showed up language selection links, and one was in use, be
216208 # smart (and sensible) and save that language as the user's preference
217209 global $wgLoginLanguageSelector;
218210 if( $wgLoginLanguageSelector && $this->mLanguage )
219 - $user->setOption( 'language', $this->mLanguage );
220 -
221 - # Send out an email authentication message if needed
222 - if( $wgEmailAuthentication && User::isValidEmailAddr( $user->getEmail() ) ) {
223 - global $wgOut;
224 - $error = $user->sendConfirmationMail();
225 - if( WikiError::isError( $error ) ) {
226 - $wgOut->addWikiMsg( 'confirmemail_sendfailed', $error->getMessage() );
 211+ $this->mLogin->mUser->setOption( 'language', $this->mLanguage );
 212+ $this->mLogin->mUser->saveSettings();
 213+
 214+ if( $byEmail ) {
 215+ if( $result == Login::MAIL_ERROR ){
 216+ # FIXME: we are totally screwed if we end up here...
 217+ $this->showMainForm( wfMsg( 'mailerror', $this->mLogin->mMailResult->getMessage() ) );
227218 } else {
228 - $wgOut->addWikiMsg( 'confirmemail_oncreate' );
 219+ $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
 220+ $wgOut->addWikiMsg( 'accmailtext', $this->mLogin->mUser->getName(), $this->mLogin->mUser->getEmail() );
 221+ $wgOut->returnToMain( false );
229222 }
230 - }
231 -
232 - # Save settings (including confirmation token)
233 - $user->saveSettings();
234 -
235 - # If not logged in, assume the new account as the current one and set
236 - # session cookies then show a "welcome" message or a "need cookies"
237 - # message as needed
238 - if( $wgUser->isAnon() ) {
239 - $wgUser = $user;
240 - $wgUser->setCookies();
241 - wfRunHooks( 'AddNewAccount', array( $wgUser ) );
242 - $wgUser->addNewUserLogEntry();
243 - if( $this->hasSessionCookie() ) {
244 - return $this->successfulCreation();
245 - } else {
246 - return $this->cookieRedirectCheck();
247 - }
 223+
248224 } else {
249 - # Confirm that the account was created
250 - global $wgOut;
251 - $self = SpecialPage::getTitleFor( 'Userlogin' );
252 - $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
253 - $wgOut->setArticleRelated( false );
254 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
255 - $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $user->getName() ) );
256 - $wgOut->returnToMain( false, $self );
257 - wfRunHooks( 'AddNewAccount', array( $user ) );
258 - $user->addNewUserLogEntry();
259 - return true;
260 - }
261 - }
262225
263 - /**
264 - * Deeper mechanics of initialising a new user and passing it
265 - * off to Login::initUser()
266 - * return Bool whether the user was successfully created
267 - */
268 - protected function addNewAccountInternal() {
269 - global $wgUser, $wgOut;
270 - global $wgEnableSorbs, $wgProxyWhitelist;
271 - global $wgMemc, $wgAccountCreationThrottle;
272 - global $wgAuth, $wgMinimalPasswordLength;
273 - global $wgEmailConfirmToEdit;
274 -
275 - # If the user passes an invalid domain, something is fishy
276 - if( !$wgAuth->validDomain( $this->mDomain ) ) {
277 - $this->showMainForm( wfMsg( 'wrongpassword' ) );
278 - return false;
279 - }
280 -
281 - # If we are not allowing users to login locally, we should be checking
282 - # to see if the user is actually able to authenticate to the authenti-
283 - # cation server before they create an account (otherwise, they can
284 - # create a local account and login as any domain user). We only need
285 - # to check this for domains that aren't local.
286 - if( !in_array( $this->mDomain, array( 'local', '' ) )
287 - && !$wgAuth->canCreateAccounts()
288 - && ( !$wgAuth->userExists( $this->mUsername )
289 - || !$wgAuth->authenticate( $this->mUsername, $this->mPassword )
290 - ) )
291 - {
292 - $this->showMainForm( wfMsg( 'wrongpassword' ) );
293 - return false;
294 - }
295 -
296 - $ip = wfGetIP();
297 - if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
298 - $wgUser->inSorbsBlacklist( $ip ) )
299 - {
300 - $this->showMainForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . htmlspecialchars( $ip ) . ')' );
301 - return false;
302 - }
303 -
304 - # Now create a dummy user ($user) and check if it is valid
305 - $name = trim( $this->mUsername );
306 - $user = User::newFromName( $name, 'creatable' );
307 - if ( is_null( $user ) ) {
308 - $this->showMainForm( wfMsg( 'noname' ) );
309 - return false;
310 - }
311 -
312 - if ( 0 != $user->idForName() ) {
313 - $this->showMainForm( wfMsg( 'userexists' ) );
314 - return false;
315 - }
316 -
317 - if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
318 - $this->showMainForm( wfMsg( 'badretype' ) );
319 - return false;
320 - }
321 -
322 - # check for minimal password length
323 - $valid = $user->isValidPassword( $this->mPassword );
324 - if ( $valid !== true ) {
325 - if ( !$this->mCreateaccountMail ) {
326 - $this->showMainForm( wfMsgExt( $valid, array( 'parsemag' ), $wgMinimalPasswordLength ) );
327 - return false;
 226+ # There might be a message stored from the confirmation mail
 227+ # send, which we can display.
 228+ if( $wgEmailAuthentication && $this->mLogin->mMailResult ) {
 229+ global $wgOut;
 230+ if( WikiError::isError( $this->mLogin->mMailResult ) ) {
 231+ $wgOut->addWikiMsg( 'confirmemail_sendfailed', $this->mLogin->mMailResult->getMessage() );
 232+ } else {
 233+ $wgOut->addWikiMsg( 'confirmemail_oncreate' );
 234+ }
 235+ }
 236+
 237+ # If not logged in, assume the new account as the current
 238+ # one and set session cookies then show a "welcome" message
 239+ # or a "need cookies" message as needed
 240+ if( $wgUser->isAnon() ) {
 241+ $wgUser = $this->mLogin->mUser;
 242+ $wgUser->setCookies();
 243+ if( $this->hasSessionCookie() ) {
 244+ return $this->successfulCreation();
 245+ } else {
 246+ return $this->cookieRedirectCheck();
 247+ }
328248 } else {
329 - # do not force a password for account creation by email
330 - # set invalid password, it will be replaced later by a random generated password
331 - $this->mPassword = null;
 249+ # Show confirmation that the account was created
 250+ global $wgOut;
 251+ $self = SpecialPage::getTitleFor( 'Userlogin' );
 252+ $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
 253+ $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $this->mLogin->mUser->getName() ) );
 254+ $wgOut->returnToMain( false, $self );
 255+ return true;
332256 }
333257 }
334 -
335 - # if you need a confirmed email address to edit, then obviously you
336 - # need an email address.
337 - if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) ) {
338 - $this->showMainForm( wfMsg( 'noemailtitle' ) );
339 - return false;
340 - }
341 -
342 - if( !empty( $this->mEmail ) && !User::isValidEmailAddr( $this->mEmail ) ) {
343 - $this->showMainForm( wfMsg( 'invalidemailaddress' ) );
344 - return false;
345 - }
346 -
347 - # Set some additional data so the AbortNewAccount hook can be used for
348 - # more than just username validation
349 - $user->setEmail( $this->mEmail );
350 - $user->setRealName( $this->mRealName );
351 -
352 - $abortError = '';
353 - if( !wfRunHooks( 'AbortNewAccount', array( $user, &$abortError ) ) ) {
354 - # Hook point to add extra creation throttles and blocks
355 - wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
356 - $this->showMainForm( $abortError );
357 - return false;
358 - }
359 -
360 - if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
361 - $key = wfMemcKey( 'acctcreate', 'ip', $ip );
362 - $value = $wgMemc->get( $key );
363 - if ( !$value ) {
364 - $wgMemc->set( $key, 0, 86400 );
365 - }
366 - if ( $value >= $wgAccountCreationThrottle ) {
367 - $this->showMainForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $wgAccountCreationThrottle ) );
368 - return false;
369 - }
370 - $wgMemc->incr( $key );
371 - }
372 -
373 - if( !$wgAuth->addUser( $user, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
374 - $this->showMainForm( wfMsg( 'externaldberror' ) );
375 - return false;
376 - }
377 -
378 - $this->mLogin->mUser = $user;
379 - $this->mLogin->initUser( false );
380 - return true;
381258 }
382259
383260 /**
@@ -617,10 +494,11 @@
618495 if ( !$this->hasSessionCookie() ) {
619496 return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) );
620497 } else {
621 - return SpecialUserLogin::successfulLogin(
622 - 'welcomecreate',
 498+ return SpecialUserlogin::successfulLogin(
 499+ array( 'welcomecreate' ),
623500 $this->mReturnTo,
624 - $this->mReturnToQuery );
 501+ $this->mReturnToQuery
 502+ );
625503 }
626504 }
627505
Index: branches/happy-melon/phase3/includes/specials/SpecialResetpass.php
@@ -12,6 +12,48 @@
1313 public function __construct() {
1414 parent::__construct( 'Resetpass' );
1515 }
 16+
 17+ public $mFormFields = array(
 18+ 'Name' => array(
 19+ 'type' => 'info',
 20+ 'label-message' => 'yourname',
 21+ 'default' => '',
 22+ ),
 23+ 'Password' => array(
 24+ 'type' => 'password',
 25+ 'label-message' => 'oldpassword',
 26+ 'size' => '20',
 27+ 'id' => 'wpPassword',
 28+ 'required' => '',
 29+ ),
 30+ 'NewPassword' => array(
 31+ 'type' => 'password',
 32+ 'label-message' => 'newpassword',
 33+ 'size' => '20',
 34+ 'id' => 'wpNewPassword',
 35+ 'required' => '',
 36+ ),
 37+ 'Retype' => array(
 38+ 'type' => 'password',
 39+ 'label-message' => 'retypenew',
 40+ 'size' => '20',
 41+ 'id' => 'wpRetype',
 42+ 'required' => '',
 43+ ),
 44+ 'Remember' => array(
 45+ 'type' => 'check',
 46+ 'label-message' => 'remembermypassword',
 47+ 'id' => 'wpRemember',
 48+ ),
 49+ );
 50+ public $mSubmitMsg = 'resetpass-submit-loggedin';
 51+ public $mHeaderMsg = '';
 52+ public $mHeaderMsgType = 'error';
 53+
 54+ protected $mUsername;
 55+ protected $mOldpass;
 56+ protected $mNewpass;
 57+ protected $mRetype;
1658
1759 /**
1860 * Main execution point
@@ -19,175 +61,142 @@
2062 function execute( $par ) {
2163 global $wgUser, $wgAuth, $wgOut, $wgRequest;
2264
23 - $this->mUserName = $wgRequest->getVal( 'wpName' );
 65+ $this->mUsername = $wgRequest->getVal( 'wpName', $wgUser->getName() );
2466 $this->mOldpass = $wgRequest->getVal( 'wpPassword' );
2567 $this->mNewpass = $wgRequest->getVal( 'wpNewPassword' );
2668 $this->mRetype = $wgRequest->getVal( 'wpRetype' );
 69+ $this->mRemember = $wgRequest->getVal( 'wpRemember' );
 70+ $this->mReturnTo = $wgRequest->getVal( 'returnto' );
 71+ $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
2772
2873 $this->setHeaders();
2974 $this->outputHeader();
3075
3176 if( !$wgAuth->allowPasswordChange() ) {
32 - $this->error( wfMsg( 'resetpass_forbidden' ) );
33 - return;
 77+ $wgOut->showErrorPage( 'errorpagetitle', 'resetpass_forbidden' );
 78+ return false;
3479 }
3580
3681 if( !$wgRequest->wasPosted() && !$wgUser->isLoggedIn() ) {
37 - $this->error( wfMsg( 'resetpass-no-info' ) );
38 - return;
 82+ $wgOut->showErrorPage( 'errorpagetitle', 'resetpass-no-info' );
 83+ return false;
3984 }
4085
41 - if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getVal('token') ) ) {
42 - try {
43 - $this->attemptReset( $this->mNewpass, $this->mRetype );
44 - $wgOut->addWikiMsg( 'resetpass_success' );
45 - if( !$wgUser->isLoggedIn() ) {
46 - $data = array(
47 - 'wpName' => $this->mUserName,
48 - 'wpPassword' => $this->mNewpass,
49 - 'returnto' => $wgRequest->getVal( 'returnto' ),
50 - );
51 - if( $wgRequest->getCheck( 'wpRemember' ) ) {
52 - $data['wpRemember'] = 1;
53 - }
54 - $login = new LoginForm( new FauxRequest( $data, true ) );
55 - $login->login();
 86+ if( $wgRequest->wasPosted()
 87+ && $wgUser->matchEditToken( $wgRequest->getVal('wpEditToken') )
 88+ && $this->attemptReset() )
 89+ {
 90+ # Log the user in if they're not already (ie we're
 91+ # coming from the e-mail-password-reset route
 92+ if( !$wgUser->isLoggedIn() ) {
 93+ $data = array(
 94+ 'wpName' => $this->mUsername,
 95+ 'wpPassword' => $this->mNewpass,
 96+ 'returnto' => $this->mReturnTo,
 97+ );
 98+ if( $this->mRemember ) {
 99+ $data['wpRemember'] = 1;
56100 }
57 - $titleObj = Title::newFromText( $wgRequest->getVal( 'returnto' ) );
58 - if ( !$titleObj instanceof Title ) {
59 - $titleObj = Title::newMainPage();
60 - }
61 - $wgOut->redirect( $titleObj->getFullURL() );
62 - } catch( PasswordError $e ) {
63 - $this->error( $e->getMessage() );
 101+ $login = new Login( new FauxRequest( $data, true ) );
 102+ $login->attemptLogin();
 103+
 104+ # Redirect out to the appropriate target.
 105+ SpecialUserlogin::successfulLogin(
 106+ 'resetpass_success',
 107+ $this->mReturnTo,
 108+ $this->mReturnToQuery,
 109+ $login->mLoginResult
 110+ );
 111+ } else {
 112+ # Redirect out to the appropriate target.
 113+ SpecialUserlogin::successfulLogin(
 114+ 'resetpass_success',
 115+ $this->mReturnTo,
 116+ $this->mReturnToQuery
 117+ );
64118 }
 119+ } else {
 120+ $this->showForm();
65121 }
66 - $this->showForm();
67122 }
68123
69 - function error( $msg ) {
70 - global $wgOut;
71 - $wgOut->addHTML( Xml::element('p', array( 'class' => 'error' ), $msg ) );
72 - }
73 -
74124 function showForm() {
75 - global $wgOut, $wgUser, $wgRequest;
 125+ global $wgOut, $wgUser;
76126
77127 $wgOut->disallowUserJs();
78 -
79 - $self = SpecialPage::getTitleFor( 'Resetpass' );
80 - if ( !$this->mUserName ) {
81 - $this->mUserName = $wgUser->getName();
82 - }
83 - $rememberMe = '';
84 - if ( !$wgUser->isLoggedIn() ) {
85 - $rememberMe = '<tr>' .
86 - '<td></td>' .
87 - '<td class="mw-input">' .
88 - Xml::checkLabel( wfMsg( 'remembermypassword' ),
89 - 'wpRemember', 'wpRemember',
90 - $wgRequest->getCheck( 'wpRemember' ) ) .
91 - '</td>' .
92 - '</tr>';
93 - $submitMsg = 'resetpass_submit';
94 - $oldpassMsg = 'resetpass-temp-password';
 128+
 129+ if( $wgUser->isLoggedIn() ){
 130+ unset( $this->mFormFields['Remember'] );
95131 } else {
96 - $oldpassMsg = 'oldpassword';
97 - $submitMsg = 'resetpass-submit-loggedin';
 132+ # Request is coming from Special:UserLogin after it
 133+ # authenticated someone with a temporary password.
 134+ $this->mFormFields['Password']['label-message'] = 'resetpass-temp-password';
 135+ $this->mSubmitMsg = 'resetpass_submit';
98136 }
 137+ $this->mFormFields['Name']['default'] = $this->mUsername;
 138+
 139+ $header = $this->mHeaderMsg
 140+ ? Xml::element( 'div', array( 'class' => "{$this->mHeaderMsgType}box" ), wfMsg( $this->mHeaderMsg ) )
 141+ : '';
 142+
 143+ $form = new HTMLForm( $this->mFormFields, '' );
 144+ $form->suppressReset();
 145+ $form->setSubmitText( wfMsg( $this->mSubmitMsg ) );
 146+ $form->setTitle( $this->getTitle() );
 147+ $form->loadData();
 148+
 149+ $formContents = ''
 150+ . $form->getBody()
 151+ . $form->getButtons()
 152+ . $form->getHiddenFields()
 153+ . Html::hidden( 'wpName', $this->mUsername )
 154+ . Html::hidden( 'returnto', $this->mReturnTo )
 155+ ;
 156+ $formOutput = $form->wrapForm( $formContents );
 157+
99158 $wgOut->addHTML(
100 - Xml::fieldset( wfMsg( 'resetpass_header' ) ) .
101 - Xml::openElement( 'form',
102 - array(
103 - 'method' => 'post',
104 - 'action' => $self->getLocalUrl(),
105 - 'id' => 'mw-resetpass-form' ) ) . "\n" .
106 - Xml::hidden( 'token', $wgUser->editToken() ) . "\n" .
107 - Xml::hidden( 'wpName', $this->mUserName ) . "\n" .
108 - Xml::hidden( 'returnto', $wgRequest->getVal( 'returnto' ) ) . "\n" .
109 - wfMsgExt( 'resetpass_text', array( 'parse' ) ) . "\n" .
110 - Xml::openElement( 'table', array( 'id' => 'mw-resetpass-table' ) ) . "\n" .
111 - $this->pretty( array(
112 - array( 'wpName', 'username', 'text', $this->mUserName ),
113 - array( 'wpPassword', $oldpassMsg, 'password', $this->mOldpass ),
114 - array( 'wpNewPassword', 'newpassword', 'password', null ),
115 - array( 'wpRetype', 'retypenew', 'password', null ),
116 - ) ) . "\n" .
117 - $rememberMe .
118 - "<tr>\n" .
119 - "<td></td>\n" .
120 - '<td class="mw-input">' .
121 - Xml::submitButton( wfMsg( $submitMsg ) ) .
122 - "</td>\n" .
123 - "</tr>\n" .
124 - Xml::closeElement( 'table' ) .
125 - Xml::closeElement( 'form' ) .
126 - Xml::closeElement( 'fieldset' ) . "\n"
 159+ $header
 160+ . Html::rawElement( 'fieldset', array( 'class' => 'visualClear' ), ''
 161+ . Html::element( 'legend', array(), wfMsg( 'resetpass_header' ) )
 162+ . $formOutput
 163+ )
127164 );
128165 }
129166
130 - function pretty( $fields ) {
131 - $out = '';
132 - foreach ( $fields as $list ) {
133 - list( $name, $label, $type, $value ) = $list;
134 - if( $type == 'text' ) {
135 - $field = htmlspecialchars( $value );
136 - } else {
137 - $attribs = array( 'id' => $name );
138 - if ( $name == 'wpNewPassword' || $name == 'wpRetype' ) {
139 - $attribs = array_merge( $attribs,
140 - User::passwordChangeInputAttribs() );
141 - }
142 - if ( $name == 'wpPassword' ) {
143 - $attribs[] = 'autofocus';
144 - }
145 - $field = Html::input( $name, $value, $type, $attribs );
146 - }
147 - $out .= "<tr>\n";
148 - $out .= "\t<td class='mw-label'>";
149 - if ( $type != 'text' )
150 - $out .= Xml::label( wfMsg( $label ), $name );
151 - else
152 - $out .= wfMsgHtml( $label );
153 - $out .= "</td>\n";
154 - $out .= "\t<td class='mw-input'>";
155 - $out .= $field;
156 - $out .= "</td>\n";
157 - $out .= "</tr>";
158 - }
159 - return $out;
160 - }
161 -
162167 /**
163 - * @throws PasswordError when cannot set the new password because requirements not met.
 168+ * Try to reset the user's password
164169 */
165 - protected function attemptReset( $newpass, $retype ) {
166 - $user = User::newFromName( $this->mUserName );
 170+ protected function attemptReset() {
 171+ $user = User::newFromName( $this->mUsername );
167172 if( !$user || $user->isAnon() ) {
168 - throw new PasswordError( 'no such user' );
 173+ $this->mHeaderMsg = 'no such user';
 174+ return false;
169175 }
170176
171 - if( $newpass !== $retype ) {
172 - wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'badretype' ) );
173 - throw new PasswordError( wfMsg( 'badretype' ) );
 177+ if( $this->mNewpass !== $this->mRetype ) {
 178+ wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'badretype' ) );
 179+ $this->mHeaderMsg = 'badretype';
 180+ return false;
174181 }
175182
176183 if( !$user->checkTemporaryPassword($this->mOldpass) && !$user->checkPassword($this->mOldpass) ) {
177 - wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'wrongpassword' ) );
178 - throw new PasswordError( wfMsg( 'resetpass-wrong-oldpass' ) );
 184+ wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'wrongpassword' ) );
 185+ $this->mHeaderMsg = 'resetpass-wrong-oldpass';
 186+ return false;
179187 }
180188
181189 try {
182190 $user->setPassword( $this->mNewpass );
183 - wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'success' ) );
 191+ wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'success' ) );
184192 $this->mNewpass = $this->mOldpass = $this->mRetypePass = '';
185193 } catch( PasswordError $e ) {
186 - wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'error' ) );
187 - throw new PasswordError( $e->getMessage() );
188 - return;
 194+ wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'error' ) );
 195+ $this->mHeaderMsg = $e->getMessage();
 196+ return false;
189197 }
190198
191199 $user->setCookies();
192200 $user->saveSettings();
 201+ return true;
193202 }
194203 }
Index: branches/happy-melon/phase3/languages/messages/MessagesEn.php
@@ -1090,6 +1090,7 @@
10911091 and you no longer wish to change it, you may ignore this message and
10921092 continue using your old password.',
10931093 'noemail' => 'There is no e-mail address recorded for user "$1".',
 1094+'noemailcreate' => 'You need to provide a valid email address',
10941095 'passwordsent' => 'A new password has been sent to the e-mail address registered for "$1".
10951096 Please log in again after you receive it.',
10961097 'blocked-mailpassword' => 'Your IP address is blocked from editing, and so is not allowed to use the password recovery function to prevent abuse.',

Follow-up revisions

RevisionCommit summaryAuthorDate
r56274Follow-up to r56256, per comments in code reviewhappy-melon17:43, 13 September 2009
r56275Follow-up to r56256: use $wgLang->formatNum() as appropriatehappy-melon17:46, 13 September 2009

Comments

#Comment by Nikerabbit (talk | contribs)   13:59, 13 September 2009
+		$form = new HTMLForm( $this->mFormFields,  );

Should the constructor be fixed? The '' looks like bad design.

+				$wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $this->mLogin->mUser->getName() ) );

addWikiMsg?

+		if( $code != self::SUCCESS ){

since self::SUCCESS is defined as 0, I wouldn't use != here, but !== isntead.

+		if ( is_null( $user ) ) {

Dunno if it is a agreed convention but I use === null.

+	 * @return Status code; Login::SUCCESS == the user was successfully created

But then you do:

-		return self::SUCCESS;
+		return true;
+				return $this->showMainForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $wgAccountCreationThrottle ) ); 

Does showMainForm take HTML or not? If it does, there is many raw-html messages, and if not, the above one will not work.
wgAccountCreationThrottle and wgMinimalPasswordLength probably should be formatted with $wgLang->formatNum().

#Comment by Happy-melon (talk | contribs)   17:44, 13 September 2009

Mostly done in r56274. What do you mean by the last part?

#Comment by Happy-melon (talk | contribs)   17:45, 13 September 2009

(I mean "Does showMainForm take HTML or not? If it does, there is many raw-html messages, and if not, the above one will not work")

Status & tagging log