@plone/volto 17.0.0-alpha.24 → 17.0.0-alpha.26

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 (130) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +92 -4
  3. package/CONTRIBUTING.md +5 -1
  4. package/README.md +9 -7
  5. package/cypress/support/commands.js +12 -9
  6. package/cypress.config.js +1 -0
  7. package/locales/ca/LC_MESSAGES/volto.po +41 -15
  8. package/locales/ca.json +1 -1
  9. package/locales/de/LC_MESSAGES/volto.po +41 -15
  10. package/locales/de.json +1 -1
  11. package/locales/en/LC_MESSAGES/volto.po +40 -14
  12. package/locales/en.json +1 -1
  13. package/locales/es/LC_MESSAGES/volto.po +69 -43
  14. package/locales/es.json +1 -1
  15. package/locales/eu/LC_MESSAGES/volto.po +40 -14
  16. package/locales/eu.json +1 -1
  17. package/locales/fi/LC_MESSAGES/volto.po +40 -14
  18. package/locales/fi.json +1 -1
  19. package/locales/fr/LC_MESSAGES/volto.po +41 -15
  20. package/locales/fr.json +1 -1
  21. package/locales/it/LC_MESSAGES/volto.po +40 -14
  22. package/locales/it.json +1 -1
  23. package/locales/ja/LC_MESSAGES/volto.po +40 -14
  24. package/locales/ja.json +1 -1
  25. package/locales/nl/LC_MESSAGES/volto.po +41 -15
  26. package/locales/nl.json +1 -1
  27. package/locales/pt/LC_MESSAGES/volto.po +41 -15
  28. package/locales/pt.json +1 -1
  29. package/locales/pt_BR/LC_MESSAGES/volto.po +40 -14
  30. package/locales/pt_BR.json +1 -1
  31. package/locales/ro/LC_MESSAGES/volto.po +41 -15
  32. package/locales/ro.json +1 -1
  33. package/locales/volto.pot +41 -15
  34. package/locales/zh_CN/LC_MESSAGES/volto.po +41 -15
  35. package/locales/zh_CN.json +1 -1
  36. package/package.json +4 -4
  37. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +90 -0
  38. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +6 -0
  39. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +6 -0
  40. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +6 -0
  41. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +10 -0
  42. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +10 -0
  43. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +30 -0
  44. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +10 -0
  45. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +6 -0
  46. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +6 -0
  47. package/packages/volto-slate/package.json +1 -1
  48. package/packages/volto-slate/src/editor/render.jsx +2 -3
  49. package/src/actions/index.js +4 -0
  50. package/src/actions/navroot/navroot.js +16 -0
  51. package/src/actions/navroot/navroot.test.js +15 -0
  52. package/src/actions/relations/relations.js +17 -0
  53. package/src/actions/site/site.js +16 -0
  54. package/src/actions/site/site.test.js +15 -0
  55. package/src/actions/userSession/userSession.js +17 -1
  56. package/src/components/manage/Blocks/Block/Settings.jsx +2 -0
  57. package/src/components/manage/Blocks/Block/Settings.test.jsx +90 -0
  58. package/src/components/manage/Blocks/Image/schema.js +5 -1
  59. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +18 -11
  60. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +42 -25
  61. package/src/components/manage/Blocks/ToC/View.jsx +75 -13
  62. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +4 -13
  63. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.test.jsx +44 -0
  64. package/src/components/manage/Contents/Contents.jsx +27 -0
  65. package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +65 -38
  66. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +11 -9
  67. package/src/components/manage/Controlpanels/Relations/Relations.jsx +3 -3
  68. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +8 -7
  69. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +15 -9
  70. package/src/components/manage/Controlpanels/Rules/AddRule.jsx +1 -1
  71. package/src/components/manage/Controlpanels/Rules/EditRule.jsx +1 -1
  72. package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +95 -5
  73. package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +127 -99
  74. package/src/components/manage/Diff/DiffField.jsx +25 -1
  75. package/src/components/manage/Form/BlockDataForm.jsx +3 -2
  76. package/src/components/manage/Form/BlockDataForm.test.jsx +34 -2
  77. package/src/components/manage/LinksToItem/LinksToItem.jsx +1 -1
  78. package/src/components/manage/LinksToItem/LinksToItem.test.jsx +5 -2
  79. package/src/components/manage/Messages/Messages.jsx +32 -99
  80. package/src/components/manage/Messages/Messages.test.jsx +0 -1
  81. package/src/components/manage/Sharing/Sharing.jsx +50 -21
  82. package/src/components/manage/UniversalLink/UniversalLink.jsx +4 -6
  83. package/src/components/manage/Widgets/ArrayWidget.jsx +3 -1
  84. package/src/components/manage/Widgets/ArrayWidget.test.jsx +45 -1
  85. package/src/components/manage/Widgets/FormFieldWrapper.jsx +1 -1
  86. package/src/components/manage/Widgets/RegistryImageWidget.jsx +210 -0
  87. package/src/components/manage/Widgets/RegistryImageWidget.test.jsx +91 -0
  88. package/src/components/manage/Widgets/SelectWidget.jsx +15 -1
  89. package/src/components/manage/Widgets/SelectWidget.test.jsx +45 -1
  90. package/src/components/theme/Comments/Comment.stories.jsx +84 -0
  91. package/src/components/theme/Comments/Comments.jsx +273 -378
  92. package/src/components/theme/ContentMetadataTags/ContentMetadataTags.jsx +37 -3
  93. package/src/components/theme/Login/Login.jsx +159 -241
  94. package/src/components/theme/Logo/Logo.Multilingual.test.jsx +131 -1
  95. package/src/components/theme/Logo/Logo.jsx +35 -29
  96. package/src/components/theme/Logo/Logo.test.jsx +135 -1
  97. package/src/components/theme/Logout/Logout.jsx +36 -83
  98. package/src/components/theme/Navigation/Navigation.jsx +86 -171
  99. package/src/components/theme/Search/SearchTags.jsx +30 -60
  100. package/src/components/theme/SearchWidget/SearchWidget.jsx +15 -3
  101. package/src/components/theme/SearchWidget/SearchWidget.test.jsx +8 -0
  102. package/src/components/theme/Sitemap/Sitemap.jsx +24 -13
  103. package/src/components/theme/Sitemap/Sitemap.test.jsx +23 -2
  104. package/src/components/theme/View/View.jsx +2 -0
  105. package/src/config/ControlPanels.js +0 -1
  106. package/src/config/Widgets.jsx +2 -0
  107. package/src/config/index.js +15 -3
  108. package/src/constants/ActionTypes.js +4 -0
  109. package/src/express-middleware/images.js +1 -0
  110. package/src/helpers/MessageLabels/MessageLabels.js +26 -4
  111. package/src/helpers/Site/index.js +21 -0
  112. package/src/helpers/index.js +1 -0
  113. package/src/reducers/index.js +4 -0
  114. package/src/reducers/navroot/navroot.js +79 -0
  115. package/src/reducers/navroot/navroot.test.js +110 -0
  116. package/src/reducers/relations/relations.js +74 -46
  117. package/src/reducers/site/site.js +51 -0
  118. package/src/reducers/site/site.test.js +67 -0
  119. package/src/reducers/userSession/userSession.js +15 -1
  120. package/src/server.jsx +9 -0
  121. package/test-setup-config.js +1 -0
  122. package/theme/themes/pastanaga/collections/form.overrides +46 -0
  123. package/theme/themes/pastanaga/elements/input.overrides +10 -0
  124. package/theme/themes/pastanaga/elements/label.overrides +10 -0
  125. package/theme/themes/pastanaga/extras/login.less +3 -0
  126. package/webpack-plugins/webpack-less-plugin.js +19 -0
  127. package/.gitignore~ +0 -71
  128. package/news/4547.breaking~ +0 -1
  129. package/package.json~ +0 -444
  130. package/src/config/index.js~ +0 -223
