r55990 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r55989‎ | r55990 | r55991 >
Date:19:00, 7 September 2009
Author:happy-melon
Status:reverted (Comments)
Tags:
Comment:
* Totally refactor includes/specials/SpecialUserlogin.php:
** Split backend stuff out into includes/Login.php
** Split account creation and login stuff up, into includes/specials/CreateAccount.php and includes/specials/Userlogin.php.
* Reimplement the special pages as subclasses of SpecialPage
* Use HTMLForm to generate the input forms
** Deprecate and delete includes/templates/Userlogin.php, which is horrible and old :D

This changes the syntax of the UserLoginForm and UserCreateForm hooks, and AuthPlugin::modifyUITemplate; they now receive the SpecialPage subclass rather than the template to work with. Update everything I could find in SVN to accommodate this.
Modified paths:
  • /trunk/extensions/AntiSpoof/AntiSpoof.php (modified) (history)
  • /trunk/extensions/ConfirmAccount/SpecialConfirmAccount.php (modified) (history)
  • /trunk/extensions/ConfirmEdit/ConfirmEdit_body.php (modified) (history)
  • /trunk/extensions/LdapAuthentication/LdapAuthentication.php (modified) (history)
  • /trunk/extensions/WikimediaIncubator/CreateAccountTestWiki.php (modified) (history)
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/docs/hooks.txt (modified) (history)
  • /trunk/phase3/includes/AuthPlugin.php (modified) (history)
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/Login.php (added) (history)
  • /trunk/phase3/includes/SpecialPage.php (modified) (history)
  • /trunk/phase3/includes/api/ApiLogin.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialCreateAccount.php (added) (history)
  • /trunk/phase3/includes/specials/SpecialResetpass.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialUserlogin.php (modified) (history)
  • /trunk/phase3/includes/templates/Userlogin.php (deleted) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesQqq.php (modified) (history)
  • /trunk/phase3/skins/common/shared.css (modified) (history)

Diff [purge]

Index: trunk/phase3/skins/common/shared.css
@@ -105,6 +105,8 @@
106106
107107 table.mw-htmlform-nolabel td.mw-label { width: 0 !important; }
108108
 109+#wpLoginAttempt, #wpCreateaccount { margin-right:0; }
 110+
109111 /**
110112 * Image captions
111113 */
Index: trunk/phase3/docs/hooks.txt
@@ -244,8 +244,8 @@
245245 'AbortLogin': Return false to cancel account login.
246246 $user: the User object being authenticated against
247247 $password: the password being submitted, not yet checked for validity
248 -&$retval: a LoginForm class constant to return from authenticateUserData();
249 - default is LoginForm::ABORTED. Note that the client may be using
 248+&$retval: a Login class constant to return from authenticateUserData();
 249+ default is Login::ABORTED. Note that the client may be using
250250 a machine API rather than the HTML user interface.
251251
252252 'AbortMove': allows to abort moving an article (title)
@@ -944,7 +944,7 @@
945945 succeeded or failed. No return data is accepted; this hook is for auditing only.
946946 $user: the User object being authenticated against
947947 $password: the password being submitted and found wanting
948 -$retval: a LoginForm class constant with authenticateUserData() return
 948+$retval: a Login class constant with authenticateUserData() return
949949 value (SUCCESS, WRONG_PASS, etc)
950950
951951 'LogLine': Processes a single log entry on Special:Log
@@ -1519,7 +1519,7 @@
15201520 determine if the password was valid
15211521
15221522 'UserCreateForm': change to manipulate the login form
1523 -$template: SimpleTemplate instance for the form
 1523+$sp: SpecialCreateAccount instance
15241524
15251525 'UserCryptPassword': called when hashing a password, return false to implement
15261526 your own hashing method
@@ -1589,7 +1589,7 @@
15901590 $inject_html: Any HTML to inject after the "logged in" message.
15911591
15921592 'UserLoginForm': change to manipulate the login form
1593 -$template: SimpleTemplate instance for the form
 1593+$sp: SpecialUserLogin instance
15941594
15951595 'UserLoginMailPassword': Block users from emailing passwords
15961596 $name: the username to email the password of.
Index: trunk/phase3/includes/api/ApiLogin.php
@@ -60,7 +60,7 @@
6161 'wpName' => $params['name'],
6262 'wpPassword' => $params['password'],
6363 'wpDomain' => $params['domain'],
64 - 'wpRemember' => ''
 64+ 'wpRemember' => '1'
