Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Genesys PGR
Genesys Website
Commits
c65701a4
Commit
c65701a4
authored
Jan 17, 2019
by
Matija Obreza
Browse files
Merge branch '176-user-profile-ftp-password'
* 176-user-profile-ftp-password: User profile: FTP password
parents
897a5f24
17855b1c
Changes
11
Hide whitespace changes
Inline
Side-by-side
locales/en/common.json
View file @
c65701a4
...
...
@@ -8,6 +8,7 @@
"OK"
:
"OK"
,
"close"
:
"Close"
,
"collapse"
:
"Collapse"
,
"confirm"
:
"Confirm"
,
"delete"
:
"Delete"
,
"download"
:
"Download"
,
"edit"
:
"Edit"
,
...
...
locales/en/translations.json
View file @
c65701a4
...
...
@@ -1866,6 +1866,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"
}
...
...
src/constants/userRoles.ts
View file @
c65701a4
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
};
src/service/UserService.ts
View file @
c65701a4
...
...
@@ -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
*
...
...
src/ui/common/authorized/Authorize.tsx
View file @
c65701a4
...
...
@@ -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
});
...
...
src/ui/layout/headers/v1/MenuBar.tsx
View file @
c65701a4
...
...
@@ -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
}
/>
);
}
...
...
src/ui/layout/headers/v1/MenuItem.tsx
View file @
c65701a4
...
...
@@ -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
>
);
}
}
...
...
src/ui/layout/headers/v1/MobileNavigation.tsx
View file @
c65701a4
...
...
@@ -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
>
);
})
}
...
...
src/user/translations.json
View file @
c65701a4
...
...
@@ -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"
}
...
...
src/user/ui/c/UserProfileCard.tsx
View file @
c65701a4
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
>
);
...
...
src/user/ui/dashboard/c/FtpPasswordDialog.tsx
0 → 100644
View file @
c65701a4
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
);
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment