r56937 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r56936‎ | r56937 | r56938 >
Date:00:49, 26 September 2009
Author:brion
Status:ok (Comments)
Tags:
Comment:
Revert broken rewrite of login system; totally broken.
* Login doesn't attach to session properly, so can't stay logged in!
* Password field shown in plaintext!

If it just DOESN'T WORK please keep it on a work branch, don't put it in trunk!

Reverted:
r56682
r56683
r56684
r56686
r56688
r56696
r56699
r56702
r56703
r56704
r56782
r56896
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/Lockout (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/ExternalUser.php (modified) (history)
  • /trunk/phase3/includes/HTMLForm.php (modified) (history)
  • /trunk/phase3/includes/Login.php (deleted) (history)
  • /trunk/phase3/includes/SpecialPage.php (modified) (history)
  • /trunk/phase3/includes/api/ApiLogin.php (modified) (history)
  • /trunk/phase3/includes/parser/Parser.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialCreateAccount.php (deleted) (history)
  • /trunk/phase3/includes/specials/SpecialResetpass.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialUserlogin.php (modified) (history)
  • /trunk/phase3/includes/templates/Userlogin.php (added) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /trunk/phase3/languages/messages/MessagesQqq.php (modified) (history)
  • /trunk/phase3/maintenance/language/messages.inc (modified) (history)
  • /trunk/phase3/skins/common/shared.css (modified) (history)
  • /trunk/phase3/skins/monobook/main.css (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/language/messages.inc
@@ -141,8 +141,6 @@
142142 'category-file-count',
143143 'category-file-count-limited',
144144 'listingcontinuesabbrev',
145 - 'index-category',
146 - 'noindex-category',
147145 ),
148146 'mainpage' => array(
149147 'linkprefix',
Index: trunk/phase3/skins/monobook/main.css
@@ -1015,10 +1015,59 @@
10161016 margin-top: 2em;
10171017 }
10181018
 1019+div#userloginForm form,
 1020+div#userlogin form#userlogin2 {
 1021+ margin: 0 3em 1em 0;
 1022+ border: 1px solid #aaa;
 1023+ clear: both;
 1024+ padding: 1.5em 2em;
 1025+ background-color: #f9f9f9;
 1026+ float: left;
 1027+}
 1028+.rtl div#userloginForm form,
 1029+.rtl div#userlogin form#userlogin2 {
 1030+ float: right;
 1031+}
 1032+
 1033+div#userloginForm table,
 1034+div#userlogin form#userlogin2 table {
 1035+ background-color: #f9f9f9;
 1036+}
 1037+
 1038+div#userloginForm h2,
 1039+div#userlogin form#userlogin2 h2 {
 1040+ padding-top: 0;
 1041+}
 1042+
 1043+div#userlogin .captcha,
 1044+div#userloginForm .captcha {
 1045+ border: 1px solid #bbb;
 1046+ padding: 1.5em 2em;
 1047+ background-color: white;
 1048+}
 1049+
 1050+#loginend, #signupend {
 1051+ clear: both;
 1052+}
 1053+
10191054 #userloginprompt, #languagelinks {
10201055 font-size: 85%;
10211056 }
10221057
 1058+#login-sectiontip {
 1059+ font-size: 85%;
 1060+ line-height: 1.2;
 1061+ padding-top: 2em;
 1062+}
 1063+
 1064+#userlogin .loginText, #userlogin .loginPassword {
 1065+ width: 12em;
 1066+}
 1067+
 1068+#userloginlink a, #wpLoginattempt, #wpCreateaccount {
 1069+ font-weight: bold;
 1070+}
 1071+
