UNPKG

@plone/volto

Version:
1,604 lines (1,543 loc) 65.9 kB
/** * Contents component. * @module components/manage/Contents/Contents */ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { compose } from 'redux'; import { createPortal } from 'react-dom'; import { Link } from 'react-router-dom'; import { Button, Container as SemanticContainer, Divider, Dropdown, Menu, Input, Segment, Table, Loader, Dimmer, } from 'semantic-ui-react'; import concat from 'lodash/concat'; import filter from 'lodash/filter'; import find from 'lodash/find'; import indexOf from 'lodash/indexOf'; import keys from 'lodash/keys'; import map from 'lodash/map'; import mapValues from 'lodash/mapValues'; import pull from 'lodash/pull'; import move from 'lodash-move'; import { FormattedMessage, defineMessages, injectIntl } from 'react-intl'; import { asyncConnect } from '@plone/volto/helpers/AsyncConnect'; import { getBaseUrl } from '@plone/volto/helpers/Url/Url'; import { searchContent } from '@plone/volto/actions/search/search'; import { deleteContent, orderContent, sortContent, updateColumnsContent, getContent, } from '@plone/volto/actions/content/content'; import { copyContent, moveContent, cut, copy, } from '@plone/volto/actions/clipboard/clipboard'; import { listActions } from '@plone/volto/actions/actions/actions'; import Indexes, { defaultIndexes } from '@plone/volto/constants/Indexes'; import Pagination from '@plone/volto/components/theme/Pagination/Pagination'; import Popup from '@plone/volto/components/theme/Popup/Popup'; import Toolbar from '@plone/volto/components/manage/Toolbar/Toolbar'; import Toast from '@plone/volto/components/manage/Toast/Toast'; import Icon from '@plone/volto/components/theme/Icon/Icon'; import Unauthorized from '@plone/volto/components/theme/Unauthorized/Unauthorized'; import ContentsBreadcrumbs from '@plone/volto/components/manage/Contents/ContentsBreadcrumbs'; import ContentsIndexHeader from '@plone/volto/components/manage/Contents/ContentsIndexHeader'; import ContentsItem from '@plone/volto/components/manage/Contents/ContentsItem'; import { ContentsRenameModal } from '@plone/volto/components/manage/Contents'; import ContentsUploadModal from '@plone/volto/components/manage/Contents/ContentsUploadModal'; import ContentsDeleteModal from '@plone/volto/components/manage/Contents/ContentsDeleteModal'; import ContentsWorkflowModal from '@plone/volto/components/manage/Contents/ContentsWorkflowModal'; import ContentsTagsModal from '@plone/volto/components/manage/Contents/ContentsTagsModal'; import ContentsPropertiesModal from '@plone/volto/components/manage/Contents/ContentsPropertiesModal'; import Helmet from '@plone/volto/helpers/Helmet/Helmet'; import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable'; import config from '@plone/volto/registry'; import backSVG from '@plone/volto/icons/back.svg'; import cutSVG from '@plone/volto/icons/cut.svg'; import deleteSVG from '@plone/volto/icons/delete.svg'; import copySVG from '@plone/volto/icons/copy.svg'; import tagSVG from '@plone/volto/icons/tag.svg'; import renameSVG from '@plone/volto/icons/rename.svg'; import semaphoreSVG from '@plone/volto/icons/semaphore.svg'; import uploadSVG from '@plone/volto/icons/upload.svg'; import propertiesSVG from '@plone/volto/icons/properties.svg'; import pasteSVG from '@plone/volto/icons/paste.svg'; import zoomSVG from '@plone/volto/icons/zoom.svg'; import checkboxUncheckedSVG from '@plone/volto/icons/checkbox-unchecked.svg'; import checkboxCheckedSVG from '@plone/volto/icons/checkbox-checked.svg'; import checkboxIndeterminateSVG from '@plone/volto/icons/checkbox-indeterminate.svg'; import configurationSVG from '@plone/volto/icons/configuration-app.svg'; import sortDownSVG from '@plone/volto/icons/sort-down.svg'; import sortUpSVG from '@plone/volto/icons/sort-up.svg'; import downKeySVG from '@plone/volto/icons/down-key.svg'; import moreSVG from '@plone/volto/icons/more.svg'; import clearSVG from '@plone/volto/icons/clear.svg'; const messages = defineMessages({ back: { id: 'Back', defaultMessage: 'Back', }, contents: { id: 'Contents', defaultMessage: 'Contents', }, copy: { id: 'Copy', defaultMessage: 'Copy', }, cut: { id: 'Cut', defaultMessage: 'Cut', }, error: { id: "You can't paste this content here", defaultMessage: "You can't paste this content here", }, delete: { id: 'Delete', defaultMessage: 'Delete', }, deleteError: { id: 'The item could not be deleted.', defaultMessage: 'The item could not be deleted.', }, loading: { id: 'loading', defaultMessage: 'Loading', }, home: { id: 'Home', defaultMessage: 'Home', }, filter: { id: 'Filter…', defaultMessage: 'Filter…', }, messageCopied: { id: 'Item(s) copied.', defaultMessage: 'Item(s) copied.', }, messageCut: { id: 'Item(s) cut.', defaultMessage: 'Item(s) cut.', }, messageUpdate: { id: 'Item(s) has been updated.', defaultMessage: 'Item(s) has been updated.', }, messageReorder: { id: 'Item successfully moved.', defaultMessage: 'Item successfully moved.', }, messagePasted: { id: 'Item(s) pasted.', defaultMessage: 'Item(s) pasted.', }, messageWorkflowUpdate: { id: 'Item(s) state has been updated.', defaultMessage: 'Item(s) state has been updated.', }, paste: { id: 'Paste', defaultMessage: 'Paste', }, properties: { id: 'Properties', defaultMessage: 'Properties', }, rearrangeBy: { id: 'Rearrange items by…', defaultMessage: 'Rearrange items by…', }, rename: { id: 'Rename', defaultMessage: 'Rename', }, select: { id: 'Select…', defaultMessage: 'Select…', }, selected: { id: '{count} selected', defaultMessage: '{count} selected', }, selectColumns: { id: 'Select columns to show', defaultMessage: 'Select columns to show', }, sort: { id: 'sort', defaultMessage: 'sort', }, state: { id: 'State', defaultMessage: 'State', }, tags: { id: 'Tags', defaultMessage: 'Tags', }, upload: { id: 'Upload', defaultMessage: 'Upload', }, success: { id: 'Success', defaultMessage: 'Success', }, publicationDate: { id: 'Publication date', defaultMessage: 'Publication date', }, createdOn: { id: 'Created on', defaultMessage: 'Created on', }, expirationDate: { id: 'Expiration date', defaultMessage: 'Expiration date', }, id: { id: 'ID', defaultMessage: 'ID', }, uid: { id: 'UID', defaultMessage: 'UID', }, reviewState: { id: 'Review state', defaultMessage: 'Review state', }, folder: { id: 'Folder', defaultMessage: 'Folder', }, excludedFromNavigation: { id: 'Excluded from navigation', defaultMessage: 'Excluded from navigation', }, objectSize: { id: 'Object Size', defaultMessage: 'Object Size', }, lastCommentedDate: { id: 'Last comment date', defaultMessage: 'Last comment date', }, totalComments: { id: 'Total comments', defaultMessage: 'Total comments', }, creator: { id: 'Creator', defaultMessage: 'Creator', }, endDate: { id: 'End Date', defaultMessage: 'End Date', }, startDate: { id: 'Start Date', defaultMessage: 'Start Date', }, all: { id: 'All', defaultMessage: 'All', }, }); /** * Contents class. * @class Contents * @extends Component */ class Contents extends Component { /** * Property types. * @property {Object} propTypes Property types. * @static */ static propTypes = { action: PropTypes.string, source: PropTypes.arrayOf(PropTypes.string), searchContent: PropTypes.func.isRequired, cut: PropTypes.func.isRequired, copy: PropTypes.func.isRequired, copyContent: PropTypes.func.isRequired, deleteContent: PropTypes.func.isRequired, moveContent: PropTypes.func.isRequired, orderContent: PropTypes.func.isRequired, sortContent: PropTypes.func.isRequired, updateColumnsContent: PropTypes.func.isRequired, clipboardRequest: PropTypes.shape({ loading: PropTypes.bool, loaded: PropTypes.bool, }).isRequired, deleteRequest: PropTypes.shape({ loading: PropTypes.bool, loaded: PropTypes.bool, }).isRequired, updateRequest: PropTypes.shape({ loading: PropTypes.bool, loaded: PropTypes.bool, }).isRequired, searchRequest: PropTypes.shape({ loading: PropTypes.bool, loaded: PropTypes.bool, }).isRequired, items: PropTypes.arrayOf( PropTypes.shape({ '@id': PropTypes.string, '@type': PropTypes.string, title: PropTypes.string, description: PropTypes.string, }), ), breadcrumbs: PropTypes.arrayOf( PropTypes.shape({ title: PropTypes.string, url: PropTypes.string, }), ).isRequired, total: PropTypes.number.isRequired, pathname: PropTypes.string.isRequired, }; /** * Default properties. * @property {Object} defaultProps Default properties. * @static */ static defaultProps = { items: [], action: null, source: null, index: { order: keys(Indexes), values: mapValues(Indexes, (value, key) => ({ ...value, selected: indexOf(defaultIndexes, key) !== -1, })), selectedCount: defaultIndexes.length + 1, }, }; /** * Constructor * @method constructor * @param {Object} props Component properties * @constructs ContentsComponent */ constructor(props) { super(props); this.onDeselect = this.onDeselect.bind(this); this.onSelect = this.onSelect.bind(this); this.onSelectAll = this.onSelectAll.bind(this); this.onSelectIndex = this.onSelectIndex.bind(this); this.onSelectNone = this.onSelectNone.bind(this); this.onDeleteOk = this.onDeleteOk.bind(this); this.onDeleteCancel = this.onDeleteCancel.bind(this); this.onUploadOk = this.onUploadOk.bind(this); this.onUploadCancel = this.onUploadCancel.bind(this); this.onRenameOk = this.onRenameOk.bind(this); this.onRenameCancel = this.onRenameCancel.bind(this); this.onTagsOk = this.onTagsOk.bind(this); this.onTagsCancel = this.onTagsCancel.bind(this); this.onPropertiesOk = this.onPropertiesOk.bind(this); this.onPropertiesCancel = this.onPropertiesCancel.bind(this); this.onWorkflowOk = this.onWorkflowOk.bind(this); this.onWorkflowCancel = this.onWorkflowCancel.bind(this); this.onChangeFilter = this.onChangeFilter.bind(this); this.onChangePage = this.onChangePage.bind(this); this.onChangePageSize = this.onChangePageSize.bind(this); this.onOrderIndex = this.onOrderIndex.bind(this); this.onOrderItem = this.onOrderItem.bind(this); this.onSortItems = this.onSortItems.bind(this); this.onMoveToTop = this.onMoveToTop.bind(this); this.onChangeSelected = this.onChangeSelected.bind(this); this.onMoveToBottom = this.onMoveToBottom.bind(this); this.cut = this.cut.bind(this); this.copy = this.copy.bind(this); this.delete = this.delete.bind(this); this.upload = this.upload.bind(this); this.rename = this.rename.bind(this); this.tags = this.tags.bind(this); this.properties = this.properties.bind(this); this.workflow = this.workflow.bind(this); this.paste = this.paste.bind(this); this.fetchContents = this.fetchContents.bind(this); this.orderTimeout = null; this.state = { selected: [], showDelete: false, showUpload: false, showRename: false, showTags: false, showProperties: false, showWorkflow: false, itemsToDelete: [], items: this.props.items, filter: '', currentPage: 0, pageSize: 50, index: this.props.index || { order: keys(Indexes), values: mapValues(Indexes, (value, key) => ({ ...value, selected: indexOf(defaultIndexes, key) !== -1, })), selectedCount: defaultIndexes.length + 1, }, sort_on: this.props.sort?.on || 'getObjPositionInParent', sort_order: this.props.sort?.order || 'ascending', isClient: false, }; this.filterTimeout = null; } /** * Component did mount * @method componentDidMount * @returns {undefined} */ componentDidMount() { this.fetchContents(); this.setState({ isClient: true }); } /** * Component will receive props * @method componentWillReceiveProps * @param {Object} nextProps Next properties * @returns {undefined} */ UNSAFE_componentWillReceiveProps(nextProps) { if ( (this.props.clipboardRequest.loading && nextProps.clipboardRequest.loaded) || (this.props.deleteRequest.loading && nextProps.deleteRequest.loaded) || (this.props.updateRequest.loading && nextProps.updateRequest.loaded) ) { this.fetchContents(nextProps.pathname); } if (this.props.updateRequest.loading && nextProps.updateRequest.loaded) { this.props.toastify.toast.success( <Toast success title={this.props.intl.formatMessage(messages.success)} content={this.props.intl.formatMessage(messages.messageUpdate)} />, ); } if (this.props.pathname !== nextProps.pathname) { // Refetching content to sync the current object in the toolbar this.props.getContent(getBaseUrl(nextProps.pathname)); this.setState( { currentPage: 0, selected: [], }, () => this.setState({ filter: '' }, () => this.fetchContents(nextProps.pathname), ), ); } if (this.props.searchRequest.loading && nextProps.searchRequest.loaded) { this.setState({ items: nextProps.items, }); } if ( this.props.clipboardRequest.loading && nextProps.clipboardRequest.error ) { const msgBody = nextProps.clipboardRequest.error?.response?.body?.message || this.props.intl.formatMessage(messages.error); this.props.toastify.toast.error( <Toast error title={this.props.intl.formatMessage(messages.error)} content={msgBody} />, ); } if ( this.props.clipboardRequest.loading && nextProps.clipboardRequest.loaded ) { this.props.toastify.toast.success( <Toast success title={this.props.intl.formatMessage(messages.success)} content={this.props.intl.formatMessage(messages.messagePasted)} />, ); } if (this.props.deleteRequest.loading && nextProps.deleteRequest.error) { this.props.toastify.toast.error( <Toast error title={this.props.intl.formatMessage(messages.deleteError)} content={this.props.intl.formatMessage(messages.deleteError)} />, ); } if (this.props.orderRequest.loading && nextProps.orderRequest.loaded) { this.props.toastify.toast.success( <Toast success title={this.props.intl.formatMessage(messages.success)} content={this.props.intl.formatMessage(messages.messageReorder)} />, ); } } /** * On deselect handler * @method onDeselect * @param {object} event Event object * @param {string} value Value * @returns {undefined} */ onDeselect(event, { value }) { this.setState({ selected: pull(this.state.selected, value), }); } /** * On select handler * @method onSelect * @param {object} event Event object * @returns {undefined} */ onSelect(event, id) { if (indexOf(this.state.selected, id) === -1) { this.setState({ selected: concat(this.state.selected, id), }); } else { this.setState({ selected: pull(this.state.selected, id), }); } } /** * On select all handler * @method onSelectAll * @returns {undefined} */ onSelectAll() { this.setState({ selected: map(this.state.items, (item) => item['@id']), }); } /** * On select none handler * @method onSelectNone * @returns {undefined} */ onSelectNone() { this.setState({ selected: [], }); } /** * On select index * @method onSelectIndex * @param {object} event Event object. * @param {string} value Index value. * @returns {undefined} */ onSelectIndex(event, { value }) { let newIndex = { ...this.state.index, selectedCount: this.state.index.selectedCount + (this.state.index.values[value].selected ? -1 : 1), values: mapValues(this.state.index.values, (indexValue, indexKey) => ({ ...indexValue, selected: indexKey === value ? !indexValue.selected : indexValue.selected, })), }; this.setState({ index: newIndex, }); this.props.updateColumnsContent(getBaseUrl(this.props.pathname), newIndex); } /** * On change filter * @method onChangeFilter * @param {object} event Event object. * @param {string} value Filter value. * @returns {undefined} */ onChangeFilter(event, { value }) { const self = this; clearTimeout(self.filterTimeout); this.setState( { filter: value, }, () => { self.filterTimeout = setTimeout(() => { self.fetchContents(); }, 200); }, ); } /** * On change selected values (Filter) * @method onChangeSelected * @param {object} event Event object. * @param {string} value Filter value. * @returns {undefined} */ onChangeSelected(event, { value }) { event.stopPropagation(); const { items, selected } = this.state; const filteredItems = filter(selected, (selectedItem) => find(items, (item) => item['@id'] === selectedItem) .title.toLowerCase() .includes(value.toLowerCase()), ); this.setState({ filteredItems, selectedMenuFilter: value, }); } /** * On change page * @method onChangePage * @param {object} event Event object. * @param {string} value Page value. * @returns {undefined} */ onChangePage(event, { value }) { this.setState( { currentPage: value, selected: [], }, () => this.fetchContents(), ); } /** * On change page size * @method onChangePageSize * @param {object} event Event object. * @param {string} value Page size value. * @returns {undefined} */ onChangePageSize(event, { value }) { this.setState( { pageSize: value, currentPage: 0, selected: [], }, () => this.fetchContents(), ); } /** * On order index * @method onOrderIndex * @param {number} index Index * @param {number} delta Delta * @returns {undefined} */ onOrderIndex(index, delta) { this.setState({ index: { ...this.state.index, order: move(this.state.index.order, index, index + delta), }, }); this.props.updateColumnsContent( getBaseUrl(this.props.pathname), this.state.index, ); } /** * On order item * @method onOrderItem * @param {string} id Item id * @param {number} itemIndex Item index * @param {number} delta Delta * @returns {undefined} */ onOrderItem(id, itemIndex, delta, backend) { if (backend) { this.props.orderContent( getBaseUrl(this.props.pathname), id.replace(/^.*\//, ''), delta, ); } else { this.setState({ items: move(this.state.items, itemIndex, itemIndex + delta), }); } } /** * On sort items * @method onSortItems * @param {object} event Event object * @param {string} value Item index * @returns {undefined} */ onSortItems(event, { value }) { const values = value.split('|'); this.setState({ sort_on: values[0], sort_order: values[1], selected: [], }); this.props.sortContent( getBaseUrl(this.props.pathname), values[0], values[1], ); } /** * On move to top * @method onMoveToTop * @param {object} event Event object * @param {string} value Item index * @returns {undefined} */ onMoveToTop(event, { value }) { const id = this.state.items[value]['@id']; this.props .orderContent( getBaseUrl(this.props.pathname), id.replace(/^.*\//, ''), 'top', ) .then(() => { this.setState( { currentPage: 0, selected: [], }, () => this.fetchContents(), ); }); } /** * On move to bottom * @method onMoveToBottom * @param {object} event Event object * @param {string} value Item index * @returns {undefined} */ onMoveToBottom(event, { value }) { const id = this.state.items[value]['@id']; this.props .orderContent( getBaseUrl(this.props.pathname), id.replace(/^.*\//, ''), 'bottom', ) .then(() => { this.setState( { currentPage: 0, selected: [], }, () => this.fetchContents(), ); }); } /** * On delete ok * @method onDeleteOk * @returns {undefined} */ onDeleteOk() { this.props.deleteContent(this.state.itemsToDelete); this.setState({ showDelete: false, itemsToDelete: [], selected: [], }); } /** * On delete cancel * @method onDeleteCancel * @returns {undefined} */ onDeleteCancel() { this.setState({ showDelete: false, itemsToDelete: [], }); } /** * On upload ok * @method onUploadOk * @returns {undefined} */ onUploadOk() { this.fetchContents(); this.setState({ showUpload: false, }); } /** * On upload cancel * @method onUploadCancel * @returns {undefined} */ onUploadCancel() { this.setState({ showUpload: false, }); } /** * On rename ok * @method onRenameOk * @returns {undefined} */ onRenameOk() { this.setState({ showRename: false, selected: [], }); } /** * On rename cancel * @method onRenameCancel * @returns {undefined} */ onRenameCancel() { this.setState({ showRename: false, }); } /** * On tags ok * @method onTagsOk * @returns {undefined} */ onTagsOk() { this.setState({ showTags: false, selected: [], }); } /** * On tags cancel * @method onTagsCancel * @returns {undefined} */ onTagsCancel() { this.setState({ showTags: false, }); } /** * On properties ok * @method onPropertiesOk * @returns {undefined} */ onPropertiesOk() { this.setState({ showProperties: false, selected: [], }); } /** * On properties cancel * @method onPropertiesCancel * @returns {undefined} */ onPropertiesCancel() { this.setState({ showProperties: false, }); } /** * On workflow ok * @method onWorkflowOk * @returns {undefined} */ onWorkflowOk() { this.fetchContents(); this.setState({ showWorkflow: false, selected: [], }); this.props.toastify.toast.success( <Toast success title={this.props.intl.formatMessage(messages.success)} content={this.props.intl.formatMessage(messages.messageWorkflowUpdate)} />, ); } /** * On workflow cancel * @method onWorkflowCancel * @returns {undefined} */ onWorkflowCancel() { this.setState({ showWorkflow: false, }); } /** * Get field by id * @method getFieldById * @param {string} id Id of object * @param {string} field Field of object * @returns {string} Field of object */ getFieldById(id, field) { const item = find(this.state.items, { '@id': id }); return item ? item[field] : ''; } /** * Fetch contents handler * @method fetchContents * @param {string} pathname Pathname to fetch contents. * @returns {undefined} */ fetchContents(pathname) { if (this.state.pageSize === this.props.intl.formatMessage(messages.all)) { //'All' this.props.searchContent(getBaseUrl(pathname || this.props.pathname), { 'path.depth': 1, sort_on: this.state.sort_on, sort_order: this.state.sort_order, metadata_fields: '_all', b_size: 100000000, show_inactive: true, ...(this.state.filter && { SearchableText: `${this.state.filter}*` }), }); } else { this.props.searchContent(getBaseUrl(pathname || this.props.pathname), { 'path.depth': 1, sort_on: this.state.sort_on, sort_order: this.state.sort_order, metadata_fields: '_all', ...(this.state.filter && { SearchableText: `${this.state.filter}*` }), b_size: this.state.pageSize, b_start: this.state.currentPage * this.state.pageSize, show_inactive: true, }); } } /** * Cut handler * @method cut * @param {Object} event Event object. * @param {string} value Value of the event. * @returns {undefined} */ cut(event, { value }) { this.props.cut(value ? [value] : this.state.selected); this.onSelectNone(); this.props.toastify.toast.success( <Toast success title={this.props.intl.formatMessage(messages.success)} content={this.props.intl.formatMessage(messages.messageCut)} />, ); } /** * Copy handler * @method copy * @param {Object} event Event object. * @param {string} value Value of the event. * @returns {undefined} */ copy(event, { value }) { this.props.copy(value ? [value] : this.state.selected); this.onSelectNone(); this.props.toastify.toast.success( <Toast success title={this.props.intl.formatMessage(messages.success)} content={this.props.intl.formatMessage(messages.messageCopied)} />, ); } /** * Delete handler * @method delete * @param {Object} event Event object. * @param {string} value Value of the event. * @returns {undefined} */ delete(event, { value }) { this.setState({ showDelete: true, itemsToDelete: value ? [value] : this.state.selected, }); } /** * Upload handler * @method upload * @returns {undefined} */ upload() { this.setState({ showUpload: true, }); } /** * Rename handler * @method rename * @returns {undefined} */ rename() { this.setState({ showRename: true, }); } /** * Tags handler * @method tags * @returns {undefined} */ tags() { this.setState({ showTags: true, }); } /** * Properties handler * @method properties * @returns {undefined} */ properties() { this.setState({ showProperties: true, }); } /** * Workflow handler * @method workflow * @returns {undefined} */ workflow() { this.setState({ showWorkflow: true, }); } /** * Paste handler * @method paste * @returns {undefined} */ paste() { if (this.props.action === 'copy') { this.props.copyContent( this.props.source, getBaseUrl(this.props.pathname), ); } if (this.props.action === 'cut') { this.props.moveContent( this.props.source, getBaseUrl(this.props.pathname), ); } } /** * Render method. * @method render * @returns {string} Markup for the component. */ render() { const selected = this.state.selected.length > 0; const filteredItems = this.state.filteredItems || this.state.selected; const path = getBaseUrl(this.props.pathname); const folderContentsAction = find(this.props.objectActions, { id: 'folderContents', }); const loading = (this.props.clipboardRequest?.loading && !this.props.clipboardRequest?.error) || (this.props.deleteRequest?.loading && !this.props.deleteRequest?.error) || (this.props.updateRequest?.loading && !this.props.updateRequest?.error) || (this.props.orderRequest?.loading && !this.props.orderRequest?.error) || (this.props.searchRequest?.loading && !this.props.searchRequest?.error); const Container = config.getComponent({ name: 'Container' }).component || SemanticContainer; return this.props.token && this.props.objectActions?.length > 0 ? ( <> {folderContentsAction ? ( <Container id="page-contents" className="folder-contents" aria-live="polite" > <Dimmer.Dimmable as="div" blurring dimmed={loading}> <Dimmer active={loading} inverted> <Loader indeterminate size="massive"> {this.props.intl.formatMessage(messages.loading)} </Loader> </Dimmer> <Helmet title={this.props.intl.formatMessage(messages.contents)} /> <div className="container"> <article id="content"> <ContentsDeleteModal open={this.state.showDelete} onCancel={this.onDeleteCancel} onOk={this.onDeleteOk} items={this.state.items} itemsToDelete={this.state.itemsToDelete} /> <ContentsUploadModal open={this.state.showUpload} onCancel={this.onUploadCancel} onOk={this.onUploadOk} pathname={getBaseUrl(this.props.pathname)} /> <ContentsRenameModal open={this.state.showRename} onCancel={this.onRenameCancel} onOk={this.onRenameOk} items={map(this.state.selected, (item) => ({ url: item, title: this.getFieldById(item, 'title'), id: this.getFieldById(item, 'id'), }))} /> <ContentsTagsModal open={this.state.showTags} onCancel={this.onTagsCancel} onOk={this.onTagsOk} items={map(this.state.selected, (item) => ({ url: item, subjects: this.getFieldById(item, 'Subject'), }))} /> <ContentsPropertiesModal open={this.state.showProperties} onCancel={this.onPropertiesCancel} onOk={this.onPropertiesOk} items={this.state.selected} values={map(this.state.selected, (id) => find(this.state.items, { '@id': id }), ).filter((item) => item)} /> {this.state.showWorkflow && ( <ContentsWorkflowModal open={this.state.showWorkflow} onCancel={this.onWorkflowCancel} onOk={this.onWorkflowOk} items={this.state.selected} /> )} <section id="content-core"> <Segment.Group raised> <Menu secondary attached className="top-menu"> <Menu.Menu className="top-menu-menu"> <Popup trigger={ <Menu.Item icon as={Button} onClick={this.upload} className="upload" aria-label={this.props.intl.formatMessage( messages.upload, )} > <Icon name={uploadSVG} color="#007eb1" size="24px" className="upload" /> </Menu.Item> } position="top center" content={this.props.intl.formatMessage( messages.upload, )} size="mini" /> </Menu.Menu> <Menu.Menu className="top-menu-menu"> <Popup trigger={ <Menu.Item icon as={Button} onClick={this.rename} disabled={!selected} aria-label={this.props.intl.formatMessage( messages.rename, )} > <Icon name={renameSVG} color={selected ? '#826a6a' : 'grey'} size="24px" className="rename" /> </Menu.Item> } position="top center" content={this.props.intl.formatMessage( messages.rename, )} size="mini" /> <Popup trigger={ <Menu.Item icon as={Button} onClick={this.workflow} disabled={!selected} aria-label={this.props.intl.formatMessage( messages.state, )} > <Icon name={semaphoreSVG} color={selected ? '#826a6a' : 'grey'} size="24px" className="semaphore" /> </Menu.Item> } position="top center" content={this.props.intl.formatMessage( messages.state, )} size="mini" /> <Popup trigger={ <Menu.Item icon as={Button} onClick={this.tags} disabled={!selected} aria-label={this.props.intl.formatMessage( messages.tags, )} > <Icon name={tagSVG} color={selected ? '#826a6a' : 'grey'} size="24px" className="tag" /> </Menu.Item> } position="top center" content={this.props.intl.formatMessage( messages.tags, )} size="mini" /> <Popup trigger={ <Menu.Item icon as={Button} onClick={this.properties} disabled={!selected} aria-label={this.props.intl.formatMessage( messages.properties, )} > <Icon name={propertiesSVG} color={selected ? '#826a6a' : 'grey'} size="24px" className="properties" /> </Menu.Item> } position="top center" content={this.props.intl.formatMessage( messages.properties, )} size="mini" /> </Menu.Menu> <Menu.Menu className="top-menu-menu"> <Popup trigger={ <Menu.Item icon as={Button} onClick={this.cut} disabled={!selected} aria-label={this.props.intl.formatMessage( messages.cut, )} > <Icon name={cutSVG} color={selected ? '#826a6a' : 'grey'} size="24px" className="cut" /> </Menu.Item> } position="top center" content={this.props.intl.formatMessage( messages.cut, )} size="mini" /> <Popup trigger={ <Menu.Item icon as={Button} onClick={this.copy} disabled={!selected} aria-label={this.props.intl.formatMessage( messages.copy, )} > <Icon name={copySVG} color={selected ? '#826a6a' : 'grey'} size="24px" className="copy" /> </Menu.Item> } position="top center" content={this.props.intl.formatMessage( messages.copy, )} size="mini" /> <Popup trigger={ <Menu.Item icon as={Button} onClick={this.paste} disabled={!this.props.action} aria-label={this.props.intl.formatMessage( messages.paste, )} > <Icon name={pasteSVG} color={selected ? '#826a6a' : 'grey'} size="24px" className="paste" /> </Menu.Item> } position="top center" content={this.props.intl.formatMessage( messages.paste, )} size="mini" /> <Popup trigger={ <Menu.Item icon as={Button} onClick={this.delete} disabled={!selected} aria-label={this.props.intl.formatMessage( messages.delete, )} > <Icon name={deleteSVG} color={selected ? '#e40166' : 'grey'} size="24px" className="delete" /> </Menu.Item> } position="top center" content={this.props.intl.formatMessage( messages.delete, )} size="mini" /> </Menu.Menu> <Menu.Menu position="right" className="top-menu-searchbox" > <div className="ui right aligned category search item"> <Input type="text" transparent placeholder={this.props.intl.formatMessage( messages.filter, )} size="small" value={this.state.filter} onChange={this.onChangeFilter} /> {this.state.filter && ( <Button className="icon icon-container" onClick={() => { this.onChangeFilter('', { value: '' }); }} > <Icon name={clearSVG} size="30px" color="#e40166" /> </Button> )} <Icon name={zoomSVG} size="30px" color="#007eb1" className="zoom" style={{ flexShrink: '0' }} /> <div className="results" /> </div> </Menu.Menu> </Menu> <Segment secondary attached className="contents-breadcrumbs" > <ContentsBreadcrumbs items={this.props.breadcrumbs} /> <Dropdown item upward={false} icon={ <Icon name={moreSVG} size="24px" color="#826a6a" /> } className="right floating selectIndex" > <Dropdown.Menu className="left"> <Dropdown.Header content={this.props.intl.formatMessage( messages.selectColumns, )} /> <Dropdown.Menu scrolling> {map( filter( this.state.index.order, (index) => index !== 'sortable_title', ), (index) => ( <Dropdown.Item key={index} value={index} onClick={this.onSelectIndex} className="iconAlign" > {this.state.index.values[index].selected ? ( <Icon name={checkboxCheckedSVG} size="24px" color="#007eb1" className={ this.state.index.values[index].label } /> ) : ( <Icon name={checkboxUncheckedSVG} className={ this.state.index.values[index].label } size="24px" /> )} <span> {' '} {this.props.intl.formatMessage({ id: this.state.index.values[index] .label, defaultMessage: this.state.index.values[index].label, })} </span> </Dropdown.Item> ), )} </Dropdown.Menu> </Dropdown.Menu> </Dropdown> </Segment> <div className="contents-table-wrapper"> <Table selectable compact singleLine attached> <Table.Header> <Table.Row> <Table.HeaderCell> <Popup menu={true} position="bottom left" flowing={true} basic={true} on="click" popper={{ className: 'dropdown-popup', }} trigger={ <Icon name={configurationSVG} size="24px" color="#826a6a" className="dropdown-popup-trigger configuration-svg" /> } > <Menu vertical borderless fluid> <Menu.Header content={this.props.intl.formatMessage( messages.rearrangeBy, )} /> {map( [ 'id', 'sortable_title', 'EffectiveDate', 'CreationDate', 'ModificationDate', 'portal_type', ], (index) => ( <Dropdown key={index} item simple className={`sort_${index} icon-align`} icon={ <Icon name={downKeySVG} size="24px" className="left" /> } text={this.props.intl.formatMessage({ id: Indexes[index].label,