UNPKG

cspace-ui

Version:
393 lines (336 loc) 13.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _immutable = _interopRequireDefault(require("immutable")); var _reactIntl = require("react-intl"); var _get = _interopRequireDefault(require("lodash/get")); var _cspaceInput = require("cspace-input"); var _configHelpers = require("../../helpers/configHelpers"); var _PermissionButton = _interopRequireDefault(require("../../../styles/cspace-ui/PermissionButton.css")); var _PermissionsInput = _interopRequireDefault(require("../../../styles/cspace-ui/PermissionsInput.css")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } const { MiniButton } = _cspaceInput.components; const { getPath, pathPropType } = _cspaceInput.helpers.pathHelpers; const permMessages = (0, _reactIntl.defineMessages)({ '': { "id": "permissionsInput.perm.none", "defaultMessage": "None" }, RL: { "id": "permissionsInput.perm.read", "defaultMessage": "Read" }, CRUL: { "id": "permissionsInput.perm.write", "defaultMessage": "Write" }, CRUDL: { "id": "permissionsInput.perm.delete", "defaultMessage": "Delete" } }); const serviceTypeMessages = (0, _reactIntl.defineMessages)({ object: { "id": "permissionsInput.serviceType.object", "defaultMessage": "Objects" }, procedure: { "id": "permissionsInput.serviceType.procedure", "defaultMessage": "Procedures" }, authority: { "id": "permissionsInput.serviceType.authority", "defaultMessage": "Authorities" }, utility: { "id": "permissionsInput.serviceType.utility", "defaultMessage": "Utility Resources" }, security: { "id": "permissionsInput.serviceType.security", "defaultMessage": "Security Resources" } }); const serviceTypes = ['object', 'procedure', 'authority', 'utility', 'security']; const propTypes = { /* eslint-disable react/no-unused-prop-types */ name: _propTypes.default.string, parentPath: pathPropType, subpath: pathPropType, /* eslint-enable react/no-unused-prop-types */ readOnly: _propTypes.default.bool, resourceNames: _propTypes.default.instanceOf(_immutable.default.List), value: _propTypes.default.oneOfType([_propTypes.default.instanceOf(_immutable.default.List), _propTypes.default.instanceOf(_immutable.default.Map)]), readPerms: _propTypes.default.func, onCommit: _propTypes.default.func }; const contextTypes = { intl: _reactIntl.intlShape, config: _propTypes.default.object }; class PermissionsInput extends _react.Component { constructor(props, context) { super(props, context); this.handleHeaderButtonClick = this.handleHeaderButtonClick.bind(this); this.handleRadioChange = this.handleRadioChange.bind(this); } componentDidMount() { const { resourceNames, readPerms } = this.props; const { config } = this.context; if (!resourceNames && readPerms) { readPerms(config); } } getPermsMap() { // The services layer gives us a list of resources and action groups. Build a map of resource // names to UI permissions. let permissionsList = this.props.value; if (!permissionsList) { return undefined; } if (!_immutable.default.List.isList(permissionsList)) { permissionsList = _immutable.default.List.of(permissionsList); } const perms = {}; permissionsList.forEach(permission => { const resourceName = permission.get('resourceName'); const actionGroup = permission.get('actionGroup'); perms[resourceName] = actionGroup; }); return perms; } getRecordTypeConfigs() { const { config } = this.context; const { resourceNames } = this.props; return resourceNames.map(resourceName => (0, _configHelpers.getRecordTypeConfigByServicePath)(config, resourceName)).filter(recordTypeConfig => recordTypeConfig && !recordTypeConfig.disabled); } updatePerms(updates) { const perms = this.getPermsMap() || {}; Object.assign(perms, updates); return _immutable.default.List(Object.keys(perms).filter(resourceName => !!perms[resourceName]).map(resourceName => _immutable.default.Map({ resourceName, actionGroup: perms[resourceName] }))); } stageUpdate(recordType, actionGroup) { let updates = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; /* The updates arg is mutated by this method. */ /* eslint-disable no-param-reassign */ const { config } = this.context; const serviceConfig = (0, _get.default)(config, ['recordTypes', recordType, 'serviceConfig']); const { documentName, servicePath, serviceType } = serviceConfig; const resourceName = servicePath; const workflowDeleteResourceName = "/".concat(resourceName, "/*/workflow/delete"); updates[resourceName] = actionGroup; if (serviceType === 'object' || serviceType === 'procedure' || serviceType === 'authority' || serviceType === 'utility' && // TODO: These utility records don't have soft delete. Make this configured, instead of // hardcoded. resourceName !== 'idgenerators' && resourceName !== 'structureddates' && // TODO: Relation records have soft delete, but the UI issues hard deletes (for // consistency with pre-5.0 behavior). Therefore hard-delete permission needs to be // retained. Make this configured, instead of hard-coded. resourceName !== 'relations') { // For object, procedure, authority, and utility types, replace delete permission with // permissions on the delete workflow transition. (Security records do not have // soft-delete). if (actionGroup) { if (actionGroup.includes('D')) { updates[workflowDeleteResourceName] = 'CRUDL'; updates[resourceName] = actionGroup.replace('D', ''); } else { updates[workflowDeleteResourceName] = 'RL'; } } else { updates[workflowDeleteResourceName] = ''; } } if (resourceName === 'relations') { // The UI might switch to soft-deleting relations in the future, so also grant permission on // the delete workflow transition if delete permission exists. if (actionGroup) { if (actionGroup.includes('D')) { updates[workflowDeleteResourceName] = 'CRUDL'; } else { updates[workflowDeleteResourceName] = 'RL'; } } else { updates[workflowDeleteResourceName] = ''; } } if (serviceType === 'authority') { // Permissions on authorities should be set on both the authorities and their items. const itemResourceName = documentName; updates[itemResourceName] = actionGroup; } if (resourceName === 'authorization/roles') { // If the authorization/roles resource can be read, allow permissions to be read and // listed. This allows the permissions list to be displayed when viewing the role. const permissionsResourceName = 'authorization/permissions'; updates[permissionsResourceName] = actionGroup && actionGroup.includes('R') ? 'RL' : ''; } // Always allow read and list on servicegroups. updates.servicegroups = 'RL'; return updates; /* eslint-enable no-param-reassign */ } handleHeaderButtonClick(event) { const { servicetype: serviceType, actiongroup: actionGroup } = event.currentTarget.dataset; const { onCommit } = this.props; if (onCommit) { const stagedUpdates = {}; this.getRecordTypeConfigs().filter(recordTypeConfig => recordTypeConfig.serviceConfig.serviceType === serviceType).forEach(recordTypeConfig => { this.stageUpdate(recordTypeConfig.name, actionGroup, stagedUpdates); }); const updatedPerms = this.updatePerms(stagedUpdates); onCommit(getPath(this.props), updatedPerms); } } handleRadioChange(event) { const { value: actionGroup, checked: selected } = event.target; const { name: recordType } = event.target.dataset; const { onCommit } = this.props; if (selected && onCommit) { const stagedUpdates = this.stageUpdate(recordType, actionGroup); const updatedPerms = this.updatePerms(stagedUpdates); onCommit(getPath(this.props), updatedPerms); } } renderHeaderButton(serviceType, actionGroup) { const { readOnly } = this.props; return _react.default.createElement(MiniButton, { autoWidth: true, onClick: this.handleHeaderButtonClick, "data-servicetype": serviceType, "data-actiongroup": actionGroup, disabled: readOnly }, _react.default.createElement(_reactIntl.FormattedMessage, permMessages[actionGroup])); } renderRadioButton(perms, recordType, resourceName, value) { const { readOnly } = this.props; const className = readOnly ? _PermissionButton.default.readOnly : _PermissionButton.default.normal; let checked = false; if (perms) { let effectivePerms = perms[resourceName]; if (effectivePerms === 'CRUL') { // Check for soft-delete perm, and synthesize a delete perm if present. const workflowDeleteResourceName = "/".concat(resourceName, "/*/workflow/delete"); if (perms[workflowDeleteResourceName] === 'CRUDL') { effectivePerms = 'CRUDL'; } } checked = value ? effectivePerms === value : !effectivePerms; } return (// FIXME: Do I really need for if I'm wrapping the input in the label? // eslint-disable-next-line jsx-a11y/label-has-for _react.default.createElement("label", { className: className }, _react.default.createElement(_reactIntl.FormattedMessage, permMessages[value]), _react.default.createElement("input", { checked: checked, "data-name": recordType, type: "radio", value: value, disabled: readOnly, onChange: this.handleRadioChange }), _react.default.createElement("div", null)) ); } renderPermRows() { const { intl } = this.context; const perms = this.getPermsMap() || {}; const sections = []; serviceTypes.forEach(serviceType => { const rows = []; this.getRecordTypeConfigs().filter(recordTypeConfig => recordTypeConfig.serviceConfig.serviceType === serviceType).sort((recordTypeConfigA, recordTypeConfigB) => { // Primary sort by sortOrder let sortOrderA = recordTypeConfigA.sortOrder; let sortOrderB = recordTypeConfigB.sortOrder; if (typeof sortOrderA !== 'number') { sortOrderA = Number.MAX_VALUE; } if (typeof sortOrderB !== 'number') { sortOrderB = Number.MAX_VALUE; } if (sortOrderA !== sortOrderB) { return sortOrderA > sortOrderB ? 1 : -1; } // Secondary sort by label const labelA = intl.formatMessage(recordTypeConfigA.messages.record.collectionName); const labelB = intl.formatMessage(recordTypeConfigB.messages.record.collectionName); // FIXME: This should be locale aware return labelA.localeCompare(labelB); }).forEach(recordTypeConfig => { const name = recordTypeConfig.name; const resourceName = recordTypeConfig.serviceConfig.servicePath; const nameMessage = (0, _get.default)(recordTypeConfig, ['messages', 'record', 'collectionName']); const formattedName = nameMessage ? intl.formatMessage(nameMessage) : "[ ".concat(name, " ]"); rows.push(_react.default.createElement("div", { key: name }, _react.default.createElement("div", null, formattedName), _react.default.createElement("div", null, this.renderRadioButton(perms, name, resourceName, ''), this.renderRadioButton(perms, name, resourceName, 'RL'), this.renderRadioButton(perms, name, resourceName, 'CRUL'), this.renderRadioButton(perms, name, resourceName, 'CRUDL')))); }); sections.push(_react.default.createElement("section", { key: serviceType }, _react.default.createElement("header", null, _react.default.createElement("h3", null, _react.default.createElement(_reactIntl.FormattedMessage, serviceTypeMessages[serviceType])), _react.default.createElement("ul", null, _react.default.createElement("li", null, this.renderHeaderButton(serviceType, '')), _react.default.createElement("li", null, this.renderHeaderButton(serviceType, 'RL')), _react.default.createElement("li", null, this.renderHeaderButton(serviceType, 'CRUL')), _react.default.createElement("li", null, this.renderHeaderButton(serviceType, 'CRUDL')))), rows)); }); return sections; } render() { const { readOnly, resourceNames } = this.props; if (!resourceNames) { return null; } let { value } = this.props; if (value && !_immutable.default.List.isList(value)) { value = _immutable.default.List.of(value); } const className = readOnly ? _PermissionsInput.default.readOnly : _PermissionsInput.default.common; return _react.default.createElement("div", { className: className }, this.renderPermRows()); } } exports.default = PermissionsInput; PermissionsInput.propTypes = propTypes; PermissionsInput.contextTypes = contextTypes;