10231072 /*
10241073 ** IE/Mac fixes, hope to find a validating way to move this
10251074 ** to a separate stylesheet. This would work but doesn't validate:
Index: trunk/phase3/skins/common/shared.css
@@ -802,5 +802,3 @@
803803 position: relative;
804804 top: -16px;
805805 }
806 -
807 -#wpLoginAttempt, #wpCreateaccount { margin-right:0; }
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 Login class constant to return from authenticateUserData();
249 - default is Login::ABORTED. Note that the client may be using
 248+&$retval: a LoginForm class constant to return from authenticateUserData();
 249+ default is LoginForm::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)
@@ -950,7 +950,7 @@
951951 succeeded or failed. No return data is accepted; this hook is for auditing only.
952952 $user: the User object being authenticated against
953953 $password: the password being submitted and found wanting
954 -$retval: a Login class constant with authenticateUserData() return
 954+$retval: a LoginForm class constant with authenticateUserData() return
955955 value (SUCCESS, WRONG_PASS, etc)
956956
957957 'LogLine': Processes a single log entry on Special:Log
@@ -1532,7 +1532,7 @@
15331533 determine if the password was valid
15341534
15351535 'UserCreateForm': change to manipulate the login form
1536 -$sp: SpecialCreateAccount instance
 1536+$template: SimpleTemplate instance for the form
15371537
15381538 'UserCryptPassword': called when hashing a password, return false to implement
15391539 your own hashing method
@@ -1602,7 +1602,7 @@
16031603 $inject_html: Any HTML to inject after the "logged in" message.
16041604
16051605 'UserLoginForm': change to manipulate the login form
1606 -$sp: SpecialCreateAccount instance
 1606+$template: SimpleTemplate instance for the form
16071607
16081608 'UserLoginMailPassword': Block users from emailing passwords
16091609 $name: the username to email the password of.
Index: trunk/phase3/includes/Login.php
@@ -1,596 +0,0 @@
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 THROTTLED = 10;
19 - const FAILED = 11;
20 - const READ_ONLY = 12;
21 -
22 - const MAIL_PASSCHANGE_FORBIDDEN = 21;
23 - const MAIL_BLOCKED = 22;
24 - const MAIL_PING_THROTTLED = 23;
25 - const MAIL_PASS_THROTTLED = 24;
26 - const MAIL_EMPTY_EMAIL = 25;
27 - const MAIL_BAD_IP = 26;
28 - const MAIL_ERROR = 27;
29 -
30 - const CREATE_BLOCKED = 40;
31 - const CREATE_EXISTS = 41;
32 - const CREATE_SORBS = 42;
33 - const CREATE_BADDOMAIN = 43;
34 - const CREATE_BADNAME = 44;
35 - const CREATE_BADPASS = 45;
36 - const CREATE_NEEDEMAIL = 46;
37 - const CREATE_BADEMAIL = 47;
38 -
39 - protected $mName;
40 - protected $mPassword;
41 - public $mRemember; # 0 or 1
42 - public $mEmail;
43 - public $mDomain;
44 - public $mRealname;
45 -
46 - private $mExtUser = null;
47 -
48 - public $mUser;
49 -
50 - public $mLoginResult = '';
51 - public $mMailResult = '';
52 - public $mCreateResult = '';
53 -
54 - /**
55 - * Constructor
56 - * @param WebRequest $request A WebRequest object passed by reference.
57 - * uses $wgRequest if not given.
58 - */
59 - public function __construct( &$request=null ) {
60 - global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
61 - if( !$request ) $request = &$wgRequest;
62 -
63 - $this->mName = $request->getText( 'wpName' );
64 - $this->mPassword = $request->getText( 'wpPassword' );
65 - $this->mDomain = $request->getText( 'wpDomain' );
66 - $this->mRemember = $request->getCheck( 'wpRemember' ) ? 1 : 0;
67 -
68 - if( $wgEnableEmail ) {
69 - $this->mEmail = $request->getText( 'wpEmail' );
70 - } else {
71 - $this->mEmail = '';
72 - }
73 - if( !in_array( 'realname', $wgHiddenPrefs ) ) {
74 - $this->mRealName = $request->getText( 'wpRealName' );
75 - } else {
76 - $this->mRealName = '';
77 - }
78 -
79 - if( !$wgAuth->validDomain( $this->mDomain ) ) {
80 - $this->mDomain = 'invaliddomain';
81 - }
82 - $wgAuth->setDomain( $this->mDomain );
83 -
84 - # Load the user, if they exist in the local database.
85 - $this->mUser = User::newFromName( trim( $this->mName ), 'usable' );
86 - }
87 -
88 - /**
89 - * Having initialised the Login object with (at least) the wpName
90 - * and wpPassword pair, attempt to authenticate the user and log
91 - * them into the wiki. Authentication may come from the local
92 - * user database, or from an AuthPlugin- or ExternalUser-based
93 - * foreign database; in the latter case, a local user record may
94 - * or may not be created and initialised.
95 - * @return a Login class constant representing the status.
96 - */
97 - public function attemptLogin(){
98 - global $wgUser;
99 -
100 - $code = $this->authenticateUserData();
101 - if( $code != self::SUCCESS ){
102 - return $code;
103 - }
104 -
105 - # Log the user in and remember them if they asked for that.
106 - if( (bool)$this->mRemember != (bool)$wgUser->getOption( 'rememberpassword' ) ) {
107 - $wgUser->setOption( 'rememberpassword', $this->mRemember ? 1 : 0 );
108 - $wgUser->saveSettings();
109 - } else {
110 - $wgUser->invalidateCache();
111 - }
112 - $wgUser->setCookies();
113 -
114 - # Reset the password throttle
115 - $key = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
116 - global $wgMemc;
117 - $wgMemc->delete( $key );
118 -
119 - wfRunHooks( 'UserLoginComplete', array( &$wgUser, &$this->mLoginResult ) );
120 -
121 - return self::SUCCESS;
122 - }
123 -
124 - /**
125 - * Check whether there is an external authentication mechanism from
126 - * which we can automatically authenticate the user and create a
127 - * local account for them.
128 - * @return integer Status code. Login::SUCCESS == clear to proceed
129 - * with user creation.
130 - */
131 - protected function canAutoCreate() {
132 - global $wgAuth, $wgUser, $wgAutocreatePolicy;
133 -
134 - if( $wgUser->isBlockedFromCreateAccount() ) {
135 - wfDebug( __METHOD__.": user is blocked from account creation\n" );
136 - return self::CREATE_BLOCKED;
137 - }
138 -
139 - # If the external authentication plugin allows it, automatically
140 - # create a new account for users that are externally defined but
141 - # have not yet logged in.
142 - if( $this->mExtUser ) {
143 - # mExtUser is neither null nor false, so use the new
144 - # ExternalAuth system.
145 - if( $wgAutocreatePolicy == 'never' ) {
146 - return self::NOT_EXISTS;
147 - }
148 - if( !$this->mExtUser->authenticate( $this->mPassword ) ) {
149 - return self::WRONG_PLUGIN_PASS;
150 - }
151 - } else {
152 - # Old AuthPlugin.
153 - if( !$wgAuth->autoCreate() ) {
154 - return self::NOT_EXISTS;
155 - }
156 - if( !$wgAuth->userExists( $this->mUser->getName() ) ) {
157 - wfDebug( __METHOD__.": user does not exist\n" );
158 - return self::NOT_EXISTS;
159 - }
160 - if( !$wgAuth->authenticate( $this->mUser->getName(), $this->mPassword ) ) {
161 - wfDebug( __METHOD__.": \$wgAuth->authenticate() returned false, aborting\n" );
162 - return self::WRONG_PLUGIN_PASS;
163 - }
164 - }
165 -
166 - return self::SUCCESS;
167 - }
168 -
169 - /**
170 - * Internally authenticate the login request.
171 - *
172 - * This may create a local account as a side effect if the
173 - * authentication plugin allows transparent local account
174 - * creation.
175 - */
176 - protected function authenticateUserData() {
177 - global $wgUser, $wgAuth;
178 -
179 - if ( '' == $this->mName ) {
180 - $this->mLoginResult = 'noname';
181 - return self::NO_NAME;
182 - }
183 -
184 - global $wgPasswordAttemptThrottle;
185 - $throttleCount = 0;
186 - if ( is_array( $wgPasswordAttemptThrottle ) ) {
187 - $throttleKey = wfMemcKey( 'password-throttle', wfGetIP(), md5( $this->mName ) );
188 - $count = $wgPasswordAttemptThrottle['count'];
189 - $period = $wgPasswordAttemptThrottle['seconds'];
190 -
191 - global $wgMemc;
192 - $throttleCount = $wgMemc->get( $throttleKey );
193 - if ( !$throttleCount ) {
194 - $wgMemc->add( $throttleKey, 1, $period ); # Start counter
195 - } else if ( $throttleCount < $count ) {
196 - $wgMemc->incr($throttleKey);
197 - } else if ( $throttleCount >= $count ) {
198 - $this->mLoginResult = 'login-throttled';
199 - return self::THROTTLED;
200 - }
201 - }
202 -
203 - # Unstub $wgUser now, and check to see if we're logging in as the same
204 - # name. As well as the obvious, unstubbing $wgUser (say by calling
205 - # getName()) calls the UserLoadFromSession hook, which potentially
206 - # creates the user in the database. Until we load $wgUser, checking
207 - # for user existence using User::newFromName($name)->getId() below
208 - # will effectively be using stale data.
209 - if ( $wgUser->getName() === $this->mName ) {
210 - wfDebug( __METHOD__.": already logged in as {$this->mName}\n" );
211 - return self::SUCCESS;
212 - }
213 -
214 - $this->mExtUser = ExternalUser::newFromName( $this->mName );
215 -
216 - # If the given username produces a valid ExternalUser, which is
217 - # linked to an existing local user, use that, regardless of
218 - # whether the username matches up.
219 - if( $this->mExtUser ){
220 - $user = $this->mExtUser->getLocalUser();
221 - if( $user instanceof User ){
222 - $this->mUser = $user;
223 - }
224 - }
225 -
226 - # TODO: Allow some magic here for invalid external names, e.g., let the
227 - # user choose a different wiki name.
228 - if( is_null( $this->mUser ) || !User::isUsableName( $this->mUser->getName() ) ) {
229 - return self::ILLEGAL;
230 - }
231 -
232 - # If the user doesn't exist in the local database, our only chance
233 - # is for an external auth plugin to autocreate the local user first.
234 - if ( $this->mUser->getID() == 0 ) {
235 - if ( $this->canAutoCreate() == self::SUCCESS ) {
236 -
237 - $isAutoCreated = true;
238 - wfDebug( __METHOD__.": creating account\n" );
239 -
240 - if( !wfRunHooks( 'AbortNewAccountAuto', array( $this->mUser, &$this->mCreateResult ) ) ) {
241 - wfDebug( __METHOD__ . ": a hook blocked creation\n" );
242 - return self::ABORTED;
243 - }
244 -
245 - $result = $this->initUser( true );
246 - if( $result !== self::SUCCESS ){
247 - return $result;
248 - }
249 -
250 - } else {
251 - return $this->canAutoCreate();
252 - }
253 - } else {
254 - $isAutoCreated = false;
255 - $this->mUser->load();
256 - }
257 -
258 - # Give general extensions, such as a captcha, a chance to abort logins
259 - if( !wfRunHooks( 'AbortLogin', array( $this->mUser, $this->mPassword, &$this->mLoginResult ) ) ) {
260 - return self::ABORTED;
261 - }
262 -
263 - if( !$this->mUser->checkPassword( $this->mPassword ) ) {
264 - if( $this->mUser->checkTemporaryPassword( $this->mPassword ) ) {
265 - # The e-mailed temporary password should not be used for actual
266 - # logins; that's a very sloppy habit, and insecure if an
267 - # attacker has a few seconds to click "search" on someone's
268 - # open mail reader.
269 - #
270 - # Allow it to be used only to reset the password a single time
271 - # to a new value, which won't be in the user's e-mail archives
272 - #
273 - # For backwards compatibility, we'll still recognize it at the
274 - # login form to minimize surprises for people who have been
275 - # logging in with a temporary password for some time.
276 - #
277 - # As a side-effect, we can authenticate the user's e-mail ad-
278 - # dress if it's not already done, since the temporary password
279 - # was sent via e-mail.
280 - if( !$this->mUser->isEmailConfirmed() ) {
281 - $this->mUser->confirmEmail();
282 - $this->mUser->saveSettings();
283 - }
284 -
285 - # At this point we just return an appropriate code/ indicating
286 - # that the UI should show a password reset form; bot interfaces
287 - # etc will probably just fail cleanly here.
288 - $retval = self::RESET_PASS;
289 - } else {
290 - if( $this->mPassword === '' ){
291 - $retval = self::EMPTY_PASS;
292 - $this->mLoginResult = 'wrongpasswordempty';
293 - } else {
294 - $retval = self::WRONG_PASS;
295 - $this->mLoginResult = 'wrongpassword';
296 - }
297 - }
298 - } else {
299 - $wgAuth->updateUser( $this->mUser );
300 - $wgUser = $this->mUser;
301 -
302 - # Reset throttle after a successful login
303 - if( $throttleCount ) {
304 - $wgMemc->delete( $throttleKey );
305 - }
306 -
307 - if( $isAutoCreated ) {
308 - # Must be run after $wgUser is set, for correct new user log
309 - wfRunHooks( 'AuthPluginAutoCreate', array( $wgUser ) );
310 - }
311 -
312 - $retval = self::SUCCESS;
313 - }
314 - wfRunHooks( 'LoginAuthenticateAudit', array( $this->mUser, $this->mPassword, $retval ) );
315 - return $retval;
316 - }
317 -
318 - /**
319 - * Actually add a user to the database.
320 - * Give it a User object that has been initialised with a name.
321 - *
322 - * @param $autocreate Bool is this is an autocreation from an external
323 - * authentication database?
324 - * @param $byEmail Bool is this request going to be handled by sending
325 - * the password by email?
326 - * @return Bool whether creation was successful (should only fail for
327 - * Db errors etc).
328 - */
329 - protected function initUser( $autocreate=false, $byEmail=false ) {
330 - global $wgAuth, $wgUser;
331 -
332 - $fields = array(
333 - 'name' => User::getCanonicalName( $this->mName ),
334 - 'password' => $byEmail ? null : User::crypt( $this->mPassword ),
335 - 'email' => $this->mEmail,
336 - 'options' => array(
337 - 'rememberpassword' => $this->mRemember ? 1 : 0,
338 - ),
339 - );
340 -
341 - $this->mUser = User::createNew( $this->mName, $fields );
342 -
343 - if( $this->mUser === null ){
344 - return null;
345 - }
346 -
347 - # Let old AuthPlugins play with the user
348 - $wgAuth->initUser( $this->mUser, $autocreate );
349 -
350 - # Or new ExternalUser plugins
351 - if( $this->mExtUser ) {
352 - $this->mExtUser->link( $this->mUser->getId() );
353 - $email = $this->mExtUser->getPref( 'emailaddress' );
354 - if( $email && !$this->mEmail ) {
355 - $this->mUser->setEmail( $email );
356 - }
357 - }
358 -
359 - # Update user count and newuser logs
360 - $ssUpdate = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
361 - $ssUpdate->doUpdate();
362 - if( $autocreate )
363 - $this->mUser->addNewUserLogEntryAutoCreate();
364 - elseif( $wgUser->isAnon() )
365 - # Avoid spamming IP addresses all over the newuser log
366 - $this->mUser->addNewUserLogEntry( $this->mUser, $byEmail );
367 - else
368 - $this->mUser->addNewUserLogEntry( $wgUser, $byEmail );
369 -
370 - # Run hooks
371 - wfRunHooks( 'AddNewAccount', array( $this->mUser ) );
372 -
373 - return true;
374 - }
375 -
376 - /**
377 - * Entry point to create a new local account from user-supplied
378 - * data loaded from the WebRequest. We handle initialising the
379 - * email here because it's needed for some backend things; frontend
380 - * interfaces calling this should handle recording things like
381 - * preference options
382 - * @param $byEmail Bool whether to email the user their new password
383 - * @return Status code; Login::SUCCESS == the user was successfully created
384 - */
385 - public function attemptCreation( $byEmail=false ) {
386 - global $wgUser, $wgOut;
387 - global $wgEnableSorbs, $wgProxyWhitelist;
388 - global $wgMemc, $wgAccountCreationThrottle;
389 - global $wgAuth;
390 - global $wgEmailAuthentication, $wgEmailConfirmToEdit;
391 -
392 - if( wfReadOnly() )
393 - return self::READ_ONLY;
394 -
395 - # If the user passes an invalid domain, something is fishy
396 - if( !$wgAuth->validDomain( $this->mDomain ) ) {
397 - $this->mCreateResult = 'wrongpassword';
398 - return self::CREATE_BADDOMAIN;
399 - }
400 -
401 - # If we are not allowing users to login locally, we should be checking
402 - # to see if the user is actually able to authenticate to the authenti-
403 - # cation server before they create an account (otherwise, they can
404 - # create a local account and login as any domain user). We only need
405 - # to check this for domains that aren't local.
406 - if( !in_array( $this->mDomain, array( 'local', '' ) )
407 - && !$wgAuth->canCreateAccounts()
408 - && ( !$wgAuth->userExists( $this->mUsername )
409 - || !$wgAuth->authenticate( $this->mUsername, $this->mPassword )
410 - ) )
411 - {
412 - $this->mCreateResult = 'wrongpassword';
413 - return self::WRONG_PLUGIN_PASS;
414 - }
415 -
416 - $ip = wfGetIP();
417 - if ( $wgEnableSorbs && !in_array( $ip, $wgProxyWhitelist ) &&
418 - $wgUser->inSorbsBlacklist( $ip ) )
419 - {
420 - $this->mCreateResult = 'sorbs_create_account_reason';
421 - return self::CREATE_SORBS;
422 - }
423 -
424 - # Now create a dummy user ($user) and check if it is valid
425 - $name = trim( $this->mName );
426 - $user = User::newFromName( $name, 'creatable' );
427 - if ( is_null( $user ) ) {
428 - $this->mCreateResult = 'noname';
429 - return self::CREATE_BADNAME;
430 - }
431 -
432 - if ( $this->mUser->idForName() != 0 ) {
433 - $this->mCreateResult = 'userexists';
434 - return self::CREATE_EXISTS;
435 - }
436 -
437 - # Check that the password is acceptable, if we're actually
438 - # going to use it
439 - if( !$byEmail ){
440 - $valid = $this->mUser->isValidPassword( $this->mPassword );
441 - if ( $valid !== true ) {
442 - $this->mCreateResult = $valid;
443 - return self::CREATE_BADPASS;
444 - }
445 - }
446 -
447 - # if you need a confirmed email address to edit, then obviously you
448 - # need an email address. Equally if we're going to send the password to it.
449 - if ( $wgEmailConfirmToEdit && empty( $this->mEmail ) || $byEmail ) {
450 - $this->mCreateResult = 'noemailcreate';
451 - return self::CREATE_NEEDEMAIL;
452 - }
453 -
454 - if( !empty( $this->mEmail ) && !User::isValidEmailAddr( $this->mEmail ) ) {
455 - $this->mCreateResult = 'invalidemailaddress';
456 - return self::CREATE_BADEMAIL;
457 - }
458 -
459 - # Set some additional data so the AbortNewAccount hook can be used for
460 - # more than just username validation
461 - $this->mUser->setEmail( $this->mEmail );
462 - $this->mUser->setRealName( $this->mRealName );
463 -
464 - if( !wfRunHooks( 'AbortNewAccount', array( $this->mUser, &$this->mCreateResult ) ) ) {
465 - # Hook point to add extra creation throttles and blocks
466 - wfDebug( __METHOD__ . ": a hook blocked creation\n" );
467 - return self::ABORTED;
468 - }
469 -
470 - if ( $wgAccountCreationThrottle && $wgUser->isPingLimitable() ) {
471 - $key = wfMemcKey( 'acctcreate', 'ip', $ip );
472 - $value = $wgMemc->get( $key );
473 - if ( !$value ) {
474 - $wgMemc->set( $key, 0, 86400 );
475 - }
476 - if ( $value >= $wgAccountCreationThrottle ) {
477 - return self::THROTTLED;
478 - }
479 - $wgMemc->incr( $key );
480 - }
481 -
482 - # Since we're creating a new local user, give the external
483 - # database a chance to synchronise.
484 - if( !$wgAuth->addUser( $this->mUser, $this->mPassword, $this->mEmail, $this->mRealName ) ) {
485 - $this->mCreateResult = 'externaldberror';
486 - return self::ABORTED;
487 - }
488 -
489 - $result = $this->initUser( false, $byEmail );
490 - if( $result === null )
491 - # It's unlikely we'd get here without some exception
492 - # being thrown, but it's probably possible...
493 - return self::FAILED;
494 -
495 -
496 - # Send out an email message if needed
497 - if( $byEmail ){
498 - $this->mailPassword( 'createaccount-title', 'createaccount-text' );
499 - if( WikiError::isError( $this->mMailResult ) ){
500 - # FIXME: If the password email hasn't gone out,
501 - # then the account is inaccessible :(
502 - return self::MAIL_ERROR;
503 - } else {
504 - return self::SUCCESS;
505 - }
506 - } else {
507 - if( $wgEmailAuthentication && User::isValidEmailAddr( $this->mUser->getEmail() ) )
508 - {
509 - $this->mMailResult = $this->mUser->sendConfirmationMail();
510 - return WikiError::isError( $this->mMailResult )
511 - ? self::MAIL_ERROR
512 - : self::SUCCESS;
513 - }
514 - }
515 - return true;
516 - }
517 -
518 - /**
519 - * Email the user a new password, if appropriate to do so.
520 - * @param $text String message key
521 - * @param $title String message key
522 - * @return Status code
523 - */
524 - public function mailPassword( $text='passwordremindertext', $title='passwordremindertitle' ) {
525 - global $wgUser, $wgOut, $wgAuth, $wgServer, $wgScript, $wgNewPasswordExpiry;
526 -
527 - if( wfReadOnly() )
528 - return self::READ_ONLY;
529 -
530 - # If we let the email go out, it will take users to a form where
531 - # they are forced to change their password, so don't let us go
532 - # there if we don't want passwords changed.
533 - if( !$wgAuth->allowPasswordChange() )
534 - return self::MAIL_PASSCHANGE_FORBIDDEN;
535 -
536 - # Check against blocked IPs
537 - # FIXME: -- should we not?
538 - if( $wgUser->isBlocked() )
539 - return self::MAIL_BLOCKED;
540 -
541 - # Check for hooks
542 - if( !wfRunHooks( 'UserLoginMailPassword', array( $this->mName, &$this->mMailResult ) ) )
543 - return self::ABORTED;
544 -
545 - # Check against the rate limiter
546 - if( $wgUser->pingLimiter( 'mailpassword' ) )
547 - return self::MAIL_PING_THROTTLED;
548 -
549 - # Check for a valid name
550 - if ($this->mName === '' )
551 - return self::NO_NAME;
552 - $this->mUser = User::newFromName( $this->mName );
553 - if( is_null( $this->mUser ) )
554 - return self::NO_NAME;
555 -
556 - # And that the resulting user actually exists
557 - if ( $this->mUser->getId() === 0 )
558 - return self::NOT_EXISTS;
559 -
560 - # Check against password throttle
561 - if ( $this->mUser->isPasswordReminderThrottled() )
562 - return self::MAIL_PASS_THROTTLED;
563 -
564 - # User doesn't have email address set
565 - if ( $this->mUser->getEmail() === '' )
566 - return self::MAIL_EMPTY_EMAIL;
567 -
568 - # Don't send to people who are acting fishily by hiding their IP
569 - $ip = wfGetIP();
570 - if( !$ip )
571 - return self::MAIL_BAD_IP;
572 -
573 - # Let hooks do things with the data
574 - wfRunHooks( 'User::mailPasswordInternal', array(&$wgUser, &$ip, &$this->mUser) );
575 -
576 - $newpass = $this->mUser->randomPassword();
577 - $this->mUser->setNewpassword( $newpass, true );
578 - $this->mUser->saveSettings();
579 -
580 - $message = wfMsgExt( $text, array( 'parsemag' ), $ip, $this->mUser->getName(), $newpass,
581 - $wgServer . $wgScript, round( $wgNewPasswordExpiry / 86400 ) );
582 - $this->mMailResult = $this->mUser->sendMail( wfMsg( $title ), $message );
583 -
584 - if( WikiError::isError( $this->mMailResult ) ) {
585 - return self::MAIL_ERROR;
586 - } else {
587 - return self::SUCCESS;
588 - }
589 - }
590 -}
591 -
592 -/**
593 - * For backwards compatibility, mainly with the state constants, which
594 - * could be referred to in old extensions with the old class name.
595 - * @deprecated
596 - */
597 -class LoginForm extends Login {}
\ No newline at end of file
Index: trunk/phase3/includes/ExternalUser.php
@@ -285,22 +285,4 @@
286286 'eu_external_id' => $this->getId() ),
287287 __METHOD__ );
288288 }
289 -
290 - /**
291 - * Check whether this external user id is already linked with
292 - * a local user.
293 - * @return Mixed User if the account is linked, Null otherwise.
294 - */
295 - public final function getLocalUser(){
296 - $dbr = wfGetDb( DB_SLAVE );
297 - $row = $dbr->selectRow(
298 - 'external_user',
299 - '*',
300 - array( 'eu_external_id' => $this->getId() )
301 - );
302 - return $row
303 - ? User::newFromId( $row->eu_wiki_id )
304 - : null;
305 - }
306 -
307289 }
Index: trunk/phase3/includes/parser/Parser.php
@@ -3332,39 +3332,25 @@
33333333 }
33343334 if ( isset( $this->mDoubleUnderscores['hiddencat'] ) && $this->mTitle->getNamespace() == NS_CATEGORY ) {
33353335 $this->mOutput->setProperty( 'hiddencat', 'y' );
3336 - $this->addTrackingCategory( 'hidden-category-category' );
 3336+
 3337+ $containerCategory = Title::makeTitleSafe( NS_CATEGORY, wfMsgForContent( 'hidden-category-category' ) );
 3338+ if ( $containerCategory ) {
 3339+ $this->mOutput->addCategory( $containerCategory->getDBkey(), $this->getDefaultSort() );
 3340+ } else {
 3341+ wfDebug( __METHOD__.": [[MediaWiki:hidden-category-category]] is not a valid title!\n" );
 3342+ }
33373343 }
33383344 # (bug 8068) Allow control over whether robots index a page.
33393345 #
33403346 # FIXME (bug 14899): __INDEX__ always overrides __NOINDEX__ here! This
33413347 # is not desirable, the last one on the page should win.
3342 - if( isset( $this->mDoubleUnderscores['noindex'] ) && $this->mTitle->canUseNoindex() ) {
 3348+ if( isset( $this->mDoubleUnderscores['noindex'] ) ) {
33433349 $this->mOutput->setIndexPolicy( 'noindex' );
3344 - $this->addTrackingCategory( 'noindex-category' );
3345 - }
3346 - if( isset( $this->mDoubleUnderscores['index'] ) && $this->mTitle->canUseNoindex() ){
 3350+ } elseif( isset( $this->mDoubleUnderscores['index'] ) ) {
33473351 $this->mOutput->setIndexPolicy( 'index' );
3348 - $this->addTrackingCategory( 'index-category' );
33493352 }
33503353 wfProfileOut( __METHOD__ );
33513354 return $text;
3352 - }
3353 -
3354 - /**
3355 - * Add a tracking category, getting the title from a system message,
3356 - * or print a debug message if the title is invalid.
3357 - * @param $msg String message key
3358 - * @return Bool whether the addition was successful
3359 - */
3360 - protected function addTrackingCategory( $msg ){
3361 - $containerCategory = Title::makeTitleSafe( NS_CATEGORY, wfMsgForContent( $msg ) );
3362 - if ( $containerCategory ) {
3363 - $this->mOutput->addCategory( $containerCategory->getDBkey(), $this->getDefaultSort() );
3364 - return true;
3365 - } else {
3366 - wfDebug( __METHOD__.": [[MediaWiki:$msg]] is not a valid title!\n" );
3367 - return false;
3368 - }
33693355 }
33703356
33713357 /**
Index: trunk/phase3/includes/HTMLForm.php
@@ -1,54 +1,16 @@
22 <?php
33
4 -/**
5 - * Object handling generic submission, CSRF protection, layout and
6 - * other logic for UI forms. in a reusable manner.
7 - *
8 - * In order to generate the form, the HTMLForm object takes an array
9 - * structure detailing the form fields available. Each element of the
10 - * array is a basic property-list, including the type of field, the
11 - * label it is to be given in the form, callbacks for validation and
12 - * 'filtering', and other pertinent information.
13 - *
14 - * Field types are implemented as subclasses of the generic HTMLFormField
15 - * object, and typically implement at least getInputHTML, which generates
16 - * the HTML for the input field to be placed in the table.
17 - *
18 - * The constructor input is an associative array of $fieldname => $info,
19 - * where $info is an Associative Array with any of the following:
20 - *
21 - * 'class' -- the subclass of HTMLFormField that will be used
22 - * to create the object. *NOT* the CSS class!
23 - * 'type' -- roughly translates into the <select> type attribute.
24 - * if 'class' is not specified, this is used as a map
25 - * through HTMLForm::$typeMappings to get the class name.
26 - * 'default' -- default value when the form is displayed
27 - * 'id' -- HTML id attribute
28 - * 'options' -- varies according to the specific object.
29 - * 'label-message' -- message key for a message to use as the label.
30 - * can be an array of msg key and then parameters to
31 - * the message.
32 - * 'label' -- alternatively, a raw text message. Overridden by
33 - * label-message
34 - * 'help-message' -- message key for a message to use as a help text.
35 - * can be an array of msg key and then parameters to
36 - * the message.
37 - * 'required' -- passed through to the object, indicating that it
38 - * is a required field.
39 - * 'size' -- the length of text fields
40 - * 'filter-callback -- a function name to give you the chance to
41 - * massage the inputted value before it's processed.
42 - * @see HTMLForm::filter()
43 - * 'validation-callback' -- a function name to give you the chance
44 - * to impose extra validation on the field input.
45 - * @see HTMLForm::validate()
46 - *
47 - * TODO: Document 'section' / 'subsection' stuff
48 - */
494 class HTMLForm {
505 static $jsAdded = false;
516
52 - # A mapping of 'type' inputs onto standard HTMLFormField subclasses
 7+ /* The descriptor is an array of arrays.
 8+ i.e. array(
 9+ 'fieldname' => array( 'section' => 'section/subsection',
 10+ properties... ),
 11+ ...
 12+ )
 13+ */
 14+
5315 static $typeMappings = array(
5416 'text' => 'HTMLTextField',
5517 'select' => 'HTMLSelectField',
@@ -60,44 +22,14 @@
6123 'float' => 'HTMLFloatField',
6224 'info' => 'HTMLInfoField',
6325 'selectorother' => 'HTMLSelectOrOtherField',
64 - 'submit' => 'HTMLSubmitField',
65 - 'hidden' => 'HTMLHiddenField',
66 -
6726 # HTMLTextField will output the correct type="" attribute automagically.
6827 # There are about four zillion other HTML 5 input types, like url, but
6928 # we don't use those at the moment, so no point in adding all of them.
7029 'email' => 'HTMLTextField',
7130 'password' => 'HTMLTextField',
7231 );
73 -
74 - protected $mMessagePrefix;
75 - protected $mFlatFields;
76 - protected $mFieldTree;
77 - protected $mShowReset = false;
78 - public $mFieldData;
79 -
80 - protected $mSubmitCallback;
81 - protected $mValidationErrorMessage;
82 -
83 - protected $mPre = '';
84 - protected $mHeader = '';
85 - protected $mPost = '';
86 -
87 - protected $mSubmitID;
88 - protected $mSubmitText;
89 - protected $mTitle;
90 -
91 - protected $mHiddenFields = array();
92 - protected $mButtons = array();
93 -
94 - protected $mWrapperLegend = false;
9532
96 - /**
97 - * Build a new HTMLForm from an array of field attributes
98 - * @param $descriptor Array of Field constructs, as described above
99 - * @param $messagePrefix String a prefix to go in front of default messages
100 - */
101 - public function __construct( $descriptor, $messagePrefix='' ) {
 33+ function __construct( $descriptor, $messagePrefix ) {
10234 $this->mMessagePrefix = $messagePrefix;
10335
10436 // Expand out into a tree.
@@ -111,7 +43,7 @@
11244
11345 $info['name'] = $fieldname;
11446
115 - $field = self::loadInputFromParameters( $info );
 47+ $field = $this->loadInputFromParameters( $info );
11648 $field->mParent = $this;
11749
11850 $setSection =& $loadedDescriptor;
@@ -134,12 +66,10 @@
13567 }
13668
13769 $this->mFieldTree = $loadedDescriptor;
 70+
 71+ $this->mShowReset = true;
13872 }
13973
140 - /**
141 - * Add the HTMLForm-specific JavaScript, if it hasn't been
142 - * done already.
143 - */
14474 static function addJS() {
14575 if( self::$jsAdded ) return;
14676
@@ -148,11 +78,6 @@
14979 $wgOut->addScriptClass( 'htmlform' );
15080 }
15181
152 - /**
153 - * Initialise a new Object for the field
154 - * @param $descriptor input Descriptor, as described above
155 - * @return HTMLFormField subclass
156 - */
15782 static function loadInputFromParameters( $descriptor ) {
15883 if ( isset( $descriptor['class'] ) ) {
15984 $class = $descriptor['class'];
@@ -170,21 +95,15 @@
17196 return $obj;
17297 }
17398
174 - /**
175 - * The here's-one-I-made-earlier option: do the submission if
176 - * posted, or display the form with or without funky valiation
177 - * errors
178 - * @return Bool whether submission was successful.
179 - */
18099 function show() {
181100 $html = '';
182101
183102 self::addJS();
184103
185 - # Load data from the request.
 104+ // Load data from the request.
186105 $this->loadData();
187106
188 - # Try a submission
 107+ // Try a submission
189108 global $wgUser, $wgRequest;
190109 $editToken = $wgRequest->getVal( 'wpEditToken' );
191110
@@ -195,31 +114,23 @@
196115 if( $result === true )
197116 return $result;
198117
199 - # Display form.
 118+ // Display form.
200119 $this->displayForm( $result );
201 - return false;
202120 }
203121
204 - /**
205 - * Validate all the fields, and call the submision callback
206 - * function if everything is kosher.
207 - * @return Mixed Bool true == Successful submission, Bool false
208 - * == No submission attempted, anything else == Error to
209 - * display.
210 - */
 122+ /** Return values:
 123+ * TRUE == Successful submission
 124+ * FALSE == No submission attempted
 125+ * Anything else == Error to display.
 126+ */
211127 function trySubmit() {
212 - # Check for validation
 128+ // Check for validation
213129 foreach( $this->mFlatFields as $fieldname => $field ) {
214 - if ( !empty( $field->mParams['nodata'] ) )
215 - continue;
216 - if ( $field->validate(
217 - $this->mFieldData[$fieldname],
218 - $this->mFieldData )
219 - !== true )
220 - {
221 - return isset( $this->mValidationErrorMessage )
222 - ? $this->mValidationErrorMessage
223 - : array( 'htmlform-invalid-input' );
 130+ if ( !empty( $field->mParams['nodata'] ) ) continue;
 131+ if ( $field->validate( $this->mFieldData[$fieldname],
 132+ $this->mFieldData ) !== true ) {
 133+ return isset( $this->mValidationErrorMessage ) ?
 134+ $this->mValidationErrorMessage : array( 'htmlform-invalid-input' );
224135 }
225136 }
226137
@@ -232,69 +143,18 @@
233144 return $res;
234145 }
235146
236 - /**
237 - * Set a callback to a function to do something with the form
238 - * once it's been successfully validated.
239 - * @param $cb String function name. The function will be passed
240 - * the output from HTMLForm::filterDataForSubmit, and must
241 - * return Bool true on success, Bool false if no submission
242 - * was attempted, or String HTML output to display on error.
243 - */
244147 function setSubmitCallback( $cb ) {
245148 $this->mSubmitCallback = $cb;
246149 }
247150
248 - /**
249 - * Set a message to display on a validation error.
250 - * @param $msg Mixed String or Array of valid inputs to wfMsgExt()
251 - * (so each entry can be either a String or Array)
252 - */
253151 function setValidationErrorMessage( $msg ) {
254152 $this->mValidationErrorMessage = $msg;
255153 }
256 -
257 - /**
258 - * Set the introductory message, overwriting any existing message.
259 - * @param $msg String complete text of message to display
260 - */
261 - function setIntro( $msg ) { $this->mPre = $msg; }
262154
263 - /**
264 - * Add introductory text.
265 - * @param $msg String complete text of message to display
266 - */
267 - function addPreText( $msg ) { $this->mPre .= $msg; }
268 -
269 - /**
270 - * Add header text, inside the form.
271 - * @param $msg String complete text of message to display
272 - */
273 - function addHeaderText( $msg ) { $this->mHeader .= $msg; }
274 -
275 - /**
276 - * Add text to the end of the display.
277 - * @param $msg String complete text of message to display
278 - */
279 - function addPostText( $msg ) { $this->mPost .= $msg; }
280 -
281 - /**
282 - * Add a hidden field to the output
283 - * @param $name String field name
284 - * @param $value String field value
285 - */
286 - public function addHiddenField( $name, $value ){
287 - $this->mHiddenFields[ $name ] = $value;
 155+ function setIntro( $msg ) {
 156+ $this->mIntro = $msg;
288157 }
289 -
290 - public function addButton( $name, $value, $id=null ){
291 - $this->mButtons[] = compact( 'name', 'value', 'id' );
292 - }
293158
294 - /**
295 - * Display the form (sending to wgOut), with an appropriate error
296 - * message or stack of messages, and any validation errors, etc.
297 - * @param $submitResult Mixed output from HTMLForm::trySubmit()
298 - */
299159 function displayForm( $submitResult ) {
300160 global $wgOut;
301161
@@ -302,67 +162,44 @@
303163 $this->displayErrors( $submitResult );
304164 }
305165
306 - $html = ''
307 - . $this->mHeader
308 - . $this->getBody()
309 - . $this->getHiddenFields()
310 - . $this->getButtons()
311 - ;
 166+ if ( isset( $this->mIntro ) ) {
 167+ $wgOut->addHTML( $this->mIntro );
 168+ }
312169
 170+ $html = $this->getBody();
 171+
 172+ // Hidden fields
 173+ $html .= $this->getHiddenFields();
 174+
 175+ // Buttons
 176+ $html .= $this->getButtons();
 177+
313178 $html = $this->wrapForm( $html );
314179
315 - $wgOut->addHTML( ''
316 - . $this->mPre
317 - . $html
318 - . $this->mPost
319 - );
 180+ $wgOut->addHTML( $html );
320181 }
321182
322 - /**
323 - * Wrap the form innards in an actual <form> element
324 - * @param $html String HTML contents to wrap.
325 - * @return String wrapped HTML.
326 - */
327183 function wrapForm( $html ) {
328 -
329 - # Include a <fieldset> wrapper for style, if requested.
330 - if( $this->mWrapperLegend !== false ){
331 - $html = Xml::fieldset( $this->mWrapperLegend, $html );
332 - }
333 -
334184 return Html::rawElement(
335185 'form',
336186 array(
337187 'action' => $this->getTitle()->getFullURL(),
338188 'method' => 'post',
339 - 'class' => 'visualClear',
340189 ),
341190 $html
342191 );
343192 }
344193
345 - /**
346 - * Get the hidden fields that should go inside the form.
347 - * @return String HTML.
348 - */
349194 function getHiddenFields() {
350195 global $wgUser;
351196 $html = '';
352197
353198 $html .= Html::hidden( 'wpEditToken', $wgUser->editToken() ) . "\n";
354199 $html .= Html::hidden( 'title', $this->getTitle() ) . "\n";
355 -
356 - foreach( $this->mHiddenFields as $name => $value ){
357 - $html .= Html::hidden( $name, $value ) . "\n";
358 - }
359200
360201 return $html;
361202 }
362203
363 - /**
364 - * Get the submit and (potentially) reset buttons.
365 - * @return String HTML.
366 - */
367204 function getButtons() {
368205 $html = '';
369206
@@ -385,31 +222,13 @@
386223 ) . "\n";
387224 }
388225
389 - foreach( $this->mButtons as $button ){
390 - $attrs = array(
391 - 'type' => 'submit',
392 - 'name' => $button['name'],
393 - 'value' => $button['value']
394 - );
395 - if( isset( $button['id'] ) )
396 - $attrs['id'] = $button['id'];
397 - $html .= Html::element( 'input', $attrs );
398 - }
399 -
400226 return $html;
401227 }
402228
403 - /**
404 - * Get the whole body of the form.
405 - */
406229 function getBody() {
407230 return $this->displaySection( $this->mFieldTree );
408231 }
409232
410 - /**
411 - * Format and display an error message stack.
412 - * @param $errors Mixed String or Array of message keys
413 - */
414233 function displayErrors( $errors ) {
415234 if ( is_array( $errors ) ) {
416235 $errorstr = $this->formatErrors( $errors );
@@ -423,11 +242,6 @@
424243 $wgOut->addHTML( $errorstr );
425244 }
426245
427 - /**
428 - * Format a stack of error messages into a single HTML string
429 - * @param $errors Array of message keys/values
430 - * @return String HTML, a <ul> list of errors
431 - */
432246 static function formatErrors( $errors ) {
433247 $errorstr = '';
434248 foreach ( $errors as $error ) {
@@ -449,70 +263,30 @@
450264 return $errorstr;
451265 }
452266
453 - /**
454 - * Set the text for the submit button
455 - * @param $t String plaintext.
456 - */
457267 function setSubmitText( $t ) {
458268 $this->mSubmitText = $t;
459269 }
460270
461 - /**
462 - * Get the text for the submit button, either customised or a default.
463 - * @return unknown_type
464 - */
465271 function getSubmitText() {
466 - return $this->mSubmitText
467 - ? $this->mSubmitText
468 - : wfMsg( 'htmlform-submit' );
 272+ return isset( $this->mSubmitText ) ? $this->mSubmitText : wfMsg( 'htmlform-submit' );
469273 }
470274
471 - /**
472 - * Set the id for the submit button.
473 - * @param $t String. FIXME: Integrity is *not* validated
474 - */
475275 function setSubmitID( $t ) {
476276 $this->mSubmitID = $t;
477277 }
478 -
479 - /**
480 - * Prompt the whole form to be wrapped in a <fieldset>, with
481 - * this text as its <legend> element.
482 - * @param $legend String HTML to go inside the <legend> element.
483 - * Will be escaped
484 - */
485 - public function setWrapperLegend( $legend ){ $this->mWrapperLegend = $legend; }
486278
487 - /**
488 - * Set the prefix for various default messages
489 - * TODO: currently only used for the <fieldset> legend on forms
490 - * with multiple sections; should be used elsewhre?
491 - * @param $p String
492 - */
493279 function setMessagePrefix( $p ) {
494280 $this->mMessagePrefix = $p;
495281 }
496282
497 - /**
498 - * Set the title for form submission
499 - * @param $t Title of page the form is on/should be posted to
500 - */
501283 function setTitle( $t ) {
502284 $this->mTitle = $t;
503285 }
504286
505 - /**
506 - * Get the title
507 - * @return Title
508 - */
509287 function getTitle() {
510288 return $this->mTitle;
511289 }
512290
513 - /**
514 - * TODO: Document
515 - * @param $fields
516 - */
517291 function displaySection( $fields ) {
518292 $tableHtml = '';
519293 $subsectionHtml = '';
@@ -521,8 +295,8 @@
522296 foreach( $fields as $key => $value ) {
523297 if ( is_object( $value ) ) {
524298 $v = empty( $value->mParams['nodata'] )
525 - ? $this->mFieldData[$key]
526 - : $value->getDefault();
 299+ ? $this->mFieldData[$key]
 300+ : $value->getDefault();
527301 $tableHtml .= $value->getTableRow( $v );
528302
529303 if( $value->getLabel() != '&nbsp;' )
@@ -545,9 +319,6 @@
546320 return $subsectionHtml . "\n" . $tableHtml;
547321 }
548322
549 - /**
550 - * Construct the form fields from the Descriptor array
551 - */
552323 function loadData() {
553324 global $wgRequest;
554325
@@ -562,7 +333,7 @@
563334 }
564335 }
565336
566 - # Filter data.
 337+ // Filter data.
567338 foreach( $fieldData as $name => &$value ) {
568339 $field = $this->mFlatFields[$name];
569340 $value = $field->filter( $value, $this->mFlatFields );
@@ -571,60 +342,33 @@
572343 $this->mFieldData = $fieldData;
573344 }
574345
575 - /**
576 - * Stop a reset button being shown for this form
577 - * @param $suppressReset Bool set to false to re-enable the
578 - * button again
579 - */
 346+ function importData( $fieldData ) {
 347+ // Filter data.
 348+ foreach( $fieldData as $name => &$value ) {
 349+ $field = $this->mFlatFields[$name];
 350+ $value = $field->filter( $value, $this->mFlatFields );
 351+ }
 352+
 353+ foreach( $this->mFlatFields as $fieldname => $field ) {
 354+ if ( !isset( $fieldData[$fieldname] ) )
 355+ $fieldData[$fieldname] = $field->getDefault();
 356+ }
 357+
 358+ $this->mFieldData = $fieldData;
 359+ }
 360+
580361 function suppressReset( $suppressReset = true ) {
581362 $this->mShowReset = !$suppressReset;
582363 }
583364
584 - /**
585 - * Overload this if you want to apply special filtration routines
586 - * to the form as a whole, after it's submitted but before it's
587 - * processed.
588 - * @param $data
589 - * @return unknown_type
590 - */
591365 function filterDataForSubmit( $data ) {
592366 return $data;
593367 }
594368 }
595369
596 -/**
597 - * The parent class to generate form fields. Any field type should
598 - * be a subclass of this.
599 - */
600370 abstract class HTMLFormField {
601 -
602 - protected $mValidationCallback;
603 - protected $mFilterCallback;
604 - protected $mName;
605 - public $mParams;
606 - protected $mLabel; # String label. Set on construction
607 - protected $mID;
608 - protected $mDefault;
609 - public $mParent;
610 -
611 - /**
612 - * This function must be implemented to return the HTML to generate
613 - * the input object itself. It should not implement the surrounding
614 - * table cells/rows, or labels/help messages.
615 - * @param $value String the value to set the input to; eg a default
616 - * text for a text input.
617 - * @return String valid HTML.
618 - */
619371 abstract function getInputHTML( $value );
620372
621 - /**
622 - * Override this function to add specific validation checks on the
623 - * field input. Don't forget to call parent::validate() to ensure
624 - * that the user-defined callback mValidationCallback is still run
625 - * @param $value String the value the field was submitted with
626 - * @param $alldata $all the data collected from the form
627 - * @return Mixed Bool true on success, or String error to display.
628 - */
629373 function validate( $value, $alldata ) {
630374 if ( isset( $this->mValidationCallback ) ) {
631375 return call_user_func( $this->mValidationCallback, $value, $alldata );
@@ -651,12 +395,6 @@
652396 return true;
653397 }
654398
655 - /**
656 - * Get the value that this input has been set to from a posted form,
657 - * or the input's default value if it has not been set.
658 - * @param $request WebRequest
659 - * @return String the value
660 - */
661399 function loadDataFromRequest( $request ) {
662400 if( $request->getCheck( $this->mName ) ) {
663401 return $request->getText( $this->mName );
@@ -665,14 +403,9 @@
666404 }
667405 }
668406
669 - /**
670 - * Initialise the object
671 - * @param $params Associative Array. See HTMLForm doc for syntax.
672 - */
673407 function __construct( $params ) {
674408 $this->mParams = $params;
675409
676 - # Generate the label from a message, if possible
677410 if( isset( $params['label-message'] ) ) {
678411 $msgInfo = $params['label-message'];
679412
@@ -720,14 +453,8 @@
721454 }
722455 }
723456
724 - /**
725 - * Get the complete table row for the input, including help text,
726 - * labels, and whatever.
727 - * @param $value String the value to set the input to.
728 - * @return String complete HTML table row.
729 - */
730457 function getTableRow( $value ) {
731 - # Check for invalid data.
 458+ // Check for invalid data.
732459 global $wgRequest;
733460
734461 $errors = $this->validate( $value, $this->mParent->mFieldData );
@@ -790,14 +517,7 @@
791518 }
792519 }
793520
794 - /**
795 - * flatten an array of options to a single array, for instance,
796 - * a set of <options> inside <optgroups>.
797 - * @param $options Associative Array with values either Strings
798 - * or Arrays
799 - * @return Array flattened input
800 - */
801 - public static function flattenOptions( $options ) {
 521+ static function flattenOptions( $options ) {
802522 $flatOpts = array();
803523
804524 foreach( $options as $key => $value ) {
@@ -813,11 +533,11 @@
814534 }
815535
816536 class HTMLTextField extends HTMLFormField {
 537+ # Override in derived classes to use other Xml::... functions
 538+ protected $mFunction = 'input';
817539
818540 function getSize() {
819 - return isset( $this->mParams['size'] )
820 - ? $this->mParams['size']
821 - : 45;
 541+ return isset( $this->mParams['size'] ) ? $this->mParams['size'] : 45;
822542 }
823543
824544 function getInputHTML( $value ) {
@@ -852,25 +572,21 @@
853573 $attribs[$param] = '';
854574 }
855575 }
856 -
857 - # Implement tiny differences between some field variants
858 - # here, rather than creating a new class for each one which
859 - # is essentially just a clone of this one.
860576 if ( isset( $this->mParams['type'] ) ) {
861577 switch ( $this->mParams['type'] ) {
862 - case 'email':
863 - $attribs['type'] = 'email';
864 - break;
865 - case 'int':
866 - $attribs['type'] = 'number';
867 - break;
868 - case 'float':
869 - $attribs['type'] = 'number';
870 - $attribs['step'] = 'any';
871 - break;
872 - case 'password':
873 - $attribs['type'] = 'password';
874 - break;
 578+ case 'email':
 579+ $attribs['type'] = 'email';
 580+ break;
 581+ case 'int':
 582+ $attribs['type'] = 'number';
 583+ break;
 584+ case 'float':
 585+ $attribs['type'] = 'number';
 586+ $attribs['step'] = 'any';
 587+ break;
 588+ case 'password':
 589+ $attribs['type'] = 'password';
 590+ break;
875591 }
876592 }
877593 }
@@ -879,15 +595,9 @@
880596 }
881597 }
882598
883 -/**
884 - * A field that will contain a numeric value
885 - */
886599 class HTMLFloatField extends HTMLTextField {
887 -
888600 function getSize() {
889 - return isset( $this->mParams['size'] )
890 - ? $this->mParams['size']
891 - : 20;
 601+ return isset( $this->mParams['size'] ) ? $this->mParams['size'] : 20;
892602 }
893603
894604 function validate( $value, $alldata ) {
@@ -901,8 +611,8 @@
902612
903613 $in_range = true;
904614
905 - # The "int" part of these message names is rather confusing.
906 - # They make equal sense for all numbers.
 615+ # The "int" part of these message names is rather confusing. They make
 616+ # equal sense for all numbers.
907617 if ( isset( $this->mParams['min'] ) ) {
908618 $min = $this->mParams['min'];
909619 if ( $min > $value )
@@ -919,9 +629,6 @@
920630 }
921631 }
922632
923 -/**
924 - * A field that must contain a number
925 - */
926633 class HTMLIntField extends HTMLFloatField {
927634 function validate( $value, $alldata ) {
928635 $p = parent::validate( $value, $alldata );
@@ -936,9 +643,6 @@
937644 }
938645 }
939646
940 -/**
941 - * A checkbox field
942 - */
943647 class HTMLCheckField extends HTMLFormField {
944648 function getInputHTML( $value ) {
945649 if ( !empty( $this->mParams['invert'] ) )
@@ -953,12 +657,8 @@
954658 Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel );
955659 }
956660
957 - /**
958 - * For a checkbox, the label goes on the right hand side, and is
959 - * added in getInputHTML(), rather than HTMLFormField::getRow()
960 - */
961661 function getLabel() {
962 - return '&nbsp;';
 662+ return '&nbsp;'; // In the right-hand column.
963663 }
964664
965665 function loadDataFromRequest( $request ) {
@@ -982,9 +682,6 @@
983683 }
984684 }
985685
986 -/**
987 - * A select dropdown field. Basically a wrapper for Xmlselect class
988 - */
989686 class HTMLSelectField extends HTMLFormField {
990687
991688 function validate( $value, $alldata ) {
@@ -1001,9 +698,9 @@
1002699 function getInputHTML( $value ) {
1003700 $select = new XmlSelect( $this->mName, $this->mID, strval( $value ) );
1004701
1005 - # If one of the options' 'name' is int(0), it is automatically selected.
1006 - # because PHP sucks and things int(0) == 'some string'.
1007 - # Working around this by forcing all of them to strings.
 702+ // If one of the options' 'name' is int(0), it is automatically selected.
 703+ // because PHP sucks and things int(0) == 'some string'.
 704+ // Working around this by forcing all of them to strings.
1008705 $options = array_map( 'strval', $this->mParams['options'] );
1009706
1010707 if( !empty( $this->mParams['disabled'] ) ) {
@@ -1016,9 +713,6 @@
1017714 }
1018715 }
1019716
1020 -/**
1021 - * Select dropdown field, with an additional "other" textbox.
1022 - */
1023717 class HTMLSelectOrOtherField extends HTMLTextField {
1024718 static $jsAdded = false;
1025719
@@ -1089,19 +783,15 @@
1090784 }
1091785 }
1092786
1093 -/**
1094 - * Multi-select field
1095 - */
1096787 class HTMLMultiSelectField extends HTMLFormField {
1097 -
1098788 function validate( $value, $alldata ) {
1099789 $p = parent::validate( $value, $alldata );
1100790 if( $p !== true ) return $p;
1101791
1102792 if( !is_array( $value ) ) return false;
1103793
1104 - # If all options are valid, array_intersect of the valid options
1105 - # and the provided options will return the provided options.
 794+ // If all options are valid, array_intersect of the valid options and the provided
 795+ // options will return the provided options.
1106796 $validOptions = HTMLFormField::flattenOptions( $this->mParams['options'] );
1107797
1108798 $validValues = array_intersect( $value, $validOptions );
@@ -1144,7 +834,7 @@
1145835 }
1146836
1147837 function loadDataFromRequest( $request ) {
1148 - # won't work with getCheck
 838+ // won't work with getCheck
1149839 if( $request->getCheck( 'wpEditToken' ) ) {
1150840 $arr = $request->getArray( $this->mName );
1151841
@@ -1170,9 +860,6 @@
1171861 }
1172862 }
1173863
1174 -/**
1175 - * Radio checkbox fields.
1176 - */
1177864 class HTMLRadioField extends HTMLFormField {
1178865 function validate( $value, $alldata ) {
1179866 $p = parent::validate( $value, $alldata );
@@ -1189,10 +876,6 @@
1190877 return wfMsgExt( 'htmlform-select-badoption', 'parseinline' );
1191878 }
1192879
1193 - /**
1194 - * This returns a block of all the radio options, in one cell.
1195 - * @see includes/HTMLFormField#getInputHTML()
1196 - */
1197880 function getInputHTML( $value ) {
1198881 $html = $this->formatOptions( $this->mParams['options'], $value );
1199882
@@ -1207,7 +890,6 @@
1208891 $attribs['disabled'] = 'disabled';
1209892 }
1210893
1211 - # TODO: should this produce an unordered list perhaps?
1212894 foreach( $options as $label => $info ) {
1213895 if( is_array( $info ) ) {
1214896 $html .= Html::rawElement( 'h1', array(), $label ) . "\n";
@@ -1231,9 +913,6 @@
1232914 }
1233915 }
1234916
1235 -/**
1236 - * An information field (text blob), not a proper input.
1237 - */
1238917 class HTMLInfoField extends HTMLFormField {
1239918 function __construct( $info ) {
1240919 $info['nodata'] = true;
@@ -1257,30 +936,3 @@
1258937 return false;
1259938 }
1260939 }
1261 -
1262 -class HTMLHiddenField extends HTMLFormField {
1263 -
1264 - public function getTableRow( $value ){
1265 - $this->mParent->addHiddenField(
1266 - $this->mParams['name'],
1267 - $this->mParams['default']
1268 - );
1269 - return '';
1270 - }
1271 -
1272 - public function getInputHTML( $value ){ return ''; }
1273 -}
1274 -
1275 -class HTMLSubmitField extends HTMLFormField {
1276 -
1277 - public function getTableRow( $value ){
1278 - $this->mParent->addButton(
1279 - $this->mParams['name'],
1280 - $this->mParams['default'],
1281 - isset($this->mParams['id']) ? $this->mParams['id'] : null
1282 - );
1283 - }
1284 -
1285 - public function getInputHTML( $value ){ return ''; }
1286 -}
1287 -
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' => '1'
 64+ 'wpRemember' => ''
6565 ));
6666
6767 // Init session if necessary
@@ -68,11 +68,19 @@
6969 wfSetupSession();
7070 }
7171
72 - $login = new Login( $req );
73 - switch ( $authRes = $login->attemptLogin() ) {
74 - case Login::SUCCESS :
 72+ $loginForm = new LoginForm($req);
 73+ switch ($authRes = $loginForm->authenticateUserData()) {
 74+ case LoginForm :: 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+
7785 $result['result'] = 'Success';
7886 $result['lguserid'] = intval($wgUser->getId());
7987 $result['lgusername'] = $wgUser->getName();
@@ -81,35 +89,35 @@
8290 $result['sessionid'] = session_id();
8391 break;
8492
85 - case Login::NO_NAME :
 93+ case LoginForm :: NO_NAME :
8694 $result['result'] = 'NoName';
8795 break;
88 - case Login::ILLEGAL :
 96+ case LoginForm :: ILLEGAL :
8997 $result['result'] = 'Illegal';
9098 break;
91 - case Login::WRONG_PLUGIN_PASS :
 99+ case LoginForm :: WRONG_PLUGIN_PASS :
92100 $result['result'] = 'WrongPluginPass';
93101 break;
94 - case Login::NOT_EXISTS :
 102+ case LoginForm :: NOT_EXISTS :
95103 $result['result'] = 'NotExists';
96104 break;
97 - case Login::WRONG_PASS :
 105+ case LoginForm :: WRONG_PASS :
98106 $result['result'] = 'WrongPass';
99107 break;
100 - case Login::EMPTY_PASS :
 108+ case LoginForm :: EMPTY_PASS :
101109 $result['result'] = 'EmptyPass';
102110 break;
103 - case Login::CREATE_BLOCKED :
 111+ case LoginForm :: CREATE_BLOCKED :
104112 $result['result'] = 'CreateBlocked';
105113 $result['details'] = 'Your IP address is blocked from account creation';
106114 break;
107 - case Login::THROTTLED :
 115+ case LoginForm :: THROTTLED :
108116 global $wgPasswordAttemptThrottle;
109117 $result['result'] = 'Throttled';
110 - $result['wait'] = intval( $wgPasswordAttemptThrottle['seconds'] );
 118+ $result['wait'] = intval($wgPasswordAttemptThrottle['seconds']);
111119 break;
112120 default :
113 - ApiBase::dieDebug( __METHOD__, "Unhandled case value: {$authRes}" );
 121+ ApiBase :: dieDebug(__METHOD__, "Unhandled case value: {$authRes}");
114122 }
115123
116124 $this->getResult()->addValue(null, 'login', $result);
Index: trunk/phase3/includes/AutoLoader.php
@@ -134,8 +134,6 @@
135135 'LinksUpdate' => 'includes/LinksUpdate.php',
136136 'LocalisationCache' => 'includes/LocalisationCache.php',
137137 'LocalisationCache_BulkLoad' => 'includes/LocalisationCache.php',
138 - 'LoginForm' => 'includes/Login.php', # For B/C
139 - 'Login' => 'includes/Login.php',
140138 'LogPage' => 'includes/LogPage.php',
141139 'LogPager' => 'includes/LogEventsList.php',
142140 'LogEventsList' => 'includes/LogEventsList.php',
@@ -495,7 +493,6 @@
496494 'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php',
497495 'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php',
498496 'ContribsPager' => 'includes/specials/SpecialContributions.php',
499 - 'SpecialCreateAccount' => 'includes/specials/SpecialCreateAccount.php',
500497 'DBLockForm' => 'includes/specials/SpecialLockdb.php',
501498 'DBUnlockForm' => 'includes/specials/SpecialUnlockdb.php',
502499 'DeadendPagesPage' => 'includes/specials/SpecialDeadendpages.php',
@@ -516,6 +513,7 @@
517514 'ImportStringSource' => 'includes/Import.php',
518515 'LinkSearchPage' => 'includes/specials/SpecialLinkSearch.php',
519516 'ListredirectsPage' => 'includes/specials/SpecialListredirects.php',
 517+ 'LoginForm' => 'includes/specials/SpecialUserlogin.php',
520518 'LonelyPagesPage' => 'includes/specials/SpecialLonelypages.php',
521519 'LongPagesPage' => 'includes/specials/SpecialLongpages.php',
522520 'MIMEsearchPage' => 'includes/specials/SpecialMIMEsearch.php',
@@ -564,7 +562,6 @@
565563 'UnwatchedpagesPage' => 'includes/specials/SpecialUnwatchedpages.php',
566564 'UploadForm' => 'includes/specials/SpecialUpload.php',
567565 'UploadFormMogile' => 'includes/specials/SpecialUploadMogile.php',
568 - 'SpecialUserLogin' => 'includes/specials/SpecialUserlogin.php',
569566 'UserrightsPage' => 'includes/specials/SpecialUserrights.php',
570567 'UsersPager' => 'includes/specials/SpecialListusers.php',
571568 'WantedCategoriesPage' => 'includes/specials/SpecialWantedcategories.php',
Index: trunk/phase3/includes/AuthPlugin.php
@@ -62,13 +62,12 @@
6363 /**
6464 * Modify options in the login template.
6565 *
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.
 66+ * @param $template UserLoginTemplate object.
 67+ * @param $type String 'signup' or 'login'.
6968 */
70 - public function modifyUITemplate( &$sp, $type=null ) {
 69+ public function modifyUITemplate( &$template, &$type ) {
7170 # Override this!
72 - $sp->mDomains = false;
 71+ $template->set( 'usedomain', false );
7372 }
7473
7574 /**
Index: trunk/phase3/includes/specials/SpecialCreateAccount.php
@@ -1,524 +0,0 @@
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 - protected $mLogin;
14 -
15 - public $mDomains = array();
16 -
17 - public $mUseEmail = true; # Can be switched off by AuthPlugins etc
18 - public $mUseRealname = true;
19 - public $mUseRemember = true;
20 -
21 - public $mFormHeader = '';
22 - public $mFormFields = array(
23 - 'Name' => array(
24 - 'type' => 'text',
25 - 'label-message' => 'yourname',
26 - 'id' => 'wpName2',
27 - 'tabindex' => '1',
28 - 'size' => '20',
29 - 'required' => '1',
30 - 'autofocus' => '',
31 - ),
32 - 'Password' => array(
33 - 'type' => 'password',
34 - 'label-message' => 'yourpassword',
35 - 'size' => '20',
36 - 'id' => 'wpPassword2',
37 - 'required' => '',
38 - ),
39 - 'Retype' => array(
40 - 'type' => 'password',
41 - 'label-message' => 'yourpasswordagain',
42 - 'size' => '20',
43 - 'id' => 'wpRetype',
44 - 'required' => '',
45 - ),
46 - 'Email' => array(
47 - 'type' => 'email',
48 - 'label-message' => 'youremail',
49 - 'size' => '20',
50 - 'id' => 'wpEmail',
51 - ),
52 - 'RealName' => array(
53 - 'type' => 'text',
54 - 'label-message' => 'yourrealname',
55 - 'id' => 'wpRealName',
56 - 'tabindex' => '1',
57 - 'size' => '20',
58 - ),
59 - 'Remember' => array(
60 - 'type' => 'check',
61 - 'label-message' => 'remembermypassword',
62 - 'id' => 'wpRemember',
63 - ),
64 - 'Domain' => array(
65 - 'type' => 'select',
66 - 'id' => 'wpDomain',
67 - 'label-message' => 'yourdomainname',
68 - 'options' => null,
69 - 'default' => null,
70 - ),
71 - );
72 -
73 - public function __construct(){
74 - parent::__construct( 'CreateAccount', 'createaccount' );
75 - $this->mLogin = new Login();
76 - $this->mFormFields['RealName']['label-help'] = 'prefs-help-realname';
77 - }
78 -
79 - public function execute( $par ){
80 - global $wgUser, $wgOut;
81 -
82 - $this->setHeaders();
83 - $this->loadQuery();
84 -
85 - # Block signup here if in readonly. Keeps user from
86 - # going through the process (filling out data, etc)
87 - # and being informed later.
88 - if ( wfReadOnly() ) {
89 - $wgOut->readOnlyPage();
90 - return;
91 - }
92 - # Bail out straightaway on permissions errors
93 - if ( !$this->userCanExecute( $wgUser ) ) {
94 - $this->displayRestrictionError();
95 - return;
96 - } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
97 - $this->userBlockedMessage();
98 - return;
99 - } elseif ( count( $permErrors = $this->getTitle()->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
100 - $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
101 - return;
102 - }
103 -
104 - if( $this->mPosted ) {
105 - $this->addNewAccount( $this->mCreateaccountMail );
106 - } else {
107 - $this->showMainForm('');
108 - }
109 - }
110 -
111 - /**
112 - * Load the member variables from the request parameters
113 - */
114 - protected function loadQuery(){
115 - global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
116 - $this->mCreateaccountMail = $wgRequest->getCheck( 'wpCreateaccountMail' )
117 - && $wgEnableEmail;
118 -
119 - $this->mUsername = $wgRequest->getText( 'wpName' );
120 - $this->mPassword = $wgRequest->getText( 'wpPassword' );
121 - $this->mRetype = $wgRequest->getText( 'wpRetype' );
122 - $this->mDomain = $wgRequest->getText( 'wpDomain' );
123 - $this->mReturnTo = $wgRequest->getVal( 'returnto' );
124 - $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
125 - $this->mPosted = $wgRequest->wasPosted();
126 - $this->mCreateaccountMail = $wgRequest->getCheck( 'wpCreateaccountMail' )
127 - && $wgEnableEmail;
128 - $this->mRemember = $wgRequest->getCheck( 'wpRemember' );
129 - $this->mLanguage = $wgRequest->getText( 'uselang' );
130 -
131 - if ( $wgRedirectOnLogin ) {
132 - $this->mReturnTo = $wgRedirectOnLogin;
133 - $this->mReturnToQuery = '';
134 - }
135 -
136 - if( $wgEnableEmail ) {
137 - $this->mEmail = $wgRequest->getText( 'wpEmail' );
138 - } else {
139 - $this->mEmail = '';
140 - }
141 - if( !in_array( 'realname', $wgHiddenPrefs ) ) {
142 - $this->mRealName = $wgRequest->getText( 'wpRealName' );
143 - } else {
144 - $this->mRealName = '';
145 - }
146 -
147 - if( !$wgAuth->validDomain( $this->mDomain ) ) {
148 - $this->mDomain = 'invaliddomain';
149 - }
150 - $wgAuth->setDomain( $this->mDomain );
151 -
152 - # When switching accounts, it sucks to get automatically logged out
153 - $returnToTitle = Title::newFromText( $this->mReturnTo );
154 - if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) {
155 - $this->mReturnTo = '';
156 - $this->mReturnToQuery = '';
157 - }
158 - }
159 -
160 - /**
161 - * Create a new user account from the provided data
162 - */
163 - protected function addNewAccount( $byEmail=false ) {
164 - global $wgUser, $wgEmailAuthentication;
165 -
166 - # Do a quick check that the user actually managed to type
167 - # the password in the same both times
168 - if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
169 - return $this->showMainForm( wfMsgExt( 'badretype', 'parseinline' ) );
170 - }
171 -
172 - # Create the account and abort if there's a problem doing so
173 - $status = $this->mLogin->attemptCreation( $byEmail );
174 - switch( $status ){
175 - case Login::SUCCESS:
176 - case Login::MAIL_ERROR:
177 - break;
178 -
179 - case Login::CREATE_BADDOMAIN:
180 - case Login::CREATE_EXISTS:
181 - case Login::NO_NAME:
182 - case Login::CREATE_NEEDEMAIL:
183 - case Login::CREATE_BADEMAIL:
184 - case Login::CREATE_BADNAME:
185 - case Login::WRONG_PLUGIN_PASS:
186 - case Login::ABORTED:
187 - return $this->showMainForm( wfMsgExt( $this->mLogin->mCreateResult, 'parseinline' ) );
188 -
189 - case Login::CREATE_SORBS:
190 - return $this->showMainForm( wfMsgExt( 'sorbs_create_account_reason', 'parseinline' ) . ' (' . wfGetIP() . ')' );
191 -
192 - case Login::CREATE_BLOCKED:
193 - return $this->userBlockedMessage();
194 -
195 - case Login::CREATE_BADPASS:
196 - global $wgMinimalPasswordLength;
197 - return $this->showMainForm( wfMsgExt( $this->mLogin->mCreateResult, array( 'parsemag' ), $wgMinimalPasswordLength ) );
198 -
199 - case Login::THROTTLED:
200 - global $wgAccountCreationThrottle;
201 - return $this->showMainForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $wgAccountCreationThrottle ) );
202 -
203 - default:
204 - throw new MWException( "Unhandled status code $status in " . __METHOD__ );
205 - }
206 -
207 - # If we showed up language selection links, and one was in use, be
208 - # smart (and sensible) and save that language as the user's preference
209 - global $wgLoginLanguageSelector;
210 - if( $wgLoginLanguageSelector && $this->mLanguage )
211 - $this->mLogin->mUser->setOption( 'language', $this->mLanguage );
212 - $this->mLogin->mUser->saveSettings();
213 -
214 - if( $byEmail ) {
215 - if( $result == Login::MAIL_ERROR ){
216 - # FIXME: we are totally screwed if we end up here...
217 - $this->showMainForm( wfMsgExt( 'mailerror', 'parseinline', $this->mLogin->mMailResult->getMessage() ) );
218 - } else {
219 - $wgOut->setPageTitle( wfMsg( 'accmailtitle' ) );
220 - $wgOut->addWikiMsg( 'accmailtext', $this->mLogin->mUser->getName(), $this->mLogin->mUser->getEmail() );
221 - $wgOut->returnToMain( false );
222 - }
223 -
224 - } else {
225 -
226 - # There might be a message stored from the confirmation mail
227 - # send, which we can display.
228 - if( $wgEmailAuthentication && $this->mLogin->mMailResult ) {
229 - global $wgOut;
230 - if( WikiError::isError( $this->mLogin->mMailResult ) ) {
231 - $wgOut->addWikiMsg( 'confirmemail_sendfailed', $this->mLogin->mMailResult->getMessage() );
232 - } else {
233 - $wgOut->addWikiMsg( 'confirmemail_oncreate' );
234 - }
235 - }
236 -
237 - # If not logged in, assume the new account as the current
238 - # one and set session cookies then show a "welcome" message
239 - # or a "need cookies" message as needed
240 - if( $wgUser->isAnon() ) {
241 - $wgUser = $this->mLogin->mUser;
242 - $wgUser->setCookies();
243 - if( $this->hasSessionCookie() ) {
244 - return $this->successfulCreation();
245 - } else {
246 - return $this->cookieRedirectCheck();
247 - }
248 - } else {
249 - # Show confirmation that the account was created
250 - global $wgOut;
251 - $self = SpecialPage::getTitleFor( 'Userlogin' );
252 - $wgOut->setPageTitle( wfMsgHtml( 'accountcreated' ) );
253 - $wgOut->addHTML( wfMsgWikiHtml( 'accountcreatedtext', $this->mLogin->mUser->getName() ) );
254 - $wgOut->returnToMain( false, $self );
255 - return true;
256 - }
257 - }
258 - }
259 -
260 - /**
261 - * Run any hooks registered for logins, then
262 - * display a message welcoming the user.
263 - */
264 - protected function successfulCreation(){
265 - global $wgUser, $wgOut;
266 -
267 - # Run any hooks; display injected HTML
268 - $injected_html = '';
269 - wfRunHooks('UserLoginComplete', array(&$wgUser, &$injected_html));
270 -
271 - SpecialUserLogin::displaySuccessfulLogin(
272 - 'welcomecreation',
273 - $injected_html,
274 - $this->mReturnTo,
275 - $this->mReturnToQuery );
276 - }
277 -
278 - /**
279 - * Display a message indicating that account creation from their IP has
280 - * been blocked by a (range)block with 'block account creation' enabled.
281 - * It's likely that this feature will be used for blocking large numbers
282 - * of innocent people, e.g. range blocks on schools. Don't blame it on
283 - * the user. There's a small chance that it really is the user's fault,
284 - * i.e. the username is blocked and they haven't bothered to log out
285 - * before trying to create an account to evade it, but we'll leave that
286 - * to their guilty conscience to figure out...
287 - */
288 - protected function userBlockedMessage() {
289 - global $wgOut, $wgUser;
290 -
291 - $wgOut->setPageTitle( wfMsg( 'cantcreateaccounttitle' ) );
292 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
293 - $wgOut->setArticleRelated( false );
294 -
295 - $ip = wfGetIP();
296 - $blocker = User::whoIs( $wgUser->mBlock->mBy );
297 - $block_reason = $wgUser->mBlock->mReason;
298 -
299 - if ( strval( $block_reason ) === '' ) {
300 - $block_reason = wfMsgExt( 'blockednoreason', 'parseinline' );
301 - }
302 - $wgOut->addWikiMsg( 'cantcreateaccount-text', $ip, $block_reason, $blocker );
303 - $wgOut->returnToMain( false );
304 - }
305 -
306 - /**
307 - * Show the main input form, with an appropriate error message
308 - * from a previous iteration, if necessary
309 - * @param $msg String HTML of message received previously
310 - * @param $msgtype String type of message, usually 'error'
311 - */
312 - protected function showMainForm( $msg, $msgtype = 'error' ) {
313 - global $wgUser, $wgOut, $wgHiddenPrefs, $wgEnableEmail;
314 - global $wgCookiePrefix, $wgLoginLanguageSelector;
315 - global $wgAuth, $wgEmailConfirmToEdit, $wgCookieExpiration;
316 -
317 - # Parse the error message if we got one
318 - if( $msg ){
319 - if( $msgtype == 'error' ){
320 - $msg = wfMsgExt( 'createaccounterror', array( 'parseinline', 'replaceafter' ), $msg );
321 - }
322 - $msg = Html::rawElement(
323 - 'div',
324 - array( 'class' => $msgtype . 'box' ),
325 - $msg
326 - );
327 - } else {
328 - $msg = '';
329 - }
330 -
331 - # Make sure the returnTo strings don't get lost if the
332 - # user changes language, etc
333 - $linkq = array();
334 - if ( !empty( $this->mReturnTo ) ) {
335 - $linkq['returnto'] = wfUrlencode( $this->mReturnTo );
336 - if ( !empty( $this->mReturnToQuery ) )
337 - $linkq['returntoquery'] = wfUrlencode( $this->mReturnToQuery );
338 - }
339 -
340 - # Pass any language selection on to the mode switch link
341 - if( $wgLoginLanguageSelector && $this->mLanguage )
342 - $linkq['uselang'] = $this->mLanguage;
343 -
344 - $skin = $wgUser->getSkin();
345 - $link = $skin->link(
346 - SpecialPage::getTitleFor( 'Userlogin' ),
347 - wfMsgHtml( 'gotaccountlink' ),
348 - array(),
349 - $linkq );
350 - $link = $wgUser->isLoggedIn()
351 - ? ''
352 - : wfMsgWikiHtml( 'gotaccount', $link );
353 -
354 - # Prepare language selection links as needed
355 - $langSelector = $wgLoginLanguageSelector
356 - ? Html::rawElement(
357 - 'div',
358 - array( 'id' => 'languagelinks' ),
359 - SpecialUserLogin::makeLanguageSelector( $this->getTitle(), $this->mReturnTo ) )
360 - : '';
361 -
362 - # Give authentication and captcha plugins a chance to
363 - # modify the form, by hook or by using $wgAuth
364 - $wgAuth->modifyUITemplate( $this, 'new' );
365 - wfRunHooks( 'UserCreateForm', array( &$this ) );
366 -
367 - # The most likely use of the hook is to enable domains;
368 - # check that now, and add fields if necessary
369 - if( $this->mDomains ){
370 - $this->mFormFields['Domain']['options'] = $this->mDomains;
371 - $this->mFormFields['Domain']['default'] = $this->mDomain;
372 - } else {
373 - unset( $this->mFormFields['Domain'] );
374 - }
375 -
376 - # Or to switch email on or off
377 - if( !$wgEnableEmail || !$this->mUseEmail ){
378 - unset( $this->mFormFields['Email'] );
379 - } else {
380 - if( $wgEmailConfirmToEdit ){
381 - $this->mFormFields['Email']['help-message'] = 'prefs-help-email-required';
382 - $this->mFormFields['Email']['required'] = '';
383 - } else {
384 - $this->mFormFields['Email']['help-message'] = 'prefs-help-email';
385 - }
386 - }
387 -
388 - # Or to play with realname
389 - if( in_array( 'realname', $wgHiddenPrefs ) || !$this->mUseRealname ){
390 - unset( $this->mFormFields['Realname'] );
391 - }
392 -
393 - # Or to tweak the 'remember my password' checkbox
394 - if( !($wgCookieExpiration > 0) || !$this->mUseRemember ){
395 - # Remove it altogether
396 - unset( $this->mFormFields['Remember'] );
397 - } elseif( $wgUser->getOption( 'rememberpassword' ) || $this->mRemember ){
398 - # Or check it by default
399 - # FIXME: this doesn't always work?
400 - $this->mFormFields['Remember']['checked'] = '1';
401 - }
402 -
403 - $form = new HTMLForm( $this->mFormFields );
404 -
405 - $form->setTitle( $this->getTitle() );
406 - $form->setSubmitText( wfMsg( 'createaccount' ) );
407 - $form->setSubmitId( 'wpCreateaccount' );
408 - $form->suppressReset();
409 - $form->setWrapperLegend( wfMsg( 'createaccount' ) );
410 -
411 - $form->addHiddenField( 'returnto', $this->mReturnTo );
412 - $form->addHiddenField( 'returntoquery', $this->mReturnToQuery );
413 -
414 - $form->addHeaderText( ''
415 - . Html::rawElement( 'p', array( 'id' => 'userloginlink' ),
416 - $link )
417 - . $this->mFormHeader
418 - . $langSelector
419 - );
420 - $form->addPreText( ''
421 - . $msg
422 - . Html::rawElement(
423 - 'div',
424 - array( 'id' => 'signupstart' ),
425 - wfMsgExt( 'loginstart', array( 'parseinline' ) )
426 - )
427 - );
428 - $form->addPostText(
429 - Html::rawElement(
430 - 'div',
431 - array( 'id' => 'signupend' ),
432 - wfMsgExt( 'loginend', array( 'parseinline' ) )
433 - )
434 - );
435 -
436 - # Add a 'send password by email' button if available
437 - if( $wgEnableEmail && $wgUser->isLoggedIn() ){
438 - $form->addButton(
439 - 'wpCreateaccountMail',
440 - wfMsg( 'createaccountmail' ),
441 - 'wpCreateaccountMail'
442 - );
443 - }
444 -
445 - $form->loadData();
446 -
447 - $wgOut->setPageTitle( wfMsg( 'createaccount' ) );
448 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
449 - $wgOut->setArticleRelated( false );
450 - $wgOut->disallowUserJs(); # Stop malicious userscripts sniffing passwords
451 -
452 -
453 - $form->displayForm( false );
454 -
455 - }
456 -
457 - /**
458 - * Check if a session cookie is present.
459 - *
460 - * This will not pick up a cookie set during _this_ request, but is meant
461 - * to ensure that the client is returning the cookie which was set on a
462 - * previous pass through the system.
463 - *
464 - * @private
465 - */
466 - protected function hasSessionCookie() {
467 - global $wgDisableCookieCheck, $wgRequest;
468 - return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
469 - }
470 -
471 - /**
472 - * Do a redirect back to the same page, so we can check any
473 - * new session cookies.
474 - */
475 - protected function cookieRedirectCheck() {
476 - global $wgOut;
477 -
478 - $query = array( 'wpCookieCheck' => '1' );
479 - if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo;
480 - $check = $this->getTitle()->getFullURL( $query );
481 -
482 - return $wgOut->redirect( $check );
483 - }
484 -
485 - /**
486 - * Check the cookies and show errors if they're not enabled.
487 - * @param $type String action being performed
488 - */
489 - protected function onCookieRedirectCheck() {
490 - if ( !$this->hasSessionCookie() ) {
491 - return $this->mainLoginForm( wfMsgExt( 'nocookiesnew', array( 'parseinline' ) ) );
492 - } else {
493 - return SpecialUserlogin::successfulLogin(
494 - array( 'welcomecreate' ),
495 - $this->mReturnTo,
496 - $this->mReturnToQuery
497 - );
498 - }
499 - }
500 -
501 - /**
502 - * Add text to the header. Only write to $mFormHeader directly
503 - * if you're determined to overwrite anything that other
504 - * extensions might have added.
505 - * @param $text String HTML
506 - */
507 - public function addFormHeader( $text ){
508 - $this->mFormHeader .= $text;
509 - }
510 -
511 - /**
512 - * Since the UserCreateForm hook was changed to pass a SpecialPage
513 - * instead of a QuickTemplate derivative, old extensions might
514 - * easily try calling these methods expecing them to exist. Tempting
515 - * though it is to let them have the fatal error, let's at least
516 - * fail gracefully...
517 - * @deprecated
518 - */
519 - public function set(){
520 - wfDeprecated( __METHOD__ );
521 - }
522 - public function addInputItem(){
523 - wfDeprecated( __METHOD__ );
524 - }
525 -}
Index: trunk/phase3/includes/specials/SpecialUserlogin.php
@@ -1,275 +1,962 @@
22 <?php
33 /**
4 - * SpecialPage for logging users into the wiki
 4+ * @file
55 * @ingroup SpecialPage
66 */
77
8 -class SpecialUserLogin extends SpecialPage {
 8+/**
 9+ * constructor
 10+ */
 11+function wfSpecialUserlogin( $par = '' ) {
 12+ global $wgRequest;
 13+ if( session_id() == '' ) {
 14+ wfSetupSession();
 15+ }
916
10 - var $mUsername, $mPassword, $mReturnTo, $mCookieCheck, $mPosted;
11 - var $mCreateaccount, $mCreateaccountMail, $mMailmypassword;
12 - var $mRemember, $mDomain, $mLanguage;
 17+ $form = new LoginForm( $wgRequest, $par );
 18+ $form->execute();
 19+}
 20+
 21+/**
 22+ * implements Special:Login
 23+ * @ingroup SpecialPage
 24+ */
 25+class LoginForm {
 26+
 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;
 38+
 39+ var $mName, $mPassword, $mRetype, $mReturnTo, $mCookieCheck, $mPosted;
 40+ var $mAction, $mCreateaccount, $mCreateaccountMail, $mMailmypassword;
 41+ var $mLoginattempt, $mRemember, $mEmail, $mDomain, $mLanguage;
1342 var $mSkipCookieCheck, $mReturnToQuery;
1443
15 - public $mDomains = array();
 44+ private $mExtUser = null;
1645
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 - );
 46+ /**
 47+ * Constructor
 48+ * @param WebRequest $request A WebRequest object passed by reference
 49+ */
 50+ function LoginForm( &$request, $par = '' ) {
 51+ global $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
4652
47 - protected $mLogin; # Login object
 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' )
 66+ && $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' );
4872
49 - public function __construct(){
50 - parent::__construct( 'Userlogin' );
 73+ if ( $wgRedirectOnLogin ) {
 74+ $this->mReturnTo = $wgRedirectOnLogin;
 75+ $this->mReturnToQuery = '';
 76+ }
 77+
 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+
 89+ if( !$wgAuth->validDomain( $this->mDomain ) ) {
 90+ $this->mDomain = 'invaliddomain';
 91+ }
 92+ $wgAuth->setDomain( $this->mDomain );
 93+
 94+ # When switching accounts, it sucks to get automatically logged out
 95+ $returnToTitle = Title::newFromText( $this->mReturnTo );
 96+ if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) {
 97+ $this->mReturnTo = '';
 98+ $this->mReturnToQuery = '';
 99+ }
51100 }
52101
53 - function execute( $par ) {
54 - global $wgRequest;
 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+ }
55119
56 - # Redirect out for account creation, for B/C
57 - $type = ( $par == 'signup' ) ? $par : $wgRequest->getText( 'type' );
58 - if( $type == 'signup' ){
59 - $sp = new SpecialCreateAccount();
60 - $sp->execute( $par );
 120+ /**
 121+ * @private
 122+ */
 123+ function addNewAccountMailPassword() {
 124+ global $wgOut;
 125+
 126+ if ('' == $this->mEmail) {
 127+ $this->mainLoginForm( wfMsg( 'noemail', htmlspecialchars( $this->mName ) ) );
61128 return;
62129 }
63 -
64 - # Because we're transitioning from logged-out, who might not
65 - # have a session, to logged-in, who always do, we need to make
66 - # sure that we *always* have a session...
67 - if( session_id() == '' ) {
68 - wfSetupSession();
 130+
 131+ $u = $this->addNewaccountInternal();
 132+
 133+ if ($u == NULL) {
 134+ return;
69135 }
70 -
71 - $this->loadQuery();
72 - $this->mLogin = new Login();
73136
74 - if ( $wgRequest->getCheck( 'wpCookieCheck' ) ) {
75 - $this->onCookieRedirectCheck();
 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() ) );
 151+ } else {
 152+ $wgOut->addWikiMsg( 'accmailtext', $u->getName(), $u->getEmail() );
 153+ $wgOut->returnToMain( false );
 154+ }
 155+ $u = 0;
 156+ }
 157+
 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 )
