From e5e5697a612a71315d2b9cae917befb863232cea Mon Sep 17 00:00:00 2001 From: pkupczyk <piotr.kupczyk@id.ethz.ch> Date: Wed, 2 Sep 2020 17:28:02 +0200 Subject: [PATCH] NG_UI : user management : SSDM-10062 - add initial version of 'Roles' section --- openbis_ng_ui/src/js/common/consts/ids.js | 2 + .../src/js/components/users/form/UserForm.jsx | 105 ++++++++-- .../components/users/form/UserFormButtons.jsx | 16 +- .../users/form/UserFormController.js | 5 + .../users/form/UserFormControllerAddGroup.js | 7 +- .../users/form/UserFormControllerAddRole.js | 42 ++++ .../users/form/UserFormControllerChange.js | 16 +- .../users/form/UserFormControllerLoad.js | 61 +++++- .../users/form/UserFormControllerRemove.js | 19 ++ .../components/users/form/UserFormFacade.js | 6 +- .../users/form/UserFormParameters.jsx | 56 ------ .../users/form/UserFormParametersRole.jsx | 186 ++++++++++++++++++ 12 files changed, 424 insertions(+), 97 deletions(-) create mode 100644 openbis_ng_ui/src/js/components/users/form/UserFormControllerAddRole.js delete mode 100644 openbis_ng_ui/src/js/components/users/form/UserFormParameters.jsx create mode 100644 openbis_ng_ui/src/js/components/users/form/UserFormParametersRole.jsx diff --git a/openbis_ng_ui/src/js/common/consts/ids.js b/openbis_ng_ui/src/js/common/consts/ids.js index 2dc98bd93bb..e37e8f47a69 100644 --- a/openbis_ng_ui/src/js/common/consts/ids.js +++ b/openbis_ng_ui/src/js/common/consts/ids.js @@ -2,6 +2,7 @@ const WEB_APP_ID = 'openbis_ng_ui' const TYPES_GRID_ID = 'types_grid' const USERS_GRID_ID = 'users_grid' const USER_GROUPS_GRID_ID = 'user_groups_grid' +const USER_ROLES_GRID_ID = 'user_roles_grid' const VOCABULARY_TERMS_GRID_ID = 'vocabulary_terms_grid' export default { @@ -9,5 +10,6 @@ export default { TYPES_GRID_ID, USERS_GRID_ID, USER_GROUPS_GRID_ID, + USER_ROLES_GRID_ID, VOCABULARY_TERMS_GRID_ID } diff --git a/openbis_ng_ui/src/js/components/users/form/UserForm.jsx b/openbis_ng_ui/src/js/components/users/form/UserForm.jsx index dee72a3bad0..fb2dd9089c1 100644 --- a/openbis_ng_ui/src/js/components/users/form/UserForm.jsx +++ b/openbis_ng_ui/src/js/components/users/form/UserForm.jsx @@ -13,12 +13,14 @@ import logger from '@src/js/common/logger.js' import UserFormController from './UserFormController.js' import UserFormFacade from './UserFormFacade.js' -import UserFormParameters from './UserFormParameters.jsx' +import UserFormParametersUser from './UserFormParametersUser.jsx' +import UserFormParametersGroup from './UserFormParametersGroup.jsx' +import UserFormParametersRole from './UserFormParametersRole.jsx' import UserFormButtons from './UserFormButtons.jsx' const styles = () => ({}) -const columns = [ +const USER_GROUPS_GRID_COLUMNS = [ { field: 'code.value', label: 'Code', @@ -30,6 +32,22 @@ const columns = [ } ] +const USER_ROLES_GRID_COLUMNS = [ + { + field: 'space.value', + label: 'Space', + sort: 'asc' + }, + { + field: 'project.value', + label: 'Project' + }, + { + field: 'role.value', + label: 'Role' + } +] + class UserForm extends React.PureComponent { constructor(props) { super(props) @@ -54,7 +72,7 @@ class UserForm extends React.PureComponent { this.controller.handleSelectionChange() } - handleSelectedRowChange(row) { + handleSelectedGroupRowChange(row) { const { controller } = this if (row) { controller.handleSelectionChange('group', { id: row.id }) @@ -63,8 +81,21 @@ class UserForm extends React.PureComponent { } } - handleGridControllerRef(gridController) { - this.controller.gridController = gridController + handleSelectedRoleRowChange(row) { + const { controller } = this + if (row) { + controller.handleSelectionChange('role', { id: row.id }) + } else { + controller.handleSelectionChange() + } + } + + handleGroupsGridControllerRef(gridController) { + this.controller.groupsGridController = gridController + } + + handleRolesGridControllerRef(gridController) { + this.controller.rolesGridController = gridController } render() { @@ -85,20 +116,31 @@ class UserForm extends React.PureComponent { } renderMainPanel() { - const { groups, selection } = this.state + const { groups, roles, selection } = this.state return ( <GridContainer onClick={this.handleClickContainer}> <Header>Groups</Header> <Grid id={ids.USER_GROUPS_GRID_ID} - controllerRef={this.handleGridControllerRef} - columns={columns} + controllerRef={this.handleGroupsGridControllerRef} + columns={USER_GROUPS_GRID_COLUMNS} rows={groups} selectedRowId={ selection && selection.type === 'group' ? selection.params.id : null } - onSelectedRowChange={this.handleSelectedRowChange} + onSelectedRowChange={this.handleSelectedGroupRowChange} + /> + <Header>Roles</Header> + <Grid + id={ids.USER_ROLES_GRID_ID} + controllerRef={this.handleRolesGridControllerRef} + columns={USER_ROLES_GRID_COLUMNS} + rows={roles} + selectedRowId={ + selection && selection.type === 'role' ? selection.params.id : null + } + onSelectedRowChange={this.handleSelectedRoleRowChange} /> </GridContainer> ) @@ -106,24 +148,44 @@ class UserForm extends React.PureComponent { renderAdditionalPanel() { const { controller } = this - const { user, groups, selection, mode } = this.state + const { user, groups, roles, selection, mode } = this.state const selectedRow = controller.gridController ? controller.gridController.getSelectedRow() : null return ( - <UserFormParameters - controller={controller} - user={user} - groups={groups} - selection={selection} - selectedRow={selectedRow} - mode={mode} - onChange={controller.handleChange} - onSelectionChange={controller.handleSelectionChange} - onBlur={controller.handleBlur} - /> + <div> + <UserFormParametersUser + controller={controller} + user={user} + selection={selection} + mode={mode} + onChange={controller.handleChange} + onSelectionChange={controller.handleSelectionChange} + onBlur={controller.handleBlur} + /> + <UserFormParametersGroup + controller={controller} + groups={groups} + selection={selection} + selectedRow={selectedRow} + mode={mode} + onChange={controller.handleChange} + onSelectionChange={controller.handleSelectionChange} + onBlur={controller.handleBlur} + /> + <UserFormParametersRole + controller={controller} + roles={roles} + selection={selection} + selectedRow={selectedRow} + mode={mode} + onChange={controller.handleChange} + onSelectionChange={controller.handleSelectionChange} + onBlur={controller.handleBlur} + /> + </div> ) } @@ -137,6 +199,7 @@ class UserForm extends React.PureComponent { onSave={controller.handleSave} onCancel={controller.handleCancel} onAddGroup={controller.handleAddGroup} + onAddRole={controller.handleAddRole} onRemove={controller.handleRemove} user={user} selection={selection} diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormButtons.jsx b/openbis_ng_ui/src/js/components/users/form/UserFormButtons.jsx index c1a3593de65..e39689eca60 100644 --- a/openbis_ng_ui/src/js/components/users/form/UserFormButtons.jsx +++ b/openbis_ng_ui/src/js/components/users/form/UserFormButtons.jsx @@ -28,7 +28,7 @@ class UserFormButtons extends React.PureComponent { } renderAdditionalButtons(classes) { - const { onAddGroup, onRemove } = this.props + const { onAddGroup, onAddRole, onRemove } = this.props return ( <React.Fragment> @@ -38,20 +38,28 @@ class UserFormButtons extends React.PureComponent { styles={{ root: classes.button }} onClick={onAddGroup} /> + <Button + name='addRole' + label='Add Role' + styles={{ root: classes.button }} + onClick={onAddRole} + /> <Button name='remove' label='Remove' styles={{ root: classes.button }} - disabled={!this.isGroupSelected()} + disabled={!this.isGroupOrRoleSelected()} onClick={onRemove} /> </React.Fragment> ) } - isGroupSelected() { + isGroupOrRoleSelected() { const { selection } = this.props - return selection && selection.type === 'group' + return ( + selection && (selection.type === 'group' || selection.type === 'role') + ) } } diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormController.js b/openbis_ng_ui/src/js/components/users/form/UserFormController.js index ed8df64537a..f103ba44f18 100644 --- a/openbis_ng_ui/src/js/components/users/form/UserFormController.js +++ b/openbis_ng_ui/src/js/components/users/form/UserFormController.js @@ -1,6 +1,7 @@ import PageController from '@src/js/components/common/page/PageController.js' import UserFormControllerLoad from './UserFormControllerLoad.js' import UserFormControllerAddGroup from './UserFormControllerAddGroup.js' +import UserFormControllerAddRole from './UserFormControllerAddRole.js' import UserFormControllerRemove from './UserFormControllerRemove.js' import UserFormControllerValidate from './UserFormControllerValidate.js' import UserFormControllerChange from './UserFormControllerChange.js' @@ -37,6 +38,10 @@ export default class UserFormController extends PageController { return new UserFormControllerAddGroup(this).execute() } + handleAddRole() { + return new UserFormControllerAddRole(this).execute() + } + handleRemove() { return new UserFormControllerRemove(this).execute() } diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddGroup.js b/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddGroup.js index bccefc4823a..c9a4e0d1a07 100644 --- a/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddGroup.js +++ b/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddGroup.js @@ -1,10 +1,9 @@ import FormUtil from '@src/js/components/common/form/FormUtil.js' -export default class UserFormControllerAdd { +export default class UserFormControllerAddGroup { constructor(controller) { this.controller = controller this.context = controller.context - this.gridController = controller.gridController } async execute() { @@ -35,8 +34,8 @@ export default class UserFormControllerAdd { await this.controller.changed(true) - if (this.gridController) { - await this.gridController.showSelectedRow() + if (this.controller.groupsGridController) { + await this.controller.groupsGridController.showSelectedRow() } } } diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddRole.js b/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddRole.js new file mode 100644 index 00000000000..324589b2008 --- /dev/null +++ b/openbis_ng_ui/src/js/components/users/form/UserFormControllerAddRole.js @@ -0,0 +1,42 @@ +import FormUtil from '@src/js/components/common/form/FormUtil.js' + +export default class UserFormControllerAddRole { + constructor(controller) { + this.controller = controller + this.context = controller.context + } + + async execute() { + let { roles, rolesCounter } = this.context.getState() + + const newRole = { + id: 'role-' + rolesCounter++, + space: FormUtil.createField({}), + project: FormUtil.createField({}), + role: FormUtil.createField({}), + original: null + } + + const newRoles = Array.from(roles) + newRoles.push(newRole) + + await this.context.setState(state => ({ + ...state, + roles: newRoles, + rolesCounter, + selection: { + type: 'role', + params: { + id: newRole.id, + part: 'space' + } + } + })) + + await this.controller.changed(true) + + if (this.controller.rolesGridController) { + await this.controller.rolesGridController.showSelectedRow() + } + } +} diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormControllerChange.js b/openbis_ng_ui/src/js/components/users/form/UserFormControllerChange.js index 3c8ebf6927f..c34ab33b69c 100644 --- a/openbis_ng_ui/src/js/components/users/form/UserFormControllerChange.js +++ b/openbis_ng_ui/src/js/components/users/form/UserFormControllerChange.js @@ -1,11 +1,6 @@ import PageControllerChange from '@src/js/components/common/page/PageControllerChange.js' export default class UserFormControllerChange extends PageControllerChange { - constructor(controller) { - super(controller) - this.gridController = controller.gridController - } - async execute(type, params) { if (type === 'user') { const { field, value } = params @@ -14,8 +9,15 @@ export default class UserFormControllerChange extends PageControllerChange { const { id, field, value } = params await this.changeCollectionItemField('groups', id, field, value) - if (this.gridController) { - this.gridController.showSelectedRow() + if (this.controller.groupsGridController) { + await this.controller.groupsGridController.showSelectedRow() + } + } else if (type === 'role') { + const { id, field, value } = params + await this.changeCollectionItemField('roles', id, field, value) + + if (this.controller.rolesGridController) { + await this.controller.rolesGridController.showSelectedRow() } } } diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormControllerLoad.js b/openbis_ng_ui/src/js/components/users/form/UserFormControllerLoad.js index f84c97374df..c2bf5ef3f71 100644 --- a/openbis_ng_ui/src/js/components/users/form/UserFormControllerLoad.js +++ b/openbis_ng_ui/src/js/components/users/form/UserFormControllerLoad.js @@ -28,12 +28,25 @@ export default class UserFormControllerLoad extends PageControllerLoad { ) } - const selection = this._createSelection(groups) + let rolesCounter = 0 + let roles = [] + + if (loadedUser && loadedUser.getRoleAssignments()) { + roles = loadedUser + .getRoleAssignments() + .map(loadedRole => + this._createRole('role-' + rolesCounter++, loadedRole) + ) + } + + const selection = this._createSelection(groups, roles) return this.context.setState({ user, groups, groupsCounter, + roles, + rolesCounter, selection, original: { user: user.original, @@ -79,6 +92,26 @@ export default class UserFormControllerLoad extends PageControllerLoad { return user } + _createRole(id, loadedRole) { + const role = { + id: id, + space: FormUtil.createField({ + value: _.get(loadedRole, 'space.code', null), + enabled: false + }), + project: FormUtil.createField({ + value: _.get(loadedRole, 'project.permId.permId', null), + enabled: false + }), + role: FormUtil.createField({ + value: _.get(loadedRole, 'role', null), + enabled: false + }) + } + role.original = _.cloneDeep(role) + return role + } + _createGroup(id, loadedGroup) { const group = { id: id, @@ -95,10 +128,11 @@ export default class UserFormControllerLoad extends PageControllerLoad { return group } - _createSelection(newGroups) { + _createSelection(newGroups, newRoles) { const { selection: oldSelection, - groups: oldGroups + groups: oldGroups, + roles: oldRoles } = this.context.getState() if (!oldSelection) { @@ -121,6 +155,27 @@ export default class UserFormControllerLoad extends PageControllerLoad { } } } + } else if (oldSelection.type === 'role') { + const oldRole = _.find( + oldRoles, + oldRole => oldRole.id === oldSelection.params.id + ) + const newRole = _.find( + newRoles, + newRole => + newRole.space.value === oldRole.space.value && + newRole.project.value === oldRole.project.value && + newRole.role.value === oldRole.role.value + ) + + if (newRole) { + return { + type: 'role', + params: { + id: newRole.id + } + } + } } else { return null } diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormControllerRemove.js b/openbis_ng_ui/src/js/components/users/form/UserFormControllerRemove.js index 45c3217abfb..065258f986d 100644 --- a/openbis_ng_ui/src/js/components/users/form/UserFormControllerRemove.js +++ b/openbis_ng_ui/src/js/components/users/form/UserFormControllerRemove.js @@ -8,6 +8,8 @@ export default class UserFormControllerRemove { const { selection } = this.context.getState() if (selection.type === 'group') { this._handleRemoveGroup(selection.params.id) + } else if (selection.type === 'role') { + this._handleRemoveRole(selection.params.id) } } @@ -27,4 +29,21 @@ export default class UserFormControllerRemove { this.controller.changed(true) } + + _handleRemoveRole(roleId) { + const { roles } = this.context.getState() + + const roleIndex = roles.findIndex(role => role.id === roleId) + + const newRoles = Array.from(roles) + newRoles.splice(roleIndex, 1) + + this.context.setState(state => ({ + ...state, + roles: newRoles, + selection: null + })) + + this.controller.changed(true) + } } diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormFacade.js b/openbis_ng_ui/src/js/components/users/form/UserFormFacade.js index 2d5c565c394..44489b8df47 100644 --- a/openbis_ng_ui/src/js/components/users/form/UserFormFacade.js +++ b/openbis_ng_ui/src/js/components/users/form/UserFormFacade.js @@ -5,7 +5,8 @@ export default class UserFormFacade { const id = new openbis.PersonPermId(userId) const fo = new openbis.PersonFetchOptions() fo.withSpace() - fo.withRoleAssignments() + fo.withRoleAssignments().withSpace() + fo.withRoleAssignments().withProject() return openbis.getPersons([id], fo).then(map => { return map[userId] }) @@ -15,7 +16,8 @@ export default class UserFormFacade { const criteria = new openbis.AuthorizationGroupSearchCriteria() const fo = new openbis.AuthorizationGroupFetchOptions() fo.withUsers() - fo.withRoleAssignments() + fo.withRoleAssignments().withSpace() + fo.withRoleAssignments().withProject() return openbis.searchAuthorizationGroups(criteria, fo).then(result => { return result.getObjects().filter(group => { return group.getUsers().some(user => { diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormParameters.jsx b/openbis_ng_ui/src/js/components/users/form/UserFormParameters.jsx deleted file mode 100644 index d2e6b752845..00000000000 --- a/openbis_ng_ui/src/js/components/users/form/UserFormParameters.jsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react' -import { withStyles } from '@material-ui/core/styles' -import logger from '@src/js/common/logger.js' - -import UserFormParametersUser from './UserFormParametersUser.jsx' -import UserFormParametersGroup from './UserFormParametersGroup.jsx' - -const styles = () => ({}) - -class UserFormParameters extends React.PureComponent { - constructor(props) { - super(props) - } - - render() { - logger.log(logger.DEBUG, 'UserFormParameters.render') - - const { - controller, - user, - groups, - selection, - selectedRow, - mode, - onChange, - onSelectionChange, - onBlur - } = this.props - - return ( - <div> - <UserFormParametersUser - controller={controller} - user={user} - selection={selection} - mode={mode} - onChange={onChange} - onSelectionChange={onSelectionChange} - onBlur={onBlur} - /> - <UserFormParametersGroup - controller={controller} - groups={groups} - selection={selection} - selectedRow={selectedRow} - mode={mode} - onChange={onChange} - onSelectionChange={onSelectionChange} - onBlur={onBlur} - /> - </div> - ) - } -} - -export default withStyles(styles)(UserFormParameters) diff --git a/openbis_ng_ui/src/js/components/users/form/UserFormParametersRole.jsx b/openbis_ng_ui/src/js/components/users/form/UserFormParametersRole.jsx new file mode 100644 index 00000000000..4032b45c0aa --- /dev/null +++ b/openbis_ng_ui/src/js/components/users/form/UserFormParametersRole.jsx @@ -0,0 +1,186 @@ +import React from 'react' +import { withStyles } from '@material-ui/core/styles' +import Container from '@src/js/components/common/form/Container.jsx' +import Header from '@src/js/components/common/form/Header.jsx' +import TextField from '@src/js/components/common/form/TextField.jsx' +import logger from '@src/js/common/logger.js' + +const styles = theme => ({ + field: { + paddingBottom: theme.spacing(1) + } +}) + +class UserFormParametersRole extends React.PureComponent { + constructor(props) { + super(props) + this.state = {} + this.references = { + space: React.createRef(), + project: React.createRef(), + role: React.createRef() + } + this.handleChange = this.handleChange.bind(this) + this.handleFocus = this.handleFocus.bind(this) + this.handleBlur = this.handleBlur.bind(this) + } + + componentDidMount() { + this.focus() + } + + componentDidUpdate(prevProps) { + const prevSelection = prevProps.selection + const selection = this.props.selection + + if (prevSelection !== selection) { + this.focus() + } + } + + focus() { + const role = this.getRole(this.props) + if (role && this.props.selection) { + const { part } = this.props.selection.params + if (part) { + const reference = this.references[part] + if (reference && reference.current) { + reference.current.focus() + } + } + } + } + + handleChange(event) { + const role = this.getRole(this.props) + this.props.onChange('role', { + id: role.id, + field: event.target.name, + value: event.target.value + }) + } + + handleFocus(event) { + const role = this.getRole(this.props) + this.props.onSelectionChange('role', { + id: role.id, + part: event.target.name + }) + } + + handleBlur() { + this.props.onBlur() + } + + render() { + logger.log(logger.DEBUG, 'UserFormParametersRole.render') + + const role = this.getRole(this.props) + if (!role) { + return null + } + + return ( + <Container> + <Header>Role</Header> + {this.renderSpace(role)} + {this.renderProject(role)} + {this.renderRole(role)} + </Container> + ) + } + + renderSpace(role) { + const { visible, enabled, error, value } = { ...role.space } + + if (!visible) { + return null + } + + const { mode, classes } = this.props + return ( + <div className={classes.field}> + <TextField + reference={this.references.space} + label='Space' + name='space' + mandatory={true} + error={error} + disabled={!enabled} + value={value} + mode={mode} + onChange={this.handleChange} + onFocus={this.handleFocus} + onBlur={this.handleBlur} + /> + </div> + ) + } + + renderProject(role) { + const { visible, enabled, error, value } = { ...role.project } + + if (!visible) { + return null + } + + const { mode, classes } = this.props + return ( + <div className={classes.field}> + <TextField + reference={this.references.project} + label='Project' + name='project' + mandatory={true} + error={error} + disabled={!enabled} + value={value} + mode={mode} + onChange={this.handleChange} + onFocus={this.handleFocus} + onBlur={this.handleBlur} + /> + </div> + ) + } + + renderRole(role) { + const { visible, enabled, error, value } = { ...role.role } + + if (!visible) { + return null + } + + const { mode, classes } = this.props + return ( + <div className={classes.field}> + <TextField + reference={this.references.role} + label='Role' + name='role' + mandatory={true} + error={error} + disabled={!enabled} + value={value} + mode={mode} + onChange={this.handleChange} + onFocus={this.handleFocus} + onBlur={this.handleBlur} + /> + </div> + ) + } + + getRole(props) { + let { roles, selection } = props + + if (selection && selection.type === 'role') { + let [role] = roles.filter(role => role.id === selection.params.id) + return role + } else { + return null + } + } +} + +export default withStyles(styles)(UserFormParametersRole) -- GitLab