application/components/admin/group.js
- import _ from 'lodash';
- import React, {Component} from 'react';
- import Immutable from 'immutable';
-
- import generatePassword from 'password-generator';
-
- import {List, ListItem} from 'material-ui/List';
- import Subheader from 'material-ui/Subheader';
- import Avatar from 'material-ui/Avatar';
- import Divider from 'material-ui/Divider';
- import Checkbox from 'material-ui/Checkbox';
- import LinearProgress from 'material-ui/LinearProgress';
- import PersonIcon from 'material-ui/svg-icons/social/person-outline';
- import ReloadIcon from 'material-ui/svg-icons/action/autorenew';
-
- import Dialog from 'material-ui/Dialog';
- import FlatButton from 'material-ui/FlatButton';
- import RaisedButton from 'material-ui/RaisedButton';
- import IconButton from 'material-ui/IconButton';
- import TextField from 'material-ui/TextField';
-
- import {Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn} from 'material-ui/Table';
- import {Toolbar, ToolbarGroup, ToolbarSeparator, ToolbarTitle} from 'material-ui/Toolbar';
-
- import {connect} from 'react-redux';
- import ExpandingCard from './../expanding-card';
- import ReduxPaginator from './../common/redux-paginator';
- import DeleteButton from './../common/delete-button';
-
- import CurrentUser from './../../current-user';
-
- import DescriptiveAcls from './../../data/descriptive-acls';
-
- import {
- aclsMaybeFetch,
- aclsFetch,
- aclsFetchOne,
- aclSetSelected,
- aclsFetchResources,
- aclUpdate,
- aclsRemove
- } from './../../redux/actions/acl-actions';
-
- import {
- snackbarShowMessage
- } from './../../redux/actions/snackbar-actions';
-
- import {Row, Col} from './../flexbox';
-
- class GroupCard extends Component {
- constructor(props) {
- super(props);
-
- this.state = {
- hasMore: false,
- hasLoaded: false,
- modalOpen: false,
- selected: []
- };
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.openCard == 'group' && nextProps.openCard != this.props.openCard) {
- this.handleToggle(true);
- }
- }
-
- handleToggle(expanded) {
- if (this.props.onToggle) {
- this.props.onToggle('group', expanded);
- }
-
- if (expanded && !this.state.hasLoaded) {
- const {dispatch} = this.props;
- dispatch(aclsMaybeFetch())
- .then(() => this.setState({hasLoaded: true}));
- }
- }
-
- handleAclClick(acl, e) {
- const {dispatch} = this.props;
-
- dispatch(aclsFetchOne(acl.get('name')))
- .then(() => dispatch(aclsFetchResources()))
- .then(() => dispatch(aclSetSelected(acl.get('name'))))
- .then(() => {
- this.setState({
- 'modalOpen': true
- });
- });
-
- }
-
- handleNewAcl(e) {
- const {dispatch} = this.props;
- dispatch(aclsFetchResources())
- .then(() => dispatch(aclSetSelected(false)))
- .then(() => this.setState({'modalOpen': true}));
- }
-
- handleModalClose(e) {
- this.setState({'modalOpen': false, 'selected': [], 'selectedItems': []});
- this.tableBody.setState({selectedRows: []});
- }
-
- handleRowSelection(selection) {
- let selectedItems = [];
-
- if (selection == 'all') {
- selectedItems = this.props.acls.items.map((item) => item.get('name'));
- } else {
- const items = this.props.acls.items.toJS();
- selectedItems = selection.map((i) =>items[i].name);
- }
-
- this.setState({
- 'selectedItems': selectedItems,
- 'selected': selection
- });
- }
-
- handlePagination(page) {
- if (this.props.acls.pagination.current == page) {
- return;
- }
-
- const {dispatch} = this.props;
- dispatch(aclsFetch({page: page}));
- }
-
- handleDelete(next) {
- const names = this.state.selectedItems.join(',');
- const {dispatch} = this.props;
-
- dispatch(aclsRemove(names))
- .then(() => dispatch(aclsFetch()))
- .then(() => this.tableBody.setState({selectedRows: []}))
- .then(() => next());
-
- return true;
- }
-
- render() {
- let items = this.props.acls.items.map((item, i) => {
-
- return (
- <TableRow
- key={i}
- selected={this.state.selected == 'all' || this.state.selected.indexOf(i) != -1 ? true : false}
- selectable={item.get('name') != 'Administrator' ? true : false}
- >
- <TableRowColumn>{item.get('name') != 'Administrator' ? (<a onClick={this.handleAclClick.bind(this, item)}>{item.get('name')}</a>) : item.get('name')}</TableRowColumn>
- <TableRowColumn></TableRowColumn>
- </TableRow>
- );
- });
-
- return (
- <ExpandingCard
- onToggle={this.handleToggle.bind(this)}
- title="Groups"
- subtitle="Manage group and ACL permissions"
- expanded={this.props.openCard === 'group'}
- >
-
- <Row middle='xs'>
- <Col start='xs'>
- <DeleteButton
- disabled={this.state.selectedItems && this.state.selectedItems.length ? false : true}
- onDelete={this.handleDelete.bind(this)}
- />
- <RaisedButton
- primary={true}
- label="Add Group"
- onClick={this.handleNewAcl.bind(this)}
- />
- </Col>
- </Row>
-
-
- <Table
- fixedHeader={true}
- selectable={true}
- multiSelectable={true}
- onRowSelection={this.handleRowSelection.bind(this)}
- >
- <TableHeader>
- <TableRow>
- <TableHeaderColumn>Name</TableHeaderColumn>
- <TableHeaderColumn></TableHeaderColumn>
- </TableRow>
- </TableHeader>
- <TableBody
- ref={tableBody => this.tableBody = tableBody}
- deselectOnClickaway={false}
- >
- {items}
- </TableBody>
- </Table>
-
- {
- this.state.modalOpen
- ? <AclModal onClose={this.handleModalClose.bind(this)} open={this.state.modalOpen} />
- : ''
- }
- </ExpandingCard>
- );
- }
- }
-
- let mapStateToProps = (state) => {
- return {
- acls: state.acls
- };
- }
-
- export default connect(mapStateToProps)(GroupCard);
-
- class UnwrappedAclModal extends Component {
- constructor(props) {
- super(props);
-
- this.state = {
- loaded: false,
- acl: Immutable.fromJS({
- 'name': false,
- 'description': false,
- 'accesses': {}
- }),
- descriptiveAcls: Immutable.fromJS(DescriptiveAcls)
- };
-
- }
-
- componentWillMount() {
- const acl = this.props.acls.items.find((item) => {
- return item.get('name') == this.props.acls.selected;
- });
-
- if (acl) {
- this.setState({'acl': acl, 'loaded': true});
- } else {
- this.setState({'acl': Immutable.fromJS({
- 'name': false,
- 'description': false,
- 'accesses': {},
- }), 'loaded': true});
- }
- }
-
- handleClose(e) {
- this.props.onClose(e);
- }
-
- handleSave(e) {
- let acl = this.state.acl;
-
- const {dispatch} = this.props;
-
- let data = {
- 'name': acl.get('name'),
- 'description': acl.get('description'),
- 'accesses': acl.get('accesses').toJS()
- };
-
- dispatch(aclUpdate(data))
- .then(() => dispatch(aclsFetch()))
- .then(() => dispatch(snackbarShowMessage('Group updated')))
- .then(() => {
- this.props.onClose(e);
- });
- }
-
- handleFieldUpdate(e, val) {
- const prop = e.target.name;
- let acl = this.state.acl;
-
- this.setState({'acl': acl.set(prop, val)});
- }
-
- handleCheck(e, checked) {
-
- let currentAcls = this.state.acl.get('accesses');
- let groupLabel = e.target.name.substr(0, e.target.name.indexOf(' :: '));
- let actionLabel = e.target.name.substr(e.target.name.indexOf(' :: ')+4);
-
- // first find the acls for the targeted action
- const allAcls = this.state.descriptiveAcls;
- const group = allAcls.find((item) => item.get('label') == groupLabel);
- const action = group.get('actions').find((item) => item.get('label') == actionLabel);
-
- // if it's empty, set the default list
- if (!currentAcls || !currentAcls.size) {
- currentAcls = allAcls.find((item) => item.get('label') == 'defaultSet').get('acls');
- }
-
- // convert to a javascript object to make this easier to work with
- currentAcls = currentAcls.toJS();
-
- if (checked) {
- // first grab the ACLs for the new items
- const newAcls = action.get('acls').toJS();
-
- for (let i in newAcls) {
- let newAclGroup = newAcls[i];
- if (!_.has(currentAcls, newAclGroup.group)) {
- currentAcls[newAclGroup.group] = newAclGroup.actions;
- continue;
- }
-
- for (let j in newAclGroup.actions) {
- let newAction = newAclGroup.actions[j];
- currentAcls[newAclGroup.group].push(newAction);
- }
- currentAcls[newAclGroup.group] = _.uniq(currentAcls[newAclGroup.group]);
- }
- } else {
- // sorta the opposite.
- const removeAcls = action.get('acls').toJS();
-
- for (let i in removeAcls) {
- let removeAclGroup = removeAcls[i];
- // doesn't exist, which is odd...
- if (!_.has(currentAcls, removeAclGroup.group)) {
- currentAcls[removeAclGroup.group] = removeAclGroup.actions;
- continue;
- }
-
- for (let j in removeAclGroup.actions) {
- let existingAction = removeAclGroup.actions[j];
- for (let k in currentAcls[removeAclGroup.group]) {
- console.log(currentAcls[removeAclGroup.group][k].action, existingAction.action);
- if (currentAcls[removeAclGroup.group][k].action == existingAction.action) {
- currentAcls[removeAclGroup.group][k].allowed = "0";
- }
- }
- }
- currentAcls[removeAclGroup.group] = _.uniq(currentAcls[removeAclGroup.group]);
- }
- }
-
- this.setState({'acl': this.state.acl.set('accesses', Immutable.fromJS(currentAcls))});
- return;
- }
-
- render() {
-
- const actions = [
- <FlatButton
- label="Cancel"
- primary={false}
- onClick={this.handleClose.bind(this)}
- />,
- <FlatButton
- label="Save"
- primary={true}
- keyboardFocused={true}
- onClick={this.handleSave.bind(this)}
- />
- ];
-
- let resources = [];
- const accesses = this.state.acl.get('accesses').toJS();
- const allAcls = this.state.descriptiveAcls.toJS();
-
- // seems more complicated than it is.
- // looping through the descriptive acls.
- for (let i in allAcls) {
- let accessGroup = allAcls[i];
-
- // skip the default set
- if (accessGroup.label == 'defaultSet') {
- continue;
- }
-
- let options = [];
- for (let j in accessGroup.actions) {
- let accessAction = accessGroup.actions[j];
-
- // assume it's unchecked
- let isChecked = false;
- for (let k in accessAction.acls) {
- let aclSet = accessAction.acls[k];
-
- const currentAcls = accesses[aclSet.group];
- if (!currentAcls) {
- continue;
- }
-
- const total = aclSet.actions.length;
- let count = 0;
- _.find(currentAcls, (acl) => {
- if (acl.action == '*' && acl.allowed == "1") {
- count = total;
- return true;
- }
- for (let i in aclSet.actions) {
- if (acl.action == aclSet.actions[i].action
- && acl.allowed == aclSet.actions[i].allowed) {
- count++;
- }
- }
- });
-
- if (count == total) {
- isChecked = true;
- }
- }
-
- options.push(
- <Checkbox label={accessAction.label} name={accessGroup.label + ' :: ' + accessAction.label} defaultChecked={isChecked} onCheck={this.handleCheck.bind(this)} />
- );
- }
-
- resources.push(
- <div>
- <h4>{accessGroup.label}</h4>
- {options}
- </div>
- );
- }
-
- return (
- <Dialog
- title="Edit Group"
- actions={actions}
- modal={false}
- open={this.props.open}
- onRequestClose={this.handleClose.bind(this)}
- autoScrollBodyContent={true}
- >
-
- <TextField
- floatingLabelText="Name"
- value={this.state.acl.get('name') ? this.state.acl.get('name') : ''}
- name='name'
- disabled={this.props.acls.selected ? true : false}
- fullWidth={true}
- onChange={this.handleFieldUpdate.bind(this)}
- />
-
- <TextField
- floatingLabelText="Description"
- value={this.state.acl.get('description') ? this.state.acl.get('description') : ''}
- name='description'
- multiline={true}
- fullWidth={true}
- onChange={this.handleFieldUpdate.bind(this)}
- />
-
- <Divider />
- <h3>Resources</h3>
- {resources}
- <p className='align-center small'>
- If your publication requires more granular access controls, please <a href="mailto:support@getsnworks.com">contact support</a>.
- </p>
- </Dialog>
- );
- }
- }
-
- const AclModal = connect(mapStateToProps)(UnwrappedAclModal);