Merge in Login rewrite, second time lucky.
authorHappy-melon <happy-melon@users.mediawiki.org>
Sun, 20 Sep 2009 20:28:27 +0000 (20:28 +0000)
committerHappy-melon <happy-melon@users.mediawiki.org>
Sun, 20 Sep 2009 20:28:27 +0000 (20:28 +0000)
14 files changed:
RELEASE-NOTES
docs/hooks.txt
includes/AuthPlugin.php
includes/AutoLoader.php
includes/Login.php [new file with mode: 0644]
includes/SpecialPage.php
includes/api/ApiLogin.php
includes/specials/SpecialCreateAccount.php [new file with mode: 0644]
includes/specials/SpecialResetpass.php
includes/specials/SpecialUserlogin.php
includes/templates/Userlogin.php [deleted file]
languages/messages/MessagesEn.php
languages/messages/MessagesQqq.php
skins/common/shared.css

index 85681c9..1838004 100644 (file)
@@ -87,6 +87,11 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
   correctly (img_auth only)
 * $wgUploadMaintenance added to disable file deletions and restorations during
   maintenance
+* UserLoginForm and UserCreateForm hooks, and AuthPlugin::modifyUITemplate, now receive a
+  SpecialPage subclass instead of a QuickTemplate subclass.  Hence there is no
+  $template->set(), etc.  The hook has access to most of the stuff that will go into the
+  Login/Create form; see the documentation on HTMLForm for syntax for extra fields.
+  LoginForm class is deprecated, its state constants are now in the Login class.
 
 === New features in 1.16 ===
 
index 11a0e50..325a062 100644 (file)
@@ -244,8 +244,8 @@ $block: The block from which the autoblock is coming.
 'AbortLogin': Return false to cancel account login.
 $user: the User object being authenticated against
 $password: the password being submitted, not yet checked for validity
-&$retval: a LoginForm class constant to return from authenticateUserData();
-          default is LoginForm::ABORTED. Note that the client may be using
+&$retval: a Login class constant to return from authenticateUserData();
+          default is Login::ABORTED. Note that the client may be using
           a machine API rather than the HTML user interface.
 
 'AbortMove': allows to abort moving an article (title)
@@ -944,7 +944,7 @@ $code: language code
 succeeded or failed. No return data is accepted; this hook is for auditing only.
 $user: the User object being authenticated against
 $password: the password being submitted and found wanting
-$retval: a LoginForm class constant with authenticateUserData() return
+$retval: a Login class constant with authenticateUserData() return
        value (SUCCESS, WRONG_PASS, etc)
 
 'LogLine': Processes a single log entry on Special:Log
@@ -1526,7 +1526,7 @@ override the default password checks
        determine if the password was valid
 
 'UserCreateForm': change to manipulate the login form
-$template: SimpleTemplate instance for the form
+$sp: SpecialCreateAccount instance
 
 'UserCryptPassword': called when hashing a password, return false to implement
 your own hashing method
@@ -1596,7 +1596,7 @@ $user: the user object that was created on login
 $inject_html: Any HTML to inject after the "logged in" message.
 
 'UserLoginForm': change to manipulate the login form
-$template: SimpleTemplate instance for the form
+$sp: SpecialCreateAccount instance
 
 'UserLoginMailPassword': Block users from emailing passwords
 $name: the username to email the password of.
index eacabd4..97cb11f 100644 (file)
@@ -62,12 +62,13 @@ class AuthPlugin {
        /**
         * Modify options in the login template.
         *
-        * @param $template UserLoginTemplate object.
-        * @param $type String 'signup' or 'login'.
+        * @param $sp SpecialUserlogin or SpecialCreateAccount object.
+        * @param $type String 'signup' or 'login'. Redundant because
+        *    you can just use instanceof to tell the two cases apart.
         */
-       public function modifyUITemplate( &$template, &$type ) {
+       public function modifyUITemplate( &$sp, $type=null ) {
                # Override this!
-               $template->set( 'usedomain', false );
+               $sp->mDomains = false;
        }
 
        /**
index 7b7303d..3f4ecf4 100644 (file)
@@ -134,6 +134,8 @@ $wgAutoloadLocalClasses = array(
        'LinksUpdate' => 'includes/LinksUpdate.php',
        'LocalisationCache' => 'includes/LocalisationCache.php',
        'LocalisationCache_BulkLoad' => 'includes/LocalisationCache.php',
+       'LoginForm' => 'includes/Login.php', # For B/C
+       'Login' => 'includes/Login.php',
        'LogPage' => 'includes/LogPage.php',
        'LogPager' => 'includes/LogEventsList.php',
        'LogEventsList' => 'includes/LogEventsList.php',
@@ -493,6 +495,7 @@ $wgAutoloadLocalClasses = array(
        'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php',
        'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php',
        'ContribsPager' => 'includes/specials/SpecialContributions.php',
+       'SpecialCreateAccount' => 'includes/specials/SpecialCreateAccount.php',
        'DBLockForm' => 'includes/specials/SpecialLockdb.php',
        'DBUnlockForm' => 'includes/specials/SpecialUnlockdb.php',
        'DeadendPagesPage' => 'includes/specials/SpecialDeadendpages.php',
@@ -513,7 +516,6 @@ $wgAutoloadLocalClasses = array(
        'ImportStringSource' => 'includes/Import.php',
        'LinkSearchPage' => 'includes/specials/SpecialLinkSearch.php',
        'ListredirectsPage' => 'includes/specials/SpecialListredirects.php',
-       'LoginForm' => 'includes/specials/SpecialUserlogin.php',
        'LonelyPagesPage' => 'includes/specials/SpecialLonelypages.php',
        'LongPagesPage' => 'includes/specials/SpecialLongpages.php',
        'MIMEsearchPage' => 'includes/specials/SpecialMIMEsearch.php',
@@ -562,6 +564,7 @@ $wgAutoloadLocalClasses = array(
        'UnwatchedpagesPage' => 'includes/specials/SpecialUnwatchedpages.php',
        'UploadForm' => 'includes/specials/SpecialUpload.php',
        'UploadFormMogile' => 'includes/specials/SpecialUploadMogile.php',
+       'SpecialUserLogin' => 'includes/specials/SpecialUserlogin.php',
        'UserrightsPage' => 'includes/specials/SpecialUserrights.php',
        'UsersPager' => 'includes/specials/SpecialListusers.php',
        'WantedCategoriesPage' => 'includes/specials/SpecialWantedcategories.php',
diff --git a/includes/Login.php b/includes/Login.php
new file mode 100644 (file)
index 0000000..a2d8879
--- /dev/null
@@ -0,0 +1,565 @@
+<?php
+
+/**
+ * Encapsulates the backend activities of logging a user into the wiki.
+ */
+class Login {
+
+       const SUCCESS = 0;
+       const NO_NAME = 1;
+       const ILLEGAL = 2;
+       const WRONG_PLUGIN_PASS = 3;
+       const NOT_EXISTS = 4;
+       const WRONG_PASS = 5;
+       const EMPTY_PASS = 6;
+       const RESET_PASS = 7;
+       const ABORTED = 8;
+       const THROTTLED = 10;
+       const FAILED = 11;
+       const READ_ONLY = 12;
+       
+       const MAIL_PASSCHANGE_FORBIDDEN = 21;
+       const MAIL_BLOCKED = 22;
+       const MAIL_PING_THROTTLED = 23;
+       const MAIL_PASS_THROTTLED = 24;
+       const MAIL_EMPTY_EMAIL = 25;
+       const MAIL_BAD_IP = 26;
+       const MAIL_ERROR = 27;
+       
+       const CREATE_BLOCKED = 40;
+       const CREATE_EXISTS = 41;
+       const CREATE_SORBS = 42;
+       const CREATE_BADDOMAIN = 43;
+       const CREATE_BADNAME = 44;
+       const CREATE_BADPASS = 45;
+       const CREATE_NEEDEMAIL = 46;
+       const CREATE_BADEMAIL = 47;
+
+       protected $mName;
+       protected $mPassword;
+       public $mRemember; # 0 or 1
+       public $mEmail;
+       public $mDomain;
+       public $mRealname;
+
+       private $mExtUser = null;
+       
+       public $mUser;
+       
+       public $mLoginResult = '';
+       public $mMailResult = '';
+       public $mCreateResult = '';
+
+       /**
+        * Constructor
+        * @param WebRequest $request A WebRequest object passed by reference.
+        *     uses $wgRequest if not given.
+        */
+       public function __construct( &$request=null ) {
+               global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
+               if( !$request ) $request = &$wgRequest;
+
+               $this->mName = $request->getText( 'wpName' );
+               $this->mPassword = $request->getText( 'wpPassword' );
+               $this->mDomain = $request->getText( 'wpDomain' );
+               $this->mRemember = $request->getCheck( 'wpRemember' ) ? 1 : 0;
+
+               if( $wgEnableEmail ) {
+                       $this->mEmail = $request->getText( 'wpEmail' );
+               } else {
+                       $this->mEmail = '';
+               }
+               if( !in_array( 'realname', $wgHiddenPrefs ) ) {
+                   $this->mRealName = $request->getText( 'wpRealName' );
+               } else {
+                   $this->mRealName = '';
+               }
+
+               if( !$wgAuth->validDomain( $this->mDomain ) ) {
+                       $this->mDomain = 'invaliddomain';
+               }
+               $wgAuth->setDomain( $this->mDomain );
+
+               # Load the user, if they exist in the local database.
+               $this->mUser = User::newFromName( trim( $this->mName ), 'usable' );
+       }
+       
+       /**
+        * Having initialised the Login object with (at least) the wpName
+        * and wpPassword pair, attempt to authenticate the user and log
+        * them into the wiki.  Authentication may come from the local 
+        * user database, or from an AuthPlugin- or ExternalUser-based
+        * foreign database; in the latter case, a local user record may
+        * or may not be created and initialised.  
+        * @return a Login class constant representing the status.
+        */
+       public function attemptLogin(){
+               global $wgUser;
+               
+               $code = $this->authenticateUserData();
+               if( $code != self::SUCCESS ){
+                       return $code;
+               }
+               
+               # Log the user in and remember them if they asked for that.
+               if( (bool)$this->mRemember != (bool)$wgUser->getOption( 'rememberpassword' ) ) {
+                       $wgUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
+                       $wgUser->saveSettings();
+               } else {
+                       $wgUser->invalidateCache();
+               }
+               $wgUser->setCookies();
+
+               # Reset the password throttle
+               $key = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
+               global $wgMemc;
+               $wgMemc->delete( $key );
+               
+               wfRunHooks( 'UserLoginComplete', array( &$wgUser, &$this->mLoginResult ) );
+               
+               return self::SUCCESS;
+       }
+
+       /**
+        * Check whether there is an external authentication mechanism from
+        * which we can automatically authenticate the user and create a 
+        * local account for them. 
+        * @return integer Status code.  Login::SUCCESS == clear to proceed
+        *    with user creation.
+        */
+       protected function canAutoCreate() {
+               global $wgAuth, $wgUser, $wgAutocreatePolicy;
+
+               if( $wgUser->isBlockedFromCreateAccount() ) {
+                       wfDebug( __METHOD__.": user is blocked from account creation\n" );
+                       return self::CREATE_BLOCKED;
+               }
+
+               # If the external authentication plugin allows it, automatically 
+               # create a new account for users that are externally defined but 
+               # have not yet logged in.
+               if( $this->mExtUser ) {
+                       # mExtUser is neither null nor false, so use the new 
+                       # ExternalAuth system.
+                       if( $wgAutocreatePolicy == 'never' ) {
+                               return self::NOT_EXISTS;
+                       }
+                       if( !$this->mExtUser->authenticate( $this->mPassword ) ) {
+                               return self::WRONG_PLUGIN_PASS;
+                       }
+               } else {
+                       # Old AuthPlugin.
+                       if( !$wgAuth->autoCreate() ) {
+                               return self::NOT_EXISTS;
+                       }
+                       if( !$wgAuth->userExists( $this->mUser->getName() ) ) {
+                               wfDebug( __METHOD__.": user does not exist\n" );
+                               return self::NOT_EXISTS;
+                       }
+                       if( !$wgAuth->authenticate( $this->mUser->getName(), $this->mPassword ) ) {
+                               wfDebug( __METHOD__.": \$wgAuth->authenticate() returned false, aborting\n" );
+                               return self::WRONG_PLUGIN_PASS;
+                       }
+               }
+
+               return self::SUCCESS;
+       }
+
+       /**
+        * Internally authenticate the login request.
+        *
+        * This may create a local account as a side effect if the
+        * authentication plugin allows transparent local account
+        * creation.
+        */
+       protected function authenticateUserData() {
+               global $wgUser, $wgAuth;
+               
+               if ( '' == $this->mName ) {
+                       return self::NO_NAME;
+               }
+               
+               global $wgPasswordAttemptThrottle;
+               $throttleCount = 0;
+               if ( is_array( $wgPasswordAttemptThrottle ) ) {
+                       $throttleKey = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
+                       $count = $wgPasswordAttemptThrottle['count'];
+                       $period = $wgPasswordAttemptThrottle['seconds'];
+                       
+                       global $wgMemc;
+                       $throttleCount = $wgMemc->get( $throttleKey );
+                       if ( !$throttleCount ) {
+                               $wgMemc->add( $throttleKey, 1, $period ); # Start counter
+                       } else if ( $throttleCount < $count ) {
+                               $wgMemc->incr($throttleKey);
+                       } else if ( $throttleCount >= $count ) {
+                               return self::THROTTLED;
+                       }
+               }
+
+               # Unstub $wgUser now, and check to see if we're logging in as the same
+               # name. As well as the obvious, unstubbing $wgUser (say by calling
+               # getName()) calls the UserLoadFromSession hook, which potentially
+               # creates the user in the database. Until we load $wgUser, checking
+               # for user existence using User::newFromName($name)->getId() below
+               # will effectively be using stale data.
+               if ( $wgUser->getName() === $this->mName ) {
+                       wfDebug( __METHOD__.": already logged in as {$this->mName}\n" );
+                       return self::SUCCESS;
+               }
+
+               $this->mExtUser = ExternalUser::newFromName( $this->mName );
+
+               # TODO: Allow some magic here for invalid external names, e.g., let the
+               # user choose a different wiki name.
+               if( is_null( $this->mUser ) || !User::isUsableName( $this->mUser->getName() ) ) {
+                       return self::ILLEGAL;
+               }
+
+               # If the user doesn't exist in the local database, our only chance 
+               # is for an external auth plugin to autocreate the local user.
+               if ( $this->mUser->getID() == 0 ) {
+                       if ( $this->canAutoCreate() == self::SUCCESS ) {
+                               $isAutoCreated = true;
+                               wfDebug( __METHOD__.": creating account\n" );
+                               $this->initUser( true );
+                       } else {
+                               return $this->canAutoCreate();
+                       }
+               } else {
+                       $isAutoCreated = false;
+                       $this->mUser->load();
+               }
+
+               # Give general extensions, such as a captcha, a chance to abort logins
+               $abort = self::ABORTED;
+               if( !wfRunHooks( 'AbortLogin', array( $this->mUser, $this->mPassword, &$abort ) ) ) {
+                       return $abort;
+               }
+
+               if( !$this->mUser->checkPassword( $this->mPassword ) ) {
+                       if( $this->mUser->checkTemporaryPassword( $this->mPassword ) ) {
+                               # The e-mailed temporary password should not be used for actual
+                               # logins; that's a very sloppy habit, and insecure if an
+                               # attacker has a few seconds to click "search" on someone's 
+                               # open mail reader.
+                               #
+                               # Allow it to be used only to reset the password a single time
+                               # to a new value, which won't be in the user's e-mail archives
+                               #
+                               # For backwards compatibility, we'll still recognize it at the
+                               # login form to minimize surprises for people who have been
+                               # logging in with a temporary password for some time.
+                               #
+                               # As a side-effect, we can authenticate the user's e-mail ad-
+                               # dress if it's not already done, since the temporary password
+                               # was sent via e-mail.
+                               if( !$this->mUser->isEmailConfirmed() ) {
+                                       $this->mUser->confirmEmail();
+                                       $this->mUser->saveSettings();
+                               }
+
+                               # At this point we just return an appropriate code/ indicating
+                               # that the UI should show a password reset form; bot interfaces
+                               # etc will probably just fail cleanly here.
+                               $retval = self::RESET_PASS;
+                       } else {
+                               $retval = ( $this->mPassword === '' ) ? self::EMPTY_PASS : self::WRONG_PASS;
+                       }
+               } else {
+                       $wgAuth->updateUser( $this->mUser );
+                       $wgUser = $this->mUser;
+
+                       # Reset throttle after a successful login
+                       if( $throttleCount ) {
+                               $wgMemc->delete( $throttleKey );
+                       }
+
+                       if( $isAutoCreated ) {
+                               # Must be run after $wgUser is set, for correct new user log
+                               wfRunHooks( 'AuthPluginAutoCreate', array( $wgUser ) );
+                       }
+
+                       $retval = self::SUCCESS;
+               }
+               wfRunHooks( 'LoginAuthenticateAudit', array( $this->mUser, $this->mPassword, $retval ) );
+               return $retval;
+       }
+
+       /**
+        * Actually add a user to the database.
+        * Give it a User object that has been initialised with a name.
+        *
+        * @param $autocreate Bool is this is an autocreation from an external
+        *   authentication database?
+        * @param $byEmail Bool is this request going to be handled by sending
+        *   the password by email?
+        * @return Bool whether creation was successful (should only fail for
+        *   Db errors etc).
+        */
+       protected function initUser( $autocreate=false, $byEmail=false ) {
+               global $wgAuth;
+
+               $fields = array(
+                       'name' => $this->mName,
+                       'password' => $byEmail ? null : $this->mPassword,
+                       'email' => $this->mEmail,
+                       'options' => array(
+                               'rememberpassword' => $this->mRemember ? 1 : 0,
+                       ),
+               );
+               
+               $this->mUser = User::createNew( $this->mName, $fields );
+               
+               if( $this->mUser === null ){
+                       return null;
+               }
+
+               # Let old AuthPlugins play with the user
+               $wgAuth->initUser( $this->mUser, $autocreate );
+
+               # Or new ExternalUser plugins
+               if( $this->mExtUser ) {
+                       $this->mExtUser->link( $this->mUser->getId() );
+                       $email = $this->mExtUser->getPref( 'emailaddress' );
+                       if( $email && !$this->mEmail ) {
+                               $this->mUser->setEmail( $email );
+                       }
+               }
+
+               # Update user count and newuser logs
+               $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
+               $ssUpdate->doUpdate();
+               if( $autocreate )
+                       $this->mUser->addNewUserLogEntryAutoCreate();
+               else
+                       $this->mUser->addNewUserLogEntry( $byEmail );
+               
+               # Run hooks
+               wfRunHooks( 'AddNewAccount', array( $this->mUser ) );
+
+               return true;
+       }
+
+       /**
+        * Entry point to create a new local account from user-supplied
+        * data loaded from the WebRequest.  We handle initialising the 
+        * email here because it's needed for some backend things; frontend
+        * interfaces calling this should handle recording things like 
+        * preference options
+        * @param $byEmail Bool whether to email the user their new password
+        * @return Status code; Login::SUCCESS == the user was successfully created
+        */
+       public function attemptCreation( $byEmail=false ) {
+               global $wgUser, $wgOut;
+               global $wgEnableSorbs, $wgProxyWhitelist;
+               global $wgMemc, $wgAccountCreationThrottle;
+               global $wgAuth;
+               global $wgEmailAuthentication, $wgEmailConfirmToEdit;
+
+               if( wfReadOnly() ) 
+                       return self::READ_ONLY;
+                       
+               # If the user passes an invalid domain, something is fishy
+               if( !$wgAuth->validDomain( $this->mDomain ) ) {
+                       $this->mCreateResult = 'wrongpassword';
+                       return self::CREATE_BADDOMAIN;
+               }
+
+               # If we are not allowing users to login locally, we should be checking
+               # to see if the user is actually able to authenticate to the authenti-
+               # cation server before they create an account (otherwise, they can
+               # create a local account and login as any domain user). We only need
+               # to check this for domains that aren't local.
+               if(    !in_array( $this->mDomain, array( 'local', '' ) ) 
+                       && !$wgAuth->canCreateAccounts() 
+                       && ( !$wgAuth->userExists( $this->mUsername ) 
+                               || !$wgAuth->authenticate( $this->mUsername, $this->mPassword ) 
+                       ) ) 
+               {
+                       $this->mCreateResult = 'wrongpassword';
+                       return self::WRONG_PLUGIN_PASS;
+               }
+
+               $ip = wfGetIP();
+               if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
+                 $wgUser->inSorbsBlacklist( $ip ) )
+               {
+                       $this->mCreateResult = 'sorbs_create_account_reason';
+                       return self::CREATE_SORBS;
+               }
+
+               # Now create a dummy user ($user) and check if it is valid
+               $name = trim( $this->mName );
+               $user = User::newFromName( $name, 'creatable' );
+               if ( is_null( $user ) ) {
+                       $this->mCreateResult = 'noname';
+                       return self::CREATE_BADNAME;
+               }
+
+               if ( $this->mUser->idForName() != 0 ) {
+                       $this->mCreateResult = 'userexists';
+                       return self::CREATE_EXISTS;
+               }
+
+               # Check that the password is acceptable, if we're actually
+               # going to use it
+               if( !$byEmail ){
+                       $valid = $this->mUser->isValidPassword( $this->mPassword );
+                       if ( $valid !== true ) {
+                               $this->mCreateResult = $valid;
+                               return self::CREATE_BADPASS;
+                       }
+               }
+
+               # if you need a confirmed email address to edit, then obviously you
+               # need an email address. Equally if we're going to send the password to it.
+               if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) || $byEmail ) {
+                       $this->mCreateResult = 'noemailcreate';
+                       return self::CREATE_NEEDEMAIL;
+               }
+
+               if( !empty( $this->mEmail ) && !User::isValidEmailAddr( $this->mEmail ) ) {
+                       $this->mCreateResult = 'invalidemailaddress';
+                       return self::CREATE_BADEMAIL;
+               }
+
+               # Set some additional data so the AbortNewAccount hook can be used for
+               # more than just username validation
+               $this->mUser->setEmail( $this->mEmail );
+               $this->mUser->setRealName( $this->mRealName );
+
+               if( !wfRunHooks( 'AbortNewAccount', array( $this->mUser, &$this->mCreateResult ) ) ) {
+                       # Hook point to add extra creation throttles and blocks
+                       wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
+                       return self::ABORTED;
+               }
+
+               if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
+                       $key = wfMemcKey( 'acctcreate', 'ip', $ip );
+                       $value = $wgMemc->get( $key );
+                       if ( !$value ) {
+                               $wgMemc->set( $key, 0, 86400 );
+                       }
+                       if ( $value >= $wgAccountCreationThrottle ) {
+                               return self::THROTTLED;
+                       }
+                       $wgMemc->incr( $key );
+               }
+
+               # Since we're creating a new local user, give the external 
+               # database a chance to synchronise.
+               if( !$wgAuth->addUser( $this->mUser, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
+                       $this->mCreateResult = 'externaldberror';
+                       return self::ABORTED;
+               }
+
+               $result = $this->initUser( false, $byEmail );
+               if( $result === null )
+                       # It's unlikely we'd get here without some exception 
+                       # being thrown, but it's probably possible...
+                       return self::FAILED;
+                       
+       
+               # Send out an email message if needed
+               if( $byEmail ){
+                       $this->mailPassword( 'createaccount-title', 'createaccount-text' );
+                       if( WikiError::isError( $this->mMailResult ) ){
+                               # FIXME: If the password email hasn't gone out, 
+                               # then the account is inaccessible :(
+                               return self::MAIL_ERROR;
+                       } else {
+                               return self::SUCCESS;
+                       }
+               } else {
+                       if( $wgEmailAuthentication && User::isValidEmailAddr( $this->mUser->getEmail() ) ) 
+                       {
+                               $this->mMailResult = $this->mUser->sendConfirmationMail();
+                               return WikiError::isError( $this->mMailResult ) 
+                                       ? self::MAIL_ERROR 
+                                       : self::SUCCESS;
+                       }
+               }
+               return true;
+       }
+
+       /**
+        * Email the user a new password, if appropriate to do so.
+        * @param $text String message key
+        * @param $title String message key
+        * @return Status code
+        */
+       public function mailPassword( $text='passwordremindertext', $title='passwordremindertitle' ) {
+               global $wgUser, $wgOut, $wgAuth, $wgServer, $wgScript, $wgNewPasswordExpiry;
+
+               if( wfReadOnly() ) 
+                       return self::READ_ONLY;
+
+               # If we let the email go out, it will take users to a form where
+               # they are forced to change their password, so don't let us go 
+               # there if we don't want passwords changed.
+               if( !$wgAuth->allowPasswordChange() ) 
+                       return self::MAIL_PASSCHANGE_FORBIDDEN;
+
+               # Check against blocked IPs
+               # FIXME: -- should we not?
+               if( $wgUser->isBlocked() )
+                       return self::MAIL_BLOCKED;
+
+               # Check for hooks
+               if( !wfRunHooks( 'UserLoginMailPassword', array( $this->mName, &$this->mMailResult ) ) )
+                       return self::ABORTED;
+
+               # Check against the rate limiter
+               if( $wgUser->pingLimiter( 'mailpassword' ) )
+                       return self::MAIL_PING_THROTTLED;
+
+               # Check for a valid name
+               if ($this->mName === '' )
+                       return self::NO_NAME;
+               $this->mUser = User::newFromName( $this->mName );
+               if( is_null( $this->mUser ) )
+                       return self::NO_NAME;
+
+               # And that the resulting user actually exists
+               if ( $this->mUser->getId() === 0 )
+                       return self::NOT_EXISTS;
+
+               # Check against password throttle
+               if ( $this->mUser->isPasswordReminderThrottled() )
+                       return self::MAIL_PASS_THROTTLED;
+               
+               # User doesn't have email address set
+               if ( $this->mUser->getEmail() === '' )
+                       return self::MAIL_EMPTY_EMAIL;
+
+               # Don't send to people who are acting fishily by hiding their IP
+               $ip = wfGetIP();
+               if( !$ip )
+                       return self::MAIL_BAD_IP;
+
+               # Let hooks do things with the data
+               wfRunHooks( 'User::mailPasswordInternal', array(&$wgUser, &$ip, &$this->mUser) );
+
+               $newpass = $this->mUser->randomPassword();
+               $this->mUser->setNewpassword( $newpass, true );
+               $this->mUser->saveSettings();
+
+               $message = wfMsgExt( $text, array( 'parsemag' ), $ip, $this->mUser->getName(), $newpass,
+                               $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) );
+               $this->mMailResult = $this->mUser->sendMail( wfMsg( $title ), $message );
+               
+               if( WikiError::isError( $this->mMailResult ) ) {
+                       return self::MAIL_ERROR;
+               } else {
+                       return self::SUCCESS;
+               }
+       }
+}
+
+/**
+ * For backwards compatibility, mainly with the state constants, which
+ * could be referred to in old extensions with the old class name.
+ * @deprecated
+ */
+class LoginForm extends Login {}
\ No newline at end of file
index dbf0974..228d3f9 100644 (file)
@@ -112,8 +112,8 @@ class SpecialPage {
                'Listredirects'             => array( 'SpecialPage', 'Listredirects' ), 
 
                # Login/create account
-               'Userlogin'                 => array( 'SpecialPage', 'Userlogin' ),             
-               'CreateAccount'             => array( 'SpecialRedirectToSpecial', 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) ),
+               'Userlogin'                 => 'SpecialUserLogin',              
+               'CreateAccount'             => 'SpecialCreateAccount',
 
                # Users and rights
                'Blockip'                   => array( 'SpecialPage', 'Blockip', 'block' ),
index 66ff8df..f2a96aa 100644 (file)
@@ -60,7 +60,7 @@ class ApiLogin extends ApiBase {
                        'wpName' => $params['name'],
                        'wpPassword' => $params['password'],
                        'wpDomain' => $params['domain'],
-                       'wpRemember' => ''
+                       'wpRemember' => '1'
                ));
 
                // Init session if necessary
@@ -68,19 +68,11 @@ class ApiLogin extends ApiBase {
                        wfSetupSession();
                }
 
-               $loginForm = new LoginForm($req);
-               switch ($authRes = $loginForm->authenticateUserData()) {
-                       case LoginForm :: SUCCESS :
+               $loginForm = new Login( $req );
+               switch ( $authRes = $loginForm->attemptLogin() ) {
+                       case Login::SUCCESS :
                                global $wgUser, $wgCookiePrefix;
 
-                               $wgUser->setOption('rememberpassword', 1);
-                               $wgUser->setCookies();
-
-                               // Run hooks. FIXME: split back and frontend from this hook.
-                               // FIXME: This hook should be placed in the backend
-                               $injected_html = '';
-                               wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
-
                                $result['result'] = 'Success';
                                $result['lguserid'] = intval($wgUser->getId());
                                $result['lgusername'] = $wgUser->getName();
@@ -89,35 +81,35 @@ class ApiLogin extends ApiBase {
                                $result['sessionid'] = session_id();
                                break;
 
-                       case LoginForm :: NO_NAME :
+                       case Login::NO_NAME :
                                $result['result'] = 'NoName';
                                break;
-                       case LoginForm :: ILLEGAL :
+                       case Login::ILLEGAL :
                                $result['result'] = 'Illegal';
                                break;
-                       case LoginForm :: WRONG_PLUGIN_PASS :
+                       case Login::WRONG_PLUGIN_PASS :
                                $result['result'] = 'WrongPluginPass';
                                break;
-                       case LoginForm :: NOT_EXISTS :
+                       case Login::NOT_EXISTS :
                                $result['result'] = 'NotExists';
                                break;
-                       case LoginForm :: WRONG_PASS :
+                       case Login::WRONG_PASS :
                                $result['result'] = 'WrongPass';
                                break;
-                       case LoginForm :: EMPTY_PASS :
+                       case Login::EMPTY_PASS :
                                $result['result'] = 'EmptyPass';
                                break;
-                       case LoginForm :: CREATE_BLOCKED :
+                       case Login::CREATE_BLOCKED :
                                $result['result'] = 'CreateBlocked';
                                $result['details'] = 'Your IP address is blocked from account creation';
                                break;
-                       case LoginForm :: THROTTLED :
+                       case Login::THROTTLED :
                                global $wgPasswordAttemptThrottle;
                                $result['result'] = 'Throttled';
-                               $result['wait'] = intval($wgPasswordAttemptThrottle['seconds']);
+                               $result['wait'] = intval( $wgPasswordAttemptThrottle['seconds'] );
                                break;
                        default :
-                               ApiBase :: dieDebug(__METHOD__, "Unhandled case value: {$authRes}");
+                               ApiBase::dieDebug( __METHOD__, "Unhandled case value: {$authRes}" );
                }
 
                $this->getResult()->addValue(null, 'login', $result);
diff --git a/includes/specials/SpecialCreateAccount.php b/includes/specials/SpecialCreateAccount.php
new file mode 100644 (file)
index 0000000..ccd394c
--- /dev/null
@@ -0,0 +1,519 @@
+<?php
+/**
+ * Special page for creating/registering new user accounts.
+ * @ingroup SpecialPage
+ */
+class SpecialCreateAccount extends SpecialPage {
+
+       var $mUsername, $mPassword, $mRetype, $mReturnTo, $mPosted;
+       var $mCreateaccountMail, $mRemember, $mEmail, $mDomain, $mLanguage;
+       var $mReturnToQuery;
+       
+       protected $mLogin;
+
+       public $mDomains = array();
+       
+       public $mUseEmail = true; # Can be switched off by AuthPlugins etc
+       public $mUseRealname = true;
+       public $mUseRemember = true;
+       
+       public $mFormHeader = '';
+       public $mFormFields = array(
+               'Name' => array(
+                       'type'          => 'text',
+                       'label-message' => 'yourname',
+                       'id'            => 'wpName2',
+                       'tabindex'      => '1',
+                       'size'          => '20',
+                       'required'      => '1',
+                       'autofocus'     => '',
+               ),
+               'Password' => array(
+                       'type'          => 'password',
+                       'label-message' => 'yourpassword',
+                       'size'          => '20',
+                       'id'            => 'wpPassword2',
+                       'required'      => '',
+               ),
+               'Retype' => array(
+                       'type'          => 'password',
+                       'label-message' => 'yourpasswordagain',
+                       'size'          => '20',
+                       'id'            => 'wpRetype',
+                       'required'      => '',
+               ),
+               'Email' => array(
+                       'type'          => 'email',
+                       'label-message' => 'youremail',
+                       'size'          => '20',
+                       'id'            => 'wpEmail',
+               ),
+               'RealName' => array(
+                       'type'          => 'text',
+                       'label-message' => 'yourrealname',
+                       'id'            => 'wpRealName',
+                       'tabindex'      => '1',
+                       'size'          => '20',
+               ),
+               'Remember' => array(
+                       'type'          => 'check',
+                       'label-message' => 'remembermypassword',
+                       'id'            => 'wpRemember',
+               ),
+               'Domain' => array(
+                       'type'          => 'select',
+                       'id'            => 'wpDomain',
+                       'label-message' => 'yourdomainname',
+                       'options'       => null,
+                       'default'       => null, 
+               ),
+       );
+       
+       public function __construct(){
+               parent::__construct( 'CreateAccount', 'createaccount' );
+               $this->mLogin = new Login();
+               $this->mFormFields['RealName']['label-help'] = 'prefs-help-realname';
+       }
+       
+       public function execute( $par ){
+               global $wgUser, $wgOut;
+               
+               $this->setHeaders();
+               $this->loadQuery();
+               
+               # Block signup here if in readonly. Keeps user from 
+               # going through the process (filling out data, etc) 
+               # and being informed later.
+               if ( wfReadOnly() ) {
+                       $wgOut->readOnlyPage();
+                       return;
+               } 
+               # Bail out straightaway on permissions errors
+               if ( !$this->userCanExecute( $wgUser ) ) {
+                       $this->displayRestrictionError();
+                       return;
+               } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
+                       $this->userBlockedMessage();
+                       return;
+               } elseif ( count( $permErrors = $this->getTitle()->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
+                       $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
+                       return;
+               }       
+               
+               if( $this->mPosted ) {
+                       $this->addNewAccount( $this->mCreateaccountMail );
+               } else {
+                       $this->showMainForm('');
+               }
+       }
+       
+       /**
+        * Load the member variables from the request parameters
+        */
+       protected function loadQuery(){
+               global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
+               $this->mCreateaccountMail = $wgRequest->getCheck( 'wpCreateaccountMail' )
+                                           && $wgEnableEmail;
+               
+               $this->mUsername = $wgRequest->getText( 'wpName' );
+               $this->mPassword = $wgRequest->getText( 'wpPassword' );
+               $this->mRetype = $wgRequest->getText( 'wpRetype' );
+               $this->mDomain = $wgRequest->getText( 'wpDomain' );
+               $this->mReturnTo = $wgRequest->getVal( 'returnto' );
+               $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
+               $this->mPosted = $wgRequest->wasPosted();
+               $this->mCreateaccountMail = $wgRequest->getCheck( 'wpCreateaccountMail' )
+                                           && $wgEnableEmail;
+               $this->mRemember = $wgRequest->getCheck( 'wpRemember' );
+               $this->mLanguage = $wgRequest->getText( 'uselang' );
+               
+               if ( $wgRedirectOnLogin ) {
+                       $this->mReturnTo = $wgRedirectOnLogin;
+                       $this->mReturnToQuery = '';
+               }
+
+               if( $wgEnableEmail ) {
+                       $this->mEmail = $wgRequest->getText( 'wpEmail' );
+               } else {
+                       $this->mEmail = '';
+               }
+               if( !in_array( 'realname', $wgHiddenPrefs ) ) {
+                   $this->mRealName = $wgRequest->getText( 'wpRealName' );
+               } else {
+                   $this->mRealName = '';
+               }
+
+               if( !$wgAuth->validDomain( $this->mDomain ) ) {
+                       $this->mDomain = 'invaliddomain';
+               }
+               $wgAuth->setDomain( $this->mDomain );
+
+               # When switching accounts, it sucks to get automatically logged out
+               $returnToTitle = Title::newFromText( $this->mReturnTo );
+               if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) {
+                       $this->mReturnTo = '';
+                       $this->mReturnToQuery = '';
+               }
+       }
+
+       /**
+        * Create a new user account from the provided data
+        */
+       protected function addNewAccount( $byEmail=false ) {
+               global $wgUser, $wgEmailAuthentication;
+       
+               # Do a quick check that the user actually managed to type
+               # the password in the same both times
+               if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
+                       return $this->showMainForm( wfMsgExt( 'badretype', 'parseinline' ) );
+               }
+               
+               # Create the account and abort if there's a problem doing so
+               $status = $this->mLogin->attemptCreation( $byEmail );
+               switch( $status ){
+                       case Login::SUCCESS: 
+                       case Login::MAIL_ERROR: 
+                               break;
+                               
+                       case Login::CREATE_BADDOMAIN: 
+                       case Login::CREATE_EXISTS: 
+                       case Login::NO_NAME:
+                       case Login::CREATE_NEEDEMAIL: 
+                       case Login::CREATE_BADEMAIL: 
+                       case Login::CREATE_BADNAME:
+                       case Login::WRONG_PLUGIN_PASS:
+                       case Login::ABORTED:
+                               return $this->showMainForm( wfMsgExt( $this->mLogin->mCreateResult, 'parseinline' ) );
+                       
+                       case Login::CREATE_SORBS: 
+                               return $this->showMainForm( wfMsgExt( 'sorbs_create_account_reason', 'parseinline' ) . ' (' . wfGetIP() . ')' );
+                               
+                       case Login::CREATE_BLOCKED:
+                               return $this->userBlockedMessage();
+                               
+                       case Login::CREATE_BADPASS:
+                               global $wgMinimalPasswordLength;
+                               return $this->showMainForm( wfMsgExt( $this->mLogin->mCreateResult, array( 'parsemag' ), $wgMinimalPasswordLength ) );
+                               
+                       case Login::THROTTLED: 
+                               global $wgAccountCreationThrottle;
+                               return $this->showMainForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $wgAccountCreationThrottle ) ); 
+                       
+                       default: 
+                               throw new MWException( "Unhandled status code $status in " . __METHOD__ );
+               }
+
+               # If we showed up language selection links, and one was in use, be
+               # smart (and sensible) and save that language as the user's preference
+               global $wgLoginLanguageSelector;
+               if( $wgLoginLanguageSelector && $this->mLanguage )
+                       $this->mLogin->mUser->setOption( 'language', $this->mLanguage );
+               $this->mLogin->mUser->saveSettings();
+       
+               if( $byEmail ) {
+                       if( $result == Login::MAIL_ERROR ){
+                               # FIXME: we are totally screwed if we end up here...
+                               $this->showMainForm( wfMsgExt( 'mailerror', 'parseinline', $this->mLogin->mMailResult->getMessage() ) );
+                       } else {
+                               $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
+                               $wgOut->addWikiMsg( 'accmailtext', $this->mLogin->mUser->getName(), $this->mLogin->mUser->getEmail() );
+                               $wgOut->returnToMain( false );
+                       }
+                       
+               } else {
+
+                       # There might be a message stored from the confirmation mail
+                       # send, which we can display.
+                       if( $wgEmailAuthentication && $this->mLogin->mMailResult ) {
+                               global $wgOut;
+                               if( WikiError::isError( $this->mLogin->mMailResult ) ) {
+                                       $wgOut->addWikiMsg( 'confirmemail_sendfailed', $this->mLogin->mMailResult->getMessage() );
+                               } else {
+                                       $wgOut->addWikiMsg( 'confirmemail_oncreate' );
+                               }
+                       }
+                       
+                       # If not logged in, assume the new account as the current 
+                       # one and set session cookies then show a "welcome" message 
+                       # or a "need cookies" message as needed
+                       if( $wgUser->isAnon() ) {
+                               $wgUser = $this->mLogin->mUser;
+                               $wgUser->setCookies();
+                               if( $this->hasSessionCookie() ) {
+                                       return $this->successfulCreation();
+                               } else {
+                                       return $this->cookieRedirectCheck();
+                               }
+                       } else {
+                               # Show confirmation that the account was created
+                               global $wgOut;
+                               $self = SpecialPage::getTitleFor( 'Userlogin' );
+                               $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
+                               $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $this->mLogin->mUser->getName() ) );
+                               $wgOut->returnToMain( false, $self );
+                               return true;
+                       }
+               }
+       }
+
+       /**
+        * Run any hooks registered for logins, then 
+        * display a message welcoming the user.
+        */
+       protected function successfulCreation(){
+               global $wgUser, $wgOut;
+
+               # Run any hooks; display injected HTML
+               $injected_html = '';
+               wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
+
+               SpecialUserLogin::displaySuccessfulLogin( 
+                       'welcomecreation', 
+                       $injected_html,
+                       $this->mReturnTo,
+                       $this->mReturnToQuery );
+       }
+
+       /**
+        * Display a message indicating that account creation from their IP has 
+        * been blocked by a (range)block with 'block account creation' enabled. 
+        * It's likely that this feature will be used for blocking large numbers 
+        * of innocent people, e.g. range blocks on schools. Don't blame it on 
+        * the user. There's a small chance that it really is the user's fault, 
+        * i.e. the username is blocked and they haven't bothered to log out 
+        * before trying to create an account to evade it, but we'll leave that 
+        * to their guilty conscience to figure out...
+        */
+       protected function userBlockedMessage() {
+               global $wgOut, $wgUser;
+
+               $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) );
+               $wgOut->setRobotPolicy( 'noindex,nofollow' );
+               $wgOut->setArticleRelated( false );
+
+               $ip = wfGetIP();
+               $blocker = User::whoIs( $wgUser->mBlock->mBy );
+               $block_reason = $wgUser->mBlock->mReason;
+
+               if ( strval( $block_reason ) === '' ) {
+                       $block_reason = wfMsgExt( 'blockednoreason', 'parseinline' );
+               }
+               $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $block_reason, $blocker );
+               $wgOut->returnToMain( false );
+       }
+
+       /**
+        * Show the main input form, with an appropriate error message
+        * from a previous iteration, if necessary
+        * @param $msg String HTML of message received previously
+        * @param $msgtype String type of message, usually 'error'
+        */
+       protected function showMainForm( $msg, $msgtype = 'error' ) {
+               global $wgUser, $wgOut, $wgHiddenPrefs, $wgEnableEmail;
+               global $wgCookiePrefix, $wgLoginLanguageSelector;
+               global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration;
+               
+               # Parse the error message if we got one
+               if( $msg ){
+                       if( $msgtype == 'error' ){
+                               $msg = wfMsgExt( 'loginerror', 'parseinline' ) . ' ' . $msg;
+                       }
+                       $msg = Html::rawElement(
+                               'div',
+                               array( 'class' => $msgtype . 'box' ),
+                               $msg
+                       );
+               } else {
+                       $msg = '';
+               }
+
+               # Make sure the returnTo strings don't get lost if the
+               # user changes language, etc
+               $linkq = array();
+               if ( !empty( $this->mReturnTo ) ) {
+                       $linkq['returnto'] = wfUrlencode( $this->mReturnTo );
+                       if ( !empty( $this->mReturnToQuery ) )
+                               $linkq['returntoquery'] = wfUrlencode( $this->mReturnToQuery );
+               }
+
+               # Pass any language selection on to the mode switch link
+               if( $wgLoginLanguageSelector && $this->mLanguage )
+                       $linkq['uselang'] = $this->mLanguage;
+
+               $skin = $wgUser->getSkin();
+               $link = $skin->link( 
+                       SpecialPage::getTitleFor( 'Userlogin' ),
+                       wfMsgHtml( 'gotaccountlink' ),
+                       array(),
+                       $linkq );
+               $link = $wgUser->isLoggedIn()
+                       ? ''
+                       : wfMsgWikiHtml( 'gotaccount', $link );
+               
+               # Prepare language selection links as needed
+               $langSelector = $wgLoginLanguageSelector 
+                       ? Html::rawElement( 
+                               'div',
+                               array( 'id' => 'languagelinks' ),
+                               SpecialUserLogin::makeLanguageSelector( $this->getTitle(), $this->mReturnTo ) )
+                       : '';
+               
+               # Add a  'send password by email' button if available
+               $buttons = '';
+               if( $wgEnableEmail && $wgUser->isLoggedIn() ){
+                       $buttons = Html::element(
+                               'input',
+                               array( 
+                                       'type'  => 'submit',
+                                       'name'  => 'wpCreateaccountMail',
+                                       'value' => wfMsg( 'createaccountmail' ),
+                                       'id'    => 'wpCreateaccountMail',
+                               ) 
+                       );
+               }
+               
+               # Give authentication and captcha plugins a chance to 
+               # modify the form, by hook or by using $wgAuth
+               $wgAuth->modifyUITemplate( $this, 'new' );
+               wfRunHooks( 'UserCreateForm', array( &$this ) );
+               
+               # The most likely use of the hook is to enable domains;
+               # check that now, and add fields if necessary
+               if( $this->mDomains ){
+                       $this->mFormFields['Domain']['options'] = $this->mDomains;
+                       $this->mFormFields['Domain']['default'] = $this->mDomain;
+               } else {
+                       unset( $this->mFormFields['Domain'] );
+               }
+               
+               # Or to switch email on or off
+               if( !$wgEnableEmail || !$this->mUseEmail ){
+                       unset( $this->mFormFields['Email'] );
+               } else {
+                       if( $wgEmailConfirmToEdit ){
+                               $this->mFormFields['Email']['label-help'] = 'prefs-help-email-required';
+                               $this->mFormFields['Email']['required'] = '';
+                       } else {
+                               $this->mFormFields['Email']['label-help'] = 'prefs-help-email';
+                       }
+               }
+               
+               # Or to play with realname
+               if( in_array( 'realname', $wgHiddenPrefs ) || !$this->mUseRealname ){
+                       unset( $this->mFormFields['Realname'] );
+               }
+               
+               # Or to tweak the 'remember my password' checkbox
+               if( !($wgCookieExpiration > 0) || !$this->mUseRemember ){
+                       # Remove it altogether
+                       unset( $this->mFormFields['Remember'] );
+               } elseif( $wgUser->getOption( 'rememberpassword' ) || $this->mRemember ){
+                       # Or check it by default
+                       # FIXME: this doesn't always work?
+                       $this->mFormFields['Remember']['checked'] = '1';
+               }
+               
+               $form = new HTMLForm( $this->mFormFields, '' );
+               $form->setTitle( $this->getTitle() );
+               $form->setSubmitText( wfMsg( 'createaccount' ) );
+               $form->setSubmitId( 'wpCreateaccount' );
+               $form->suppressReset();
+               $form->loadData();
+               
+               $formContents = '' 
+                       . Html::rawElement( 'p', array( 'id' => 'userloginlink' ),
+                               $link )
+                       . $this->mFormHeader
+                       . $langSelector
+                       . $form->getBody() 
+                       . $form->getButtons()
+                       . $buttons
+                       . Xml::hidden( 'returnto', $this->mReturnTo )
+                       . Xml::hidden( 'returntoquery', $this->mReturnToQuery )
+               ;
+
+               $wgOut->setPageTitle( wfMsg( 'createaccount' ) );
+               $wgOut->setRobotPolicy( 'noindex,nofollow' );
+               $wgOut->setArticleRelated( false );
+               $wgOut->disallowUserJs();  # Stop malicious userscripts sniffing passwords
+
+               $wgOut->addHTML( 
+                       Html::rawElement( 
+                               'div', 
+                               array( 'id' => 'loginstart' ), 
+                               wfMsgExt( 'loginstart', array( 'parseinline' ) )
+                       ) . 
+                       $msg . 
+                       Html::rawElement(
+                               'div',
+                               array( 'id' => 'userloginForm' ),
+                               $form->wrapForm( $formContents )
+                       ) . 
+                       Html::rawElement( 
+                               'div', 
+                               array( 'id' => 'loginend' ), 
+                               wfMsgExt( 'loginend', array( 'parseinline' ) )
+                       )
+               );
+               
+       }
+
+       /**
+        * Check if a session cookie is present.
+        *
+        * This will not pick up a cookie set during _this_ request, but is meant
+        * to ensure that the client is returning the cookie which was set on a
+        * previous pass through the system.
+        *
+        * @private
+        */
+       protected function hasSessionCookie() {
+               global $wgDisableCookieCheck, $wgRequest;
+               return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
+       }
+
+       /**
+        * Do a redirect back to the same page, so we can check any
+        * new session cookies.
+        */
+       protected function cookieRedirectCheck() {
+               global $wgOut;
+
+               $query = array( 'wpCookieCheck' => '1' );
+               if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo;
+               $check = $this->getTitle()->getFullURL( $query );
+
+               return $wgOut->redirect( $check );
+       }
+
+       /**
+        * Check the cookies and show errors if they're not enabled.
+        * @param $type String action being performed
+        */
+       protected function onCookieRedirectCheck() {
+               if ( !$this->hasSessionCookie() ) {
+                       return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) );
+               } else {
+                       return SpecialUserlogin::successfulLogin( 
+                               array( 'welcomecreate' ), 
+                               $this->mReturnTo, 
+                               $this->mReturnToQuery
+                       );
+               }
+       }
+       
+       /**
+        * Since the UserCreateForm hook was changed to pass a SpecialPage
+        * instead of a QuickTemplate derivative, old extensions might
+        * easily try calling these methods expecing them to exist.  Tempting
+        * though it is to let them have the fatal error, let's at least
+        * fail gracefully...
+        * @deprecated
+        */
+       public function set(){
+               wfDeprecated( __METHOD__ );
+       }
+       public function addInputItem(){
+               wfDeprecated( __METHOD__ );
+       }
+}
index 3e49354..df0969e 100644 (file)
@@ -12,6 +12,48 @@ class SpecialResetpass extends SpecialPage {
        public function __construct() {
                parent::__construct( 'Resetpass' );
        }
+       
+       public $mFormFields = array(
+               'Name' => array(
+                       'type'          => 'info',
+                       'label-message' => 'yourname',
+                       'default'       => '',
+               ),
+               'Password' => array(
+                       'type'          => 'password',
+                       'label-message' => 'oldpassword',
+                       'size'          => '20',
+                       'id'            => 'wpPassword',
+                       'required'      => '',
+               ),
+               'NewPassword' => array(
+                       'type'          => 'password',
+                       'label-message' => 'newpassword',
+                       'size'          => '20',
+                       'id'            => 'wpNewPassword',
+                       'required'      => '',
+               ),
+               'Retype' => array(
+                       'type'          => 'password',
+                       'label-message' => 'retypenew',
+                       'size'          => '20',
+                       'id'            => 'wpRetype',
+                       'required'      => '',
+               ),
+               'Remember' => array(
+                       'type'          => 'check',
+                       'label-message' => 'remembermypassword',
+                       'id'            => 'wpRemember',
+               ),
+       );
+       public $mSubmitMsg = 'resetpass-submit-loggedin';
+       public $mHeaderMsg = '';
+       public $mHeaderMsgType = 'error';
+       
+       protected $mUsername;
+       protected $mOldpass;
+       protected $mNewpass;
+       protected $mRetype;
 
        /**
         * Main execution point
@@ -19,176 +61,142 @@ class SpecialResetpass extends SpecialPage {
        function execute( $par ) {
                global $wgUser, $wgAuth, $wgOut, $wgRequest;
 
-               $this->mUserName = $wgRequest->getVal( 'wpName' );
+               $this->mUsername = $wgRequest->getVal( 'wpName', $wgUser->getName() );
                $this->mOldpass = $wgRequest->getVal( 'wpPassword' );
                $this->mNewpass = $wgRequest->getVal( 'wpNewPassword' );
                $this->mRetype = $wgRequest->getVal( 'wpRetype' );
+               $this->mRemember = $wgRequest->getVal( 'wpRemember' );
+               $this->mReturnTo = $wgRequest->getVal( 'returnto' );
+               $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
                
                $this->setHeaders();
                $this->outputHeader();
 
                if( !$wgAuth->allowPasswordChange() ) {
-                       $this->error( wfMsg( 'resetpass_forbidden' ) );
-                       return;
+                       $wgOut->showErrorPage( 'errorpagetitle', 'resetpass_forbidden' );
+                       return false;
                }
 
                if( !$wgRequest->wasPosted() && !$wgUser->isLoggedIn() ) {
-                       $this->error( wfMsg( 'resetpass-no-info' ) );
-                       return;
+                       $wgOut->showErrorPage( 'errorpagetitle', 'resetpass-no-info' );
+                       return false;
                }
 
-               if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getVal('token') ) ) {
-                       try {
-                               $this->attemptReset( $this->mNewpass, $this->mRetype );
-                               $wgOut->addWikiMsg( 'resetpass_success' );
-                               if( !$wgUser->isLoggedIn() ) {
-                                       $data = array(
-                                               'action'     => 'submitlogin',
-                                               'wpName'     => $this->mUserName,
-                                               'wpPassword' => $this->mNewpass,
-                                               'returnto'   => $wgRequest->getVal( 'returnto' ),
-                                       );
-                                       if( $wgRequest->getCheck( 'wpRemember' ) ) {
-                                               $data['wpRemember'] = 1;
-                                       }
-                                       $login = new LoginForm( new FauxRequest( $data, true ) );
-                                       $login->execute();
+               if( $wgRequest->wasPosted() 
+                   && $wgUser->matchEditToken( $wgRequest->getVal('wpEditToken') )
+                       && $this->attemptReset() )
+               {
+                       # Log the user in if they're not already (ie we're 
+                       # coming from the e-mail-password-reset route
+                       if( !$wgUser->isLoggedIn() ) {
+                               $data = array(
+                                       'wpName'     => $this->mUsername,
+                                       'wpPassword' => $this->mNewpass,
+                                       'returnto'   => $this->mReturnTo,
+                               );
+                               if( $this->mRemember ) {
+                                       $data['wpRemember'] = 1;
                                }
-                               $titleObj = Title::newFromText( $wgRequest->getVal( 'returnto' ) );
-                               if ( !$titleObj instanceof Title ) {
-                                       $titleObj = Title::newMainPage();
-                               }
-                               $wgOut->redirect( $titleObj->getFullURL() );
-                       } catch( PasswordError $e ) {
-                               $this->error( $e->getMessage() );
+                               $login = new Login( new FauxRequest( $data, true ) );
+                               $login->attemptLogin();
+                       
+                               # Redirect out to the appropriate target.
+                               SpecialUserlogin::successfulLogin( 
+                                       'resetpass_success', 
+                                       $this->mReturnTo, 
+                                       $this->mReturnToQuery,
+                                       $login->mLoginResult
+                               );
+                       } else {
+                               # Redirect out to the appropriate target.
+                               SpecialUserlogin::successfulLogin( 
+                                       'resetpass_success', 
+                                       $this->mReturnTo, 
+                                       $this->mReturnToQuery
+                               );
                        }
+               } else {
+                       $this->showForm();
                }
-               $this->showForm();
-       }
-
-       function error( $msg ) {
-               global $wgOut;
-               $wgOut->addHTML( Xml::element('p', array( 'class' => 'error' ), $msg ) );
        }
 
        function showForm() {
-               global $wgOut, $wgUser, $wgRequest;
+               global $wgOut, $wgUser;
 
                $wgOut->disallowUserJs();
-
-               $self = SpecialPage::getTitleFor( 'Resetpass' );
-               if ( !$this->mUserName ) {
-                       $this->mUserName = $wgUser->getName();
-               }
-               $rememberMe = '';
-               if ( !$wgUser->isLoggedIn() ) {
-                       $rememberMe = '<tr>' .
-                               '<td></td>' .
-                               '<td class="mw-input">' .
-                                       Xml::checkLabel( wfMsg( 'remembermypassword' ),
-                                               'wpRemember', 'wpRemember',
-                                               $wgRequest->getCheck( 'wpRemember' ) ) .
-                               '</td>' .
-                       '</tr>';
-                       $submitMsg = 'resetpass_submit';
-                       $oldpassMsg = 'resetpass-temp-password';
+               
+               if( $wgUser->isLoggedIn() ){
+                       unset( $this->mFormFields['Remember'] );
                } else {
-                       $oldpassMsg = 'oldpassword';
-                       $submitMsg = 'resetpass-submit-loggedin';
+                       # Request is coming from Special:UserLogin after it
+                       # authenticated someone with a temporary password.
+                       $this->mFormFields['Password']['label-message'] = 'resetpass-temp-password';
+                       $this->mSubmitMsg = 'resetpass_submit';
                }
+               $this->mFormFields['Name']['default'] = $this->mUsername;
+               
+               $header = $this->mHeaderMsg
+                       ? Xml::element( 'div', array( 'class' => "{$this->mHeaderMsgType}box" ), wfMsg( $this->mHeaderMsg ) )
+                       : '';
+                               
+               $form = new HTMLForm( $this->mFormFields, '' );
+               $form->suppressReset();
+               $form->setSubmitText( wfMsg( $this->mSubmitMsg ) );
+               $form->setTitle( $this->getTitle() );
+               $form->loadData();
+               
+               $formContents = '' 
+                       . $form->getBody()
+                       . $form->getButtons()
+                       . $form->getHiddenFields()
+                       . Html::hidden( 'wpName', $this->mUsername )
+                       . Html::hidden( 'returnto', $this->mReturnTo )
+                       ;
+               $formOutput = $form->wrapForm( $formContents );
+               
                $wgOut->addHTML(
-                       Xml::fieldset( wfMsg( 'resetpass_header' ) ) .
-                       Xml::openElement( 'form',
-                               array(
-                                       'method' => 'post',
-                                       'action' => $self->getLocalUrl(),
-                                       'id' => 'mw-resetpass-form' ) ) . "\n" .
-                       Xml::hidden( 'token', $wgUser->editToken() ) . "\n" .
-                       Xml::hidden( 'wpName', $this->mUserName ) . "\n" .
-                       Xml::hidden( 'returnto', $wgRequest->getVal( 'returnto' ) ) . "\n" .
-                       wfMsgExt( 'resetpass_text', array( 'parse' ) ) . "\n" .
-                       Xml::openElement( 'table', array( 'id' => 'mw-resetpass-table' ) ) . "\n" .
-                       $this->pretty( array(
-                               array( 'wpName', 'username', 'text', $this->mUserName ),
-                               array( 'wpPassword', $oldpassMsg, 'password', $this->mOldpass ),
-                               array( 'wpNewPassword', 'newpassword', 'password', null ),
-                               array( 'wpRetype', 'retypenew', 'password', null ),
-                       ) ) . "\n" .
-                       $rememberMe .
-                       "<tr>\n" .
-                               "<td></td>\n" .
-                               '<td class="mw-input">' .
-                                       Xml::submitButton( wfMsg( $submitMsg ) ) .
-                               "</td>\n" .
-                       "</tr>\n" .
-                       Xml::closeElement( 'table' ) .
-                       Xml::closeElement( 'form' ) .
-                       Xml::closeElement( 'fieldset' ) . "\n"
+                       $header
+                       . Html::rawElement( 'fieldset', array( 'class' => 'visualClear' ), ''
+                               . Html::element( 'legend', array(), wfMsg( 'resetpass_header' ) )
+                               . $formOutput
+                       )
                );
        }
 
-       function pretty( $fields ) {
-               $out = '';
-               foreach ( $fields as $list ) {
-                       list( $name, $label, $type, $value ) = $list;
-                       if( $type == 'text' ) {
-                               $field = htmlspecialchars( $value );
-                       } else {
-                               $attribs = array( 'id' => $name );
-                               if ( $name == 'wpNewPassword' || $name == 'wpRetype' ) {
-                                       $attribs = array_merge( $attribs,
-                                               User::passwordChangeInputAttribs() );
-                               }
-                               if ( $name == 'wpPassword' ) {
-                                       $attribs[] = 'autofocus';
-                               }
-                               $field = Html::input( $name, $value, $type, $attribs );
-                       }
-                       $out .= "<tr>\n";
-                       $out .= "\t<td class='mw-label'>";
-                       if ( $type != 'text' )
-                               $out .= Xml::label( wfMsg( $label ), $name );
-                       else 
-                               $out .=  wfMsgHtml( $label );
-                       $out .= "</td>\n";
-                       $out .= "\t<td class='mw-input'>";
-                       $out .= $field;
-                       $out .= "</td>\n";
-                       $out .= "</tr>";
-               }
-               return $out;
-       }
-
        /**
-        * @throws PasswordError when cannot set the new password because requirements not met.
+        * Try to reset the user's password 
         */
-       protected function attemptReset( $newpass, $retype ) {
-               $user = User::newFromName( $this->mUserName );
+       protected function attemptReset() {
+               $user = User::newFromName( $this->mUsername );
                if( !$user || $user->isAnon() ) {
-                       throw new PasswordError( 'no such user' );
+                       $this->mHeaderMsg = 'no such user';
+                       return false;
                }
                
-               if( $newpass !== $retype ) {
-                       wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'badretype' ) );
-                       throw new PasswordError( wfMsg( 'badretype' ) );
+               if( $this->mNewpass !== $this->mRetype ) {
+                       wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'badretype' ) );
+                       $this->mHeaderMsg = 'badretype';
+                       return false;
                }
 
                if( !$user->checkTemporaryPassword($this->mOldpass) && !$user->checkPassword($this->mOldpass) ) {
-                       wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'wrongpassword' ) );
-                       throw new PasswordError( wfMsg( 'resetpass-wrong-oldpass' ) );
+                       wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'wrongpassword' ) );
+                       $this->mHeaderMsg = 'resetpass-wrong-oldpass';
+                       return false;
                }
                
                try {
                        $user->setPassword( $this->mNewpass );
-                       wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'success' ) );
+                       wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'success' ) );
                        $this->mNewpass = $this->mOldpass = $this->mRetypePass = '';
                } catch( PasswordError $e ) {
-                       wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'error' ) );
-                       throw new PasswordError( $e->getMessage() );
-                       return;
+                       wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'error' ) );
+                       $this->mHeaderMsg = $e->getMessage();
+                       return false;
                }
                
                $user->setCookies();
                $user->saveSettings();