6565 ));
6666
6767 // Init session if necessary
@@ -68,19 +68,11 @@
6969 wfSetupSession();
7070 }
7171
72 - $loginForm = new LoginForm($req);
73 - switch ($authRes = $loginForm->authenticateUserData()) {
74 - case LoginForm :: SUCCESS :
 72+ $loginForm = new Login( $req );
 73+ switch ( $authRes = $loginForm->login() ) {
 74+ case Login::SUCCESS :
7575 global $wgUser, $wgCookiePrefix;
7676
77 - $wgUser->setOption('rememberpassword', 1);
78 - $wgUser->setCookies();
79 -
80 - // Run hooks. FIXME: split back and frontend from this hook.
81 - // FIXME: This hook should be placed in the backend
82 - $injected_html = '';
83 - wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
84 -
8577 $result['result'] = 'Success';
8678 $result['lguserid'] = intval($wgUser->getId());
8779 $result['lgusername'] = $wgUser->getName();
@@ -89,35 +81,35 @@
9082 $result['sessionid'] = session_id();
9183 break;
9284
93 - case LoginForm :: NO_NAME :
 85+ case Login::NO_NAME :
9486 $result['result'] = 'NoName';
9587 break;
96 - case LoginForm :: ILLEGAL :
 88+ case Login::ILLEGAL :
9789 $result['result'] = 'Illegal';
9890 break;
99 - case LoginForm :: WRONG_PLUGIN_PASS :
 91+ case Login::WRONG_PLUGIN_PASS :
10092 $result['result'] = 'WrongPluginPass';
10193 break;
102 - case LoginForm :: NOT_EXISTS :
 94+ case Login::NOT_EXISTS :
10395 $result['result'] = 'NotExists';
10496 break;
105 - case LoginForm :: WRONG_PASS :
 97+ case Login::WRONG_PASS :
10698 $result['result'] = 'WrongPass';
10799 break;
108 - case LoginForm :: EMPTY_PASS :
 100+ case Login::EMPTY_PASS :
109101 $result['result'] = 'EmptyPass';
110102 break;
111 - case LoginForm :: CREATE_BLOCKED :
 103+ case Login::CREATE_BLOCKED :
112104 $result['result'] = 'CreateBlocked';
113105 $result['details'] = 'Your IP address is blocked from account creation';
114106 break;
115 - case LoginForm :: THROTTLED :
 107+ case Login::THROTTLED :
116108 global $wgPasswordAttemptThrottle;
117109 $result['result'] = 'Throttled';
118110 $result['wait'] = intval($wgPasswordAttemptThrottle['seconds']);
119111 break;
120112 default :
121 - ApiBase :: dieDebug(__METHOD__, "Unhandled case value: {$authRes}");
 113+ ApiBase::dieDebug(__METHOD__, "Unhandled case value: {$authRes}");
122114 }
123115
124116 $this->getResult()->addValue(null, 'login', $result);
Index: trunk/phase3/includes/AutoLoader.php
@@ -136,6 +136,8 @@
137137 'LinksUpdate' => 'includes/LinksUpdate.php',
138138 'LocalisationCache' => 'includes/LocalisationCache.php',
139139 'LocalisationCache_BulkLoad' => 'includes/LocalisationCache.php',
 140+ 'LoginForm' => 'includes/Login.php', # For B/C
 141+ 'Login' => 'includes/Login.php',
140142 'LogPage' => 'includes/LogPage.php',
141143 'LogPager' => 'includes/LogEventsList.php',
142144 'LogEventsList' => 'includes/LogEventsList.php',
@@ -491,6 +493,7 @@
492494 'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php',
493495 'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php',
494496 'ContribsPager' => 'includes/specials/SpecialContributions.php',
 497+ 'SpecialCreateAccount' => 'includes/specials/SpecialCreateAccount.php',
495498 'DBLockForm' => 'includes/specials/SpecialLockdb.php',
496499 'DBUnlockForm' => 'includes/specials/SpecialUnlockdb.php',
497500 'DeadendPagesPage' => 'includes/specials/SpecialDeadendpages.php',
@@ -511,7 +514,6 @@
512515 'ImportStringSource' => 'includes/Import.php',
513516 'LinkSearchPage' => 'includes/specials/SpecialLinkSearch.php',
514517 'ListredirectsPage' => 'includes/specials/SpecialListredirects.php',
515 - 'LoginForm' => 'includes/specials/SpecialUserlogin.php',
516518 'LonelyPagesPage' => 'includes/specials/SpecialLonelypages.php',
517519 'LongPagesPage' => 'includes/specials/SpecialLongpages.php',
518520 'MIMEsearchPage' => 'includes/specials/SpecialMIMEsearch.php',
@@ -560,6 +562,7 @@
561563 'UnwatchedpagesPage' => 'includes/specials/SpecialUnwatchedpages.php',
562564 'UploadForm' => 'includes/specials/SpecialUpload.php',
563565 'UploadFormMogile' => 'includes/specials/SpecialUploadMogile.php',
 566+ 'SpecialUserLogin' => 'includes/specials/SpecialUserlogin.php',
564567 'UserrightsPage' => 'includes/specials/SpecialUserrights.php',
565568 'UsersPager' => 'includes/specials/SpecialListusers.php',
566569 'WantedCategoriesPage' => 'includes/specials/SpecialWantedcategories.php',
Index: trunk/phase3/includes/AuthPlugin.php
@@ -62,12 +62,13 @@
6363 /**
6464 * Modify options in the login template.
6565 *
66 - * @param $template UserLoginTemplate object.
67 - * @param $type String 'signup' or 'login'.
 66+ * @param $sp SpecialUserlogin or SpecialCreateAccount object.
 67+ * @param $type String 'signup' or 'login'. Redundant because
 68+ * you can just use instanceof to tell the two cases apart.
6869 */
69 - public function modifyUITemplate( &$template, &$type ) {
 70+ public function modifyUITemplate( &$sp, $type=null ) {
7071 # Override this!
71 - $template->set( 'usedomain', false );
 72+ $sp->mDomains = false;
7273 }
7374
7475 /**
Index: trunk/phase3/includes/Login.php
@@ -0,0 +1,380 @@
 2+<?php
 3+
 4+/**
 5+ * Encapsulates the backend activities of logging a user into the wiki.
 6+ */
 7+class Login {
 8+
 9+ const SUCCESS = 0;
 10+ const NO_NAME = 1;
 11+ const ILLEGAL = 2;
 12+ const WRONG_PLUGIN_PASS = 3;
 13+ const NOT_EXISTS = 4;
 14+ const WRONG_PASS = 5;
 15+ const EMPTY_PASS = 6;
 16+ const RESET_PASS = 7;
 17+ const ABORTED = 8;
 18+ const CREATE_BLOCKED = 9;
 19+ const THROTTLED = 10;
 20+
 21+ const MAIL_READ_ONLY = 11;
 22+ const MAIL_PASSCHANGE_FORBIDDEN = 12;
 23+ const MAIL_BLOCKED = 13;
 24+ const MAIL_PING_THROTTLED = 14;
 25+ const MAIL_PASS_THROTTLED = 15;
 26+ const MAIL_EMPTY_EMAIL = 16;
 27+ const MAIL_BAD_IP = 17;
 28+ const MAIL_ERROR = 18;
 29+
 30+ var $mName, $mPassword, $mPosted;
 31+ var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage;
 32+
 33+ private $mExtUser = null;
 34+
 35+ public $mUser;
 36+ public $mMailResult;
 37+
 38+ /**
 39+ * Constructor
 40+ * @param WebRequest $request A WebRequest object passed by reference.
 41+ * uses $wgRequest if not given.
 42+ */
 43+ public function __construct( &$request=null ) {
 44+ global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
 45+ if( !$request ) $request = &$wgRequest;
 46+
 47+ $this->mName = $request->getText( 'wpName' );
 48+ $this->mPassword = $request->getText( 'wpPassword' );
 49+ $this->mDomain = $request->getText( 'wpDomain' );
 50+ $this->mPosted = $request->wasPosted();
 51+ $this->mRemember = $request->getCheck( 'wpRemember' );
 52+
 53+ if( $wgEnableEmail ) {
 54+ $this->mEmail = $request->getText( 'wpEmail' );
 55+ } else {
 56+ $this->mEmail = '';
 57+ }
 58+ if( !in_array( 'realname', $wgHiddenPrefs ) ) {
 59+ $this->mRealName = $request->getText( 'wpRealName' );
 60+ } else {
 61+ $this->mRealName = '';
 62+ }
 63+
 64+ if( !$wgAuth->validDomain( $this->mDomain ) ) {
 65+ $this->mDomain = 'invaliddomain';
 66+ }
 67+ $wgAuth->setDomain( $this->mDomain );
 68+
 69+ # Attempt to generate the User
 70+ $this->mUser = User::newFromName( $this->mName );
 71+ }
 72+
 73+ /**
 74+ * Actually add a user to the database.
 75+ * Give it a User object that has been initialised with a name.
 76+ *
 77+ * @param $u User object.
 78+ * @param $autocreate boolean -- true if this is an autocreation via auth plugin
 79+ * @return User object.
 80+ */
 81+ public function initUser( $autocreate ) {
 82+ global $wgAuth;
 83+
 84+ $this->mUser->addToDatabase();
 85+
 86+ if ( $wgAuth->allowPasswordChange() ) {
 87+ $this->mUser->setPassword( $this->mPassword );
 88+ }
 89+
 90+ $this->mUser->setEmail( $this->mEmail );
 91+ $this->mUser->setRealName( $this->mRealName );
 92+ $this->mUser->setToken();
 93+
 94+ $wgAuth->initUser( $this->mUser, $autocreate );
 95+
 96+ if( $this->mExtUser ) {
 97+ $this->mExtUser->link( $this->mUser->getId() );
 98+ $email = $this->mExtUser->getPref( 'emailaddress' );
 99+ if( $email && !$this->mEmail ) {
 100+ $this->mUser->setEmail( $email );
 101+ }
 102+ }
 103+
 104+ $this->mUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
 105+ $this->mUser->saveSettings();
 106+
 107+ # Update user count
 108+ $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
 109+ $ssUpdate->doUpdate();
 110+
 111+ return $this->mUser;
 112+ }
 113+
 114+ public function login(){
 115+ global $wgUser;
 116+ $code = $this->authenticateUserData();
 117+ if( !$code == self::SUCCESS ){
 118+ return $code;
 119+ }
 120+ if( (bool)$this->mRemember != (bool)$wgUser->getOption( 'rememberpassword' ) ) {
 121+ $wgUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
 122+ $wgUser->saveSettings();
 123+ } else {
 124+ $wgUser->invalidateCache();
 125+ }
 126+ $wgUser->setCookies();
 127+
 128+ # Reset the throttle
 129+ $key = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
 130+ global $wgMemc;
 131+ $wgMemc->delete( $key );
 132+
 133+ $injected_html = '';
 134+ wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
 135+
 136+ return self::SUCCESS;
 137+ }
 138+
 139+ /**
 140+ * Internally authenticate the login request.
 141+ *
 142+ * This may create a local account as a side effect if the
 143+ * authentication plugin allows transparent local account
 144+ * creation.
 145+ */
 146+ public function authenticateUserData() {
 147+ global $wgUser, $wgAuth;
 148+ if ( '' == $this->mName ) {
 149+ return self::NO_NAME;
 150+ }
 151+
 152+ global $wgPasswordAttemptThrottle;
 153+
 154+ $throttleCount = 0;
 155+ if ( is_array( $wgPasswordAttemptThrottle ) ) {
 156+ $throttleKey = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
 157+ $count = $wgPasswordAttemptThrottle['count'];
 158+ $period = $wgPasswordAttemptThrottle['seconds'];
 159+
 160+ global $wgMemc;
 161+ $throttleCount = $wgMemc->get( $throttleKey );
 162+ if ( !$throttleCount ) {
 163+ $wgMemc->add( $throttleKey, 1, $period ); // start counter
 164+ } else if ( $throttleCount < $count ) {
 165+ $wgMemc->incr($throttleKey);
 166+ } else if ( $throttleCount >= $count ) {
 167+ return self::THROTTLED;
 168+ }
 169+ }
 170+
 171+ # Load $wgUser now, and check to see if we're logging in as the same
 172+ # name. This is necessary because loading $wgUser (say by calling
 173+ # getName()) calls the UserLoadFromSession hook, which potentially
 174+ # creates the user in the database. Until we load $wgUser, checking
 175+ # for user existence using User::newFromName($name)->getId() below
 176+ # will effectively be using stale data.
 177+ if ( $wgUser->getName() === $this->mName ) {
 178+ wfDebug( __METHOD__.": already logged in as {$this->mName}\n" );
 179+ return self::SUCCESS;
 180+ }
 181+
 182+ $this->mExtUser = ExternalUser::newFromName( $this->mName );
 183+
 184+ # TODO: Allow some magic here for invalid external names, e.g., let the
 185+ # user choose a different wiki name.
 186+ if( is_null( $this->mUser ) || !User::isUsableName( $this->mUser->getName() ) ) {
 187+ return self::ILLEGAL;
 188+ }
 189+
 190+ $isAutoCreated = false;
 191+ if ( 0 == $this->mUser->getID() ) {
 192+ $status = $this->attemptAutoCreate( $this->mUser );
 193+ if ( $status !== self::SUCCESS ) {
 194+ return $status;
 195+ } else {
 196+ $isAutoCreated = true;
 197+ }
 198+ } else {
 199+ $this->mUser->load();
 200+ }
 201+
 202+ # Give general extensions, such as a captcha, a chance to abort logins
 203+ $abort = self::ABORTED;
 204+ if( !wfRunHooks( 'AbortLogin', array( $this->mUser, $this->mPassword, &$abort ) ) ) {
 205+ return $abort;
 206+ }
 207+
 208+ if( !$this->mUser->checkPassword( $this->mPassword ) ) {
 209+ if( $this->mUser->checkTemporaryPassword( $this->mPassword ) ) {
 210+ # The e-mailed temporary password should not be used for actual
 211+ # logins; that's a very sloppy habit, and insecure if an
 212+ # attacker has a few seconds to click "search" on someone's
 213+ # open mail reader.
 214+ #
 215+ # Allow it to be used only to reset the password a single time
 216+ # to a new value, which won't be in the user's e-mail archives
 217+ #
 218+ # For backwards compatibility, we'll still recognize it at the
 219+ # login form to minimize surprises for people who have been
 220+ # logging in with a temporary password for some time.
 221+ #
 222+ # As a side-effect, we can authenticate the user's e-mail ad-
 223+ # dress if it's not already done, since the temporary password
 224+ # was sent via e-mail.
 225+ if( !$this->mUser->isEmailConfirmed() ) {
 226+ $this->mUser->confirmEmail();
 227+ $this->mUser->saveSettings();
 228+ }
 229+
 230+ # At this point we just return an appropriate code/ indicating
 231+ # that the UI should show a password reset form; bot interfaces
 232+ # etc will probably just fail cleanly here.
 233+ $retval = self::RESET_PASS;
 234+ } else {
 235+ $retval = '' == $this->mPassword ? self::EMPTY_PASS : self::WRONG_PASS;
 236+ }
 237+ } else {
 238+ $wgAuth->updateUser( $this->mUser );
 239+ $wgUser = $this->mUser;
 240+
 241+ # Reset throttle after a successful login
 242+ if( $throttleCount ) {
 243+ $wgMemc->delete( $throttleKey );
 244+ }
 245+
 246+ if( $isAutoCreated ) {
 247+ # Must be run after $wgUser is set, for correct new user log
 248+ wfRunHooks( 'AuthPluginAutoCreate', array( $wgUser ) );
 249+ }
 250+
 251+ $retval = self::SUCCESS;
 252+ }
 253+ wfRunHooks( 'LoginAuthenticateAudit', array( $this->mUser, $this->mPassword, $retval ) );
 254+ return $retval;
 255+ }
 256+
 257+ /**
 258+ * Attempt to automatically create a user on login. Only succeeds if there
 259+ * is an external authentication method which allows it.
 260+ * @return integer Status code
 261+ */
 262+ public function attemptAutoCreate( $user ) {
 263+ global $wgAuth, $wgUser, $wgAutocreatePolicy;
 264+
 265+ if( $wgUser->isBlockedFromCreateAccount() ) {
 266+ wfDebug( __METHOD__.": user is blocked from account creation\n" );
 267+ return self::CREATE_BLOCKED;
 268+ }
 269+
 270+ # If the external authentication plugin allows it, automatically cre-
 271+ # ate a new account for users that are externally defined but have not
 272+ # yet logged in.
 273+ if( $this->mExtUser ) {
 274+ # mExtUser is neither null nor false, so use the new ExternalAuth
 275+ # system.
 276+ if( $wgAutocreatePolicy == 'never' ) {
 277+ return self::NOT_EXISTS;
 278+ }
 279+ if( !$this->mExtUser->authenticate( $this->mPassword ) ) {
 280+ return self::WRONG_PLUGIN_PASS;
 281+ }
 282+ } else {
 283+ # Old AuthPlugin.
 284+ if( !$wgAuth->autoCreate() ) {
 285+ return self::NOT_EXISTS;
 286+ }
 287+ if( !$wgAuth->userExists( $user->getName() ) ) {
 288+ wfDebug( __METHOD__.": user does not exist\n" );
 289+ return self::NOT_EXISTS;
 290+ }
 291+ if( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) {
 292+ wfDebug( __METHOD__.": \$wgAuth->authenticate() returned false, aborting\n" );
 293+ return self::WRONG_PLUGIN_PASS;
 294+ }
 295+ }
 296+
 297+ wfDebug( __METHOD__.": creating account\n" );
 298+ $this->initUser( true );
 299+ return self::SUCCESS;
 300+ }
 301+
 302+ /**
 303+ * Email the user a new password, if appropriate to do so.
 304+ * @param $text String message key
 305+ * @param $title String message key
 306+ * @return Status code
 307+ */
 308+ public function mailPassword( $text='passwordremindertext', $title='passwordremindertitle' ) {
 309+ global $wgUser, $wgOut, $wgAuth, $wgServer, $wgScript, $wgNewPasswordExpiry;
 310+
 311+ if( wfReadOnly() )
 312+ return self::MAIL_READ_ONLY;
 313+
 314+ if( !$wgAuth->allowPasswordChange() )
 315+ return self::MAIL_PASSCHANGE_FORBIDDEN;
 316+
 317+ # Check against blocked IPs
 318+ # FIXME: -- should we not?
 319+ if( $wgUser->isBlocked() )
 320+ return self::MAIL_BLOCKED;
 321+
 322+ # Check for hooks
 323+ $error = null;
 324+ if ( ! wfRunHooks( 'UserLoginMailPassword', array( $this->mName, &$error ) ) )
 325+ return $error;
 326+
 327+ # Check against the rate limiter
 328+ if( $wgUser->pingLimiter( 'mailpassword' ) )
 329+ return self::MAIL_PING_THROTTLED;
 330+
 331+ # Check for a valid name
 332+ if ( '' == $this->mName )
 333+ return self::NO_NAME;
 334+ $this->mUser = User::newFromName( $this->mName );
 335+ if( is_null( $this->mUser ) )
 336+ return self::NO_NAME;
 337+
 338+ # And that the resulting user actually exists
 339+ if ( 0 == $this->mUser->getId() )
 340+ return self::NOT_EXISTS;
 341+
 342+ # Check against password throttle
 343+ if ( $this->mUser->isPasswordReminderThrottled() )
 344+ return self::MAIL_PASS_THROTTLED;
 345+
 346+ # User doesn't have email address set
 347+ if ( '' == $this->mUser->getEmail() )
 348+ return self::MAIL_EMPTY_EMAIL;
 349+
 350+ # Don't send to people who are acting fishily by hiding their IP
 351+ $ip = wfGetIP();
 352+ if( !$ip )
 353+ return self::MAIL_BAD_IP;
 354+
 355+ # Let hooks do things with the data
 356+ wfRunHooks( 'User::mailPasswordInternal', array(&$wgUser, &$ip, &$this->mUser) );
 357+
 358+ $newpass = $this->mUser->randomPassword();
 359+ $this->mUser->setNewpassword( $newpass, true );
 360+ $this->mUser->saveSettings();
 361+
 362+ $message = wfMsgExt( $text, array( 'parsemag' ), $ip, $this->mUser->getName(), $newpass,
 363+ $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) );
 364+ $this->mMailResult = $this->mUser->sendMail( wfMsg( $title ), $message );
 365+
 366+ if( WikiError::isError( $this->mMailResult ) ) {
 367+ var_dump( $message );
 368+ return self::SUCCESS;
 369+ #return self::MAIL_ERROR;
 370+ } else {
 371+ return self::SUCCESS;
 372+ }
 373+ }
 374+}
 375+
 376+/**
 377+ * For backwards compatibility, mainly with the state constants, which
 378+ * could be referred to in old extensions with the old class name.
 379+ * @deprecated
 380+ */
 381+class LoginForm extends Login {}
\ No newline at end of file
Property changes on: trunk/phase3/includes/Login.php
___________________________________________________________________
Added: svn:eol-style
1382 + native
Index: trunk/phase3/includes/specials/SpecialUserlogin.php
@@ -1,90 +1,108 @@
22 <?php
33 /**
4 - * @file
 4+ * SpecialPage for logging users into the wiki
55 * @ingroup SpecialPage
66 */
77
8 -/**
9 - * constructor
10 - */
11 -function wfSpecialUserlogin( $par = '' ) {
12 - global $wgRequest;
13 - if( session_id() == '' ) {
14 - wfSetupSession();
15 - }
 8+class SpecialUserLogin extends SpecialPage {
169
17 - $form = new LoginForm( $wgRequest, $par );
18 - $form->execute();
19 -}
 10+ var $mUsername, $mPassword, $mReturnTo, $mCookieCheck, $mPosted;
 11+ var $mCreateaccount, $mCreateaccountMail, $mMailmypassword;
 12+ var $mRemember, $mDomain, $mLanguage;
 13+ var $mSkipCookieCheck, $mReturnToQuery;
2014
21 -/**
22 - * implements Special:Login
23 - * @ingroup SpecialPage
24 - */
25 -class LoginForm {
 15+ public $mDomains = array();
2616
27 - const SUCCESS = 0;
28 - const NO_NAME = 1;
29 - const ILLEGAL = 2;
30 - const WRONG_PLUGIN_PASS = 3;
31 - const NOT_EXISTS = 4;
32 - const WRONG_PASS = 5;
33 - const EMPTY_PASS = 6;
34 - const RESET_PASS = 7;
35 - const ABORTED = 8;
36 - const CREATE_BLOCKED = 9;
37 - const THROTTLED = 10;
 17+ public $mFormHeader = ''; # Can be filled by hooks etc
 18+ public $mFormFields = array(
 19+ 'Name' => array(
 20+ 'type' => 'text',
 21+ 'label-message' => 'yourname',
 22+ 'id' => 'wpName1',
 23+ 'tabindex' => '1',
 24+ 'size' => '20',
 25+ 'required' => '1',
 26+ ),
 27+ 'Password' => array(
 28+ 'type' => 'password',
 29+ 'label-message' => 'yourpassword',
 30+ 'size' => '20',
 31+ 'id' => 'wpPassword1',
 32+ ),
 33+ 'Domain' => array(
 34+ 'type' => 'select',
 35+ 'id' => 'wpDomain',
 36+ 'label-message' => 'yourdomainname',
 37+ 'options' => null,
 38+ 'default' => null,
 39+ ),
 40+ 'Remember' => array(
 41+ 'type' => 'check',
 42+ 'label-message' => 'remembermypassword',
 43+ 'id' => 'wpRemember',
 44+ )
 45+ );
3846
39 - var $mName, $mPassword, $mRetype, $mReturnTo, $mCookieCheck, $mPosted;
40 - var $mAction, $mCreateaccount, $mCreateaccountMail, $mMailmypassword;
41 - var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage;
42 - var $mSkipCookieCheck, $mReturnToQuery;
 47+ protected $mLogin; # Login object
4348
44 - private $mExtUser = null;
 49+ public function __construct(){
 50+ parent::__construct( 'Userlogin' );
 51+ }
4552
 53+ function execute( $par ) {
 54+ global $wgRequest;
 55+ $this->loadQuery();
 56+ $this->mLogin = new Login();
 57+
 58+ # Redirect out for account creation, for B/C
 59+ $type = ( $par == 'signup' ) ? $par : $wgRequest->getText( 'type' );
 60+ if( $type == 'signup' ){
 61+ $sp = new SpecialCreateAccount();
 62+ $sp->execute( $par );
 63+ return;
 64+ }
 65+
 66+ if ( !is_null( $this->mCookieCheck ) ) {
 67+ $this->onCookieRedirectCheck();
 68+ return;
 69+ } else if( $this->mPosted ) {
 70+ if ( $this->mMailmypassword ) {
 71+ return $this->showMailPage();
 72+ } else {
 73+ return $this->processLogin();
 74+ }
 75+ } else {
 76+ $this->mainLoginForm( '' );
 77+ }
 78+ }
 79+
4680 /**
47 - * Constructor
48 - * @param WebRequest $request A WebRequest object passed by reference
 81+ * Load member variables from the HTTP request data
 82+ * @param $par String the fragment passed to execute()
4983 */
50 - function LoginForm( &$request, $par = '' ) {
51 - global $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
 84+ protected function loadQuery(){
 85+ global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
5286
53 - $this->mType = ( $par == 'signup' ) ? $par : $request->getText( 'type' ); # Check for [[Special:Userlogin/signup]]
54 - $this->mName = $request->getText( 'wpName' );
55 - $this->mPassword = $request->getText( 'wpPassword' );
56 - $this->mRetype = $request->getText( 'wpRetype' );
57 - $this->mDomain = $request->getText( 'wpDomain' );
58 - $this->mReturnTo = $request->getVal( 'returnto' );
59 - $this->mReturnToQuery = $request->getVal( 'returntoquery' );
60 - $this->mCookieCheck = $request->getVal( 'wpCookieCheck' );
61 - $this->mPosted = $request->wasPosted();
62 - $this->mCreateaccount = $request->getCheck( 'wpCreateaccount' );
63 - $this->mCreateaccountMail = $request->getCheck( 'wpCreateaccountMail' )
64 - && $wgEnableEmail;
65 - $this->mMailmypassword = $request->getCheck( 'wpMailmypassword' )
 87+ $this->mUsername = $wgRequest->getText( 'wpName' );
 88+ $this->mPassword = $wgRequest->getText( 'wpPassword' );
 89+ $this->mDomain = $wgRequest->getText( 'wpDomain' );
 90+ $this->mLanguage = $wgRequest->getText( 'uselang' );
 91+
 92+ $this->mReturnTo = $wgRequest->getVal( 'returnto' );
 93+ $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
 94+ $this->mCookieCheck = $wgRequest->getVal( 'wpCookieCheck' );
 95+
 96+ $this->mMailmypassword = $wgRequest->getCheck( 'wpMailmypassword' )
6697 && $wgEnableEmail;
67 - $this->mLoginattempt = $request->getCheck( 'wpLoginattempt' );
68 - $this->mAction = $request->getVal( 'action' );
69 - $this->mRemember = $request->getCheck( 'wpRemember' );
70 - $this->mLanguage = $request->getText( 'uselang' );
71 - $this->mSkipCookieCheck = $request->getCheck( 'wpSkipCookieCheck' );
 98+ $this->mRemember = $wgRequest->getCheck( 'wpRemember' );
 99+ $this->mSkipCookieCheck = $wgRequest->getCheck( 'wpSkipCookieCheck' );
 100+ $this->mPosted = $wgRequest->wasPosted();
72101
73102 if ( $wgRedirectOnLogin ) {
74103 $this->mReturnTo = $wgRedirectOnLogin;
75104 $this->mReturnToQuery = '';
76105 }
77106
78 - if( $wgEnableEmail ) {
79 - $this->mEmail = $request->getText( 'wpEmail' );
80 - } else {
81 - $this->mEmail = '';
82 - }
83 - if( !in_array( 'realname', $wgHiddenPrefs ) ) {
84 - $this->mRealName = $request->getText( 'wpRealName' );
85 - } else {
86 - $this->mRealName = '';
87 - }
88 -
89107 if( !$wgAuth->validDomain( $this->mDomain ) ) {
90108 $this->mDomain = 'invaliddomain';
91109 }
@@ -98,519 +116,349 @@
99117 }
100118 }
101119
102 - function execute() {
103 - if ( !is_null( $this->mCookieCheck ) ) {
104 - $this->onCookieRedirectCheck( $this->mCookieCheck );
105 - return;
106 - } else if( $this->mPosted ) {
107 - if( $this->mCreateaccount ) {
108 - return $this->addNewAccount();
109 - } else if ( $this->mCreateaccountMail ) {
110 - return $this->addNewAccountMailPassword();
111 - } else if ( $this->mMailmypassword ) {
112 - return $this->mailPassword();
113 - } else if ( ( 'submitlogin' == $this->mAction ) || $this->mLoginattempt ) {
114 - return $this->processLogin();
115 - }
116 - }
117 - $this->mainLoginForm( '' );
118 - }
119 -
120120 /**
121 - * @private
 121+ * Show the main login form
 122+ * @param $msg String a message key for a warning/error message
 123+ * that may have been generated on a previous iteration
122124 */
123 - function addNewAccountMailPassword() {
124 - global $wgOut;
 125+ protected function mainLoginForm( $msg, $msgtype = 'error' ) {
 126+ global $wgUser, $wgOut, $wgEnableEmail;
 127+ global $wgCookiePrefix, $wgLoginLanguageSelector;
 128+ global $wgAuth, $wgCookieExpiration;
125129
126 - if ('' == $this->mEmail) {
127 - $this->mainLoginForm( wfMsg( 'noemail', htmlspecialchars( $this->mName ) ) );
128 - return;
 130+ # Preload the name field with something if we can
 131+ if ( '' == $this->mUsername ) {
 132+ if ( $wgUser->isLoggedIn() ) {
 133+ $this->mUsername = $wgUser->getName();
 134+ } elseif( isset( $_COOKIE[$wgCookiePrefix.'UserName'] ) ) {
 135+ $this->mUsername = $_COOKIE[$wgCookiePrefix.'UserName'];
 136+ }
129137 }
130 -
131 - $u = $this->addNewaccountInternal();
132 -
133 - if ($u == NULL) {
134 - return;
135 - }
136 -
137 - // Wipe the initial password and mail a temporary one
138 - $u->setPassword( null );
139 - $u->saveSettings();
140 - $result = $this->mailPasswordInternal( $u, false, 'createaccount-title', 'createaccount-text' );
141 -
142 - wfRunHooks( 'AddNewAccount', array( $u, true ) );
143 - $u->addNewUserLogEntry();
144 -
145 - $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
146 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
147 - $wgOut->setArticleRelated( false );
148 -
149 - if( WikiError::isError( $result ) ) {
150 - $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) );
 138+ if( $this->mUsername ){
 139+ $this->mFormFields['Name']['default'] = $this->mUsername;
 140+ $this->mFormFields['Password']['autofocus'] = '1';
151141 } else {
152 - $wgOut->addWikiMsg( 'accmailtext', $u->getName(), $u->getEmail() );
153 - $wgOut->returnToMain( false );
 142+ $this->mFormFields['Name']['autofocus'] = '1';
154143 }
155 - $u = 0;
156 - }
157144
158 -
159 - /**
160 - * @private
161 - */
162 - function addNewAccount() {
163 - global $wgUser, $wgEmailAuthentication;
164 -
165 - # Create the account and abort if there's a problem doing so
166 - $u = $this->addNewAccountInternal();
167 - if( $u == NULL )
168 - return;
169 -
170 - # If we showed up language selection links, and one was in use, be
171 - # smart (and sensible) and save that language as the user's preference
172 - global $wgLoginLanguageSelector;
173 - if( $wgLoginLanguageSelector && $this->mLanguage )
174 - $u->setOption( 'language', $this->mLanguage );
175 -
176 - # Send out an email authentication message if needed
177 - if( $wgEmailAuthentication && User::isValidEmailAddr( $u->getEmail() ) ) {
178 - global $wgOut;
179 - $error = $u->sendConfirmationMail();
180 - if( WikiError::isError( $error ) ) {
181 - $wgOut->addWikiMsg( 'confirmemail_sendfailed', $error->getMessage() );
182 - } else {
183 - $wgOut->addWikiMsg( 'confirmemail_oncreate' );
 145+ # Parse the error message if we got one
 146+ if( $msg ){
 147+ if( $msgtype == 'error' ){
 148+ $msg = wfMsg( 'loginerror' ) . ' ' . $msg;
184149 }
185 - }
186 -
187 - # Save settings (including confirmation token)
188 - $u->saveSettings();
189 -
190 - # If not logged in, assume the new account as the current one and set
191 - # session cookies then show a "welcome" message or a "need cookies"
192 - # message as needed
193 - if( $wgUser->isAnon() ) {
194 - $wgUser = $u;
195 - $wgUser->setCookies();
196 - wfRunHooks( 'AddNewAccount', array( $wgUser ) );
197 - $wgUser->addNewUserLogEntry();
198 - if( $this->hasSessionCookie() ) {
199 - return $this->successfulCreation();
200 - } else {
201 - return $this->cookieRedirectCheck( 'new' );
202 - }
 150+ $msg = Html::rawElement(
 151+ 'div',
 152+ array( 'class' => $msgtype . 'box' ),
 153+ $msg
 154+ );
203155 } else {
204 - # Confirm that the account was created
205 - global $wgOut;
206 - $self = SpecialPage::getTitleFor( 'Userlogin' );
207 - $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
208 - $wgOut->setArticleRelated( false );
209 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
210 - $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $u->getName() ) );
211 - $wgOut->returnToMain( false, $self );
212 - wfRunHooks( 'AddNewAccount', array( $u ) );
213 - $u->addNewUserLogEntry();
214 - return true;
 156+ $msg = '';
215157 }
216 - }
217158
218 - /**
219 - * @private
220 - */
221 - function addNewAccountInternal() {
222 - global $wgUser, $wgOut;
223 - global $wgEnableSorbs, $wgProxyWhitelist;
224 - global $wgMemc, $wgAccountCreationThrottle;
225 - global $wgAuth, $wgMinimalPasswordLength;
226 - global $wgEmailConfirmToEdit;
227 -
228 - // If the user passes an invalid domain, something is fishy
229 - if( !$wgAuth->validDomain( $this->mDomain ) ) {
230 - $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
231 - return false;
 159+ # Make sure the returnTo strings don't get lost if the
 160+ # user changes language, etc
 161+ $linkq = array();
 162+ if ( !empty( $this->mReturnTo ) ) {
 163+ $linkq['returnto'] = wfUrlencode( $this->mReturnTo );
 164+ if ( !empty( $this->mReturnToQuery ) )
 165+ $linkq['returntoquery'] = wfUrlencode( $this->mReturnToQuery );
232166 }
233167
234 - // If we are not allowing users to login locally, we should be checking
235 - // to see if the user is actually able to authenticate to the authenti-
236 - // cation server before they create an account (otherwise, they can
237 - // create a local account and login as any domain user). We only need
238 - // to check this for domains that aren't local.
239 - if( 'local' != $this->mDomain && '' != $this->mDomain ) {
240 - if( !$wgAuth->canCreateAccounts() && ( !$wgAuth->userExists( $this->mName ) || !$wgAuth->authenticate( $this->mName, $this->mPassword ) ) ) {
241 - $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
242 - return false;
243 - }
244 - }
 168+ # Pass any language selection on to the mode switch link
 169+ if( $wgLoginLanguageSelector && $this->mLanguage )
 170+ $linkq['uselang'] = $this->mLanguage;
245171
246 - if ( wfReadOnly() ) {
247 - $wgOut->readOnlyPage();
248 - return false;
249 - }
 172+ $skin = $wgUser->getSkin();
 173+ $link = $skin->link(
 174+ SpecialPage::getTitleFor( 'CreateAccount' ),
 175+ wfMsgHtml( 'nologinlink' ),
 176+ array(),
 177+ $linkq );
250178
251 - # Check permissions
252 - if ( !$wgUser->isAllowed( 'createaccount' ) ) {
253 - $this->userNotPrivilegedMessage();
254 - return false;
255 - } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
256 - $this->userBlockedMessage();
257 - return false;
258 - }
 179+ # Don't show a "create account" link if the user can't
 180+ $link = $wgUser->isAllowed( 'createaccount' ) && !$wgUser->isLoggedIn()
 181+ ? wfMsgWikiHtml( 'nologin', $link )
 182+ : '';
259183
260 - $ip = wfGetIP();
261 - if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
262 - $wgUser->inSorbsBlacklist( $ip ) )
263 - {
264 - $this->mainLoginForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . htmlspecialchars( $ip ) . ')' );
265 - return;
 184+ # Prepare language selection links as needed
 185+ $langSelector = $wgLoginLanguageSelector
 186+ ? Html::rawElement(
 187+ 'div',
 188+ array( 'id' => 'languagelinks' ),
 189+ self::makeLanguageSelector( $this->getTitle(), $this->mReturnTo ) )
 190+ : '';
 191+
 192+ # Add a 'mail reset' button if available
 193+ $buttons = '';
 194+ if( $wgEnableEmail && $wgAuth->allowPasswordChange() ){
 195+ $buttons = Html::element(
 196+ 'input',
 197+ array(
 198+ 'type' => 'submit',
 199+ 'name' => 'wpMailmypassword',
 200+ 'value' => wfMsg( 'mailmypassword' ),
 201+ 'id' => 'wpMailmypassword',
 202+ )
 203+ );
266204 }
267205
268 - # Now create a dummy user ($u) and check if it is valid
269 - $name = trim( $this->mName );
270 - $u = User::newFromName( $name, 'creatable' );
271 - if ( is_null( $u ) ) {
272 - $this->mainLoginForm( wfMsg( 'noname' ) );
273 - return false;
 206+ # Give authentication and captcha plugins a chance to
 207+ # modify the form, by hook or by using $wgAuth
 208+ $wgAuth->modifyUITemplate( $this, 'login' );
 209+ wfRunHooks( 'UserLoginForm', array( &$this ) );
 210+
 211+ # The most likely use of the hook is to enable domains;
 212+ # check that now, and add fields if necessary
 213+ if( $this->mDomains ){
 214+ $this->mFormFields['Domain']['options'] = $this->mDomains;
 215+ $this->mFormFields['Domain']['default'] = $this->mDomain;
 216+ } else {
 217+ unset( $this->mFormFields['Domain'] );
274218 }
275 -
276 - if ( 0 != $u->idForName() ) {
277 - $this->mainLoginForm( wfMsg( 'userexists' ) );
278 - return false;
 219+
 220+ # Or to tweak the 'remember my password' checkbox
 221+ if( !($wgCookieExpiration > 0) ){
 222+ # Remove it altogether
 223+ unset( $this->mFormFields['Remember'] );
 224+ } elseif( $wgUser->getOption( 'rememberpassword' ) || $this->mRemember ){
 225+ # Or check it by default
 226+ # FIXME: this doesn't always work?
 227+ $this->mFormFields['Remember']['checked'] = '1';
279228 }
 229+
 230+ $form = new HTMLForm( $this->mFormFields, '' );
 231+ $form->setTitle( $this->getTitle() );
 232+ $form->setSubmitText( wfMsg( 'login' ) );
 233+ $form->setSubmitId( 'wpLoginAttempt' );
 234+ $form->suppressReset();
 235+ $form->loadData();
 236+
 237+ $formContents = ''
 238+ . Html::rawElement( 'p', array( 'id' => 'userloginlink' ),
 239+ $link )
 240+ . Html::rawElement( 'div', array( 'id' => 'userloginprompt' ),
 241+ wfMsgExt( 'loginprompt', array( 'parseinline' ) ) )
 242+ . $this->mFormHeader
 243+ . $langSelector
 244+ . $form->getBody()
 245+ . $form->getButtons()
 246+ . $buttons
 247+ . Xml::hidden( 'returnto', $this->mReturnTo )
 248+ . Xml::hidden( 'returntoquery', $this->mReturnToQuery )
 249+ ;
280250
281 - if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
282 - $this->mainLoginForm( wfMsg( 'badretype' ) );
283 - return false;
284 - }
 251+ $wgOut->setPageTitle( wfMsg( 'login' ) );
 252+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 253+ $wgOut->setArticleRelated( false );
 254+ $wgOut->disallowUserJs(); # Stop malicious userscripts sniffing passwords
285255
286 - # check for minimal password length
287 - $valid = $u->isValidPassword( $this->mPassword );
288 - if ( $valid !== true ) {
289 - if ( !$this->mCreateaccountMail ) {
290 - $this->mainLoginForm( wfMsgExt( $valid, array( 'parsemag' ), $wgMinimalPasswordLength ) );
291 - return false;
292 - } else {
293 - # do not force a password for account creation by email
294 - # set invalid password, it will be replaced later by a random generated password
295 - $this->mPassword = null;
296 - }
297 - }
 256+ $wgOut->addHTML(
 257+ Html::rawElement(
 258+ 'div',
 259+ array( 'id' => 'loginstart' ),
 260+ wfMsgExt( 'loginstart', array( 'parseinline' ) )
 261+ ) .
 262+ $msg .
 263+ Html::rawElement(
 264+ 'div',
 265+ array( 'id' => 'userloginForm' ),
 266+ $form->wrapForm( $formContents )
 267+ ) .
 268+ Html::rawElement(
 269+ 'div',
 270+ array( 'id' => 'loginend' ),
 271+ wfMsgExt( 'loginend', array( 'parseinline' ) )
 272+ )
 273+ );
298274
299 - # if you need a confirmed email address to edit, then obviously you
300 - # need an email address.
301 - if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) ) {
302 - $this->mainLoginForm( wfMsg( 'noemailtitle' ) );
303 - return false;
304 - }
 275+ }
305276
306 - if( !empty( $this->mEmail ) && !User::isValidEmailAddr( $this->mEmail ) ) {
307 - $this->mainLoginForm( wfMsg( 'invalidemailaddress' ) );
308 - return false;
309 - }
310 -
311 - # Set some additional data so the AbortNewAccount hook can be used for
312 - # more than just username validation
313 - $u->setEmail( $this->mEmail );
314 - $u->setRealName( $this->mRealName );
315 -
316 - $abortError = '';
317 - if( !wfRunHooks( 'AbortNewAccount', array( $u, &$abortError ) ) ) {
318 - // Hook point to add extra creation throttles and blocks
319 - wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
320 - $this->mainLoginForm( $abortError );
321 - return false;
322 - }
323 -
324 - if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
325 - $key = wfMemcKey( 'acctcreate', 'ip', $ip );
326 - $value = $wgMemc->get( $key );
327 - if ( !$value ) {
328 - $wgMemc->set( $key, 0, 86400 );
329 - }
330 - if ( $value >= $wgAccountCreationThrottle ) {
331 - $this->throttleHit( $wgAccountCreationThrottle );
332 - return false;
333 - }
334 - $wgMemc->incr( $key );
335 - }
336 -
337 - if( !$wgAuth->addUser( $u, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
338 - $this->mainLoginForm( wfMsg( 'externaldberror' ) );
339 - return false;
340 - }
341 -
342 - return $this->initUser( $u, false );
343 - }
344 -
345277 /**
346 - * Actually add a user to the database.
347 - * Give it a User object that has been initialised with a name.
 278+ * Check if a session cookie is present.
348279 *
349 - * @param $u User object.
350 - * @param $autocreate boolean -- true if this is an autocreation via auth plugin
351 - * @return User object.
 280+ * This will not pick up a cookie set during _this_ request, but is meant
 281+ * to ensure that the client is returning the cookie which was set on a
 282+ * previous pass through the system.
 283+ *
352284 * @private
353285 */
354 - function initUser( $u, $autocreate ) {
355 - global $wgAuth;
 286+ protected function hasSessionCookie() {
 287+ global $wgDisableCookieCheck, $wgRequest;
 288+ return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
 289+ }
356290
357 - $u->addToDatabase();
 291+ /**
 292+ * Do a redirect back to the same page, so we can check any
 293+ * new session cookies.
 294+ */
 295+ protected function cookieRedirectCheck() {
 296+ global $wgOut;
358297
359 - if ( $wgAuth->allowPasswordChange() ) {
360 - $u->setPassword( $this->mPassword );
361 - }
 298+ $query = array( 'wpCookieCheck' => '1');
 299+ if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo;
 300+ $check = $this->getTitle()->getFullURL( $query );
362301
363 - $u->setEmail( $this->mEmail );
364 - $u->setRealName( $this->mRealName );
365 - $u->setToken();
 302+ return $wgOut->redirect( $check );
 303+ }
366304
367 - $wgAuth->initUser( $u, $autocreate );
368 -
369 - if ( $this->mExtUser ) {
370 - $this->mExtUser->link( $u->getId() );
371 - $email = $this->mExtUser->getPref( 'emailaddress' );
372 - if ( $email && !$this->mEmail ) {
373 - $u->setEmail( $email );
374 - }
 305+ /**
 306+ * Check the cookies and show errors if they're not enabled.
 307+ * @param $type String action being performed
 308+ */
 309+ protected function onCookieRedirectCheck() {
 310+ if ( !$this->hasSessionCookie() ) {
 311+ return $this->mainLoginForm( wfMsgExt( 'nocookieslogin', array( 'parseinline' ) ) );
 312+ } else {
 313+ return self::successfulLogin( 'loginsuccess', $this->mReturnTo, $this->mReturnToQuery );
375314 }
376 -
377 - $u->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
378 - $u->saveSettings();
379 -
380 - # Update user count
381 - $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
382 - $ssUpdate->doUpdate();
383 -
384 - return $u;
385315 }
386316
387317 /**
388 - * Internally authenticate the login request.
389 - *
390 - * This may create a local account as a side effect if the
391 - * authentication plugin allows transparent local account
392 - * creation.
393 - *
394 - * @public
 318+ * Produce a bar of links which allow the user to select another language
 319+ * during login/registration but retain "returnto"
 320+ * @param $title Title to use in the link
 321+ * @param $returnTo query string to append
 322+ * @return String HTML for bar
395323 */
396 - function authenticateUserData() {
397 - global $wgUser, $wgAuth;
398 - if ( '' == $this->mName ) {
399 - return self::NO_NAME;
400 - }
401 -
402 - global $wgPasswordAttemptThrottle;
 324+ public static function makeLanguageSelector( $title, $returnTo=false ) {
 325+ global $wgLang;
403326
404 - $throttleCount = 0;
405 - if ( is_array( $wgPasswordAttemptThrottle ) ) {
406 - $throttleKey = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
407 - $count = $wgPasswordAttemptThrottle['count'];
408 - $period = $wgPasswordAttemptThrottle['seconds'];
409 -
410 - global $wgMemc;
411 - $throttleCount = $wgMemc->get( $throttleKey );
412 - if ( !$throttleCount ) {
413 - $wgMemc->add( $throttleKey, 1, $period ); // start counter
414 - } else if ( $throttleCount < $count ) {
415 - $wgMemc->incr($throttleKey);
416 - } else if ( $throttleCount >= $count ) {
417 - return self::THROTTLED;
 327+ $msg = wfMsgForContent( 'loginlanguagelinks' );
 328+ if( $msg != '' && !wfEmptyMsg( 'loginlanguagelinks', $msg ) ) {
 329+ $langs = explode( "\n", $msg );
 330+ $links = array();
 331+ foreach( $langs as $lang ) {
 332+ $lang = trim( $lang, '* ' );
 333+ $parts = explode( '|', $lang );
 334+ if (count($parts) >= 2) {
 335+ $links[] = SpecialUserLogin::makeLanguageSelectorLink(
 336+ $parts[0], $parts[1], $title, $returnTo );
 337+ }
418338 }
 339+ return count( $links ) > 0 ? wfMsgHtml( 'loginlanguagelabel', $wgLang->pipeList( $links ) ) : '';
 340+ } else {
 341+ return '';
419342 }
 343+ }
420344
421 - // Load $wgUser now, and check to see if we're logging in as the same
422 - // name. This is necessary because loading $wgUser (say by calling
423 - // getName()) calls the UserLoadFromSession hook, which potentially
424 - // creates the user in the database. Until we load $wgUser, checking
425 - // for user existence using User::newFromName($name)->getId() below
426 - // will effectively be using stale data.
427 - if ( $wgUser->getName() === $this->mName ) {
428 - wfDebug( __METHOD__.": already logged in as {$this->mName}\n" );
429 - return self::SUCCESS;
430 - }
 345+ /**
 346+ * Create a language selector link for a particular language
 347+ * Links back to this page preserving type and returnto
 348+ * @param $text Link text
 349+ * @param $lang Language code
 350+ * @param $title Title to link to
 351+ * @param $returnTo String returnto query
 352+ */
 353+ public static function makeLanguageSelectorLink( $text, $lang, $title, $returnTo=false ) {
 354+ global $wgUser;
 355+ $attr = array( 'uselang' => $lang );
 356+ if( $returnTo )
 357+ $attr['returnto'] = $returnTo;
 358+ $skin = $wgUser->getSkin();
 359+ return $skin->linkKnown(
 360+ $title,
 361+ htmlspecialchars( $text ),
 362+ array(),
 363+ $attr
 364+ );
 365+ }
431366
432 - $this->mExtUser = ExternalUser::newFromName( $this->mName );
 367+ /**
 368+ * Display a "login successful" page.
 369+ * @param $msgname String message key to display
 370+ * @param $html String HTML to optionally add
 371+ * @param $returnto Title to returnto
 372+ * @param $returntoQuery String query string for returnto link
 373+ */
 374+ public static function displaySuccessfulLogin( $msgname, $injected_html='', $returnto=false, $returntoQuery=false ) {
 375+ global $wgOut, $wgUser;
433376
434 - # TODO: Allow some magic here for invalid external names, e.g., let the
435 - # user choose a different wiki name.
436 - $u = User::newFromName( $this->mName );
437 - if( is_null( $u ) || !User::isUsableName( $u->getName() ) ) {
438 - return self::ILLEGAL;
439 - }
 377+ $wgOut->setPageTitle( wfMsg( 'loginsuccesstitle' ) );
 378+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 379+ $wgOut->setArticleRelated( false );
 380+ $wgOut->addWikiMsg( $msgname, $wgUser->getName() );
 381+ $wgOut->addHTML( $injected_html );
440382
441 - $isAutoCreated = false;
442 - if ( 0 == $u->getID() ) {
443 - $status = $this->attemptAutoCreate( $u );
444 - if ( $status !== self::SUCCESS ) {
445 - return $status;
446 - } else {
447 - $isAutoCreated = true;
448 - }
 383+ if ( $returnto ) {
 384+ $wgOut->returnToMain( null, $returnto, $this->mReturnToQuery );
449385 } else {
450 - $u->load();
 386+ $wgOut->returnToMain( null );
451387 }
452 -
453 - // Give general extensions, such as a captcha, a chance to abort logins
454 - $abort = self::ABORTED;
455 - if( !wfRunHooks( 'AbortLogin', array( $u, $this->mPassword, &$abort ) ) ) {
456 - return $abort;
457 - }
458 -
459 - if (!$u->checkPassword( $this->mPassword )) {
460 - if( $u->checkTemporaryPassword( $this->mPassword ) ) {
461 - // The e-mailed temporary password should not be used for actu-
462 - // al logins; that's a very sloppy habit, and insecure if an
463 - // attacker has a few seconds to click "search" on someone's o-
464 - // pen mail reader.
465 - //
466 - // Allow it to be used only to reset the password a single time
467 - // to a new value, which won't be in the user's e-mail ar-
468 - // chives.
469 - //
470 - // For backwards compatibility, we'll still recognize it at the
471 - // login form to minimize surprises for people who have been
472 - // logging in with a temporary password for some time.
473 - //
474 - // As a side-effect, we can authenticate the user's e-mail ad-
475 - // dress if it's not already done, since the temporary password
476 - // was sent via e-mail.
477 - if( !$u->isEmailConfirmed() ) {
478 - $u->confirmEmail();
479 - $u->saveSettings();
480 - }
481 -
482 - // At this point we just return an appropriate code/ indicating
483 - // that the UI should show a password reset form; bot inter-
484 - // faces etc will probably just fail cleanly here.
485 - $retval = self::RESET_PASS;
486 - } else {
487 - $retval = '' == $this->mPassword ? self::EMPTY_PASS : self::WRONG_PASS;
488 - }
489 - } else {
490 - $wgAuth->updateUser( $u );
491 - $wgUser = $u;
492 -
493 - // Please reset throttle for successful logins, thanks!
494 - if($throttleCount) {
495 - $wgMemc->delete($throttleKey);
496 - }
497 -
498 - if ( $isAutoCreated ) {
499 - // Must be run after $wgUser is set, for correct new user log
500 - wfRunHooks( 'AuthPluginAutoCreate', array( $wgUser ) );
501 - }
502 -
503 - $retval = self::SUCCESS;
504 - }
505 - wfRunHooks( 'LoginAuthenticateAudit', array( $u, $this->mPassword, $retval ) );
506 - return $retval;
507388 }
508389
509390 /**
510 - * Attempt to automatically create a user on login. Only succeeds if there
511 - * is an external authentication method which allows it.
512 - * @return integer Status code
 391+ * Run any hooks registered for logins, then HTTP redirect to
 392+ * $this->mReturnTo (or Main Page if that's undefined). Formerly we had a
 393+ * nice message here, but that's really not as useful as just being sent to
 394+ * wherever you logged in from. It should be clear that the action was
 395+ * successful, given the lack of error messages plus the appearance of your
 396+ * name in the upper right.
513397 */
514 - function attemptAutoCreate( $user ) {
515 - global $wgAuth, $wgUser, $wgAutocreatePolicy;
 398+ public static function successfulLogin( $message, $returnTo='', $returnToQuery='' ) {
 399+ global $wgUser, $wgOut;
516400
517 - if ( $wgUser->isBlockedFromCreateAccount() ) {
518 - wfDebug( __METHOD__.": user is blocked from account creation\n" );
519 - return self::CREATE_BLOCKED;
520 - }
 401+ # Run any hooks; display injected HTML if any, else redirect
 402+ $injected_html = '';
 403+ wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
521404
522 - /**
523 - * If the external authentication plugin allows it, automatically cre-
524 - * ate a new account for users that are externally defined but have not
525 - * yet logged in.
526 - */
527 - if ( $this->mExtUser ) {
528 - # mExtUser is neither null nor false, so use the new ExternalAuth
529 - # system.
530 - if ( $wgAutocreatePolicy == 'never' ) {
531 - return self::NOT_EXISTS;
532 - }
533 - if ( !$this->mExtUser->authenticate( $this->mPassword ) ) {
534 - return self::WRONG_PLUGIN_PASS;
535 - }
 405+ if( $injected_html !== '' ) {
 406+ SpecialUserLogin::displaySuccessfulLogin( $message, $injected_html );
536407 } else {
537 - # Old AuthPlugin.
538 - if ( !$wgAuth->autoCreate() ) {
539 - return self::NOT_EXISTS;
 408+ $titleObj = Title::newFromText( $returnTo );
 409+ if ( !$titleObj instanceof Title ) {
 410+ $titleObj = Title::newMainPage();
540411 }
541 - if ( !$wgAuth->userExists( $user->getName() ) ) {
542 - wfDebug( __METHOD__.": user does not exist\n" );
543 - return self::NOT_EXISTS;
544 - }
545 - if ( !$wgAuth->authenticate( $user->getName(), $this->mPassword ) ) {
546 - wfDebug( __METHOD__.": \$wgAuth->authenticate() returned false, aborting\n" );
547 - return self::WRONG_PLUGIN_PASS;
548 - }
 412+ $wgOut->redirect( $titleObj->getFullURL( $returnToQuery ) );
549413 }
550 -
551 - wfDebug( __METHOD__.": creating account\n" );
552 - $user = $this->initUser( $user, true );
553 - return self::SUCCESS;
554414 }
 415+
555416
556 - function processLogin() {
 417+ protected function processLogin(){
557418 global $wgUser, $wgAuth;
558 -
559 - switch ( $this->authenticateUserData() ) {
560 - case self::SUCCESS:
561 - # We've verified now, update the real record
562 - if( (bool)$this->mRemember != (bool)$wgUser->getOption( 'rememberpassword' ) ) {
563 - $wgUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
564 - $wgUser->saveSettings();
565 - } else {
566 - $wgUser->invalidateCache();
567 - }
568 - $wgUser->setCookies();
569 -
570 - // Reset the throttle
571 - $key = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
572 - global $wgMemc;
573 - $wgMemc->delete( $key );
574 -
 419+ $result = $this->mLogin->login();
 420+ switch ( $result ) {
 421+ case Login::SUCCESS:
575422 if( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
576 - /* Replace the language object to provide user interface in
577 - * correct language immediately on this first page load.
578 - */
 423+ # Replace the language object to provide user interface in
 424+ # correct language immediately on this first page load.
579425 global $wgLang, $wgRequest;
580426 $code = $wgRequest->getVal( 'uselang', $wgUser->getOption( 'language' ) );
581427 $wgLang = Language::factory( $code );
582 - return $this->successfulLogin();
 428+ return self::successfulLogin( 'loginsuccess', $this->mReturnTo, $this->mReturnToQuery );
583429 } else {
584 - return $this->cookieRedirectCheck( 'login' );
 430+ # Do a redirect check to ensure that the cookies are
 431+ # being retained by the user's browser.
 432+ return $this->cookieRedirectCheck();
585433 }
586434 break;
587435
588 - case self::NO_NAME:
589 - case self::ILLEGAL:
 436+ case Login::NO_NAME:
 437+ case Login::ILLEGAL:
590438 $this->mainLoginForm( wfMsg( 'noname' ) );
591439 break;
592 - case self::WRONG_PLUGIN_PASS:
 440+ case Login::WRONG_PLUGIN_PASS:
593441 $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
594442 break;
595 - case self::NOT_EXISTS:
 443+ case Login::NOT_EXISTS:
596444 if( $wgUser->isAllowed( 'createaccount' ) ){
597445 $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $this->mName ) ) );
598446 } else {
599447 $this->mainLoginForm( wfMsg( 'nosuchusershort', htmlspecialchars( $this->mName ) ) );
600448 }
601449 break;
602 - case self::WRONG_PASS:
 450+ case Login::WRONG_PASS:
603451 $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
604452 break;
605 - case self::EMPTY_PASS:
 453+ case Login::EMPTY_PASS:
606454 $this->mainLoginForm( wfMsg( 'wrongpasswordempty' ) );
607455 break;
608 - case self::RESET_PASS:
 456+ case Login::RESET_PASS:
609457 $this->resetLoginForm( wfMsg( 'resetpass_announce' ) );
610458 break;
611 - case self::CREATE_BLOCKED:
 459+ case Login::CREATE_BLOCKED:
612460 $this->userBlockedMessage();
613461 break;
614 - case self::THROTTLED:
 462+ case Login::THROTTLED:
615463 $this->mainLoginForm( wfMsg( 'login-throttled' ) );
616464 break;
617465 default:
@@ -618,6 +466,11 @@
619467 }
620468 }
621469
 470+ /**
 471+ * 'Shell out' to Special:ResetPass to get the user to
 472+ * set a new permanent password from a temporary one.
 473+ * @param $error String message
 474+ */
622475 function resetLoginForm( $error ) {
623476 global $wgOut;
624477 $wgOut->addHTML( Xml::element('p', array( 'class' => 'error' ), $error ) );
@@ -626,438 +479,67 @@
627480 }
628481
629482 /**
630 - * @private
 483+ * Attempt to send the user a password-reset mail, and display
 484+ * the results (good, bad or ugly).
 485+ * @return unknown_type
631486 */
632 - function mailPassword() {
633 - global $wgUser, $wgOut, $wgAuth;
634 -
635 - if ( wfReadOnly() ) {
636 - $wgOut->readOnlyPage();
637 - return false;
638 - }
639 -
640 - if( !$wgAuth->allowPasswordChange() ) {
641 - $this->mainLoginForm( wfMsg( 'resetpass_forbidden' ) );
642 - return;
643 - }
644 -
645 - # Check against blocked IPs
646 - # fixme -- should we not?
647 - if( $wgUser->isBlocked() ) {
648 - $this->mainLoginForm( wfMsg( 'blocked-mailpassword' ) );
649 - return;
650 - }
651 -
652 - // Check for hooks
653 - $error = null;
654 - if ( ! wfRunHooks( 'UserLoginMailPassword', array( $this->mName, &$error ) ) ) {
655 - $this->mainLoginForm( $error );
656 - return;
657 - }
658 -
659 - # Check against the rate limiter
660 - if( $wgUser->pingLimiter( 'mailpassword' ) ) {
661 - $wgOut->rateLimited();
662 - return;
663 - }
664 -
665 - if ( '' == $this->mName ) {
666 - $this->mainLoginForm( wfMsg( 'noname' ) );
667 - return;
668 - }
669 - $u = User::newFromName( $this->mName );
670 - if( is_null( $u ) ) {
671 - $this->mainLoginForm( wfMsg( 'noname' ) );
672 - return;
673 - }
674 - if ( 0 == $u->getID() ) {
675 - $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $u->getName() ) ) );
676 - return;
677 - }
678 -
679 - # Check against password throttle
680 - if ( $u->isPasswordReminderThrottled() ) {
681 - global $wgPasswordReminderResendTime;
682 - # Round the time in hours to 3 d.p., in case someone is specifying
683 - # minutes or seconds.
684 - $this->mainLoginForm( wfMsgExt( 'throttled-mailpassword', array( 'parsemag' ),
685 - round( $wgPasswordReminderResendTime, 3 ) ) );
686 - return;
687 - }
688 -
689 - $result = $this->mailPasswordInternal( $u, true, 'passwordremindertitle', 'passwordremindertext' );
690 - if( WikiError::isError( $result ) ) {
691 - $this->mainLoginForm( wfMsg( 'mailerror', $result->getMessage() ) );
692 - } else {
693 - $this->mainLoginForm( wfMsg( 'passwordsent', $u->getName() ), 'success' );
694 - }
695 - }
696 -
697 -
698 - /**
699 - * @param object user
700 - * @param bool throttle
701 - * @param string message name of email title
702 - * @param string message name of email text
703 - * @return mixed true on success, WikiError on failure
704 - * @private
705 - */
706 - function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle', $emailText = 'passwordremindertext' ) {
707 - global $wgServer, $wgScript, $wgUser, $wgNewPasswordExpiry;
708 -
709 - if ( '' == $u->getEmail() ) {
710 - return new WikiError( wfMsg( 'noemail', $u->getName() ) );
711 - }
712 - $ip = wfGetIP();
713 - if( !$ip ) {
714 - return new WikiError( wfMsg( 'badipaddress' ) );
715 - }
716 -
717 - wfRunHooks( 'User::mailPasswordInternal', array(&$wgUser, &$ip, &$u) );
718 -
719 - $np = $u->randomPassword();
720 - $u->setNewpassword( $np, $throttle );
721 - $u->saveSettings();
722 -
723 - $m = wfMsgExt( $emailText, array( 'parsemag' ), $ip, $u->getName(), $np,
724 - $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) );
725 - $result = $u->sendMail( wfMsg( $emailTitle ), $m );
726 -
727 - return $result;
728 - }
729 -
730 -
731 - /**
732 - * Run any hooks registered for logins, then HTTP redirect to
733 - * $this->mReturnTo (or Main Page if that's undefined). Formerly we had a
734 - * nice message here, but that's really not as useful as just being sent to
735 - * wherever you logged in from. It should be clear that the action was
736 - * successful, given the lack of error messages plus the appearance of your
737 - * name in the upper right.
738 - *
739 - * @private
740 - */
741 - function successfulLogin() {
742 - global $wgUser, $wgOut;
743 -
744 - # Run any hooks; display injected HTML if any, else redirect
745 - $injected_html = '';
746 - wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
747 -
748 - if( $injected_html !== '' ) {
749 - $this->displaySuccessfulLogin( 'loginsuccess', $injected_html );
750 - } else {
751 - $titleObj = Title::newFromText( $this->mReturnTo );
752 - if ( !$titleObj instanceof Title ) {
753 - $titleObj = Title::newMainPage();
754 - }
755 - $wgOut->redirect( $titleObj->getFullURL( $this->mReturnToQuery ) );
756 - }
757 - }
758 -
759 - /**
760 - * Run any hooks registered for logins, then display a message welcoming
761 - * the user.
762 - *
763 - * @private
764 - */
765 - function successfulCreation() {
766 - global $wgUser, $wgOut;
767 -
768 - # Run any hooks; display injected HTML
769 - $injected_html = '';
770 - wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
771 -
772 - $this->displaySuccessfulLogin( 'welcomecreation', $injected_html );
773 - }
774 -
775 - /**
776 - * Display a "login successful" page.
777 - */
778 - private function displaySuccessfulLogin( $msgname, $injected_html ) {
779 - global $wgOut, $wgUser;
780 -
781 - $wgOut->setPageTitle( wfMsg( 'loginsuccesstitle' ) );
782 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
783 - $wgOut->setArticleRelated( false );
784 - $wgOut->addWikiMsg( $msgname, $wgUser->getName() );
785 - $wgOut->addHTML( $injected_html );
786 -
787 - if ( !empty( $this->mReturnTo ) ) {
788 - $wgOut->returnToMain( null, $this->mReturnTo, $this->mReturnToQuery );
789 - } else {
790 - $wgOut->returnToMain( null );
791 - }
792 - }
793 -
794 - /** */
795 - function userNotPrivilegedMessage($errors) {
 487+ protected function showMailPage(){
796488 global $wgOut;
 489+ $result = $this->mLogin->mailPassword();
797490
798 - $wgOut->setPageTitle( wfMsg( 'permissionserrors' ) );
799 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
800 - $wgOut->setArticleRelated( false );
801 -
802 - $wgOut->addWikitext( $wgOut->formatPermissionsErrorMessage( $errors, 'createaccount' ) );
803 - // Stuff that might want to be added at the end. For example, instruc-
804 - // tions if blocked.
805 - $wgOut->addWikiMsg( 'cantcreateaccount-nonblock-text' );
806 -
807 - $wgOut->returnToMain( false );
808 - }
809 -
810 - /** */
811 - function userBlockedMessage() {
812 - global $wgOut, $wgUser;
813 -
814 - # Let's be nice about this, it's likely that this feature will be used
815 - # for blocking large numbers of innocent people, e.g. range blocks on
816 - # schools. Don't blame it on the user. There's a small chance that it
817 - # really is the user's fault, i.e. the username is blocked and they
818 - # haven't bothered to log out before trying to create an account to
819 - # evade it, but we'll leave that to their guilty conscience to figure
820 - # out.
821 -
822 - $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) );
823 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
824 - $wgOut->setArticleRelated( false );
825 -
826 - $ip = wfGetIP();
827 - $blocker = User::whoIs( $wgUser->mBlock->mBy );
828 - $block_reason = $wgUser->mBlock->mReason;
829 -
830 - if ( strval( $block_reason ) === '' ) {
831 - $block_reason = wfMsg( 'blockednoreason' );
832 - }
833 - $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $block_reason, $blocker );
834 - $wgOut->returnToMain( false );
835 - }
836 -
837 - /**
838 - * @private
839 - */
840 - function mainLoginForm( $msg, $msgtype = 'error' ) {
841 - global $wgUser, $wgOut, $wgHiddenPrefs, $wgEnableEmail;
842 - global $wgCookiePrefix, $wgLoginLanguageSelector;
843 - global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration;
844 -
845 - $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
846 -
847 - if ( $this->mType == 'signup' ) {
848 - // Block signup here if in readonly. Keeps user from
849 - // going through the process (filling out data, etc)
850 - // and being informed later.
851 - if ( wfReadOnly() ) {
 491+ switch( $result ){
 492+ case Login::MAIL_READ_ONLY :
852493 $wgOut->readOnlyPage();
853494 return;
854 - } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
855 - $this->userBlockedMessage();
 495+ case Login::MAIL_PASSCHANGE_FORBIDDEN:
 496+ $this->mainLoginForm( wfMsg( 'resetpass_forbidden' ) );
856497 return;
857 - } elseif ( count( $permErrors = $titleObj->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
858 - $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
 498+ case Login::MAIL_BLOCKED:
 499+ $this->mainLoginForm( wfMsg( 'blocked-mailpassword' ) );
859500 return;
860 - }
 501+ case Login::MAIL_PING_THROTTLED:
 502+ $wgOut->rateLimited();
 503+ return;
 504+ case Login::MAIL_PASS_THROTTLED:
 505+ global $wgPasswordReminderResendTime;
 506+ # Round the time in hours to 3 d.p., in case someone
 507+ # is specifying minutes or seconds.
 508+ $this->mainLoginForm( wfMsgExt(
 509+ 'throttled-mailpassword',
 510+ array( 'parsemag' ),
 511+ round( $wgPasswordReminderResendTime, 3 )
 512+ ) );
 513+ return;
 514+ case Login::NO_NAME:
 515+ $this->mainLoginForm( wfMsg( 'noname' ) );
 516+ return;
 517+ case Login::NOT_EXISTS:
 518+ $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $this->mLogin->mUser->getName() ) ) );
 519+ return;
 520+ case Login::MAIL_EMPTY_EMAIL:
 521+ $this->mainLoginForm( wfMsg( 'noemail', $this->mLogin->mUser->getName() ) );
 522+ return;
 523+ case Login::MAIL_BAD_IP:
 524+ $this->mainLoginForm( wfMsg( 'badipaddress' ) );
 525+ return;
 526+ case Login::MAIL_ERROR:
 527+ $this->mainLoginForm( wfMsg( 'mailerror', $this->mLogin->mMailResult->getMessage() ) );
 528+ return;
 529+ case Login::SUCCESS:
 530+ $this->mainLoginForm( wfMsg( 'passwordsent', $this->mLogin->mUser->getName() ), 'success' );
 531+ return;
861532 }
862 -
863 - if ( '' == $this->mName ) {
864 - if ( $wgUser->isLoggedIn() ) {
865 - $this->mName = $wgUser->getName();
866 - } else {
867 - $this->mName = isset( $_COOKIE[$wgCookiePrefix.'UserName'] ) ? $_COOKIE[$wgCookiePrefix.'UserName'] : null;
868 - }
869 - }
870 -
871 - $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
872 -
873 - if ( $this->mType == 'signup' ) {
874 - $template = new UsercreateTemplate();
875 - $q = 'action=submitlogin&type=signup';
876 - $linkq = 'type=login';
877 - $linkmsg = 'gotaccount';
878 - } else {
879 - $template = new UserloginTemplate();
880 - $q = 'action=submitlogin&type=login';
881 - $linkq = 'type=signup';
882 - $linkmsg = 'nologin';
883 - }
884 -
885 - if ( !empty( $this->mReturnTo ) ) {
886 - $returnto = '&returnto=' . wfUrlencode( $this->mReturnTo );
887 - if ( !empty( $this->mReturnToQuery ) )
888 - $returnto .= '&returntoquery=' .
889 - wfUrlencode( $this->mReturnToQuery );
890 - $q .= $returnto;
891 - $linkq .= $returnto;
892 - }
893 -
894 - # Pass any language selection on to the mode switch link
895 - if( $wgLoginLanguageSelector && $this->mLanguage )
896 - $linkq .= '&uselang=' . $this->mLanguage;
897 -
898 - $link = '<a href="' . htmlspecialchars ( $titleObj->getLocalUrl( $linkq ) ) . '">';
899 - $link .= wfMsgHtml( $linkmsg . 'link' ); # Calling either 'gotaccountlink' or 'nologinlink'
900 - $link .= '</a>';
901 -
902 - # Don't show a "create account" link if the user can't
903 - if( $this->showCreateOrLoginLink( $wgUser ) )
904 - $template->set( 'link', wfMsgWikiHtml( $linkmsg, $link ) );
905 - else
906 - $template->set( 'link', '' );
907 -
908 - $template->set( 'header', '' );
909 - $template->set( 'name', $this->mName );
910 - $template->set( 'password', $this->mPassword );
911 - $template->set( 'retype', $this->mRetype );
912 - $template->set( 'email', $this->mEmail );
913 - $template->set( 'realname', $this->mRealName );
914 - $template->set( 'domain', $this->mDomain );
915 -
916 - $template->set( 'action', $titleObj->getLocalUrl( $q ) );
917 - $template->set( 'message', $msg );
918 - $template->set( 'messagetype', $msgtype );
919 - $template->set( 'createemail', $wgEnableEmail && $wgUser->isLoggedIn() );
920 - $template->set( 'userealname', !in_array( 'realname', $wgHiddenPrefs ) );
921 - $template->set( 'useemail', $wgEnableEmail );
922 - $template->set( 'emailrequired', $wgEmailConfirmToEdit );
923 - $template->set( 'canreset', $wgAuth->allowPasswordChange() );
924 - $template->set( 'canremember', ( $wgCookieExpiration > 0 ) );
925 - $template->set( 'remember', $wgUser->getOption( 'rememberpassword' ) or $this->mRemember );
926 -
927 - # Prepare language selection links as needed
928 - if( $wgLoginLanguageSelector ) {
929 - $template->set( 'languages', $this->makeLanguageSelector() );
930 - if( $this->mLanguage )
931 - $template->set( 'uselang', $this->mLanguage );
932 - }
933 -
934 - // Give authentication and captcha plugins a chance to modify the form
935 - $wgAuth->modifyUITemplate( $template, $this->mType );
936 - if ( $this->mType == 'signup' ) {
937 - wfRunHooks( 'UserCreateForm', array( &$template ) );
938 - } else {
939 - wfRunHooks( 'UserLoginForm', array( &$template ) );
940 - }
941 -
942 - $wgOut->setPageTitle( wfMsg( 'userlogin' ) );
943 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
944 - $wgOut->setArticleRelated( false );
945 - $wgOut->disallowUserJs(); // just in case...
946 - $wgOut->addTemplate( $template );
947533 }
948534
949535 /**
950 - * @private
 536+ * Since the UserLoginForm hook was changed to pass a SpecialPage
 537+ * instead of a QuickTemplate derivative, old extensions might
 538+ * easily try calling this method expecing it to exist. Tempting
 539+ * though it is to let them have the fatal error, let's at least
 540+ * fail gracefully...
 541+ * @deprecated
951542 */
952 - function showCreateOrLoginLink( &$user ) {
953 - if( $this->mType == 'signup' ) {
954 - return( true );
955 - } elseif( $user->isAllowed( 'createaccount' ) ) {
956 - return( true );
957 - } else {
958 - return( false );
959 - }
 543+ public function set(){
 544+ wfDeprecated( __METHOD__ );
960545 }
961 -
962 - /**
963 - * Check if a session cookie is present.
964 - *
965 - * This will not pick up a cookie set during _this_ request, but is meant
966 - * to ensure that the client is returning the cookie which was set on a
967 - * previous pass through the system.
968 - *
969 - * @private
970 - */
971 - function hasSessionCookie() {
972 - global $wgDisableCookieCheck, $wgRequest;
973 - return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
974 - }
975 -
976 - /**
977 - * @private
978 - */
979 - function cookieRedirectCheck( $type ) {
980 - global $wgOut;
981 -
982 - $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
983 - $query = array( 'wpCookieCheck' => $type );
984 - if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo;
985 - $check = $titleObj->getFullURL( $query );
986 -
987 - return $wgOut->redirect( $check );
988 - }
989 -
990 - /**
991 - * @private
992 - */
993 - function onCookieRedirectCheck( $type ) {
994 - if ( !$this->hasSessionCookie() ) {
995 - if ( $type == 'new' ) {
996 - return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) );
997 - } else if ( $type == 'login' ) {
998 - return $this->mainLoginForm( wfMsgExt( 'nocookieslogin', array( 'parseinline' ) ) );
999 - } else {
1000 - # shouldn't happen
1001 - return $this->mainLoginForm( wfMsg( 'error' ) );
1002 - }
1003 - } else {
1004 - return $this->successfulLogin();
1005 - }
1006 - }
1007 -
1008 - /**
1009 - * @private
1010 - */
1011 - function throttleHit( $limit ) {
1012 - $this->mainLoginForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $limit ) );
1013 - }
1014 -
1015 - /**
1016 - * Produce a bar of links which allow the user to select another language
1017 - * during login/registration but retain "returnto"
1018 - *
1019 - * @return string
1020 - */
1021 - function makeLanguageSelector() {
1022 - global $wgLang;
1023 -
1024 - $msg = wfMsgForContent( 'loginlanguagelinks' );
1025 - if( $msg != '' && !wfEmptyMsg( 'loginlanguagelinks', $msg ) ) {
1026 - $langs = explode( "\n", $msg );
1027 - $links = array();
1028 - foreach( $langs as $lang ) {
1029 - $lang = trim( $lang, '* ' );
1030 - $parts = explode( '|', $lang );
1031 - if (count($parts) >= 2) {
1032 - $links[] = $this->makeLanguageSelectorLink( $parts[0], $parts[1] );
1033 - }
1034 - }
1035 - return count( $links ) > 0 ? wfMsgHtml( 'loginlanguagelabel', $wgLang->pipeList( $links ) ) : '';
1036 - } else {
1037 - return '';
1038 - }
1039 - }
1040 -
1041 - /**
1042 - * Create a language selector link for a particular language
1043 - * Links back to this page preserving type and returnto
1044 - *
1045 - * @param $text Link text
1046 - * @param $lang Language code
1047 - */
1048 - function makeLanguageSelectorLink( $text, $lang ) {
1049 - global $wgUser;
1050 - $self = SpecialPage::getTitleFor( 'Userlogin' );
1051 - $attr = array( 'uselang' => $lang );
1052 - if( $this->mType == 'signup' )
1053 - $attr['type'] = 'signup';
1054 - if( $this->mReturnTo )
1055 - $attr['returnto'] = $this->mReturnTo;
1056 - $skin = $wgUser->getSkin();
1057 - return $skin->linkKnown(
1058 - $self,
1059 - htmlspecialchars( $text ),
1060 - array(),
1061 - $attr
1062 - );
1063 - }
1064546 }
Index: trunk/phase3/includes/specials/SpecialCreateAccount.php
@@ -0,0 +1,641 @@
 2+<?php
 3+/**
 4+ * Special page for creating/registering new user accounts.
 5+ * @ingroup SpecialPage
 6+ */
 7+class SpecialCreateAccount extends SpecialPage {
 8+
 9+ var $mUsername, $mPassword, $mRetype, $mReturnTo, $mPosted;
 10+ var $mCreateaccountMail, $mRemember, $mEmail, $mDomain, $mLanguage;
 11+ var $mReturnToQuery;
 12+
 13+ public $mDomains = array();
 14+
 15+ public $mUseEmail = true; # Can be switched off by AuthPlugins etc
 16+ public $mUseRealname = true;
 17+ public $mUseRemember = true;
 18+
 19+ public $mFormHeader = '';
 20+ public $mFormFields = array(
 21+ 'Name' => array(
 22+ 'type' => 'text',
 23+ 'label-message' => 'yourname',
 24+ 'id' => 'wpName2',
 25+ 'tabindex' => '1',
 26+ 'size' => '20',
 27+ 'required' => '1',
 28+ 'autofocus' => '',
 29+ ),
 30+ 'Password' => array(
 31+ 'type' => 'password',
 32+ 'label-message' => 'yourpassword',
 33+ 'size' => '20',
 34+ 'id' => 'wpPassword2',
 35+ 'required' => '',
 36+ ),
 37+ 'Retype' => array(
 38+ 'type' => 'password',
 39+ 'label-message' => 'yourpasswordagain',
 40+ 'size' => '20',
 41+ 'id' => 'wpRetype',
 42+ 'required' => '',
 43+ ),
 44+ 'Email' => array(
 45+ 'type' => 'email',
 46+ 'label-message' => 'youremail',
 47+ 'size' => '20',
 48+ 'id' => 'wpEmail',
 49+ ),
 50+ 'RealName' => array(
 51+ 'type' => 'text',
 52+ 'label-message' => 'yourrealname',
 53+ 'id' => 'wpRealName',
 54+ 'tabindex' => '1',
 55+ 'size' => '20',
 56+ ),
 57+ 'Remember' => array(
 58+ 'type' => 'check',
 59+ 'label-message' => 'remembermypassword',
 60+ 'id' => 'wpRemember',
 61+ ),
 62+ 'Domain' => array(
 63+ 'type' => 'select',
 64+ 'id' => 'wpDomain',
 65+ 'label-message' => 'yourdomainname',
 66+ 'options' => null,
 67+ 'default' => null,
 68+ ),
 69+ );
 70+
 71+ public function __construct(){
 72+ parent::__construct( 'CreateAccount', 'createaccount' );
 73+ $this->mLogin = new Login();
 74+ $this->mFormFields['RealName']['help'] = wfMsg( 'prefs-help-realname' );
 75+ }
 76+
 77+ public function execute( $par ){
 78+ global $wgUser, $wgOut;
 79+
 80+ $this->setHeaders();
 81+ $this->loadQuery();
 82+
 83+ # Block signup here if in readonly. Keeps user from
 84+ # going through the process (filling out data, etc)
 85+ # and being informed later.
 86+ if ( wfReadOnly() ) {
 87+ $wgOut->readOnlyPage();
 88+ return;
 89+ }
 90+ # Bail out straightaway on permissions errors
 91+ if ( !$this->userCanExecute( $wgUser ) ) {
 92+ $this->displayRestrictionError();
 93+ return;
 94+ } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
 95+ $this->userBlockedMessage();
 96+ return;
 97+ } elseif ( count( $permErrors = $this->getTitle()->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
 98+ var_dump('error');
 99+ $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
 100+ return;
 101+ }
 102+
 103+ if( $this->mPosted ) {
 104+ if ( $this->mCreateaccountMail ) {
 105+ return $this->addNewAccountMailPassword();
 106+ } else {
 107+ return $this->addNewAccount();
 108+ }
 109+ } else {
 110+ $this->showMainForm('');
 111+ }
 112+ }
 113+
 114+ /**
 115+ * Load the member variables from the request parameters
 116+ */
 117+ protected function loadQuery(){
 118+ global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
 119+ $this->mCreateaccountMail = $wgRequest->getCheck( 'wpCreateaccountMail' )
 120+ && $wgEnableEmail;
 121+
 122+ $this->mUsername = $wgRequest->getText( 'wpName' );
 123+ $this->mPassword = $wgRequest->getText( 'wpPassword' );
 124+ $this->mRetype = $wgRequest->getText( 'wpRetype' );
 125+ $this->mDomain = $wgRequest->getText( 'wpDomain' );
 126+ $this->mReturnTo = $wgRequest->getVal( 'returnto' );
 127+ $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
 128+ $this->mPosted = $wgRequest->wasPosted();
 129+ $this->mCreateaccountMail = $wgRequest->getCheck( 'wpCreateaccountMail' )
 130+ && $wgEnableEmail;
 131+ $this->mRemember = $wgRequest->getCheck( 'wpRemember' );
 132+ $this->mLanguage = $wgRequest->getText( 'uselang' );
 133+
 134+ if ( $wgRedirectOnLogin ) {
 135+ $this->mReturnTo = $wgRedirectOnLogin;
 136+ $this->mReturnToQuery = '';
 137+ }
 138+
 139+ if( $wgEnableEmail ) {
 140+ $this->mEmail = $wgRequest->getText( 'wpEmail' );
 141+ } else {
 142+ $this->mEmail = '';
 143+ }
 144+ if( !in_array( 'realname', $wgHiddenPrefs ) ) {
 145+ $this->mRealName = $wgRequest->getText( 'wpRealName' );
 146+ } else {
 147+ $this->mRealName = '';
 148+ }
 149+
 150+ if( !$wgAuth->validDomain( $this->mDomain ) ) {
 151+ $this->mDomain = 'invaliddomain';
 152+ }
 153+ $wgAuth->setDomain( $this->mDomain );
 154+
 155+ # When switching accounts, it sucks to get automatically logged out
 156+ $returnToTitle = Title::newFromText( $this->mReturnTo );
 157+ if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) {
 158+ $this->mReturnTo = '';
 159+ $this->mReturnToQuery = '';
 160+ }
 161+ }
 162+
 163+ /**
 164+ * Add a new account, and mail its password to the user
 165+ */
 166+ protected function addNewAccountMailPassword() {
 167+ global $wgOut;
 168+
 169+ if( !$this->mEmail ) {
 170+ $this->showMainForm( wfMsg( 'noemail', htmlspecialchars( $this->mUsername ) ) );
 171+ return;
 172+ }
 173+
 174+ if( !$this->addNewaccountInternal() ) {
 175+ return;
 176+ }
 177+
 178+ # Wipe the initial password
 179+ $this->mLogin->mUser->setPassword( null );
 180+ $this->mLogin->mUser->saveSettings();
 181+
 182+ # And mail them a temporary one
 183+ $result = $this->mLogin->mailPassword( 'createaccount-title', 'createaccount-text' );
 184+
 185+ wfRunHooks( 'AddNewAccount', array( $this->mLogin->mUser, true ) );
 186+ $this->mLogin->mUser->addNewUserLogEntry();
 187+
 188+ $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
 189+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 190+ $wgOut->setArticleRelated( false );
 191+
 192+ if( $result != Login::SUCCESS ) {
 193+ if( $result == Login::MAIL_ERROR ){
 194+ $this->showMainForm( wfMsg( 'mailerror', $this->mLogin->mMailResult->getMessage() ) );
 195+ } else {
 196+ $this->showMainForm( wfMsg( 'mailerror' ) );
 197+ }
 198+ } else {
 199+ $wgOut->addWikiMsg( 'accmailtext', $this->mLogin->mUserer->getName(), $this->mLogin->mUser->getEmail() );
 200+ $wgOut->returnToMain( false );
 201+ }
 202+ }
 203+
 204+ /**
 205+ * Create a new user account from the provided data
 206+ */
 207+ protected function addNewAccount() {
 208+ global $wgUser, $wgEmailAuthentication;
 209+
 210+ # Create the account and abort if there's a problem doing so
 211+ if( !$this->addNewAccountInternal() )
 212+ return;
 213+ $user = $this->mLogin->mUser;
 214+
 215+ # If we showed up language selection links, and one was in use, be
 216+ # smart (and sensible) and save that language as the user's preference
 217+ global $wgLoginLanguageSelector;
 218+ if( $wgLoginLanguageSelector && $this->mLanguage )
 219+ $user->setOption( 'language', $this->mLanguage );
 220+
 221+ # Send out an email authentication message if needed
 222+ if( $wgEmailAuthentication && User::isValidEmailAddr( $user->getEmail() ) ) {
 223+ global $wgOut;
 224+ $error = $user->sendConfirmationMail();
 225+ if( WikiError::isError( $error ) ) {
 226+ $wgOut->addWikiMsg( 'confirmemail_sendfailed', $error->getMessage() );
 227+ } else {
 228+ $wgOut->addWikiMsg( 'confirmemail_oncreate' );
 229+ }
 230+ }
 231+
 232+ # Save settings (including confirmation token)
 233+ $user->saveSettings();
 234+
 235+ # If not logged in, assume the new account as the current one and set
 236+ # session cookies then show a "welcome" message or a "need cookies"
 237+ # message as needed
 238+ if( $wgUser->isAnon() ) {
 239+ $wgUser = $user;
 240+ $wgUser->setCookies();
 241+ wfRunHooks( 'AddNewAccount', array( $wgUser ) );
 242+ $wgUser->addNewUserLogEntry();
 243+ if( $this->hasSessionCookie() ) {
 244+ return $this->successfulCreation();
 245+ } else {
 246+ return $this->cookieRedirectCheck();
 247+ }
 248+ } else {
 249+ # Confirm that the account was created
 250+ global $wgOut;
 251+ $self = SpecialPage::getTitleFor( 'Userlogin' );
 252+ $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
 253+ $wgOut->setArticleRelated( false );
 254+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 255+ $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $user->getName() ) );
 256+ $wgOut->returnToMain( false, $self );
 257+ wfRunHooks( 'AddNewAccount', array( $user ) );
 258+ $user->addNewUserLogEntry();
 259+ return true;
 260+ }
 261+ }
 262+
 263+ /**
 264+ * Deeper mechanics of initialising a new user and passing it
 265+ * off to Login::initUser()
 266+ * return Bool whether the user was successfully created
 267+ */
 268+ protected function addNewAccountInternal() {
 269+ global $wgUser, $wgOut;
 270+ global $wgEnableSorbs, $wgProxyWhitelist;
 271+ global $wgMemc, $wgAccountCreationThrottle;
 272+ global $wgAuth, $wgMinimalPasswordLength;
 273+ global $wgEmailConfirmToEdit;
 274+
 275+ # If the user passes an invalid domain, something is fishy
 276+ if( !$wgAuth->validDomain( $this->mDomain ) ) {
 277+ $this->showMainForm( wfMsg( 'wrongpassword' ) );
 278+ return false;
 279+ }
 280+
 281+ # If we are not allowing users to login locally, we should be checking
 282+ # to see if the user is actually able to authenticate to the authenti-
 283+ # cation server before they create an account (otherwise, they can
 284+ # create a local account and login as any domain user). We only need
 285+ # to check this for domains that aren't local.
 286+ if( !in_array( $this->mDomain, array( 'local', '' ) )
 287+ && !$wgAuth->canCreateAccounts()
 288+ && ( !$wgAuth->userExists( $this->mUsername )
 289+ || !$wgAuth->authenticate( $this->mUsername, $this->mPassword )
 290+ ) )
 291+ {
 292+ $this->showMainForm( wfMsg( 'wrongpassword' ) );
 293+ return false;
 294+ }
 295+
 296+ $ip = wfGetIP();
 297+ if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
 298+ $wgUser->inSorbsBlacklist( $ip ) )
 299+ {
 300+ $this->showMainForm( wfMsg( 'sorbs_create_account_reason' ) . ' (' . htmlspecialchars( $ip ) . ')' );
 301+ return false;
 302+ }
 303+
 304+ # Now create a dummy user ($user) and check if it is valid
 305+ $name = trim( $this->mUsername );
 306+ $user = User::newFromName( $name, 'creatable' );
 307+ if ( is_null( $user ) ) {
 308+ $this->showMainForm( wfMsg( 'noname' ) );
 309+ return false;
 310+ }
 311+
 312+ if ( 0 != $user->idForName() ) {
 313+ $this->showMainForm( wfMsg( 'userexists' ) );
 314+ return false;
 315+ }
 316+
 317+ if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
 318+ $this->showMainForm( wfMsg( 'badretype' ) );
 319+ return false;
 320+ }
 321+
 322+ # check for minimal password length
 323+ $valid = $user->isValidPassword( $this->mPassword );
 324+ if ( $valid !== true ) {
 325+ if ( !$this->mCreateaccountMail ) {
 326+ $this->showMainForm( wfMsgExt( $valid, array( 'parsemag' ), $wgMinimalPasswordLength ) );
 327+ return false;
 328+ } else {
 329+ # do not force a password for account creation by email
 330+ # set invalid password, it will be replaced later by a random generated password
 331+ $this->mPassword = null;
 332+ }
 333+ }
 334+
 335+ # if you need a confirmed email address to edit, then obviously you
 336+ # need an email address.
 337+ if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) ) {
 338+ $this->showMainForm( wfMsg( 'noemailtitle' ) );
 339+ return false;
 340+ }
 341+
 342+ if( !empty( $this->mEmail ) && !User::isValidEmailAddr( $this->mEmail ) ) {
 343+ $this->showMainForm( wfMsg( 'invalidemailaddress' ) );
 344+ return false;
 345+ }
 346+
 347+ # Set some additional data so the AbortNewAccount hook can be used for
 348+ # more than just username validation
 349+ $user->setEmail( $this->mEmail );
 350+ $user->setRealName( $this->mRealName );
 351+
 352+ $abortError = '';
 353+ if( !wfRunHooks( 'AbortNewAccount', array( $user, &$abortError ) ) ) {
 354+ # Hook point to add extra creation throttles and blocks
 355+ wfDebug( "LoginForm::addNewAccountInternal: a hook blocked creation\n" );
 356+ $this->showMainForm( $abortError );
 357+ return false;
 358+ }
 359+
 360+ if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
 361+ $key = wfMemcKey( 'acctcreate', 'ip', $ip );
 362+ $value = $wgMemc->get( $key );
 363+ if ( !$value ) {
 364+ $wgMemc->set( $key, 0, 86400 );
 365+ }
 366+ if ( $value >= $wgAccountCreationThrottle ) {
 367+ $this->showMainForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $wgAccountCreationThrottle ) );
 368+ return false;
 369+ }
 370+ $wgMemc->incr( $key );
 371+ }
 372+
 373+ if( !$wgAuth->addUser( $user, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
 374+ $this->showMainForm( wfMsg( 'externaldberror' ) );
 375+ return false;
 376+ }
 377+
 378+ $this->mLogin->mUser = $user;
 379+ $this->mLogin->initUser( false );
 380+ return true;
 381+ }
 382+
 383+ /**
 384+ * Run any hooks registered for logins, then
 385+ * display a message welcoming the user.
 386+ */
 387+ protected function successfulCreation(){
 388+ global $wgUser, $wgOut;
 389+
 390+ # Run any hooks; display injected HTML
 391+ $injected_html = '';
 392+ wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
 393+
 394+ SpecialUserLogin::displaySuccessfulLogin(
 395+ 'welcomecreation',
 396+ $injected_html,
 397+ $this->mReturnTo,
 398+ $this->mReturnToQuery );
 399+ }
 400+
 401+ /**
 402+ * Display a message indicating that account creation from their IP has
 403+ * been blocked by a (range)block with 'block account creation' enabled.
 404+ * It's likely that this feature will be used for blocking large numbers
 405+ * of innocent people, e.g. range blocks on schools. Don't blame it on
 406+ * the user. There's a small chance that it really is the user's fault,
 407+ * i.e. the username is blocked and they haven't bothered to log out
 408+ * before trying to create an account to evade it, but we'll leave that
 409+ * to their guilty conscience to figure out...
 410+ */
 411+ protected function userBlockedMessage() {
 412+ global $wgOut, $wgUser;
 413+
 414+ $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) );
 415+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 416+ $wgOut->setArticleRelated( false );
 417+
 418+ $ip = wfGetIP();
 419+ $blocker = User::whoIs( $wgUser->mBlock->mBy );
 420+ $block_reason = $wgUser->mBlock->mReason;
 421+
 422+ if ( strval( $block_reason ) === '' ) {
 423+ $block_reason = wfMsg( 'blockednoreason' );
 424+ }
 425+ $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $block_reason, $blocker );
 426+ $wgOut->returnToMain( false );
 427+ }
 428+
 429+ /**
 430+ * Show the main input form, with an appropriate error message
 431+ * from a previous iteration, if necessary
 432+ * @param $msg String HTML of message received previously
 433+ * @param $msgtype String type of message, usually 'error'
 434+ */
 435+ protected function showMainForm( $msg, $msgtype = 'error' ) {
 436+ global $wgUser, $wgOut, $wgHiddenPrefs, $wgEnableEmail;
 437+ global $wgCookiePrefix, $wgLoginLanguageSelector;
 438+ global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration;
 439+
 440+ # Parse the error message if we got one
 441+ if( $msg ){
 442+ if( $msgtype == 'error' ){
 443+ $msg = wfMsg( 'loginerror' ) . ' ' . $msg;
 444+ }
 445+ $msg = Html::rawElement(
 446+ 'div',
 447+ array( 'class' => $msgtype . 'box' ),
 448+ $msg
 449+ );
 450+ } else {
 451+ $msg = '';
 452+ }
 453+
 454+ # Make sure the returnTo strings don't get lost if the
 455+ # user changes language, etc
 456+ $linkq = array();
 457+ if ( !empty( $this->mReturnTo ) ) {
 458+ $linkq['returnto'] = wfUrlencode( $this->mReturnTo );
 459+ if ( !empty( $this->mReturnToQuery ) )
 460+ $linkq['returntoquery'] = wfUrlencode( $this->mReturnToQuery );
 461+ }
 462+
 463+ # Pass any language selection on to the mode switch link
 464+ if( $wgLoginLanguageSelector && $this->mLanguage )
 465+ $linkq['uselang'] = $this->mLanguage;
 466+
 467+ $skin = $wgUser->getSkin();
 468+ $link = $skin->link(
 469+ SpecialPage::getTitleFor( 'Userlogin' ),
 470+ wfMsgHtml( 'gotaccountlink' ),
 471+ array(),
 472+ $linkq );
 473+ $link = $wgUser->isLoggedIn()
 474+ ? ''
 475+ : wfMsgWikiHtml( 'gotaccount', $link );
 476+
 477+ # Prepare language selection links as needed
 478+ $langSelector = $wgLoginLanguageSelector
 479+ ? Html::rawElement(
 480+ 'div',
 481+ array( 'id' => 'languagelinks' ),
 482+ SpecialUserLogin::makeLanguageSelector( $this->getTitle(), $this->mReturnTo ) )
 483+ : '';
 484+
 485+ # Add a 'send password by email' button if available
 486+ $buttons = '';
 487+ if( $wgEnableEmail && $wgUser->isLoggedIn() ){
 488+ $buttons = Html::element(
 489+ 'input',
 490+ array(
 491+ 'type' => 'submit',
 492+ 'name' => 'wpCreateaccountMail',
 493+ 'value' => wfMsg( 'createaccountmail' ),
 494+ 'id' => 'wpCreateaccountMail',
 495+ )
 496+ );
 497+ }
 498+
 499+ # Give authentication and captcha plugins a chance to
 500+ # modify the form, by hook or by using $wgAuth
 501+ $wgAuth->modifyUITemplate( $this, 'new' );
 502+ wfRunHooks( 'UserCreateForm', array( &$this ) );
 503+
 504+ # The most likely use of the hook is to enable domains;
 505+ # check that now, and add fields if necessary
 506+ if( $this->mDomains ){
 507+ $this->mFormFields['Domain']['options'] = $this->mDomains;
 508+ $this->mFormFields['Domain']['default'] = $this->mDomain;
 509+ } else {
 510+ unset( $this->mFormFields['Domain'] );
 511+ }
 512+
 513+ # Or to switch email on or off
 514+ if( !$wgEnableEmail || !$this->mUseEmail ){
 515+ unset( $this->mFormFields['Email'] );
 516+ } else {
 517+ if( $wgEmailConfirmToEdit ){
 518+ $this->mFormFields['Email']['help'] = wfMsg( 'prefs-help-email-required' );
 519+ $this->mFormFields['Email']['required'] = '';
 520+ } else {
 521+ $this->mFormFields['Email']['help'] = wfMsg( 'prefs-help-email' );
 522+ }
 523+ }
 524+
 525+ # Or to play with realname
 526+ if( in_array( 'realname', $wgHiddenPrefs ) || !$this->mUseRealname ){
 527+ unset( $this->mFormFields['Realname'] );
 528+ }
 529+
 530+ # Or to tweak the 'remember my password' checkbox
 531+ if( !($wgCookieExpiration > 0) || !$this->mUseRemember ){
 532+ # Remove it altogether
 533+ unset( $this->mFormFields['Remember'] );
 534+ } elseif( $wgUser->getOption( 'rememberpassword' ) || $this->mRemember ){
 535+ # Or check it by default
 536+ # FIXME: this doesn't always work?
 537+ $this->mFormFields['Remember']['checked'] = '1';
 538+ }
 539+
 540+ $form = new HTMLForm( $this->mFormFields, '' );
 541+ $form->setTitle( $this->getTitle() );
 542+ $form->setSubmitText( wfMsg( 'createaccount' ) );
 543+ $form->setSubmitId( 'wpCreateaccount' );
 544+ $form->suppressReset();
 545+ $form->loadData();
 546+
 547+ $formContents = ''
 548+ . Html::rawElement( 'p', array( 'id' => 'userloginlink' ),
 549+ $link )
 550+ . $this->mFormHeader
 551+ . $langSelector
 552+ . $form->getBody()
 553+ . $form->getButtons()
 554+ . $buttons
 555+ . Xml::hidden( 'returnto', $this->mReturnTo )
 556+ . Xml::hidden( 'returntoquery', $this->mReturnToQuery )
 557+ ;
 558+
 559+ $wgOut->setPageTitle( wfMsg( 'createaccount' ) );
 560+ $wgOut->setRobotPolicy( 'noindex,nofollow' );
 561+ $wgOut->setArticleRelated( false );
 562+ $wgOut->disallowUserJs(); # Stop malicious userscripts sniffing passwords
 563+
 564+ $wgOut->addHTML(
 565+ Html::rawElement(
 566+ 'div',
 567+ array( 'id' => 'loginstart' ),
 568+ wfMsgExt( 'loginstart', array( 'parseinline' ) )
 569+ ) .
 570+ $msg .
 571+ Html::rawElement(
 572+ 'div',
 573+ array( 'id' => 'userloginForm' ),
 574+ $form->wrapForm( $formContents )
 575+ ) .
 576+ Html::rawElement(
 577+ 'div',
 578+ array( 'id' => 'loginend' ),
 579+ wfMsgExt( 'loginend', array( 'parseinline' ) )
 580+ )
 581+ );
 582+
 583+ }
 584+
 585+ /**
 586+ * Check if a session cookie is present.
 587+ *
 588+ * This will not pick up a cookie set during _this_ request, but is meant
 589+ * to ensure that the client is returning the cookie which was set on a
 590+ * previous pass through the system.
 591+ *
 592+ * @private
 593+ */
 594+ protected function hasSessionCookie() {
 595+ global $wgDisableCookieCheck, $wgRequest;
 596+ return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
 597+ }
 598+
 599+ /**
 600+ * Do a redirect back to the same page, so we can check any
 601+ * new session cookies.
 602+ */
 603+ protected function cookieRedirectCheck() {
 604+ global $wgOut;
 605+
 606+ $query = array( 'wpCookieCheck' => '1' );
 607+ if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo;
 608+ $check = $this->getTitle()->getFullURL( $query );
 609+
 610+ return $wgOut->redirect( $check );
 611+ }
 612+
 613+ /**
 614+ * Check the cookies and show errors if they're not enabled.
 615+ * @param $type String action being performed
 616+ */
 617+ protected function onCookieRedirectCheck() {
 618+ if ( !$this->hasSessionCookie() ) {
 619+ return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) );
 620+ } else {
 621+ return SpecialUserLogin::successfulLogin(
 622+ 'welcomecreate',
 623+ $this->mReturnTo,
 624+ $this->mReturnToQuery );
 625+ }
 626+ }
 627+
 628+ /**
 629+ * Since the UserCreateForm hook was changed to pass a SpecialPage
 630+ * instead of a QuickTemplate derivative, old extensions might
 631+ * easily try calling these methods expecing them to exist. Tempting
 632+ * though it is to let them have the fatal error, let's at least
 633+ * fail gracefully...
 634+ * @deprecated
 635+ */
 636+ public function set(){
 637+ wfDeprecated( __METHOD__ );
 638+ }
 639+ public function addInputItem(){
 640+ wfDeprecated( __METHOD__ );
 641+ }
 642+}
