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;
}