76168 return;
77 - } else if( $wgRequest->wasPosted() ) {
78 - if ( $this->mMailmypassword ) {
79 - return $this->showMailPage();
 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() );
80182 } else {
81 - return $this->processLogin();
 183+ $wgOut->addWikiMsg( 'confirmemail_oncreate' );
82184 }
 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+ }
83203 } else {
84 - $this->mainLoginForm( '' );
 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;
85215 }
86216 }
87217
88218 /**
89 - * Load member variables from the HTTP request data
90 - * @param $par String the fragment passed to execute()
 219+ * @private
91220 */
92 - protected function loadQuery(){
93 - global $wgRequest, $wgAuth, $wgHiddenPrefs, $wgEnableEmail, $wgRedirectOnLogin;
 221+ function addNewAccountInternal() {
 222+ global $wgUser, $wgOut;
 223+ global $wgEnableSorbs, $wgProxyWhitelist;
 224+ global $wgMemc, $wgAccountCreationThrottle;
 225+ global $wgAuth, $wgMinimalPasswordLength;
 226+ global $wgEmailConfirmToEdit;
94227
95 - $this->mUsername = $wgRequest->getText( 'wpName' );
96 - $this->mPassword = $wgRequest->getText( 'wpPassword' );
97 - $this->mDomain = $wgRequest->getText( 'wpDomain' );
98 - $this->mLanguage = $wgRequest->getText( 'uselang' );
 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;
 232+ }
99233
100 - $this->mReturnTo = $wgRequest->getVal( 'returnto' );
101 - $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
 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+ }
