diff options
9 files changed, 137 insertions, 124 deletions
diff --git a/it/it-tests/src/test/resources/user/MyAccountPageTest/should_change_password.html b/it/it-tests/src/test/resources/user/MyAccountPageTest/should_change_password.html index 50da6be2ed7..12e4a89af7d 100644 --- a/it/it-tests/src/test/resources/user/MyAccountPageTest/should_change_password.html +++ b/it/it-tests/src/test/resources/user/MyAccountPageTest/should_change_password.html @@ -45,16 +45,6 @@ <td></td> </tr> <tr> - <td>click</td> - <td>id=change-password</td> - <td></td> -</tr> -<tr> - <td>waitForElementPresent</td> - <td>id=old_password</td> - <td></td> -</tr> -<tr> <td>type</td> <td>id=old_password</td> <td>password</td> @@ -71,7 +61,12 @@ </tr> <tr> <td>click</td> - <td>commit</td> + <td>id=change-password</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.alert-success</td> <td></td> </tr> <tr> diff --git a/server/sonar-web/src/main/js/api/users.js b/server/sonar-web/src/main/js/api/users.js index ec3052731c0..e9fc97df7b9 100644 --- a/server/sonar-web/src/main/js/api/users.js +++ b/server/sonar-web/src/main/js/api/users.js @@ -18,8 +18,20 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import $ from 'jquery'; +import { post, postJSON } from '../helpers/request.js'; export function getCurrentUser () { const url = baseUrl + '/api/users/current'; return $.get(url); } + +export function changePassword (login, password, previousPassword) { + const url = window.baseUrl + '/api/users/change_password'; + const data = { login, password }; + + if (previousPassword != null) { + data.previousPassword = previousPassword; + } + + return post(url, data); +} diff --git a/server/sonar-web/src/main/js/apps/account/change-password-view.js b/server/sonar-web/src/main/js/apps/account/change-password-view.js deleted file mode 100644 index 8f0cd4bc13d..00000000000 --- a/server/sonar-web/src/main/js/apps/account/change-password-view.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import $ from 'jquery'; -import ModalForm from '../../components/common/modal-form'; -import Template from './templates/account-change-password.hbs'; -import { translate } from '../../helpers/l10n'; - -export default ModalForm.extend({ - template: Template, - - onFormSubmit: function () { - ModalForm.prototype.onFormSubmit.apply(this, arguments); - if (this.checkPasswords()) { - this.sendRequest(); - } else { - this.showErrors([{ msg: translate('user.password_doesnt_match_confirmation') }]); - } - }, - - checkPasswords: function () { - var p1 = this.$('#password').val(), - p2 = this.$('#password_confirmation').val(); - return p1 === p2; - }, - - sendRequest: function () { - var that = this; - var data = { - login: window.SS.user, - password: this.$('#password').val(), - previousPassword: this.$('#old_password').val() - }; - var opts = { - type: 'POST', - url: baseUrl + '/api/users/change_password', - data: data, - statusCode: { - // do not show global error - 400: null - } - }; - this.disableForm(); - $.ajax(opts).done(function () { - that.destroy(); - }).fail(function (jqXHR) { - that.enableForm(); - that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings); - }); - } -}); diff --git a/server/sonar-web/src/main/js/apps/account/components/Password.js b/server/sonar-web/src/main/js/apps/account/components/Password.js index 7da00b1e9f6..eaf01092dc0 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Password.js +++ b/server/sonar-web/src/main/js/apps/account/components/Password.js @@ -19,23 +19,119 @@ */ import React, { Component } from 'react'; -import ChangePasswordView from '../change-password-view'; +import { changePassword } from '../../../api/users'; +import { translate } from '../../../helpers/l10n'; export default class Password extends Component { - handleChangePassword () { - new ChangePasswordView().render(); + state = { + success: false, + errors: null + } + + handleSuccessfulChange () { + this.refs.oldPassword.value = ''; + this.refs.password.value = ''; + this.refs.passwordConfirmation.value = ''; + this.setState({ success: true, errors: null }); + } + + handleFailedChange (e) { + e.response.json().then(r => { + this.refs.oldPassword.focus(); + this.setErrors(r.errors.map(e => e.msg)); + }); + } + + setErrors (errors) { + this.setState({ + success: false, + errors + }); + } + + handleChangePassword (e) { + e.preventDefault(); + + const { user } = this.props; + const oldPassword = this.refs.oldPassword.value; + const password = this.refs.password.value; + const passwordConfirmation = this.refs.passwordConfirmation.value; + + if (password !== passwordConfirmation) { + this.refs.password.focus(); + this.setErrors([translate('user.password_doesnt_match_confirmation')]); + } else { + changePassword(user.login, password, oldPassword) + .then(this.handleSuccessfulChange.bind(this)) + .catch(this.handleFailedChange.bind(this)); + } } render () { + const { success, errors } = this.state; + return ( <section> - <h2 className="spacer-bottom">Password</h2> - <button - id="change-password" - onClick={this.handleChangePassword.bind(this)} - type="submit"> - Change Password - </button> + <h2 className="spacer-bottom"> + {translate('my_profile.password.title')} + </h2> + + <form onSubmit={this.handleChangePassword.bind(this)}> + {success && ( + <div className="alert alert-success"> + {translate('my_profile.password.changed')} + </div> + )} + + {errors && errors.map((e, i) => ( + <div key={i} className="alert alert-danger">{e}</div> + ))} + + <div className="modal-field"> + <label htmlFor="old_password"> + {translate('my_profile.password.old')} + <em className="mandatory">*</em> + </label> + <input + ref="oldPassword" + autoComplete="off" + id="old_password" + name="old_password" + required={true} + type="password"/> + </div> + <div className="modal-field"> + <label htmlFor="password"> + {translate('my_profile.password.new')} + <em className="mandatory">*</em> + </label> + <input + ref="password" + autoComplete="off" + id="password" + name="password" + required={true} + type="password"/> + </div> + <div className="modal-field"> + <label htmlFor="password_confirmation"> + {translate('my_profile.password.confirm')} + <em className="mandatory">*</em> + </label> + <input + ref="passwordConfirmation" + autoComplete="off" + id="password_confirmation" + name="password_confirmation" + required={true} + type="password"/> + </div> + <div className="modal-field"> + <button id="change-password" type="submit"> + {translate('my_profile.password.submit')} + </button> + </div> + </form> </section> ); } diff --git a/server/sonar-web/src/main/js/apps/account/components/Security.js b/server/sonar-web/src/main/js/apps/account/components/Security.js index 684fb74c56b..93426874058 100644 --- a/server/sonar-web/src/main/js/apps/account/components/Security.js +++ b/server/sonar-web/src/main/js/apps/account/components/Security.js @@ -31,7 +31,7 @@ export default function Security ({ user }) { {user.canChangePassword && ( <div className="column-half"> - <Password/> + <Password user={user}/> </div> )} </div> diff --git a/server/sonar-web/src/main/js/apps/account/containers/SecurityContainer.js b/server/sonar-web/src/main/js/apps/account/containers/SecurityContainer.js index 4fdb31f19bf..2e6561506f3 100644 --- a/server/sonar-web/src/main/js/apps/account/containers/SecurityContainer.js +++ b/server/sonar-web/src/main/js/apps/account/containers/SecurityContainer.js @@ -17,12 +17,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import React from 'react'; +import { connect } from 'react-redux'; import Security from './../components/Security'; -export default function SecurityContainer () { - const { user } = window.sonarqube; - - return <Security user={user}/>; +function mapStateToProps (state) { + return { + user: state.user + }; } + +export default connect(mapStateToProps)(Security); diff --git a/server/sonar-web/src/main/js/apps/account/store/reducers.js b/server/sonar-web/src/main/js/apps/account/store/reducers.js index 23921840d8b..626a3875ba4 100644 --- a/server/sonar-web/src/main/js/apps/account/store/reducers.js +++ b/server/sonar-web/src/main/js/apps/account/store/reducers.js @@ -48,6 +48,7 @@ function removeProjectNotifications (state, project) { } export const initialState = { + user: window.sonarqube.user, projectNotifications: window.sonarqube.notifications.project }; diff --git a/server/sonar-web/src/main/js/apps/account/templates/account-change-password.hbs b/server/sonar-web/src/main/js/apps/account/templates/account-change-password.hbs deleted file mode 100644 index 8cfe6bde02f..00000000000 --- a/server/sonar-web/src/main/js/apps/account/templates/account-change-password.hbs +++ /dev/null @@ -1,26 +0,0 @@ -<form id="pass_form_tag" name="pass_form_tag"> - <div class="modal-head"> - <h2>{{t 'my_profile.password.title'}}</h2> - </div> - - <div class="modal-body"> - <div class="js-modal-messages"></div> - <div class="modal-field"> - <label for="old_password">{{t 'my_profile.password.old'}}<em class="mandatory">*</em></label> - <input autocomplete="off" id="old_password" name="old_password" required type="password"> - </div> - <div class="modal-field"> - <label for="password">{{t 'my_profile.password.new'}}<em class="mandatory">*</em></label> - <input autocomplete="off" id="password" name="password" required type="password"> - </div> - <div class="modal-field"> - <label for="password_confirmation">{{t 'my_profile.password.confirm'}}<em class="mandatory">*</em></label> - <input autocomplete="off" id="password_confirmation" name="password_confirmation" required type="password"> - </div> - </div> - - <div class="modal-foot"> - <input name="commit" type="submit" value="{{t 'my_profile.password.submit'}}"> - <a class="js-modal-close" href="#">{{t 'cancel'}}</a> - </div> -</form> diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 76b0d18e512..e46298ecc67 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -2141,11 +2141,11 @@ my_profile.email=Email my_profile.groups=Groups my_profile.scm_accounts=SCM Accounts my_profile.password.title=Change password -my_profile.password.old=Old value -my_profile.password.new=New value -my_profile.password.confirm=Confirm new value +my_profile.password.old=Old Password +my_profile.password.new=New Password +my_profile.password.confirm=Confirm Password my_profile.password.submit=Change password -my_profile.password.changed=Password changed +my_profile.password.changed=The password has been changed! my_profile.password.empty=Password can not be empty my_profile.password.wrong_old=Wrong old password my_profile.notifications.submit=Save changes |