@plone/volto 18.0.0-alpha.17 → 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.
- package/CHANGELOG.md +48 -2
- package/locales/ca/LC_MESSAGES/volto.po +17 -42
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +15 -40
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +14 -39
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +15 -40
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +15 -40
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +15 -40
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +15 -40
- package/locales/fr.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +15 -40
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +15 -40
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +14 -39
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +15 -40
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +15 -40
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +15 -40
- package/locales/ro.json +1 -1
- package/locales/volto.pot +15 -40
- package/locales/zh_CN/LC_MESSAGES/volto.po +15 -40
- package/locales/zh_CN.json +1 -1
- package/package.json +11 -21
- package/src/components/index.js +0 -6
- package/src/components/manage/Add/Add.jsx +1 -1
- package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +8 -2
- package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +2 -2
- package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +33 -5
- package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.test.jsx +12 -0
- package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +22 -11
- package/src/components/manage/Controlpanels/Groups/RenderGroups.test.jsx +21 -0
- package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +30 -21
- package/src/components/manage/Controlpanels/Users/RenderUsers.test.jsx +27 -1
- package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +29 -7
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +51 -3
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.test.jsx +8 -0
- package/src/components/manage/Form/Form.jsx +1 -1
- package/src/components/theme/SlotRenderer/SlotRenderer.tsx +8 -1
- package/src/config/Blocks.jsx +63 -67
- package/src/config/Loadables.jsx +0 -22
- package/src/config/Widgets.jsx +0 -2
- package/src/config/index.js +0 -13
- package/src/helpers/User/User.js +29 -0
- package/src/helpers/index.js +6 -1
- package/test-setup-config.js +0 -30
- package/types/components/index.d.ts +0 -6
- package/types/config/Blocks.d.ts +0 -51
- package/types/config/Loadables.d.ts +0 -10
- package/types/config/Widgets.d.ts +0 -2
- package/types/helpers/User/User.d.ts +18 -0
- package/types/helpers/index.d.ts +1 -1
- package/webpack-plugins/webpack-bundle-analyze-plugin.js +1 -1
- package/src/components/manage/AnchorPlugin/components/Link/index.jsx +0 -37
- package/src/components/manage/AnchorPlugin/components/LinkButton/index.jsx +0 -126
- package/src/components/manage/AnchorPlugin/index.jsx +0 -82
- package/src/components/manage/AnchorPlugin/linkStrategy.js +0 -21
- package/src/components/manage/AnchorPlugin/utils/EditorUtils.js +0 -47
- package/src/components/manage/Blocks/HeroImageLeft/Data.jsx +0 -29
- package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +0 -493
- package/src/components/manage/Blocks/HeroImageLeft/Edit.test.jsx +0 -58
- package/src/components/manage/Blocks/HeroImageLeft/View.jsx +0 -37
- package/src/components/manage/Blocks/HeroImageLeft/View.test.jsx +0 -9
- package/src/components/manage/Blocks/HeroImageLeft/schema.js +0 -43
- package/src/components/manage/Blocks/Table/Cell.jsx +0 -206
- package/src/components/manage/Blocks/Table/Cell.test.jsx +0 -19
- package/src/components/manage/Blocks/Table/Edit.jsx +0 -748
- package/src/components/manage/Blocks/Table/Edit.test.jsx +0 -44
- package/src/components/manage/Blocks/Table/Readme.md +0 -5
- package/src/components/manage/Blocks/Table/View.jsx +0 -51
- package/src/components/manage/Blocks/Table/View.test.jsx +0 -41
- package/src/components/manage/Blocks/Text/Edit.jsx +0 -372
- package/src/components/manage/Blocks/Text/Edit.test.jsx +0 -46
- package/src/components/manage/Blocks/Text/Readme.md +0 -5
- package/src/components/manage/Blocks/Text/Schema.jsx +0 -31
- package/src/components/manage/Blocks/Text/View.jsx +0 -26
- package/src/components/manage/Blocks/Text/View.test.jsx +0 -28
- package/src/components/manage/LinkDetectionPlugin/link-detection-plugin.jsx +0 -227
- package/src/components/manage/LinkDetectionPlugin/utils.js +0 -12
- package/src/components/manage/Widgets/WysiwygWidget.jsx +0 -350
- package/src/components/manage/Widgets/WysiwygWidget.stories.jsx +0 -24
- package/src/components/manage/Widgets/WysiwygWidget.test.jsx +0 -37
- package/src/config/RichTextEditor/Blocks.jsx +0 -29
- package/src/config/RichTextEditor/FromHTML.jsx +0 -8
- package/src/config/RichTextEditor/Plugins.jsx +0 -59
- package/src/config/RichTextEditor/Styles.jsx +0 -69
- package/src/config/RichTextEditor/ToHTML.jsx +0 -262
- 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
|
-
|
|
107
|
-
<Dropdown
|
|
108
|
-
<Dropdown.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
148
|
-
<Dropdown
|
|
149
|
-
|
|
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="
|
|
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={
|
|
158
|
-
<FormattedMessage id="
|
|
174
|
+
<Icon name={trashSVG} size="15px" />
|
|
175
|
+
<FormattedMessage id="Delete" defaultMessage="Delete" />
|
|
159
176
|
</Dropdown.Item>
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
|
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((
|
|
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={(
|
|
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={(
|
|
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 {
|
|
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.
|
|
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
|
|
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: [],
|
|
@@ -309,7 +309,7 @@ class Form extends Component {
|
|
|
309
309
|
// Set focus to first input if available
|
|
310
310
|
document
|
|
311
311
|
.querySelector(`.field-wrapper-${this.props.metadataFieldFocus} input`)
|
|
312
|
-
|
|
312
|
+
?.focus();
|
|
313
313
|
|
|
314
314
|
// Reset focus field
|
|
315
315
|
this.props.resetMetadataFocus();
|
|
@@ -44,7 +44,14 @@ const SlotRenderer = ({
|
|
|
44
44
|
}) => {
|
|
45
45
|
// ^^ Weird compilation issue for Jest tests, that forced to re-declare the type above
|
|
46
46
|
const SlotComponent = component;
|
|
47
|
-
return
|
|
47
|
+
return (
|
|
48
|
+
<SlotComponent
|
|
49
|
+
key={name}
|
|
50
|
+
content={content}
|
|
51
|
+
pathname={pathname}
|
|
52
|
+
navRoot={navRoot}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
48
55
|
},
|
|
49
56
|
)}
|
|
50
57
|
</>
|