102245
103 - $this->mMailmypassword = $wgRequest->getCheck( 'wpMailmypassword' )
104 - && $wgEnableEmail;
105 - $this->mRemember = $wgRequest->getCheck( 'wpRemember' );
106 - $this->mSkipCookieCheck = $wgRequest->getCheck( 'wpSkipCookieCheck' );
 246+ if ( wfReadOnly() ) {
 247+ $wgOut->readOnlyPage();
 248+ return false;
 249+ }
107250
108 - if( !$wgAuth->validDomain( $this->mDomain ) ) {
109 - $this->mDomain = 'invaliddomain';
 251+ # Check permissions
 252+ if ( !$wgUser->isAllowed( 'createaccount' ) ) {
 253+ $this->userNotPrivilegedMessage();
 254+ return false;
 255+ } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
 256+ $this->userBlockedMessage();
 257+ return false;
110258 }
111 - $wgAuth->setDomain( $this->mDomain );
112 -
113 - if ( $wgRedirectOnLogin ) {
114 - $this->mReturnTo = $wgRedirectOnLogin;
115 - $this->mReturnToQuery = '';
 259+
 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;
116266 }
117 - # When switching accounts, it sucks to get automatically logged out
118 - $returnToTitle = Title::newFromText( $this->mReturnTo );
119 - if( is_object( $returnToTitle ) && $returnToTitle->isSpecial( 'Userlogout' ) ) {
120 - $this->mReturnTo = '';
121 - $this->mReturnToQuery = '';
 267+
 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;
122274 }
 275+
 276+ if ( 0 != $u->idForName() ) {
 277+ $this->mainLoginForm( wfMsg( 'userexists' ) );
 278+ return false;
 279+ }
 280+
 281+ if ( 0 != strcmp( $this->mPassword, $this->mRetype ) ) {
 282+ $this->mainLoginForm( wfMsg( 'badretype' ) );
 283+ return false;
 284+ }
 285+
 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+ }
 298+
 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+ }
 305+
 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 );
