From 6d5a6f23f0ef682fffce573f98a139ae3f228380 Mon Sep 17 00:00:00 2001 From: nileshgulia1 Date: Tue, 13 Feb 2024 14:49:47 +0530 Subject: [PATCH] fix: View.jsx customization for redirects --- .../volto/components/theme/View/Readme.md | 1 + .../volto/components/theme/View/View.jsx | 299 ++++++++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 src/customizations/volto/components/theme/View/Readme.md create mode 100644 src/customizations/volto/components/theme/View/View.jsx diff --git a/src/customizations/volto/components/theme/View/Readme.md b/src/customizations/volto/components/theme/View/Readme.md new file mode 100644 index 0000000..0a2502b --- /dev/null +++ b/src/customizations/volto/components/theme/View/Readme.md @@ -0,0 +1 @@ +This cusomization needs to be removed in volto 17. diff --git a/src/customizations/volto/components/theme/View/View.jsx b/src/customizations/volto/components/theme/View/View.jsx new file mode 100644 index 0000000..e836e45 --- /dev/null +++ b/src/customizations/volto/components/theme/View/View.jsx @@ -0,0 +1,299 @@ +/** + * View container. + * @module components/theme/View/View + */ + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { compose } from 'redux'; +import { Redirect } from 'react-router-dom'; +import { Portal } from 'react-portal'; +import { injectIntl } from 'react-intl'; +import qs from 'query-string'; + +import { + ContentMetadataTags, + Comments, + Tags, + Toolbar, +} from '@plone/volto/components'; +import { listActions, getContent } from '@plone/volto/actions'; +import { + BodyClass, + getBaseUrl, + flattenToAppURL, + getLayoutFieldname, + hasApiExpander, +} from '@plone/volto/helpers'; + +import config from '@plone/volto/registry'; + +/** + * View container class. + * @class View + * @extends Component + */ +class View extends Component { + /** + * Property types. + * @property {Object} propTypes Property types. + * @static + */ + static propTypes = { + actions: PropTypes.shape({ + object: PropTypes.arrayOf(PropTypes.object), + object_buttons: PropTypes.arrayOf(PropTypes.object), + user: PropTypes.arrayOf(PropTypes.object), + }), + listActions: PropTypes.func.isRequired, + /** + * Action to get the content + */ + getContent: PropTypes.func.isRequired, + /** + * Pathname of the object + */ + pathname: PropTypes.string.isRequired, + location: PropTypes.shape({ + search: PropTypes.string, + pathname: PropTypes.string, + }).isRequired, + /** + * Version id of the object + */ + versionId: PropTypes.string, + /** + * Content of the object + */ + content: PropTypes.shape({ + /** + * Layout of the object + */ + layout: PropTypes.string, + /** + * Allow discussion of the object + */ + allow_discussion: PropTypes.bool, + /** + * Title of the object + */ + title: PropTypes.string, + /** + * Description of the object + */ + description: PropTypes.string, + /** + * Type of the object + */ + '@type': PropTypes.string, + /** + * Subjects of the object + */ + subjects: PropTypes.arrayOf(PropTypes.string), + is_folderish: PropTypes.bool, + }), + error: PropTypes.shape({ + /** + * Error type + */ + status: PropTypes.number, + }), + }; + + /** + * Default properties. + * @property {Object} defaultProps Default properties. + * @static + */ + static defaultProps = { + actions: null, + content: null, + versionId: null, + error: null, + }; + + state = { + hasObjectButtons: null, + isClient: false, + }; + + componentDidMount() { + // Do not trigger the actions action if the expander is present + if (!hasApiExpander('actions', getBaseUrl(this.props.pathname))) { + this.props.listActions(getBaseUrl(this.props.pathname)); + } + + this.props.getContent( + getBaseUrl(this.props.pathname), + this.props.versionId, + ); + this.setState({ isClient: true }); + } + + /** + * Component will receive props + * @method componentWillReceiveProps + * @param {Object} nextProps Next properties + * @returns {undefined} + */ + UNSAFE_componentWillReceiveProps(nextProps) { + if (nextProps.pathname !== this.props.pathname) { + // Do not trigger the actions action if the expander is present + if (!hasApiExpander('actions', getBaseUrl(nextProps.pathname))) { + this.props.listActions(getBaseUrl(nextProps.pathname)); + } + + this.props.getContent( + getBaseUrl(nextProps.pathname), + this.props.versionId, + ); + } + + if (nextProps.actions.object_buttons) { + const objectButtons = nextProps.actions.object_buttons; + this.setState({ + hasObjectButtons: !!objectButtons.length, + }); + } + } + + /** + * Default fallback view + * @method getViewDefault + * @returns {string} Markup for component. + */ + getViewDefault = () => config.views.defaultView; + + /** + * Get view by content type + * @method getViewByType + * @returns {string} Markup for component. + */ + getViewByType = () => + config.views.contentTypesViews[this.props.content['@type']] || null; + + /** + * Get view by content layout property + * @method getViewByLayout + * @returns {string} Markup for component. + */ + getViewByLayout = () => + config.views.layoutViews[ + this.props.content[getLayoutFieldname(this.props.content)] + ] || null; + + /** + * Cleans the component displayName (specially for connected components) + * which have the Connect(componentDisplayName) + * @method cleanViewName + * @param {string} dirtyDisplayName The displayName + * @returns {string} Clean displayName (no Connect(...)). + */ + cleanViewName = (dirtyDisplayName) => + dirtyDisplayName + .replace('Connect(', '') + .replace('injectIntl(', '') + .replace(')', '') + .replace('connect(', '') + .toLowerCase(); + + /** + * Render method. + * @method render + * @returns {string} Markup for the component. + */ + render() { + const { views } = config; + if (this.props.error && this.props.error.code === 301) { + const redirect = flattenToAppURL(this.props.error.url).split('?')[0]; + return ; + } else if (this.props.error && !this.props.connectionRefused) { + let FoundView; + if (this.props.error.status === undefined) { + // For some reason, while development and if CORS is in place and the + // requested resource is 404, it returns undefined as status, then the + // next statement will fail + FoundView = views.errorViews.corsError; + } else { + FoundView = views.errorViews[this.props.error.status.toString()]; + } + if (!FoundView) { + FoundView = views.errorViews['404']; // default to 404 + } + return ( +
+ +
+ ); + } + if (!this.props.content) { + return ; + } + const RenderedView = + this.getViewByLayout() || this.getViewByType() || this.getViewDefault(); + + return ( +
+ + {/* Body class if displayName in component is set */} + + + {config.settings.showTags && + this.props.content.subjects && + this.props.content.subjects.length > 0 && ( + + )} + {/* Add opt-in social sharing if required, disabled by default */} + {/* In the future this might be parameterized from the app config */} + {/* */} + {this.props.content.allow_discussion && ( + + )} + {this.state.isClient && ( + + } /> + + )} +
+ ); + } +} + +export default compose( + injectIntl, + connect( + (state, props) => ({ + actions: state.actions.actions, + token: state.userSession.token, + content: state.content.data, + error: state.content.get.error, + apiError: state.apierror.error, + connectionRefused: state.apierror.connectionRefused, + pathname: props.location.pathname, + versionId: + qs.parse(props.location.search) && + qs.parse(props.location.search).version, + }), + { + listActions, + getContent, + }, + ), +)(View);