@@ -96,6 +96,7 @@ class GroupsControlpanel extends Component {
96
96
  this.updateGroupRole = this.updateGroupRole.bind(this);
97
97
  this.state = {
98
98
  search: '',
99
+ isLoading: false,
99
100
  addGroupError: '',
100
101
  showDelete: false,
101
102
  groupToDelete: undefined,
@@ -173,8 +174,18 @@ class GroupsControlpanel extends Component {
173
174
  * @returns {undefined}
174
175
  */
175
176
  onSearchGroups(event) {
177
+ this.setState({ isLoading: true });
176
178
  event.preventDefault();
177
- this.props.listGroups(this.state.search);
179
+ this.props
180
+ .listGroups(this.state.search)
181
+ .then(() => {
182
+ this.setState({ isLoading: false });
183
+ })
184
+ .catch((error) => {
185
+ this.setState({ isLoading: false });
186
+ // eslint-disable-next-line no-console
187
+ console.error('Error searching group', error);
188
+ });
178
189
  }
179
190
 
180
191
  /**
@@ -487,7 +498,11 @@ class GroupsControlpanel extends Component {
487
498
  <Form.Field>
488
499
  <Input
489
500
  name="SearchableText"
490
- action={{ icon: 'search' }}
501
+ action={{
502
+ icon: 'search',
503
+ loading: this.state.isLoading,
504
+ disabled: this.state.isLoading,
505
+ }}
491
506
  placeholder={this.props.intl.formatMessage(
492
507
  messages.searchGroups,
493
508
  )}
@@ -499,43 +514,55 @@ class GroupsControlpanel extends Component {
499
514
  </Segment>
500
515
  <Form>
501
516
  <div className="table">
502
- <Table padded striped attached unstackable>
503
- <Table.Header>
504
- <Table.Row>
505
- <Table.HeaderCell>
506
- <FormattedMessage
507
- id="Groupname"
508
- defaultMessage="Groupname"
509
- />
510
- </Table.HeaderCell>
511
- {this.props.roles.map((role) => (
512
- <Table.HeaderCell key={role.id}>
513
- {role.title}
517
+ {((this.props.many_groups &&
518
+ this.state.groupEntries.length > 0) ||
519
+ !this.props.many_groups) && (
520
+ <Table padded striped attached unstackable>
521
+ <Table.Header>
522
+ <Table.Row>
523
+ <Table.HeaderCell>
524
+ <FormattedMessage
525
+ id="Groupname"
526
+ defaultMessage="Groupname"
527
+ />
528
+ </Table.HeaderCell>
529
+ {this.props.roles.map((role) => (
530
+ <Table.HeaderCell key={role.id}>
531
+ {role.title}
532
+ </Table.HeaderCell>
533
+ ))}
534
+ <Table.HeaderCell>
535
+ <FormattedMessage
536
+ id="Actions"
537
+ defaultMessage="Actions"
538
+ />
514
539
  </Table.HeaderCell>
515
- ))}
516
- <Table.HeaderCell>
517
- <FormattedMessage id="Actions" defaultMessage="Actions" />
518
- </Table.HeaderCell>
519
- </Table.Row>
520
- </Table.Header>
521
- <Table.Body data-group="groups">
522
- {this.state.groupEntries
523
- .slice(
524
- this.state.currentPage * 10,
525
- this.state.pageSize * (this.state.currentPage + 1),
526
- )
527
- .map((group) => (
528
- <RenderGroups
529
- key={group.id}
530
- onDelete={this.deleteGroup}
531
- roles={this.props.roles}
532
- group={group}
533
- updateGroups={this.updateGroupRole}
534
- inheritedRole={this.state.authenticatedRole}
535
- />
536
- ))}
537
- </Table.Body>
538
- </Table>
540
+ </Table.Row>
541
+ </Table.Header>
542
+ <Table.Body data-group="groups">
543
+ {this.state.groupEntries
544
+ .slice(
545
+ this.state.currentPage * 10,
546
+ this.state.pageSize * (this.state.currentPage + 1),
547
+ )
548
+ .map((group) => (
549
+ <RenderGroups
550
+ key={group.id}
551
+ onDelete={this.deleteGroup}
552
+ roles={this.props.roles}
553
+ group={group}
554
+ updateGroups={this.updateGroupRole}
555
+ inheritedRole={this.state.authenticatedRole}
556
+ />
557
+ ))}
558
+ </Table.Body>
559
+ </Table>
560
+ )}
561
+ {this.state.groupEntries.length === 0 && this.state.search && (
562
+ <Segment>
563
+ {this.props.intl.formatMessage(messages.groupSearchNoResults)}
564
+ </Segment>
565
+ )}
539
566
  </div>
540
567
  <div className="contents-pagination">
541
568
  <Pagination
@@ -5,15 +5,15 @@ import { useSelector, useDispatch } from 'react-redux';
5
5
  import { Divider, Segment, Table } from 'semantic-ui-react';
6
6
  import { queryRelations } from '@plone/volto/actions';
7
7
  import { flattenToAppURL } from '@plone/volto/helpers';
8
- import { UniversalLink } from '@plone/volto/components';
8
+ import { ConditionalLink } from '@plone/volto/components';
9
9
 
10
10
  const BrokenRelations = () => {
11
11
  const dispatch = useDispatch();
12
12
  const brokenRelationStats = useSelector(
13
- (state) => state.relations?.stats?.broken || {},
13
+ (state) => state.relations?.stats?.data?.broken || {},
14
14
  );
15
15
  const brokenRelations = useSelector(
16
- (state) => state.relations?.subrequests?.broken?.relations,
16
+ (state) => state.relations?.subrequests?.broken?.data,
17
17
  );
18
18
 
19
19
  useEffect(() => {
@@ -60,20 +60,22 @@ const BrokenRelations = () => {
60
60
  }).map((el, index) => (
61
61
  <Table.Row key={index}>
62
62
  <Table.Cell>
63
- <UniversalLink
64
- href={`${flattenToAppURL(el[0])}/edit`}
63
+ <ConditionalLink
64
+ to={`${el[0]}/edit`}
65
65
  openLinkInNewTab={true}
66
+ condition={el[0].includes('http')}
66
67
  >
67
68
  {flattenToAppURL(el[0])}
68
- </UniversalLink>
69
+ </ConditionalLink>
69
70
  </Table.Cell>
70
71
  <Table.Cell>
71
- <UniversalLink
72
- href={`${flattenToAppURL(el[1])}/edit`}
72
+ <ConditionalLink
73
+ to={`${el[1]}/edit`}
73
74
  openLinkInNewTab={true}
75
+ condition={el[1].includes('http')}
74
76
  >
75
77
  {flattenToAppURL(el[1])}
76
- </UniversalLink>
78
+ </ConditionalLink>
77
79
  </Table.Cell>
78
80
  </Table.Row>
79
81
  ))}
@@ -24,10 +24,10 @@ const RelationsControlPanel = () => {
24
24
  const dispatch = useDispatch();
25
25
 
26
26
  const brokenRelations = useSelector(
27
- (state) => state.relations?.stats?.broken,
27
+ (state) => state.relations?.stats?.data?.broken,
28
28
  );
29
29
 
30
- const relations_stats = useSelector((state) => state.relations?.stats?.stats);
30
+ const relations_stats = useSelector((state) => state.relations?.stats?.data);
31
31
  const actions = useSelector((state) => state.actions?.actions ?? {});
32
32
  const can_edit = find(actions.object, {
33
33
  id: 'edit',
@@ -58,7 +58,7 @@ const RelationsControlPanel = () => {
58
58
  <h1>
59
59
  <FormattedMessage id="Relations" defaultMessage="Relations" />
60
60
  </h1>
61
- {!relations_stats ? (
61
+ {relations_stats?.error ? (
62
62
  <React.Fragment>
63
63
  <Divider hidden />
64
64
  <Message warning>
@@ -11,6 +11,7 @@ import {
11
11
  createRelations,
12
12
  deleteRelations,
13
13
  queryRelations,
14
+ resetSearchContent,
14
15
  searchContent,
15
16
  } from '@plone/volto/actions';
16
17
 
@@ -28,9 +29,10 @@ const ListingTemplate = ({
28
29
  const MAX = 40; // Maximum of rows and columns
29
30
  const MAX_RELATIONS = 1000;
30
31
 
31
- const stats = useSelector((state) => state.relations?.stats || null);
32
+ const stats = useSelector((state) => state.relations?.stats?.data || null);
33
+
32
34
  let relations = useSelector(
33
- (state) => state.relations?.relations?.[relationtype]?.items || [],
35
+ (state) => state.relations?.relations?.data?.[relationtype]?.items || [],
34
36
  );
35
37
 
36
38
  let potential_targets_objects = useSelector(
@@ -49,7 +51,8 @@ const ListingTemplate = ({
49
51
 
50
52
  // Editable if plone.api.relations available
51
53
  const editable = useSelector(
52
- (state) => state.relations?.relations?.[relationtype]?.readonly !== true,
54
+ (state) =>
55
+ state.relations?.relations?.data?.[relationtype]?.readonly !== true,
53
56
  );
54
57
 
55
58
  let relationMatrix = {};
@@ -192,8 +195,7 @@ const ListingTemplate = ({
192
195
  ),
193
196
  );
194
197
  } else {
195
- // TODO Better just reset redux store
196
- dispatch(searchContent('/findstenichätsch', null, 'potential_targets'));
198
+ dispatch(resetSearchContent('potential_targets'));
197
199
  }
198
200
 
199
201
  // Fetch fresh potential sources
@@ -211,8 +213,7 @@ const ListingTemplate = ({
211
213
  ),
212
214
  );
213
215
  } else {
214
- // TODO Better just reset redux store
215
- dispatch(searchContent('/findstenichätsch', null, 'potential_sources'));
216
+ dispatch(resetSearchContent('potential_sources'));
216
217
  }
217
218
  }, [
218
219
  dispatch,
@@ -17,7 +17,11 @@ import {
17
17
  import withObjectBrowser from '@plone/volto/components/manage/Sidebar/ObjectBrowser';
18
18
  import { messages } from '@plone/volto/helpers';
19
19
  import { Icon, Toast } from '@plone/volto/components';
20
- import { rebuildRelations, queryRelations } from '@plone/volto/actions';
20
+ import {
21
+ getRelationStats,
22
+ queryRelations,
23
+ rebuildRelations,
24
+ } from '@plone/volto/actions';
21
25
  import RelationsListing from './RelationsListing';
22
26
  import BrokenRelations from './BrokenRelations';
23
27
  import helpSVG from '@plone/volto/icons/help.svg';
@@ -39,12 +43,14 @@ const RelationsMatrix = (props) => {
39
43
  id: 'plone_setup',
40
44
  });
41
45
 
42
- const relationtypes = useSelector((state) => state.relations?.stats?.stats);
46
+ const relationtypes = useSelector(
47
+ (state) => state.relations?.stats?.data?.stats,
48
+ );
43
49
  const relationsListError = useSelector(
44
- (state) => state.relations?.list?.error?.response?.body?.error,
50
+ (state) => state.relations?.stats?.error?.response?.body?.error,
45
51
  );
46
52
  const brokenRelations = useSelector(
47
- (state) => state.relations?.stats?.broken,
53
+ (state) => state.relations?.stats?.data?.broken,
48
54
  );
49
55
 
50
56
  let filter_options = useSelector((state) => state.groups.filter_groups);
@@ -67,7 +73,7 @@ const RelationsMatrix = (props) => {
67
73
  }
68
74
 
69
75
  useEffect(() => {
70
- dispatch(queryRelations());
76
+ dispatch(getRelationStats());
71
77
  }, [dispatch]);
72
78
 
73
79
  const onReset = (event) => {
@@ -128,9 +134,7 @@ const RelationsMatrix = (props) => {
128
134
  const rebuildRelationsHandler = (flush = false) => {
129
135
  dispatch(rebuildRelations(flush))
130
136
  .then(() => {
131
- dispatch(queryRelations());
132
- })
133
- .then(() => {
137
+ dispatch(getRelationStats());
134
138
  dispatch(queryRelations(null, true, 'broken'));
135
139
  })
136
140
  .then(() => {
@@ -443,7 +447,9 @@ const RelationsMatrix = (props) => {
443
447
  ) : null}
444
448
  </div>
445
449
  ) : (
446
- <p>{relationsListError?.message}</p>
450
+ <p>
451
+ <b>{relationsListError?.type}</b> {relationsListError?.message}
452
+ </p>
447
453
  )}
448
454
  </Tab.Pane>
449
455
  ),
@@ -179,7 +179,7 @@ class AddRule extends Component {
179
179
  const { title, description, event, cascading, stop, enabled } = this.state;
180
180
  const triggeringEvents =
181
181
  this.props.events?.items && this.props.events?.items.length > 0
182
- ? this.props.events?.items.map((event) => [event.title, event.token])
182
+ ? this.props.events?.items.map((event) => [event.token, event.title])
183
183
  : '';
184
184
 
185
185
  return (
@@ -209,7 +209,7 @@ class EditRule extends Component {
209
209
 
210
210
  const triggeringEvents =
211
211
  this.props.events?.items && this.props.events?.items.length > 0
212
- ? this.props.events?.items.map((event) => [event.title, event.token])
212
+ ? this.props.events?.items.map((event) => [event.token, event.title])
213
213
  : '';
214
214
 
215
215
  return (
@@ -7,8 +7,14 @@ import React, { Component } from 'react';
7
7
  import { FormattedMessage, injectIntl } from 'react-intl';
8
8
  import { Dropdown, Table, Checkbox } from 'semantic-ui-react';
9
9
  import trashSVG from '@plone/volto/icons/delete.svg';
10
- import { Icon } from '@plone/volto/components';
10
+ import editSVG from '@plone/volto/icons/editing.svg';
11
+ import { Icon, ModalForm, Toast } from '@plone/volto/components';
12
+ import { updateUser } from '@plone/volto/actions';
11
13
  import ploneSVG from '@plone/volto/icons/plone.svg';
14
+ import { compose } from 'redux';
15
+ import { connect } from 'react-redux';
16
+ import { messages } from '@plone/volto/helpers';
17
+ import { toast } from 'react-toastify';
12
18
 
13
19
  /**
14
20
  * UsersControlpanelUser class.
@@ -43,9 +49,14 @@ class RenderUsers extends Component {
43
49
  */
44
50
  constructor(props) {
45
51
  super(props);
46
- this.state = {};
52
+ this.state = {
53
+ user: {},
54
+ };
47
55
  this.onChange = this.onChange.bind(this);
56
+ this.onEditUserError = this.onEditUserError.bind(this);
57
+ this.onEditUserSubmit = this.onEditUserSubmit.bind(this);
48
58
  }
59
+
49
60
  /**
50
61
  * @param {*} event
51
62
  * @param {*} { value }
@@ -56,6 +67,49 @@ class RenderUsers extends Component {
56
67
  const [user, role] = value.split('&role=');
57
68
  this.props.updateUser(user, role);
58
69
  }
70
+
71
+ componentDidUpdate(prevProps) {
72
+ if (
73
+ prevProps.updateRequest.loading &&
74
+ this.props.updateRequest.loaded &&
75
+ this.state?.user?.id === this.props?.user?.id
76
+ ) {
77
+ this.setState({ user: {} });
78
+ this.props.listUsers();
79
+ return toast.success(
80
+ <Toast
81
+ success
82
+ title={this.props.intl.formatMessage(messages.success)}
83
+ content={this.props.intl.formatMessage(messages.updateUserSuccess)}
84
+ />,
85
+ );
86
+ }
87
+ }
88
+
89
+ onEditUserSubmit(data, callback) {
90
+ // Do not handle groups and roles in this form
91
+ delete data.groups;
92
+ delete data.roles;
93
+ this.props.updateUserData(data.id, data);
94
+ }
95
+
96
+ onEditUserError() {
97
+ return toast.error(
98
+ <Toast
99
+ error
100
+ title={this.props.intl.formatMessage(messages.error)}
101
+ content={this.props.intl.formatMessage(
102
+ messages.addUserFormPasswordAndSendPasswordTogetherNotAllowed,
103
+ )}
104
+ />,
105
+ );
106
+ }
107
+
108
+ onClickEdit(props) {
109
+ const { formData } = props;
110
+ this.setState({ user: { ...formData } });
111
+ }
112
+
59
113
  /**
60
114
  * Render method.
61
115
  * @method render
@@ -67,7 +121,8 @@ class RenderUsers extends Component {
67
121
  <Table.Cell className="fullname">
68
122
  {this.props.user.fullname
69
123
  ? this.props.user.fullname
70
- : this.props.user.username}
124
+ : this.props.user.username}{' '}
125
+ ({this.props.user.username})
71
126
  </Table.Cell>
72
127
  {this.props.roles.map((role) => (
73
128
  <Table.Cell key={role.id}>
@@ -91,7 +146,20 @@ class RenderUsers extends Component {
91
146
  <Table.Cell textAlign="right">
92
147
  <Dropdown icon="ellipsis horizontal">
93
148
  <Dropdown.Menu className="left">
149
+ {this.props.userschema && (
150
+ <Dropdown.Item
151
+ id="edit-user-button"
152
+ onClick={() => {
153
+ this.onClickEdit({ formData: this.props.user });
154
+ }}
155
+ value={this.props.user['@id']}
156
+ >
157
+ <Icon name={editSVG} size="15px" />
158
+ <FormattedMessage id="Edit" defaultMessage="Edit" />
159
+ </Dropdown.Item>
160
+ )}
94
161
  <Dropdown.Item
162
+ id="delete-user-button"
95
163
  onClick={this.props.onDelete}
96
164
  value={this.props.user['@id']}
97
165
  >
@@ -101,9 +169,31 @@ class RenderUsers extends Component {
101
169
  </Dropdown.Menu>
102
170
  </Dropdown>
103
171
  </Table.Cell>
172
+ {Object.keys(this.state.user).length > 0 &&
173
+ this.props.userschema.loaded && (
174
+ <ModalForm
175
+ className="modal"
176
+ onSubmit={this.onEditUserSubmit}
177
+ submitError={this.state.editUserError}
178
+ formData={this.state.user}
179
+ onCancel={() => this.setState({ user: {} })}
180
+ title={this.props.intl.formatMessage(
181
+ messages.updateUserFormTitle,
182
+ )}
183
+ loading={this.props.updateRequest.loading}
184
+ schema={this.props.userschema.userschema}
185
+ />
186
+ )}
104
187
  </Table.Row>
105
188
  );
106
189
  }
107
190
  }
108
-
109
- export default injectIntl(RenderUsers);
191
+ export default compose(
192
+ injectIntl,
193
+ connect(
194
+ (state, props) => ({
195
+ updateRequest: state.users?.update,
196
+ }),
197
+ { updateUserData: updateUser },
198
+ ),
199
+ )(RenderUsers);