+               return true;
        }
 }
index da06df0..3a54f01 100644 (file)
 <?php
 /**
- * @file
+ * SpecialPage for logging users into the wiki
  * @ingroup SpecialPage
  */
 
-/**
- * constructor
- */
-function wfSpecialUserlogin( $par = '' ) {
-       global $wgRequest;
-       if( session_id() == '' ) {
-               wfSetupSession();
-       }
-
-       $form = new LoginForm( $wgRequest, $par );
-       $form->execute();
-}
-
-/**
- * implements Special:Login
- * @ingroup SpecialPage
- */
-class LoginForm {
+class SpecialUserLogin extends SpecialPage {
 
-       const SUCCESS = 0;
-       const NO_NAME = 1;
-       const ILLEGAL = 2;
-       const WRONG_PLUGIN_PASS = 3;
-       const NOT_EXISTS = 4;
-       const WRONG_PASS = 5;
-       const EMPTY_PASS = 6;
-       const RESET_PASS = 7;
-       const ABORTED = 8;
-       const CREATE_BLOCKED = 9;
-       const THROTTLED = 10;
-
-       var $mName, $mPassword, $mRetype, $mReturnTo, $mCookieCheck, $mPosted;
-       var $mAction, $mCreateaccount, $mCreateaccountMail, $mMailmypassword;
-       var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage;
+       var $mUsername, $mPassword, $mReturnTo, $mCookieCheck, $mPosted;
+       var $mCreateaccount, $mCreateaccountMail, $mMailmypassword;
+       var $mRemember, $mDomain, $mLanguage;
        var $mSkipCookieCheck, $mReturnToQuery;
 
-       private $mExtUser = null;
-
-       /**
-        * Constructor
-        * @param WebRequest $request A WebRequest object passed by reference
-        */
-       function LoginForm( &$request, $par = '' ) {
-               global $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
-
-               $this->mType = ( $par == 'signup' ) ? $par : $request->getText( 'type' ); # Check for [[Special:Userlogin/signup]]
-               $this->mName = $request->getText( 'wpName' );
-               $this->mPassword = $request->getText( 'wpPassword' );
-               $this->mRetype = $request->getText( 'wpRetype' );
-               $this->mDomain = $request->getText( 'wpDomain' );
-               $this->mReturnTo = $request->getVal( 'returnto' );
-               $this->mReturnToQuery = $request->getVal( 'returntoquery' );
-               $this->mCookieCheck = $request->getVal( 'wpCookieCheck' );
-               $this->mPosted = $request->wasPosted();
-               $this->mCreateaccount = $request->getCheck( 'wpCreateaccount' );
-               $this->mCreateaccountMail = $request->getCheck( 'wpCreateaccountMail' )
-                                           && $wgEnableEmail;
-               $this->mMailmypassword = $request->getCheck( 'wpMailmypassword' )
-                                        && $wgEnableEmail;
-               $this->mLoginattempt = $request->getCheck( 'wpLoginattempt' );
-               $this->mAction = $request->getVal( 'action' );
-               $this->mRemember = $request->getCheck( 'wpRemember' );
-               $this->mLanguage = $request->getText( 'uselang' );
-               $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' );
-
-               if ( $wgRedirectOnLogin ) {
-                       $this->mReturnTo = $wgRedirectOnLogin;
-                       $this->mReturnToQuery = '';
-               }
-
-               if( $wgEnableEmail ) {
-                       $this->mEmail = $request->getText( 'wpEmail' );
-               } else {
-                       $this->mEmail = '';
-               }
-               if( !in_array( 'realname', $wgHiddenPrefs ) ) {
-                   $this->mRealName = $request->getText( 'wpRealName' );
-               } else {
-                   $this->mRealName = '';
-               }
-
-               if( !$wgAuth->validDomain( $this->mDomain ) ) {
-                       $this->mDomain = 'invaliddomain';
-               }
-               $wgAuth->setDomain( $this->mDomain );
-
-               # When switching accounts, it sucks to get automatically logged out
-               $returnToTitle = Title::newFromText( $this->mReturnTo );
-               if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) {
-                       $this->mReturnTo = '';
-                       $this->mReturnToQuery = '';
-               }
-       }
-
-       function execute() {
-               if ( !is_null( $this->mCookieCheck ) ) {
-                       $this->onCookieRedirectCheck( $this->mCookieCheck );
-                       return;
-               } else if( $this->mPosted ) {
-                       if( $this->mCreateaccount ) {
-                               return $this->addNewAccount();
-                       } else if ( $this->mCreateaccountMail ) {
-                               return $this->addNewAccountMailPassword();
-                       } else if ( $this->mMailmypassword ) {
-                               return $this->mailPassword();
-                       } else if ( ( 'submitlogin' == $this->mAction ) || $this->mLoginattempt ) {
-                               return $this->processLogin();
-                       }
-               }
-               $this->mainLoginForm( '' );
-       }
-
-       /**
-        * @private
-        */
-       function addNewAccountMailPassword() {
-               global $wgOut;
-
-               if ('' == $this->mEmail) {
-                       $this->mainLoginForm( wfMsg( 'noemail', htmlspecialchars( $this->mName ) ) );
-                       return;
-               }
-
-               $u = $this->addNewaccountInternal();
-
-               if ($u == NULL) {
-                       return;
-               }
-
-               // Wipe the initial password and mail a temporary one
-               $u->setPassword( null );
-               $u->saveSettings();
-               $result = $this->mailPasswordInternal( $u, false, 'createaccount-title', 'createaccount-text' );
-
-               wfRunHooks( 'AddNewAccount', array( $u, true ) );
-               $u->addNewUserLogEntry();
-
-               $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
-               $wgOut->setRobotPolicy( 'noindex,nofollow' );
-               $wgOut->setArticleRelated( false );
-
-               if( WikiError::isError( $result ) ) {
-                       $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) );
-               } else {
-                       $wgOut->addWikiMsg( 'accmailtext', $u->getName(), $u->getEmail() );
-                       $wgOut->returnToMain( false );
-               }
-               $u = 0;
-       }
-
-
-       /**
-        * @private
-        */
-       function addNewAccount() {
-               global $wgUser, $wgEmailAuthentication;
-
-               # Create the account and abort if there's a problem doing so
-               $u = $this->addNewAccountInternal();
-               if( $u == NULL )
-                       return;
-
-               # If we showed up language selection links, and one was in use, be
-               # smart (and sensible) and save that language as the user's preference
-               global $wgLoginLanguageSelector;
-               if( $wgLoginLanguageSelector && $this->mLanguage )
-                       $u->setOption( 'language', $this->mLanguage );
-
-               # Send out an email authentication message if needed
-               if( $wgEmailAuthentication && User::isValidEmailAddr( $u->getEmail() ) ) {
-                       global $wgOut;
-                       $error = $u->sendConfirmationMail();
-                       if( WikiError::isError( $error ) ) {
-                               $wgOut->addWikiMsg( 'confirmemail_sendfailed', $error->getMessage() );
-                       } else {
-                               $wgOut->addWikiMsg( 'confirmemail_oncreate' );
-                       }
-               }
-
-               # Save settings (including confirmation token)
-               $u->saveSettings();
-
-               # If not logged in, assume the new account as the current one and set
-               # session cookies then show a "welcome" message or a "need cookies"
-               # message as needed
-               if( $wgUser->isAnon() ) {
-                       $wgUser = $u;
-                       $wgUser->setCookies();
-                       wfRunHooks( 'AddNewAccount', array( $wgUser ) );
-                       $wgUser->addNewUserLogEntry();
-                       if( $this->hasSessionCookie() ) {
-                               return $this->successfulCreation();
-                       } else {
-                               return $this->cookieRedirectCheck( 'new' );
-                       }
-               } else {
-                       # Confirm that the account was created
-                       global $wgOut;
-                       $self = SpecialPage::getTitleFor( 'Userlogin' );
-                       $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
-                       $wgOut->setArticleRelated( false );
-                       $wgOut->setRobotPolicy( 'noindex,nofollow' );
-                       $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $u->getName() ) );
-                       $wgOut->returnToMain( false, $self );
-                       wfRunHooks( 'AddNewAccount', array( $u ) );
-                       $u->addNewUserLogEntry();
-                       return true;
-               }
+       public $mDomains = array();
+
+       public $mFormHeader = ''; # Can be filled by hooks etc
+       public $mFormFields = array(
+               'Name' => array(
+                       'type'          => 'text',
+                       'label-message' => 'yourname',
+                       'id'            => 'wpName1',
+                       'tabindex'      => '1',
+                       'size'          => '20',
+                       'required'      => '1',
+               ),
+               'Password' => array(
+                       'type'          => 'password',
+                       'label-message' => 'yourpassword',
+                       'size'          => '20',
+                       'id'            => 'wpPassword1',
+               ),
+               'Domain' => array(
+                       'type'          => 'select',
+                       'id'            => 'wpDomain',
+                       'label-message' => 'yourdomainname',
+                       'options'       => null,
+                       'default'       => null, 
+               ),
+               'Remember' => array(
+                       'type'          => 'check',
+                       'label-message' => 'remembermypassword',
+                       'id'            => 'wpRemember',
+               )
+       );
+
+       protected $mLogin; # Login object
+
+       public function __construct(){
+               parent::__construct( 'Userlogin' );
        }
 
