@plone/volto 17.0.0-alpha.23 → 17.0.0-alpha.25

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 (81) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +58 -1
  3. package/CONTRIBUTING.md +5 -1
  4. package/README.md +2 -1
  5. package/locales/ca/LC_MESSAGES/volto.po +37 -0
  6. package/locales/ca.json +1 -1
  7. package/locales/de/LC_MESSAGES/volto.po +37 -0
  8. package/locales/de.json +1 -1
  9. package/locales/en/LC_MESSAGES/volto.po +37 -0
  10. package/locales/en.json +1 -1
  11. package/locales/es/LC_MESSAGES/volto.po +37 -0
  12. package/locales/es.json +1 -1
  13. package/locales/eu/LC_MESSAGES/volto.po +37 -0
  14. package/locales/eu.json +1 -1
  15. package/locales/fi/LC_MESSAGES/volto.po +37 -0
  16. package/locales/fi.json +1 -1
  17. package/locales/fr/LC_MESSAGES/volto.po +37 -0
  18. package/locales/fr.json +1 -1
  19. package/locales/it/LC_MESSAGES/volto.po +37 -0
  20. package/locales/it.json +1 -1
  21. package/locales/ja/LC_MESSAGES/volto.po +37 -0
  22. package/locales/ja.json +1 -1
  23. package/locales/nl/LC_MESSAGES/volto.po +37 -0
  24. package/locales/nl.json +1 -1
  25. package/locales/pt/LC_MESSAGES/volto.po +37 -0
  26. package/locales/pt.json +1 -1
  27. package/locales/pt_BR/LC_MESSAGES/volto.po +37 -0
  28. package/locales/pt_BR.json +1 -1
  29. package/locales/ro/LC_MESSAGES/volto.po +37 -0
  30. package/locales/ro.json +1 -1
  31. package/locales/volto.pot +38 -1
  32. package/locales/zh_CN/LC_MESSAGES/volto.po +37 -0
  33. package/locales/zh_CN.json +1 -1
  34. package/package.json +2 -2
  35. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +90 -0
  36. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +6 -0
  37. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +6 -0
  38. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +6 -0
  39. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +10 -0
  40. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +10 -0
  41. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +30 -0
  42. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +10 -0
  43. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +6 -0
  44. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +6 -0
  45. package/packages/volto-slate/package.json +1 -1
  46. package/src/actions/index.js +1 -0
  47. package/src/actions/relations/relations.js +17 -0
  48. package/src/components/manage/Blocks/Image/schema.js +5 -1
  49. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +18 -11
  50. package/src/components/manage/Blocks/ToC/Schema.jsx +36 -7
  51. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +4 -3
  52. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.test.jsx +44 -0
  53. package/src/components/manage/Contents/Contents.jsx +27 -0
  54. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +11 -9
  55. package/src/components/manage/Controlpanels/Relations/Relations.jsx +3 -3
  56. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +8 -7
  57. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +15 -9
  58. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +25 -10
  59. package/src/components/manage/Diff/DiffField.jsx +25 -1
  60. package/src/components/manage/LinksToItem/LinksToItem.jsx +1 -1
  61. package/src/components/manage/LinksToItem/LinksToItem.test.jsx +1 -1
  62. package/src/components/manage/Sharing/Sharing.jsx +11 -5
  63. package/src/components/manage/Widgets/FormFieldWrapper.jsx +1 -1
  64. package/src/components/theme/Comments/Comment.stories.jsx +84 -0
  65. package/src/components/theme/Comments/Comments.jsx +273 -378
  66. package/src/components/theme/Error/ServerError.jsx +29 -0
  67. package/src/components/theme/Logout/Logout.jsx +36 -83
  68. package/src/components/theme/Search/SearchTags.jsx +30 -60
  69. package/src/components/theme/Sitemap/Sitemap.jsx +24 -13
  70. package/src/components/theme/Sitemap/Sitemap.test.jsx +23 -2
  71. package/src/config/Views.jsx +2 -0
  72. package/src/constants/ActionTypes.js +1 -0
  73. package/src/middleware/api.js +14 -2
  74. package/src/reducers/relations/relations.js +74 -46
  75. package/src/server.jsx +9 -0
  76. package/theme/themes/pastanaga/collections/form.overrides +46 -0
  77. package/theme/themes/pastanaga/elements/input.overrides +6 -0
  78. package/theme/themes/pastanaga/elements/label.overrides +10 -0
  79. package/.gitignore~ +0 -71
  80. package/news/4547.breaking~ +0 -1
  81. package/src/config/index.js~ +0 -223
