@plone/volto 17.0.0-alpha.7 → 17.0.0-alpha.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +82 -0
  3. package/locales/ca/LC_MESSAGES/volto.po +146 -0
  4. package/locales/ca.json +1 -1
  5. package/locales/de/LC_MESSAGES/volto.po +146 -0
  6. package/locales/de.json +1 -1
  7. package/locales/en/LC_MESSAGES/volto.po +146 -0
  8. package/locales/en.json +1 -1
  9. package/locales/es/LC_MESSAGES/volto.po +146 -0
  10. package/locales/es.json +1 -1
  11. package/locales/eu/LC_MESSAGES/volto.po +146 -0
  12. package/locales/eu.json +1 -1
  13. package/locales/fi/LC_MESSAGES/volto.po +4762 -0
  14. package/locales/fi.json +1 -1
  15. package/locales/fr/LC_MESSAGES/volto.po +146 -0
  16. package/locales/fr.json +1 -1
  17. package/locales/it/LC_MESSAGES/volto.po +146 -0
  18. package/locales/it.json +1 -1
  19. package/locales/ja/LC_MESSAGES/volto.po +146 -0
  20. package/locales/ja.json +1 -1
  21. package/locales/nl/LC_MESSAGES/volto.po +801 -643
  22. package/locales/nl.json +1 -1
  23. package/locales/pt/LC_MESSAGES/volto.po +146 -0
  24. package/locales/pt.json +1 -1
  25. package/locales/pt_BR/LC_MESSAGES/volto.po +146 -0
  26. package/locales/pt_BR.json +1 -1
  27. package/locales/ro/LC_MESSAGES/volto.po +146 -0
  28. package/locales/ro.json +1 -1
  29. package/locales/volto.pot +147 -1
  30. package/locales/zh_CN/LC_MESSAGES/volto.po +146 -0
  31. package/locales/zh_CN.json +1 -1
  32. package/package.json +1 -1
  33. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +1 -1
  34. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +1 -1
  35. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +1 -1
  36. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +1 -1
  37. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +1 -1
  38. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +1 -1
  39. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +1 -1
  40. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +1 -1
  41. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +1 -1
  42. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +1 -1
  43. package/packages/volto-slate/package.json +1 -1
  44. package/packages/volto-slate/src/blocks/Text/SlashMenu.jsx +4 -3
  45. package/packages/volto-slate/src/editor/deserialize.js +0 -1
  46. package/razzle.config.js +5 -0
  47. package/src/actions/index.js +6 -0
  48. package/src/actions/relations/rebuild.js +25 -0
  49. package/src/actions/relations/relations.js +86 -0
  50. package/src/actions/relations/relations.test.js +15 -0
  51. package/src/components/index.js +1 -0
  52. package/src/components/manage/BlockChooser/BlockChooser.jsx +8 -3
  53. package/src/components/manage/BlockChooser/BlockChooser.test.jsx +5 -0
  54. package/src/components/manage/Contents/Contents.jsx +5 -1
  55. package/src/components/manage/Contents/ContentsItem.jsx +6 -0
  56. package/src/components/manage/Controlpanels/Controlpanels.jsx +9 -0
  57. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +66 -0
  58. package/src/components/manage/Controlpanels/Relations/Relations.jsx +114 -0
  59. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +479 -0
  60. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +531 -0
  61. package/src/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel.jsx +3 -3
  62. package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +51 -82
  63. package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +79 -75
  64. package/src/components/manage/Toast/Toast.jsx +1 -1
  65. package/src/components/theme/NotFound/NotFound.jsx +55 -41
  66. package/src/components/theme/View/RenderBlocks.jsx +7 -1
  67. package/src/components/theme/Widgets/RelationsWidget.jsx +13 -11
  68. package/src/config/ControlPanels.js +2 -0
  69. package/src/constants/ActionTypes.js +4 -0
  70. package/src/constants/Languages.js +8 -4
  71. package/src/helpers/Api/Api.js +1 -1
  72. package/src/helpers/Html/Html.jsx +3 -1
  73. package/src/helpers/Html/Html.test.jsx +5 -0
  74. package/src/helpers/MessageLabels/MessageLabels.js +72 -0
  75. package/src/reducers/actions/actions.js +1 -1
  76. package/src/reducers/breadcrumbs/breadcrumbs.js +1 -1
  77. package/src/reducers/index.js +2 -0
  78. package/src/reducers/navigation/navigation.js +1 -1
  79. package/src/reducers/relations/relations.js +173 -0
  80. package/src/reducers/types/types.js +1 -1
  81. package/src/routes.js +5 -0
  82. package/theme/themes/pastanaga/extras/userscontrolpanel.less +99 -76