-       /**
-        * @private
-        */
-       function addNewAccountInternal() {
-               global $wgUser, $wgOut;
-               global $wgEnableSorbs, $wgProxyWhitelist;
-               global $wgMemc, $wgAccountCreationThrottle;
-               global $wgAuth, $wgMinimalPasswordLength;
-               global $wgEmailConfirmToEdit;
-
-               // If the user passes an invalid domain, something is fishy
-               if( !$wgAuth->validDomain( $this->mDomain ) ) {
-                       $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
-                       return false;
-               }
-
-               // If we are not allowing users to login locally, we should be checking
-               // to see if the user is actually able to authenticate to the authenti-
-               // cation server before they create an account (otherwise, they can
-               // create a local account and login as any domain user). We only need
-               // to check this for domains that aren't local.
-               if( 'local' != $this->mDomain && '' != $this->mDomain ) {
-                       if( !$wgAuth->canCreateAccounts() && ( !$wgAuth->userExists( $this->mName ) || !$wgAuth->authenticate( $this->mName, $this->mPassword ) ) ) {
-                               $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
-                               return false;
-                       }
-               }
-
-               if ( wfReadOnly() ) {
-                       $wgOut->readOnlyPage();
-                       return false;
-               }
-
-               # Check permissions
-               if ( !$wgUser->isAllowed( 'createaccount' ) ) {
-                       $this->userNotPrivilegedMessage();
-                       return false;
-               } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
-                       $this->userBlockedMessage();
-                       return false;
-               }
+       function execute( $par ) {
+               global $wgRequest;
 
-               $ip = wfGetIP();
-               if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
-                 $wgUser->inSorbsBlacklist( $ip ) )
-               {
-                       $this->mainLoginForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . htmlspecialchars( $ip ) . ')' );
+               # Redirect out for account creation, for B/C
+               $type = ( $par == 'signup' ) ? $par : $wgRequest->getText( 'type' );
+               if( $type == 'signup' ){
+                       $sp = new SpecialCreateAccount();
+                       $sp->execute( $par );
                        return;
                }
