diff --git a/client/app/assets/less/ant.less b/client/app/assets/less/ant.less index c9985fa36d..4673f19e7e 100644 --- a/client/app/assets/less/ant.less +++ b/client/app/assets/less/ant.less @@ -1,5 +1,6 @@ @import '~antd/lib/style/core/iconfont'; @import '~antd/lib/style/core/motion'; +@import '~antd/lib/alert/style/index'; @import '~antd/lib/input/style/index'; @import '~antd/lib/input-number/style/index'; @import '~antd/lib/date-picker/style/index'; diff --git a/client/app/components/dynamic-form/DynamicForm.jsx b/client/app/components/dynamic-form/DynamicForm.jsx index f3d2eae656..558db28630 100644 --- a/client/app/components/dynamic-form/DynamicForm.jsx +++ b/client/app/components/dynamic-form/DynamicForm.jsx @@ -180,7 +180,7 @@ export const DynamicForm = Form.create()(class DynamicForm extends React.Compone htmlType: 'button', className: action.pullRight ? 'pull-right m-t-10' : 'm-t-10', type: action.type, - disabled: inProgress || (isFieldsTouched() && action.disableWhenDirty), + disabled: (isFieldsTouched() && action.disableWhenDirty), loading: inProgress, onClick: this.handleAction, }; diff --git a/client/app/components/users/UserEdit.jsx b/client/app/components/users/UserEdit.jsx index edcf9ff4d6..9192bc4170 100644 --- a/client/app/components/users/UserEdit.jsx +++ b/client/app/components/users/UserEdit.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import Alert from 'antd/lib/alert'; import Button from 'antd/lib/button'; import Icon from 'antd/lib/icon'; import Input from 'antd/lib/input'; @@ -6,7 +7,8 @@ import Tooltip from 'antd/lib/tooltip'; import Modal from 'antd/lib/modal'; import { react2angular } from 'react2angular'; import { User } from '@/services/user'; -import { currentUser } from '@/services/auth'; +import { currentUser, clientConfig } from '@/services/auth'; +import { absoluteUrl } from '@/services/utils'; import { UserProfile } from '../proptypes'; import { DynamicForm } from '../dynamic-form/DynamicForm'; @@ -17,7 +19,11 @@ export class UserEdit extends React.Component { constructor(props) { super(props); - this.state = { user: this.props.user, changePassword: false }; + this.state = { + user: this.props.user, + changingPassword: false, + sendingPasswordEmail: false, + }; } handleSave = (values, onSuccess, onError) => { @@ -44,7 +50,18 @@ export class UserEdit extends React.Component { }; openChangePasswordModal = () => { - this.setState({ changePassword: true }); + this.setState({ changingPassword: true }); + }; + + sendPasswordReset = () => { + const { user } = this.state; + this.setState({ sendingPasswordEmail: true }); + + User.sendPasswordReset(user).then((passwordResetLink) => { + this.setState({ passwordResetLink }); + }).finally(() => { + this.setState({ sendingPasswordEmail: false }); + }); }; changePasswordModal() { @@ -56,9 +73,9 @@ export class UserEdit extends React.Component { return ( { this.setState({ changePassword: false }); }} + onCancel={() => { this.setState({ changingPassword: false }); }} footer={null} destroyOnClose > @@ -69,11 +86,9 @@ export class UserEdit extends React.Component { regenerateApiKey = () => { const doRegenerate = () => { - User.regenerateApiKey(this.state.user).then(({ data }) => { - if (data) { - const { user } = this.state; - this.setState({ user: { ...user, apiKey: data.api_key } }); - } + User.regenerateApiKey(this.state.user).then((apiKey) => { + const { user } = this.state; + this.setState({ user: { ...user, apiKey } }); }); }; @@ -105,6 +120,37 @@ export class UserEdit extends React.Component { ); } + renderPasswordReset() { + const { user, sendingPasswordEmail, passwordResetLink } = this.state; + + return ( + + + {passwordResetLink && + + The mail server is not configured, please send the following link + to {user.name} to reset their password: + +

+ ) : 'The user should receive a link to reset their password by email soon.'} + type="success" + className="m-t-20" + afterClose={() => { this.setState({ passwordResetLink: null }); }} + closable + /> + } +
+ ); + } + render() { const { user } = this.state; @@ -136,13 +182,13 @@ export class UserEdit extends React.Component {

{user.name}


- {this.changePasswordModal()} {!user.isDisabled && ( {this.renderApiKey()}
+ {this.changePasswordModal()} - {currentUser.isAdmin && } + {currentUser.isAdmin && this.renderPasswordReset()}
)} diff --git a/client/app/pages/users/show.html b/client/app/pages/users/show.html index b64db680e7..85223c801c 100644 --- a/client/app/pages/users/show.html +++ b/client/app/pages/users/show.html @@ -7,8 +7,6 @@
-
-
This user is disabled. @@ -16,22 +14,8 @@
-
- -
-

- The user should receive a link to reset their password by email soon. -

-

- You don't have mail server configured, please send the following link - to {{user.name}} to reset their password:
- -

-
diff --git a/client/app/pages/users/show.js b/client/app/pages/users/show.js index e2014c4aa4..538c1a2031 100644 --- a/client/app/pages/users/show.js +++ b/client/app/pages/users/show.js @@ -1,16 +1,14 @@ import { each } from 'lodash'; import settingsMenu from '@/services/settingsMenu'; -import { absoluteUrl } from '@/services/utils'; import template from './show.html'; import './settings.less'; function UserCtrl( $scope, $routeParams, $http, $location, toastr, - clientConfig, currentUser, User, + currentUser, User, ) { $scope.userId = $routeParams.userId; $scope.currentUser = currentUser; - $scope.clientConfig = clientConfig; if ($scope.userId === undefined) { $scope.userId = currentUser.id; @@ -57,14 +55,6 @@ function UserCtrl( }; }); - $scope.sendPasswordReset = () => { - $scope.disablePasswordResetButton = true; - $http.post(`api/users/${$scope.user.id}/reset_password`).success((data) => { - $scope.disablePasswordResetButton = false; - $scope.passwordResetLink = absoluteUrl(data.reset_link); - }); - }; - $scope.resendInvitation = () => { $http.post(`api/users/${$scope.user.id}/invite`).success(() => { toastr.success('Invitation sent.', { diff --git a/client/app/services/user.js b/client/app/services/user.js index 11765ceb56..0c46877e9b 100644 --- a/client/app/services/user.js +++ b/client/app/services/user.js @@ -68,15 +68,29 @@ function deleteUser(user) { function regenerateApiKey(user) { return $http .post(`api/users/${user.id}/regenerate_api_key`) - .success((data) => { + .then(({ data }) => { toastr.success('The API Key has been updated.'); - return data; + return data.api_key; }) - .error((response) => { + .catch((response) => { + const message = + response.data && response.data.message + ? response.data.message + : `Failed regenerating API Key: ${response.statusText}`; + + toastr.error(message); + }); +} + +function sendPasswordReset(user) { + return $http + .post(`api/users/${user.id}/reset_password`) + .then(({ data }) => data.reset_link) + .catch((response) => { const message = response.message ? response.message - : `Failed regenerating API Key: ${response.statusText}`; + : `Failed to send password reset email: ${response.statusText}`; toastr.error(message); }); @@ -98,6 +112,7 @@ function UserService($resource) { UserResource.disableUser = disableUser; UserResource.deleteUser = deleteUser; UserResource.regenerateApiKey = regenerateApiKey; + UserResource.sendPasswordReset = sendPasswordReset; return UserResource; }