@plone/volto 18.29.0 → 18.30.0
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/.eslintignore +1 -0
- package/.eslintrc +26 -0
- package/CHANGELOG.md +32 -0
- package/cypress/support/guillotina.js +1 -0
- package/cypress.config.js +1 -0
- package/locales/af/LC_MESSAGES/volto.po +5 -0
- package/locales/af.json +1 -1
- package/locales/ar/LC_MESSAGES/volto.po +5 -0
- package/locales/ar.json +1 -1
- package/locales/bg/LC_MESSAGES/volto.po +5 -0
- package/locales/bg.json +1 -1
- package/locales/bn/LC_MESSAGES/volto.po +5 -0
- package/locales/bn.json +1 -1
- package/locales/ca/LC_MESSAGES/volto.po +5 -0
- package/locales/ca.json +1 -1
- package/locales/cs/LC_MESSAGES/volto.po +5 -0
- package/locales/cs.json +1 -1
- package/locales/cy/LC_MESSAGES/volto.po +5 -0
- package/locales/cy.json +1 -1
- package/locales/da/LC_MESSAGES/volto.po +5 -0
- package/locales/da.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +5 -0
- package/locales/de.json +1 -1
- package/locales/el/LC_MESSAGES/volto.po +5 -0
- package/locales/el.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +5 -0
- package/locales/en.json +1 -1
- package/locales/en_AU/LC_MESSAGES/volto.po +5 -0
- package/locales/en_AU.json +1 -1
- package/locales/en_GB/LC_MESSAGES/volto.po +5 -0
- package/locales/en_GB.json +1 -1
- package/locales/eo/LC_MESSAGES/volto.po +5 -0
- package/locales/eo.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +19 -14
- package/locales/es.json +1 -1
- package/locales/et/LC_MESSAGES/volto.po +5 -0
- package/locales/et.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +8 -3
- package/locales/eu.json +1 -1
- package/locales/fa/LC_MESSAGES/volto.po +5 -0
- package/locales/fa.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +5 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +5 -0
- package/locales/fr.json +1 -1
- package/locales/fu/LC_MESSAGES/volto.po +5 -0
- package/locales/fu.json +1 -1
- package/locales/gl/LC_MESSAGES/volto.po +5 -0
- package/locales/gl.json +1 -1
- package/locales/he/LC_MESSAGES/volto.po +5 -0
- package/locales/he.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +5 -0
- package/locales/hi.json +1 -1
- package/locales/hr/LC_MESSAGES/volto.po +5 -0
- package/locales/hr.json +1 -1
- package/locales/hu/LC_MESSAGES/volto.po +5 -0
- package/locales/hu.json +1 -1
- package/locales/hy/LC_MESSAGES/volto.po +5 -0
- package/locales/hy.json +1 -1
- package/locales/id/LC_MESSAGES/volto.po +5 -0
- package/locales/id.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +5 -0
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +5 -0
- package/locales/ja.json +1 -1
- package/locales/ka/LC_MESSAGES/volto.po +5 -0
- package/locales/ka.json +1 -1
- package/locales/kn/LC_MESSAGES/volto.po +5 -0
- package/locales/kn.json +1 -1
- package/locales/ko/LC_MESSAGES/volto.po +5 -0
- package/locales/ko.json +1 -1
- package/locales/lt/LC_MESSAGES/volto.po +5 -0
- package/locales/lt.json +1 -1
- package/locales/lv/LC_MESSAGES/volto.po +5 -0
- package/locales/lv.json +1 -1
- package/locales/mi/LC_MESSAGES/volto.po +5 -0
- package/locales/mi.json +1 -1
- package/locales/mk/LC_MESSAGES/volto.po +5 -0
- package/locales/mk.json +1 -1
- package/locales/my/LC_MESSAGES/volto.po +5 -0
- package/locales/my.json +1 -1
- package/locales/nb_NO/LC_MESSAGES/volto.po +5 -0
- package/locales/nb_NO.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +8 -3
- package/locales/nl.json +1 -1
- package/locales/nn/LC_MESSAGES/volto.po +5 -0
- package/locales/nn.json +1 -1
- package/locales/pl/LC_MESSAGES/volto.po +5 -0
- package/locales/pl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +5 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +5 -0
- package/locales/pt_BR.json +1 -1
- package/locales/rm/LC_MESSAGES/volto.po +5 -0
- package/locales/rm.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +5 -0
- package/locales/ro.json +1 -1
- package/locales/ru/LC_MESSAGES/volto.po +5 -0
- package/locales/ru.json +1 -1
- package/locales/sk/LC_MESSAGES/volto.po +5 -0
- package/locales/sk.json +1 -1
- package/locales/sl/LC_MESSAGES/volto.po +5 -0
- package/locales/sl.json +1 -1
- package/locales/sm/LC_MESSAGES/volto.po +5 -0
- package/locales/sm.json +1 -1
- package/locales/sq/LC_MESSAGES/volto.po +5 -0
- package/locales/sq.json +1 -1
- package/locales/sr/LC_MESSAGES/volto.po +5 -0
- package/locales/sr.json +1 -1
- package/locales/sr@cyrl/LC_MESSAGES/volto.po +5 -0
- package/locales/sr@cyrl.json +1 -1
- package/locales/sr@latn/LC_MESSAGES/volto.po +5 -0
- package/locales/sr@latn.json +1 -1
- package/locales/sv/LC_MESSAGES/volto.po +5 -0
- package/locales/sv.json +1 -1
- package/locales/ta/LC_MESSAGES/volto.po +59 -53
- package/locales/ta.json +1 -1
- package/locales/te/LC_MESSAGES/volto.po +5 -0
- package/locales/te.json +1 -1
- package/locales/th/LC_MESSAGES/volto.po +5 -0
- package/locales/th.json +1 -1
- package/locales/to/LC_MESSAGES/volto.po +5 -0
- package/locales/to.json +1 -1
- package/locales/tr/LC_MESSAGES/volto.po +5 -0
- package/locales/tr.json +1 -1
- package/locales/uk/LC_MESSAGES/volto.po +5 -0
- package/locales/uk.json +1 -1
- package/locales/vi/LC_MESSAGES/volto.po +5 -0
- package/locales/vi.json +1 -1
- package/locales/volto.pot +6 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +5 -0
- package/locales/zh_CN.json +1 -1
- package/locales/zh_Hant/LC_MESSAGES/volto.po +5 -0
- package/locales/zh_Hant.json +1 -1
- package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +5 -0
- package/locales/zh_Hant_HK.json +1 -1
- package/news/7428.feat +1 -0
- package/package.json +12 -11
- package/src/components/manage/BlockChooser/BlockChooser.jsx +1 -0
- package/src/components/manage/Controlpanels/Relations/Relations.jsx +1 -1
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +575 -630
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.test.jsx +4 -3
- package/src/components/manage/Sidebar/SidebarPortal.test.tsx +42 -0
- package/src/components/manage/Sidebar/SidebarPortal.tsx +48 -0
- package/src/components/manage/Toolbar/Toolbar.jsx +14 -4
- package/src/components/manage/Widgets/ImageWidget.jsx +11 -4
- package/src/helpers/Extensions/withBlockSchemaEnhancer.jsx +4 -1
- package/src/helpers/Url/Url.js +1 -0
- package/theme/themes/pastanaga/extras/toolbar.less +22 -5
- package/types/components/manage/Controlpanels/Users/UsersControlpanel.d.ts +6 -2
- package/types/components/manage/Controlpanels/index.d.ts +1 -1
- package/types/components/manage/Sidebar/SidebarPortal.d.ts +7 -15
- package/types/helpers/Extensions/withBlockSchemaEnhancer.d.ts +4 -5
- package/src/components/manage/Sidebar/SidebarPortal.jsx +0 -47
- package/src/components/manage/Sidebar/SidebarPortal.test.jsx +0 -26
|
@@ -33,13 +33,13 @@ import find from 'lodash/find';
|
|
|
33
33
|
import map from 'lodash/map';
|
|
34
34
|
import pull from 'lodash/pull';
|
|
35
35
|
import difference from 'lodash/difference';
|
|
36
|
-
|
|
37
|
-
import
|
|
38
|
-
import { FormattedMessage,
|
|
36
|
+
|
|
37
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
38
|
+
import { FormattedMessage, useIntl } from 'react-intl';
|
|
39
39
|
import { createPortal } from 'react-dom';
|
|
40
|
-
import {
|
|
40
|
+
import { useSelector, useDispatch } from 'react-redux';
|
|
41
|
+
import { useLocation } from 'react-router-dom';
|
|
41
42
|
import { toast } from 'react-toastify';
|
|
42
|
-
import { bindActionCreators, compose } from 'redux';
|
|
43
43
|
import {
|
|
44
44
|
Confirm,
|
|
45
45
|
Container,
|
|
@@ -53,151 +53,132 @@ import {
|
|
|
53
53
|
} from 'semantic-ui-react';
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
* UsersControlpanel
|
|
57
|
-
* @
|
|
58
|
-
* @extends Component
|
|
56
|
+
* UsersControlpanel functional component.
|
|
57
|
+
* @function UsersControlpanel
|
|
59
58
|
*/
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
* @property {Object} propTypes Property types.
|
|
64
|
-
* @static
|
|
65
|
-
*/
|
|
66
|
-
static propTypes = {
|
|
67
|
-
listRoles: PropTypes.func.isRequired,
|
|
68
|
-
listUsers: PropTypes.func.isRequired,
|
|
69
|
-
updateUser: PropTypes.func,
|
|
70
|
-
listGroups: PropTypes.func.isRequired,
|
|
71
|
-
pathname: PropTypes.string.isRequired,
|
|
72
|
-
roles: PropTypes.arrayOf(
|
|
73
|
-
PropTypes.shape({
|
|
74
|
-
'@id': PropTypes.string,
|
|
75
|
-
'@type': PropTypes.string,
|
|
76
|
-
id: PropTypes.string,
|
|
77
|
-
}),
|
|
78
|
-
).isRequired,
|
|
79
|
-
users: PropTypes.arrayOf(
|
|
80
|
-
PropTypes.shape({
|
|
81
|
-
username: PropTypes.string,
|
|
82
|
-
fullname: PropTypes.string,
|
|
83
|
-
roles: PropTypes.arrayOf(PropTypes.string),
|
|
84
|
-
}),
|
|
85
|
-
).isRequired,
|
|
86
|
-
user: PropTypes.shape({
|
|
87
|
-
'@id': PropTypes.string,
|
|
88
|
-
id: PropTypes.string,
|
|
89
|
-
description: PropTypes.string,
|
|
90
|
-
email: PropTypes.string,
|
|
91
|
-
fullname: PropTypes.string,
|
|
92
|
-
groups: PropTypes.object,
|
|
93
|
-
location: PropTypes.string,
|
|
94
|
-
portrait: PropTypes.string,
|
|
95
|
-
home_page: PropTypes.string,
|
|
96
|
-
roles: PropTypes.arrayOf(PropTypes.string),
|
|
97
|
-
username: PropTypes.string,
|
|
98
|
-
}).isRequired,
|
|
99
|
-
};
|
|
59
|
+
const UsersControlpanel = () => {
|
|
60
|
+
const intl = useIntl();
|
|
61
|
+
const dispatch = useDispatch();
|
|
100
62
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
this.onDeleteOk = this.onDeleteOk.bind(this);
|
|
114
|
-
this.onDeleteCancel = this.onDeleteCancel.bind(this);
|
|
115
|
-
this.onAddUserSubmit = this.onAddUserSubmit.bind(this);
|
|
116
|
-
this.onAddUserError = this.onAddUserError.bind(this);
|
|
117
|
-
this.onAddUserSuccess = this.onAddUserSuccess.bind(this);
|
|
118
|
-
this.updateUserRole = this.updateUserRole.bind(this);
|
|
119
|
-
this.state = {
|
|
120
|
-
search: '',
|
|
121
|
-
isLoading: false,
|
|
122
|
-
showAddUser: false,
|
|
123
|
-
showAddUserErrorConfirm: false,
|
|
124
|
-
addUserError: '',
|
|
125
|
-
showDelete: false,
|
|
126
|
-
userToDelete: undefined,
|
|
127
|
-
entries: [],
|
|
128
|
-
isClient: false,
|
|
129
|
-
currentPage: 0,
|
|
130
|
-
pageSize: 10,
|
|
131
|
-
loginUsingEmail: false,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
63
|
+
// Redux state selectors
|
|
64
|
+
const roles = useSelector((state) => state.roles.roles);
|
|
65
|
+
const users = useSelector((state) => state.users.users);
|
|
66
|
+
const user = useSelector((state) => state.users.user);
|
|
67
|
+
const userId = useSelector((state) =>
|
|
68
|
+
state.userSession.token ? jwtDecode(state.userSession.token).sub : '',
|
|
69
|
+
);
|
|
70
|
+
const groups = useSelector((state) => state.groups.groups);
|
|
71
|
+
const many_users = useSelector(
|
|
72
|
+
(state) => state.controlpanels?.controlpanel?.data?.many_users,
|
|
73
|
+
);
|
|
134
74
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
};
|
|
75
|
+
const location = useLocation();
|
|
76
|
+
const pathname = location.pathname;
|
|
77
|
+
const deleteRequest = useSelector((state) => state.users.delete);
|
|
78
|
+
const createRequest = useSelector((state) => state.users.create);
|
|
79
|
+
const loadRolesRequest = useSelector((state) => state.roles);
|
|
80
|
+
const inheritedRole = useSelector(
|
|
81
|
+
(state) => state.authRole.authenticatedRole,
|
|
82
|
+
);
|
|
83
|
+
const userschema = useSelector((state) => state.userschema);
|
|
84
|
+
const controlPanelData = useSelector(
|
|
85
|
+
(state) => state.controlpanels?.controlpanel,
|
|
86
|
+
);
|
|
148
87
|
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
88
|
+
// Action creators
|
|
89
|
+
const listRolesAction = useCallback(() => dispatch(listRoles()), [dispatch]);
|
|
90
|
+
const listUsersAction = useCallback(
|
|
91
|
+
(params) => dispatch(listUsers(params)),
|
|
92
|
+
[dispatch],
|
|
93
|
+
);
|
|
94
|
+
const listGroupsAction = useCallback(
|
|
95
|
+
() => dispatch(listGroups()),
|
|
96
|
+
[dispatch],
|
|
97
|
+
);
|
|
98
|
+
const getControlpanelAction = useCallback(
|
|
99
|
+
(panel) => dispatch(getControlpanel(panel)),
|
|
100
|
+
[dispatch],
|
|
101
|
+
);
|
|
102
|
+
const deleteUserAction = useCallback(
|
|
103
|
+
(userId) => dispatch(deleteUser(userId)),
|
|
104
|
+
[dispatch],
|
|
105
|
+
);
|
|
106
|
+
const updateUserAction = useCallback(
|
|
107
|
+
(userId, data) => dispatch(updateUser(userId, data)),
|
|
108
|
+
[dispatch],
|
|
109
|
+
);
|
|
110
|
+
const updateGroupAction = useCallback(
|
|
111
|
+
(groupId, data) => dispatch(updateGroup(groupId, data)),
|
|
112
|
+
[dispatch],
|
|
113
|
+
);
|
|
114
|
+
const getUserSchemaAction = useCallback(
|
|
115
|
+
() => dispatch(getUserSchema()),
|
|
116
|
+
[dispatch],
|
|
117
|
+
);
|
|
118
|
+
const getUserAction = useCallback(
|
|
119
|
+
(userId) => dispatch(getUser(userId)),
|
|
120
|
+
[dispatch],
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const [search, setSearch] = useState('');
|
|
124
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
125
|
+
const [showAddUser, setShowAddUser] = useState(false);
|
|
126
|
+
const [addUserError, setAddUserError] = useState('');
|
|
127
|
+
const [showDelete, setShowDelete] = useState(false);
|
|
128
|
+
const [userToDelete, setUserToDelete] = useState(undefined);
|
|
129
|
+
const [entries, setEntries] = useState([]);
|
|
130
|
+
const [isClient, setIsClient] = useState(false);
|
|
131
|
+
const [currentPage, setCurrentPage] = useState(0);
|
|
132
|
+
const [pageSize] = useState(10);
|
|
133
|
+
// eslint-disable-next-line no-unused-vars
|
|
134
|
+
const [loginUsingEmail, setLoginUsingEmail] = useState(false); // Reserved for future use to disable username field when email login is enabled
|
|
135
|
+
const [error, setError] = useState(null);
|
|
136
|
+
|
|
137
|
+
const fetchData = useCallback(async () => {
|
|
138
|
+
await getControlpanelAction('usergroup');
|
|
139
|
+
await listRolesAction();
|
|
140
|
+
if (!many_users) {
|
|
141
|
+
listGroupsAction();
|
|
142
|
+
await listUsersAction();
|
|
143
|
+
setEntries(users);
|
|
144
|
+
}
|
|
145
|
+
await getUserSchemaAction();
|
|
146
|
+
await getUserAction(userId);
|
|
147
|
+
}, [
|
|
148
|
+
getControlpanelAction,
|
|
149
|
+
listRolesAction,
|
|
150
|
+
many_users,
|
|
151
|
+
listGroupsAction,
|
|
152
|
+
listUsersAction,
|
|
153
|
+
users,
|
|
154
|
+
getUserSchemaAction,
|
|
155
|
+
getUserAction,
|
|
156
|
+
userId,
|
|
157
|
+
]);
|
|
156
158
|
|
|
157
159
|
/**
|
|
158
|
-
*
|
|
159
|
-
* @method
|
|
160
|
+
* Check login using email status from security control panel
|
|
161
|
+
* @method checkLoginUsingEmailStatus
|
|
160
162
|
* @returns {undefined}
|
|
161
163
|
*/
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
(this.props.deleteRequest.loading && nextProps.deleteRequest.loaded) ||
|
|
173
|
-
(this.props.createRequest.loading && nextProps.createRequest.loaded)
|
|
174
|
-
) {
|
|
175
|
-
this.props.listUsers({
|
|
176
|
-
search: this.state.search,
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
if (this.props.deleteRequest.loading && nextProps.deleteRequest.loaded) {
|
|
180
|
-
this.onDeleteUserSuccess();
|
|
181
|
-
}
|
|
182
|
-
if (this.props.createRequest.loading && nextProps.createRequest.loaded) {
|
|
183
|
-
this.onAddUserSuccess();
|
|
184
|
-
}
|
|
185
|
-
if (this.props.createRequest.loading && nextProps.createRequest.error) {
|
|
186
|
-
this.onAddUserError(nextProps.createRequest.error);
|
|
187
|
-
}
|
|
188
|
-
if (
|
|
189
|
-
this.props.loadRolesRequest.loading &&
|
|
190
|
-
nextProps.loadRolesRequest.error
|
|
191
|
-
) {
|
|
192
|
-
this.setState({
|
|
193
|
-
error: nextProps.loadRolesRequest.error,
|
|
194
|
-
});
|
|
164
|
+
const checkLoginUsingEmailStatus = useCallback(async () => {
|
|
165
|
+
try {
|
|
166
|
+
await getControlpanelAction('security');
|
|
167
|
+
if (controlPanelData?.data?.use_email_as_login) {
|
|
168
|
+
setLoginUsingEmail(controlPanelData.data.use_email_as_login);
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
// eslint-disable-next-line no-console
|
|
172
|
+
console.error('Error fetching security control panel', error);
|
|
195
173
|
}
|
|
196
|
-
}
|
|
174
|
+
}, [getControlpanelAction, controlPanelData]);
|
|
197
175
|
|
|
198
|
-
getUserFromProps(
|
|
199
|
-
|
|
200
|
-
|
|
176
|
+
const getUserFromProps = useCallback(
|
|
177
|
+
(value) => {
|
|
178
|
+
return find(users, ['@id', value]);
|
|
179
|
+
},
|
|
180
|
+
[users],
|
|
181
|
+
);
|
|
201
182
|
|
|
202
183
|
/**
|
|
203
184
|
* Search handler
|
|
@@ -205,22 +186,24 @@ class UsersControlpanel extends Component {
|
|
|
205
186
|
* @param {object} event Event object.
|
|
206
187
|
* @returns {undefined}
|
|
207
188
|
*/
|
|
208
|
-
onSearch(
|
|
209
|
-
event
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
search:
|
|
214
|
-
})
|
|
215
|
-
.then(() => {
|
|
216
|
-
this.setState({ isLoading: false });
|
|
189
|
+
const onSearch = useCallback(
|
|
190
|
+
(event) => {
|
|
191
|
+
event.preventDefault();
|
|
192
|
+
setIsLoading(true);
|
|
193
|
+
listUsersAction({
|
|
194
|
+
search: search,
|
|
217
195
|
})
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
196
|
+
.then(() => {
|
|
197
|
+
setIsLoading(false);
|
|
198
|
+
})
|
|
199
|
+
.catch((error) => {
|
|
200
|
+
setIsLoading(false);
|
|
201
|
+
// eslint-disable-next-line no-console
|
|
202
|
+
console.error('Error searching users', error);
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
[listUsersAction, search],
|
|
206
|
+
);
|
|
224
207
|
|
|
225
208
|
/**
|
|
226
209
|
* On change search handler
|
|
@@ -228,67 +211,94 @@ class UsersControlpanel extends Component {
|
|
|
228
211
|
* @param {object} event Event object.
|
|
229
212
|
* @returns {undefined}
|
|
230
213
|
*/
|
|
231
|
-
onChangeSearch(event) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
});
|
|
235
|
-
}
|
|
214
|
+
const onChangeSearch = (event) => {
|
|
215
|
+
setSearch(event.target.value);
|
|
216
|
+
};
|
|
236
217
|
|
|
237
218
|
/**
|
|
238
|
-
*
|
|
239
|
-
* @method
|
|
219
|
+
* Handle delete user click
|
|
220
|
+
* @method handleDeleteUser
|
|
240
221
|
* @param {object} event Event object.
|
|
241
222
|
* @param {string} value username.
|
|
242
223
|
* @returns {undefined}
|
|
243
224
|
*/
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
225
|
+
const handleDeleteUser = useCallback(
|
|
226
|
+
(event, data) => {
|
|
227
|
+
// Handle both formats: direct value from event target or object with value
|
|
228
|
+
const value =
|
|
229
|
+
data?.value || event?.target?.value || event?.currentTarget?.value;
|
|
230
|
+
if (value) {
|
|
231
|
+
setShowDelete(true);
|
|
232
|
+
setUserToDelete(getUserFromProps(value));
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
[getUserFromProps],
|
|
236
|
+
);
|
|
252
237
|
|
|
253
238
|
/**
|
|
254
239
|
* On delete ok
|
|
255
240
|
* @method onDeleteOk
|
|
256
241
|
* @returns {undefined}
|
|
257
242
|
*/
|
|
258
|
-
onDeleteOk() {
|
|
259
|
-
if (
|
|
260
|
-
|
|
243
|
+
const onDeleteOk = useCallback(() => {
|
|
244
|
+
if (userToDelete) {
|
|
245
|
+
const deleteAction = deleteUserAction(userToDelete.id);
|
|
246
|
+
if (deleteAction && typeof deleteAction.then === 'function') {
|
|
247
|
+
deleteAction
|
|
248
|
+
.then(() => {
|
|
249
|
+
// Handle success
|
|
250
|
+
setUserToDelete(undefined);
|
|
251
|
+
setShowDelete(false);
|
|
252
|
+
|
|
253
|
+
// Refresh users list
|
|
254
|
+
listUsersAction({ search: search });
|
|
255
|
+
|
|
256
|
+
// Show success message
|
|
257
|
+
toast.success(
|
|
258
|
+
<Toast
|
|
259
|
+
success
|
|
260
|
+
title={intl.formatMessage(messages.success)}
|
|
261
|
+
content={intl.formatMessage(messages.userDeleted)}
|
|
262
|
+
/>,
|
|
263
|
+
);
|
|
264
|
+
})
|
|
265
|
+
.catch((error) => {
|
|
266
|
+
// Handle error
|
|
267
|
+
// eslint-disable-next-line no-console
|
|
268
|
+
console.error('Error deleting user', error);
|
|
269
|
+
});
|
|
270
|
+
}
|
|
261
271
|
}
|
|
262
|
-
}
|
|
272
|
+
}, [userToDelete, deleteUserAction, listUsersAction, search, intl]);
|
|
263
273
|
|
|
264
274
|
/**
|
|
265
275
|
* On delete cancel
|
|
266
276
|
* @method onDeleteCancel
|
|
267
277
|
* @returns {undefined}
|
|
268
278
|
*/
|
|
269
|
-
onDeleteCancel() {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
userToDelete: undefined,
|
|
274
|
-
});
|
|
275
|
-
}
|
|
279
|
+
const onDeleteCancel = () => {
|
|
280
|
+
setShowDelete(false);
|
|
281
|
+
setUserToDelete(undefined);
|
|
282
|
+
};
|
|
276
283
|
|
|
277
284
|
/**
|
|
278
285
|
*@param {object} user
|
|
279
286
|
*@returns {undefined}
|
|
280
287
|
*@memberof UsersControlpanel
|
|
281
288
|
*/
|
|
282
|
-
addUserToGroup = (
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
+
const addUserToGroup = useCallback(
|
|
290
|
+
(user) => {
|
|
291
|
+
const { groups: userGroups, username } = user;
|
|
292
|
+
userGroups.forEach((group) => {
|
|
293
|
+
updateGroupAction(group, {
|
|
294
|
+
users: {
|
|
295
|
+
[username]: true,
|
|
296
|
+
},
|
|
297
|
+
});
|
|
289
298
|
});
|
|
290
|
-
}
|
|
291
|
-
|
|
299
|
+
},
|
|
300
|
+
[updateGroupAction],
|
|
301
|
+
);
|
|
292
302
|
|
|
293
303
|
/**
|
|
294
304
|
* Callback to be called by the ModalForm when the form is submitted.
|
|
@@ -297,119 +307,112 @@ class UsersControlpanel extends Component {
|
|
|
297
307
|
* @param {func} callback to set new form data in the ModalForm
|
|
298
308
|
* @returns {undefined}
|
|
299
309
|
*/
|
|
300
|
-
onAddUserSubmit
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
this.setState({
|
|
320
|
-
addUserSetFormDataCallback: callback,
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
}
|
|
310
|
+
const onAddUserSubmit = useCallback(
|
|
311
|
+
(data, callback) => {
|
|
312
|
+
const { groups: userGroups, sendPasswordReset, password } = data;
|
|
313
|
+
if (
|
|
314
|
+
sendPasswordReset !== undefined &&
|
|
315
|
+
sendPasswordReset === true &&
|
|
316
|
+
password !== undefined
|
|
317
|
+
) {
|
|
318
|
+
toast.error(
|
|
319
|
+
<Toast
|
|
320
|
+
error
|
|
321
|
+
title={intl.formatMessage(messages.error)}
|
|
322
|
+
content={intl.formatMessage(
|
|
323
|
+
messages.addUserFormPasswordAndSendPasswordTogetherNotAllowed,
|
|
324
|
+
)}
|
|
325
|
+
/>,
|
|
326
|
+
);
|
|
327
|
+
} else {
|
|
328
|
+
if (userGroups && userGroups.length > 0) addUserToGroup(data);
|
|
324
329
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
330
|
+
const createUserAction = createUser(data, sendPasswordReset);
|
|
331
|
+
dispatch(createUserAction)
|
|
332
|
+
.then(() => {
|
|
333
|
+
// Handle success
|
|
334
|
+
if (callback) {
|
|
335
|
+
callback({});
|
|
336
|
+
}
|
|
337
|
+
setShowAddUser(false);
|
|
338
|
+
setAddUserError(undefined);
|
|
339
|
+
|
|
340
|
+
// Refresh users list
|
|
341
|
+
listUsersAction({ search: search });
|
|
342
|
+
|
|
343
|
+
// Show success message
|
|
344
|
+
toast.success(
|
|
345
|
+
<Toast
|
|
346
|
+
success
|
|
347
|
+
title={intl.formatMessage(messages.success)}
|
|
348
|
+
content={intl.formatMessage(messages.userCreated)}
|
|
349
|
+
/>,
|
|
350
|
+
);
|
|
351
|
+
})
|
|
352
|
+
.catch((error) => {
|
|
353
|
+
// Handle error
|
|
354
|
+
setAddUserError(
|
|
355
|
+
error.response?.body?.error?.message || 'Error creating user',
|
|
356
|
+
);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
[intl, addUserToGroup, dispatch, search, listUsersAction],
|
|
361
|
+
);
|
|
345
362
|
|
|
346
363
|
/**
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
* @
|
|
350
|
-
*/
|
|
351
|
-
onDeleteUserSuccess() {
|
|
352
|
-
this.setState({
|
|
353
|
-
userToDelete: undefined,
|
|
354
|
-
showDelete: false,
|
|
355
|
-
});
|
|
356
|
-
toast.success(
|
|
357
|
-
<Toast
|
|
358
|
-
success
|
|
359
|
-
title={this.props.intl.formatMessage(messages.success)}
|
|
360
|
-
content={this.props.intl.formatMessage(messages.userDeleted)}
|
|
361
|
-
/>,
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
* @param {*} data
|
|
368
|
-
* @param {*} callback
|
|
369
|
-
* @memberof UsersControlpanel
|
|
364
|
+
* Update user role
|
|
365
|
+
* @param {*} name
|
|
366
|
+
* @param {*} value
|
|
370
367
|
*/
|
|
371
|
-
updateUserRole
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
368
|
+
const updateUserRole = useCallback(
|
|
369
|
+
(name, value) => {
|
|
370
|
+
setEntries(
|
|
371
|
+
map(entries, (entry) => ({
|
|
372
|
+
...entry,
|
|
373
|
+
roles:
|
|
374
|
+
entry.id === name && !entry.roles.includes(value)
|
|
375
|
+
? [...entry.roles, value]
|
|
376
|
+
: entry.id !== name
|
|
377
|
+
? entry.roles
|
|
378
|
+
: pull(entry.roles, value),
|
|
379
|
+
})),
|
|
380
|
+
);
|
|
381
|
+
},
|
|
382
|
+
[entries],
|
|
383
|
+
);
|
|
384
|
+
|
|
384
385
|
/**
|
|
385
|
-
*
|
|
386
|
+
* Update user role submit
|
|
386
387
|
* @param {*} event
|
|
387
|
-
* @memberof UsersControlpanel
|
|
388
388
|
*/
|
|
389
|
-
updateUserRoleSubmit = (
|
|
390
|
-
e
|
|
389
|
+
const updateUserRoleSubmit = useCallback(
|
|
390
|
+
(e) => {
|
|
391
|
+
e.stopPropagation();
|
|
391
392
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
393
|
+
const roleIds = roles.map((item) => item.id);
|
|
394
|
+
entries.forEach((item) => {
|
|
395
|
+
const userData = { roles: {} };
|
|
396
|
+
const removedRoles = difference(roleIds, item.roles);
|
|
396
397
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
398
|
+
removedRoles.forEach((role) => {
|
|
399
|
+
userData.roles[role] = false;
|
|
400
|
+
});
|
|
401
|
+
item.roles.forEach((role) => {
|
|
402
|
+
userData.roles[role] = true;
|
|
403
|
+
});
|
|
404
|
+
updateUserAction(item.id, userData);
|
|
402
405
|
});
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
406
|
+
toast.success(
|
|
407
|
+
<Toast
|
|
408
|
+
success
|
|
409
|
+
title={intl.formatMessage(messages.success)}
|
|
410
|
+
content={intl.formatMessage(messages.updateRoles)}
|
|
411
|
+
/>,
|
|
412
|
+
);
|
|
413
|
+
},
|
|
414
|
+
[roles, entries, updateUserAction, intl],
|
|
415
|
+
);
|
|
413
416
|
|
|
414
417
|
/**
|
|
415
418
|
* Handle Errors after createUser()
|
|
@@ -417,11 +420,9 @@ class UsersControlpanel extends Component {
|
|
|
417
420
|
* @param {object} error object. Requires the property .message
|
|
418
421
|
* @returns {undefined}
|
|
419
422
|
*/
|
|
420
|
-
onAddUserError(error) {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
});
|
|
424
|
-
}
|
|
423
|
+
const onAddUserError = useCallback((error) => {
|
|
424
|
+
setAddUserError(error.response.body.error.message);
|
|
425
|
+
}, []);
|
|
425
426
|
|
|
426
427
|
/**
|
|
427
428
|
* On change page
|
|
@@ -430,359 +431,303 @@ class UsersControlpanel extends Component {
|
|
|
430
431
|
* @param {string} value Page value.
|
|
431
432
|
* @returns {undefined}
|
|
432
433
|
*/
|
|
433
|
-
onChangePage = (event, { value }) => {
|
|
434
|
-
|
|
435
|
-
currentPage: value,
|
|
436
|
-
});
|
|
434
|
+
const onChangePage = (event, { value }) => {
|
|
435
|
+
setCurrentPage(value);
|
|
437
436
|
};
|
|
438
437
|
|
|
439
|
-
componentDidUpdate(prevProps, prevState) {
|
|
440
|
-
if (this.props.users !== prevProps.users) {
|
|
441
|
-
this.setState({
|
|
442
|
-
entries: this.props.users,
|
|
443
|
-
});
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
438
|
/**
|
|
448
439
|
* Filters the roles a user can assign when adding a user.
|
|
449
440
|
* @method canAssignAdd
|
|
450
441
|
* @returns {arry}
|
|
451
442
|
*/
|
|
452
|
-
canAssignAdd(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
443
|
+
const canAssignAdd = useCallback(
|
|
444
|
+
(isManager) => {
|
|
445
|
+
if (isManager) return roles;
|
|
446
|
+
return user?.roles
|
|
447
|
+
? roles.filter((role) => user.roles.includes(role.id))
|
|
448
|
+
: [];
|
|
449
|
+
},
|
|
450
|
+
[roles, user],
|
|
451
|
+
);
|
|
460
452
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
453
|
+
useEffect(() => {
|
|
454
|
+
setIsClient(true);
|
|
455
|
+
fetchData();
|
|
456
|
+
checkLoginUsingEmailStatus();
|
|
457
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
458
|
+
|
|
459
|
+
useEffect(() => {
|
|
460
|
+
setEntries(users);
|
|
461
|
+
}, [users]);
|
|
462
|
+
|
|
463
|
+
useEffect(() => {
|
|
464
|
+
if (createRequest?.error && !createRequest?.loading) {
|
|
465
|
+
onAddUserError(createRequest.error);
|
|
469
466
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
467
|
+
}, [createRequest?.error, createRequest?.loading, onAddUserError]);
|
|
468
|
+
|
|
469
|
+
useEffect(() => {
|
|
470
|
+
if (loadRolesRequest?.error && !loadRolesRequest?.loading) {
|
|
471
|
+
setError(loadRolesRequest.error);
|
|
472
|
+
}
|
|
473
|
+
}, [loadRolesRequest?.error, loadRolesRequest?.loading]);
|
|
474
|
+
|
|
475
|
+
if (error) {
|
|
476
|
+
return <Error error={error} />;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const usernameToDelete = userToDelete ? userToDelete.username : '';
|
|
480
|
+
|
|
481
|
+
// Copy the userschema using JSON serialization/deserialization
|
|
482
|
+
// this is really ugly, but if we don't do this the original value
|
|
483
|
+
// of the userschema is changed and it is used like that through
|
|
484
|
+
// the lifecycle of the application
|
|
485
|
+
let adduserschema = {};
|
|
486
|
+
let isUserManager = false;
|
|
487
|
+
if (userschema?.loaded) {
|
|
488
|
+
isUserManager = isManager(user);
|
|
489
|
+
adduserschema = JSON.parse(JSON.stringify(userschema?.userschema));
|
|
490
|
+
|
|
491
|
+
// Add custom form fields to the schema
|
|
492
|
+
adduserschema.properties.username = {
|
|
493
|
+
title: intl.formatMessage(messages.addUserFormUsernameTitle),
|
|
494
|
+
type: 'string',
|
|
495
|
+
description: intl.formatMessage(messages.addUserFormUsernameDescription),
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
adduserschema.properties.password = {
|
|
499
|
+
title: intl.formatMessage(messages.addUserFormPasswordTitle),
|
|
500
|
+
type: 'password',
|
|
501
|
+
description: intl.formatMessage(messages.addUserFormPasswordDescription),
|
|
502
|
+
widget: 'password',
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
adduserschema.properties.sendPasswordReset = {
|
|
506
|
+
title: intl.formatMessage(messages.addUserFormSendPasswordResetTitle),
|
|
507
|
+
type: 'boolean',
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
adduserschema.properties.roles = {
|
|
511
|
+
title: intl.formatMessage(messages.addUserFormRolesTitle),
|
|
512
|
+
type: 'array',
|
|
513
|
+
choices: canAssignAdd(isUserManager).map((role) => [role.id, role.title]),
|
|
514
|
+
noValueOption: false,
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
adduserschema.properties.groups = {
|
|
518
|
+
title: intl.formatMessage(messages.addUserGroupNameTitle),
|
|
519
|
+
type: 'array',
|
|
520
|
+
choices: groups
|
|
521
|
+
.filter((group) => canAssignGroup(isUserManager, group))
|
|
522
|
+
.map((group) => [group.id, group.id]),
|
|
523
|
+
noValueOption: false,
|
|
524
|
+
};
|
|
525
|
+
// Add custom fields to the first fieldset if they don't already exist
|
|
526
|
+
if (
|
|
527
|
+
adduserschema.fieldsets &&
|
|
528
|
+
adduserschema.fieldsets.length > 0 &&
|
|
529
|
+
!adduserschema.fieldsets[0].fields.includes('username')
|
|
530
|
+
) {
|
|
531
|
+
adduserschema.fieldsets[0].fields =
|
|
532
|
+
adduserschema.fieldsets[0].fields.concat([
|
|
533
533
|
'username',
|
|
534
534
|
'password',
|
|
535
535
|
'sendPasswordReset',
|
|
536
536
|
'roles',
|
|
537
537
|
'groups',
|
|
538
538
|
]);
|
|
539
|
-
}
|
|
540
539
|
}
|
|
540
|
+
}
|
|
541
541
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
values={{
|
|
564
|
-
username: <b>{usernameToDelete}</b>,
|
|
565
|
-
}}
|
|
566
|
-
/>
|
|
567
|
-
</ul>
|
|
568
|
-
</div>
|
|
569
|
-
}
|
|
570
|
-
onCancel={this.onDeleteCancel}
|
|
571
|
-
onConfirm={this.onDeleteOk}
|
|
572
|
-
size={null}
|
|
573
|
-
/>
|
|
574
|
-
{this.props?.userschema?.loaded && this.state.showAddUser ? (
|
|
575
|
-
<ModalForm
|
|
576
|
-
open={this.state.showAddUser}
|
|
577
|
-
className="modal"
|
|
578
|
-
onSubmit={this.onAddUserSubmit}
|
|
579
|
-
submitError={this.state.addUserError}
|
|
580
|
-
onCancel={() =>
|
|
581
|
-
this.setState({ showAddUser: false, addUserError: undefined })
|
|
582
|
-
}
|
|
583
|
-
title={this.props.intl.formatMessage(messages.addUserFormTitle)}
|
|
584
|
-
loading={this.props.createRequest.loading}
|
|
585
|
-
schema={adduserschema}
|
|
586
|
-
/>
|
|
587
|
-
) : null}
|
|
588
|
-
</div>
|
|
589
|
-
<Segment.Group raised>
|
|
590
|
-
<Segment className="primary">
|
|
591
|
-
<FormattedMessage id="Users" defaultMessage="Users" />
|
|
592
|
-
</Segment>
|
|
593
|
-
<Segment secondary>
|
|
594
|
-
<FormattedMessage
|
|
595
|
-
id="Note that roles set here apply directly to a user. The symbol{plone_svg}indicates a role inherited from membership in a group."
|
|
596
|
-
defaultMessage="Note that roles set here apply directly to a user. The symbol{plone_svg}indicates a role inherited from membership in a group."
|
|
597
|
-
values={{
|
|
598
|
-
plone_svg: (
|
|
599
|
-
<Icon
|
|
600
|
-
name={ploneSVG}
|
|
601
|
-
size="20px"
|
|
602
|
-
color="#007EB1"
|
|
603
|
-
title={'plone-svg'}
|
|
604
|
-
/>
|
|
605
|
-
),
|
|
606
|
-
}}
|
|
607
|
-
/>
|
|
608
|
-
</Segment>
|
|
609
|
-
<Segment>
|
|
610
|
-
<Form onSubmit={this.onSearch}>
|
|
611
|
-
<Form.Field>
|
|
612
|
-
<Input
|
|
613
|
-
name="SearchableText"
|
|
614
|
-
action={{
|
|
615
|
-
icon: 'search',
|
|
616
|
-
loading: this.state.isLoading,
|
|
617
|
-
disabled: this.state.isLoading,
|
|
542
|
+
return (
|
|
543
|
+
<Container className="users-control-panel">
|
|
544
|
+
<Helmet title={intl.formatMessage(messages.users)} />
|
|
545
|
+
<div className="container">
|
|
546
|
+
<Confirm
|
|
547
|
+
open={showDelete}
|
|
548
|
+
header={intl.formatMessage(messages.deleteUserConfirmTitle)}
|
|
549
|
+
content={
|
|
550
|
+
<div className="content">
|
|
551
|
+
<Dimmer active={deleteRequest?.loading}>
|
|
552
|
+
<Loader>
|
|
553
|
+
<FormattedMessage id="Loading" defaultMessage="Loading." />
|
|
554
|
+
</Loader>
|
|
555
|
+
</Dimmer>
|
|
556
|
+
|
|
557
|
+
<ul className="content">
|
|
558
|
+
<FormattedMessage
|
|
559
|
+
id="Do you really want to delete the user {username}?"
|
|
560
|
+
defaultMessage="Do you really want to delete the user {username}?"
|
|
561
|
+
values={{
|
|
562
|
+
username: <b>{usernameToDelete}</b>,
|
|
618
563
|
}}
|
|
619
|
-
placeholder={this.props.intl.formatMessage(
|
|
620
|
-
messages.searchUsers,
|
|
621
|
-
)}
|
|
622
|
-
onChange={this.onChangeSearch}
|
|
623
|
-
id="user-search-input"
|
|
624
564
|
/>
|
|
625
|
-
</
|
|
626
|
-
</Form>
|
|
627
|
-
</Segment>
|
|
628
|
-
<Form>
|
|
629
|
-
{((this.props.many_users && this.state.entries.length > 0) ||
|
|
630
|
-
!this.props.many_users) && (
|
|
631
|
-
<Table padded striped attached unstackable>
|
|
632
|
-
<Table.Header>
|
|
633
|
-
<Table.Row>
|
|
634
|
-
<Table.HeaderCell>
|
|
635
|
-
<FormattedMessage
|
|
636
|
-
id="User name"
|
|
637
|
-
defaultMessage="User name"
|
|
638
|
-
/>
|
|
639
|
-
</Table.HeaderCell>
|
|
640
|
-
{this.props.roles.map((role) => (
|
|
641
|
-
<Table.HeaderCell key={role.id}>
|
|
642
|
-
{role.title}
|
|
643
|
-
</Table.HeaderCell>
|
|
644
|
-
))}
|
|
645
|
-
<Table.HeaderCell>
|
|
646
|
-
<FormattedMessage id="Actions" defaultMessage="Actions" />
|
|
647
|
-
</Table.HeaderCell>
|
|
648
|
-
</Table.Row>
|
|
649
|
-
</Table.Header>
|
|
650
|
-
<Table.Body data-user="users">
|
|
651
|
-
{this.state.entries
|
|
652
|
-
.slice(
|
|
653
|
-
this.state.currentPage * 10,
|
|
654
|
-
this.state.pageSize * (this.state.currentPage + 1),
|
|
655
|
-
)
|
|
656
|
-
.map((user) => (
|
|
657
|
-
<RenderUsers
|
|
658
|
-
key={user.id}
|
|
659
|
-
onDelete={this.delete}
|
|
660
|
-
roles={this.props.roles}
|
|
661
|
-
user={user}
|
|
662
|
-
updateUser={this.updateUserRole}
|
|
663
|
-
inheritedRole={this.props.inheritedRole}
|
|
664
|
-
userschema={this.props.userschema}
|
|
665
|
-
listUsers={this.props.listUsers}
|
|
666
|
-
isUserManager={isUserManager}
|
|
667
|
-
/>
|
|
668
|
-
))}
|
|
669
|
-
</Table.Body>
|
|
670
|
-
</Table>
|
|
671
|
-
)}
|
|
672
|
-
{this.state.entries.length === 0 && this.state.search && (
|
|
673
|
-
<Segment>
|
|
674
|
-
{this.props.intl.formatMessage(messages.userSearchNoResults)}
|
|
675
|
-
</Segment>
|
|
676
|
-
)}
|
|
677
|
-
<div className="contents-pagination">
|
|
678
|
-
<Pagination
|
|
679
|
-
current={this.state.currentPage}
|
|
680
|
-
total={Math.ceil(
|
|
681
|
-
this.state.entries?.length / this.state.pageSize,
|
|
682
|
-
)}
|
|
683
|
-
onChangePage={this.onChangePage}
|
|
684
|
-
/>
|
|
565
|
+
</ul>
|
|
685
566
|
</div>
|
|
567
|
+
}
|
|
568
|
+
onCancel={onDeleteCancel}
|
|
569
|
+
onConfirm={onDeleteOk}
|
|
570
|
+
size={null}
|
|
571
|
+
/>
|
|
572
|
+
{userschema?.loaded && showAddUser ? (
|
|
573
|
+
<ModalForm
|
|
574
|
+
open={showAddUser}
|
|
575
|
+
className="modal"
|
|
576
|
+
onSubmit={onAddUserSubmit}
|
|
577
|
+
submitError={addUserError}
|
|
578
|
+
onCancel={() => {
|
|
579
|
+
setShowAddUser(false);
|
|
580
|
+
setAddUserError(undefined);
|
|
581
|
+
}}
|
|
582
|
+
title={intl.formatMessage(messages.addUserFormTitle)}
|
|
583
|
+
loading={createRequest?.loading}
|
|
584
|
+
schema={adduserschema}
|
|
585
|
+
/>
|
|
586
|
+
) : null}
|
|
587
|
+
</div>
|
|
588
|
+
<Segment.Group raised>
|
|
589
|
+
<Segment className="primary">
|
|
590
|
+
<FormattedMessage id="Users" defaultMessage="Users" />
|
|
591
|
+
</Segment>
|
|
592
|
+
<Segment secondary>
|
|
593
|
+
<FormattedMessage
|
|
594
|
+
id="Note that roles set here apply directly to a user. The symbol{plone_svg}indicates a role inherited from membership in a group."
|
|
595
|
+
defaultMessage="Note that roles set here apply directly to a user. The symbol{plone_svg}indicates a role inherited from membership in a group."
|
|
596
|
+
values={{
|
|
597
|
+
plone_svg: (
|
|
598
|
+
<Icon
|
|
599
|
+
name={ploneSVG}
|
|
600
|
+
size="20px"
|
|
601
|
+
color="#007EB1"
|
|
602
|
+
title={'plone-svg'}
|
|
603
|
+
/>
|
|
604
|
+
),
|
|
605
|
+
}}
|
|
606
|
+
/>
|
|
607
|
+
</Segment>
|
|
608
|
+
<Segment>
|
|
609
|
+
<Form onSubmit={onSearch}>
|
|
610
|
+
<Form.Field>
|
|
611
|
+
<Input
|
|
612
|
+
name="SearchableText"
|
|
613
|
+
action={{
|
|
614
|
+
icon: 'search',
|
|
615
|
+
loading: isLoading,
|
|
616
|
+
disabled: isLoading,
|
|
617
|
+
}}
|
|
618
|
+
placeholder={intl.formatMessage(messages.searchUsers)}
|
|
619
|
+
onChange={onChangeSearch}
|
|
620
|
+
id="user-search-input"
|
|
621
|
+
/>
|
|
622
|
+
</Form.Field>
|
|
686
623
|
</Form>
|
|
687
|
-
</Segment
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
<
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
className="save"
|
|
698
|
-
aria-label={this.props.intl.formatMessage(messages.save)}
|
|
699
|
-
onClick={this.updateUserRoleSubmit}
|
|
700
|
-
loading={this.props.createRequest.loading}
|
|
701
|
-
>
|
|
702
|
-
<Icon
|
|
703
|
-
name={saveSVG}
|
|
704
|
-
className="circled"
|
|
705
|
-
size="30px"
|
|
706
|
-
title={this.props.intl.formatMessage(messages.save)}
|
|
707
|
-
/>
|
|
708
|
-
</Button>
|
|
709
|
-
<Link to="/controlpanel" className="cancel">
|
|
710
|
-
<Icon
|
|
711
|
-
name={clearSVG}
|
|
712
|
-
className="circled"
|
|
713
|
-
aria-label={this.props.intl.formatMessage(
|
|
714
|
-
messages.cancel,
|
|
715
|
-
)}
|
|
716
|
-
size="30px"
|
|
717
|
-
title={this.props.intl.formatMessage(messages.cancel)}
|
|
624
|
+
</Segment>
|
|
625
|
+
<Form>
|
|
626
|
+
{((many_users && entries.length > 0) || !many_users) && (
|
|
627
|
+
<Table padded striped attached unstackable>
|
|
628
|
+
<Table.Header>
|
|
629
|
+
<Table.Row>
|
|
630
|
+
<Table.HeaderCell>
|
|
631
|
+
<FormattedMessage
|
|
632
|
+
id="User name"
|
|
633
|
+
defaultMessage="User name"
|
|
718
634
|
/>
|
|
719
|
-
</
|
|
720
|
-
|
|
721
|
-
id
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
635
|
+
</Table.HeaderCell>
|
|
636
|
+
{roles.map((role) => (
|
|
637
|
+
<Table.HeaderCell key={role.id}>
|
|
638
|
+
{role.title}
|
|
639
|
+
</Table.HeaderCell>
|
|
640
|
+
))}
|
|
641
|
+
<Table.HeaderCell>
|
|
642
|
+
<FormattedMessage id="Actions" defaultMessage="Actions" />
|
|
643
|
+
</Table.HeaderCell>
|
|
644
|
+
</Table.Row>
|
|
645
|
+
</Table.Header>
|
|
646
|
+
<Table.Body data-user="users">
|
|
647
|
+
{entries
|
|
648
|
+
.slice(currentPage * 10, pageSize * (currentPage + 1))
|
|
649
|
+
.map((userItem) => (
|
|
650
|
+
<RenderUsers
|
|
651
|
+
key={userItem.id}
|
|
652
|
+
onDelete={handleDeleteUser}
|
|
653
|
+
roles={roles}
|
|
654
|
+
user={userItem}
|
|
655
|
+
updateUser={updateUserRole}
|
|
656
|
+
inheritedRole={inheritedRole}
|
|
657
|
+
userschema={userschema}
|
|
658
|
+
listUsers={listUsersAction}
|
|
659
|
+
isUserManager={isUserManager}
|
|
737
660
|
/>
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
/>,
|
|
742
|
-
document.getElementById('toolbar'),
|
|
661
|
+
))}
|
|
662
|
+
</Table.Body>
|
|
663
|
+
</Table>
|
|
743
664
|
)}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
665
|
+
{entries.length === 0 && search && (
|
|
666
|
+
<Segment>
|
|
667
|
+
{intl.formatMessage(messages.userSearchNoResults)}
|
|
668
|
+
</Segment>
|
|
669
|
+
)}
|
|
670
|
+
<div className="contents-pagination">
|
|
671
|
+
<Pagination
|
|
672
|
+
current={currentPage}
|
|
673
|
+
total={Math.ceil(entries?.length / pageSize)}
|
|
674
|
+
onChangePage={onChangePage}
|
|
675
|
+
/>
|
|
676
|
+
</div>
|
|
677
|
+
</Form>
|
|
678
|
+
</Segment.Group>
|
|
679
|
+
{isClient &&
|
|
680
|
+
createPortal(
|
|
681
|
+
<Toolbar
|
|
682
|
+
pathname={pathname}
|
|
683
|
+
hideDefaultViewButtons
|
|
684
|
+
inner={
|
|
685
|
+
<>
|
|
686
|
+
<Button
|
|
687
|
+
id="toolbar-save"
|
|
688
|
+
className="save"
|
|
689
|
+
aria-label={intl.formatMessage(messages.save)}
|
|
690
|
+
onClick={updateUserRoleSubmit}
|
|
691
|
+
loading={createRequest?.loading}
|
|
692
|
+
>
|
|
693
|
+
<Icon
|
|
694
|
+
name={saveSVG}
|
|
695
|
+
className="circled"
|
|
696
|
+
size="30px"
|
|
697
|
+
title={intl.formatMessage(messages.save)}
|
|
698
|
+
/>
|
|
699
|
+
</Button>
|
|
700
|
+
<Link to="/controlpanel" className="cancel">
|
|
701
|
+
<Icon
|
|
702
|
+
name={clearSVG}
|
|
703
|
+
className="circled"
|
|
704
|
+
aria-label={intl.formatMessage(messages.cancel)}
|
|
705
|
+
size="30px"
|
|
706
|
+
title={intl.formatMessage(messages.cancel)}
|
|
707
|
+
/>
|
|
708
|
+
</Link>
|
|
709
|
+
<Button
|
|
710
|
+
id="toolbar-add"
|
|
711
|
+
aria-label={intl.formatMessage(messages.addUserButtonTitle)}
|
|
712
|
+
onClick={() => {
|
|
713
|
+
setShowAddUser(true);
|
|
714
|
+
}}
|
|
715
|
+
loading={createRequest?.loading}
|
|
716
|
+
>
|
|
717
|
+
<Icon
|
|
718
|
+
name={addUserSvg}
|
|
719
|
+
size="45px"
|
|
720
|
+
color="#826A6A"
|
|
721
|
+
title={intl.formatMessage(messages.addUserButtonTitle)}
|
|
722
|
+
/>
|
|
723
|
+
</Button>
|
|
724
|
+
</>
|
|
725
|
+
}
|
|
726
|
+
/>,
|
|
727
|
+
document.getElementById('toolbar'),
|
|
728
|
+
)}
|
|
729
|
+
</Container>
|
|
730
|
+
);
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
export default UsersControlpanel;
|