-
-               # Now create a dummy user ($u) and check if it is valid
-               $name = trim( $this->mName );
-               $u = User::newFromName( $name, 'creatable' );
-               if ( is_null( $u ) ) {
-                       $this->mainLoginForm( wfMsg( 'noname' ) );
-                       return false;
-               }
-
-               if ( 0 != $u->idForName() ) {
-                       $this->mainLoginForm( wfMsg( 'userexists' ) );
-                       return false;
-               }
-
-               if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
-                       $this->mainLoginForm( wfMsg( 'badretype' ) );
-                       return false;
-               }
-
-               # check for minimal password length
-               $valid = $u->isValidPassword( $this->mPassword );
-               if ( $valid !== true ) {
-                       if ( !$this->mCreateaccountMail ) {
-                               $this->mainLoginForm( wfMsgExt( $valid, array( 'parsemag' ), $wgMinimalPasswordLength ) );
-                               return false;
-                       } else {
-                               # do not force a password for account creation by email
-                               # set invalid password, it will be replaced later by a random generated password
-                               $this->mPassword = null;
-                       }
-               }
-
-               # if you need a confirmed email address to edit, then obviously you
-               # need an email address.
-               if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) ) {
-                       $this->mainLoginForm( wfMsg( 'noemailtitle' ) );
-                       return false;
-               }
-
-               if( !empty( $this->mEmail ) && !User::isValidEmailAddr( $this->mEmail ) ) {
-                       $this->mainLoginForm( wfMsg( 'invalidemailaddress' ) );
-                       return false;
-               }
-
-               # Set some additional data so the AbortNewAccount hook can be used for
-               # more than just username validation
-               $u->setEmail( $this->mEmail );
-               $u->setRealName( $this->mRealName );
-
-               $abortError = '';
-               if( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError ) ) ) {
-                       // Hook point to add extra creation throttles and blocks
-                       wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
-                       $this->mainLoginForm( $abortError );
-                       return false;
-               }
-
-               if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
-                       $key = wfMemcKey( 'acctcreate', 'ip', $ip );
-                       $value = $wgMemc->get( $key );
-                       if ( !$value ) {
-                               $wgMemc->set( $key, 0, 86400 );
-                       }
-                       if ( $value >= $wgAccountCreationThrottle ) {
-                               $this->throttleHit( $wgAccountCreationThrottle );
-                               return false;
-                       }
-                       $wgMemc->incr( $key );
-               }
-
-               if( !$wgAuth->addUser( $u, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
-                       $this->mainLoginForm( wfMsg( 'externaldberror' ) );
-                       return false;
-               }
-
-               return $this->initUser( $u, false );
-       }
-
-       /**
-        * Actually add a user to the database.
-        * Give it a User object that has been initialised with a name.
-        *
-        * @param $u User object.
-        * @param $autocreate boolean -- true if this is an autocreation via auth plugin
-        * @return User object.
-        * @private
-        */
-       function initUser( $u, $autocreate ) {
-               global $wgAuth;
-
-               $u->addToDatabase();
-
-               if ( $wgAuth->allowPasswordChange() ) {
-                       $u->setPassword( $this->mPassword );
-               }
-
-               $u->setEmail( $this->mEmail );
-               $u->setRealName( $this->mRealName );
-               $u->setToken();
-
-               $wgAuth->initUser( $u, $autocreate );
-
-               if ( $this->mExtUser ) {
-                       $this->mExtUser->link( $u->getId() );
-                       $email = $this->mExtUser->getPref( 'emailaddress' );
-                       if ( $email && !$this->mEmail ) {
-                               $u->setEmail( $email );
-                       }
-               }
-
-               $u->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
-               $u->saveSettings();
-
-               # Update user count
-               $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
-               $ssUpdate->doUpdate();
-
-               return $u;
-       }
-
-       /**
-        * Internally authenticate the login request.
-        *
-        * This may create a local account as a side effect if the
-        * authentication plugin allows transparent local account
-        * creation.
-        *
-        * @public
-        */
-       function authenticateUserData() {
-               global $wgUser, $wgAuth;
-               if ( '' == $this->mName ) {
-                       return self::NO_NAME;
-               }
                
-               global $wgPasswordAttemptThrottle;
-
-               $throttleCount = 0;
-               if ( is_array( $wgPasswordAttemptThrottle ) ) {
-                       $throttleKey = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
-                       $count = $wgPasswordAttemptThrottle['count'];
-                       $period = $wgPasswordAttemptThrottle['seconds'];
-                       
-                       global $wgMemc;
-                       $throttleCount = $wgMemc->get( $throttleKey );
-                       if ( !$throttleCount ) {
-                               $wgMemc->add( $throttleKey, 1, $period ); // start counter
-                       } else if ( $throttleCount < $count ) {
-                               $wgMemc->incr($throttleKey);
-                       } else if ( $throttleCount >= $count ) {
-                               return self::THROTTLED;
-                       }
-               }
-
-               // Load $wgUser now, and check to see if we're logging in as the same
-               // name. This is necessary because loading $wgUser (say by calling
-               // getName()) calls the UserLoadFromSession hook, which potentially
-               // creates the user in the database. Until we load $wgUser, checking
-               // for user existence using User::newFromName($name)->getId() below
-               // will effectively be using stale data.
-               if ( $wgUser->getName() === $this->mName ) {
-                       wfDebug( __METHOD__.": already logged in as {$this->mName}\n" );
-                       return self::SUCCESS;
-               }
-
-               $this->mExtUser = ExternalUser::newFromName( $this->mName );
-
-               # TODO: Allow some magic here for invalid external names, e.g., let the
-               # user choose a different wiki name.
-               $u = User::newFromName( $this->mName );
-               if( is_null( $u ) || !User::isUsableName( $u->getName() ) ) {
-                       return self::ILLEGAL;
+               # Because we're transitioning from logged-out, who might not
+               # have a session, to logged-in, who always do, we need to make
+               # sure that we *always* have a session...
+               if( session_id() == '' ) {
+                       wfSetupSession();
                }
+               
+               $this->loadQuery();
+               $this->mLogin = new Login();
 
-               $isAutoCreated = false;
-               if ( 0 == $u->getID() ) {
-                       $status = $this->attemptAutoCreate( $u );
-                       if ( $status !== self::SUCCESS ) {
-                               return $status;
+               if ( $wgRequest->getCheck( 'wpCookieCheck' ) ) {
+                       $this->onCookieRedirectCheck();
+                       return;
+               } else if( $wgRequest->wasPosted() ) {
+                       if ( $this->mMailmypassword ) {
+                               return $this->showMailPage();
                        } else {
-                               $isAutoCreated = true;
+                               return $this->processLogin();
                        }
                } else {
-                       $u->load();
+                       $this->mainLoginForm( '' );
                }
-
-               // Give general extensions, such as a captcha, a chance to abort logins
-               $abort = self::ABORTED;
-               if( !wfRunHooks( 'AbortLogin', array( $u, $this->mPassword, &$abort ) ) ) {
-                       return $abort;
-               }
-
-               if (!$u->checkPassword( $this->mPassword )) {
-                       if( $u->checkTemporaryPassword( $this->mPassword ) ) {
-                               // The e-mailed temporary password should not be used for actu-
-                               // al logins; that's a very sloppy habit, and insecure if an
-                               // attacker has a few seconds to click "search" on someone's o-
-                               // pen mail reader.
-                               //
-                               // Allow it to be used only to reset the password a single time
-                               // to a new value, which won't be in the user's e-mail ar-
-                               // chives.
-                               //
-                               // For backwards compatibility, we'll still recognize it at the
-                               // login form to minimize surprises for people who have been
-                               // logging in with a temporary password for some time.
-                               //
-                               // As a side-effect, we can authenticate the user's e-mail ad-
-                               // dress if it's not already done, since the temporary password
-                               // was sent via e-mail.
-                               if( !$u->isEmailConfirmed() ) {
-                                       $u->confirmEmail();
-                                       $u->saveSettings();
-                               }
-
-                               // At this point we just return an appropriate code/ indicating
-                               // that the UI should show a password reset form; bot inter-
-                               // faces etc will probably just fail cleanly here.
-                               $retval = self::RESET_PASS;
-                       } else {
-                               $retval = '' == $this->mPassword ? self::EMPTY_PASS : self::WRONG_PASS;
-                       }
-               } else {
-                       $wgAuth->updateUser( $u );
-                       $wgUser = $u;
-
-                       // Please reset throttle for successful logins, thanks!
-                       if($throttleCount) {
-                               $wgMemc->delete($throttleKey);
-                       }
-
-                       if ( $isAutoCreated ) {
-                               // Must be run after $wgUser is set, for correct new user log
-                               wfRunHooks( 'AuthPluginAutoCreate', array( $wgUser ) );
-                       }
-
-                       $retval = self::SUCCESS;
-               }
-               wfRunHooks( 'LoginAuthenticateAudit', array( $u, $this->mPassword, $retval ) );
-               return $retval;
        }
 
        /**
-        * Attempt to automatically create a user on login. Only succeeds if there
-        * is an external authentication method which allows it.
-        * @return integer Status code
+        * Load member variables from the HTTP request data
+        * @param $par String the fragment passed to execute()
         */
-       function attemptAutoCreate( $user ) {
-               global $wgAuth, $wgUser, $wgAutocreatePolicy;
-
-               if ( $wgUser->isBlockedFromCreateAccount() ) {
-                       wfDebug( __METHOD__.": user is blocked from account creation\n" );
-                       return self::CREATE_BLOCKED;
-               }
-
-               /**
-                * If the external authentication plugin allows it, automatically cre-
-                * ate a new account for users that are externally defined but have not
-                * yet logged in.
-                */
-               if ( $this->mExtUser ) {
-                       # mExtUser is neither null nor false, so use the new ExternalAuth
-                       # system.
-                       if ( $wgAutocreatePolicy == 'never' ) {
-                               return self::NOT_EXISTS;
-                       }
-                       if ( !$this->mExtUser->authenticate( $this->mPassword ) ) {
-                               return self::WRONG_PLUGIN_PASS;
-                       }
-               } else {
-                       # Old AuthPlugin.
-                       if ( !$wgAuth->autoCreate() ) {
-                               return self::NOT_EXISTS;
-                       }
-                       if ( !$wgAuth->userExists( $user->getName() ) ) {
-                               wfDebug( __METHOD__.": user does not exist\n" );
-                               return self::NOT_EXISTS;
-                       }
-                       if ( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) {
-                               wfDebug( __METHOD__.": \$wgAuth->authenticate() returned false, aborting\n" );
-                               return self::WRONG_PLUGIN_PASS;
-                       }
-               }
-
-               wfDebug( __METHOD__.": creating account\n" );
-               $user = $this->initUser( $user, true );
-               return self::SUCCESS;
-       }
-
-       function processLogin() {
-               global $wgUser, $wgAuth;
-
-               switch ( $this->authenticateUserData() ) {
-                       case self::SUCCESS:
-                               # We've verified now, update the real record
-                               if( (bool)$this->mRemember != (bool)$wgUser->getOption( 'rememberpassword' ) ) {
-                                       $wgUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
-                                       $wgUser->saveSettings();
-                               } else {
-                                       $wgUser->invalidateCache();
-                               }
-                               $wgUser->setCookies();
-
-                               // Reset the throttle
-                               $key = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
-                               global $wgMemc;
-                               $wgMemc->delete( $key );
+       protected function loadQuery(){
+               global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
 
-                               if( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
-                                       /* Replace the language object to provide user interface in
-                                        * correct language immediately on this first page load.
-                                        */
-                                       global $wgLang, $wgRequest;
-                                       $code = $wgRequest->getVal( 'uselang', $wgUser->getOption( 'language' ) );
-                                       $wgLang = Language::factory( $code );
-                                       return $this->successfulLogin();
-                               } else {
-                                       return $this->cookieRedirectCheck( 'login' );
-                               }
-                               break;
+               $this->mUsername = $wgRequest->getText( 'wpName' );
+               $this->mPassword = $wgRequest->getText( 'wpPassword' );
+               $this->mDomain = $wgRequest->getText( 'wpDomain' );
+               $this->mLanguage = $wgRequest->getText( 'uselang' );
 
-                       case self::NO_NAME:
-                       case self::ILLEGAL:
-                               $this->mainLoginForm( wfMsg( 'noname' ) );
-                               break;
-                       case self::WRONG_PLUGIN_PASS:
-                               $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
-                               break;
-                       case self::NOT_EXISTS:
-                               if( $wgUser->isAllowed( 'createaccount' ) ){
-                                       $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $this->mName ) ) );
-                               } else {
-                                       $this->mainLoginForm( wfMsg( 'nosuchusershort', htmlspecialchars( $this->mName ) ) );
-                               }
-                               break;
-                       case self::WRONG_PASS:
-                               $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
-                               break;
-                       case self::EMPTY_PASS:
-                               $this->mainLoginForm( wfMsg( 'wrongpasswordempty' ) );
-                               break;
-                       case self::RESET_PASS:
-                               $this->resetLoginForm( wfMsg( 'resetpass_announce' ) );
-                               break;
-                       case self::CREATE_BLOCKED:
-                               $this->userBlockedMessage();
-                               break;
-                       case self::THROTTLED:
-                               $this->mainLoginForm( wfMsg( 'login-throttled' ) );
-                               break;
-                       default:
-                               throw new MWException( "Unhandled case value" );
-               }
-       }
-
-       function resetLoginForm( $error ) {
-               global $wgOut;
-               $wgOut->addHTML( Xml::element('p', array( 'class' => 'error' ), $error ) );
-               $reset = new SpecialResetpass();
-               $reset->execute( null );
-       }
-
-       /**
-        * @private
-        */
-       function mailPassword() {
-               global $wgUser, $wgOut, $wgAuth;
-               
-               if ( wfReadOnly() ) {
-                       $wgOut->readOnlyPage();
-                       return false;
-               }
-               
-               if( !$wgAuth->allowPasswordChange() ) {
-                       $this->mainLoginForm( wfMsg( 'resetpass_forbidden' ) );
-                       return;
-               }
+               $this->mReturnTo = $wgRequest->getVal( 'returnto' );
+               $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
 
-               # Check against blocked IPs
-               # fixme -- should we not?
-               if( $wgUser->isBlocked() ) {
-                       $this->mainLoginForm( wfMsg( 'blocked-mailpassword' ) );
-                       return;
-               }
-               
-               // Check for hooks
-               $error = null;
-               if ( ! wfRunHooks( 'UserLoginMailPassword', array( $this->mName, &$error ) ) ) {
-                       $this->mainLoginForm( $error );
-                       return;
-               }
-
-               # Check against the rate limiter
-               if( $wgUser->pingLimiter( 'mailpassword' ) ) {
-                       $wgOut->rateLimited();
-                       return;
-               }
-
-               if ( '' == $this->mName ) {
-                       $this->mainLoginForm( wfMsg( 'noname' ) );
-                       return;
-               }
-               $u = User::newFromName( $this->mName );
-               if( is_null( $u ) ) {
-                       $this->mainLoginForm( wfMsg( 'noname' ) );
-                       return;
-               }
-               if ( 0 == $u->getID() ) {
-                       $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $u->getName() ) ) );
-                       return;
-               }
-
-               # Check against password throttle
-               if ( $u->isPasswordReminderThrottled() ) {
-                       global $wgPasswordReminderResendTime;
-                       # Round the time in hours to 3 d.p., in case someone is specifying
-                       # minutes or seconds.
-                       $this->mainLoginForm( wfMsgExt( 'throttled-mailpassword', array( 'parsemag' ),
-                               round( $wgPasswordReminderResendTime, 3 ) ) );
-                       return;
-               }
+               $this->mMailmypassword = $wgRequest->getCheck( 'wpMailmypassword' )
+                                        && $wgEnableEmail;
+               $this->mRemember = $wgRequest->getCheck( 'wpRemember' );
+               $this->mSkipCookieCheck = $wgRequest->getCheck( 'wpSkipCookieCheck' );
 
