@plone/volto
Version:
Volto
539 lines (516 loc) • 16.2 kB
JSX
/**
* Rules container.
* @module components/manage/Rules/Rules
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { Link } from 'react-router-dom';
import { createPortal } from 'react-dom';
import {
Button,
Checkbox,
Container,
Segment,
Select,
Table,
} from 'semantic-ui-react';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
import {
addRule,
getRules,
enableRules,
disableRules,
applyRulesToSubfolders,
unapplyRulesToSubfolders,
removeRules,
} from '@plone/volto/actions/rules/rules';
import Icon from '@plone/volto/components/theme/Icon/Icon';
import Toolbar from '@plone/volto/components/manage/Toolbar/Toolbar';
import backSVG from '@plone/volto/icons/back.svg';
import checkSVG from '@plone/volto/icons/check.svg';
import { toast } from 'react-toastify';
import Toast from '@plone/volto/components/manage/Toast/Toast';
const messages = defineMessages({
back: {
id: 'Back',
defaultMessage: 'Back',
},
rules: {
id: 'Rules',
defaultMessage: 'Rules',
},
success: {
id: 'Success',
defaultMessage: 'Success',
},
add: {
id: 'Added',
defaultMessage: 'Added',
},
enable: {
id: 'Enabled',
defaultMessage: 'Enabled',
},
disable: {
id: 'Disabled',
defaultMessage: 'Disabled',
},
apply: {
id: 'Applied to subfolders',
defaultMessage: 'Applied to subfolders',
},
unapply: {
id: 'Disabled apply to subfolders',
defaultMessage: 'Disabled apply to subfolders',
},
remove: {
id: 'Unassigned',
defaultMessage: 'Unassigned',
},
select_rule: {
id: 'Select rule',
defaultMessage: 'Select rule',
},
});
/**
* Rules class.
* @class Rules
* @extends Component
*/
class Rules extends Component {
/**
* Property types.
* @property {Object} propTypes Property types.
* @static
*/
static propTypes = {
getRules: PropTypes.func.isRequired,
addRule: PropTypes.func.isRequired,
enableRules: PropTypes.func.isRequired,
disableRules: PropTypes.func.isRequired,
applyRulesToSubfolders: PropTypes.func.isRequired,
unapplyRulesToSubfolders: PropTypes.func.isRequired,
removeRules: PropTypes.func.isRequired,
pathname: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
};
/**
* Constructor
* @method constructor
* @param {Object} props Component properties
* @constructs Rules
*/
constructor(props) {
super(props);
this.state = {
isClient: false,
checkedRules: [],
newRule: '',
};
}
/**
* Component did mount
* @method componentDidMount
* @returns {undefined}
*/
componentDidMount() {
this.props.getRules(getBaseUrl(this.props.pathname));
this.setState({ isClient: true });
}
componentDidUpdate(prevProps, prevState) {}
/**
* Component will receive props
* @method componentWillReceiveProps
* @param {Object} nextProps Next properties
* @returns {undefined}
*/
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props.rules.add.loading && nextProps.rules.add.loaded) {
this.props.getRules(getBaseUrl(this.props.pathname));
this.setState({ newRule: '' });
toast.success(
<Toast
success
title={this.props.intl.formatMessage(messages.success)}
content={this.props.intl.formatMessage(messages.add)}
/>,
);
}
if (this.props.rules.disable.loading && nextProps.rules.disable.loaded) {
this.props.getRules(getBaseUrl(this.props.pathname));
this.setState({ checkedRules: [] });
toast.success(
<Toast
success
title={this.props.intl.formatMessage(messages.success)}
content={this.props.intl.formatMessage(messages.disable)}
/>,
);
}
if (this.props.rules.enable.loading && nextProps.rules.enable.loaded) {
this.props.getRules(getBaseUrl(this.props.pathname));
this.setState({ checkedRules: [] });
toast.success(
<Toast
success
title={this.props.intl.formatMessage(messages.success)}
content={this.props.intl.formatMessage(messages.enable)}
/>,
);
}
if (this.props.rules.apply.loading && nextProps.rules.apply.loaded) {
this.props.getRules(getBaseUrl(this.props.pathname));
this.setState({ checkedRules: [] });
toast.success(
<Toast
success
title={this.props.intl.formatMessage(messages.success)}
content={this.props.intl.formatMessage(messages.apply)}
/>,
);
}
if (this.props.rules.unapply.loading && nextProps.rules.unapply.loaded) {
this.props.getRules(getBaseUrl(this.props.pathname));
this.setState({ checkedRules: [] });
toast.success(
<Toast
success
title={this.props.intl.formatMessage(messages.success)}
content={this.props.intl.formatMessage(messages.unapply)}
/>,
);
}
if (this.props.rules.remove.loading && nextProps.rules.remove.loaded) {
this.props.getRules(getBaseUrl(this.props.pathname));
this.setState({ checkedRules: [] });
toast.success(
<Toast
success
title={this.props.intl.formatMessage(messages.success)}
content={this.props.intl.formatMessage(messages.remove)}
/>,
);
}
}
/**
* Rule check handler
* @method handleCheckRule
* @returns {undefined}
*/
handleCheckRule = (rule) => {
const rules = this.state.checkedRules;
if (rules.includes(rule)) {
const index = rules.indexOf(rule);
if (index > -1) {
let newRules = rules;
newRules.splice(index, 1);
this.setState({ checkedRules: newRules });
}
} else {
this.setState({
checkedRules: [...this.state.checkedRules, rule],
});
}
};
/**
* Disable rules handler
* @method handleDisableRules
* @returns {undefined}
*/
handleDisableRules = () => {
this.props.disableRules(
getBaseUrl(this.props.pathname),
this.state.checkedRules,
);
};
/**
* Enable rules handler
* @method handleEnableRules
* @returns {undefined}
*/
handleEnableRules = () => {
this.props.enableRules(
getBaseUrl(this.props.pathname),
this.state.checkedRules,
);
};
/**
* Apply rules to subfolder handler
* @method handleApplyToSubfolder
* @returns {undefined}
*/
handleApplyToSubfolder = () => {
this.props.applyRulesToSubfolders(
getBaseUrl(this.props.pathname),
this.state.checkedRules,
);
};
/**
* Unapply rules to subfolder handler
* @method handleUnapplyToSubfolder
* @returns {undefined}
*/
handleUnapplyToSubfolder = () => {
this.props.unapplyRulesToSubfolders(
getBaseUrl(this.props.pathname),
this.state.checkedRules,
);
};
/**
* Remove rules handler
* @method handleRemoveRules
* @returns {undefined}
*/
handleRemoveRules = () => {
this.props.removeRules(
getBaseUrl(this.props.pathname),
this.state.checkedRules,
);
};
/**
* Remove rules handler
* @method handleAddRule
* @returns {undefined}
*/
handleAddRule = () => {
this.props.addRule(getBaseUrl(this.props.pathname), this.state.newRule);
};
/**
* Render method.
* @method render
* @returns {string} Markup for the component.
*/
render() {
const { acquired_rules, assigned_rules, assignable_rules } =
this.props.rules?.rules || {};
return (
<Container id="rules">
<Helmet title={this.props.intl.formatMessage(messages.rules)} />
<Segment.Group raised>
<Segment className="primary">
<FormattedMessage
id="Content rules for {title}"
defaultMessage="Content rules for {title}"
values={{ title: <q>{this.props.title}</q> }}
/>
</Segment>
<Segment secondary>
<FormattedMessage
id="The following content rules are active in this Page. Use the content rules control panel to create new rules or delete or modify existing ones."
defaultMessage="The following content rules are active in this Page. Use the content rules control panel to create new rules or delete or modify existing ones."
/>
</Segment>
</Segment.Group>
{acquired_rules && acquired_rules.length > 0 && (
<Table>
<Table.Body>
<Table.Row>
<Table.HeaderCell>
<FormattedMessage
id="Content rules from parent folders"
defaultMessage="Content rules from parent folders"
/>
</Table.HeaderCell>
<Table.HeaderCell>
<FormattedMessage id="Active" defaultMessage="Active" />
</Table.HeaderCell>
</Table.Row>
{acquired_rules.map((rule, i) => (
<Table.Row key={i}>
<Table.Cell>
{/* this can be a link to the control panel */}
{rule.title}({rule.trigger})
</Table.Cell>
<Table.Cell>
{rule.enabled && (
<span style={{ color: 'green' }}>
<Icon
name={checkSVG}
className="contents circled"
size="10px"
/>
</span>
)}
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
)}
{assignable_rules && assignable_rules.length > 0 && (
<Segment>
<FormattedMessage
id="Available content rules:"
defaultMessage="Available content rules:"
/>
<div style={{ display: 'flex' }}>
<Select
placeholder={this.props.intl.formatMessage(
messages.select_rule,
)}
value={this.state.newRule}
onChange={(e, { value }) => this.setState({ newRule: value })}
options={assignable_rules.map((rule, i) => {
return { key: rule.id, value: rule.id, text: rule.title };
})}
/>
<Button
onClick={this.handleAddRule}
primary
style={{ marginLeft: '10px' }}
>
<FormattedMessage id="Add" defaultMessage="Add" />
</Button>
</div>
</Segment>
)}
{assigned_rules && assigned_rules.length > 0 && (
<React.Fragment>
<Table>
<Table.Body>
<Table.Row>
<Table.HeaderCell>
<FormattedMessage id="Select" defaultMessage="Select" />
</Table.HeaderCell>
<Table.HeaderCell>
<FormattedMessage
id="Active content rules in this Page"
defaultMessage="Active content rules in this Page"
/>
</Table.HeaderCell>
<Table.HeaderCell>
<FormattedMessage
id="Applies to subfolders?"
defaultMessage="Applies to subfolders?"
/>
</Table.HeaderCell>
<Table.HeaderCell>
<FormattedMessage
id="Enabled here?"
defaultMessage="Enabled here?"
/>
</Table.HeaderCell>
<Table.HeaderCell>
<FormattedMessage id="Enabled?" defaultMessage="Enabled?" />
</Table.HeaderCell>
</Table.Row>
{assigned_rules.map((rule, i) => (
<Table.Row key={i}>
<Table.Cell>
<Checkbox
onChange={(o, { value }) => this.handleCheckRule(value)}
value={rule.id}
checked={this.state.checkedRules.includes(rule.id)}
/>
</Table.Cell>
<Table.Cell>
{rule.title}({rule.trigger})
</Table.Cell>
<Table.Cell>
{rule.bubbles && (
<span style={{ color: 'green' }}>
<Icon
name={checkSVG}
className="contents circled"
size="10px"
/>
</span>
)}
</Table.Cell>
<Table.Cell>
{rule.enabled && (
<span style={{ color: 'green' }}>
<Icon
name={checkSVG}
className="contents circled"
size="10px"
/>
</span>
)}
</Table.Cell>
<Table.Cell>
{rule.global_enabled && (
<span style={{ color: 'green' }}>
<Icon
name={checkSVG}
className="contents circled"
size="10px"
/>
</span>
)}
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
<Button onClick={this.handleEnableRules} primary>
<FormattedMessage id="Enable" defaultMessage="Enable" />
</Button>
<Button onClick={this.handleDisableRules} primary>
<FormattedMessage id="Disable" defaultMessage="Disable" />
</Button>
<Button onClick={this.handleApplyToSubfolder} primary>
<FormattedMessage
id="Apply to subfolders"
defaultMessage="Apply to subfolders"
/>
</Button>
<Button onClick={this.handleUnapplyToSubfolder} primary>
<FormattedMessage
id="Disable apply to subfolders"
defaultMessage="Disable apply to subfolders"
/>
</Button>
<Button color="youtube" onClick={this.handleRemoveRules}>
<FormattedMessage id="Unassign" defaultMessage="Unassign" />
</Button>
</React.Fragment>
)}
{this.state.isClient &&
createPortal(
<Toolbar
pathname={this.props.pathname}
hideDefaultViewButtons
inner={
<Link
to={`${getBaseUrl(this.props.pathname)}`}
className="item"
>
<Icon
name={backSVG}
className="contents circled"
size="30px"
title={this.props.intl.formatMessage(messages.back)}
/>
</Link>
}
/>,
document.getElementById('toolbar'),
)}
</Container>
);
}
}
export default compose(
injectIntl,
connect(
(state, props) => ({
rules: state.rules,
pathname: props.location.pathname,
title: state.content.data?.title || '',
}),
{
addRule,
getRules,
enableRules,
disableRules,
applyRulesToSubfolders,
unapplyRulesToSubfolders,
removeRules,
},
),
)(Rules);