@@ -3,4 +3,4 @@
3
3
  "id": "An error has occurred while editing \"{name}\" field. We have been notified and we are looking into it. Please save your work and retry. If the issue persists please contact the site administrator.",
4
4
  "defaultMessage": "An error has occurred while editing \"{name}\" field. We have been notified and we are looking into it. Please save your work and retry. If the issue persists please contact the site administrator."
5
5
  }
6
- ]
6
+ ]
@@ -3,4 +3,4 @@
3
3
  "id": "An error has occurred while rendering \"{name}\" field. We have been notified and we are looking into it. If the issue persists please contact the site administrator.",
4
4
  "defaultMessage": "An error has occurred while rendering \"{name}\" field. We have been notified and we are looking into it. If the issue persists please contact the site administrator."
5
5
  }
6
- ]
6
+ ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plone/volto-slate",
3
- "version": "17.0.0-alpha.7",
3
+ "version": "17.0.0-alpha.9",
4
4
  "description": "Slate.js integration with Volto",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -110,7 +110,7 @@ const PersistentSlashMenu = ({ editor }) => {
110
110
 
111
111
  const [slashMenuSelected, setSlashMenuSelected] = React.useState(0);
112
112
 
113
- const useAllowedBlocks = !isEmpty(allowedBlocks);
113
+ const hasAllowedBlocks = !isEmpty(allowedBlocks);
114
114
  const slashCommand = data.plaintext
115
115
  ?.toLowerCase()
116
116
  .trim()
@@ -119,12 +119,13 @@ const PersistentSlashMenu = ({ editor }) => {
119
119
  const availableBlocks = React.useMemo(
120
120
  () =>
121
121
  filter(blocksConfig, (item) =>
122
- useAllowedBlocks
122
+ hasAllowedBlocks
123
123
  ? allowedBlocks.includes(item.id)
124
124
  : typeof item.restricted === 'function'
125
125
  ? !item.restricted({ properties, block: item })
126
126
  : !item.restricted,
127
127
  )
128
+ .filter((block) => Boolean(block.title && block.id))
128
129
  .filter((block) => {
129
130
  // typed text is a substring of the title or id
130
131
  const title = translateBlockTitle(block, intl).toLowerCase();
@@ -150,7 +151,7 @@ const PersistentSlashMenu = ({ editor }) => {
150
151
  intl,
151
152
  properties,
152
153
  slashCommand,
153
- useAllowedBlocks,
154
+ hasAllowedBlocks,
154
155
  ],
155
156
  );
156
157
 
@@ -95,7 +95,6 @@ export const blockTagDeserializer = (tagname) => (editor, el, options) => {
95
95
  children = [{ text: '' }];
96
96
  }
97
97
 
98
- console.log('children', children);
99
98
  return jsx('element', { type: tagname }, children);
100
99
  };
101
100
 
package/razzle.config.js CHANGED
@@ -341,6 +341,11 @@ const defaultModify = ({
341
341
 
342
342
  if (config.devServer) {
343
343
  config.devServer.static.watch.ignored = /node_modules\/(?!@plone\/volto)/;
344
+ config.snapshot = {
345
+ managedPaths: [
346
+ /^(.+?[\\/]node_modules[\\/](?!(@plone[\\/]volto))(@.+?[\\/])?.+?)[\\/]/,
347
+ ],
348
+ };
344
349
  }
345
350
 
346
351
  return config;
@@ -71,6 +71,12 @@ export {
71
71
  purgeMessages,
72
72
  } from '@plone/volto/actions/messages/messages';
73
73
  export { getNavigation } from '@plone/volto/actions/navigation/navigation';
74
+ export {
75
+ createRelations,
76
+ deleteRelations,
77
+ queryRelations,
78
+ } from '@plone/volto/actions/relations/relations';
79
+ export { rebuildRelations } from '@plone/volto/actions/relations/rebuild';
74
80
  export { listRoles } from '@plone/volto/actions/roles/roles';
75
81
  export {
76
82
  getSchema,
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Rebuild relations action.
3
+ * @module actions/relations/rebuild
4
+ */
5
+
6
+ import { REBUILD_RELATIONS } from '@plone/volto/constants/ActionTypes';
7
+
8
+ /**
9
+ * Rebuild relation function.
10
+ * @function rebuildRelations
11
+ * @param {Boolean} flush Flush intids
12
+ * @returns {Object} Rebuild relation action.
13
+ */
14
+ export function rebuildRelations(flush = false) {
15
+ let path = '/@relations';
16
+ var searchParams = new URLSearchParams();
17
+ searchParams.append('rebuild', '1');
18
+ flush && searchParams.append('flush', '1');
19
+ const searchParamsToString = searchParams.toString();
20
+ path += `?${searchParamsToString}`;
21
+ return {
22
+ type: REBUILD_RELATIONS,
23
+ request: { op: 'get', path: path },
24
+ };
25
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Relations actions.
3
+ * @module actions/relations/relations
4
+ */
5
+
6
+ import {
7
+ CREATE_RELATIONS,
8
+ DELETE_RELATIONS,
9
+ LIST_RELATIONS,
10
+ } from '@plone/volto/constants/ActionTypes';
11
+
12
+ /**
13
+ * Create relation function.
14
+ * @function createRelations
15
+ * @param {Object|Array} content Relation data.
16
+ * @returns {Object} Create relation action.
17
+ */
18
+ export function createRelations(content) {
19
+ return {
20
+ type: CREATE_RELATIONS,
21
+ request: {
22
+ op: 'post',
23
+ path: '/@relations',
24
+ data: {
25
+ items: content,
26
+ },
27
+ },
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Delete relation function.
33
+ * @function deleteRelations
34
+ * @param {string} id Relation id
35
+ * @returns {Object} Delete relation action.
36
+ */
37
+ export function deleteRelations(content) {
38
+ return {
39
+ type: DELETE_RELATIONS,
40
+ request: {
41
+ op: 'del',
42
+ path: `/@relations`,
43
+ data: {
44
+ items: content,
45
+ },
46
+ },
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Query relations
52
+ * @function queryRelations
53
+ * @param {string} relation Name of relation
54
+ * @param {boolean} onlyBroken
55
+ * @returns {Object} List relations action
56
+ */
57
+ export function queryRelations(
58
+ relation = null,
59
+ onlyBroken = false,
60
+ subrequest = null,
61
+ source = null,
62
+ target = null,
63
+ query_source = null,
64
+ query_target = null,
65
+ ) {
66
+ let path = '/@relations';
67
+ var searchParams = new URLSearchParams();
68
+ relation && searchParams.append('relation', relation);
69
+ onlyBroken && searchParams.append('onlyBroken', onlyBroken);
70
+ source && searchParams.append('source', source);
71
+ target && searchParams.append('target', target);
72
+ query_source && searchParams.append('query_source', query_source);
73
+ query_target && searchParams.append('query_target', query_target);
74
+ const searchParamsToString = searchParams.toString();
75
+ if (searchParamsToString) {
76
+ path += `?${searchParamsToString}`;
77
+ }
78
+ return {
79
+ type: LIST_RELATIONS,
80
+ subrequest,
81
+ request: {
82
+ op: 'get',
83
+ path: path,
84
+ },
85
+ };
86
+ }
@@ -0,0 +1,15 @@
1
+ import { queryRelations } from './relations';
2
+ import { LIST_RELATIONS } from '@plone/volto/constants/ActionTypes';
3
+
4
+ describe('Relations action', () => {
5
+ describe('queryRelations', () => {
6
+ it('should create an action to get relations of type "relatedItems"', () => {
7
+ const relation = 'relatedItems';
8
+ const action = queryRelations(relation);
9
+
10
+ expect(action.type).toEqual(LIST_RELATIONS);
11
+ expect(action.request.op).toEqual('get');
12
+ expect(action.request.path).toEqual(`/@relations?relation=${relation}`);
13
+ });
14
+ });
15
+ });
@@ -89,6 +89,7 @@ export ContentTypeSchema from '@plone/volto/components/manage/Controlpanels/Cont
89
89
  export ContentTypesActions from '@plone/volto/components/manage/Controlpanels/ContentTypesActions';
90
90
  export UsersControlpanel from '@plone/volto/components/manage/Controlpanels/Users/UsersControlpanel';
91
91
  export UserGroupMembershipControlPanel from '@plone/volto/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel';
92
+ export Relations from '@plone/volto/components/manage/Controlpanels/Relations/Relations';
92
93
  export GroupsControlpanel from '@plone/volto/components/manage/Controlpanels/Groups/GroupsControlpanel';
93
94
  export RulesControlpanel from '@plone/volto/components/manage/Controlpanels/Rules/Rules';
94
95
  export AddRuleControlpanel from '@plone/volto/components/manage/Controlpanels/Rules/AddRule';
@@ -33,17 +33,22 @@ const BlockChooser = ({
33
33
  properties = {},
34
34
  }) => {
35
35
  const intl = useIntl();
36
- const useAllowedBlocks = !isEmpty(allowedBlocks);
36
+ const hasAllowedBlocks = !isEmpty(allowedBlocks);
37
37
 
38
38
  const filteredBlocksConfig = filter(blocksConfig, (item) => {
39
+ // Check if the block is well formed (has at least id and title)
40
+ const blockIsWellFormed = Boolean(item.title && item.id);
41
+ if (!blockIsWellFormed) {
42
+ return false;
43
+ }
39
44
  if (showRestricted) {
40
- if (useAllowedBlocks) {
45
+ if (hasAllowedBlocks) {
41
46
  return allowedBlocks.includes(item.id);
42
47
  } else {
43
48
  return true;
44
49
  }
45
50
  } else {
46
- if (useAllowedBlocks) {
51
+ if (hasAllowedBlocks) {
47
52
  return allowedBlocks.includes(item.id);
48
53
  } else {
49
54
  // Overload restricted as a function, so we can decide the availability of a block
@@ -108,6 +108,11 @@ config.blocks.blocksConfig = {
108
108
  restricted: false,
109
109
  mostUsed: false,
110
110
  },
111
+ malformedBlock: {
112
+ icon: blockSVG,
113
+ group: 'common',
114
+ restricted: false,
115
+ },
111
116
  };
112
117
 
113
118
  const mockStore = configureStore();
@@ -1013,6 +1013,7 @@ class Contents extends Component {
1013
1013
  sort_order: this.state.sort_order,
1014
1014
  metadata_fields: '_all',
1015
1015
  b_size: 100000000,
1016
+ show_inactive: true,
1016
1017
  ...(this.state.filter && { SearchableText: `${this.state.filter}*` }),
1017
1018
  });
1018
1019
  } else {
@@ -1024,6 +1025,7 @@ class Contents extends Component {
1024
1025
  ...(this.state.filter && { SearchableText: `${this.state.filter}*` }),
1025
1026
  b_size: this.state.pageSize,
1026
1027
  b_start: this.state.currentPage * this.state.pageSize,
1028
+ show_inactive: true,
1027
1029
  });
1028
1030
  }
1029
1031
  }
@@ -1785,7 +1787,9 @@ class Contents extends Component {
1785
1787
  <Menu.Header
1786
1788
  content={this.props.intl.formatMessage(
1787
1789
  messages.selected,
1788
- { count: this.state.selected.length },
1790
+ {
1791
+ count: this.state.selected.length,
1792
+ },
1789
1793
  )}
1790
1794
  />
1791
1795
  <Input
@@ -169,6 +169,12 @@ export const ContentsItemComponent = ({
169
169
  <FormattedMessage id="Expired" defaultMessage="Expired" />
170
170
  </Button>
171
171
  )}
172
+ {item.EffectiveDate !== 'None' &&
173
+ new Date(item.EffectiveDate).getTime() > new Date().getTime() && (
174
+ <Button className="button-margin effective-future" size="mini">
175
+ <FormattedMessage id="Scheduled" defaultMessage="Scheduled" />
176
+ </Button>
177
+ )}
172
178
  </Link>
173
179
  </Table.Cell>
174
180
  {map(indexes, (index) => (
@@ -90,6 +90,10 @@ const messages = defineMessages({
90
90
  id: 'Content Rules',
91
91
  defaultMessage: 'Content Rules',
92
92
  },
93
+ relations: {
94
+ id: 'Relations',
95
+ defaultMessage: 'Relations',
96
+ },
93
97
  });
94
98
 
95
99
  /**
@@ -153,6 +157,11 @@ function Controlpanels({
153
157
  group: intl.formatMessage(messages.general),
154
158
  title: intl.formatMessage(messages.urlmanagement),
155
159
  },
160
+ {
161
+ '@id': '/relations',
162
+ group: intl.formatMessage(messages.content),
163
+ title: intl.formatMessage(messages.relations),
164
+ },
156
165
  {
157
166
  '@id': '/moderate-comments',
158
167
  group: intl.formatMessage(messages.content),
@@ -0,0 +1,66 @@
1
+ import React, { useEffect } from 'react';
2
+ import { uniqBy } from 'lodash';
3
+ import { FormattedMessage } from 'react-intl';
4
+ import { useSelector, useDispatch } from 'react-redux';
5
+ import { Divider, Segment, Table } from 'semantic-ui-react';
6
+ import { queryRelations } from '@plone/volto/actions';
7
+ import { flattenToAppURL } from '@plone/volto/helpers';
8
+ import { UniversalLink } from '@plone/volto/components';
9
+
10
+ const BrokenRelations = () => {
11
+ const dispatch = useDispatch();
12
+ const brokenRelationStats = useSelector(
13
+ (state) => state.relations?.stats?.broken || {},
14
+ );
15
+ const brokenRelations = useSelector(
16
+ (state) => state.relations?.subrequests?.broken?.relations,
17
+ );
18
+
19
+ useEffect(() => {
20
+ dispatch(queryRelations(null, true, 'broken'));
21
+ }, [dispatch]);
22
+
23
+ return brokenRelations && Object.keys(brokenRelations).length > 0 ? (
24
+ <>
25
+ <Divider section hidden />
26
+ <Segment>
27
+ <h3>
28
+ <FormattedMessage
29
+ id="Broken relations"
30
+ defaultMessage="Broken relations"
31
+ />
32
+ </h3>
33
+ {Object.keys(brokenRelations).map((relationname) => (
34
+ <div key={relationname}>
35
+ <Divider section hidden />
36
+ <h4>
37
+ {brokenRelationStats[relationname]} broken <i>{relationname}</i>{' '}
38
+ relations
39
+ </h4>
40
+ <Table>
41
+ <Table.Body>
42
+ {uniqBy(brokenRelations[relationname].items, function (el) {
43
+ return el[0];
44
+ }).map((el) => (
45
+ <Table.Row key={el[0]}>
46
+ <Table.Cell>
47
+ <UniversalLink
48
+ href={`${flattenToAppURL(el[0])}/edit`}
49
+ openLinkInNewTab={true}
50
+ >
51
+ {flattenToAppURL(el[0])}
52
+ </UniversalLink>
53
+ </Table.Cell>
54
+ <Table.Cell>{el[1]}</Table.Cell>
55
+ </Table.Row>
56
+ ))}
57
+ </Table.Body>
58
+ </Table>
59
+ </div>
60
+ ))}
61
+ </Segment>
62
+ </>
63
+ ) : null;
64
+ };
65
+
66
+ export default BrokenRelations;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Relations Control Panel
3
+ */
4
+ import React, { useEffect } from 'react';
5
+ import { find } from 'lodash';
6
+ import { useSelector } from 'react-redux';
7
+ import { Portal } from 'react-portal';
8
+ import { useHistory } from 'react-router';
9
+ import { Link, useLocation } from 'react-router-dom';
10
+ import { FormattedMessage, useIntl } from 'react-intl';
11
+ import { useDispatch } from 'react-redux';
12
+ import { Divider, Message, Segment } from 'semantic-ui-react';
13
+ import { Helmet, messages } from '@plone/volto/helpers';
14
+ import { listActions } from '@plone/volto/actions';
15
+ import { Icon, Toolbar } from '@plone/volto/components';
16
+ import { getParentUrl } from '@plone/volto/helpers';
17
+ import RelationsMatrix from '@plone/volto/components/manage/Controlpanels/Relations/RelationsMatrix';
18
+ import backSVG from '@plone/volto/icons/back.svg';
19
+
20
+ const RelationsControlPanel = () => {
21
+ const intl = useIntl();
22
+ const history = useHistory();
23
+ const location = useLocation();
24
+ const dispatch = useDispatch();
25
+
26
+ const brokenRelations = useSelector(
27
+ (state) => state.relations?.stats?.broken,
28
+ );
29
+
30
+ const relations_stats = useSelector((state) => state.relations?.stats?.stats);
31
+ const actions = useSelector((state) => state.actions?.actions ?? {});
32
+ const can_edit = find(actions.object, {
33
+ id: 'edit',
34
+ });
35
+
36
+ useEffect(() => {
37
+ dispatch(listActions('/'));
38
+ }, [dispatch]);
39
+
40
+ return (
41
+ <>
42
+ <div className="relations-control-panel">
43
+ <Helmet title={intl.formatMessage(messages.relations)} />
44
+ {can_edit ? (
45
+ <Segment.Group raised>
46
+ <Segment className="primary">
47
+ {brokenRelations && Object.keys(brokenRelations).length > 0 ? (
48
+ <React.Fragment>
49
+ <Message warning>
50
+ <FormattedMessage
51
+ id="Some relations are broken. Please fix."
52
+ defaultMessage="Some relations are broken. Please fix."
53
+ />
54
+ </Message>
55
+ <Divider hidden />
56
+ </React.Fragment>
57
+ ) : null}
58
+ <h1>
59
+ <FormattedMessage id="Relations" defaultMessage="Relations" />
60
+ </h1>
61
+ {!relations_stats ? (
62
+ <React.Fragment>
63
+ <Divider hidden />
64
+ <Message warning>
65
+ <FormattedMessage
66
+ id="Please upgrade to plone.restapi >= 8.35.3."
67
+ defaultMessage="Please upgrade to plone.restapi >= 8.35.3."
68
+ />
69
+ </Message>
70
+ </React.Fragment>
71
+ ) : null}
72
+ </Segment>
73
+ <Segment>
74
+ <RelationsMatrix />
75
+ </Segment>
76
+ </Segment.Group>
77
+ ) : (
78
+ <Segment.Group>
79
+ <Segment>
80
+ <FormattedMessage id="Relations" defaultMessage="Relations" />
81
+ <Divider hidden />
82
+ <FormattedMessage
83
+ id="You have not the required permission for this control panel."
84
+ defaultMessage="You have not the required permission for this control panel."
85
+ />
86
+ </Segment>
87
+ </Segment.Group>
88
+ )}
89
+ </div>
90
+
91
+ {__CLIENT__ && (
92
+ <Portal node={document.getElementById('toolbar')}>
93
+ <Toolbar
94
+ pathname={location.pathname}
95
+ hideDefaultViewButtons
96
+ inner={
97
+ <Link
98
+ className="item"
99
+ to="#"
100
+ onClick={() => {
101
+ history.push(getParentUrl(location.pathname));
102
+ }}
103
+ >
104
+ <Icon name={backSVG} className="contents circled" size="30px" />
105
+ </Link>
106
+ }
107
+ />
108
+ </Portal>
109
+ )}
110
+ </>
111
+ );
112
+ };
113
+
114
+ export default RelationsControlPanel;