@@ -23,7 +23,7 @@ export default function withQuerystringResults(WrappedComponent) {
23
23
  } = props;
24
24
  const { settings } = config;
25
25
  const querystring = data.querystring || data; // For backwards compat with data saved before Blocks schema. Note, this is also how the Search block passes data to ListingBody
26
-
26
+ const subrequestID = content?.UID + '-' + id;
27
27
  const { b_size = settings.defaultPageSize } = querystring; // batchsize
28
28
 
29
29
  // save the path so it won't trigger dispatch on eager router location change
@@ -52,31 +52,33 @@ export default function withQuerystringResults(WrappedComponent) {
52
52
 
53
53
  const folderItems = content?.is_folderish ? content.items : [];
54
54
  const hasQuery = querystring?.query?.length > 0;
55
- const hasLoaded = hasQuery ? querystringResults?.[id]?.loaded : true;
55
+ const hasLoaded = hasQuery
56
+ ? querystringResults?.[subrequestID]?.loaded
57
+ : true;
56
58
 
57
59
  const listingItems = hasQuery
58
- ? querystringResults?.[id]?.items || []
60
+ ? querystringResults?.[subrequestID]?.items || []
59
61
  : folderItems;
60
62
 
61
63
  const showAsFolderListing = !hasQuery && content?.items_total > b_size;
62
64
  const showAsQueryListing =
63
- hasQuery && querystringResults?.[id]?.total > b_size;
65
+ hasQuery && querystringResults?.[subrequestID]?.total > b_size;
64
66
 
65
67
  const totalPages = showAsFolderListing
66
68
  ? Math.ceil(content.items_total / b_size)
67
69
  : showAsQueryListing
68
- ? Math.ceil(querystringResults[id].total / b_size)
70
+ ? Math.ceil(querystringResults[subrequestID].total / b_size)
69
71
  : 0;
70
72
 
71
73
  const prevBatch = showAsFolderListing
72
74
  ? content.batching?.prev
73
75
  : showAsQueryListing
74
- ? querystringResults[id].batching?.prev
76
+ ? querystringResults[subrequestID].batching?.prev
75
77
  : null;
76
78
  const nextBatch = showAsFolderListing
77
79
  ? content.batching?.next
78
80
  : showAsQueryListing
79
- ? querystringResults[id].batching?.next
81
+ ? querystringResults[subrequestID].batching?.next
80
82
  : null;
81
83
 
82
84
  const isImageGallery =
@@ -86,7 +88,12 @@ export default function withQuerystringResults(WrappedComponent) {
86
88
  useDeepCompareEffect(() => {
87
89
  if (hasQuery) {
88
90
  dispatch(
89
- getQueryStringResults(initialPath, adaptedQuery, id, currentPage),
91
+ getQueryStringResults(
92
+ initialPath,
93
+ adaptedQuery,
94
+ subrequestID,
95
+ currentPage,
96
+ ),
90
97
  );
91
98
  } else if (isImageGallery && !hasQuery) {
92
99
  // when used as image gallery, it doesn't need a query to list children
@@ -104,7 +111,7 @@ export default function withQuerystringResults(WrappedComponent) {
104
111
  },
105
112
  ],
106
113
  },
107
- id,
114
+ subrequestID,
108
115
  ),
109
116
  );
110
117
  } else {
@@ -113,7 +120,7 @@ export default function withQuerystringResults(WrappedComponent) {
113
120
  adaptedQueryRef.current = adaptedQuery;
114
121
  currentPageRef.current = currentPage;
115
122
  }, [
116
- id,
123
+ subrequestID,
117
124
  isImageGallery,
118
125
  adaptedQuery,
119
126
  hasQuery,
@@ -126,7 +133,7 @@ export default function withQuerystringResults(WrappedComponent) {
126
133
  <WrappedComponent
127
134
  {...props}
128
135
  onPaginationChange={(e, { activePage }) => setCurrentPage(activePage)}
129
- total={querystringResults?.[id]?.total}
136
+ total={querystringResults?.[subrequestID]?.total}
130
137
  batch_size={b_size}
131
138
  currentPage={currentPage}
132
139
  totalPages={totalPages}
@@ -1,8 +1,37 @@
1
- const TableOfContentsSchema = ({ data }) => {
1
+ import { defineMessages } from 'react-intl';
2
+
3
+ const messages = defineMessages({
4
+ toc: {
5
+ id: 'toc',
6
+ defaultMessage: 'Table of Contents',
7
+ },
8
+ Title: {
9
+ id: 'Title',
10
+ defaultMessage: 'Title',
11
+ },
12
+ HideTitle: {
13
+ id: 'Hide title',
14
+ defaultMessage: 'Hide title',
15
+ },
16
+ Entries: {
17
+ id: 'Entries',
18
+ defaultMessage: 'Entries',
19
+ },
20
+ Ordered: {
21
+ id: 'Ordered',
22
+ defaultMessage: 'Ordered',
23
+ },
24
+ Sticky: {
25
+ id: 'Sticky',
26
+ defaultMessage: 'Sticky',
27
+ },
28
+ });
29
+
30
+ const TableOfContentsSchema = ({ data, intl }) => {
2
31
  const { variation = 'default' } = data;
3
32
 
4
33
  return {
5
- title: 'Table of Contents',
34
+ title: intl.formatMessage(messages.toc),
6
35
  fieldsets: [
7
36
  {
8
37
  id: 'default',
@@ -17,14 +46,14 @@ const TableOfContentsSchema = ({ data }) => {
17
46
  ],
18
47
  properties: {
19
48
  title: {
20
- title: 'Block title',
49
+ title: intl.formatMessage(messages.Title),
21
50
  },
22
51
  hide_title: {
23
- title: 'Hide title',
52
+ title: intl.formatMessage(messages.HideTitle),
24
53
  type: 'boolean',
25
54
  },
26
55
  levels: {
27
- title: 'Entries',
56
+ title: intl.formatMessage(messages.Entries),
28
57
  isMulti: true,
29
58
  choices: [
30
59
  ['h1', 'h1'],
@@ -36,11 +65,11 @@ const TableOfContentsSchema = ({ data }) => {
36
65
  ],
37
66
  },
38
67
  ordered: {
39
- title: 'Ordered',
68
+ title: intl.formatMessage(messages.Ordered),
40
69
  type: 'boolean',
41
70
  },
42
71
  sticky: {
43
- title: 'Sticky',
72
+ title: intl.formatMessage(messages.Sticky),
44
73
  type: 'boolean',
45
74
  },
46
75
  },
@@ -12,7 +12,9 @@ import { useHistory } from 'react-router-dom';
12
12
  import AnchorLink from 'react-anchor-link-smooth-scroll';
13
13
  import Slugger from 'github-slugger';
14
14
 
15
- const RenderListItems = ({ items, data, history }) => {
15
+ const RenderListItems = ({ items, data }) => {
16
+ const history = useHistory();
17
+
16
18
  return map(items, (item) => {
17
19
  const { id, level, title, override_toc, plaintext } = item;
18
20
  const slug = override_toc
@@ -50,7 +52,6 @@ const RenderListItems = ({ items, data, history }) => {
50
52
  * @extends Component
51
53
  */
52
54
  const View = ({ data, tocEntries }) => {
53
- const history = useHistory();
54
55
  return (
55
56
  <>
56
57
  {data.title && !data.hide_title ? (
@@ -70,7 +71,7 @@ const View = ({ data, tocEntries }) => {
70
71
  bulleted={!data.ordered}
71
72
  as={data.ordered ? 'ol' : 'ul'}
72
73
  >
73
- <RenderListItems items={tocEntries} data={data} history={history} />
74
+ <RenderListItems items={tocEntries} data={data} />
74
75
  </List>
75
76
  </>
76
77
  );
@@ -0,0 +1,44 @@
1
+ import renderer from 'react-test-renderer';
2
+ import configureStore from 'redux-mock-store';
3
+ import { Provider } from 'react-intl-redux';
4
+ import { MemoryRouter } from 'react-router-dom';
5
+ import DefaultTocRenderer from './DefaultTocRenderer';
6
+
7
+ const mockStore = configureStore();
8
+
9
+ const data = { '@type': 'toc', variation: 'default' };
10
+
11
+ const tocEntries = [
12
+ {
13
+ level: 2,
14
+ title: 'Hello this is a sample page',
15
+ items: [
16
+ {
17
+ level: 3,
18
+ title: 'Test level 3',
19
+ items: [],
20
+ id: 'be612682-6df9-4a5e-b3a1-9dec5d82ae14',
21
+ parentId: '3a8bff13-3245-44f6-8a35-e0defef5898e',
22
+ },
23
+ ],
24
+ id: '3a8bff13-3245-44f6-8a35-e0defef5898e',
25
+ },
26
+ ];
27
+
28
+ test('renders a default toc renderer component', () => {
29
+ const store = mockStore({
30
+ intl: {
31
+ locale: 'en',
32
+ messages: {},
33
+ },
34
+ });
35
+ const component = renderer.create(
36
+ <Provider store={store}>
37
+ <MemoryRouter>
38
+ <DefaultTocRenderer data={data} tocEntries={tocEntries} />
39
+ </MemoryRouter>
40
+ </Provider>,
41
+ );
42
+ const json = component.toJSON();
43
+ expect(json).toMatchSnapshot();
44
+ });
@@ -1334,6 +1334,9 @@ class Contents extends Component {
1334
1334
  as={Button}
1335
1335
  onClick={this.upload}
1336
1336
  className="upload"
1337
+ aria-label={this.props.intl.formatMessage(
1338
+ messages.upload,
1339
+ )}
1337
1340
  >
1338
1341
  <Icon
1339
1342
  name={uploadSVG}
@@ -1358,6 +1361,9 @@ class Contents extends Component {
1358
1361
  as={Button}
1359
1362
  onClick={this.rename}
1360
1363
  disabled={!selected}
1364
+ aria-label={this.props.intl.formatMessage(
1365
+ messages.rename,
1366
+ )}
1361
1367
  >
1362
1368
  <Icon
1363
1369
  name={renameSVG}
@@ -1380,6 +1386,9 @@ class Contents extends Component {
1380
1386
  as={Button}
1381
1387
  onClick={this.workflow}
1382
1388
  disabled={!selected}
1389
+ aria-label={this.props.intl.formatMessage(
1390
+ messages.state,
1391
+ )}
1383
1392
  >
1384
1393
  <Icon
1385
1394
  name={semaphoreSVG}
@@ -1402,6 +1411,9 @@ class Contents extends Component {
1402
1411
  as={Button}
1403
1412
  onClick={this.tags}
1404
1413
  disabled={!selected}
1414
+ aria-label={this.props.intl.formatMessage(
1415
+ messages.tags,
1416
+ )}
1405
1417
  >
1406
1418
  <Icon
1407
1419
  name={tagSVG}
@@ -1425,6 +1437,9 @@ class Contents extends Component {
1425
1437
  as={Button}
1426
1438
  onClick={this.properties}
1427
1439
  disabled={!selected}
1440
+ aria-label={this.props.intl.formatMessage(
1441
+ messages.properties,
1442
+ )}
1428
1443
  >
1429
1444
  <Icon
1430
1445
  name={propertiesSVG}
@@ -1449,6 +1464,9 @@ class Contents extends Component {
1449
1464
  as={Button}
1450
1465
  onClick={this.cut}
1451
1466
  disabled={!selected}
1467
+ aria-label={this.props.intl.formatMessage(
1468
+ messages.cut,
1469
+ )}
1452
1470
  >
1453
1471
  <Icon
1454
1472
  name={cutSVG}
@@ -1471,6 +1489,9 @@ class Contents extends Component {
1471
1489
  as={Button}
1472
1490
  onClick={this.copy}
1473
1491
  disabled={!selected}
1492
+ aria-label={this.props.intl.formatMessage(
1493
+ messages.copy,
1494
+ )}
1474
1495
  >
1475
1496
  <Icon
1476
1497
  name={copySVG}
@@ -1494,6 +1515,9 @@ class Contents extends Component {
1494
1515
  as={Button}
1495
1516
  onClick={this.paste}
1496
1517
  disabled={!this.props.action}
1518
+ aria-label={this.props.intl.formatMessage(
1519
+ messages.paste,
1520
+ )}
1497
1521
  >
1498
1522
  <Icon
1499
1523
  name={pasteSVG}
@@ -1517,6 +1541,9 @@ class Contents extends Component {
1517
1541
  as={Button}
1518
1542
  onClick={this.delete}
1519
1543
  disabled={!selected}
1544
+ aria-label={this.props.intl.formatMessage(
1545
+ messages.delete,
1546
+ )}
1520
1547
  >
1521
1548
  <Icon
1522
1549
  name={deleteSVG}
@@ -5,15 +5,15 @@ import { useSelector, useDispatch } from 'react-redux';
5
5
  import { Divider, Segment, Table } from 'semantic-ui-react';
6
6
  import { queryRelations } from '@plone/volto/actions';
7
7
  import { flattenToAppURL } from '@plone/volto/helpers';
8
- import { UniversalLink } from '@plone/volto/components';
8
+ import { ConditionalLink } from '@plone/volto/components';
9
9
 
10
10
  const BrokenRelations = () => {
11
11
  const dispatch = useDispatch();
12
12
  const brokenRelationStats = useSelector(
13
- (state) => state.relations?.stats?.broken || {},
13
+ (state) => state.relations?.stats?.data?.broken || {},
14
14
  );
15
15
  const brokenRelations = useSelector(
16
- (state) => state.relations?.subrequests?.broken?.relations,
16
+ (state) => state.relations?.subrequests?.broken?.data,
17
17
  );
18
18
 
19
19
  useEffect(() => {
@@ -60,20 +60,22 @@ const BrokenRelations = () => {
60
60
  }).map((el, index) => (
61
61
  <Table.Row key={index}>
62
62
  <Table.Cell>
63
- <UniversalLink
64
- href={`${flattenToAppURL(el[0])}/edit`}
63
+ <ConditionalLink
64
+ to={`${el[0]}/edit`}
65
65
  openLinkInNewTab={true}
66
+ condition={el[0].includes('http')}
66
67
  >
67
68
  {flattenToAppURL(el[0])}
68
- </UniversalLink>
69
+ </ConditionalLink>
69
70
  </Table.Cell>
70
71
  <Table.Cell>
71
- <UniversalLink
72
- href={`${flattenToAppURL(el[1])}/edit`}
72
+ <ConditionalLink
73
+ to={`${el[1]}/edit`}
73
74
  openLinkInNewTab={true}
75
+ condition={el[1].includes('http')}
74
76
  >
75
77
  {flattenToAppURL(el[1])}
76
- </UniversalLink>
78
+ </ConditionalLink>
77
79
  </Table.Cell>
78
80
  </Table.Row>
79
81
  ))}
@@ -24,10 +24,10 @@ const RelationsControlPanel = () => {
24
24
  const dispatch = useDispatch();
25
25
 
26
26
  const brokenRelations = useSelector(
27
- (state) => state.relations?.stats?.broken,
27
+ (state) => state.relations?.stats?.data?.broken,
28
28
  );
29
29
 
30
- const relations_stats = useSelector((state) => state.relations?.stats?.stats);
30
+ const relations_stats = useSelector((state) => state.relations?.stats?.data);
31
31
  const actions = useSelector((state) => state.actions?.actions ?? {});
32
32
  const can_edit = find(actions.object, {
33
33
  id: 'edit',
@@ -58,7 +58,7 @@ const RelationsControlPanel = () => {
58
58
  <h1>
59
59
  <FormattedMessage id="Relations" defaultMessage="Relations" />
60
60
  </h1>
61
- {!relations_stats ? (
61
+ {relations_stats?.error ? (
62
62
  <React.Fragment>
63
63
  <Divider hidden />
64
64
  <Message warning>
@@ -11,6 +11,7 @@ import {
11
11
  createRelations,
12
12
  deleteRelations,
13
13
  queryRelations,
14
+ resetSearchContent,
14
15
  searchContent,
15
16
  } from '@plone/volto/actions';
16
17
 
@@ -28,9 +29,10 @@ const ListingTemplate = ({
28
29
  const MAX = 40; // Maximum of rows and columns
29
30
  const MAX_RELATIONS = 1000;
30
31
 
31
- const stats = useSelector((state) => state.relations?.stats || null);
32
+ const stats = useSelector((state) => state.relations?.stats?.data || null);
33
+
32
34
  let relations = useSelector(
33
- (state) => state.relations?.relations?.[relationtype]?.items || [],
35
+ (state) => state.relations?.relations?.data?.[relationtype]?.items || [],
34
36
  );
35
37
 
36
38
  let potential_targets_objects = useSelector(
@@ -49,7 +51,8 @@ const ListingTemplate = ({
49
51
 
50
52
  // Editable if plone.api.relations available
51
53
  const editable = useSelector(
52
- (state) => state.relations?.relations?.[relationtype]?.readonly !== true,
54
+ (state) =>
55
+ state.relations?.relations?.data?.[relationtype]?.readonly !== true,
53
56
  );
54
57
 
55
58
  let relationMatrix = {};
@@ -192,8 +195,7 @@ const ListingTemplate = ({
192
195
  ),
193
196
  );
194
197
  } else {
195
- // TODO Better just reset redux store
196
- dispatch(searchContent('/findstenichätsch', null, 'potential_targets'));
198
+ dispatch(resetSearchContent('potential_targets'));
197
199
  }
198
200
 
199
201
  // Fetch fresh potential sources
@@ -211,8 +213,7 @@ const ListingTemplate = ({
211
213
  ),
212
214
  );
213
215
  } else {
214
- // TODO Better just reset redux store
215
- dispatch(searchContent('/findstenichätsch', null, 'potential_sources'));
216
+ dispatch(resetSearchContent('potential_sources'));
216
217
  }
217
218
  }, [
218
219
  dispatch,
@@ -17,7 +17,11 @@ import {
17
17
  import withObjectBrowser from '@plone/volto/components/manage/Sidebar/ObjectBrowser';
18
18
  import { messages } from '@plone/volto/helpers';
19
19
  import { Icon, Toast } from '@plone/volto/components';
20
- import { rebuildRelations, queryRelations } from '@plone/volto/actions';
20
+ import {
21
+ getRelationStats,
22
+ queryRelations,
23
+ rebuildRelations,
24
+ } from '@plone/volto/actions';
21
25
  import RelationsListing from './RelationsListing';
22
26
  import BrokenRelations from './BrokenRelations';
23
27
  import helpSVG from '@plone/volto/icons/help.svg';
@@ -39,12 +43,14 @@ const RelationsMatrix = (props) => {
39
43
  id: 'plone_setup',
40
44
  });
41
45
 
42
- const relationtypes = useSelector((state) => state.relations?.stats?.stats);
46
+ const relationtypes = useSelector(
47
+ (state) => state.relations?.stats?.data?.stats,
48
+ );
43
49
  const relationsListError = useSelector(
44
- (state) => state.relations?.list?.error?.response?.body?.error,
50
+ (state) => state.relations?.stats?.error?.response?.body?.error,
45
51
  );
46
52
  const brokenRelations = useSelector(
47
- (state) => state.relations?.stats?.broken,
53
+ (state) => state.relations?.stats?.data?.broken,
48
54
  );
49
55
 
50
56
  let filter_options = useSelector((state) => state.groups.filter_groups);
@@ -67,7 +73,7 @@ const RelationsMatrix = (props) => {
67
73
  }
68
74
 
69
75
  useEffect(() => {
70
- dispatch(queryRelations());
76
+ dispatch(getRelationStats());
71
77
  }, [dispatch]);
72
78
 
73
79
  const onReset = (event) => {
@@ -128,9 +134,7 @@ const RelationsMatrix = (props) => {
128
134
  const rebuildRelationsHandler = (flush = false) => {
129
135
  dispatch(rebuildRelations(flush))
130
136
  .then(() => {
131
- dispatch(queryRelations());
132
- })
133
- .then(() => {
137
+ dispatch(getRelationStats());
134
138
  dispatch(queryRelations(null, true, 'broken'));
135
139
  })
136
140
  .then(() => {
@@ -443,7 +447,9 @@ const RelationsMatrix = (props) => {
443
447
  ) : null}
444
448
  </div>
445
449
  ) : (
446
- <p>{relationsListError?.message}</p>
450
+ <p>
451
+ <b>{relationsListError?.type}</b> {relationsListError?.message}
452
+ </p>
447
453
  )}
448
454
  </Tab.Pane>
449
455
  ),
@@ -107,6 +107,7 @@ class UsersControlpanel extends Component {
107
107
  isClient: false,
108
108
  currentPage: 0,
109
109
  pageSize: 10,
110
+ loginUsingEmail: false,
110
111
  };
111
112
  }
112
113
 
@@ -122,6 +123,14 @@ class UsersControlpanel extends Component {
122
123
  }
123
124
  };
124
125
 
126
+ // Because username field needs to be disabled if email login is enabled!
127
+ checkLoginUsingEmailStatus = async () => {
128
+ await this.props.getControlpanel('security');
129
+ this.setState({
130
+ loginUsingEmail: this.props.controlPanelData?.data.use_email_as_login,
131
+ });
132
+ };
133
+
125
134
  /**
126
135
  * Component did mount
127
136
  * @method componentDidMount
@@ -132,6 +141,7 @@ class UsersControlpanel extends Component {
132
141
  isClient: true,
133
142
  });
134
143
  this.fetchData();
144
+ this.checkLoginUsingEmailStatus();
135
145
  }
136
146
 
137
147
  UNSAFE_componentWillReceiveProps(nextProps) {
@@ -425,7 +435,7 @@ class UsersControlpanel extends Component {
425
435
  id: 'default',
426
436
  title: 'FIXME: User Data',
427
437
  fields: [
428
- 'username',
438
+ ...(!this.state.loginUsingEmail ? ['username'] : []),
429
439
  'fullname',
430
440
  'email',
431
441
  'password',
@@ -436,15 +446,19 @@ class UsersControlpanel extends Component {
436
446
  },
437
447
  ],
438
448
  properties: {
439
- username: {
440
- title: this.props.intl.formatMessage(
441
- messages.addUserFormUsernameTitle,
442
- ),
443
- type: 'string',
444
- description: this.props.intl.formatMessage(
445
- messages.addUserFormUsernameDescription,
446
- ),
447
- },
449
+ ...(!this.state.loginUsingEmail
450
+ ? {
451
+ username: {
452
+ title: this.props.intl.formatMessage(
453
+ messages.addUserFormUsernameTitle,
454
+ ),
455
+ type: 'string',
456
+ description: this.props.intl.formatMessage(
457
+ messages.addUserFormUsernameDescription,
458
+ ),
459
+ },
460
+ }
461
+ : {}),
448
462
  fullname: {
449
463
  title: this.props.intl.formatMessage(
450
464
  messages.addUserFormFullnameTitle,
@@ -670,6 +684,7 @@ export default compose(
670
684
  createRequest: state.users.create,
671
685
  loadRolesRequest: state.roles,
672
686
  inheritedRole: state.authRole.authenticatedRole,
687
+ controlPanelData: state.controlpanels?.controlpanel,
673
688
  }),
674
689
  (dispatch) =>
675
690
  bindActionCreators(
@@ -17,6 +17,7 @@ import { useSelector } from 'react-redux';
17
17
  import { Api } from '@plone/volto/helpers';
18
18
  import configureStore from '@plone/volto/store';
19
19
  import { DefaultView } from '@plone/volto/components/';
20
+ import { serializeNodes } from '@plone/volto-slate/editor/render';
20
21
 
21
22
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
22
23
 
@@ -69,7 +70,7 @@ const DiffField = ({
69
70
  .replace('\u202F', ' '),
70
71
  );
71
72
  break;
72
- case 'json':
73
+ case 'json': {
73
74
  const api = new Api();
74
75
  const history = createBrowserHistory();
75
76
  const store = configureStore(window.__data, history, api);
@@ -90,6 +91,29 @@ const DiffField = ({
90
91
  ),
91
92
  );
92
93
  break;
94
+ }
95
+ case 'slate': {
96
+ const api = new Api();
97
+ const history = createBrowserHistory();
98
+ const store = configureStore(window.__data, history, api);
99
+ parts = diffWords(
100
+ ReactDOMServer.renderToStaticMarkup(
101
+ <Provider store={store}>
102
+ <ConnectedRouter history={history}>
103
+ {serializeNodes(one)}
104
+ </ConnectedRouter>
105
+ </Provider>,
106
+ ),
107
+ ReactDOMServer.renderToStaticMarkup(
108
+ <Provider store={store}>
109
+ <ConnectedRouter history={history}>
110
+ {serializeNodes(two)}
111
+ </ConnectedRouter>
112
+ </Provider>,
113
+ ),
114
+ );
115
+ break;
116
+ }
93
117
  case 'textarea':
94
118
  default:
95
119
  parts = diffWords(one, two);
@@ -44,7 +44,7 @@ const LinksToItem = (props) => {
44
44
 
45
45
  const title = useSelector((state) => state.content.data?.title || '');
46
46
  const myrelations = useSelector(
47
- (state) => state.relations.subrequests[itempath]?.relations,
47
+ (state) => state.relations.subrequests[itempath]?.data,
48
48
  );
49
49
  const actions = useSelector((state) => state.actions?.actions ?? {});
50
50
  const ploneSetupAction = find(actions.user, {
@@ -20,7 +20,7 @@ describe('LinksToItem', () => {
20
20
  relations: {
21
21
  subrequests: {
22
22
  '/page-1': {
23
- relations: {
23
+ data: {
24
24
  isReferencing: {
25
25
  items: [
26
26
  {
@@ -246,9 +246,9 @@ class SharingComponent extends Component {
246
246
  * @returns {undefined}
247
247
  */
248
248
  onToggleInherit() {
249
- this.setState({
250
- inherit: !this.state.inherit,
251
- });
249
+ this.setState((state) => ({
250
+ inherit: !state.inherit,
251
+ }));
252
252
  }
253
253
 
254
254
  /**
@@ -404,9 +404,15 @@ class SharingComponent extends Component {
404
404
  <Segment attached>
405
405
  <Form.Field>
406
406
  <Checkbox
407
- checked={this.state.inherit}
407
+ id="inherit-permissions-checkbox"
408
+ name="inherit-permissions-checkbox"
409
+ defaultChecked={this.state.inherit}
408
410
  onChange={this.onToggleInherit}
409
- label={this.props.intl.formatMessage(messages.inherit)}
411
+ label={
412
+ <label htmlFor="inherit-permissions-checkbox">
413
+ {this.props.intl.formatMessage(messages.inherit)}
414
+ </label>
415
+ }
410
416
  />
411
417
  </Form.Field>
412
418
  <p className="help">