-               $result = $this->mailPasswordInternal( $u, true, 'passwordremindertitle', 'passwordremindertext' );
-               if( WikiError::isError( $result ) ) {
-                       $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) );
-               } else {
-                       $this->mainLoginForm( wfMsg( 'passwordsent', $u->getName() ), 'success' );
+               if( !$wgAuth->validDomain( $this->mDomain ) ) {
+                       $this->mDomain = 'invaliddomain';
                }
-       }
-
-
-       /**
-        * @param object user
-        * @param bool throttle
-        * @param string message name of email title
-        * @param string message name of email text
-        * @return mixed true on success, WikiError on failure
-        * @private
-        */
-       function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle', $emailText = 'passwordremindertext' ) {
-               global $wgServer, $wgScript, $wgUser, $wgNewPasswordExpiry;
-
-               if ( '' == $u->getEmail() ) {
-                       return new WikiError( wfMsg( 'noemail', $u->getName() ) );
+               $wgAuth->setDomain( $this->mDomain );
+       
+               if ( $wgRedirectOnLogin ) {
+                       $this->mReturnTo = $wgRedirectOnLogin;
+                       $this->mReturnToQuery = '';
                }
-               $ip = wfGetIP();
-               if( !$ip ) {
-                       return new WikiError( wfMsg( 'badipaddress' ) );
+               # When switching accounts, it sucks to get automatically logged out
+               $returnToTitle = Title::newFromText( $this->mReturnTo );
+               if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) {
+                       $this->mReturnTo = '';
+                       $this->mReturnToQuery = '';
                }
-               
-               wfRunHooks( 'User::mailPasswordInternal', array(&$wgUser, &$ip, &$u) );
-
-               $np = $u->randomPassword();
-               $u->setNewpassword( $np, $throttle );
-               $u->saveSettings();
-
-               $m = wfMsgExt( $emailText, array( 'parsemag' ), $ip, $u->getName(), $np,
-                               $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) );
-               $result = $u->sendMail( wfMsg( $emailTitle ), $m );
-
-               return $result;
        }
 
-
        /**
-        * Run any hooks registered for logins, then HTTP redirect to
-        * $this->mReturnTo (or Main Page if that's undefined).  Formerly we had a
-        * nice message here, but that's really not as useful as just being sent to
-        * wherever you logged in from.  It should be clear that the action was
-        * successful, given the lack of error messages plus the appearance of your
-        * name in the upper right.
-        *
-        * @private
+        * Show the main login form
+        * @param $msg String a message key for a warning/error message
+        * that may have been generated on a previous iteration
         */