Property changes on: trunk/phase3/includes/specials/SpecialCreateAccount.php
___________________________________________________________________
Added: svn:eol-style
1643 + native
Index: trunk/phase3/includes/specials/SpecialResetpass.php
@@ -43,7 +43,6 @@
4444 $wgOut->addWikiMsg( 'resetpass_success' );
4545 if( !$wgUser->isLoggedIn() ) {
4646 $data = array(
47 - 'action' => 'submitlogin',
4847 'wpName' => $this->mUserName,
4948 'wpPassword' => $this->mNewpass,
5049 'returnto' => $wgRequest->getVal( 'returnto' ),
@@ -52,7 +51,7 @@
5352 $data['wpRemember'] = 1;
5453 }
5554 $login = new LoginForm( new FauxRequest( $data, true ) );
56 - $login->execute();
 55+ $login->login();
5756 }
5857 $titleObj = Title::newFromText( $wgRequest->getVal( 'returnto' ) );
5958 if ( !$titleObj instanceof Title ) {
Index: trunk/phase3/includes/SpecialPage.php
@@ -112,8 +112,8 @@
113113 'Listredirects' => array( 'SpecialPage', 'Listredirects' ),
114114
115115 # Login/create account
116 - 'Userlogin' => array( 'SpecialPage', 'Userlogin' ),
117 - 'CreateAccount' => array( 'SpecialRedirectToSpecial', 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) ),
 116+ 'Userlogin' => 'SpecialUserLogin',
 117+ 'CreateAccount' => 'SpecialCreateAccount',