123343 }
124344
125345 /**
126 - * Show the main login form
127 - * @param $msg String a message key for a warning/error message
128 - * that may have been generated on a previous iteration
 346+ * Actually add a user to the database.
 347+ * Give it a User object that has been initialised with a name.
 348+ *
 349+ * @param $u User object.
 350+ * @param $autocreate boolean -- true if this is an autocreation via auth plugin
 351+ * @return User object.
 352+ * @private
129353 */
130 - protected function mainLoginForm( $msg, $msgtype = 'error' ) {
131 - global $wgUser, $wgOut, $wgEnableEmail;
132 - global $wgCookiePrefix, $wgLoginLanguageSelector;
133 - global $wgAuth, $wgCookieExpiration;
 354+ function initUser( $u, $autocreate ) {
 355+ global $wgAuth;
134356
135 - # Preload the name field with something if we can
136 - if ( '' == $this->mUsername ) {
137 - if ( $wgUser->isLoggedIn() ) {
138 - $this->mUsername = $wgUser->getName();
139 - } elseif( isset( $_COOKIE[$wgCookiePrefix.'UserName'] ) ) {
140 - $this->mUsername = $_COOKIE[$wgCookiePrefix.'UserName'];
 357+ $u->addToDatabase();
 358+
 359+ if ( $wgAuth->allowPasswordChange() ) {
 360+ $u->setPassword( $this->mPassword );
 361+ }
 362+
 363+ $u->setEmail( $this->mEmail );
 364+ $u->setRealName( $this->mRealName );
 365+ $u->setToken();
 366+
 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 );
141374 }
142375 }
143 - if( $this->mUsername ){
144 - $this->mFormFields['Name']['default'] = $this->mUsername;
145 - $this->mFormFields['Password']['autofocus'] = '1';
146 - } else {
147 - $this->mFormFields['Name']['autofocus'] = '1';
 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;
 385+ }
 386+
 387+ /**
 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
 395+ */
 396+ function authenticateUserData() {
 397+ global $wgUser, $wgAuth;
 398+ if ( '' == $this->mName ) {
 399+ return self::NO_NAME;
148400 }
 401+
 402+ global $wgPasswordAttemptThrottle;
149403
150 - # Parse the error message if we got one
151 - if( $msg ){
152 - if( $msgtype == 'error' ){
153 - $msg = wfMsgExt( 'loginerror', 'parseinline' ) . ' ' . $msg;
 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;
154418 }
155 - $msg = Html::rawElement(
156 - 'div',
157 - array( 'class' => $msgtype . 'box' ),
158 - $msg
159 - );
 419+ }
 420+
 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+ }
 431+
 432+ $this->mExtUser = ExternalUser::newFromName( $this->mName );
 433+
 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+ }
 440+
 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+ }
