@plone/volto 18.0.0-alpha.18 → 18.0.0-alpha.19

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 (92) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/locales/ca/LC_MESSAGES/volto.po +17 -42
  3. package/locales/ca.json +1 -1
  4. package/locales/de/LC_MESSAGES/volto.po +15 -40
  5. package/locales/de.json +1 -1
  6. package/locales/en/LC_MESSAGES/volto.po +14 -39
  7. package/locales/en.json +1 -1
  8. package/locales/es/LC_MESSAGES/volto.po +15 -40
  9. package/locales/es.json +1 -1
  10. package/locales/eu/LC_MESSAGES/volto.po +15 -40
  11. package/locales/eu.json +1 -1
  12. package/locales/fi/LC_MESSAGES/volto.po +15 -40
  13. package/locales/fi.json +1 -1
  14. package/locales/fr/LC_MESSAGES/volto.po +15 -40
  15. package/locales/fr.json +1 -1
  16. package/locales/it/LC_MESSAGES/volto.po +15 -40
  17. package/locales/it.json +1 -1
  18. package/locales/ja/LC_MESSAGES/volto.po +15 -40
  19. package/locales/ja.json +1 -1
  20. package/locales/nl/LC_MESSAGES/volto.po +14 -39
  21. package/locales/nl.json +1 -1
  22. package/locales/pt/LC_MESSAGES/volto.po +15 -40
  23. package/locales/pt.json +1 -1
  24. package/locales/pt_BR/LC_MESSAGES/volto.po +15 -40
  25. package/locales/pt_BR.json +1 -1
  26. package/locales/ro/LC_MESSAGES/volto.po +15 -40
  27. package/locales/ro.json +1 -1
  28. package/locales/volto.pot +15 -40
  29. package/locales/zh_CN/LC_MESSAGES/volto.po +15 -40
  30. package/locales/zh_CN.json +1 -1
  31. package/package.json +10 -20
  32. package/src/components/index.js +0 -6
  33. package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +8 -2
  34. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +2 -2
  35. package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +33 -5
  36. package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.test.jsx +12 -0
  37. package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +22 -11
  38. package/src/components/manage/Controlpanels/Groups/RenderGroups.test.jsx +21 -0
  39. package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +30 -21
  40. package/src/components/manage/Controlpanels/Users/RenderUsers.test.jsx +27 -1
  41. package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +29 -7
  42. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +51 -3
  43. package/src/components/manage/Controlpanels/Users/UsersControlpanel.test.jsx +8 -0
  44. package/src/config/Blocks.jsx +63 -67
  45. package/src/config/Loadables.jsx +0 -22
  46. package/src/config/Widgets.jsx +0 -2
  47. package/src/config/index.js +0 -13
  48. package/src/helpers/User/User.js +29 -0
  49. package/src/helpers/index.js +6 -1
  50. package/test-setup-config.js +0 -30
  51. package/types/components/index.d.ts +0 -6
  52. package/types/config/Blocks.d.ts +0 -51
  53. package/types/config/Loadables.d.ts +0 -10
  54. package/types/config/Widgets.d.ts +0 -2
  55. package/types/helpers/User/User.d.ts +18 -0
  56. package/types/helpers/index.d.ts +1 -1
  57. package/webpack-plugins/webpack-bundle-analyze-plugin.js +1 -1
  58. package/src/components/manage/AnchorPlugin/components/Link/index.jsx +0 -37
  59. package/src/components/manage/AnchorPlugin/components/LinkButton/index.jsx +0 -126
  60. package/src/components/manage/AnchorPlugin/index.jsx +0 -82
  61. package/src/components/manage/AnchorPlugin/linkStrategy.js +0 -21
  62. package/src/components/manage/AnchorPlugin/utils/EditorUtils.js +0 -47
  63. package/src/components/manage/Blocks/HeroImageLeft/Data.jsx +0 -29
  64. package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +0 -493
  65. package/src/components/manage/Blocks/HeroImageLeft/Edit.test.jsx +0 -58
  66. package/src/components/manage/Blocks/HeroImageLeft/View.jsx +0 -37
  67. package/src/components/manage/Blocks/HeroImageLeft/View.test.jsx +0 -9
  68. package/src/components/manage/Blocks/HeroImageLeft/schema.js +0 -43
  69. package/src/components/manage/Blocks/Table/Cell.jsx +0 -206
  70. package/src/components/manage/Blocks/Table/Cell.test.jsx +0 -19
  71. package/src/components/manage/Blocks/Table/Edit.jsx +0 -748
  72. package/src/components/manage/Blocks/Table/Edit.test.jsx +0 -44
  73. package/src/components/manage/Blocks/Table/Readme.md +0 -5
  74. package/src/components/manage/Blocks/Table/View.jsx +0 -51
  75. package/src/components/manage/Blocks/Table/View.test.jsx +0 -41
  76. package/src/components/manage/Blocks/Text/Edit.jsx +0 -372
  77. package/src/components/manage/Blocks/Text/Edit.test.jsx +0 -46
  78. package/src/components/manage/Blocks/Text/Readme.md +0 -5
  79. package/src/components/manage/Blocks/Text/Schema.jsx +0 -31
  80. package/src/components/manage/Blocks/Text/View.jsx +0 -26
  81. package/src/components/manage/Blocks/Text/View.test.jsx +0 -28
  82. package/src/components/manage/LinkDetectionPlugin/link-detection-plugin.jsx +0 -227
  83. package/src/components/manage/LinkDetectionPlugin/utils.js +0 -12
  84. package/src/components/manage/Widgets/WysiwygWidget.jsx +0 -350
  85. package/src/components/manage/Widgets/WysiwygWidget.stories.jsx +0 -24
  86. package/src/components/manage/Widgets/WysiwygWidget.test.jsx +0 -37
  87. package/src/config/RichTextEditor/Blocks.jsx +0 -29
  88. package/src/config/RichTextEditor/FromHTML.jsx +0 -8
  89. package/src/config/RichTextEditor/Plugins.jsx +0 -59
  90. package/src/config/RichTextEditor/Styles.jsx +0 -69
  91. package/src/config/RichTextEditor/ToHTML.jsx +0 -262
  92. package/src/config/RichTextEditor/index.js +0 -25