118118
119119 # Users and rights
120120 'Blockip' => array( 'SpecialPage', 'Blockip', 'block' ),
Index: trunk/phase3/includes/templates/Userlogin.php
@@ -1,323 +0,0 @@
2 -<?php
3 -/**
4 - * @defgroup Templates Templates
5 - * @file
6 - * @ingroup Templates
7 - */
8 -if( !defined( 'MEDIAWIKI' ) ) die( -1 );
9 -
10 -/**
11 - * HTML template for Special:Userlogin form
12 - * @ingroup Templates
13 - */
14 -class UserloginTemplate extends QuickTemplate {
15 - function execute() {
16 - if( $this->data['message'] ) {
17 -?>
18 - <div class="<?php $this->text('messagetype') ?>box">
19 - <?php if ( $this->data['messagetype'] == 'error' ) { ?>
20 - <h2><?php $this->msg('loginerror') ?></h2>
21 - <?php } ?>
22 - <?php $this->html('message') ?>
23 - </div>
24 - <div class="visualClear"></div>
25 -<?php } ?>
26 -
27 -<div id="loginstart"><?php $this->msgWiki( 'loginstart' ); ?></div>
28 -<div id="userloginForm">
29 -<form name="userlogin" method="post" action="<?php $this->text('action') ?>">
30 - <h2><?php $this->msg('login') ?></h2>
31 - <p id="userloginlink"><?php $this->html('link') ?></p>
32 - <?php $this->html('header'); /* pre-table point for form plugins... */ ?>
33 - <div id="userloginprompt"><?php $this->msgWiki('loginprompt') ?></div>
34 - <?php if( @$this->haveData( 'languages' ) ) { ?><div id="languagelinks"><p><?php $this->html( 'languages' ); ?></p></div><?php } ?>
35 - <table>
36 - <tr>
37 - <td class="mw-label"><label for='wpName1'><?php $this->msg('yourname') ?></label></td>
38 - <td class="mw-input">
39 - <?php
40 - echo Html::input( 'wpName', $this->data['name'], 'text', array(
41 - 'class' => 'loginText',
42 - 'id' => 'wpName1',
43 - 'tabindex' => '1',
44 - 'size' => '20',
45 - 'required'
46 - # Can't do + array( 'autofocus' ) because + for arrays in PHP
47 - # only works right for associative arrays! Thanks, PHP.
48 - ) + ( $this->data['name'] ? array() : array( 'autofocus' => '' ) ) ); ?>
49 -
50 - </td>
51 - </tr>
52 - <tr>
53 - <td class="mw-label"><label for='wpPassword1'><?php $this->msg('yourpassword') ?></label></td>
54 - <td class="mw-input">
55 - <?php
56 - echo Html::input( 'wpPassword', null, 'password', array(
57 - 'class' => 'loginPassword',
58 - 'id' => 'wpPassword1',
59 - 'tabindex' => '2',
60 - 'size' => '20'
61 - ) + ( $this->data['name'] ? array( 'autofocus' ) : array() ) ); ?>
62 -
63 - </td>
64 - </tr>
65 - <?php if( $this->data['usedomain'] ) {
66 - $doms = "";
67 - foreach( $this->data['domainnames'] as $dom ) {
68 - $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
69 - }
70 - ?>
71 - <tr id="mw-user-domain-section">
72 - <td class="mw-label"><?php $this->msg( 'yourdomainname' ) ?></td>
73 - <td class="mw-input">
74 - <select name="wpDomain" value="<?php $this->text( 'domain' ) ?>"
75 - tabindex="3">
76 - <?php echo $doms ?>
77 - </select>
78 - </td>
79 - </tr>
80 - <?php }
81 - if( $this->data['canremember'] ) { ?>
82 - <tr>
83 - <td></td>
84 - <td class="mw-input">
85 - <?php
86 - echo Html::input( 'wpRemember', '1', 'checkbox', array(
87 - 'tabindex' => '4',
88 - 'id' => 'wpRemember'
89 - ) + ( $this->data['remember'] ? array( 'checked' ) : array() ) ); ?>
90 -
91 - <label for="wpRemember"><?php $this->msg('remembermypassword') ?></label>
92 - </td>
93 - </tr>
94 -<?php } ?>
95 - <tr>
96 - <td></td>
97 - <td class="mw-submit">
98 - <?php
99 - echo Html::input( 'wpLoginAttempt', wfMsg( 'login' ), 'submit', array(
100 - 'id' => 'wpLoginAttempt',
101 - 'tabindex' => '5'
102 - ) );
103 - if ( $this->data['useemail'] && $this->data['canreset'] ) {
104 - echo '&nbsp;';
105 - echo Html::input( 'wpMailmypassword', wfMsg( 'mailmypassword' ), 'submit', array(
106 - 'id' => 'wpMailmypassword',
107 - 'tabindex' => '6'
108 - ) );
109 - } ?>
110 -
111 - </td>
112 - </tr>
113 - </table>
114 -<?php if( @$this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
115 -</form>
116 -</div>
117 -<div id="loginend"><?php $this->msgWiki( 'loginend' ); ?></div>
118 -<?php
119 -
120 - }
121 -}
122 -
123 -/**
124 - * @ingroup Templates
125 - */
126 -class UsercreateTemplate extends QuickTemplate {
127 - function addInputItem( $name, $value, $type, $msg, $helptext = false ) {
128 - $this->data['extraInput'][] = array(
129 - 'name' => $name,
130 - 'value' => $value,
131 - 'type' => $type,
132 - 'msg' => $msg,
133 - 'helptext' => $helptext,
134 - );
135 - }
136 -
137 - function execute() {
138 - if( $this->data['message'] ) {
139 -?>
140 - <div class="<?php $this->text('messagetype') ?>box">
141 - <?php if ( $this->data['messagetype'] == 'error' ) { ?>
142 - <h2><?php $this->msg('loginerror') ?></h2>
143 - <?php } ?>
144 - <?php $this->html('message') ?>
145 - </div>
146 - <div class="visualClear"></div>
147 -<?php } ?>
148 -<div id="userlogin">
149 -
150 -<form name="userlogin2" id="userlogin2" method="post" action="<?php $this->text('action') ?>">
151 - <h2><?php $this->msg('createaccount') ?></h2>
152 - <p id="userloginlink"><?php $this->html('link') ?></p>
153 - <?php $this->html('header'); /* pre-table point for form plugins... */ ?>
154 - <?php if( @$this->haveData( 'languages' ) ) { ?><div id="languagelinks"><p><?php $this->html( 'languages' ); ?></p></div><?php } ?>
155 - <table>
156 - <tr>
157 - <td class="mw-label"><label for='wpName2'><?php $this->msg('yourname') ?></label></td>
158 - <td class="mw-input">
159 - <?php
160 - echo Html::input( 'wpName', null, 'text', array(
161 - 'class' => 'loginText',
162 - 'id' => 'wpName2',
163 - 'tabindex' => '1',
164 - 'size' => '20',
165 - 'required',
166 - 'autofocus'
167 - ) ); ?>
168 - </td>
169 - </tr>
170 - <tr>
171 - <td class="mw-label"><label for='wpPassword2'><?php $this->msg('yourpassword') ?></label></td>
172 - <td class="mw-input">
173 -<?php
174 - echo Html::input( 'wpPassword', null, 'password', array(
175 - 'class' => 'loginPassword',
176 - 'id' => 'wpPassword2',
177 - 'tabindex' => '2',
178 - 'size' => '20'
179 - ) + User::passwordChangeInputAttribs() ); ?>
180 - </td>
181 - </tr>
182 - <?php if( $this->data['usedomain'] ) {
183 - $doms = "";
184 - foreach( $this->data['domainnames'] as $dom ) {
185 - $doms .= "<option>" . htmlspecialchars( $dom ) . "</option>";
186 - }
187 - ?>
188 - <tr>
189 - <td class="mw-label"><?php $this->msg( 'yourdomainname' ) ?></td>
190 - <td class="mw-input">
191 - <select name="wpDomain" value="<?php $this->text( 'domain' ) ?>"
192 - tabindex="3">
193 - <?php echo $doms ?>
194 - </select>
195 - </td>
196 - </tr>
197 - <?php } ?>
198 - <tr>
199 - <td class="mw-label"><label for='wpRetype'><?php $this->msg('yourpasswordagain') ?></label></td>
200 - <td class="mw-input">
201 - <?php
202 - echo Html::input( 'wpRetype', null, 'password', array(
203 - 'class' => 'loginPassword',
204 - 'id' => 'wpRetype',
205 - 'tabindex' => '4',
206 - 'size' => '20'
207 - ) + User::passwordChangeInputAttribs() ); ?>
208 - </td>
209 - </tr>
210 - <tr>
211 - <?php if( $this->data['useemail'] ) { ?>
212 - <td class="mw-label"><label for='wpEmail'><?php $this->msg('youremail') ?></label></td>
213 - <td class="mw-input">
214 - <?php
215 - echo Html::input( 'wpEmail', $this->data['email'], 'email', array(
216 - 'class' => 'loginText',
217 - 'id' => 'wpEmail',
218 - 'tabindex' => '5',
219 - 'size' => '20'
220 - ) ); ?>
221 - <div class="prefsectiontip">
222 - <?php if( $this->data['emailrequired'] ) {
223 - $this->msgWiki('prefs-help-email-required');
224 - } else {
225 - $this->msgWiki('prefs-help-email');
226 - } ?>
227 - </div>
228 - </td>
229 - <?php } ?>
230 - <?php if( $this->data['userealname'] ) { ?>
231 - </tr>
232 - <tr>
233 - <td class="mw-label"><label for='wpRealName'><?php $this->msg('yourrealname') ?></label></td>
234 - <td class="mw-input">
235 - <input type='text' class='loginText' name="wpRealName" id="wpRealName"
236 - tabindex="6"
237 - value="<?php $this->text('realname') ?>" size='20' />
238 - <div class="prefsectiontip">
239 - <?php $this->msgWiki('prefs-help-realname'); ?>
240 - </div>
241 - </td>
242 - <?php } ?>
243 - </tr>
244 - <?php if( $this->data['canremember'] ) { ?>
245 - <tr>
246 - <td></td>
247 - <td class="mw-input">
248 - <input type='checkbox' name="wpRemember"
249 - tabindex="7"
250 - value="1" id="wpRemember"
251 - <?php if( $this->data['remember'] ) { ?>checked="checked"<?php } ?>
252 - /> <label for="wpRemember"><?php $this->msg('remembermypassword') ?></label>
253 - </td>
254 - </tr>
255 -<?php }
256 -
257 - $tabIndex = 8;
258 - if ( isset( $this->data['extraInput'] ) && is_array( $this->data['extraInput'] ) ) {
259 - foreach ( $this->data['extraInput'] as $inputItem ) { ?>
260 - <tr>
261 - <?php
262 - if ( !empty( $inputItem['msg'] ) && $inputItem['type'] != 'checkbox' ) {
263 - ?><td class="mw-label"><label for="<?php
264 - echo htmlspecialchars( $inputItem['name'] ); ?>"><?php
265 - $this->msgWiki( $inputItem['msg'] ) ?></label><?php
266 - } else {
267 - ?><td><?php
268 - }
269 - ?></td>
270 - <td class="mw-input">
271 - <input type="<?php echo htmlspecialchars( $inputItem['type'] ) ?>" name="<?php
272 - echo htmlspecialchars( $inputItem['name'] ); ?>"
273 - tabindex="<?php echo $tabIndex++; ?>"
274 - value="<?php
275 - if ( $inputItem['type'] != 'checkbox' ) {
276 - echo htmlspecialchars( $inputItem['value'] );
277 - } else {
278 - echo '1';
279 - }
280 - ?>" id="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"
281 - <?php
282 - if ( $inputItem['type'] == 'checkbox' && !empty( $inputItem['value'] ) )
283 - echo 'checked="checked"';
284 - ?> /> <?php
285 - if ( $inputItem['type'] == 'checkbox' && !empty( $inputItem['msg'] ) ) {
286 - ?>
287 - <label for="<?php echo htmlspecialchars( $inputItem['name'] ); ?>"><?php
288 - $this->msgHtml( $inputItem['msg'] ) ?></label><?php
289 - }
290 - if( $inputItem['helptext'] !== false ) {
291 - ?>
292 - <div class="prefsectiontip">
293 - <?php $this->msgWiki( $inputItem['helptext'] ); ?>
294 - </div>
295 - <?php } ?>
296 - </td>
297 - </tr>
298 -<?php
299 -
300 - }
301 - }
302 -?>
303 - <tr>
304 - <td></td>
305 - <td class="mw-submit">
306 - <input type='submit' name="wpCreateaccount" id="wpCreateaccount"
307 - tabindex="<?php echo $tabIndex++; ?>"
308 - value="<?php $this->msg('createaccount') ?>" />
309 - <?php if( $this->data['createemail'] ) { ?>
310 - <input type='submit' name="wpCreateaccountMail" id="wpCreateaccountMail"
311 - tabindex="<?php echo $tabIndex++; ?>"
312 - value="<?php $this->msg('createaccountmail') ?>" />
313 - <?php } ?>
314 - </td>
315 - </tr>
316 - </table>
317 -<?php if( @$this->haveData( 'uselang' ) ) { ?><input type="hidden" name="uselang" value="<?php $this->text( 'uselang' ); ?>" /><?php } ?>
318 -</form>
319 -</div>
320 -<div id="signupend"><?php $this->msgWiki( 'signupend' ); ?></div>
321 -<?php
322 -
323 - }
324 -}
Index: trunk/phase3/languages/messages/MessagesQqq.php
@@ -596,7 +596,7 @@
597597 {{Identical|Log in}}",
598598 '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.",
599599 'loginprompt' => 'A small notice in the log in form.',
600 -'userlogin' => 'Name of special page [[Special:UserLogin]] where a user can log in or click to create a user account.',
 600+'userlogin' => 'Name of special page [[Special:UserLogin]] where a user can log in.',
601601 'logout' => '{{Identical|Log out}}',
602602 'userlogout' => '{{Identical|Log out}}',
603603 '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.
@@ -604,7 +604,7 @@
605605 {{Identical|Not logged in}}',
606606 'nologin' => 'A message shown in the log in form. $1 is a link to the account creation form, and the text of it is "[[MediaWiki:Nologinlink/{{SUBPAGENAME}}|{{int:nologinlink}}]]".',
607607 'nologinlink' => 'Text of the link to the account creation form. Before that link, the message [[MediaWiki:Nologin/{{SUBPAGENAME}}]] appears.',
608 -'createaccount' => 'Used on the submit button in the form where you register a new account.',
 608+'createaccount' => 'The title of Special:CreateAccount, where users can register a new account. Used on Special:SpecialPages, and also on the submit button in the form where you register a new account.',
609609 'gotaccount' => 'A message shown in the account creation form. $1 is a link to the log in form, and the text of it is "[[MediaWiki:Gotaccountlink/{{SUBPAGENAME}}|{{int:gotaccountlink}}]]".',
610610 'gotaccountlink' => 'Text of the link to the log in form. Before that link, the message [[MediaWiki:Gotaccount/{{SUBPAGENAME}}]] appears.
611611
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -1042,7 +1042,7 @@
10431043 'login' => 'Log in',
10441044 'nav-login-createaccount' => 'Log in / create account',
10451045 'loginprompt' => 'You must have cookies enabled to log in to {{SITENAME}}.',
1046 -'userlogin' => 'Log in / create account',
 1046+'userlogin' => 'Log in',
10471047 'logout' => 'Log out',
10481048 'userlogout' => 'Log out',
10491049 'notloggedin' => 'Not logged in',
Index: trunk/phase3/RELEASE-NOTES
@@ -82,6 +82,11 @@
8383 * (bug 20489) $wgIllegalFileChars added to override the default list of illegal characters in file names.
8484 * (bug 19646) $wgImgAuthDetails added to display reason access to uploaded file was denied to users(img_auth only)
8585 * (bug 19646) $wgImgAuthPublicTest added to test to see if img_auth set up correctly (img_auth only)
 86+* UserLoginForm and UserCreateForm hooks, and AuthPlugin::modifyUITemplate, now receive a
 87+ SpecialPage subclass instead of a QuickTemplate subclass. Hence there is no
 88+ $template->set(), etc. The hook has access to most of the stuff that will go into the
 89+ Login/Create form; see the documentation on HTMLForm for syntax for extra fields.
 90+* LoginForm class is deprecated, its state constants are now in the Login class.
8691
8792 === New features in 1.16 ===
8893
Index: trunk/extensions/AntiSpoof/AntiSpoof.php
@@ -103,15 +103,17 @@
104104 /**
105105 * Set the ignore spoof thingie
106106 */
107 -function asUserCreateFormHook( &$template ) {
 107+function asUserCreateFormHook( &$sp ) {
108108 global $wgRequest, $wgAntiSpoofAccounts, $wgUser;
109109
110110 wfLoadExtensionMessages( 'AntiSpoof' );
111111
112112 if( $wgAntiSpoofAccounts && $wgUser->isAllowed( 'override-antispoof' ) )
113 - $template->addInputItem( 'wpIgnoreAntiSpoof',
114 - $wgRequest->getCheck('wpIgnoreAntiSpoof'),
115 - 'checkbox', 'antispoof-ignore' );
 113+ $sp->mFormFields['IgnoreAntiSpoof'] = array(
 114+ 'type' => 'check',
 115+ 'default' => $wgRequest->getCheck('wpIgnoreAntiSpoof'),
 116+ 'label-message' => 'antispoof-ignore'
 117+ );
116118 return true;
117119 }
118120
Index: trunk/extensions/ConfirmEdit/ConfirmEdit_body.php
@@ -39,8 +39,8 @@
4040 return self::getInstance()->triggerUserLogin( $user, $password, $retval );
4141 }
4242
43 - static function injectUserLogin( &$template ) {
44 - return self::getInstance()->injectUserLogin( $template );
 43+ static function injectUserLogin( &$sp ) {
 44+ return self::getInstance()->injectUserLogin( $sp );
4545 }
4646
4747 static function confirmUserLogin( $u, $pass, &$retval ) {
@@ -144,18 +144,18 @@
145145 * @param SimpleTemplate $template
146146 * @return bool true to keep running callbacks
147147 */
148 - function injectUserCreate( &$template ) {
 148+ function injectUserCreate( &$sp ) {
149149 global $wgCaptchaTriggers, $wgOut, $wgUser;
150150 if ( $wgCaptchaTriggers['createaccount'] ) {
151151 if ( $wgUser->isAllowed( 'skipcaptcha' ) ) {
152152 wfDebug( "ConfirmEdit: user group allows skipping captcha on account creation\n" );
153153 return true;
154154 }
155 - $template->set( 'header',
 155+ $sp->mFormHeader .=
156156 "<div class='captcha'>" .
157157 $wgOut->parse( $this->getMessage( 'createaccount' ) ) .
158158 $this->getForm() .
159 - "</div>\n" );
 159+ "</div>\n";
160160 }
161161 return true;
162162 }
@@ -163,18 +163,17 @@
164164 /**
165165 * Inject a captcha into the user login form after a failed
166166 * password attempt as a speedbump for mass attacks.
167 - * @fixme if multiple thingies insert a header, could break
168167 * @param SimpleTemplate $template
169168 * @return bool true to keep running callbacks
170169 */
171 - function injectUserLogin( &$template ) {
 170+ function injectUserLogin( &$sp ) {
172171 if ( $this->isBadLoginTriggered() ) {
173172 global $wgOut;
174 - $template->set( 'header',
 173+ $sp->mFormHeader .=
175174 "<div class='captcha'>" .
176175 $wgOut->parse( $this->getMessage( 'badlogin' ) ) .
177176 $this->getForm() .
178 - "</div>\n" );
 177+ "</div>\n";
179178 }
180179 return true;
181180 }
@@ -190,7 +189,7 @@
191190 */
192191 function triggerUserLogin( $user, $password, $retval ) {
193192 global $wgCaptchaTriggers, $wgCaptchaBadLoginExpiration, $wgMemc;
194 - if ( $retval == LoginForm::WRONG_PASS && $wgCaptchaTriggers['badlogin'] ) {
 193+ if ( $retval == Login::WRONG_PASS && $wgCaptchaTriggers['badlogin'] ) {
195194 $key = $this->badLoginKey();
196195 $count = $wgMemc->get( $key );
197196 if ( !$count ) {
@@ -557,7 +556,7 @@
558557 if ( !$this->passCaptcha() ) {
559558 $message = wfMsg( 'captcha-badlogin-fail' );
560559 // Emulate a bad-password return to confuse the shit out of attackers
561 - $retval = LoginForm::WRONG_PASS;
 560+ $retval = Login::WRONG_PASS;
562561 return false;
563562 }
564563 }
Index: trunk/extensions/LdapAuthentication/LdapAuthentication.php
@@ -376,38 +376,43 @@
377377 /**
378378 * Modify options in the login template.
379379 *
380 - * @param UserLoginTemplate $template
 380+ * @param $sp SpecialUserlogin or SpecialCreateAccount object
381381 * @access public
382382 */
383 - function modifyUITemplate( &$template ) {
 383+ public function modifyUITemplate( &$sp ) {
384384 global $wgLDAPDomainNames, $wgLDAPUseLocal;
385385 global $wgLDAPAddLDAPUsers;
386386 global $wgLDAPAutoAuthDomain;
387387
388388 $this->printDebug( "Entering modifyUITemplate", NONSENSITIVE );
 389+
 390+ if( $sp instanceof SpecialUserlogin ){
389391
390 - if ( !isset( $wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) || !$wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) {
391 - $template->set( 'create', false );
392 - }
 392+ $tempDomArr = $wgLDAPDomainNames;
 393+ if( $wgLDAPUseLocal ) {
 394+ $this->printDebug( "Allowing the local domain, adding it to the list.", NONSENSITIVE );
 395+ array_push( $tempDomArr, 'local' );
 396+ }
 397+
 398+ if( isset( $wgLDAPAutoAuthDomain ) ) {
 399+ $this->printDebug( "Allowing auto-authentication login, removing the domain from the list.", NONSENSITIVE );
 400+
 401+ //There is no reason for people to log in directly to the wiki if the are using an
 402+ //auto-authentication domain. If they try to, they are probably up to something fishy.
 403+ unset( $tempDomArr[array_search( $wgLDAPAutoAuthDomain, $tempDomArr )] );
 404+ }
 405+
 406+ $sp->mDomains = $tempDomArr;
 407+
 408+ } else { # SpecialCreateAccount
393409
394 - $template->set( 'usedomain', true );
395 - $template->set( 'useemail', false );
396 -
397 - $tempDomArr = $wgLDAPDomainNames;
398 - if ( $wgLDAPUseLocal ) {
399 - $this->printDebug( "Allowing the local domain, adding it to the list.", NONSENSITIVE );
400 - array_push( $tempDomArr, 'local' );
 410+ # FIXME: This achieves nothing; what is the purpose?
 411+ if( !isset( $wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) || !$wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) {
 412+ $sp->set( 'create', false );
 413+ }
 414+
 415+ $sp->mUseEmail = false;
401416 }
402 -
403 - if ( isset( $wgLDAPAutoAuthDomain ) ) {
404 - $this->printDebug( "Allowing auto-authentication login, removing the domain from the list.", NONSENSITIVE );
405 -
406 - //There is no reason for people to log in directly to the wiki if the are using an
407 - //auto-authentication domain. If they try to, they are probably up to something fishy.
408 - unset( $tempDomArr[array_search( $wgLDAPAutoAuthDomain, $tempDomArr )] );
409 - }
410 -
411 - $template->set( 'domainnames', $tempDomArr );
412417 }
413418
414419 /**
Index: trunk/extensions/WikimediaIncubator/CreateAccountTestWiki.php
@@ -4,14 +4,14 @@
55 * This can be used to work with my toolserver project (http://toolserver.org/~robin/?tool=proposewiki), so users don't *have* to change their preferences (automatically is always better :p)
66 */
77 class AutoTestWiki {
8 - function onUserCreateForm( $template ) {
 8+ function onUserCreateForm( $sp ) {
99 global $wgRequest;
1010 $projectvalue = strtolower( $wgRequest->getVal( 'testwikiproject', '' ) );
1111 $codevalue = strtolower( $wgRequest->getVal( 'testwikicode', '' ) );
1212 if ( preg_match( '/[a-z][a-z][a-z]?/', $codevalue ) && in_array( $projectvalue, array( 'p', 'b', 't', 'q', 'n' ) ) ) {
13 - $template->set( 'header', '<input type="hidden" name="testwiki-project" value="' . $projectvalue . '" />
 13+ $sp->mFormHeader .= '<input type="hidden" name="testwiki-project" value="' . $projectvalue . '" />
1414 <input type="hidden" name="testwiki-code" value="' . $codevalue . '" />
15 - ' );
 15+ ';
1616 }
1717 return true;
1818 }
Index: trunk/extensions/ConfirmAccount/SpecialConfirmAccount.php
@@ -125,11 +125,11 @@
126126 $wgExtensionMessagesFiles['ConfirmAccount'] = $dir . 'ConfirmAccount.i18n.php';
127127 $wgExtensionAliasesFiles['ConfirmAccount'] = $dir . 'ConfirmAccount.alias.php';
128128
129 -function efAddRequestLoginText( &$template ) {
 129+function efAddRequestLoginText( &$sp ) {
130130 global $wgUser;
131131 wfLoadExtensionMessages( 'ConfirmAccount' );
132132 if ( !$wgUser->isAllowed( 'createaccount' ) ) {
133 - $template->set( 'header', wfMsgExt( 'requestaccount-loginnotice', array( 'parse' ) ) );
 133+ $sp->mFormHeader .= wfMsgExt( 'requestaccount-loginnotice', array( 'parse' ) );
134134 }
135135 return true;
136136 }

Follow-up revisions

RevisionCommit summaryAuthorDate
r55991Followup to r55990 - Remove debugging hack :(happy-melon19:07, 7 September 2009
r55994Followup to r55990 - rename method to avoid PHP Strict warningshappy-melon19:20, 7 September 2009
r55997Follow-up to r55990, per comments:...happy-melon19:52, 7 September 2009
r56058Revert broken rewrite of login forms:...brion18:40, 8 September 2009

Comments

#Comment by Nikerabbit (talk | contribs)   19:43, 7 September 2009

Something before I forget it.

+               if ( strval( $block_reason ) ===  ) {
+                       $block_reason = wfMsg( 'blockednoreason' );
+               }
+               $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $block_reason, $blocker );

Double parsing

+               if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {

!== ?

+                       $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $user->getName() ) );

addWikiMsg?

+               $this->mFormFields['RealName']['help'] = wfMsg( 'prefs-help-realname' );

isn't there help-message?

+                       Html::rawElement(
+                               'div',
+                               array( 'id' => 'loginstart' ),
+                               wfMsgExt( 'loginstart', array( 'parseinline' ) )
+                       ) .

$wgOut->wrapWikiMsg?

+                       . Xml::hidden( 'returnto', $this->mReturnTo )

Is Html::hidden safe to use yet?

#Comment by Raymond (talk | contribs)   16:34, 8 September 2009

Seen on translatewiki at creating an account:

[08-Sep-2009 16:30:19] PHP Fatal error: Using $this when not in object context in /var/www/w/includes/specials/SpecialUserlogin.php on line 383

Account create because I got for this testcreating a confirmation email

Status & tagging log