UNPKG

@plone/volto

Version:
476 lines (460 loc) 15.4 kB
import React, { useState, useEffect, useCallback } from 'react'; import { useDispatch, useSelector, shallowEqual } from 'react-redux'; import { Link } from 'react-router-dom'; import { createPortal } from 'react-dom'; import { useClient } from '@plone/volto/hooks/client/useClient'; import { Accordion, Button, Container, Divider, Header, Label, Message, Segment, Dimmer, Loader, } from 'semantic-ui-react'; import { FormattedMessage, defineMessages, useIntl } from 'react-intl'; import { installAddon, listAddons, uninstallAddon, upgradeAddon, } from '@plone/volto/actions/addons/addons'; import Helmet from '@plone/volto/helpers/Helmet/Helmet'; 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 circleBottomSVG from '@plone/volto/icons/circle-bottom.svg'; import circleTopSVG from '@plone/volto/icons/circle-top.svg'; import backSVG from '@plone/volto/icons/back.svg'; import { toast } from 'react-toastify'; const messages = defineMessages({ activateAndDeactivate: { id: 'Activate and deactivate', defaultMessage: 'Activate and deactivate add-ons in the lists below.', }, addAddons: { id: 'Add Addons', defaultMessage: 'To make new add-ons show up here, add them to your configuration, build, and restart the server process. For detailed instructions see', }, addonsSettings: { id: 'Add-ons Settings', defaultMessage: 'Add-ons Settings', }, available: { id: 'Available', defaultMessage: 'Available', }, availableVersion: { id: 'Latest version', defaultMessage: 'Latest version', }, back: { id: 'Back', defaultMessage: 'Back', }, installed: { id: 'Installed', defaultMessage: 'Installed', }, installedVersion: { id: 'Installed version', defaultMessage: 'Installed version', }, noUninstallProfile: { id: 'No uninstall profile', defaultMessage: 'This addon does not provide an uninstall profile.', }, update: { id: 'Update', defaultMessage: 'Update', }, updatesAvailable: { id: 'Updates available', defaultMessage: 'Updates available', }, updateInstalledAddons: { id: 'Update installed addons:', defaultMessage: 'Update installed addons:', }, uninstall: { id: 'Uninstall', defaultMessage: 'Uninstall', }, addOns: { id: 'Add-ons', defaultMessage: 'Add-ons', }, installingAnAddon: { id: 'Installing a third party add-on', defaultMessage: 'Installing a third party add-on', }, success: { id: 'Success', defaultMessage: 'Success', }, error: { id: 'Error', defaultMessage: 'Error', }, addonUpgraded: { id: 'Addon upgraded succesfuly', defaultMessage: 'Addon upgraded succesfuly', }, addonNotUpgraded: { id: 'Addon could not be upgraded', defaultMessage: 'Addon could not be upgraded', }, addonInstalled: { id: 'Addon installed succesfuly', defaultMessage: 'Addon installed succesfuly', }, addonNotInstalled: { id: 'Addon could not be installed', defaultMessage: 'Addon could not be installed', }, addonUninstalled: { id: 'Addon uninstalled succesfuly', defaultMessage: 'Addon uninstalled succesfuly', }, addonNotUninstalled: { id: 'Addon could not be uninstalled', defaultMessage: 'Addon could not be uninstalled', }, }); const AddonsControlpanel = (props) => { const { title } = props; const intl = useIntl(); const dispatch = useDispatch(); const pathname = props.location.pathname; const [activeIndex, setactiveIndex] = useState(-1); const isClient = useClient(); const installedAddons = useSelector( (state) => state.addons.installedAddons, shallowEqual, ); const availableAddons = useSelector( (state) => state.addons.availableAddons, shallowEqual, ); const upgradableAddons = useSelector( (state) => state.addons.upgradableAddons, shallowEqual, ); const loadingAddons = useSelector((state) => state.addons.loading); useEffect(() => { dispatch(listAddons()); }, [dispatch]); const onInstall = useCallback( (event, { value }) => { event.preventDefault(); dispatch(installAddon(value)) .then(() => { toast.success( <Toast success title={intl.formatMessage(messages.success)} content={intl.formatMessage(messages.addonInstalled, { title: title, })} />, ); }) .catch(() => { toast.error( <Toast error title={intl.formatMessage(messages.error)} content={intl.formatMessage(messages.addonNotInstalled)} />, ); }) .finally(() => dispatch(listAddons())); }, [dispatch, title, intl], ); const onUninstall = useCallback( (event, { value }) => { event.preventDefault(); dispatch(uninstallAddon(value)) .then(() => { toast.success( <Toast success title={intl.formatMessage(messages.success)} content={intl.formatMessage(messages.addonUninstalled)} />, ); }) .catch(() => { toast.error( <Toast error title={intl.formatMessage(messages.error)} content={intl.formatMessage(messages.addonNotUninstalled, { title: title, })} />, ); }) .finally(() => dispatch(listAddons())); }, [dispatch, intl, title], ); const onUpgrade = useCallback( (event, { value }) => { event.preventDefault(); dispatch(upgradeAddon(value)) .then(() => { toast.success( <Toast success title={intl.formatMessage(messages.success)} content={intl.formatMessage(messages.addonUpgraded)} />, ); }) .catch(() => { toast.error( <Toast error title={intl.formatMessage(messages.error)} content={intl.formatMessage(messages.addonNotUpgraded)} />, ); }) .finally(() => dispatch(listAddons())); }, [dispatch, intl], ); const onAccordionClick = (event, item) => { const newIndex = activeIndex === item.index ? -1 : item.index; setactiveIndex(newIndex); }; return ( <Container id="page-addons" className="controlpanel-addons"> <Helmet title={intl.formatMessage(messages.addOns)} /> <Segment.Group raised> <Segment className="primary"> <FormattedMessage id="Add-ons Settings" defaultMessage="Add-ons Settings" /> </Segment> {loadingAddons ? ( <Dimmer active> <Loader /> </Dimmer> ) : ( <> {upgradableAddons.length > 0 && ( <Message attached> <Message.Header> <FormattedMessage id="Updates available" defaultMessage="Updates available" /> </Message.Header> <FormattedMessage id="Update installed addons" defaultMessage="Update installed addons" /> </Message> )} <Segment> <Header as="h3"> <FormattedMessage id="Activate and deactivate" defaultMessage="Activate and deactivate add-ons in the lists below." /> </Header> <FormattedMessage id="Add Addons" defaultMessage="To make new add-ons show up here, add them to your configuration, build, and restart the server process. For detailed instructions see" /> &nbsp; <a href="https://6.docs.plone.org/install/" target="_blank" rel="noopener noreferrer" > {intl.formatMessage(messages.installingAnAddon)} </a> . </Segment> <Segment key="header-available" secondary> <FormattedMessage id="Available" defaultMessage="Available" />: <Label circular>{availableAddons.length}</Label> </Segment> <Segment key="body-available" attached> <Accordion> <Divider /> {availableAddons.map((item) => ( <div key={item.id}> <Accordion.Title active={activeIndex === item.id} index={item.id} onClick={onAccordionClick} > {item.title} <Icon name={ activeIndex === item.id ? circleTopSVG : circleBottomSVG } size="23px" className={`accordionToggle ${item.title}`} /> </Accordion.Title> <Accordion.Content active={activeIndex === item.id}> <div className="description">{item.description}</div> {item.uninstall_profile_id === '' && ( <div> <Message icon="warning" warning> <FormattedMessage id="No uninstall profile" defaultMessage="This addon does not provide an uninstall profile." /> </Message> </div> )} <Button.Group floated="right"> <Button primary onClick={onInstall} value={item.id} className="installAction" > <FormattedMessage id="Install" defaultMessage="Install" className="button-label" /> </Button> </Button.Group> <div className="version"> <FormattedMessage id="Latest version" defaultMessage="Latest version" /> : &nbsp; {item.version} </div> </Accordion.Content> <Divider /> </div> ))} </Accordion> </Segment> <Segment key="header-installed" secondary> <FormattedMessage id="Installed" defaultMessage="Installed" />: <Label circular>{installedAddons.length}</Label> </Segment> <Segment key="body-installed" attached> <Accordion> <Divider /> {installedAddons.map((item) => ( <div key={item.id}> <Accordion.Title active={activeIndex === item.id} index={item.id} onClick={onAccordionClick} className={ item.upgrade_info.available ? 'updateAvailable' : '' } > {item.title} {item.upgrade_info.available && ( <Label> <FormattedMessage id="Update" defaultMessage="Update" /> </Label> )} <Icon name={ activeIndex === item.id ? circleTopSVG : circleBottomSVG } size="24px" className={`accordionToggle ${item.title}`} color="#878f93" /> </Accordion.Title> <Accordion.Content active={activeIndex === item.id}> <div className="description">{item.description}</div> <Button.Group floated="right"> {item.upgrade_info.available && ( <Button primary onClick={onUpgrade} value={item.id}> <FormattedMessage id="upgradeVersions" defaultMessage="Update from version {origin} to {destination}" values={{ origin: item.upgrade_info.installedVersion, destination: item.upgrade_info.newVersion, }} /> </Button> )} {item.uninstall_profile_id && ( <Button negative onClick={onUninstall} value={item.id} className="uninstallAction" > <FormattedMessage id="Uninstall" defaultMessage="Uninstall" className="button-label" /> </Button> )} </Button.Group> <div className="version"> <FormattedMessage id="Installed version" defaultMessage="Installed version" /> : &nbsp; {item.version} </div> </Accordion.Content> <Divider /> </div> ))} </Accordion> </Segment> </> )} </Segment.Group> {isClient && createPortal( <Toolbar pathname={pathname} hideDefaultViewButtons inner={ <> <Link to="/controlpanel" className="item"> <Icon name={backSVG} aria-label={intl.formatMessage(messages.back)} className="contents circled" size="30px" title={intl.formatMessage(messages.back)} /> </Link> </> } />, document.getElementById('toolbar'), )} </Container> ); }; export default AddonsControlpanel;