-       function successfulLogin() {
-               global $wgUser, $wgOut;
-
-               # Run any hooks; display injected HTML if any, else redirect
-               $injected_html = '';
-               wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
+       protected function mainLoginForm( $msg, $msgtype = 'error' ) {
+               global $wgUser, $wgOut, $wgEnableEmail;
+               global $wgCookiePrefix, $wgLoginLanguageSelector;
+               global $wgAuth, $wgCookieExpiration;
 
-               if( $injected_html !== '' ) {
-                       $this->displaySuccessfulLogin( 'loginsuccess', $injected_html );
-               } else {
-                       $titleObj = Title::newFromText( $this->mReturnTo );
-                       if ( !$titleObj instanceof Title ) {
-                               $titleObj = Title::newMainPage();
+               # Preload the name field with something if we can
+               if ( '' == $this->mUsername ) {
+                       if ( $wgUser->isLoggedIn() ) {
+                               $this->mUsername = $wgUser->getName();
+                       } elseif( isset( $_COOKIE[$wgCookiePrefix.'UserName'] ) ) {
+                               $this->mUsername = $_COOKIE[$wgCookiePrefix.'UserName'];
                        }
-                       $wgOut->redirect( $titleObj->getFullURL( $this->mReturnToQuery ) );
                }
-       }
-
-       /**
-        * Run any hooks registered for logins, then display a message welcoming
-        * the user.
-        *
-        * @private
-        */
-       function successfulCreation() {
-               global $wgUser, $wgOut;
-
-               # Run any hooks; display injected HTML
-               $injected_html = '';
-               wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
-
-               $this->displaySuccessfulLogin( 'welcomecreation', $injected_html );
-       }
-
-       /**
-        * Display a "login successful" page.
-        */
-       private function displaySuccessfulLogin( $msgname, $injected_html ) {
-               global $wgOut, $wgUser;
-
-               $wgOut->setPageTitle( wfMsg( 'loginsuccesstitle' ) );
-               $wgOut->setRobotPolicy( 'noindex,nofollow' );
-               $wgOut->setArticleRelated( false );
-               $wgOut->addWikiMsg( $msgname, $wgUser->getName() );
-               $wgOut->addHTML( $injected_html );
-
-               if ( !empty( $this->mReturnTo ) ) {
-                       $wgOut->returnToMain( null, $this->mReturnTo, $this->mReturnToQuery );
+               if( $this->mUsername ){
+                       $this->mFormFields['Name']['default'] = $this->mUsername;
+                       $this->mFormFields['Password']['autofocus'] = '1';
                } else {
-                       $wgOut->returnToMain( null );
-               }
-       }
-
-       /** */
-       function userNotPrivilegedMessage($errors) {
-               global $wgOut;
-
-               $wgOut->setPageTitle( wfMsg( 'permissionserrors' ) );
-               $wgOut->setRobotPolicy( 'noindex,nofollow' );
-               $wgOut->setArticleRelated( false );
-
-               $wgOut->addWikitext( $wgOut->formatPermissionsErrorMessage( $errors, 'createaccount' ) );
-               // Stuff that might want to be added at the end. For example, instruc-
-               // tions if blocked.
-               $wgOut->addWikiMsg( 'cantcreateaccount-nonblock-text' );
-
-               $wgOut->returnToMain( false );
-       }
-
-       /** */
-       function userBlockedMessage() {
-               global $wgOut, $wgUser;
-
-               # Let's be nice about this, it's likely that this feature will be used
-               # for blocking large numbers of innocent people, e.g. range blocks on
-               # schools. Don't blame it on the user. There's a small chance that it
-               # really is the user's fault, i.e. the username is blocked and they
-               # haven't bothered to log out before trying to create an account to
-               # evade it, but we'll leave that to their guilty conscience to figure
-               # out.
-
-               $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) );
-               $wgOut->setRobotPolicy( 'noindex,nofollow' );
-               $wgOut->setArticleRelated( false );
-
-               $ip = wfGetIP();
-               $blocker = User::whoIs( $wgUser->mBlock->mBy );
-               $block_reason = $wgUser->mBlock->mReason;
-
-               if ( strval( $block_reason ) === '' ) {
-                       $block_reason = wfMsg( 'blockednoreason' );
-               }
-               $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $block_reason, $blocker );
-               $wgOut->returnToMain( false );
-       }
-
-       /**
-        * @private
-        */
-       function mainLoginForm( $msg, $msgtype = 'error' ) {
-               global $wgUser, $wgOut, $wgHiddenPrefs, $wgEnableEmail;
-               global $wgCookiePrefix, $wgLoginLanguageSelector;
-               global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration;
-               
-               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
-               
-               if ( $this->mType == 'signup' ) {
-                       // Block signup here if in readonly. Keeps user from 
-                       // going through the process (filling out data, etc) 
-                       // and being informed later.
-                       if ( wfReadOnly() ) {
-                               $wgOut->readOnlyPage();
-                               return;
-                       } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
-                               $this->userBlockedMessage();
-                               return;
-                       } elseif ( count( $permErrors = $titleObj->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
-                               $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
-                               return;
-                       }
+                       $this->mFormFields['Name']['autofocus'] = '1';
                }
 
-               if ( '' == $this->mName ) {
-                       if ( $wgUser->isLoggedIn() ) {
-                               $this->mName = $wgUser->getName();
-                       } else {
-                               $this->mName = isset( $_COOKIE[$wgCookiePrefix.'UserName'] ) ? $_COOKIE[$wgCookiePrefix.'UserName'] : null;
+               # Parse the error message if we got one
+               if( $msg ){
+                       if( $msgtype == 'error' ){
+                               $msg = wfMsgExt( 'loginerror', 'parseinline' ) . ' ' . $msg;
                        }
-               }
-
-               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
-
-               if ( $this->mType == 'signup' ) {
-                       $template = new UsercreateTemplate();
-                       $q = 'action=submitlogin&type=signup';
-                       $linkq = 'type=login';
-                       $linkmsg = 'gotaccount';
+                       $msg = Html::rawElement(
+                               'div',
+                               array( 'class' => $msgtype . 'box' ),
+                               $msg
+                       );
                } else {
-                       $template = new UserloginTemplate();
-                       $q = 'action=submitlogin&type=login';
-                       $linkq = 'type=signup';
-                       $linkmsg = 'nologin';
+                       $msg = '';
                }
 
+               # Make sure the returnTo strings don't get lost if the
+               # user changes language, etc
+               $linkq = array();
                if ( !empty( $this->mReturnTo ) ) {
-                       $returnto = '&returnto=' . wfUrlencode( $this->mReturnTo );
+                       $linkq['returnto'] = wfUrlencode( $this->mReturnTo );
                        if ( !empty( $this->mReturnToQuery ) )
-                               $returnto .= '&returntoquery=' .
-                                       wfUrlencode( $this->mReturnToQuery );
-                       $q .= $returnto;
-                       $linkq .= $returnto;
+                               $linkq['returntoquery'] = wfUrlencode( $this->mReturnToQuery );
                }
 
                # Pass any language selection on to the mode switch link
                if( $wgLoginLanguageSelector && $this->mLanguage )
-                       $linkq .= '&uselang=' . $this->mLanguage;
+                       $linkq['uselang'] = $this->mLanguage;
 
-               $link = '<a href="' . htmlspecialchars ( $titleObj->getLocalUrl( $linkq ) ) . '">';
-               $link .= wfMsgHtml( $linkmsg . 'link' ); # Calling either 'gotaccountlink' or 'nologinlink'
-               $link .= '</a>';
+               $skin = $wgUser->getSkin();
+               $link = $skin->link( 
+                       SpecialPage::getTitleFor( 'CreateAccount' ),
+                       wfMsgHtml( 'nologinlink' ),
+                       array(),
+                       $linkq );
 
                # Don't show a "create account" link if the user can't
-               if( $this->showCreateOrLoginLink( $wgUser ) )
-                       $template->set( 'link', wfMsgWikiHtml( $linkmsg, $link ) );
-               else
-                       $template->set( 'link', '' );
-
-               $template->set( 'header', '' );
-               $template->set( 'name', $this->mName );
-               $template->set( 'password', $this->mPassword );
-               $template->set( 'retype', $this->mRetype );
-               $template->set( 'email', $this->mEmail );
-               $template->set( 'realname', $this->mRealName );
-               $template->set( 'domain', $this->mDomain );
-
-               $template->set( 'action', $titleObj->getLocalUrl( $q ) );
-               $template->set( 'message', $msg );
-               $template->set( 'messagetype', $msgtype );
-               $template->set( 'createemail', $wgEnableEmail && $wgUser->isLoggedIn() );
-               $template->set( 'userealname', !in_array( 'realname', $wgHiddenPrefs ) );
-               $template->set( 'useemail', $wgEnableEmail );
-               $template->set( 'emailrequired', $wgEmailConfirmToEdit );
-               $template->set( 'canreset', $wgAuth->allowPasswordChange() );
-               $template->set( 'canremember', ( $wgCookieExpiration > 0 ) );
-               $template->set( 'remember', $wgUser->getOption( 'rememberpassword' ) or $this->mRemember  );
+               $link = $wgUser->isAllowed( 'createaccount' ) && !$wgUser->isLoggedIn()
+                       ? wfMsgWikiHtml( 'nologin', $link )
+                       : '';
 
                # Prepare language selection links as needed
-               if( $wgLoginLanguageSelector ) {
-                       $template->set( 'languages', $this->makeLanguageSelector() );
-                       if( $this->mLanguage )
-                               $template->set( 'uselang', $this->mLanguage );
-               }
-
-               // Give authentication and captcha plugins a chance to modify the form
-               $wgAuth->modifyUITemplate( $template, $this->mType );
-               if ( $this->mType == 'signup' ) {
-                       wfRunHooks( 'UserCreateForm', array( &$template ) );
+               $langSelector = $wgLoginLanguageSelector 
+                       ? Html::rawElement( 
+                               'div',
+                               array( 'id' => 'languagelinks' ),
+                               self::makeLanguageSelector( $this->getTitle(), $this->mReturnTo ) )
+                       : '';
+               
+               # Add a  'mail reset' button if available
+               $buttons = '';
+               if( $wgEnableEmail && $wgAuth->allowPasswordChange() ){
+                       $buttons = Html::element(
+                               'input',
+                               array( 
+                                       'type'  => 'submit',
+                                       'name'  => 'wpMailmypassword',
+                                       'value' => wfMsg( 'mailmypassword' ),
+                                       'id'    => 'wpMailmypassword',
+                               ) 
+                       );
+               }
+
+               # Give authentication and captcha plugins a chance to 
+               # modify the form, by hook or by using $wgAuth
+               $wgAuth->modifyUITemplate( $this, 'login' );
+               wfRunHooks( 'UserLoginForm', array( &$this ) );
+       
+               # The most likely use of the hook is to enable domains;
+               # check that now, and add fields if necessary
+               if( $this->mDomains ){
+                       $this->mFormFields['Domain']['options'] = $this->mDomains;
+                       $this->mFormFields['Domain']['default'] = $this->mDomain;
                } else {
-                       wfRunHooks( 'UserLoginForm', array( &$template ) );
+                       unset( $this->mFormFields['Domain'] );
                }
-
-               $wgOut->setPageTitle( wfMsg( 'userlogin' ) );
+               
+               # Or to tweak the 'remember my password' checkbox
+               if( !($wgCookieExpiration > 0) ){
+                       # Remove it altogether
+                       unset( $this->mFormFields['Remember'] );
+               } elseif( $wgUser->getOption( 'rememberpassword' ) || $this->mRemember ){
+                       # Or check it by default
+                       # FIXME: this doesn't always work?
+                       $this->mFormFields['Remember']['checked'] = '1';
+               }
+               
+               $form = new HTMLForm( $this->mFormFields, '' );
+               $form->setTitle( $this->getTitle() );
+               $form->setSubmitText( wfMsg( 'login' ) );
+               $form->setSubmitId( 'wpLoginAttempt' );
+               $form->suppressReset();
+               $form->loadData();
+               
+               $formContents = '' 
+                       . Html::rawElement( 'p', array( 'id' => 'userloginlink' ),
+                               $link )
+                       . Html::rawElement( 'div', array( 'id' => 'userloginprompt' ),
+                               wfMsgExt( 'loginprompt', array( 'parseinline' ) ) )
+                       . $this->mFormHeader
+                       . $langSelector
+                       . $form->getBody() 
+                       . $form->getButtons()
+                       . $buttons
+                       . Xml::hidden( 'returnto', $this->mReturnTo )
+                       . Xml::hidden( 'returntoquery', $this->mReturnToQuery )
+               ;
+
+               $wgOut->setPageTitle( wfMsg( 'login' ) );
                $wgOut->setRobotPolicy( 'noindex,nofollow' );
                $wgOut->setArticleRelated( false );
-               $wgOut->disallowUserJs();  // just in case...
-               $wgOut->addTemplate( $template );
-       }
-
-       /**
-        * @private
-        */
-       function showCreateOrLoginLink( &$user ) {
-               if( $this->mType == 'signup' ) {
-                       return( true );
-               } elseif( $user->isAllowed( 'createaccount' ) ) {
-                       return( true );
-               } else {
-                       return( false );
-               }
-       }
+               $wgOut->disallowUserJs();  # Stop malicious userscripts sniffing passwords
+
+               $wgOut->addHTML( 
+                       Html::rawElement( 
+                               'div', 
+                               array( 'id' => 'loginstart' ), 
+                               wfMsgExt( 'loginstart', array( 'parseinline' ) )
+                       ) . 
+                       $msg . 
+                       Html::rawElement(
+                               'div',
+                               array( 'id' => 'userloginForm' ),
+                               $form->wrapForm( $formContents )
+                       ) . 
+                       Html::rawElement( 
+                               'div', 
+                               array( 'id' => 'loginend' ), 
+                               wfMsgExt( 'loginend', array( 'parseinline' ) )
+                       )
+               );
+       }       
 
        /**
         * Check if a session cookie is present.
@@ -967,57 +286,49 @@ class LoginForm {
         *
         * @private
         */
-       function hasSessionCookie() {
+       protected function hasSessionCookie() {
                global $wgDisableCookieCheck, $wgRequest;
-               return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
+               return $wgDisableCookieCheck || $wgRequest->checkSessionCookie();
        }
 
        /**
-        * @private
+        * Do a redirect back to the same page, so we can check any
+        * new session cookies.
         */
-       function cookieRedirectCheck( $type ) {
+       protected function cookieRedirectCheck() {
                global $wgOut;
 
-               $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
-               $query = array( 'wpCookieCheck' => $type );
+               $query = array( 'wpCookieCheck' => '1');
                if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo;
-               $check = $titleObj->getFullURL( $query );
+               $check = $this->getTitle()->getFullURL( $query );
 
                return $wgOut->redirect( $check );
        }
 
        /**
-        * @private
+        * Check the cookies and show errors if they're not enabled.
+        * @param $type String action being performed
         */
-       function onCookieRedirectCheck( $type ) {
-               if ( !$this->hasSessionCookie() ) {
-                       if ( $type == 'new' ) {
-                               return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) );
-                       } else if ( $type == 'login' ) {
-                               return $this->mainLoginForm( wfMsgExt( 'nocookieslogin', array( 'parseinline' ) ) );
-                       } else {
-                               # shouldn't happen
-                               return $this->mainLoginForm( wfMsg( 'error' ) );
-                       }
+       protected function onCookieRedirectCheck() {
+               if ( $this->hasSessionCookie() ) {
+                       return self::successfulLogin( 
+                               'loginsuccess', 
+                               $this->mReturnTo, 
+                               $this->mReturnToQuery
+                       );
                } else {
-                       return $this->successfulLogin();
+                       return $this->mainLoginForm( wfMsgExt( 'nocookieslogin', array( 'parseinline' ) ) );
                }
        }
 
-       /**
-        * @private
-        */
-       function throttleHit( $limit ) {
-               $this->mainLoginForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $limit ) );
-       }
-
        /**
         * Produce a bar of links which allow the user to select another language
         * during login/registration but retain "returnto"
-        *
-        * @return string
+        * @param $title Title to use in the link
+        * @param $returnTo query string to append
+        * @return String HTML for bar
         */
-       function makeLanguageSelector() {
+       public static function makeLanguageSelector( $title, $returnTo=false ) {
                global $wgLang;
 
                $msg = wfMsgForContent( 'loginlanguagelinks' );
@@ -1028,7 +339,8 @@ class LoginForm {
                                $lang = trim( $lang, '* ' );
                                $parts = explode( '|', $lang );
                                if (count($parts) >= 2) {
-                                       $links[] = $this->makeLanguageSelectorLink( $parts[0], $parts[1] );
+                                       $links[] = SpecialUserLogin::makeLanguageSelectorLink( 
+                                                       $parts[0], $parts[1], $title, $returnTo );
                                }
                        }
                        return count( $links ) > 0 ? wfMsgHtml( 'loginlanguagelabel', $wgLang->pipeList( $links ) ) : '';
@@ -1040,24 +352,204 @@ class LoginForm {
        /**
         * Create a language selector link for a particular language
         * Links back to this page preserving type and returnto
-        *
         * @param $text Link text
         * @param $lang Language code
+        * @param $title Title to link to
+        * @param $returnTo String returnto query
         */
-       function makeLanguageSelectorLink( $text, $lang ) {
+       public static function makeLanguageSelectorLink( $text, $lang, $title, $returnTo=false ) {
                global $wgUser;
-               $self = SpecialPage::getTitleFor( 'Userlogin' );
                $attr = array( 'uselang' => $lang );
-               if( $this->mType == 'signup' )
-                       $attr['type'] = 'signup';
-               if( $this->mReturnTo )
-                       $attr['returnto'] = $this->mReturnTo;
+               if( $returnTo )
+                       $attr['returnto'] = $returnTo;
                $skin = $wgUser->getSkin();
                return $skin->linkKnown(
-                       $self,
+                       $title,
                        htmlspecialchars( $text ),
                        array(),
                        $attr
                );
        }
+
+       /**
+        * Display a "login successful" page.
+        * @param $message String message key of main message to display
+        * @param $html String HTML to optionally add
+        * @param $returnto Title to returnto
+        * @param $returntoQuery String query string for returnto link
+        */
+       public static function displaySuccessfulLogin( $message, $html='', $returnTo=false, $returnToQuery=false ) {
+               global $wgOut, $wgUser;
+               
+               $wgOut->setPageTitle( wfMsg( 'loginsuccesstitle' ) );
+               $wgOut->setRobotPolicy( 'noindex,nofollow' );
+               $wgOut->setArticleRelated( false );
+               $wgOut->addWikiMsg( $message, $wgUser->getName() );
+               $wgOut->addHTML( $html );
+
+               if ( $returnTo ) {
+                       $wgOut->returnToMain( null, $returnTo, $returnToQuery );
+               } else {
+                       $wgOut->returnToMain( null );
+               }
+       }
+
+       /**
+        * Display any messages generated by hooks, or HTTP redirect to
+        * $this->mReturnTo (or Main Page if that's undefined).  Formerly we had a
+        * nice message here, but that's not as useful as just being sent to
+        * wherever you logged in from.  It should be clear that the action was
+        * successful, given the lack of error messages plus the appearance of your
+        * name in the upper right.
+        * 
+        * Remember that this function can be accessed from a variety of 
+        * places, such as Special:ResetPass, or Special:CreateAccount.
+        * @param $message String message key of a message to display if
+        *   we don't redirect
+        * @param $returnTo String title of page to redirect to
+        * @param $returnToQuery String query string to add to the redirect.
+        * @param $html String empty string to go straight 
+        *   to the redirect, or valid HTML to add underneath the text.
+        */
+       public static function successfulLogin( $message, $returnTo='', $returnToQuery='', $html='' ) {
+               global $wgUser, $wgOut;
+
+               if( $html === '' ) {
+                       $titleObj = Title::newFromText( $returnTo );
+                       if ( !$titleObj instanceof Title ) {
+                               $titleObj = Title::newMainPage();
+                       }
+                       $wgOut->redirect( $titleObj->getFullURL( $returnToQuery ) );
+               } else {
+                       SpecialUserLogin::displaySuccessfulLogin( $message, $html, $returnTo, $returnToQuery );
+               }
+       }
+       
+
+       protected function processLogin(){
+               global $wgUser, $wgAuth;
+               $result = $this->mLogin->attemptLogin();
+               switch ( $result ) {
+                       case Login::SUCCESS:
+                               if( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
+                                       # Replace the language object to provide user interface in
+                                       # correct language immediately on this first page load.
+                                       global $wgLang, $wgRequest;
+                                       $code = $wgRequest->getVal( 'uselang', $wgUser->getOption( 'language' ) );
+                                       $wgLang = Language::factory( $code );
+                                       return self::successfulLogin( 
+                                               'loginsuccess', 
+                                               $this->mReturnTo, 
+                                               $this->mReturnToQuery,
+                                               $this->mLogin->mLoginResult );
+                               } else {
+                                       # Do a redirect check to ensure that the cookies are 
+                                       # being retained by the user's browser.
+                                       return $this->cookieRedirectCheck();
+                               }
+                               break;
+
+                       case Login::NO_NAME:
+                       case Login::ILLEGAL:
+                       case Login::WRONG_PLUGIN_PASS:
+                       case Login::WRONG_PASS:
+                       case Login::EMPTY_PASS:
+                       case Login::THROTTLED:
+                               $this->mainLoginForm( wfMsgExt( $this->mLogin->mLoginResult, 'parseinline' ) );
+                               break;
+                               
+                       case Login::NOT_EXISTS:
+                               if( $wgUser->isAllowed( 'createaccount' ) ){
+                                       $this->mainLoginForm( wfMsgExt( 'nosuchuser', 'parseinline', htmlspecialchars( $this->mName ) ) );
+                               } else {
+                                       $this->mainLoginForm( wfMsgExt( 'nosuchusershort', 'parseinline', htmlspecialchars( $this->mName ) ) );
+                               }
+                               break;
+                               
+                       case Login::RESET_PASS:
+                               # 'Shell out' to Special:ResetPass to get the user to 
+                               # set a new permanent password from a temporary one.
+                               $reset = new SpecialResetpass();
+                               $reset->mHeaderMsg = 'resetpass_announce';
+                               $reset->mHeaderMsgType = 'success';
+                               $reset->execute( null );
+                               break;
+                               
+                       case Login::CREATE_BLOCKED:
+                               $this->userBlockedMessage();
+                               break;
+                               
+                       case Login::ABORTED: 
+                               $msg = $this->mLogin->mLoginResult ? $this->mLogin->mLoginResult : $this->mLogin->mCreateResult;
+                               $this->mainLoginForm( wfMsgExt( $msg, 'parseinline' ) );
+                               break;
+                               
+                       default:
+                               throw new MWException( "Unhandled case value: $result" );
+               }
+       }
+
+       /**
+        * Attempt to send the user a password-reset mail, and display
+        * the results (good, bad or ugly).
+        */
+       protected function showMailPage(){
+               global $wgOut;
+               $result = $this->mLogin->mailPassword();
+
+               switch( $result ){
+                       case Login::READ_ONLY : 
+                               $wgOut->readOnlyPage();
+                               return;
+                       case Login::MAIL_PASSCHANGE_FORBIDDEN:
+                               $this->mainLoginForm( wfMsgExt( 'resetpass_forbidden', 'parseinline' ) );
+                               return;
+                       case Login::MAIL_BLOCKED: 
+                               $this->mainLoginForm( wfMsgExt( 'blocked-mailpassword', 'parseinline' ) );
+                               return;
+                       case Login::MAIL_PING_THROTTLED: 
+                               $wgOut->rateLimited();
+                               return;
+                       case Login::MAIL_PASS_THROTTLED: 
+                               global $wgPasswordReminderResendTime;
+                               # Round the time in hours to 3 d.p., in case someone 
+                               # is specifying minutes or seconds.
+                               $this->mainLoginForm( wfMsgExt( 
+                                       'throttled-mailpassword', 
+                                       array( 'parsemag' ),
+                                       round( $wgPasswordReminderResendTime, 3 )
+                               ) );
+                               return;
+                       case Login::NO_NAME: 
+                               $this->mainLoginForm( wfMsgExt( 'noname', 'parseinline' ) );
+                               return;
+                       case Login::NOT_EXISTS: 
+                               $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $this->mLogin->mUser->getName() ) ) );
+                               return;
+                       case Login::MAIL_EMPTY_EMAIL: 
+                               $this->mainLoginForm( wfMsgExt( 'noemail', 'parseinline', $this->mLogin->mUser->getName() ) );
+                               return;
+                       case Login::MAIL_BAD_IP: 
+                               $this->mainLoginForm( wfMsgExt( 'badipaddress', 'parseinline' ) );
+                               return;
+                       case Login::MAIL_ERROR: 
+                               $this->mainLoginForm( wfMsgExt( 'mailerror', 'parseinline', $this->mLogin->mMailResult->getMessage() ) );
+                               return;
+                       case Login::SUCCESS:
+                               $this->mainLoginForm( wfMsgExt( 'passwordsent', 'parseinline', $this->mLogin->mUser->getName() ), 'success' );
+                               return;
+               }
+       }
+
+       /**
+        * Since the UserLoginForm hook was changed to pass a SpecialPage
+        * instead of a QuickTemplate derivative, old extensions might
+        * easily try calling this method expecing it to exist.  Tempting
+        * though it is to let them have the fatal error, let's at least
+        * fail gracefully...
+        * @deprecated
+        */
+       public function set(){
+               wfDeprecated( __METHOD__ );
+       }
 }
diff --git a/includes/templates/Userlogin.php b/includes/templates/Userlogin.php
deleted file mode 100644 (file)
index 96713ff..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-<?php
-/**
- * @defgroup Templates Templates
- * @file
- * @ingroup Templates
- */
-if( !defined( 'MEDIAWIKI' ) ) die( -1 );
-
-/**
- * HTML template for Special:Userlogin form
- * @ingroup Templates
- */
-class UserloginTemplate extends QuickTemplate {
-       function execute() {
-               if( $this->data['message'] ) {
-?>
-       <div class="<?php $this->text('messagetype') ?>box">
-               <?php if ( $this->data['messagetype'] == 'error' ) { ?>
-                       <h2><?php $this->msg('loginerror') ?></h2>
-               <?php } ?>
-               <?php $this->html('message') ?>
-       </div>
-       <div class="visualClear"></div>
-<?php } ?>
-
-<div id="loginstart"><?php $this->msgWiki( 'loginstart' ); ?></div>
-<div id="userloginForm">
-<form name="userlogin" method="post" action="<?php $this->text('action') ?>">
-       <h2><?php $this->msg('login') ?></h2>
-       <p id="userloginlink"><?php $this->html('link') ?></p>
-       <?php $this->html('header'); /* pre-table point for form plugins... */ ?>
-       <div id="userloginprompt"><?php  $this->msgWiki('loginprompt') ?></div>
-       <?php if( @$this->haveData( 'languages' ) ) { ?><div id="languagelinks"><p><?php $this->html( 'languages' ); ?></p></div><?php } ?>
-       <table>
-               <tr>
-                       <td class="mw-label"><label for='wpName1'><?php $this->msg('yourname') ?></label></td>
-                       <td class="mw-input">
-                               <?php
-                       echo Html::input( 'wpName', $this->data['name'], 'text', array(
-                               'class' => 'loginText',
-                               'id' => 'wpName1',
-                               'tabindex' => '1',
-                               'size' => '20',
-                               'required'
-                               # Can't do + array( 'autofocus' ) because + for arrays in PHP
-                               # only works right for associative arrays!  Thanks, PHP.
-                       ) + ( $this->data['name'] ? array() : array( 'autofocus' => '' ) ) ); ?>
-
-                       </td>
-               </tr>
-               <tr>
-                       <td class="mw-label"><label for='wpPassword1'><?php $this->msg('yourpassword') ?></label></td>
-                       <td class="mw-input">
-                               <?php
-                       echo Html::input( 'wpPassword', null, 'password', array(
-                               'class' => 'loginPassword',
-                               'id' => 'wpPassword1',
-                               'tabindex' => '2',
-                               'size' => '20'
-                       ) + ( $this->data['name'] ? array( 'autofocus' ) : array() ) ); ?>
-
-                       </td>
-               </tr>
-       <?php if( $this->data['usedomain'] ) {
-               $doms = "";
-               foreach( $this->data['domainnames'] as $dom ) {
-                       $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
-               }
-       ?>
-               <tr id="mw-user-domain-section">
-                       <td class="mw-label"><?php $this->msg( 'yourdomainname' ) ?></td>
-                       <td class="mw-input">
-                               <select name="wpDomain" value="<?php $this->text( 'domain' ) ?>"
-                                       tabindex="3">
-                                       <?php echo $doms ?>
-                               </select>
-                       </td>
-               </tr>
-       <?php }
-       if( $this->data['canremember'] ) { ?>
-               <tr>
-                       <td></td>
-                       <td class="mw-input">
-                               <?php
-               echo Html::input( 'wpRemember', '1', 'checkbox', array(
-                       'tabindex' => '4',
-                       'id' => 'wpRemember'
-               ) + ( $this->data['remember'] ? array( 'checked' ) : array() ) ); ?>
-
-                               <label for="wpRemember"><?php $this->msg('remembermypassword') ?></label>
-                       </td>
-               </tr>
-<?php } ?>
-               <tr>
-                       <td></td>
-                       <td class="mw-submit">
-                               <?php
-               echo Html::input( 'wpLoginAttempt', wfMsg( 'login' ), 'submit', array(
-                       'id' => 'wpLoginAttempt',
-                       'tabindex' => '5'
-               ) );
-               if ( $this->data['useemail'] && $this->data['canreset'] ) {
-                       echo '&nbsp;';
-                       echo Html::input( 'wpMailmypassword', wfMsg( 'mailmypassword' ), 'submit', array(
-                               'id' => 'wpMailmypassword',
-                               'tabindex' => '6'
-                       ) );
-               } ?>
-
-                       </td>
-               </tr>
-       </table>
-<?php if( @$this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
-</form>
-</div>
-<div id="loginend"><?php $this->msgWiki( 'loginend' ); ?></div>
-<?php
-
-       }
-}
-
-/**
- * @ingroup Templates
- */
-class UsercreateTemplate extends QuickTemplate {
-       function addInputItem( $name, $value, $type, $msg, $helptext = false ) {
-               $this->data['extraInput'][] = array(
-                       'name' => $name,
-                       'value' => $value,
-                       'type' => $type,
-                       'msg' => $msg,
-                       'helptext' => $helptext,
-               );
-       }
-       
-       function execute() {
-               if( $this->data['message'] ) {
-?>
-       <div class="<?php $this->text('messagetype') ?>box">
-               <?php if ( $this->data['messagetype'] == 'error' ) { ?>
-                       <h2><?php $this->msg('loginerror') ?></h2>
-               <?php } ?>
-               <?php $this->html('message') ?>
-       </div>
-       <div class="visualClear"></div>
-<?php } ?>
-<div id="userlogin">
-
-<form name="userlogin2" id="userlogin2" method="post" action="<?php $this->text('action') ?>">
-       <h2><?php $this->msg('createaccount') ?></h2>
-       <p id="userloginlink"><?php $this->html('link') ?></p>
-       <?php $this->html('header'); /* pre-table point for form plugins... */ ?>
-       <?php if( @$this->haveData( 'languages' ) ) { ?><div id="languagelinks"><p><?php $this->html( 'languages' ); ?></p></div><?php } ?>
-       <table>
-               <tr>
-                       <td class="mw-label"><label for='wpName2'><?php $this->msg('yourname') ?></label></td>
-                       <td class="mw-input">
-                               <?php
-                       echo Html::input( 'wpName', $this->data['name'], 'text', array(
-                               'class' => 'loginText',
-                               'id' => 'wpName2',
-                               'tabindex' => '1',
-                               'size' => '20',
-                               'required',
-                               'autofocus'
-                       ) ); ?>
-                       </td>
-               </tr>
-               <tr>
-                       <td class="mw-label"><label for='wpPassword2'><?php $this->msg('yourpassword') ?></label></td>
-                       <td class="mw-input">
-<?php
-                       echo Html::input( 'wpPassword', null, 'password', array(
-                               'class' => 'loginPassword',
-                               'id' => 'wpPassword2',
-                               'tabindex' => '2',
-                               'size' => '20'
-                       ) + User::passwordChangeInputAttribs() ); ?>
-                       </td>
-               </tr>
-       <?php if( $this->data['usedomain'] ) {
-               $doms = "";
-               foreach( $this->data['domainnames'] as $dom ) {
-                       $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
-               }
-       ?>
-               <tr>
-                       <td class="mw-label"><?php $this->msg( 'yourdomainname' ) ?></td>
-                       <td class="mw-input">
-                               <select name="wpDomain" value="<?php $this->text( 'domain' ) ?>"
-                                       tabindex="3">
-                                       <?php echo $doms ?>
-                               </select>
-                       </td>
-               </tr>
-       <?php } ?>
-               <tr>
-                       <td class="mw-label"><label for='wpRetype'><?php $this->msg('yourpasswordagain') ?></label></td>
-                       <td class="mw-input">
-                               <?php
-               echo Html::input( 'wpRetype', null, 'password', array(
-                       'class' => 'loginPassword',
-                       'id' => 'wpRetype',
-                       'tabindex' => '4',
-                       'size' => '20'
-               ) + User::passwordChangeInputAttribs() ); ?>
-                       </td>
-               </tr>
-               <tr>
-                       <?php if( $this->data['useemail'] ) { ?>
-                               <td class="mw-label"><label for='wpEmail'><?php $this->msg('youremail') ?></label></td>
-                               <td class="mw-input">
-                                       <?php
-               echo Html::input( 'wpEmail', $this->data['email'], 'email', array(
-                       'class' => 'loginText',
-                       'id' => 'wpEmail',
-                       'tabindex' => '5',
-                       'size' => '20'
-               ) ); ?>
-                                       <div class="prefsectiontip">
-                                               <?php if( $this->data['emailrequired'] ) {
-                                                                       $this->msgWiki('prefs-help-email-required');
-                                                     } else {
-                                                                       $this->msgWiki('prefs-help-email');
-                                                     } ?>
-                                       </div>
-                               </td>
-                       <?php } ?>
-                       <?php if( $this->data['userealname'] ) { ?>
-                               </tr>
-                               <tr>
-                                       <td class="mw-label"><label for='wpRealName'><?php $this->msg('yourrealname') ?></label></td>
-                                       <td class="mw-input">
-                                               <input type='text' class='loginText' name="wpRealName" id="wpRealName"
-                                                       tabindex="6"
-                                                       value="<?php $this->text('realname') ?>" size='20' />
-                                               <div class="prefsectiontip">
-                                                       <?php $this->msgWiki('prefs-help-realname'); ?>
-                                               </div>
-                                       </td>
-                       <?php } ?>
-               </tr>
-               <?php if( $this->data['canremember'] ) { ?>
-               <tr>
-                       <td></td>
-                       <td class="mw-input">
-                               <input type='checkbox' name="wpRemember"
-                                       tabindex="7"
-                                       value="1" id="wpRemember"
-                                       <?php if( $this->data['remember'] ) { ?>checked="checked"<?php } ?>
-                                       /> <label for="wpRemember"><?php $this->msg('remembermypassword') ?></label>
-                       </td>
-               </tr>
-<?php   }
-
-               $tabIndex = 8;
-               if ( isset( $this->data['extraInput'] ) && is_array( $this->data['extraInput'] ) ) {
-                       foreach ( $this->data['extraInput'] as $inputItem ) { ?>
-               <tr>
-                       <?php 
-                               if ( !empty( $inputItem['msg'] ) && $inputItem['type'] != 'checkbox' ) {
-                                       ?><td class="mw-label"><label for="<?php 
-                                       echo htmlspecialchars( $inputItem['name'] ); ?>"><?php
-                                       $this->msgWiki( $inputItem['msg'] ) ?></label><?php
-                               } else {
-                                       ?><td><?php
-                               }
-                       ?></td>
-                       <td class="mw-input">
-                               <input type="<?php echo htmlspecialchars( $inputItem['type'] ) ?>" name="<?php
-                               echo htmlspecialchars( $inputItem['name'] ); ?>"
-                                       tabindex="<?php echo $tabIndex++; ?>"
-                                       value="<?php 
-                               if ( $inputItem['type'] != 'checkbox' ) {
-                                       echo htmlspecialchars( $inputItem['value'] );
-                               } else {
-                                       echo '1';
-                               }                                       
-                                       ?>" id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
-                                       <?php 
-                               if ( $inputItem['type'] == 'checkbox' && !empty( $inputItem['value'] ) )
-                                       echo 'checked="checked"'; 
-                                       ?> /> <?php 
-                                       if ( $inputItem['type'] == 'checkbox' && !empty( $inputItem['msg'] ) ) {
-                                               ?>
-                               <label for="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"><?php
-                                       $this->msgHtml( $inputItem['msg'] ) ?></label><?php
-                                       }
-                               if( $inputItem['helptext'] !== false ) {
-                               ?>
-                               <div class="prefsectiontip">
-                                       <?php $this->msgWiki( $inputItem['helptext'] ); ?>
-                               </div>
-                               <?php } ?>
-                       </td>
-               </tr>
-<?php                          
-                               
-                       }
-               }
-?>
-               <tr>
-                       <td></td>
-                       <td class="mw-submit">
-                               <input type='submit' name="wpCreateaccount" id="wpCreateaccount"
-                                       tabindex="<?php echo $tabIndex++; ?>"
-                                       value="<?php $this->msg('createaccount') ?>" />
-                               <?php if( $this->data['createemail'] ) { ?>
-                               <input type='submit' name="wpCreateaccountMail" id="wpCreateaccountMail"
-                                       tabindex="<?php echo $tabIndex++; ?>"
-                                       value="<?php $this->msg('createaccountmail') ?>" />
-                               <?php } ?>
-                       </td>
-               </tr>
-       </table>
-<?php if( @$this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
-</form>
-</div>
-<div id="signupend"><?php $this->msgWiki( 'signupend' ); ?></div>
-<?php
-
-       }
-}
index 042db04..b70f684 100644 (file)
@@ -1042,7 +1042,7 @@ Do not forget to change your [[Special:Preferences|{{SITENAME}} preferences]].',
 'login'                      => 'Log in',
 'nav-login-createaccount'    => 'Log in / create account',
 'loginprompt'                => 'You must have cookies enabled to log in to {{SITENAME}}.',
-'userlogin'                  => 'Log in / create account',
+'userlogin'                  => 'Log in',
 'logout'                     => 'Log out',
 'userlogout'                 => 'Log out',
 'notloggedin'                => 'Not logged in',
@@ -1090,6 +1090,7 @@ If someone else made this request, or if you have remembered your password,
 and you no longer wish to change it, you may ignore this message and
 continue using your old password.',
 'noemail'                    => 'There is no e-mail address recorded for user "$1".',
+'noemailcreate'              => 'You need to provide a valid email address',
 'passwordsent'               => 'A new password has been sent to the e-mail address registered for "$1".
 Please log in again after you receive it.',
 'blocked-mailpassword'       => 'Your IP address is blocked from editing, and so is not allowed to use the password recovery function to prevent abuse.',
index 5a266d4..1fcc1c5 100644 (file)
@@ -596,7 +596,7 @@ HTML markup cannot be used.",
 {{Identical|Log in}}",
 'nav-login-createaccount'    => "Shown to anonymous users in the upper right corner of the page. When you can't create an account, the message {{msg|login}} is shown.",
 'loginprompt'                => 'A small notice in the log in form.',
-'userlogin'                  => 'Name of special page [[Special:UserLogin]] where a user can log in or click to create a user account.',
+'userlogin'                  => 'Name of special page [[Special:UserLogin]] where a user can log in.',
 'logout'                     => '{{Identical|Log out}}',
 'userlogout'                 => '{{Identical|Log out}}',
 'notloggedin'                => 'This message is displayed in the standard skin when not logged in. The message is placed above the login link in the top right corner of pages.
index 8f46c5c..b7acae9 100644 (file)
@@ -802,3 +802,5 @@ td.mw-enhanced-rc {
        position: relative;
        top: -16px;
 }
+
+#wpLoginAttempt, #wpCreateaccount { margin-right:0; }