Commit 17855b1c authored by Viacheslav Pavlov's avatar Viacheslav Pavlov
Browse files

User profile: FTP password

FtpPasswordPage replaced with dialog implementation
added confirmation to GenerateFtpPasswordDialog, added correct roles to Authorize
parent 566dece5
......@@ -8,6 +8,7 @@
"OK": "OK",
"close": "Close",
"collapse": "Collapse",
"confirm": "Confirm",
"delete": "Delete",
"download": "Download",
"edit": "Edit",
......
......@@ -1865,6 +1865,15 @@
"unableChange": "You can't set your password if you use Google auth",
"title": "Password change"
},
"ftpPassword": {
"title": "Ftp credentials",
"credentials": "Your FTP credentials",
"newCredentials": "New FTP credentials",
"username": "FTP username",
"password": "FTP password",
"generatePassword": "Generate FTP password",
"confirm": "Do you wish to set a new FTP password for this account?"
},
"profile": {
"title": "User profile"
}
......
const ROLE_CLIENT = 'ROLE_CLIENT';
const ROLE_USER = 'ROLE_USER';
const ROLE_ADMINISTRATOR = 'ROLE_ADMINISTRATOR';
const ROLE_VETTEDUSER = 'ROLE_VETTEDUSER';
export {ROLE_CLIENT, ROLE_USER, ROLE_ADMINISTRATOR};
export {ROLE_CLIENT, ROLE_USER, ROLE_ADMINISTRATOR, ROLE_VETTEDUSER};
......@@ -14,6 +14,7 @@ const URL_GET_USER = UrlTemplate.parse(`/api/v1/user/u/{uuid}`);
const URL_DISABLE_ACCOUNT = UrlTemplate.parse(`/api/v1/user/u/{uuid}/disable`);
const URL_ENABLE_ACCOUNT = UrlTemplate.parse(`/api/v1/user/u/{uuid}/enable`);
const URL_LOCK_ACCOUNT = UrlTemplate.parse(`/api/v1/user/u/{uuid}/lock`);
const URL_GENERATE_FTP_PASSWORD = UrlTemplate.parse(`/api/v1/user/u/{uuid}/ftp-password`);
const URL_UNLOCK_ACCOUNT = UrlTemplate.parse(`/api/v1/user/u/{uuid}/unlock`);
const URL_ARCHIVE_ACCOUNT = UrlTemplate.parse(`/api/v1/user/u/{uuid}/archive`);
const URL_SEND_EMAIL = UrlTemplate.parse(`/api/v1/user/u/{uuid}/email-verification`);
......@@ -95,6 +96,24 @@ export class UserService {
}).then(({ data }) => data as User);
}
/**
* generateFtpPassword at /api/v1/user/u/{uuid}/ftp-password
*
* @param uuid uuid
*/
public static generateFtpPassword(uuid: string): Promise<string> {
const apiUrl = URL_GENERATE_FTP_PASSWORD.expand({uuid});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend({
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as string);
}
/**
* disableAccount at /api/v1/user/u/{uuid}/disable
*
......
......@@ -34,7 +34,7 @@ class Authorize extends React.Component<IAuthorizeProps, any> {
roles = role.split(/\s*,\s*/gi);
}
const show = userRoles.map((e) => roles.indexOf(e) > -1).reduce((p, c) => p || c);
const show = !roles || userRoles.map((e) => roles.indexOf(e) > -1).reduce((p, c) => p || c);
if (show) {
this.setState({show: true});
......
......@@ -39,6 +39,7 @@ class MenuBar extends React.Component<IMenuBarProps, any> {
key={ path.to }
children={ path.subMenus.map(this.renderSubMenu) }
theme={ theme }
auth={ path.auth }
/>
) : (path.inHeader !== undefined && ! path.inHeader) ? null : (
<MenuItem
......@@ -48,6 +49,7 @@ class MenuBar extends React.Component<IMenuBarProps, any> {
active={ location.pathname === path.to }
key={ path.to }
theme={ theme }
auth={ path.auth }
/>
)
);
......@@ -62,6 +64,7 @@ class MenuBar extends React.Component<IMenuBarProps, any> {
activeSub={ location.pathname === path.to }
key={ path.to }
theme={ theme }
auth={ path.auth }
/>
);
}
......
......@@ -3,6 +3,7 @@ import { translate } from 'react-i18next';
import { withStyles } from '@material-ui/core/styles';
import { Link } from 'react-router-dom';
import Collapse from '@material-ui/core/Collapse/Collapse';
import Authorize from 'ui/common/authorized/Authorize';
/*tslint:disable*/
const style = (theme) => ({
......@@ -85,6 +86,7 @@ interface IMenuItemProps extends React.ClassAttributes<any> {
activeSub?: boolean;
theme?: any;
root?: boolean;
auth: string[];
}
class MenuItem extends React.Component<IMenuItemProps, any> {
......@@ -105,25 +107,27 @@ class MenuItem extends React.Component<IMenuItemProps, any> {
private closeMenu = () => this.toggleDrawer(false);
public render() {
const {to, label, children, classes, t, activeSub, active, theme, root = false} = this.props;
const {to, label, children, classes, t, activeSub, active, theme, auth, root = false} = this.props;
return (
<div
className={ `${classes.menuItem} ${this.state.open && classes.hover} ${ active && classes.active } ${ activeSub && classes.activeSub}` }
onMouseEnter={ this.openMenu } onMouseLeave={ this.closeMenu } onClick={ this.closeMenu }
>
<Link to={ to } className={ `${ classes.menuLink }` }>
<div className={ classes.linkContent }>
<span className={ classes.linkLabel } style={ {color: root ? theme.menuItemText : theme.subItemText} }>{ typeof label === 'string' ? t(label) : label }</span>
</div>
</Link>
{ children &&
<Collapse className={ classes.collapsed } in={ this.state.open }>
<div className={ `${classes.children} ${this.state.open && classes.open}` } style={ {backgroundColor: `${theme.subItem}`, color: theme.subItemText} }>
{ children }
</div>
</Collapse>
}
</div>
<Authorize roles={ auth }>
<div
className={ `${classes.menuItem} ${this.state.open && classes.hover} ${ active && classes.active } ${ activeSub && classes.activeSub}` }
onMouseEnter={ this.openMenu } onMouseLeave={ this.closeMenu } onClick={ this.closeMenu }
>
<Link to={ to } className={ `${ classes.menuLink }` }>
<div className={ classes.linkContent }>
<span className={ classes.linkLabel } style={ {color: root ? theme.menuItemText : theme.subItemText} }>{ typeof label === 'string' ? t(label) : label }</span>
</div>
</Link>
{ children &&
<Collapse className={ classes.collapsed } in={ this.state.open }>
<div className={ `${classes.children} ${this.state.open && classes.open}` } style={ {backgroundColor: `${theme.subItem}`, color: theme.subItemText} }>
{ children }
</div>
</Collapse>
}
</div>
</Authorize>
);
}
}
......
......@@ -12,6 +12,7 @@ import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
import Authorize from 'ui/common/authorized/Authorize';
const mobile = ['md', 'sm', 'xs'] as Breakpoint[];
......@@ -168,45 +169,46 @@ class MobileNavigation extends React.Component<IMobileNavigationProps, any> {
path.subMenus &&
path.subMenus.some((subMenu) => subMenu.to === location.pathname);
return (
<ExpansionPanel
key={ path.to }
className={ `${classes.panel} ${isActive && classes.activePanel}` }
expanded={ expanded === path.to }
onChange={ this.handleChange(path.to) }
>
<ExpansionPanelSummary
className={ classes.panelSummary }
expandIcon={ path.subMenus && <ExpandMoreIcon/> }
<Authorize roles={ path.auth } key={ path.to }>
<ExpansionPanel
className={ `${classes.panel} ${isActive && classes.activePanel}` }
expanded={ expanded === path.to }
onChange={ this.handleChange(path.to) }
>
<NavLink
activeClassName={ classes.active }
to={ path.to }
className={ classes.navRootLink }
onClick={ this.props.closeMenu }>
{ typeof path.label === 'string' ? t(path.label) : path.label }
</NavLink>
</ExpansionPanelSummary>
{ path.subMenus && (
<ExpansionPanelDetails className={ classes.detail }>
<div className={ classes.subMenu }>
{ path.subMenus.map((path) => {
return (
<NavLink
key={ path.to }
className={ classes.navLink }
activeClassName={ classes.activeSub }
to={ path.to }
onClick={ this.props.closeMenu }
>
{ typeof path.label === 'string' ? t(path.label) : path.label }
</NavLink>
);
})
}
</div>
</ExpansionPanelDetails>
) }
</ExpansionPanel>
<ExpansionPanelSummary
className={ classes.panelSummary }
expandIcon={ path.subMenus && <ExpandMoreIcon/> }
>
<NavLink
activeClassName={ classes.active }
to={ path.to }
className={ classes.navRootLink }
onClick={ this.props.closeMenu }>
{ typeof path.label === 'string' ? t(path.label) : path.label }
</NavLink>
</ExpansionPanelSummary>
{ path.subMenus && (
<ExpansionPanelDetails className={ classes.detail }>
<div className={ classes.subMenu }>
{ path.subMenus.map((path) => {
return (
<NavLink
key={ path.to }
className={ classes.navLink }
activeClassName={ classes.activeSub }
to={ path.to }
onClick={ this.props.closeMenu }
>
{ typeof path.label === 'string' ? t(path.label) : path.label }
</NavLink>
);
})
}
</div>
</ExpansionPanelDetails>
) }
</ExpansionPanel>
</Authorize>
);
})
}
......
......@@ -75,6 +75,15 @@
"unableChange": "You can't set your password if you use Google auth",
"title": "Password change"
},
"ftpPassword": {
"title": "Ftp credentials",
"credentials": "Your FTP credentials",
"newCredentials": "New FTP credentials",
"username": "FTP username",
"password": "FTP password",
"generatePassword": "Generate FTP password",
"confirm": "Do you wish to set a new FTP password for this account?"
},
"profile": {
"title": "User profile"
}
......
import * as React from 'react';
import {translate} from 'react-i18next';
// Constants
import { ROLE_VETTEDUSER, ROLE_ADMINISTRATOR } from 'constants/userRoles';
// UI
import Card, {CardHeader, CardContent} from 'ui/common/Card';
import Card, { CardHeader, CardContent, CardActions } from 'ui/common/Card';
import { Properties, PropertiesItem } from 'ui/common/Properties';
import Authorize from 'ui/common/authorized/Authorize';
import PrettyDate from 'ui/common/time/PrettyDate';
import Grid from '@material-ui/core/Grid';
import FtpPasswordDialog from 'user/ui/dashboard/c/FtpPasswordDialog';
// Model
import {User} from 'model/user/User';
......@@ -36,6 +39,11 @@ const UserProfileCard = ({userProfile, admin = false, t, className, ...other}: {
</Grid>
</Grid>
</CardContent>
<Authorize roles={ [ ROLE_ADMINISTRATOR, ROLE_VETTEDUSER ] }>
<CardActions>
<FtpPasswordDialog userProfile={ userProfile } />
</CardActions>
</Authorize>
</Card>
</div>
);
......
import * as React from 'react';
import { translate } from 'react-i18next';
// model
import { User } from 'model/user/User';
// service
import { UserService } from 'service/UserService';
// ui
import { Properties, PropertiesItem } from 'ui/common/Properties';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogContent from '@material-ui/core/DialogContent';
// utilities
import confirm from 'utilities/confirmAlert';
interface IFtpPasswordPage extends React.ClassAttributes<any> {
userProfile: User;
t?: any;
}
class ChangePasswordPage extends React.Component<IFtpPasswordPage> {
public state = {
open: false,
generatedPassword: '',
};
public render() {
const { userProfile, t } = this.props;
return (
<div>
<Button className="m-20" onClick={ this.show }>{ t('user.dashboard.p.ftpPassword.generatePassword') }</Button>
<Dialog open={ this.state.open } onClose={ this.hide } maxWidth="sm" fullWidth>
<DialogTitle>{ this.state.generatedPassword ? t('user.dashboard.p.ftpPassword.newCredentials') : t('user.dashboard.p.ftpPassword.credentials') }</DialogTitle>
<DialogContent>
<Properties>
<PropertiesItem title="user.dashboard.p.ftpPassword.username">{ userProfile.email }</PropertiesItem>
<PropertiesItem title="user.dashboard.p.ftpPassword.password">{ this.state.generatedPassword || '****' }</PropertiesItem>
</Properties>
</DialogContent>
</Dialog>
</div>
);
}
private generatePassword = () => {
const { userProfile } = this.props;
UserService.generateFtpPassword(userProfile.uuid)
.then((generatedPassword) => this.setState({ generatedPassword }));
}
private show = () => {
const { t } = this.props;
this.setState({ open: true });
confirm(t('user.dashboard.p.ftpPassword.confirm'), {
confirmLabel: t('common:label.yes'),
abortLabel: t('common:label.no'),
}).then(() => {
this.generatePassword();
});
}
private hide = () => {
this.setState({ generatedPassword: null, open: false });
}
}
export default translate()(ChangePasswordPage);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment