From: Happy-melon Date: Wed, 16 Feb 2011 19:49:37 +0000 (+0000) Subject: Create a user.groups module in ResourceLoader, which bundles a CSS and JS page for... X-Git-Tag: 1.31.0-rc.0~31968 X-Git-Url: http://git.cyclocoop.org/data/Fool?a=commitdiff_plain;h=d64cd26a7c771d78405a75c5a2c5eccb2cfe771e;p=lhc%2Fweb%2Fwiklou.git Create a user.groups module in ResourceLoader, which bundles a CSS and JS page for each usergroup the user is a member of (MediaWiki:Sysop.js, MediaWiki:Autoconfirmed.css, etc). Groups '*' and 'user' are not included. --- diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 1e5d4746fa..a33bffa398 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -72,6 +72,8 @@ it from source control: http://www.mediawiki.org/wiki/Download_from_SVN (maintenance/fixDoubleRedirects.php) * (bug 23315) New body classes to allow easier styling of special pages * (bug 27159) Make email confirmation code expiration time configurable +* CSS/JS for each user group is imported from MediaWiki:Sysop.js, + MediaWiki:Autoconfirmed.css, etc. === Bug fixes in 1.18 === * (bug 23119) WikiError class and subclasses are now marked as deprecated diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 198f279af6..be99d843af 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -152,6 +152,7 @@ $wgAutoloadLocalClasses = array( 'LinksUpdate' => 'includes/LinksUpdate.php', 'LocalisationCache' => 'includes/LocalisationCache.php', 'LocalisationCache_BulkLoad' => 'includes/LocalisationCache.php', + 'Login' => 'includes/Login.php', 'LogPage' => 'includes/LogPage.php', 'LogPager' => 'includes/LogEventsList.php', 'LogEventsList' => 'includes/LogEventsList.php', @@ -211,6 +212,7 @@ $wgAutoloadLocalClasses = array( 'ResourceLoaderFileModule' => 'includes/resourceloader/ResourceLoaderFileModule.php', 'ResourceLoaderSiteModule' => 'includes/resourceloader/ResourceLoaderSiteModule.php', 'ResourceLoaderUserModule' => 'includes/resourceloader/ResourceLoaderUserModule.php', + 'ResourceLoaderUserGroupsModule' => 'includes/resourceloader/ResourceLoaderUserGroupsModule.php', 'ResourceLoaderUserOptionsModule' => 'includes/resourceloader/ResourceLoaderUserOptionsModule.php', 'ResourceLoaderStartUpModule' => 'includes/resourceloader/ResourceLoaderStartUpModule.php', 'ReverseChronologicalPager' => 'includes/Pager.php', @@ -579,6 +581,7 @@ $wgAutoloadLocalClasses = array( 'AncientPagesPage' => 'includes/specials/SpecialAncientpages.php', 'BrokenRedirectsPage' => 'includes/specials/SpecialBrokenRedirects.php', 'ContribsPager' => 'includes/specials/SpecialContributions.php', + 'SpecialCreateAccount' => 'includes/specials/SpecialCreateAccount.php', 'DBLockForm' => 'includes/specials/SpecialLockdb.php', 'DBUnlockForm' => 'includes/specials/SpecialUnlockdb.php', 'DeadendPagesPage' => 'includes/specials/SpecialDeadendpages.php', @@ -678,6 +681,7 @@ $wgAutoloadLocalClasses = array( 'UnwatchedpagesPage' => 'includes/specials/SpecialUnwatchedpages.php', 'UploadForm' => 'includes/specials/SpecialUpload.php', 'UploadSourceField' => 'includes/specials/SpecialUpload.php', + 'SpecialUserlogin' => 'includes/specials/SpecialUserlogin.php', 'UserrightsPage' => 'includes/specials/SpecialUserrights.php', 'UsersPager' => 'includes/specials/SpecialListusers.php', 'WantedCategoriesPage' => 'includes/specials/SpecialWantedcategories.php', diff --git a/includes/HTMLForm.php b/includes/HTMLForm.php index 2472bc2d60..b13854906b 100644 --- a/includes/HTMLForm.php +++ b/includes/HTMLForm.php @@ -109,6 +109,7 @@ class HTMLForm { protected $mButtons = array(); protected $mWrapperLegend = false; + protected $mTokenAction = 'Edit'; /** * Build a new HTMLForm from an array of field attributes @@ -184,7 +185,7 @@ class HTMLForm { if ( !$class ) { throw new MWException( "Descriptor with no class: " . print_r( $descriptor, true ) ); } - + $descriptor['fieldname'] = $fieldname; $obj = new $class( $descriptor ); @@ -210,14 +211,15 @@ class HTMLForm { /** * Try submitting, with edit token check first - * @return Status|boolean + * @return Status|boolean */ function tryAuthorizedSubmit() { global $wgUser, $wgRequest; $editToken = $wgRequest->getVal( 'wpEditToken' ); $result = false; - if ( $this->getMethod() != 'post' || $wgUser->matchEditToken( $editToken ) ) { + # FIXME + if ( $wgRequest->wasPosted() ){#&& $this->getMethod() != 'post' || $wgUser->matchEditToken( $editToken ) ) { $result = $this->trySubmit(); } return $result; @@ -249,6 +251,11 @@ class HTMLForm { * display. */ function trySubmit() { + # Check the session tokens + # FIXME + if ( false && !Token::match( null, $this->mTokenAction ) ) { + return array( 'sessionfailure' ); + } # Check for validation foreach ( $this->mFlatFields as $fieldname => $field ) { if ( !empty( $field->mParams['nodata'] ) ) { @@ -424,9 +431,14 @@ class HTMLForm { global $wgUser; $html = ''; - if( $this->getMethod() == 'post' ){ - $html .= Html::hidden( 'wpEditToken', $wgUser->editToken(), array( 'id' => 'wpEditToken' ) ) . "\n"; + # FIXME + $token = new Token( $this->mTokenAction ); + $html .= Html::hidden( + "wp{$this->mTokenAction}Token", + $token->set(), + array( 'id' => 'wpEditToken' ) + ) . "\n"; $html .= Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . "\n"; } @@ -579,6 +591,7 @@ class HTMLForm { $this->mSubmitTooltip = $name; } + /** * Set the id for the submit button. * @param $t String. FIXME: Integrity is *not* validated @@ -607,6 +620,15 @@ class HTMLForm { function setMessagePrefix( $p ) { $this->mMessagePrefix = $p; } + /** + * If you want to protect the form from CSRF by a token other than + * the usual wsEditToken, set something here. + * @see Token::set() + * @param $a + */ + function setTokenAction( $a ){ + $this->mTokenAction = ucfirst( $a ); + } /** * Set the title for form submission @@ -623,7 +645,7 @@ class HTMLForm { function getTitle() { return $this->mTitle; } - + /** * Set the method used to submit the form * @param $method String @@ -631,7 +653,7 @@ class HTMLForm { public function setMethod( $method='post' ){ $this->mMethod = $method; } - + public function getMethod(){ return $this->mMethod; } @@ -840,12 +862,12 @@ abstract class HTMLFormField { if ( isset( $params['name'] ) ) { $this->mName = $params['name']; } - + $validName = Sanitizer::escapeId( $this->mName ); if ( $this->mName != $validName && !isset( $params['nodata'] ) ) { throw new MWException( "Invalid name '{$this->mName}' passed to " . __METHOD__ ); } - + $this->mID = "mw-input-{$this->mName}"; if ( isset( $params['default'] ) ) { @@ -887,10 +909,10 @@ abstract class HTMLFormField { global $wgRequest; $errors = $this->validate( $value, $this->mParent->mFieldData ); - + $cellAttributes = array(); $verticalLabel = false; - + if ( !empty($this->mParams['vertical-label']) ) { $cellAttributes['colspan'] = 2; $verticalLabel = true; @@ -908,9 +930,9 @@ abstract class HTMLFormField { array( 'class' => 'mw-input' ) + $cellAttributes, $this->getInputHTML( $value ) . "\n$errors" ); - + $fieldType = get_class( $this ); - + if ($verticalLabel) { $html = Html::rawElement( 'tr', array( 'class' => 'mw-htmlform-vertical-label' ), $label ); @@ -1139,11 +1161,11 @@ class HTMLFloatField extends HTMLTextField { if ( $p !== true ) { return $p; } - + $value = trim( $value ); # http://dev.w3.org/html5/spec/common-microsyntaxes.html#real-numbers - # with the addition that a leading '+' sign is ok. + # with the addition that a leading '+' sign is ok. if ( !preg_match( '/^((\+|\-)?\d+(\.\d+)?(E(\+|\-)?\d+)?)?$/i', $value ) ) { return wfMsgExt( 'htmlform-float-invalid', 'parse' ); } @@ -1182,8 +1204,8 @@ class HTMLIntField extends HTMLFloatField { } # http://dev.w3.org/html5/spec/common-microsyntaxes.html#signed-integers - # with the addition that a leading '+' sign is ok. Note that leading zeros - # are fine, and will be left in the input, which is useful for things like + # with the addition that a leading '+' sign is ok. Note that leading zeros + # are fine, and will be left in the input, which is useful for things like # phone numbers when you know that they are integers (the HTML5 type=tel # input does not require its value to be numeric). If you want a tidier # value to, eg, save in the DB, clean it up with intval(). @@ -1415,8 +1437,8 @@ class HTMLMultiSelectField extends HTMLFormField { } else { $thisAttribs = array( 'id' => "{$this->mID}-$info", 'value' => $info ); - $checkbox = Xml::check( - $this->mName . '[]', + $checkbox = Xml::check( + $this->mName . '[]', in_array( $info, $value, true ), $attribs + $thisAttribs ); $checkbox .= ' ' . Html::rawElement( 'label', array( 'for' => "{$this->mID}-$info" ), $label ); @@ -1556,7 +1578,7 @@ class HTMLInfoField extends HTMLFormField { class HTMLHiddenField extends HTMLFormField { public function __construct( $params ) { parent::__construct( $params ); - + # Per HTML5 spec, hidden fields cannot be 'required' # http://dev.w3.org/html5/spec/states-of-the-type-attribute.html#hidden-state unset( $this->mParams['required'] ); @@ -1605,7 +1627,7 @@ class HTMLSubmitField extends HTMLFormField { protected function needsLabel() { return false; } - + /** * Button cannot be invalid */ diff --git a/includes/OutputPage.php b/includes/OutputPage.php index ca2acbf88c..f03e0e96d0 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -2555,28 +2555,29 @@ class OutputPage { // Legacy Scripts $scripts .= "\n" . $this->mScripts; + $userScripts = array( 'user.options' ); + // Add site JS if enabled if ( $wgUseSiteJs ) { $scripts .= $this->makeResourceLoaderLink( $sk, 'site', ResourceLoaderModule::TYPE_SCRIPTS ); + if( $wgUser->isLoggedIn() ){ + $userScripts[] = 'user.groups'; + } } - // Add user JS if enabled - trying to load user.options as a bundle if possible - $userOptionsAdded = false; + // Add user JS if enabled if ( $wgAllowUserJs && $wgUser->isLoggedIn() ) { $action = $wgRequest->getVal( 'action', 'view' ); if( $this->mTitle && $this->mTitle->isJsSubpage() && $sk->userCanPreview( $action ) ) { # XXX: additional security check/prompt? $scripts .= Html::inlineScript( "\n" . $wgRequest->getText( 'wpTextbox1' ) . "\n" ) . "\n"; } else { - $scripts .= $this->makeResourceLoaderLink( - $sk, array( 'user', 'user.options' ), ResourceLoaderModule::TYPE_SCRIPTS - ); - $userOptionsAdded = true; + # FIXME: this means that User:Me/Common.js doesn't load when previewing + # User:Me/Vector.js, and vice versa (bug26283) + $userScripts[] = 'user'; } } - if ( !$userOptionsAdded ) { - $scripts .= $this->makeResourceLoaderLink( $sk, 'user.options', ResourceLoaderModule::TYPE_SCRIPTS ); - } + $scripts .= $this->makeResourceLoaderLink( $sk, $userScripts, ResourceLoaderModule::TYPE_SCRIPTS ); return $scripts; } diff --git a/includes/Skin.php b/includes/Skin.php index 489bda8be5..57e535f685 100644 --- a/includes/Skin.php +++ b/includes/Skin.php @@ -546,7 +546,7 @@ abstract class Skin extends Linker { * @private */ function setupUserCss( OutputPage $out ) { - global $wgRequest; + global $wgRequest, $wgUser; global $wgUseSiteCss, $wgAllowUserCss, $wgAllowUserCssPrefs; wfProfileIn( __METHOD__ ); @@ -560,6 +560,9 @@ abstract class Skin extends Linker { // Per-site custom styles if ( $wgUseSiteCss ) { $out->addModuleStyles( 'site' ); + if( $wgUser->isLoggedIn() ){ + $out->addModuleStyles( 'user.groups' ); + } } // Per-user custom styles diff --git a/includes/User.php b/includes/User.php index 701d4d5092..de99114739 100644 --- a/includes/User.php +++ b/includes/User.php @@ -4,12 +4,6 @@ * @file */ -/** - * Int Number of characters in user_token field. - * @ingroup Constants - */ -define( 'USER_TOKEN_LENGTH', 32 ); - /** * Int Serialized record version. * @ingroup Constants @@ -41,13 +35,6 @@ class PasswordError extends MWException { * of the database. */ class User { - /** - * Global constants made accessible as class constants so that autoloader - * magic can be used. - */ - const USER_TOKEN_LENGTH = USER_TOKEN_LENGTH; - const MW_USER_VERSION = MW_USER_VERSION; - const EDIT_TOKEN_SUFFIX = EDIT_TOKEN_SUFFIX; /** * Array of Strings List of member variables which are saved to the @@ -377,7 +364,7 @@ class User { /** * Create a new user object from a user row. * The row should have all fields from the user table in it. - * @param $row Array A row from the user table + * @param $row array A row from the user table * @return User */ static function newFromRow( $row ) { @@ -627,6 +614,7 @@ class User { if( !wfRunHooks( 'isValidPassword', array( $password, &$result, $this ) ) ) return $result; + if ( $result === false ) { if( strlen( $password ) < $wgMinimalPasswordLength ) { return 'passwordtooshort'; @@ -1250,6 +1238,9 @@ class User { // Deprecated, but kept for backwards-compatibility config return false; } + + + if( in_array( wfGetIP(), $wgRateLimitsExcludedIPs ) ) { // No other good way currently to disable rate limits // for specific IPs. :P @@ -1786,7 +1777,7 @@ class User { } if( !$this->isValidPassword( $str ) ) { - global $wgMinimalPasswordLength; + global $wgMinimalPasswordLength; $valid = $this->getPasswordValidity( $str ); if ( is_array( $valid ) ) { $message = array_shift( $valid ); @@ -1796,7 +1787,7 @@ class User { $params = array( $wgMinimalPasswordLength ); } throw new PasswordError( wfMsgExt( $message, array( 'parsemag' ), $params ) ); - } + } } if( !$wgAuth->setPassword( $this, $str ) ) { @@ -2206,6 +2197,7 @@ class User { } /** + * Check if user is allowed to access a feature / make an action * @param $action String action to be checked * @return Boolean: True if action is allowed, else false @@ -2529,8 +2521,8 @@ class User { 'user_newpassword' => $this->mNewpassword, 'user_newpass_time' => $dbw->timestampOrNull( $this->mNewpassTime ), 'user_real_name' => $this->mRealName, - 'user_email' => $this->mEmail, - 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ), + 'user_email' => $this->mEmail, + 'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ), 'user_options' => '', 'user_touched' => $dbw->timestamp( $this->mTouched ), 'user_token' => $this->mToken, @@ -2589,7 +2581,6 @@ class User { } $dbw = wfGetDB( DB_MASTER ); $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' ); - $fields = array( 'user_id' => $seqVal, 'user_name' => $name, @@ -2801,7 +2792,7 @@ class User { // are shorter than this, doesn't mean people wont be able // to. Certain authentication plugins do NOT want to save // domain passwords in a mysql database, so we should - // check this (in case $wgAuth->strict() is false). + // check this (incase $wgAuth->strict() is false). if( !$this->isValidPassword( $password ) ) { return false; } @@ -2860,7 +2851,7 @@ class User { return EDIT_TOKEN_SUFFIX; } else { if( !isset( $_SESSION['wsEditToken'] ) ) { - $token = self::generateToken(); + $token = $this->generateToken(); $_SESSION['wsEditToken'] = $token; } else { $token = $_SESSION['wsEditToken']; @@ -2878,7 +2869,7 @@ class User { * @param $salt String Optional salt value * @return String The new random token */ - public static function generateToken( $salt = '' ) { + function generateToken( $salt = '' ) { $token = dechex( mt_rand() ) . dechex( mt_rand() ); return md5( $token . $salt ); } @@ -2986,7 +2977,7 @@ class User { $now = time(); $expires = $now + $wgUserEmailConfirmationTokenExpiry; $expiration = wfTimestamp( TS_MW, $expires ); - $token = self::generateToken( $this->mId . $this->mEmail . $expires ); + $token = wfGenerateToken( $this->mId . $this->mEmail . $expires ); $hash = md5( $token ); $this->load(); $this->mEmailToken = $hash; @@ -3140,7 +3131,7 @@ class User { * Get the timestamp of account creation. * * @return String|Bool Timestamp of account creation, or false for - * non-existent/anonymous user accounts. + * non-existent/anonymous user accounts. */ public function getRegistration() { return $this->getId() > 0 @@ -3152,7 +3143,7 @@ class User { * Get the timestamp of the first edit * * @return String|Bool Timestamp of first edit, or false for - * non-existent/anonymous user accounts. + * non-existent/anonymous user accounts. */ public function getFirstEditTimestamp() { if( $this->getId() == 0 ) { @@ -3342,9 +3333,9 @@ class User { * * @param $group String: the group to check for whether it can add/remove * @return Array array( 'add' => array( addablegroups ), - * 'remove' => array( removablegroups ), - * 'add-self' => array( addablegroups to self), - * 'remove-self' => array( removable groups from self) ) + * 'remove' => array( removablegroups ), + * 'add-self' => array( addablegroups to self), + * 'remove-self' => array( removable groups from self) ) */ static function changeableByGroup( $group ) { global $wgAddGroups, $wgRemoveGroups, $wgGroupsAddToSelf, $wgGroupsRemoveFromSelf; @@ -3582,31 +3573,27 @@ class User { * @param $byEmail Boolean: account made by email? * @param $reason String: user supplied reason */ - public function addNewUserLogEntry( $byEmail = false, $reason = '' ) { - global $wgUser, $wgContLang, $wgNewUserLog; + public function addNewUserLogEntry( $creator, $byEmail = false ) { + global $wgUser, $wgNewUserLog; if( empty( $wgNewUserLog ) ) { return true; // disabled } - if( $this->getName() == $wgUser->getName() ) { - $action = 'create'; - } else { - $action = 'create2'; - if ( $byEmail ) { - if ( $reason === '' ) { - $reason = wfMsgForContent( 'newuserlog-byemail' ); - } else { - $reason = $wgContLang->commaList( array( - $reason, wfMsgForContent( 'newuserlog-byemail' ) ) ); - } - } - } + $action = ( $creator == $wgUser ) + ? 'create2' # Safe to publish the creator + : 'create'; # Creator is an IP, don't splash it all over Special:Log + + $message = $byEmail + ? wfMsgForContent( 'newuserlog-byemail' ) + : ''; + $log = new LogPage( 'newusers' ); $log->addEntry( $action, $this->getUserPage(), - $reason, - array( $this->getId() ) + $message, + array( $this->getId() ), + $creator ); return true; } @@ -3616,12 +3603,18 @@ class User { * Used by things like CentralAuth and perhaps other authplugins. */ public function addNewUserLogEntryAutoCreate() { - global $wgNewUserLog, $wgLogAutocreatedAccounts; - if( !$wgNewUserLog || !$wgLogAutocreatedAccounts ) { + global $wgNewUserLog; + if( empty( $wgNewUserLog ) ) { return true; // disabled } $log = new LogPage( 'newusers', false ); - $log->addEntry( 'autocreate', $this->getUserPage(), '', array( $this->getId() ) ); + $log->addEntry( + 'autocreate', + $this->getUserPage(), + '', + array( $this->getId() ), + $this->getId() + ); return true; } diff --git a/includes/resourceloader/ResourceLoaderUserGroupsModule.php b/includes/resourceloader/ResourceLoaderUserGroupsModule.php new file mode 100644 index 0000000000..c81a999b2b --- /dev/null +++ b/includes/resourceloader/ResourceLoaderUserGroupsModule.php @@ -0,0 +1,61 @@ +getUser() ) { + $user = User::newFromName( $context->getUser() ); + if( $user instanceof User ){ + $pages = array(); + foreach( $user->getEffectiveGroups() as $group ){ + if( in_array( $group, array( '*', 'user' ) ) ){ + continue; + } + $g = ucfirst( $group ); + $pages["MediaWiki:$g.js"] = array( 'type' => 'script' ); + $pages["MediaWiki:$g.css"] = array( 'type' => 'style' ); + } + return $pages; + } + } + return array(); + } + + /* Methods */ + + public function getGroup() { + return 'user'; + } + + public function getFlip( $context ) { + global $wgContLang; + + return $wgContLang->getDir() !== $context->getDirection(); + } +} diff --git a/includes/specials/SpecialResetpass.php b/includes/specials/SpecialResetpass.php index 256cc90394..fd8bdd809b 100644 --- a/includes/specials/SpecialResetpass.php +++ b/includes/specials/SpecialResetpass.php @@ -27,14 +27,69 @@ * @ingroup SpecialPage */ class SpecialResetpass extends SpecialPage { + + public $mFormFields = array( + 'NameInfo' => array( + 'type' => 'info', + 'label-message' => 'yourname', + 'default' => '', + ), + 'Name' => array( + 'type' => 'hidden', + 'name' => 'wpName', + 'default' => null, + ), + 'OldPassword' => array( + 'type' => 'password', + 'label-message' => 'oldpassword', + 'size' => '20', + 'id' => 'wpPassword', + 'required' => '', + ), + 'NewPassword' => array( + 'type' => 'password', + 'label-message' => 'newpassword', + 'size' => '20', + 'id' => 'wpNewPassword', + 'required' => '', + ), + 'Retype' => array( + 'type' => 'password', + 'label-message' => 'retypenew', + 'size' => '20', + 'id' => 'wpRetype', + 'required' => '', + ), + 'Remember' => array( + 'type' => 'check', + 'id' => 'wpRemember', + ), + ); + + protected $mUsername; + protected $mLogin; + public function __construct() { + global $wgRequest, $wgUser, $wgLang, $wgCookieExpiration; + parent::__construct( 'Resetpass' ); + $this->mFormFields['Retype']['validation-callback'] = array( 'SpecialCreateAccount', 'formValidateRetype' ); + + $this->mUsername = $wgRequest->getVal( 'wpName', $wgUser->getName() ); + $this->mReturnTo = $wgRequest->getVal( 'returnto' ); + $this->mReturnToQuery = $wgRequest->getVal( 'returntoquery' ); + + $this->mFormFields['Remember']['label'] = wfMsgExt( + 'remembermypassword', + 'parseinline', + $wgLang->formatNum( ceil( $wgCookieExpiration / 86400 ) ) + ); } /** * Main execution point */ - function execute( $par ) { + public function execute( $par ) { global $wgUser, $wgAuth, $wgOut, $wgRequest; if ( wfReadOnly() ) { @@ -42,196 +97,134 @@ class SpecialResetpass extends SpecialPage { return; } - $this->mUserName = $wgRequest->getVal( 'wpName' ); - $this->mOldpass = $wgRequest->getVal( 'wpPassword' ); - $this->mNewpass = $wgRequest->getVal( 'wpNewPassword' ); - $this->mRetype = $wgRequest->getVal( 'wpRetype' ); - $this->mDomain = $wgRequest->getVal( 'wpDomain' ); - $this->setHeaders(); $this->outputHeader(); $wgOut->disallowUserJs(); - if( !$wgRequest->wasPosted() && !$wgUser->isLoggedIn() ) { - $this->error( wfMsg( 'resetpass-no-info' ) ); - return; + if( wfReadOnly() ){ + $wgOut->readOnlyPage(); + return false; } - - if( $wgRequest->wasPosted() && $wgRequest->getBool( 'wpCancel' ) ) { - $this->doReturnTo(); - return; + if( !$wgAuth->allowPasswordChange() ) { + $wgOut->showErrorPage( 'errorpagetitle', 'resetpass_forbidden' ); + return false; } - if( $wgRequest->wasPosted() && $wgUser->matchEditToken( $wgRequest->getVal( 'token' ) ) ) { - try { - $wgAuth->setDomain( $this->mDomain ); - if( !$wgAuth->allowPasswordChange() ) { - $this->error( wfMsg( 'resetpass_forbidden' ) ); - return; - } - - $this->attemptReset( $this->mNewpass, $this->mRetype ); - $wgOut->addWikiMsg( 'resetpass_success' ); - if( !$wgUser->isLoggedIn() ) { - LoginForm::setLoginToken(); - $token = LoginForm::getLoginToken(); - $data = array( - 'action' => 'submitlogin', - 'wpName' => $this->mUserName, - 'wpDomain' => $this->mDomain, - 'wpLoginToken' => $token, - 'wpPassword' => $this->mNewpass, - 'returnto' => $wgRequest->getVal( 'returnto' ), - ); - if( $wgRequest->getCheck( 'wpRemember' ) ) { - $data['wpRemember'] = 1; - } - $login = new LoginForm( new FauxRequest( $data, true ) ); - $login->execute( null ); - } - $this->doReturnTo(); - } catch( PasswordError $e ) { - $this->error( $e->getMessage() ); - } - } - $this->showForm(); - } - - function doReturnTo() { - global $wgRequest, $wgOut; - $titleObj = Title::newFromText( $wgRequest->getVal( 'returnto' ) ); - if ( !$titleObj instanceof Title ) { - $titleObj = Title::newMainPage(); + if( !$wgRequest->wasPosted() && !$wgUser->isLoggedIn() ) { + $wgOut->showErrorPage( 'errorpagetitle', 'resetpass-no-info' ); + return false; } - $wgOut->redirect( $titleObj->getFullURL() ); - } - function error( $msg ) { - global $wgOut; - $wgOut->addHTML( Xml::element('p', array( 'class' => 'error' ), $msg ) ); + $this->getForm()->show(); + } - function showForm() { - global $wgOut, $wgUser, $wgRequest, $wgLivePasswordStrengthChecks; + public function formSubmitCallback( $data ){ + $data['Password'] = $data['OldPassword']; + $this->mLogin = new Login( $data ); + $result = $this->attemptReset( $data ); - if ( $wgLivePasswordStrengthChecks ) { - $wgOut->addPasswordSecurity( 'wpNewPassword', 'wpRetype' ); - } - $self = $this->getTitle(); - if ( !$this->mUserName ) { - $this->mUserName = $wgUser->getName(); + if( $result === true ){ + # Log the user in if they're not already (ie we're + # coming from the e-mail-password-reset route + global $wgUser; + if( !$wgUser->isLoggedIn() ) { + $this->mLogin->attemptLogin( $data['NewPassword'] ); + # Redirect out to the appropriate target. + SpecialUserlogin::successfulLogin( + 'resetpass_success', + $this->mReturnTo, + $this->mReturnToQuery, + $this->mLogin->mLoginResult + ); + } else { + # Redirect out to the appropriate target. + SpecialUserlogin::successfulLogin( + 'resetpass_success', + $this->mReturnTo, + $this->mReturnToQuery + ); + } + return true; + } else { + return $result; } - $rememberMe = ''; - if ( !$wgUser->isLoggedIn() ) { - global $wgCookieExpiration, $wgLang; - $rememberMe = '' . - '' . - '' . - Xml::checkLabel( - wfMsgExt( 'remembermypassword', 'parsemag', $wgLang->formatNum( ceil( $wgCookieExpiration / ( 3600 * 24 ) ) ) ), - 'wpRemember', 'wpRemember', - $wgRequest->getCheck( 'wpRemember' ) ) . - '' . - ''; + } + + public function getForm( $reset=false ) { + global $wgOut, $wgUser, $wgRequest; + + if( $reset || $wgRequest->getCheck( 'reset' ) ){ + # Request is coming from Special:UserLogin after it + # authenticated someone with a temporary password. + $this->mFormFields['OldPassword']['label-message'] = 'resetpass-temp-password'; $submitMsg = 'resetpass_submit'; - $oldpassMsg = 'resetpass-temp-password'; + $this->mFormFields['OldPassword']['default'] = $wgRequest->getText( 'wpPassword' ); + #perpetuate + $this->mFormFields['reset'] = array( + 'type' => 'hidden', + 'default' => '1', + ); } else { - $oldpassMsg = 'oldpassword'; + unset( $this->mFormFields['Remember'] ); $submitMsg = 'resetpass-submit-loggedin'; } - $wgOut->addHTML( - Xml::fieldset( wfMsg( 'resetpass_header' ) ) . - Xml::openElement( 'form', - array( - 'method' => 'post', - 'action' => $self->getLocalUrl(), - 'id' => 'mw-resetpass-form' ) ) . "\n" . - Html::hidden( 'token', $wgUser->editToken() ) . "\n" . - Html::hidden( 'wpName', $this->mUserName ) . "\n" . - Html::hidden( 'wpDomain', $this->mDomain ) . "\n" . - Html::hidden( 'returnto', $wgRequest->getVal( 'returnto' ) ) . "\n" . - wfMsgExt( 'resetpass_text', array( 'parse' ) ) . "\n" . - Xml::openElement( 'table', array( 'id' => 'mw-resetpass-table' ) ) . "\n" . - $this->pretty( array( - array( 'wpName', 'username', 'text', $this->mUserName, '' ), - array( 'wpPassword', $oldpassMsg, 'password', $this->mOldpass, '' ), - array( 'wpNewPassword', 'newpassword', 'password', null, '
' ), - array( 'wpRetype', 'retypenew', 'password', null, '
' ), - ) ) . "\n" . - $rememberMe . - "\n" . - "\n" . - '' . - Xml::submitButton( wfMsg( $submitMsg ) ) . - Xml::submitButton( wfMsg( 'resetpass-submit-cancel' ), array( 'name' => 'wpCancel' ) ) . - "\n" . - "\n" . - Xml::closeElement( 'table' ) . - Xml::closeElement( 'form' ) . - Xml::closeElement( 'fieldset' ) . "\n" - ); - } - function pretty( $fields ) { - $out = ''; - foreach ( $fields as $list ) { - list( $name, $label, $type, $value, $extra ) = $list; - if( $type == 'text' ) { - $field = htmlspecialchars( $value ); - } else { - $attribs = array( 'id' => $name ); - if ( $name == 'wpNewPassword' || $name == 'wpRetype' ) { - $attribs = array_merge( $attribs, - User::passwordChangeInputAttribs() ); - } - if ( $name == 'wpPassword' ) { - $attribs[] = 'autofocus'; - } - $field = Html::input( $name, $value, $type, $attribs ); - } - $out .= "\n"; - $out .= "\t"; - if ( $type != 'text' ) - $out .= Xml::label( wfMsg( $label ), $name ); - else - $out .= wfMsgHtml( $label ); - $out .= "\n"; - $out .= "\t$field\n"; - $out .= "\t$extra\n"; - $out .= ""; - } - return $out; + $this->mFormFields['Name']['default'] = + $this->mFormFields['NameInfo']['default'] = $this->mUsername; + + $form = new HTMLForm( $this->mFormFields, '' ); + $form->suppressReset(); + $form->setSubmitText( wfMsg( $submitMsg ) ); + $form->setTitle( $this->getTitle() ); + $form->addHiddenField( 'returnto', $this->mReturnTo ); + $form->setWrapperLegend( wfMsg( 'resetpass_header' ) ); + + $form->setSubmitCallback( array( $this, 'formSubmitCallback' ) ); + $form->loadData(); + + return $form; } /** - * @throws PasswordError when cannot set the new password because requirements not met. + * Try to reset the user's password */ - protected function attemptReset( $newpass, $retype ) { - $user = User::newFromName( $this->mUserName ); - if( !$user || $user->isAnon() ) { - throw new PasswordError( 'no such user' ); + protected function attemptReset( $data ) { + + if( !$data['Name'] + || !$data['OldPassword'] + || !$data['NewPassword'] + || !$data['Retype'] ) + { + return false; } - - if( $newpass !== $retype ) { - wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'badretype' ) ); - throw new PasswordError( wfMsg( 'badretype' ) ); + + $user = $this->mLogin->getUser(); + if( !( $user instanceof User ) ){ + return wfMsgExt( 'nosuchuser', 'parse' ); } - if( !$user->checkTemporaryPassword($this->mOldpass) && !$user->checkPassword($this->mOldpass) ) { - wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'wrongpassword' ) ); - throw new PasswordError( wfMsg( 'resetpass-wrong-oldpass' ) ); + if( $data['NewPassword'] !== $data['Retype'] ) { + wfRunHooks( 'PrefsPasswordAudit', array( $user, $data['NewPassword'], 'badretype' ) ); + return wfMsgExt( 'badretype', 'parse' ); } - + + if( !$user->checkPassword( $data['OldPassword'] ) && !$user->checkTemporaryPassword( $data['OldPassword'] ) ) + { + wfRunHooks( 'PrefsPasswordAudit', array( $user, $data['NewPassword'], 'wrongpassword' ) ); + return wfMsgExt( 'resetpass-wrong-oldpass', 'parse' ); + } + try { - $user->setPassword( $this->mNewpass ); - wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'success' ) ); - $this->mNewpass = $this->mOldpass = $this->mRetypePass = ''; + $user->setPassword( $data['NewPassword'] ); + wfRunHooks( 'PrefsPasswordAudit', array( $user, $data['NewPassword'], 'success' ) ); } catch( PasswordError $e ) { - wfRunHooks( 'PrefsPasswordAudit', array( $user, $newpass, 'error' ) ); - throw new PasswordError( $e->getMessage() ); + wfRunHooks( 'PrefsPasswordAudit', array( $user, $data['NewPassword'], 'error' ) ); + return $e->getMessage(); } - + $user->setCookies(); $user->saveSettings(); + return true; } } diff --git a/resources/Resources.php b/resources/Resources.php index 553c8ae08e..085462cbd3 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -8,6 +8,7 @@ return array( 'startup' => array( 'class' => 'ResourceLoaderStartUpModule' ), 'user' => array( 'class' => 'ResourceLoaderUserModule' ), 'user.options' => array( 'class' => 'ResourceLoaderUserOptionsModule' ), + 'user.groups' => array( 'class' => 'ResourceLoaderUserGroupsModule' ), /* Skins */