160449 } else {
161 - $msg = '';
 450+ $u->load();
162451 }
163452
164 - # Make sure the returnTo strings don't get lost if the
165 - # user changes language, etc
166 - $linkq = array();
167 - if ( !empty( $this->mReturnTo ) ) {
168 - $linkq['returnto'] = wfUrlencode( $this->mReturnTo );
169 - if ( !empty( $this->mReturnToQuery ) )
170 - $linkq['returntoquery'] = wfUrlencode( $this->mReturnToQuery );
 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;
171457 }
172458
173 - # Pass any language selection on to the mode switch link
174 - if( $wgLoginLanguageSelector && $this->mLanguage )
175 - $linkq['uselang'] = $this->mLanguage;
 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+ }
176481
177 - $skin = $wgUser->getSkin();
178 - $link = $skin->link(
179 - SpecialPage::getTitleFor( 'CreateAccount' ),
180 - wfMsgHtml( 'nologinlink' ),
181 - array(),
182 - $linkq );
 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;
183492
184 - # Don't show a "create account" link if the user can't
185 - $link = $wgUser->isAllowed( 'createaccount' ) && !$wgUser->isLoggedIn()
186 - ? wfMsgWikiHtml( 'nologin', $link )
187 - : '';
 493+ // Please reset throttle for successful logins, thanks!
 494+ if($throttleCount) {
 495+ $wgMemc->delete($throttleKey);
 496+ }
188497
189 - # Prepare language selection links as needed
190 - $langSelector = $wgLoginLanguageSelector
191 - ? Html::rawElement(
192 - 'div',
193 - array( 'id' => 'languagelinks' ),
194 - self::makeLanguageSelector( $this->getTitle(), $this->mReturnTo ) )
195 - : '';
 498+ if ( $isAutoCreated ) {
 499+ // Must be run after $wgUser is set, for correct new user log
 500+ wfRunHooks( 'AuthPluginAutoCreate', array( $wgUser ) );
 501+ }
196502
197 - # Give authentication and captcha plugins a chance to
198 - # modify the form, by hook or by using $wgAuth
199 - $wgAuth->modifyUITemplate( $this, 'login' );
200 - wfRunHooks( 'UserLoginForm', array( &$this ) );
201 -
202 - # The most likely use of the hook is to enable domains;
203 - # check that now, and add fields if necessary
204 - if( $this->mDomains ){
205 - $this->mFormFields['Domain']['options'] = $this->mDomains;
206 - $this->mFormFields['Domain']['default'] = $this->mDomain;
 503+ $retval = self::SUCCESS;
 504+ }
 505+ wfRunHooks( 'LoginAuthenticateAudit', array( $u, $this->mPassword, $retval ) );
 506+ return $retval;
 507+ }
 508+
 509+ /**
 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
 513+ */
 514+ function attemptAutoCreate( $user ) {
 515+ global $wgAuth, $wgUser, $wgAutocreatePolicy;
 516+
 517+ if ( $wgUser->isBlockedFromCreateAccount() ) {
 518+ wfDebug( __METHOD__.": user is blocked from account creation\n" );
 519+ return self::CREATE_BLOCKED;
 520+ }
 521+
 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+ }
207536 } else {
208 - unset( $this->mFormFields['Domain'] );
 537+ # Old AuthPlugin.
 538+ if ( !$wgAuth->autoCreate() ) {
 539+ return self::NOT_EXISTS;
 540+ }
 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+ }
209549 }
 550+
 551+ wfDebug( __METHOD__.": creating account\n" );
 552+ $user = $this->initUser( $user, true );
 553+ return self::SUCCESS;
 554+ }
 555+
 556+ function processLogin() {
 557+ 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+
 575+ 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+ */
 579+ global $wgLang, $wgRequest;
 580+ $code = $wgRequest->getVal( 'uselang', $wgUser->getOption( 'language' ) );
 581+ $wgLang = Language::factory( $code );
 582+ return $this->successfulLogin();
 583+ } else {
 584+ return $this->cookieRedirectCheck( 'login' );
 585+ }
 586+ break;
 587+
 588+ case self::NO_NAME:
 589+ case self::ILLEGAL:
 590+ $this->mainLoginForm( wfMsg( 'noname' ) );
 591+ break;
 592+ case self::WRONG_PLUGIN_PASS:
 593+ $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
 594+ break;
 595+ case self::NOT_EXISTS:
 596+ if( $wgUser->isAllowed( 'createaccount' ) ){
 597+ $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $this->mName ) ) );
 598+ } else {
 599+ $this->mainLoginForm( wfMsg( 'nosuchusershort', htmlspecialchars( $this->mName ) ) );
 600+ }
 601+ break;
 602+ case self::WRONG_PASS:
 603+ $this->mainLoginForm( wfMsg( 'wrongpassword' ) );
 604+ break;
 605+ case self::EMPTY_PASS:
 606+ $this->mainLoginForm( wfMsg( 'wrongpasswordempty' ) );
 607+ break;
 608+ case self::RESET_PASS:
 609+ $this->resetLoginForm( wfMsg( 'resetpass_announce' ) );
 610+ break;
 611+ case self::CREATE_BLOCKED:
 612+ $this->userBlockedMessage();
 613+ break;
 614+ case self::THROTTLED:
 615+ $this->mainLoginForm( wfMsg( 'login-throttled' ) );
 616+ break;
 617+ default:
 618+ throw new MWException( "Unhandled case value" );
 619+ }
 620+ }
 621+
 622+ function resetLoginForm( $error ) {
 623+ global $wgOut;
 624+ $wgOut->addHTML( Xml::element('p', array( 'class' => 'error' ), $error ) );
 625+ $reset = new SpecialResetpass();
 626+ $reset->execute( null );
 627+ }
 628+
 629+ /**
 630+ * @private
 631+ */
 632+ function mailPassword() {
 633+ global $wgUser, $wgOut, $wgAuth;
210634
211 - # Or to tweak the 'remember my password' checkbox
212 - if( !($wgCookieExpiration > 0) ){
213 - # Remove it altogether
214 - unset( $this->mFormFields['Remember'] );
215 - } elseif( $wgUser->getOption( 'rememberpassword' ) || $this->mRemember ){
216 - # Or check it by default
217 - # FIXME: this doesn't always work?
218 - $this->mFormFields['Remember']['checked'] = '1';
 635+ if ( wfReadOnly() ) {
 636+ $wgOut->readOnlyPage();
 637+ return false;
219638 }
220639
221 - $form = new HTMLForm( $this->mFormFields, '' );
222 - $form->setTitle( $this->getTitle() );
223 - $form->setSubmitText( wfMsg( 'login' ) );
224 - $form->setSubmitId( 'wpLoginAttempt' );
225 - $form->suppressReset();
226 - $form->setWrapperLegend( wfMsg( 'userlogin' ) );
 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+ }
227651
228 - $form->addHiddenField( 'returnto', $this->mReturnTo );
229 - $form->addHiddenField( 'returntoquery', $this->mReturnToQuery );
 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+ }
230716
231 - $form->addHeaderText( ''
232 - . Html::rawElement( 'p', array( 'id' => 'userloginlink' ),
233 - $link )
234 - . Html::rawElement( 'div', array( 'id' => 'userloginprompt' ),
235 - wfMsgExt( 'loginprompt', array( 'parseinline' ) ) )
236 - . $this->mFormHeader
237 - . $langSelector
238 - );
239 - $form->addPreText( ''
240 - . $msg
241 - . Html::rawElement(
242 - 'div',
243 - array( 'id' => 'loginstart' ),
244 - wfMsgExt( 'loginstart', array( 'parseinline' ) )
245 - )
246 - );
247 - $form->addPostText(
248 - Html::rawElement(
249 - 'div',
250 - array( 'id' => 'loginend' ),
251 - wfMsgExt( 'loginend', array( 'parseinline' ) )
252 - )
253 - );
 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) {
 796+ global $wgOut;
 797+
 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;
254844
255 - # Add a 'mail reset' button if available
256 - $buttons = '';
257 - if( $wgEnableEmail && $wgAuth->allowPasswordChange() ){
258 - $form->addButton(
259 - 'wpMailmypassword',
260 - wfMsg( 'mailmypassword' ),
261 - 'wpMailmypassword'
262 - );
 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() ) {
 852+ $wgOut->readOnlyPage();
 853+ return;
 854+ } elseif ( $wgUser->isBlockedFromCreateAccount() ) {
 855+ $this->userBlockedMessage();
 856+ return;
 857+ } elseif ( count( $permErrors = $titleObj->getUserPermissionsErrors( 'createaccount', $wgUser, true ) )>0 ) {
 858+ $wgOut->showPermissionsErrorPage( $permErrors, 'createaccount' );
 859+ return;
 860+ }
263861 }
264 -
265 - $form->loadData();
266862
267 - $wgOut->setPageTitle( wfMsg( 'login' ) );
 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' ) );
268943 $wgOut->setRobotPolicy( 'noindex,nofollow' );
269944 $wgOut->setArticleRelated( false );
270 - $wgOut->disallowUserJs(); # Stop malicious userscripts sniffing passwords
 945+ $wgOut->disallowUserJs(); // just in case...
 946+ $wgOut->addTemplate( $template );
 947+ }
271948
272 - $form->displayForm( '' );
273 - }
 949+ /**
 950+ * @private
 951+ */
 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+ }
 960+ }
