UNPKG

@plone/volto

Version:
355 lines (329 loc) 9.22 kB
/** * Content type schema. * @module components/manage/Controlpanels/ContentTypeSchema */ import { getSchema, putSchema } from '@plone/volto/actions/schema/schema'; import { getParentUrl } from '@plone/volto/helpers/Url/Url'; import nth from 'lodash/nth'; import Error from '@plone/volto/components/theme/Error/Error'; import Icon from '@plone/volto/components/theme/Icon/Icon'; import Toast from '@plone/volto/components/manage/Toast/Toast'; import Toolbar from '@plone/volto/components/manage/Toolbar/Toolbar'; import { Form } from '@plone/volto/components/manage/Form'; import clearSVG from '@plone/volto/icons/clear.svg'; import saveSVG from '@plone/volto/icons/save.svg'; import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { defineMessages, injectIntl } from 'react-intl'; import { createPortal } from 'react-dom'; import { connect } from 'react-redux'; import { toast } from 'react-toastify'; import { compose } from 'redux'; import { Button, Header } from 'semantic-ui-react'; const messages = defineMessages({ title: { id: '{id} Schema', defaultMessage: '{id} Schema', }, success: { id: 'Success', defaultMessage: 'Success', }, save: { id: 'Save', defaultMessage: 'Save', }, cancel: { id: 'Cancel', defaultMessage: 'Cancel', }, error: { id: 'Error', defaultMessage: 'Error', }, typeCreated: { id: 'Schema updates', defaultMessage: 'Schema updates', }, info: { id: 'Info', defaultMessage: 'Info', }, changesSaved: { id: 'Changes saved.', defaultMessage: 'Changes saved.', }, }); /** * ContentTypeSchema class. * @class ContentTypeSchema * @extends Component */ class ContentTypeSchema extends Component { /** * Property types. * @property {Object} propTypes Property types. * @static */ static propTypes = { getSchema: PropTypes.func.isRequired, putSchema: PropTypes.func.isRequired, pathname: PropTypes.string.isRequired, id: PropTypes.string.isRequired, }; /** * Default properties * @property {Object} defaultProps Default properties. * @static */ static defaultProps = {}; /** * Constructor * @method constructor * @param {Object} props Component properties * @constructs WysiwygEditor */ constructor(props) { super(props); this.state = { error: null, schema: null, content: null, isClient: false, }; this.onCancel = this.onCancel.bind(this); this.onSubmit = this.onSubmit.bind(this); this.form = React.createRef(); } /** * Component did mount * @method componentDidMount * @returns {undefined} */ componentDidMount() { this.props.getSchema(this.props.id); this.setState({ isClient: true }); } /** * Component will receive props * @method componentWillReceiveProps * @param {Object} nextProps Next properties * @returns {undefined} */ UNSAFE_componentWillReceiveProps(nextProps) { // Schema error if (this.props.schemaRequest.loading && nextProps.schemaRequest.error) { this.setState({ error: nextProps.schemaRequest.error, }); } // Schema GET if (this.props.schemaRequest.loading && nextProps.schemaRequest.loaded) { let properties = nextProps.schema?.properties || {}; let content = {}; let value, key; for (key in properties) { value = properties[key].default; if (value) { content[key] = value; } } this.setState({ schema: nextProps.schema, content: content, }); } // Schema updated if ( this.props.schemaRequest.put.loading && nextProps.schemaRequest.put.loaded ) { // this.props.getSchema(this.props.id); toast.info( <Toast info title={this.props.intl.formatMessage(messages.info)} content={this.props.intl.formatMessage(messages.changesSaved)} />, ); } // Schema update error if ( this.props.schemaRequest.put.loading && nextProps.schemaRequest.put.error ) { toast.error( <Toast error title={this.props.intl.formatMessage(messages.error)} content={JSON.stringify( nextProps.schemaRequest.put.error.response.body?.message || nextProps.schemaRequest.put.error.response.text, )} />, ); } } /** * Submit handler * @method onSubmit * @param {object} data Form data. * @returns {undefined} */ onSubmit(data) { this.props.putSchema(this.props.id, data.schema); } /** * Cancel handler * @method onCancel * @returns {undefined} */ onCancel() { let url = getParentUrl(this.props.pathname); this.props.history.push(getParentUrl(url)); } form = React.createRef(); makeSchemaList = (schema) => { const result = { title: 'Schema', type: 'object', fieldsets: [ { fields: ['schema'], id: 'default', title: 'Default', }, ], properties: { schema: { description: 'Form schema', title: 'Form schema', type: 'schema', id: 'schema', widget: 'schema', }, }, required: [], layouts: null, }; result.layouts = schema.layouts.slice(); return result; }; isEditable = (field) => !field.behavior || field.behavior.includes('generated'); makeSchemaData = (schema, contentType) => { const fieldsets = schema.fieldsets.map((fieldset) => { const readOnlyFields = fieldset.fields.filter( (fieldId) => !this.isEditable(schema.properties[fieldId]) && fieldId !== 'changeNote', ); const userCreatedFields = fieldset.fields.filter((fieldId) => this.isEditable(schema.properties[fieldId]), ); const changeNote = fieldset.fields.filter( (fieldId) => fieldId === 'changeNote', ); return { ...fieldset, fields: [...readOnlyFields, ...userCreatedFields, ...changeNote], }; }); const result = { ...schema, fieldsets, contentType, }; return { schema: JSON.stringify(result) }; }; /** * Render method. * @method render * @returns {string} Markup for the component. */ render() { // Error if (this.state.error) { return <Error error={this.state.error} />; } if (this.state.schema) { const contentTypeSchema = this.makeSchemaList(this.state.schema); const schemaData = this.makeSchemaData(this.state.schema, this.props.id); return ( <div id="page-controlpanel-schema" className="ui container"> <Header disabled> {this.props.intl.formatMessage(messages.title, { id: this.props?.schema?.title || this.props.id, })} </Header> <Form ref={this.form} schema={contentTypeSchema} formData={schemaData} pathname={this.props.pathname} onSubmit={this.onSubmit} onCancel={this.onCancel} hideActions /> {this.state.isClient && createPortal( <Toolbar pathname={this.props.pathname} hideDefaultViewButtons inner={ <> <Button id="toolbar-save" className="save" aria-label={this.props.intl.formatMessage(messages.save)} onClick={() => this.form.current.onSubmit()} disabled={this.props.schemaRequest.put.loading} loading={this.props.schemaRequest.put.loading} > <Icon name={saveSVG} className="circled" size="30px" title={this.props.intl.formatMessage(messages.save)} /> </Button> <Button className="cancel" aria-label={this.props.intl.formatMessage( messages.cancel, )} onClick={() => this.onCancel()} > <Icon name={clearSVG} className="circled" size="30px" title={this.props.intl.formatMessage(messages.cancel)} /> </Button> </> } />, document.getElementById('toolbar'), )} </div> ); } return <div />; } } export default compose( injectIntl, connect( (state, props) => ({ schema: state.schema.schema, schemaRequest: state.schema, pathname: props.location.pathname, id: nth(props.location.pathname.split('/'), -2), }), { getSchema, putSchema, }, ), )(ContentTypeSchema);