@plone/volto
Version:
Volto
365 lines (348 loc) • 10.9 kB
JSX
/**
* Aliases container.
* @module components/manage/Aliases/Aliases
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { Link } from 'react-router-dom';
import { createPortal } from 'react-dom';
import { toast } from 'react-toastify';
import {
Button,
Checkbox,
Container,
Form,
Header,
Input,
Segment,
} from 'semantic-ui-react';
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import {
removeAliases,
addAliases,
getAliases,
} from '@plone/volto/actions/aliases/aliases';
import { getContent } from '@plone/volto/actions/content/content';
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
import { getBaseUrl } from '@plone/volto/helpers/Url/Url';
import Icon from '@plone/volto/components/theme/Icon/Icon';
import Toolbar from '@plone/volto/components/manage/Toolbar/Toolbar';
import Toast from '@plone/volto/components/manage/Toast/Toast';
import backSVG from '@plone/volto/icons/back.svg';
const messages = defineMessages({
back: {
id: 'Back',
defaultMessage: 'Back',
},
aliases: {
id: 'URL Management',
defaultMessage: 'URL Management',
},
success: {
id: 'Success',
defaultMessage: 'Success',
},
successAdd: {
id: 'Alias has been added',
defaultMessage: 'Alias has been added',
},
});
/**
* Aliases class.
* @class Aliases
* @extends Component
*/
class Aliases extends Component {
/**
* Property types.
* @property {Object} propTypes Property types.
* @static
*/
static propTypes = {
removeAliases: PropTypes.func.isRequired,
addAliases: PropTypes.func.isRequired,
getAliases: PropTypes.func.isRequired,
pathname: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
};
/**
* Constructor
* @method constructor
* @param {Object} props Component properties
* @constructs Aliases
*/
constructor(props) {
super(props);
this.state = {
isClient: false,
newAlias: '',
isAliasCorrect: false,
isAliasAlready: false,
aliasesToRemove: [],
};
}
/**
* Component did mount
* @method componentDidMount
* @returns {undefined}
*/
componentDidMount() {
this.props.getAliases(getBaseUrl(this.props.pathname), {
query: '',
manual: '',
datetime: '',
batchSize: '',
});
this.props.getContent(getBaseUrl(this.props.pathname));
this.setState({ isClient: true });
}
componentDidUpdate(prevProps, prevState) {
if (prevState.newAlias !== this.state.newAlias) {
if (this.state.newAlias.charAt(0) === '/') {
this.setState({ isAliasCorrect: true });
} else {
this.setState({ isAliasCorrect: false });
}
if (
this.props.aliases?.items.find(
(item) => item.path === this.state.newAlias,
)
) {
this.setState({ isAliasAlready: true });
} else {
this.setState({ isAliasAlready: false });
}
}
}
/**
* Component will receive props
* @method componentWillReceiveProps
* @param {Object} nextProps Next properties
* @returns {undefined}
*/
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props.aliases.add.loading && nextProps.aliases.add.loaded) {
if (nextProps.aliases.add.error) {
this.setState({ isAliasAlready: true });
} else {
this.setState({ isAliasAlready: false });
toast.success(
<Toast
success
title={this.props.intl.formatMessage(messages.success)}
content={this.props.intl.formatMessage(messages.successAdd)}
/>,
);
}
this.props.getAliases(getBaseUrl(this.props.pathname), {
query: '',
manual: '',
datetime: '',
batchSize: '',
});
}
if (this.props.aliases.remove.loading && nextProps.aliases.remove.loaded) {
this.props.getAliases(getBaseUrl(this.props.pathname), {
query: '',
manual: '',
datetime: '',
batchSize: '',
});
}
}
/**
* Url change handler
* @method handleAltChange
* @returns {undefined}
*/
handleAltChange(val) {
this.setState({ newAlias: val });
}
/**
* New alias submit handler
* @method handleSubmitAlias
* @returns {undefined}
*/
handleSubmitAlias() {
if (this.state.isAliasCorrect) {
this.props.addAliases(getBaseUrl(this.props.pathname), {
items: this.state.newAlias,
});
this.setState({ newAlias: '' });
}
}
/**
* Check to-remove aliases handler
* @method handleSubmitAlias
* @returns {undefined}
*/
handleCheckAlias(alias) {
const aliases = this.state.aliasesToRemove;
if (aliases.includes(alias)) {
const index = aliases.indexOf(alias);
if (index > -1) {
let newAliasesArr = aliases;
newAliasesArr.splice(index, 1);
this.setState({ aliasesToRemove: newAliasesArr });
}
} else {
this.setState({
aliasesToRemove: [...this.state.aliasesToRemove, alias],
});
}
}
/**
* Remove aliases handler
* @method handleRemoveAliases
* @returns {undefined}
*/
handleRemoveAliases() {
this.props.removeAliases(getBaseUrl(this.props.pathname), {
items: this.state.aliasesToRemove,
});
this.setState({ aliasesToRemove: [] });
}
/**
* Render method.
* @method render
* @returns {string} Markup for the component.
*/
render() {
return (
<Container id="aliases">
<Helmet title={this.props.intl.formatMessage(messages.aliases)} />
<Segment.Group raised>
<Segment className="primary">
<FormattedMessage
id="URL Management for {title}"
defaultMessage="URL Management for {title}"
values={{ title: <q>{this.props.title}</q> }}
/>
</Segment>
<Segment secondary>
<FormattedMessage
id="Using this form, you can manage alternative urls for an item. This is an easy way to make an item available under two different URLs."
defaultMessage="Using this form, you can manage alternative urls for an item. This is an easy way to make an item available under two different URLs."
/>
</Segment>
<Form>
<Segment>
<Header size="medium">
<FormattedMessage
id="Add a new alternative url"
defaultMessage="Add a new alternative url"
/>
</Header>
<p className="help">
<FormattedMessage
id="Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
defaultMessage="Enter the absolute path where the alternative url should exist. The path must start with '/'. Only urls that result in a 404 not found page will result in a redirect occurring."
/>
</p>
<Form.Field>
<Input
id="alternative-url-input"
name="alternative-url"
placeholder="/example"
value={this.state.newAlias}
onChange={(e) => this.handleAltChange(e.target.value)}
/>
{!this.state.isAliasCorrect && this.state.newAlias !== '' && (
<p style={{ color: 'red' }}>
<FormattedMessage
id="Alternative url path must start with a slash."
defaultMessage="Alternative url path must start with a slash."
/>
</p>
)}
{this.state.isAliasAlready && (
<p style={{ color: 'red' }}>
<FormattedMessage
id="The provided alternative url already exists!"
defaultMessage="The provided alternative url already exists!"
/>
</p>
)}
</Form.Field>
<Button
id="submit-alias"
primary
onClick={() => this.handleSubmitAlias()}
disabled={
!this.state.isAliasCorrect ||
this.state.newAlias === '' ||
this.state.isAliasAlready
}
>
<FormattedMessage id="Add" defaultMessage="Add" />
</Button>
</Segment>
</Form>
<Form>
<Segment>
<Header size="medium">
<FormattedMessage
id="Existing alternative urls for this item"
defaultMessage="Existing alternative urls for this item"
/>
</Header>
{this.props.aliases?.items.map((alias, i) => (
<Form.Field key={i}>
<Checkbox
id={`alias-check-${i}`}
onChange={(e, { value }) => this.handleCheckAlias(value)}
value={alias.path}
label={alias.path}
checked={this.state.aliasesToRemove.includes(alias.path)}
/>
</Form.Field>
))}
<Button
id="remove-alias"
onClick={() => this.handleRemoveAliases()}
primary
disabled={this.state.aliasesToRemove.length === 0}
>
<FormattedMessage id="Remove" defaultMessage="Remove" />
</Button>
</Segment>
</Form>
</Segment.Group>
{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) => ({
aliases: state.aliases,
pathname: props.location.pathname,
title: state.content.data?.title || '',
}),
{ addAliases, getAliases, removeAliases, getContent },
),
)(Aliases);