@@ -9,6 +9,7 @@ import { Dropdown, Table, Checkbox } from 'semantic-ui-react';
9
9
  import trashSVG from '@plone/volto/icons/delete.svg';
10
10
  import ploneSVG from '@plone/volto/icons/plone.svg';
11
11
  import { Icon } from '@plone/volto/components';
12
+ import { canAssignRole } from '@plone/volto/helpers';
12
13
 
13
14
  /**
14
15
  * UsersControlpanelGroups class.
@@ -38,6 +39,7 @@ class RenderGroups extends Component {
38
39
  ).isRequired,
39
40
  inheritedRole: PropTypes.array,
40
41
  onDelete: PropTypes.func.isRequired,
42
+ isUserManager: PropTypes.bool.isRequired,
41
43
  };
42
44
 
43
45
  /**
@@ -69,6 +71,12 @@ class RenderGroups extends Component {
69
71
  isAuthGroup = (roleId) => {
70
72
  return this.props.inheritedRole.includes(roleId);
71
73
  };
74
+
75
+ canDeleteGroup() {
76
+ if (this.props.isUserManager) return true;
77
+ return !this.props.group.roles.includes('Manager');
78
+ }
79
+
72
80
  /**
73
81
  * Render method.
74
82
  * @method render
@@ -98,22 +106,25 @@ class RenderGroups extends Component {
98
106
  }
99
107
  onChange={this.onChange}
100
108
  value={`${this.props.group.id}&role=${role.id}`}
109
+ disabled={!canAssignRole(this.props.isUserManager, role)}
101
110
  />
102
111
  )}
103
112
  </Table.Cell>
104
113
  ))}
105
114
  <Table.Cell textAlign="right">
106
- <Dropdown icon="ellipsis horizontal">
107
- <Dropdown.Menu className="left">
108
- <Dropdown.Item
109
- onClick={this.props.onDelete}
110
- value={this.props.group['@id']}
111
- >
112
- <Icon name={trashSVG} size="15px" />
113
- <FormattedMessage id="Delete" defaultMessage="Delete" />
114
- </Dropdown.Item>
115
- </Dropdown.Menu>
116
- </Dropdown>
115
+ {this.canDeleteGroup() && (
116
+ <Dropdown icon="ellipsis horizontal">
117
+ <Dropdown.Menu className="left">
118
+ <Dropdown.Item
119
+ onClick={this.props.onDelete}
120
+ value={this.props.group['@id']}
121
+ >
122
+ <Icon name={trashSVG} size="15px" />
123
+ <FormattedMessage id="Delete" defaultMessage="Delete" />
124
+ </Dropdown.Item>
125
+ </Dropdown.Menu>
126
+ </Dropdown>
127
+ )}
117
128
  </Table.Cell>
118
129
  </Table.Row>
119
130
  );
@@ -49,6 +49,27 @@ describe('UsersControlpanelGroups', () => {
49
49
  group={testGroups}
50
50
  roles={testRoles}
51
51
  onDelete={() => {}}
52
+ isUserManager={true}
53
+ />
54
+ </Provider>,
55
+ );
56
+ const json = component.toJSON();
57
+ expect(json).toMatchSnapshot();
58
+ });
59
+ it('renders a UsersControlpanelGroups component with no Manager user', () => {
60
+ const store = mockStore({
61
+ intl: {
62
+ locale: 'en',
63
+ messages: {},
64
+ },
65
+ });
66
+ const component = renderer.create(
67
+ <Provider store={store}>
68
+ <RenderGroups
69
+ group={testGroups}
70
+ roles={testRoles}
71
+ onDelete={() => {}}
72
+ isUserManager={false}
52
73
  />
53
74
  </Provider>,
54
75
  );
@@ -13,7 +13,7 @@ import { updateUser } from '@plone/volto/actions';
13
13
  import ploneSVG from '@plone/volto/icons/plone.svg';
14
14
  import { compose } from 'redux';
15
15
  import { connect } from 'react-redux';
16
- import { messages } from '@plone/volto/helpers';
16
+ import { messages, canAssignRole } from '@plone/volto/helpers';
17
17
  import { toast } from 'react-toastify';
18
18
 
19
19
  /**
@@ -39,6 +39,7 @@ class RenderUsers extends Component {
39
39
  }),
40
40
  ).isRequired,
41
41
  onDelete: PropTypes.func.isRequired,
42
+ isUserManager: PropTypes.bool.isRequired,
42
43
  };
43
44
 
44
45
  /**
@@ -110,6 +111,11 @@ class RenderUsers extends Component {
110
111
  this.setState({ user: { ...formData } });
111
112
  }
112
113
 
114
+ canDeleteUser() {
115
+ if (this.props.isUserManager) return true;
116
+ return !this.props.user.roles.includes('Manager');
117
+ }
118
+
113
119
  /**
114
120
  * Render method.
115
121
  * @method render
@@ -139,35 +145,38 @@ class RenderUsers extends Component {
139
145
  checked={this.props.user.roles.includes(role.id)}
140
146
  onChange={this.onChange}
141
147
  value={`${this.props.user.id}&role=${role.id}`}
148
+ disabled={!canAssignRole(this.props.isUserManager, role)}
142
149
  />
143
150
  )}
144
151
  </Table.Cell>
145
152
  ))}
146
153
  <Table.Cell textAlign="right">
147
- <Dropdown icon="ellipsis horizontal">
148
- <Dropdown.Menu className="left">
149
- {this.props.userschema && (
154
+ {this.canDeleteUser() && (
155
+ <Dropdown icon="ellipsis horizontal">
156
+ <Dropdown.Menu className="left">
157
+ {this.props.userschema && (
158
+ <Dropdown.Item
159
+ id="edit-user-button"
160
+ onClick={() => {
161
+ this.onClickEdit({ formData: this.props.user });
162
+ }}
163
+ value={this.props.user['@id']}
164
+ >
165
+ <Icon name={editSVG} size="15px" />
166
+ <FormattedMessage id="Edit" defaultMessage="Edit" />
167
+ </Dropdown.Item>
168
+ )}
150
169
  <Dropdown.Item
151
- id="edit-user-button"
152
- onClick={() => {
153
- this.onClickEdit({ formData: this.props.user });
154
- }}
170
+ id="delete-user-button"
171
+ onClick={this.props.onDelete}
155
172
  value={this.props.user['@id']}
156
173
  >
157
- <Icon name={editSVG} size="15px" />
158
- <FormattedMessage id="Edit" defaultMessage="Edit" />
174
+ <Icon name={trashSVG} size="15px" />
175
+ <FormattedMessage id="Delete" defaultMessage="Delete" />
159
176
  </Dropdown.Item>
160
- )}
161
- <Dropdown.Item
162
- id="delete-user-button"
163
- onClick={this.props.onDelete}
164
- value={this.props.user['@id']}
165
- >
166
- <Icon name={trashSVG} size="15px" />
167
- <FormattedMessage id="Delete" defaultMessage="Delete" />
168
- </Dropdown.Item>
169
- </Dropdown.Menu>
170
- </Dropdown>
177
+ </Dropdown.Menu>
178
+ </Dropdown>
179
+ )}
171
180
  </Table.Cell>
172
181
  {Object.keys(this.state.user).length > 0 &&
173
182
  this.props.userschema.loaded && (
@@ -47,7 +47,33 @@ describe('UsersControlpanelUser', () => {
47
47
  });
48
48
  const component = renderer.create(
49
49
  <Provider store={store}>
50
- <RenderUsers user={testUser} roles={testRoles} onDelete={() => {}} />
50
+ <RenderUsers
51
+ user={testUser}
52
+ roles={testRoles}
53
+ onDelete={() => {}}
54
+ isUserManager={true}
55
+ />
56
+ </Provider>,
57
+ );
58
+ const json = component.toJSON();
59
+ expect(json).toMatchSnapshot();
60
+ });
61
+
62
+ it('renders a UsersControlpanelUser component with options disabled if not allowed', () => {
63
+ const store = mockStore({
64
+ intl: {
65
+ locale: 'en',
66
+ messages: {},
67
+ },
68
+ });
69
+ const component = renderer.create(
70
+ <Provider store={store}>
71
+ <RenderUsers
72
+ user={testUser}
73
+ roles={testRoles}
74
+ onDelete={() => {}}
75
+ isUserManager={false}
76
+ />
51
77
  </Provider>,
52
78
  );
53
79
  const json = component.toJSON();
@@ -1,11 +1,12 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { cloneDeep, uniqBy } from 'lodash';
3
3
  import { useIntl } from 'react-intl';
4
- import { useSelector, useDispatch } from 'react-redux';
4
+ import { useSelector, useDispatch, shallowEqual } from 'react-redux';
5
+ import jwtDecode from 'jwt-decode';
5
6
  import { toast } from 'react-toastify';
6
7
  import { Button, Checkbox } from 'semantic-ui-react';
7
- import { messages } from '@plone/volto/helpers';
8
- import { listGroups } from '@plone/volto/actions';
8
+ import { messages, isManager, canAssignGroup } from '@plone/volto/helpers';
9
+ import { listGroups, getUser } from '@plone/volto/actions';
9
10
  import { Icon, Toast } from '@plone/volto/components';
10
11
  import { updateGroup, listUsers } from '@plone/volto/actions';
11
12
 
@@ -25,6 +26,16 @@ const ListingTemplate = ({
25
26
  const pageSize = 25;
26
27
  const [userLimit, setUserLimit] = useState(pageSize);
27
28
 
29
+ const token = useSelector((state) => state.userSession.token, shallowEqual);
30
+ const user = useSelector((state) => state.users.user);
31
+ const userId = token ? jwtDecode(token).sub : '';
32
+
33
+ useEffect(() => {
34
+ dispatch(getUser(userId));
35
+ }, [dispatch, userId]);
36
+
37
+ const isUserManager = isManager(user);
38
+
28
39
  // y axis
29
40
  let items = useSelector((state) => state.users.users);
30
41
  let show_users =
@@ -51,12 +62,17 @@ const ListingTemplate = ({
51
62
 
52
63
  // x axis
53
64
  let groups = useSelector((state) => state.groups.groups);
65
+
66
+ const getRoles = (group_id) => {
67
+ return groups.find((group) => group.id === group_id)?.roles || [];
68
+ };
69
+
54
70
  let show_matrix_options =
55
71
  !many_groups ||
56
72
  (many_groups && query_group.length > 1) ||
57
73
  groups_filter.length > 0 ||
58
74
  add_joined_groups;
59
- let matrix_options; // list of Objects (value, label)
75
+ let matrix_options; // list of Objects (value, label, roles)
60
76
  if (show_matrix_options) {
61
77
  matrix_options =
62
78
  !many_groups || (many_groups && query_group.length > 1)
@@ -90,6 +106,10 @@ const ListingTemplate = ({
90
106
  }
91
107
  return 0;
92
108
  });
109
+ matrix_options = matrix_options.map((matrix_option) => ({
110
+ ...matrix_option,
111
+ roles: getRoles(matrix_option.value),
112
+ }));
93
113
  } else {
94
114
  matrix_options = [];
95
115
  }
@@ -126,7 +146,7 @@ const ListingTemplate = ({
126
146
  },
127
147
  }),
128
148
  )
129
- .then((resp) => {
149
+ .then(() => {
130
150
  singleClick &&
131
151
  dispatch(
132
152
  listUsers({
@@ -209,13 +229,14 @@ const ListingTemplate = ({
209
229
  <Checkbox
210
230
  className="toggle-target"
211
231
  defaultChecked={false}
212
- onChange={(event, { checked }) =>
232
+ onChange={(_event, { checked }) =>
213
233
  onSelectAllHandler(
214
234
  matrix_option.value,
215
235
  items.map((el) => el.id),
216
236
  checked,
217
237
  )
218
238
  }
239
+ disabled={!canAssignGroup(isUserManager, matrix_option)}
219
240
  />
220
241
  </div>
221
242
  ))}
@@ -251,13 +272,14 @@ const ListingTemplate = ({
251
272
  checked={item.groups?.items
252
273
  ?.map((el) => el.id)
253
274
  .includes(matrix_option.value)}
254
- onChange={(event, { checked }) => {
275
+ onChange={(_event, { checked }) => {
255
276
  onSelectOptionHandler(
256
277
  { y: matrix_option.value, x: item.id },
257
278
  checked,
258
279
  true,
259
280
  );
260
281
  }}
282
+ disabled={!canAssignGroup(isUserManager, matrix_option)}
261
283
  />
262
284
  ))}
263
285
  </div>
@@ -12,7 +12,9 @@ import {
12
12
  updateUser,
13
13
  updateGroup,
14
14
  getUserSchema,
15
+ getUser,
15
16
  } from '@plone/volto/actions';
17
+ import jwtDecode from 'jwt-decode';
16
18
  import {
17
19
  Icon,
18
20
  ModalForm,
@@ -23,7 +25,12 @@ import {
23
25
  Error,
24
26
  } from '@plone/volto/components';
25
27
  import { Link } from 'react-router-dom';
26
- import { Helmet, messages } from '@plone/volto/helpers';
28
+ import {
29
+ Helmet,
30
+ messages,
31
+ isManager,
32
+ canAssignGroup,
33
+ } from '@plone/volto/helpers';
27
34
  import clearSVG from '@plone/volto/icons/clear.svg';
28
35
  import addUserSvg from '@plone/volto/icons/add-user.svg';
29
36
  import saveSVG from '@plone/volto/icons/save.svg';
@@ -77,6 +84,19 @@ class UsersControlpanel extends Component {
77
84
  roles: PropTypes.arrayOf(PropTypes.string),
78
85
  }),
79
86
  ).isRequired,
87
+ user: PropTypes.shape({
88
+ '@id': PropTypes.string,
89
+ id: PropTypes.string,
90
+ description: PropTypes.string,
91
+ email: PropTypes.string,
92
+ fullname: PropTypes.string,
93
+ groups: PropTypes.object,
94
+ location: PropTypes.string,
95
+ portrait: PropTypes.string,
96
+ home_page: PropTypes.string,
97
+ roles: PropTypes.arrayOf(PropTypes.string),
98
+ username: PropTypes.string,
99
+ }).isRequired,
80
100
  };
81
101
 
82
102
  /**
@@ -124,6 +144,7 @@ class UsersControlpanel extends Component {
124
144
  });
125
145
  }
126
146
  await this.props.getUserSchema();
147
+ await this.props.getUser(this.props.userId);
127
148
  };
128
149
 
129
150
  // Because username field needs to be disabled if email login is enabled!
@@ -406,6 +427,20 @@ class UsersControlpanel extends Component {
406
427
  }
407
428
  }
408
429
 
430
+ /**
431
+ * Filters the roles a user can assign when adding a user.
432
+ * @method canAssignAdd
433
+ * @returns {arry}
434
+ */
435
+ canAssignAdd(isManager) {
436
+ if (isManager) return this.props.roles;
437
+ return this.props.user?.roles
438
+ ? this.props.roles.filter((role) =>
439
+ this.props.user.roles.includes(role.id),
440
+ )
441
+ : [];
442
+ }
443
+
409
444
  /**
410
445
  * Render method.
411
446
  * @method render
@@ -426,7 +461,9 @@ class UsersControlpanel extends Component {
426
461
  // of the userschema is changed and it is used like that through
427
462
  // the lifecycle of the application
428
463
  let adduserschema = {};
464
+ let isUserManager = false;
429
465
  if (this.props?.userschema?.loaded) {
466
+ isUserManager = isManager(this.props.user);
430
467
  adduserschema = JSON.parse(
431
468
  JSON.stringify(this.props?.userschema?.userschema),
432
469
  );
@@ -454,13 +491,18 @@ class UsersControlpanel extends Component {
454
491
  adduserschema.properties['roles'] = {
455
492
  title: this.props.intl.formatMessage(messages.addUserFormRolesTitle),
456
493
  type: 'array',
457
- choices: this.props.roles.map((role) => [role.id, role.title]),
494
+ choices: this.canAssignAdd(isUserManager).map((role) => [
495
+ role.id,
496
+ role.title,
497
+ ]),
458
498
  noValueOption: false,
459
499
  };
460
500
  adduserschema.properties['groups'] = {
461
501
  title: this.props.intl.formatMessage(messages.addUserGroupNameTitle),
462
502
  type: 'array',
463
- choices: this.props.groups.map((group) => [group.id, group.id]),
503
+ choices: this.props.groups
504
+ .filter((group) => canAssignGroup(isUserManager, group))
505
+ .map((group) => [group.id, group.id]),
464
506
  noValueOption: false,
465
507
  };
466
508
  if (
@@ -598,6 +640,7 @@ class UsersControlpanel extends Component {
598
640
  inheritedRole={this.props.inheritedRole}
599
641
  userschema={this.props.userschema}
600
642
  listUsers={this.props.listUsers}
643
+ isUserManager={isUserManager}
601
644
  />
602
645
  ))}
603
646
  </Table.Body>
@@ -686,6 +729,10 @@ export default compose(
686
729
  (state, props) => ({
687
730
  roles: state.roles.roles,
688
731
  users: state.users.users,
732
+ user: state.users.user,
733
+ userId: state.userSession.token
734
+ ? jwtDecode(state.userSession.token).sub
735
+ : '',
689
736
  groups: state.groups.groups,
690
737
  many_users: state.controlpanels?.controlpanel?.data?.many_users,
691
738
  many_groups: state.controlpanels?.controlpanel?.data?.many_groups,
@@ -710,6 +757,7 @@ export default compose(
710
757
  updateUser,
711
758
  updateGroup,
712
759
  getUserSchema,
760
+ getUser,
713
761
  },
714
762
  dispatch,
715
763
  ),
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { render } from '@testing-library/react';
3
3
  import configureStore from 'redux-mock-store';
4
4
  import { Provider } from 'react-intl-redux';
5
+ import jwt from 'jsonwebtoken';
5
6
 
6
7
  import UsersControlpanel from './UsersControlpanel';
7
8
 
@@ -11,10 +12,17 @@ jest.mock('../../Toolbar/Toolbar', () => jest.fn(() => <div id="Portal" />));
11
12
  describe('UsersControlpanel', () => {
12
13
  it('renders a user control component', () => {
13
14
  const store = mockStore({
15
+ userSession: {
16
+ token: jwt.sign({ sub: 'john' }, 'secret'),
17
+ },
14
18
  roles: { roles: [] },
15
19
  users: {
16
20
  users: [],
17
21
  create: { loading: false },
22
+ user: {
23
+ roles: ['Manager'],
24
+ '@id': 'admin',
25
+ },
18
26
  },
19
27
  groups: {
20
28
  groups: [],
@@ -1,22 +1,19 @@
1
1
  import { defineMessages } from 'react-intl';
2
+ import { cloneDeep } from 'lodash';
2
3
 
3
4
  import ViewTitleBlock from '@plone/volto/components/manage/Blocks/Title/View';
4
5
  import ViewDescriptionBlock from '@plone/volto/components/manage/Blocks/Description/View';
5
6
  import ViewToCBlock from '@plone/volto/components/manage/Blocks/ToC/View';
6
- import ViewTextBlock from '@plone/volto/components/manage/Blocks/Text/View';
7
7
  import ViewImageBlock from '@plone/volto/components/manage/Blocks/Image/View';
8
8
  import ViewLeadImageBlock from '@plone/volto/components/manage/Blocks/LeadImage/View';
9
9
  import ViewListingBlock from '@plone/volto/components/manage/Blocks/Listing/View';
10
10
  import ViewVideoBlock from '@plone/volto/components/manage/Blocks/Video/View';
11
- import ViewHeroImageLeftBlock from '@plone/volto/components/manage/Blocks/HeroImageLeft/View';
12
11
  import ViewMapBlock from '@plone/volto/components/manage/Blocks/Maps/View';
13
12
  import ViewHTMLBlock from '@plone/volto/components/manage/Blocks/HTML/View';
14
- import ViewTableBlock from '@plone/volto/components/manage/Blocks/Table/View';
15
13
 
16
14
  import EditTitleBlock from '@plone/volto/components/manage/Blocks/Title/Edit';
17
15
  import EditDescriptionBlock from '@plone/volto/components/manage/Blocks/Description/Edit';
18
16
  import EditToCBlock from '@plone/volto/components/manage/Blocks/ToC/Edit';
19
- import EditTextBlock from '@plone/volto/components/manage/Blocks/Text/Edit';
20
17
  import EditImageBlock from '@plone/volto/components/manage/Blocks/Image/Edit';
21
18
  import EditLeadImageBlock from '@plone/volto/components/manage/Blocks/LeadImage/Edit';
22
19
  import EditListingBlock from '@plone/volto/components/manage/Blocks/Listing/Edit';
@@ -25,20 +22,15 @@ import GalleryNoResultsComponent from '@plone/volto/components/manage/Blocks/Lis
25
22
  import DefaultListingBlockTemplate from '@plone/volto/components/manage/Blocks/Listing/DefaultTemplate';
26
23
  import SummaryListingBlockTemplate from '@plone/volto/components/manage/Blocks/Listing/SummaryTemplate';
27
24
  import EditVideoBlock from '@plone/volto/components/manage/Blocks/Video/Edit';
28
- import EditHeroImageLeftBlock from '@plone/volto/components/manage/Blocks/HeroImageLeft/Edit';
29
25
  import EditMapBlock from '@plone/volto/components/manage/Blocks/Maps/Edit';
30
26
  import EditHTMLBlock from '@plone/volto/components/manage/Blocks/HTML/Edit';
31
- import EditTableBlock from '@plone/volto/components/manage/Blocks/Table/Edit';
32
27
 
33
28
  import descriptionSVG from '@plone/volto/icons/description.svg';
34
29
  import titleSVG from '@plone/volto/icons/text.svg';
35
- import textSVG from '@plone/volto/icons/subtext.svg';
36
30
  import cameraSVG from '@plone/volto/icons/camera.svg';
37
31
  import videoSVG from '@plone/volto/icons/videocamera.svg';
38
32
  import globeSVG from '@plone/volto/icons/globe.svg';
39
33
  import codeSVG from '@plone/volto/icons/code.svg';
40
- import heroSVG from '@plone/volto/icons/hero.svg';
41
- import tableSVG from '@plone/volto/icons/table.svg';
42
34
  import listingBlockSVG from '@plone/volto/icons/content-listing.svg';
43
35
  import tocSVG from '@plone/volto/icons/list-bullet.svg';
44
36
  import searchSVG from '@plone/volto/icons/zoom.svg';
@@ -47,7 +39,6 @@ import imagesSVG from '@plone/volto/icons/images.svg';
47
39
 
48
40
  import ImageGalleryListingBlockTemplate from '@plone/volto/components/manage/Blocks/Listing/ImageGallery';
49
41
  import BlockSettingsSchema from '@plone/volto/components/manage/Blocks/Block/Schema';
50
- import TextSettingsSchema from '@plone/volto/components/manage/Blocks/Text/Schema';
51
42
  import ImageSettingsSchema from '@plone/volto/components/manage/Blocks/Image/LayoutSchema';
52
43
  import ToCSettingsSchema from '@plone/volto/components/manage/Blocks/ToC/Schema';
53
44
 
@@ -79,7 +70,6 @@ import { getImageBlockSizes } from '@plone/volto/components/manage/Blocks/Image/
79
70
  import { getLeadImageBlockSizes } from '@plone/volto/components/manage/Blocks/LeadImage/utils';
80
71
 
81
72
  // block sidebar schemas (not the Dexterity Layout block settings schemas)
82
- import HeroImageLeftBlockSchema from '@plone/volto/components/manage/Blocks/HeroImageLeft/schema';
83
73
  import ListingBlockSchema from '@plone/volto/components/manage/Blocks/Listing/schema';
84
74
  import SearchBlockSchema from '@plone/volto/components/manage/Blocks/Search/schema';
85
75
 
@@ -191,6 +181,63 @@ defineMessages({
191
181
  id: 'toggleFacet',
192
182
  defaultMessage: 'Toggle',
193
183
  },
184
+ // BBB Table messages
185
+ Table: {
186
+ id: 'Table',
187
+ defaultMessage: 'Table',
188
+ },
189
+ cell: {
190
+ id: 'Cell',
191
+ defaultMessage: 'Cell',
192
+ },
193
+ insertRowBefore: {
194
+ id: 'Insert row before',
195
+ defaultMessage: 'Insert row before',
196
+ },
197
+ insertRowAfter: {
198
+ id: 'Insert row after',
199
+ defaultMessage: 'Insert row after',
200
+ },
201
+ deleteRow: {
202
+ id: 'Delete row',
203
+ defaultMessage: 'Delete row',
204
+ },
205
+ insertColBefore: {
206
+ id: 'Insert col before',
207
+ defaultMessage: 'Insert col before',
208
+ },
209
+ insertColAfter: {
210
+ id: 'Insert col after',
211
+ defaultMessage: 'Insert col after',
212
+ },
213
+ deleteCol: {
214
+ id: 'Delete col',
215
+ defaultMessage: 'Delete col',
216
+ },
217
+ fixed: {
218
+ id: 'Fixed width table cells',
219
+ defaultMessage: 'Fixed width columns',
220
+ },
221
+ compact: {
222
+ id: 'Make the table compact',
223
+ defaultMessage: 'Reduce cell padding',
224
+ },
225
+ basic: {
226
+ id: 'Reduce complexity',
227
+ defaultMessage: 'Minimalistic table design',
228
+ },
229
+ celled: {
230
+ id: 'Divide each row into separate cells',
231
+ defaultMessage: 'Add border to inner columns',
232
+ },
233
+ striped: {
234
+ id: 'Stripe alternate rows with color',
235
+ defaultMessage: 'Alternate row background color',
236
+ },
237
+ headerCell: {
238
+ id: 'Header cell',
239
+ defaultMessage: 'Header cell',
240
+ },
194
241
  });
195
242
 
196
243
  const groupBlocksOrder = [
@@ -230,25 +277,6 @@ const blocksConfig = {
230
277
  blockHasOwnFocusManagement: true,
231
278
  sidebarTab: 0,
232
279
  },
233
- text: {
234
- id: 'text',
235
- title: 'Text',
236
- icon: textSVG,
237
- group: 'text',
238
- view: ViewTextBlock,
239
- edit: EditTextBlock,
240
- schema: TextSettingsSchema,
241
- restricted: false,
242
- mostUsed: false,
243
- blockHasOwnFocusManagement: true,
244
- sidebarTab: 0,
245
- blockHasValue: (data) => {
246
- const isEmpty =
247
- !data.text ||
248
- (data.text?.blocks?.length === 1 && data.text.blocks[0].text === '');
249
- return !isEmpty;
250
- },
251
- },
252
280
  image: {
253
281
  id: 'image',
254
282
  title: 'Image',
@@ -335,21 +363,6 @@ const blocksConfig = {
335
363
  mostUsed: false,
336
364
  sidebarTab: 1,
337
365
  },
338
- hero: {
339
- id: 'hero',
340
- title: 'Hero',
341
- icon: heroSVG,
342
- group: 'common',
343
- view: ViewHeroImageLeftBlock,
344
- edit: EditHeroImageLeftBlock,
345
- schema: BlockSettingsSchema,
346
- blockSchema: HeroImageLeftBlockSchema,
347
- restricted: false,
348
- mostUsed: false,
349
- blockHasOwnFocusManagement: true,
350
- sidebarTab: 1,
351
- },
352
-
353
366
  maps: {
354
367
  id: 'maps',
355
368
  title: 'Maps',
@@ -374,19 +387,6 @@ const blocksConfig = {
374
387
  mostUsed: false,
375
388
  sidebarTab: 0,
376
389
  },
377
- table: {
378
- id: 'table',
379
- title: 'Table',
380
- icon: tableSVG,
381
- group: 'common',
382
- view: ViewTableBlock,
383
- edit: EditTableBlock,
384
- schema: BlockSettingsSchema,
385
- restricted: false,
386
- mostUsed: false,
387
- blockHasOwnFocusManagement: true,
388
- sidebarTab: 1,
389
- },
390
390
  search: {
391
391
  id: 'search',
392
392
  title: 'Search',
@@ -519,15 +519,11 @@ const blocksConfig = {
519
519
  // for the grid block, since we need to modify how the inner teaser
520
520
  // block behave in it (= no schemaEnhancer fields for teasers inside a grid)
521
521
  // Afterwards, it can be further customized in add-ons using the same technique.
522
- blocksConfig.gridBlock.blocksConfig = { ...blocksConfig };
523
- blocksConfig.gridBlock.blocksConfig.teaser = {
524
- ...blocksConfig.teaser,
525
- schemaEnhancer: gridTeaserDisableStylingSchema,
526
- };
527
- blocksConfig.gridBlock.blocksConfig.image = {
528
- ...blocksConfig.image,
529
- schemaEnhancer: gridImageDisableSizeAndPositionHandlersSchema,
530
- };
522
+ blocksConfig.gridBlock.blocksConfig = cloneDeep(blocksConfig);
523
+ blocksConfig.gridBlock.blocksConfig.teaser.schemaEnhancer =
524
+ gridTeaserDisableStylingSchema;
525
+ blocksConfig.gridBlock.blocksConfig.image.schemaEnhancer =
526
+ gridImageDisableSizeAndPositionHandlersSchema;
531
527
 
532
528
  const requiredBlocks = ['title'];
533
529