274961
275962 /**
276963 * Check if a session cookie is present.
@@ -280,49 +967,57 @@
281968 *
282969 * @private
283970 */
284 - protected function hasSessionCookie() {
 971+ function hasSessionCookie() {
285972 global $wgDisableCookieCheck, $wgRequest;
286 - return $wgDisableCookieCheck || $wgRequest->checkSessionCookie();
 973+ return $wgDisableCookieCheck ? true : $wgRequest->checkSessionCookie();
287974 }
288975
289976 /**
290 - * Do a redirect back to the same page, so we can check any
291 - * new session cookies.
 977+ * @private
292978 */
293 - protected function cookieRedirectCheck() {
 979+ function cookieRedirectCheck( $type ) {
294980 global $wgOut;
295981
296 - $query = array( 'wpCookieCheck' => '1');
 982+ $titleObj = SpecialPage::getTitleFor( 'Userlogin' );
 983+ $query = array( 'wpCookieCheck' => $type );
297984 if ( $this->mReturnTo ) $query['returnto'] = $this->mReturnTo;
298 - $check = $this->getTitle()->getFullURL( $query );
 985+ $check = $titleObj->getFullURL( $query );
299986
300987 return $wgOut->redirect( $check );
301988 }
302989
303990 /**
304 - * Check the cookies and show errors if they're not enabled.
305 - * @param $type String action being performed
 991+ * @private
306992 */
307 - protected function onCookieRedirectCheck() {
308 - if ( $this->hasSessionCookie() ) {
309 - return self::successfulLogin(
310 - 'loginsuccess',
311 - $this->mReturnTo,
312 - $this->mReturnToQuery
313 - );
 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+ }
3141003 } else {
315 - return $this->mainLoginForm( wfMsgExt( 'nocookieslogin', array( 'parseinline' ) ) );
 1004+ return $this->successfulLogin();
3161005 }
3171006 }
3181007
3191008 /**
 1009+ * @private
 1010+ */
 1011+ function throttleHit( $limit ) {
 1012+ $this->mainLoginForm( wfMsgExt( 'acct_creation_throttle_hit', array( 'parseinline' ), $limit ) );
 1013+ }
 1014+
 1015+ /**
3201016 * Produce a bar of links which allow the user to select another language
3211017 * during login/registration but retain "returnto"
322 - * @param $title Title to use in the link
323 - * @param $returnTo query string to append
324 - * @return String HTML for bar
 1018+ *
 1019+ * @return string
3251020 */
326 - public static function makeLanguageSelector( $title, $returnTo=false ) {
 1021+ function makeLanguageSelector() {
3271022 global $wgLang;
3281023
3291024 $msg = wfMsgForContent( 'loginlanguagelinks' );
@@ -333,8 +1028,7 @@
3341029 $lang = trim( $lang, '* ' );
3351030 $parts = explode( '|', $lang );
3361031 if (count($parts) >= 2) {
337 - $links[] = SpecialUserLogin::makeLanguageSelectorLink(
338 - $parts[0], $parts[1], $title, $returnTo );
 1032+ $links[] = $this->makeLanguageSelectorLink( $parts[0], $parts[1] );
3391033 }
3401034 }
3411035 return count( $links ) > 0 ? wfMsgHtml( 'loginlanguagelabel', $wgLang->pipeList( $links ) ) : '';
@@ -346,214 +1040,24 @@
3471041 /**
3481042 * Create a language selector link for a particular language
3491043 * Links back to this page preserving type and returnto
 1044+ *
3501045 * @param $text Link text
3511046 * @param $lang Language code
352 - * @param $title Title to link to
353 - * @param $returnTo String returnto query
3541047 */
355 - public static function makeLanguageSelectorLink( $text, $lang, $title, $returnTo=false ) {
 1048+ function makeLanguageSelectorLink( $text, $lang ) {
3561049 global $wgUser;
 1050+ $self = SpecialPage::getTitleFor( 'Userlogin' );
3571051 $attr = array( 'uselang' => $lang );
358 - if( $returnTo )
359 - $attr['returnto'] = $returnTo;
 1052+ if( $this->mType == 'signup' )
 1053+ $attr['type'] = 'signup';
 1054+ if( $this->mReturnTo )
 1055+ $attr['returnto'] = $this->mReturnTo;
3601056 $skin = $wgUser->getSkin();
3611057 return $skin->linkKnown(
362 - $title,
 1058+ $self,
3631059 htmlspecialchars( $text ),
3641060 array(),
3651061 $attr
3661062 );
3671063 }
368 -
369 - /**
370 - * Display a "login successful" page.
371 - * @param $message String message key of main message to display
372 - * @param $html String HTML to optionally add
373 - * @param $returnto Title to returnto
374 - * @param $returntoQuery String query string for returnto link
375 - */
376 - public static function displaySuccessfulLogin( $message, $html='', $returnTo=false, $returnToQuery=false ) {
377 - global $wgOut, $wgUser;
378 -
379 - $wgOut->setPageTitle( wfMsg( 'loginsuccesstitle' ) );
380 - $wgOut->setRobotPolicy( 'noindex,nofollow' );
381 - $wgOut->setArticleRelated( false );
382 - $wgOut->addWikiMsg( $message, $wgUser->getName() );
383 - $wgOut->addHTML( $html );
384 -
385 - if ( $returnTo ) {
386 - $wgOut->returnToMain( null, $returnTo, $returnToQuery );
387 - } else {
388 - $wgOut->returnToMain( null );
389 - }
390 - }
391 -
392 - /**
393 - * Display any messages generated by hooks, or HTTP redirect to
394 - * $this->mReturnTo (or Main Page if that's undefined). Formerly we had a
395 - * nice message here, but that's not as useful as just being sent to
396 - * wherever you logged in from. It should be clear that the action was
397 - * successful, given the lack of error messages plus the appearance of your
398 - * name in the upper right.
399 - *
400 - * Remember that this function can be accessed from a variety of
401 - * places, such as Special:ResetPass, or Special:CreateAccount.
402 - * @param $message String message key of a message to display if
403 - * we don't redirect
404 - * @param $returnTo String title of page to redirect to
405 - * @param $returnToQuery String query string to add to the redirect.
406 - * @param $html String empty string to go straight
407 - * to the redirect, or valid HTML to add underneath the text.
408 - */
409 - public static function successfulLogin( $message, $returnTo='', $returnToQuery='', $html='' ) {
410 - global $wgUser, $wgOut;
411 -
412 - if( $html === '' ) {
413 - $titleObj = Title::newFromText( $returnTo );
414 - if ( !$titleObj instanceof Title ) {
415 - $titleObj = Title::newMainPage();
416 - }
417 - $wgOut->redirect( $titleObj->getFullURL( $returnToQuery ) );
418 - } else {
419 - SpecialUserLogin::displaySuccessfulLogin( $message, $html, $returnTo, $returnToQuery );
420 - }
421 - }
422 -
423 -
424 - protected function processLogin(){
425 - global $wgUser, $wgAuth;
426 - $result = $this->mLogin->attemptLogin();
427 - switch ( $result ) {
428 - case Login::SUCCESS:
429 - if( $this->hasSessionCookie() || $this->mSkipCookieCheck ) {
430 - # Replace the language object to provide user interface in
431 - # correct language immediately on this first page load.
432 - global $wgLang, $wgRequest;
433 - $code = $wgRequest->getVal( 'uselang', $wgUser->getOption( 'language' ) );
434 - $wgLang = Language::factory( $code );
435 - return self::successfulLogin(
436 - 'loginsuccess',
437 - $this->mReturnTo,
438 - $this->mReturnToQuery,
439 - $this->mLogin->mLoginResult );
440 - } else {
441 - # Do a redirect check to ensure that the cookies are
442 - # being retained by the user's browser.
443 - return $this->cookieRedirectCheck();
444 - }
445 - break;
446 -
447 - case Login::NO_NAME:
448 - case Login::ILLEGAL:
449 - case Login::WRONG_PLUGIN_PASS:
450 - case Login::WRONG_PASS:
451 - case Login::EMPTY_PASS:
452 - case Login::THROTTLED:
453 - $this->mainLoginForm( wfMsgExt( $this->mLogin->mLoginResult, 'parseinline' ) );
454 - break;
455 -
456 - case Login::NOT_EXISTS:
457 - if( $wgUser->isAllowed( 'createaccount' ) ){
458 - $this->mainLoginForm( wfMsgExt( 'nosuchuser', 'parseinline', htmlspecialchars( $this->mUsername ) ) );
459 - } else {
460 - $this->mainLoginForm( wfMsgExt( 'nosuchusershort', 'parseinline', htmlspecialchars( $this->mUsername ) ) );
461 - }
462 - break;
463 -
464 - case Login::RESET_PASS:
465 - # 'Shell out' to Special:ResetPass to get the user to
466 - # set a new permanent password from a temporary one.
467 - $reset = new SpecialResetpass();
468 - $reset->mHeaderMsg = 'resetpass_announce';
469 - $reset->mHeaderMsgType = 'success';
470 - $reset->execute( null );
471 - break;
472 -
473 - case Login::CREATE_BLOCKED:
474 - $this->userBlockedMessage();
475 - break;
476 -
477 - case Login::ABORTED:
478 - $msg = $this->mLogin->mLoginResult ? $this->mLogin->mLoginResult : $this->mLogin->mCreateResult;
479 - $this->mainLoginForm( wfMsgExt( $msg, 'parseinline' ) );
480 - break;
481 -
482 - default:
483 - throw new MWException( "Unhandled case value: $result" );
484 - }
485 - }
486 -
487 - /**
488 - * Attempt to send the user a password-reset mail, and display
489 - * the results (good, bad or ugly).
490 - */
491 - protected function showMailPage(){
492 - global $wgOut;
493 - $result = $this->mLogin->mailPassword();
494 -
495 - switch( $result ){
496 - case Login::READ_ONLY :
497 - $wgOut->readOnlyPage();
498 - return;
499 - case Login::MAIL_PASSCHANGE_FORBIDDEN:
500 - $this->mainLoginForm( wfMsgExt( 'resetpass_forbidden', 'parseinline' ) );
501 - return;
502 - case Login::MAIL_BLOCKED:
503 - $this->mainLoginForm( wfMsgExt( 'blocked-mailpassword', 'parseinline' ) );
504 - return;
505 - case Login::MAIL_PING_THROTTLED:
506 - $wgOut->rateLimited();
507 - return;
508 - case Login::MAIL_PASS_THROTTLED:
509 - global $wgPasswordReminderResendTime;
510 - # Round the time in hours to 3 d.p., in case someone
511 - # is specifying minutes or seconds.
512 - $this->mainLoginForm( wfMsgExt(
513 - 'throttled-mailpassword',
514 - array( 'parsemag' ),
515 - round( $wgPasswordReminderResendTime, 3 )
516 - ) );
517 - return;
518 - case Login::NO_NAME:
519 - $this->mainLoginForm( wfMsgExt( 'noname', 'parseinline' ) );
520 - return;
521 - case Login::NOT_EXISTS:
522 - $this->mainLoginForm( wfMsgWikiHtml( 'nosuchuser', htmlspecialchars( $this->mLogin->mUser->getName() ) ) );
523 - return;
524 - case Login::MAIL_EMPTY_EMAIL:
525 - $this->mainLoginForm( wfMsgExt( 'noemail', 'parseinline', $this->mLogin->mUser->getName() ) );
526 - return;
527 - case Login::MAIL_BAD_IP:
528 - $this->mainLoginForm( wfMsgExt( 'badipaddress', 'parseinline' ) );
529 - return;
530 - case Login::MAIL_ERROR:
531 - $this->mainLoginForm( wfMsgExt( 'mailerror', 'parseinline', $this->mLogin->mMailResult->getMessage() ) );
532 - return;
533 - case Login::SUCCESS:
534 - $this->mainLoginForm( wfMsgExt( 'passwordsent', 'parseinline', $this->mLogin->mUser->getName() ), 'success' );
535 - return;
536 - }
537 - }
538 -
539 - /**
540 - * Add text to the header. Only write to $mFormHeader directly
541 - * if you're determined to overwrite anything that other
542 - * extensions might have added.
543 - * @param $text String HTML
544 - */
545 - public function addFormHeader( $text ){
546 - $this->mFormHeader .= $text;
547 - }
548 -
549 - /**
550 - * Since the UserLoginForm hook was changed to pass a SpecialPage
551 - * instead of a QuickTemplate derivative, old extensions might
552 - * easily try calling this method expecing it to exist. Tempting
553 - * though it is to let them have the fatal error, let's at least
554 - * fail gracefully...
555 - * @deprecated
556 - */
557 - public function set(){
558 - wfDeprecated( __METHOD__ );
559 - }
5601064 }
Index: trunk/phase3/includes/specials/SpecialResetpass.php
@@ -12,48 +12,6 @@
1313 public function __construct() {
1414 parent::__construct( 'Resetpass' );
1515 }
16 -
17 - public $mFormFields = array(
18 - 'Name' => array(
19 - 'type' => 'info',
20 - 'label-message' => 'yourname',
21 - 'default' => '',
22 - ),
23 - 'Password' => array(
24 - 'type' => 'password',
25 - 'label-message' => 'oldpassword',
26 - 'size' => '20',
27 - 'id' => 'wpPassword',
28 - 'required' => '',
29 - ),
30 - 'NewPassword' => array(
31 - 'type' => 'password',
32 - 'label-message' => 'newpassword',
33 - 'size' => '20',
34 - 'id' => 'wpNewPassword',
35 - 'required' => '',
36 - ),
37 - 'Retype' => array(
38 - 'type' => 'password',
39 - 'label-message' => 'retypenew',
40 - 'size' => '20',
41 - 'id' => 'wpRetype',
42 - 'required' => '',
43 - ),
44 - 'Remember' => array(
45 - 'type' => 'check',
46 - 'label-message' => 'remembermypassword',
47 - 'id' => 'wpRemember',
48 - ),
49 - );
50 - public $mSubmitMsg = 'resetpass-submit-loggedin';
51 - public $mHeaderMsg = '';
52 - public $mHeaderMsgType = 'error';
53 -
54 - protected $mUsername;
55 - protected $mOldpass;
56 - protected $mNewpass;
57 - protected $mRetype;
5816
5917 /**
6018 * Main execution point
@@ -61,142 +19,176 @@
6220 function execute( $par ) {
6321 global $wgUser, $wgAuth, $wgOut, $wgRequest;
6422
65 - $this->mUsername = $wgRequest->getVal( 'wpName', $wgUser->getName() );
 23+ $this->mUserName = $wgRequest->getVal( 'wpName' );
6624 $this->mOldpass = $wgRequest->getVal( 'wpPassword' );
6725 $this->mNewpass = $wgRequest->getVal( 'wpNewPassword' );
6826 $this->mRetype = $wgRequest->getVal( 'wpRetype' );
69 - $this->mRemember = $wgRequest->getVal( 'wpRemember' );
70 - $this->mReturnTo = $wgRequest->getVal( 'returnto' );
71 - $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' );
7227
7328 $this->setHeaders();
7429 $this->outputHeader();
7530
7631 if( !$wgAuth->allowPasswordChange() ) {
77 - $wgOut->showErrorPage( 'errorpagetitle', 'resetpass_forbidden' );
78 - return false;
 32+ $this->error( wfMsg( 'resetpass_forbidden' ) );
 33+ return;
7934 }
8035
8136 if( !$wgRequest->wasPosted() && !$wgUser->isLoggedIn() ) {
82 - $wgOut->showErrorPage( 'errorpagetitle', 'resetpass-no-info' );
83 - return false;
 37+ $this->error( wfMsg( 'resetpass-no-info' ) );
 38+ return;
8439 }
8540
86 - if( $wgRequest->wasPosted()
87 - && $wgUser->matchEditToken( $wgRequest->getVal('wpEditToken') )
88 - && $this->attemptReset() )
89 - {
90 - # Log the user in if they're not already (ie we're
91 - # coming from the e-mail-password-reset route
92 - if( !$wgUser->isLoggedIn() ) {
93 - $data = array(
94 - 'wpName' => $this->mUsername,
95 - 'wpPassword' => $this->mNewpass,
96 - 'returnto' => $this->mReturnTo,
97 - );
98 - if( $this->mRemember ) {
99 - $data['wpRemember'] = 1;
 41+ if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getVal('token') ) ) {
 42+ try {
 43+ $this->attemptReset( $this->mNewpass, $this->mRetype );
 44+ $wgOut->addWikiMsg( 'resetpass_success' );
 45+ if( !$wgUser->isLoggedIn() ) {
 46+ $data = array(
 47+ 'action' => 'submitlogin',
 48+ 'wpName' => $this->mUserName,
 49+ 'wpPassword' => $this->mNewpass,
 50+ 'returnto' => $wgRequest->getVal( 'returnto' ),
 51+ );
 52+ if( $wgRequest->getCheck( 'wpRemember' ) ) {
 53+ $data['wpRemember'] = 1;
 54+ }
 55+ $login = new LoginForm( new FauxRequest( $data, true ) );
 56+ $login->execute();
10057 }
101 - $login = new Login( new FauxRequest( $data, true ) );
102 - $login->attemptLogin();
103 -
104 - # Redirect out to the appropriate target.
105 - SpecialUserlogin::successfulLogin(
106 - 'resetpass_success',
107 - $this->mReturnTo,
108 - $this->mReturnToQuery,
109 - $login->mLoginResult
110 - );
111 - } else {
112 - # Redirect out to the appropriate target.
113 - SpecialUserlogin::successfulLogin(
114 - 'resetpass_success',
115 - $this->mReturnTo,
116 - $this->mReturnToQuery
117 - );
 58+ $titleObj = Title::newFromText( $wgRequest->getVal( 'returnto' ) );
 59+ if ( !$titleObj instanceof Title ) {
 60+ $titleObj = Title::newMainPage();
 61+ }
 62+ $wgOut->redirect( $titleObj->getFullURL() );
 63+ } catch( PasswordError $e ) {
 64+ $this->error( $e->getMessage() );
11865 }
119 - } else {
120 - $this->showForm();
12166 }
 67+ $this->showForm();
12268 }
12369
 70+ function error( $msg ) {
 71+ global $wgOut;
 72+ $wgOut->addHTML( Xml::element('p', array( 'class' => 'error' ), $msg ) );
 73+ }
 74+
12475 function showForm() {
125 - global $wgOut, $wgUser;
 76+ global $wgOut, $wgUser, $wgRequest;
12677
12778 $wgOut->disallowUserJs();
128 -
129 - if( $wgUser->isLoggedIn() ){
130 - unset( $this->mFormFields['Remember'] );
 79+
 80+ $self = SpecialPage::getTitleFor( 'Resetpass' );
 81+ if ( !$this->mUserName ) {
 82+ $this->mUserName = $wgUser->getName();
 83+ }
 84+ $rememberMe = '';
 85+ if ( !$wgUser->isLoggedIn() ) {
 86+ $rememberMe = '<tr>' .
 87+ '<td></td>' .
 88+ '<td class="mw-input">' .
 89+ Xml::checkLabel( wfMsg( 'remembermypassword' ),
 90+ 'wpRemember', 'wpRemember',
 91+ $wgRequest->getCheck( 'wpRemember' ) ) .
 92+ '</td>' .
 93+ '</tr>';
 94+ $submitMsg = 'resetpass_submit';
 95+ $oldpassMsg = 'resetpass-temp-password';
13196 } else {
132 - # Request is coming from Special:UserLogin after it
133 - # authenticated someone with a temporary password.
134 - $this->mFormFields['Password']['label-message'] = 'resetpass-temp-password';
135 - $this->mSubmitMsg = 'resetpass_submit';
 97+ $oldpassMsg = 'oldpassword';
 98+ $submitMsg = 'resetpass-submit-loggedin';
13699 }
137 - $this->mFormFields['Name']['default'] = $this->mUsername;
138 -
139 - $header = $this->mHeaderMsg
140 - ? Xml::element( 'div', array( 'class' => "{$this->mHeaderMsgType}box" ), wfMsg( $this->mHeaderMsg ) )
141 - : '';
142 -
143 - $form = new HTMLForm( $this->mFormFields, '' );
144 - $form->suppressReset();
145 - $form->setSubmitText( wfMsg( $this->mSubmitMsg ) );
146 - $form->setTitle( $this->getTitle() );
147 - $form->loadData();
148 -
149 - $formContents = ''
150 - . $form->getBody()
151 - . $form->getButtons()
152 - . $form->getHiddenFields()
153 - . Html::hidden( 'wpName', $this->mUsername )
154 - . Html::hidden( 'returnto', $this->mReturnTo )
155 - ;
156 - $formOutput = $form->wrapForm( $formContents );
157 -
158100 $wgOut->addHTML(
159 - $header
160 - . Html::rawElement( 'fieldset', array( 'class' => 'visualClear' ), ''
161 - . Html::element( 'legend', array(), wfMsg( 'resetpass_header' ) )
162 - . $formOutput
163 - )
 101+ Xml::fieldset( wfMsg( 'resetpass_header' ) ) .
 102+ Xml::openElement( 'form',
 103+ array(
 104+ 'method' => 'post',
 105+ 'action' => $self->getLocalUrl(),
 106+ 'id' => 'mw-resetpass-form' ) ) . "\n" .
 107+ Xml::hidden( 'token', $wgUser->editToken() ) . "\n" .
 108+ Xml::hidden( 'wpName', $this->mUserName ) . "\n" .
 109+ Xml::hidden( 'returnto', $wgRequest->getVal( 'returnto' ) ) . "\n" .
 110+ wfMsgExt( 'resetpass_text', array( 'parse' ) ) . "\n" .
 111+ Xml::openElement( 'table', array( 'id' => 'mw-resetpass-table' ) ) . "\n" .
 112+ $this->pretty( array(
 113+ array( 'wpName', 'username', 'text', $this->mUserName ),
 114+ array( 'wpPassword', $oldpassMsg, 'password', $this->mOldpass ),
 115+ array( 'wpNewPassword', 'newpassword', 'password', null ),
 116+ array( 'wpRetype', 'retypenew', 'password', null ),
 117+ ) ) . "\n" .
 118+ $rememberMe .
 119+ "<tr>\n" .
 120+ "<td></td>\n" .
 121+ '<td class="mw-input">' .
 122+ Xml::submitButton( wfMsg( $submitMsg ) ) .
 123+ "</td>\n" .
 124+ "</tr>\n" .
 125+ Xml::closeElement( 'table' ) .
 126+ Xml::closeElement( 'form' ) .
 127+ Xml::closeElement( 'fieldset' ) . "\n"
164128 );
165129 }
166130
 131+ function pretty( $fields ) {
 132+ $out = '';
 133+ foreach ( $fields as $list ) {
 134+ list( $name, $label, $type, $value ) = $list;
 135+ if( $type == 'text' ) {
 136+ $field = htmlspecialchars( $value );
 137+ } else {
 138+ $attribs = array( 'id' => $name );
 139+ if ( $name == 'wpNewPassword' || $name == 'wpRetype' ) {
 140+ $attribs = array_merge( $attribs,
 141+ User::passwordChangeInputAttribs() );
 142+ }
 143+ if ( $name == 'wpPassword' ) {
 144+ $attribs[] = 'autofocus';
 145+ }
 146+ $field = Html::input( $name, $value, $type, $attribs );
 147+ }
 148+ $out .= "<tr>\n";
 149+ $out .= "\t<td class='mw-label'>";
 150+ if ( $type != 'text' )
 151+ $out .= Xml::label( wfMsg( $label ), $name );
 152+ else
 153+ $out .= wfMsgHtml( $label );
 154+ $out .= "</td>\n";
 155+ $out .= "\t<td class='mw-input'>";
 156+ $out .= $field;
 157+ $out .= "</td>\n";
 158+ $out .= "</tr>";
 159+ }
 160+ return $out;
 161+ }
 162+
167163 /**
168 - * Try to reset the user's password
 164+ * @throws PasswordError when cannot set the new password because requirements not met.
169165 */
170 - protected function attemptReset() {
171 - $user = User::newFromName( $this->mUsername );
 166+ protected function attemptReset( $newpass, $retype ) {
 167+ $user = User::newFromName( $this->mUserName );
172168 if( !$user || $user->isAnon() ) {
173 - $this->mHeaderMsg = 'no such user';
174 - return false;
 169+ throw new PasswordError( 'no such user' );
175170 }
176171
177 - if( $this->mNewpass !== $this->mRetype ) {
178 - wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'badretype' ) );
179 - $this->mHeaderMsg = 'badretype';
180 - return false;
 172+ if( $newpass !== $retype ) {
 173+ wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'badretype' ) );
 174+ throw new PasswordError( wfMsg( 'badretype' ) );
181175 }
182176
183177 if( !$user->checkTemporaryPassword($this->mOldpass) && !$user->checkPassword($this->mOldpass) ) {
184 - wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'wrongpassword' ) );
185 - $this->mHeaderMsg = 'resetpass-wrong-oldpass';
186 - return false;
 178+ wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'wrongpassword' ) );
 179+ throw new PasswordError( wfMsg( 'resetpass-wrong-oldpass' ) );
187180 }
188181
189182 try {
190183 $user->setPassword( $this->mNewpass );
191 - wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'success' ) );
 184+ wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'success' ) );
192185 $this->mNewpass = $this->mOldpass = $this->mRetypePass = '';
193186 } catch( PasswordError $e ) {
194 - wfRunHooks( 'PrefsPasswordAudit', array( $user, $this->mNewpass, 'error' ) );
195 - $this->mHeaderMsg = $e->getMessage();
196 - return false;
 187+ wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'error' ) );
 188+ throw new PasswordError( $e->getMessage() );
 189+ return;
197190 }
198191
199192 $user->setCookies();
200193 $user->saveSettings();
201 - return true;
202194 }
203195 }
Index: trunk/phase3/includes/SpecialPage.php
@@ -112,8 +112,8 @@
113113 'Listredirects' => array( 'SpecialPage', 'Listredirects' ),
114114
115115 # Login/create account
116 - 'Userlogin' => 'SpecialUserLogin',
117 - 'CreateAccount' => 'SpecialCreateAccount',
 116+ 'Userlogin' => array( 'SpecialPage', 'Userlogin' ),
 117+ 'CreateAccount' => array( 'SpecialRedirectToSpecial', 'CreateAccount', 'Userlogin', 'signup', array( 'uselang' ) ),
118118
119119 # Users and rights
120120 'Blockip' => array( 'SpecialPage', 'Blockip', 'block' ),
Index: trunk/phase3/includes/templates/Userlogin.php
@@ -0,0 +1,323 @@
 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', $this->data['name'], '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+}
Property changes on: trunk/phase3/includes/templates/Userlogin.php
___________________________________________________________________
Added: svn:eol-style
1325 + native
Added: svn:keywords
2326 + Author Date Id Revision
Index: trunk/phase3/languages/messages/MessagesQqq.php
@@ -236,8 +236,6 @@
237237 * $1: number of files shown',
238238 'listingcontinuesabbrev' => 'Shown in contiuation of each first letter group.
239239 See http://test.wikipedia.org/wiki/Category:Test_ko?uselang={{SUBPAGENAME}}, for example.',
240 -'index-category' => 'Name of the category where pages with the <nowiki>__INDEX__</nowiki> behaviour switch are listed',
241 -'noindex-category' => 'Name of the category where pages with the <nowiki>__NOINDEX__</nowiki> behaviour switch are listed',
242240
243241 'linkprefix' => '{{optional}}',
244242 'mainpagetext' => 'Along with {{msg|mainpagedocfooter}}, the text you will see on the Main Page when your wiki is installed.',
@@ -598,7 +596,7 @@
599597 {{Identical|Log in}}",
600598 '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.",
601599 'loginprompt' => 'A small notice in the log in form.',
602 -'userlogin' => 'Name of special page [[Special:UserLogin]] where a user can log in.',
 600+'userlogin' => 'Name of special page [[Special:UserLogin]] where a user can log in or click to create a user account.',
603601 'logout' => '{{Identical|Log out}}',
604602 'userlogout' => '{{Identical|Log out}}',
605603 'notloggedin' => 'This message is displayed in the standard skin when not logged in. The message is placed above the login link in the top right corner of pages.
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -735,8 +735,6 @@
736736 'category-file-count' => '{{PLURAL:$2|This category contains only the following file.|The following {{PLURAL:$1|file is|$1 files are}} in this category, out of $2 total.}}',
737737 'category-file-count-limited' => 'The following {{PLURAL:$1|file is|$1 files are}} in the current category.',
738738 'listingcontinuesabbrev' => 'cont.',
739 -'index-category' => 'Indexed pages',
740 -'noindex-category' => 'Noindexed pages',
741739
742740 'linkprefix' => '/^(.*?)([a-zA-Z\\x80-\\xff]+)$/sD', # only translate this message to other languages if you have to change it
743741 'mainpagetext' => "<big>'''MediaWiki has been successfully installed.'''</big>",
@@ -1044,14 +1042,14 @@
10451043 'login' => 'Log in',
10461044 'nav-login-createaccount' => 'Log in / create account',
10471045 'loginprompt' => 'You must have cookies enabled to log in to {{SITENAME}}.',
1048 -'userlogin' => 'Log in',
 1046+'userlogin' => 'Log in / create account',
10491047 'logout' => 'Log out',
10501048 'userlogout' => 'Log out',
10511049 'notloggedin' => 'Not logged in',
1052 -'nologin' => "Don't have an account? '''$1'''.",
 1050+'nologin' => "Don't have an account? $1.",
10531051 'nologinlink' => 'Create an account',
10541052 'createaccount' => 'Create account',
1055 -'gotaccount' => "Already have an account? '''$1'''.",
 1053+'gotaccount' => 'Already have an account? $1.',
10561054 'gotaccountlink' => 'Log in',
10571055 'createaccountmail' => 'by e-mail',
10581056 'badretype' => 'The passwords you entered do not match.',
Index: trunk/phase3/RELEASE-NOTES
@@ -87,11 +87,6 @@
8888 correctly (img_auth only)
8989 * $wgUploadMaintenance added to disable file deletions and restorations during
9090 maintenance
91 -* UserLoginForm and UserCreateForm hooks, and AuthPlugin::modifyUITemplate, now receive a
92 - SpecialPage subclass instead of a QuickTemplate subclass. Hence there is no
93 - $template->set(), etc. The hook has access to most of the stuff that will go into the
94 - Login/Create form; see the documentation on HTMLForm for syntax for extra fields.
95 - LoginForm class is deprecated, its state constants are now in the Login class.
9691 * New hook AbortNewAccountAuto, called before account creation from AuthPlugin-
9792 or ExtUser-driven requests.
9893
@@ -240,7 +235,6 @@
241236 * A new permission, 'root', is created. Analogous to root users on Unix systems,
242237 the root permission effectively grants all other permissions on a wiki. Useful
243238 for debugging and administration.
244 -* (bug 16979) Tracking categories for __INDEX__ and __NOINDEX__
245239 * New configuration variable $wgShowPageOnRedlink that can be set to show the page
246240 instead of an edit interface when visiting a red link. The value can be specified
247241 for specific usergroups and namespaces.
Index: trunk/extensions/AntiSpoof/AntiSpoof.php
@@ -103,17 +103,15 @@
104104 /**
105105 * Set the ignore spoof thingie
106106 */
107 -function asUserCreateFormHook( &$sp ) {
 107+function asUserCreateFormHook( &$template ) {
108108 global $wgRequest, $wgAntiSpoofAccounts, $wgUser;
109109
110110 wfLoadExtensionMessages( 'AntiSpoof' );
111111
112112 if( $wgAntiSpoofAccounts && $wgUser->isAllowed( 'override-antispoof' ) )
113 - $sp->mFormFields['IgnoreAntiSpoof'] = array(
114 - 'type' => 'check',
115 - 'default' => $wgRequest->getCheck('wpIgnoreAntiSpoof'),
116 - 'label-message' => 'antispoof-ignore'
117 - );
 113+ $template->addInputItem( 'wpIgnoreAntiSpoof',
 114+ $wgRequest->getCheck('wpIgnoreAntiSpoof'),
 115+ 'checkbox', 'antispoof-ignore' );
118116 return true;
119117 }
120118
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( &$sp ) {
44 - return self::getInstance()->injectUserLogin( $sp );
 43+ static function injectUserLogin( &$template ) {
 44+ return self::getInstance()->injectUserLogin( $template );
4545 }
4646
4747 static function confirmUserLogin( $u, $pass, &$retval ) {
@@ -144,19 +144,18 @@
145145 * @param SimpleTemplate $template
146146 * @return bool true to keep running callbacks
147147 */
148 - function injectUserCreate( &$sp ) {
 148+ function injectUserCreate( &$template ) {
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 - $sp->addFormHeader(
 155+ $template->set( 'header',
156156 "<div class='captcha'>" .
157157 $wgOut->parse( $this->getMessage( 'createaccount' ) ) .
158158 $this->getForm() .
159 - "</div>\n"
160 - );
 159+ "</div>\n" );
161160 }
162161 return true;
163162 }
@@ -164,18 +163,18 @@
165164 /**
166165 * Inject a captcha into the user login form after a failed
167166 * password attempt as a speedbump for mass attacks.
 167+ * @fixme if multiple thingies insert a header, could break
168168 * @param SimpleTemplate $template
169169 * @return bool true to keep running callbacks
170170 */
171 - function injectUserLogin( &$sp ) {
 171+ function injectUserLogin( &$template ) {
172172 if ( $this->isBadLoginTriggered() ) {
173173 global $wgOut;
174 - $sp->addFormHeader(
 174+ $template->set( 'header',
175175 "<div class='captcha'>" .
176176 $wgOut->parse( $this->getMessage( 'badlogin' ) ) .
177177 $this->getForm() .
178 - "</div>\n"
179 - );
 178+ "</div>\n" );
180179 }
181180 return true;
182181 }
@@ -191,7 +190,7 @@
192191 */
193192 function triggerUserLogin( $user, $password, $retval ) {
194193 global $wgCaptchaTriggers, $wgCaptchaBadLoginExpiration, $wgMemc;
195 - if ( $retval == Login::WRONG_PASS && $wgCaptchaTriggers['badlogin'] ) {
 194+ if ( $retval == LoginForm::WRONG_PASS && $wgCaptchaTriggers['badlogin'] ) {
196195 $key = $this->badLoginKey();
197196 $count = $wgMemc->get( $key );
198197 if ( !$count ) {
@@ -558,7 +557,7 @@
559558 if ( !$this->passCaptcha() ) {
560559 $message = wfMsg( 'captcha-badlogin-fail' );
561560 // Emulate a bad-password return to confuse the shit out of attackers
562 - $retval = Login::WRONG_PASS;
 561+ $retval = LoginForm::WRONG_PASS;
563562 return false;
564563 }
565564 }
Index: trunk/extensions/LdapAuthentication/LdapAuthentication.php
@@ -376,43 +376,38 @@
377377 /**
378378 * Modify options in the login template.
379379 *
380 - * @param $sp SpecialUserlogin or SpecialCreateAccount object
 380+ * @param UserLoginTemplate $template
381381 * @access public
382382 */
383 - public function modifyUITemplate( &$sp ) {
 383+ function modifyUITemplate( &$template ) {
384384 global $wgLDAPDomainNames, $wgLDAPUseLocal;
385385 global $wgLDAPAddLDAPUsers;
386386 global $wgLDAPAutoAuthDomain;
387387
388388 $this->printDebug( "Entering modifyUITemplate", NONSENSITIVE );
389 -
390 - if( $sp instanceof SpecialUserlogin ){
391389
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
 390+ if ( !isset( $wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) || !$wgLDAPAddLDAPUsers[$_SESSION['wsDomain']] ) {
 391+ $template->set( 'create', false );
 392+ }
409393
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;
 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' );
416401 }
 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 );
417412 }
418413
419414 /**
Property changes on: trunk/extensions/Lockout
___________________________________________________________________
Deleted: svn:mergeinfo
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( $sp ) {
 8+ function onUserCreateForm( $template ) {
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 - $sp->addFormHeader( '<input type="hidden" name="testwiki-project" value="' . $projectvalue . '" />
 13+ $template->set( 'header', '<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
@@ -132,12 +132,12 @@
133133 }
134134 }
135135
136 -function efAddRequestLoginText( &$sp ) {
 136+function efAddRequestLoginText( &$template ) {
137137 global $wgUser;
138138 wfLoadExtensionMessages( 'ConfirmAccount' );
139139 # Add a link to RequestAccount from UserLogin
140140 if ( !$wgUser->isAllowed( 'createaccount' ) ) {
141 - $sp->addFormHeader( wfMsgExt( 'requestaccount-loginnotice', array( 'parse' ) ) );
 141+ $template->set( 'header', wfMsgExt( 'requestaccount-loginnotice', array( 'parse' ) ) );
142142 }
143143 return true;
144144 }

Follow-up revisions

RevisionCommit summaryAuthorDate
r56942Partial revert of r56693 related to reverts in r56937: use old values for 'us...siebrand09:47, 26 September 2009
r56943Rebuild messages files to remove reverted addition of 'index-category' and 'n...siebrand09:50, 26 September 2009
r56944Partial revert of r56692 related to reverts in r56937: use old values for 'us...siebrand10:10, 26 September 2009
r56945Update branch to r56937happy-melon10:23, 26 September 2009
r56946Changes related to reverts in r56937: use old values for 'userlogin'siebrand11:15, 26 September 2009
r56947Fix bug in HTMLForm where password fields only got the type="password" attrib...happy-melon11:20, 26 September 2009
r56963Revert r56696. Even though mentioned in r56937 ("Revert broken rewrite of log...siebrand23:47, 26 September 2009

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r56682Document and tidy HTMLForm.php. :Phappy-melon18:50, 20 September 2009
r56683New function for ExternalUser to get the local User corresponding to the exte...happy-melon18:53, 20 September 2009
r56684Merge in Login rewrite, second time lucky.happy-melon20:28, 20 September 2009
r56686Follow-ups to r56684: add a member function for extensions to add header text...happy-melon21:24, 20 September 2009
r56688(bug16979) Add tracking categories for __INDEX__ and __NOINDEX__.happy-melon22:14, 20 September 2009
r56696Follow-up to r56684; fix newuser log.happy-melon10:57, 21 September 2009
r56699Follow-up to r56684 - stop people registering invalid usernames.happy-melon11:58, 21 September 2009
r56702Follow-up to r56684 - set message keys in Login.php, and readd the ExternalUs...happy-melon13:53, 21 September 2009
r56703Follow-up to r56683 - unnecessary abstractionhappy-melon14:18, 21 September 2009
r56704Follow-up to r56684 - fix message parameters for Login::ILLEGALhappy-melon14:19, 21 September 2009
r56782Allow extra buttons and hidden fields to be included in HTMLForm. Accessible...happy-melon20:05, 22 September 2009
r56896Improve HTMLForm's support for adding content in and around the form, to impr...happy-melon21:25, 24 September 2009

Comments

#Comment by Happy-melon (talk | contribs)   08:43, 26 September 2009

rev:56688 has nothing to do with the Login stuff. Nor does rev:56683 really.

Status & tagging log