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

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 (69) hide show
  1. package/.gitignore~ +71 -0
  2. package/.yarn/install-state.gz +0 -0
  3. package/CHANGELOG.md +41 -0
  4. package/locales/ca/LC_MESSAGES/volto.po +14 -4
  5. package/locales/ca.json +1 -1
  6. package/locales/de/LC_MESSAGES/volto.po +27 -17
  7. package/locales/de.json +1 -1
  8. package/locales/en/LC_MESSAGES/volto.po +15 -5
  9. package/locales/en.json +1 -1
  10. package/locales/es/LC_MESSAGES/volto.po +15 -5
  11. package/locales/es.json +1 -1
  12. package/locales/eu/LC_MESSAGES/volto.po +14 -4
  13. package/locales/eu.json +1 -1
  14. package/locales/fi/LC_MESSAGES/volto.po +14 -4
  15. package/locales/fi.json +1 -1
  16. package/locales/fr/LC_MESSAGES/volto.po +14 -4
  17. package/locales/fr.json +1 -1
  18. package/locales/it/LC_MESSAGES/volto.po +239 -229
  19. package/locales/it.json +1 -1
  20. package/locales/ja/LC_MESSAGES/volto.po +14 -4
  21. package/locales/ja.json +1 -1
  22. package/locales/nl/LC_MESSAGES/volto.po +14 -4
  23. package/locales/nl.json +1 -1
  24. package/locales/pt/LC_MESSAGES/volto.po +14 -4
  25. package/locales/pt.json +1 -1
  26. package/locales/pt_BR/LC_MESSAGES/volto.po +15 -5
  27. package/locales/pt_BR.json +1 -1
  28. package/locales/ro/LC_MESSAGES/volto.po +14 -4
  29. package/locales/ro.json +1 -1
  30. package/locales/volto.pot +15 -5
  31. package/locales/zh_CN/LC_MESSAGES/volto.po +14 -4
  32. package/locales/zh_CN.json +1 -1
  33. package/news/4547.breaking~ +1 -0
  34. package/package.json +3 -3
  35. package/packages/volto-slate/package.json +1 -1
  36. package/src/actions/relations/rebuild.js +7 -7
  37. package/src/components/manage/Actions/Actions.jsx +133 -243
  38. package/src/components/manage/Blocks/Container/Edit.jsx +4 -1
  39. package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +1 -0
  40. package/src/components/manage/Blocks/Grid/Edit.jsx +15 -1
  41. package/src/components/manage/Blocks/Image/View.jsx +2 -1
  42. package/src/components/manage/Blocks/Maps/Edit.jsx +135 -209
  43. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +3 -2
  44. package/src/components/manage/Contents/ContentsPropertiesModal.jsx +1 -13
  45. package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +2 -2
  46. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +30 -7
  47. package/src/components/manage/Controlpanels/Relations/Relations.jsx +2 -2
  48. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +53 -59
  49. package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +2 -2
  50. package/src/components/manage/Delete/Delete.jsx +96 -171
  51. package/src/components/manage/Widgets/SelectUtils.js +1 -1
  52. package/src/components/manage/Workflow/Workflow.jsx +75 -184
  53. package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +95 -170
  54. package/src/config/Components.jsx +1 -0
  55. package/src/config/index.js~ +223 -0
  56. package/src/express-middleware/files.js +8 -6
  57. package/src/express-middleware/images.js +7 -1
  58. package/src/helpers/MessageLabels/MessageLabels.js +6 -0
  59. package/src/reducers/relations/relations.js +1 -1
  60. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +0 -90
  61. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +0 -6
  62. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +0 -6
  63. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +0 -6
  64. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +0 -10
  65. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +0 -10
  66. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +0 -30
  67. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +0 -10
  68. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +0 -6
  69. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +0 -6
@@ -1,14 +1,8 @@
1
- /**
2
- * Edit map block.
3
- * @module components/manage/Blocks/Maps/Edit
4
- */
5
-
6
- import React, { Component } from 'react';
1
+ import React, { useState, useCallback, useMemo } from 'react';
7
2
  import PropTypes from 'prop-types';
8
3
  import { Button, Input, Message } from 'semantic-ui-react';
9
- import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
4
+ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
10
5
  import cx from 'classnames';
11
- import { isEqual } from 'lodash';
12
6
  import { withBlockExtensions } from '@plone/volto/helpers';
13
7
  import { compose } from 'redux';
14
8
  import { Icon, SidebarPortal, MapsSidebar } from '@plone/volto/components';
@@ -43,230 +37,162 @@ const messages = defineMessages({
43
37
  },
44
38
  });
45
39
 
46
- /**
47
- * Edit image block class.
48
- * @class Edit
49
- * @extends Component
50
- */
51
- class Edit extends Component {
52
- /**
53
- * Property types.
54
- * @property {Object} propTypes Property types.
55
- * @static
56
- */
57
- static propTypes = {
58
- selected: PropTypes.bool.isRequired,
59
- block: PropTypes.string.isRequired,
60
- index: PropTypes.number.isRequired,
61
- data: PropTypes.objectOf(PropTypes.any).isRequired,
62
- pathname: PropTypes.string.isRequired,
63
- onChangeBlock: PropTypes.func.isRequired,
64
- onSelectBlock: PropTypes.func.isRequired,
65
- onDeleteBlock: PropTypes.func.isRequired,
66
- onFocusPreviousBlock: PropTypes.func.isRequired,
67
- onFocusNextBlock: PropTypes.func.isRequired,
68
- handleKeyDown: PropTypes.func.isRequired,
69
- };
70
-
71
- /**
72
- * Constructor
73
- * @method constructor
74
- * @param {Object} props Component properties
75
- * @constructs WysiwygEditor
76
- */
77
- constructor(props) {
78
- super(props);
79
- this.getSrc = this.getSrc.bind(this);
80
- this.state = {
81
- url: '',
82
- error: null,
83
- };
84
- this.onSubmitUrl = this.onSubmitUrl.bind(this);
85
- this.onKeyDownVariantMenuForm = this.onKeyDownVariantMenuForm.bind(this);
86
- }
87
-
88
- /**
89
- * @param {*} nextProps
90
- * @returns {boolean}
91
- * @memberof Edit
92
- */
93
- shouldComponentUpdate(nextProps) {
94
- return (
95
- this.props.selected ||
96
- nextProps.selected ||
97
- !isEqual(this.props.data, nextProps.data)
98
- );
99
- }
40
+ const Edit = React.memo((props) => {
41
+ const intl = useIntl();
42
+ const [url, setUrl] = useState('');
43
+ const [error, setError] = useState(null);
100
44
 
101
- /**
102
- * Change url handler
103
- * @method onChangeUrl
104
- * @param {Object} target Target object
105
- * @returns {undefined}
106
- */
107
- onChangeUrl = ({ target }) => {
108
- this.setState({
109
- url: target.value,
110
- });
45
+ const { onChangeBlock, data, block, selected } = props;
46
+ const onChangeUrl = ({ target }) => {
47
+ setUrl(target.value);
111
48
  };
112
49
 
113
- /**
114
- * Submit url handler
115
- * @method onSubmitUrl
116
- * @param {string} e event
117
- * @returns {undefined}
118
- */
119
- onSubmitUrl() {
120
- this.props.onChangeBlock(this.props.block, {
121
- ...this.props.data,
122
- url: this.getSrc(this.state.url),
50
+ const onSubmitUrl = useCallback(() => {
51
+ onChangeBlock(block, {
52
+ ...data,
53
+ url: getSrc(url),
123
54
  });
124
- }
55
+ }, [onChangeBlock, block, data, url]);
125
56
 
126
- /**
127
- * Keydown handler on Variant Menu Form
128
- * This is required since the ENTER key is already mapped to a onKeyDown
129
- * event and needs to be overriden with a child onKeyDown.
130
- * @method onKeyDownVariantMenuForm
131
- * @param {Object} e Event object
132
- * @returns {undefined}
133
- */
134
- onKeyDownVariantMenuForm(e) {
135
- if (e.key === 'Enter') {
136
- e.preventDefault();
137
- e.stopPropagation();
138
- this.onSubmitUrl();
139
- } else if (e.key === 'Escape') {
140
- e.preventDefault();
141
- e.stopPropagation();
142
- // TODO: Do something on ESC key
143
- }
144
- }
57
+ const onKeyDownVariantMenuForm = useCallback(
58
+ (e) => {
59
+ if (e.key === 'Enter') {
60
+ e.preventDefault();
61
+ e.stopPropagation();
62
+ onSubmitUrl();
63
+ } else if (e.key === 'Escape') {
64
+ e.preventDefault();
65
+ e.stopPropagation();
66
+ // TODO: Do something on ESC key
67
+ }
68
+ },
69
+ [onSubmitUrl],
70
+ );
145
71
 
146
- /**
147
- * get getSrc handler
148
- * @method getSrc
149
- * @param {string} embed Embed HTML code from Google Maps share option
150
- * @returns {string} Source URL
151
- */
152
- getSrc(embed) {
72
+ const getSrc = (embed) => {
153
73
  const parser = new DOMParser();
154
74
  const doc = parser.parseFromString(embed, 'text/html');
155
75
  const iframe = doc.getElementsByTagName('iframe');
156
76
  if (iframe.length === 0) {
157
- this.setState({ error: true });
77
+ setError(true);
158
78
  return '';
159
79
  }
160
- this.setState({ error: false });
80
+ setError(false);
161
81
  return iframe[0].src;
162
- }
82
+ };
163
83
 
164
- resetSubmitUrl = () => {
165
- this.setState({
166
- url: '',
167
- });
84
+ const resetSubmitUrl = () => {
85
+ setUrl('');
168
86
  };
169
87
 
170
- /**
171
- * Render method.
172
- * @method render
173
- * @returns {string} Markup for the component.
174
- */
175
- render() {
176
- const placeholder =
177
- this.props.data.placeholder ||
178
- this.props.intl.formatMessage(messages.MapsBlockInputPlaceholder);
179
- return (
180
- <div
181
- className={cx(
182
- 'block maps align',
183
- {
184
- center: !Boolean(this.props.data.align),
185
- },
186
- this.props.data.align,
187
- )}
188
- >
189
- {this.props.data.url ? (
190
- <div
191
- className={cx('maps-inner', {
192
- 'full-width': this.props.data.align === 'full',
193
- })}
194
- >
195
- <iframe
196
- title={this.props.intl.formatMessage(
197
- messages.GoogleMapsEmbeddedBlock,
198
- )}
199
- src={this.props.data.url}
200
- className="google-map"
201
- frameBorder="0"
202
- allowFullScreen
203
- />
204
- </div>
205
- ) : (
206
- <Message>
207
- <center>
208
- <img src={mapsBlockSVG} alt="" />
209
- <div className="toolbar-inner">
210
- <Input
211
- onKeyDown={this.onKeyDownVariantMenuForm}
212
- onChange={this.onChangeUrl}
213
- placeholder={placeholder}
214
- value={this.state.url}
215
- // Prevents propagation to the Dropzone and the opening
216
- // of the upload browser dialog
217
- onClick={(e) => e.stopPropagation()}
218
- />
219
- {this.state.url && (
220
- <Button.Group>
221
- <Button
222
- basic
223
- className="cancel"
224
- onClick={(e) => {
225
- e.stopPropagation();
226
- this.setState({ url: '' });
227
- }}
228
- >
229
- <Icon name={clearSVG} size="30px" />
230
- </Button>
231
- </Button.Group>
232
- )}
88
+ const placeholder = useMemo(
89
+ () =>
90
+ data.placeholder ||
91
+ intl.formatMessage(messages.MapsBlockInputPlaceholder),
92
+ [data, intl],
93
+ );
94
+
95
+ return (
96
+ <div
97
+ className={cx(
98
+ 'block maps align',
99
+ {
100
+ center: !Boolean(data.align),
101
+ },
102
+ data.align,
103
+ )}
104
+ >
105
+ {data.url ? (
106
+ <div
107
+ className={cx('maps-inner', {
108
+ 'full-width': data.align === 'full',
109
+ })}
110
+ >
111
+ <iframe
112
+ title={intl.formatMessage(messages.GoogleMapsEmbeddedBlock)}
113
+ src={data.url}
114
+ className="google-map"
115
+ frameBorder="0"
116
+ allowFullScreen
117
+ />
118
+ </div>
119
+ ) : (
120
+ <Message>
121
+ <center>
122
+ <img src={mapsBlockSVG} alt="" />
123
+ <div className="toolbar-inner">
124
+ <Input
125
+ onKeyDown={onKeyDownVariantMenuForm}
126
+ onChange={onChangeUrl}
127
+ placeholder={placeholder}
128
+ value={url}
129
+ // Prevents propagation to the Dropzone and the opening
130
+ // of the upload browser dialog
131
+ onClick={(e) => e.stopPropagation()}
132
+ />
133
+ {url && (
233
134
  <Button.Group>
234
135
  <Button
235
136
  basic
236
- primary
137
+ className="cancel"
237
138
  onClick={(e) => {
238
139
  e.stopPropagation();
239
- this.onSubmitUrl();
140
+ setUrl('');
240
141
  }}
241
142
  >
242
- <Icon name={aheadSVG} size="30px" />
143
+ <Icon name={clearSVG} size="30px" />
243
144
  </Button>
244
145
  </Button.Group>
245
- </div>
246
- <div className="message-text">
247
- <FormattedMessage
248
- id="Please enter the Embed Code provided by Google Maps -> Share -> Embed map. It should contain the <iframe> code on it."
249
- defaultMessage="Please enter the Embed Code provided by Google Maps -> Share -> Embed map. It should contain the <iframe> code on it."
250
- />
251
- {this.state.error && (
252
- <div style={{ color: 'red' }}>
253
- <FormattedMessage
254
- id="Embed code error, please follow the instructions and try again."
255
- defaultMessage="Embed code error, please follow the instructions and try again."
256
- />
257
- </div>
258
- )}
259
- </div>
260
- </center>
261
- </Message>
262
- )}
263
- {!this.props.selected && <div className="map-overlay" />}
264
- <SidebarPortal selected={this.props.selected}>
265
- <MapsSidebar {...this.props} resetSubmitUrl={this.resetSubmitUrl} />
266
- </SidebarPortal>
267
- </div>
268
- );
269
- }
270
- }
146
+ )}
147
+ <Button.Group>
148
+ <Button
149
+ basic
150
+ primary
151
+ onClick={(e) => {
152
+ e.stopPropagation();
153
+ onSubmitUrl();
154
+ }}
155
+ >
156
+ <Icon name={aheadSVG} size="30px" />
157
+ </Button>
158
+ </Button.Group>
159
+ </div>
160
+ <div className="message-text">
161
+ <FormattedMessage
162
+ id="Please enter the Embed Code provided by Google Maps -> Share -> Embed map. It should contain the <iframe> code on it."
163
+ defaultMessage="Please enter the Embed Code provided by Google Maps -> Share -> Embed map. It should contain the <iframe> code on it."
164
+ />
165
+ {error && (
166
+ <div style={{ color: 'red' }}>
167
+ <FormattedMessage
168
+ id="Embed code error, please follow the instructions and try again."
169
+ defaultMessage="Embed code error, please follow the instructions and try again."
170
+ />
171
+ </div>
172
+ )}
173
+ </div>
174
+ </center>
175
+ </Message>
176
+ )}
177
+ {!selected && <div className="map-overlay" />}
178
+ <SidebarPortal selected={selected}>
179
+ <MapsSidebar {...props} resetSubmitUrl={resetSubmitUrl} />
180
+ </SidebarPortal>
181
+ </div>
182
+ );
183
+ });
271
184
 
272
- export default compose(injectIntl, withBlockExtensions)(Edit);
185
+ Edit.propTypes = {
186
+ selected: PropTypes.bool.isRequired,
187
+ block: PropTypes.string.isRequired,
188
+ index: PropTypes.number.isRequired,
189
+ data: PropTypes.objectOf(PropTypes.any).isRequired,
190
+ pathname: PropTypes.string.isRequired,
191
+ onChangeBlock: PropTypes.func.isRequired,
192
+ onSelectBlock: PropTypes.func.isRequired,
193
+ onDeleteBlock: PropTypes.func.isRequired,
194
+ onFocusPreviousBlock: PropTypes.func.isRequired,
195
+ onFocusNextBlock: PropTypes.func.isRequired,
196
+ handleKeyDown: PropTypes.func.isRequired,
197
+ };
198
+ export default compose(withBlockExtensions)(Edit);
@@ -9,6 +9,7 @@ import { withSearch, withQueryString } from './hocs';
9
9
  import { compose } from 'redux';
10
10
  import { useSelector } from 'react-redux';
11
11
  import { isEqual, isFunction } from 'lodash';
12
+ import cx from 'classnames';
12
13
 
13
14
  const getListingBodyVariation = (data) => {
14
15
  const { variations } = config.blocks.blocksConfig.listing;
@@ -57,7 +58,7 @@ const applyDefaults = (data, root) => {
57
58
  };
58
59
 
59
60
  const SearchBlockView = (props) => {
60
- const { id, data, searchData, mode = 'view', variation } = props;
61
+ const { id, data, searchData, mode = 'view', variation, className } = props;
61
62
 
62
63
  const Layout = variation.view;
63
64
 
@@ -81,7 +82,7 @@ const SearchBlockView = (props) => {
81
82
  const listingBodyVariation = variations.find(({ id }) => id === selectedView);
82
83
 
83
84
  return (
84
- <div className="block search">
85
+ <div className={cx('block search', selectedView, className)}>
85
86
  <Layout
86
87
  {...props}
87
88
  isEditMode={mode === 'edit'}
@@ -70,14 +70,6 @@ const messages = defineMessages({
70
70
  defaultMessage:
71
71
  'If selected, this item will not appear in the navigation tree',
72
72
  },
73
- yes: {
74
- id: 'Yes',
75
- defaultMessage: 'Yes',
76
- },
77
- no: {
78
- id: 'No',
79
- defaultMessage: 'No',
80
- },
81
73
  });
82
74
 
83
75
  /**
@@ -209,11 +201,7 @@ class ContentsPropertiesModal extends Component {
209
201
  title: this.props.intl.formatMessage(
210
202
  messages.excludeFromNavTitle,
211
203
  ),
212
- type: 'array',
213
- choices: [
214
- [true, this.props.intl.formatMessage(messages.yes)],
215
- [false, this.props.intl.formatMessage(messages.no)],
216
- ],
204
+ type: 'boolean',
217
205
  },
218
206
  },
219
207
  required: [],
@@ -57,7 +57,7 @@ class RenderGroups extends Component {
57
57
  * @memberof UsersControlpanelUser
58
58
  */
59
59
  onChange(event, { value }) {
60
- const [group, role] = value.split('.');
60
+ const [group, role] = value.split('&role=');
61
61
  this.props.updateGroups(group, role);
62
62
  }
63
63
 
@@ -97,7 +97,7 @@ class RenderGroups extends Component {
97
97
  : this.props.group.roles.includes(role.id)
98
98
  }
99
99
  onChange={this.onChange}
100
- value={`${this.props.group.id}.${role.id}`}
100
+ value={`${this.props.group.id}&role=${role.id}`}
101
101
  />
102
102
  )}
103
103
  </Table.Cell>
@@ -34,15 +34,31 @@ const BrokenRelations = () => {
34
34
  <div key={relationname}>
35
35
  <Divider section hidden />
36
36
  <h4>
37
- {brokenRelationStats[relationname]} broken <i>{relationname}</i>{' '}
38
- relations
37
+ <FormattedMessage
38
+ id="countBrokenRelations"
39
+ defaultMessage="{countofrelation} broken {countofrelation, plural, one {relation} other {relations}} of type {typeofrelation}"
40
+ values={{
41
+ countofrelation: brokenRelationStats[relationname],
42
+ typeofrelation: relationname,
43
+ }}
44
+ />
39
45
  </h4>
40
- <Table>
46
+ <Table compact="very">
47
+ <Table.Header>
48
+ <Table.Row>
49
+ <Table.HeaderCell width={6}>
50
+ <FormattedMessage id="Source" defaultMessage="Source" />
51
+ </Table.HeaderCell>
52
+ <Table.HeaderCell>
53
+ <FormattedMessage id="Target" defaultMessage="Target" />
54
+ </Table.HeaderCell>
55
+ </Table.Row>
56
+ </Table.Header>
41
57
  <Table.Body>
42
58
  {uniqBy(brokenRelations[relationname].items, function (el) {
43
- return el[0];
44
- }).map((el) => (
45
- <Table.Row key={el[0]}>
59
+ return el.toString();
60
+ }).map((el, index) => (
61
+ <Table.Row key={index}>
46
62
  <Table.Cell>
47
63
  <UniversalLink
48
64
  href={`${flattenToAppURL(el[0])}/edit`}
@@ -51,7 +67,14 @@ const BrokenRelations = () => {
51
67
  {flattenToAppURL(el[0])}
52
68
  </UniversalLink>
53
69
  </Table.Cell>
54
- <Table.Cell>{el[1]}</Table.Cell>
70
+ <Table.Cell>
71
+ <UniversalLink
72
+ href={`${flattenToAppURL(el[1])}/edit`}
73
+ openLinkInNewTab={true}
74
+ >
75
+ {flattenToAppURL(el[1])}
76
+ </UniversalLink>
77
+ </Table.Cell>
55
78
  </Table.Row>
56
79
  ))}
57
80
  </Table.Body>
@@ -63,8 +63,8 @@ const RelationsControlPanel = () => {
63
63
  <Divider hidden />
64
64
  <Message warning>
65
65
  <FormattedMessage
66
- id="Please upgrade to plone.restapi >= 8.35.3."
67
- defaultMessage="Please upgrade to plone.restapi >= 8.35.3."
66
+ id="Please upgrade to plone.restapi >= 8.39.0."
67
+ defaultMessage="Please upgrade to plone.restapi >= 8.39.0."
68
68
  />
69
69
  </Message>
70
70
  </React.Fragment>
@@ -452,68 +452,62 @@ const RelationsMatrix = (props) => {
452
452
  menuItem: intl.formatMessage(messages.fixRelations),
453
453
  pane: (
454
454
  <Tab.Pane attached={true} key="rebuild">
455
- {brokenRelations && Object.keys(brokenRelations).length > 0 ? (
456
- <div>
457
- {can_fix_relations ? (
458
- <React.Fragment>
459
- <Divider hidden />
460
- <h2>
461
- {capitalize(intl.formatMessage(messages.rebuildRelations))}
462
- </h2>
455
+ <div>
456
+ {!(brokenRelations && Object.keys(brokenRelations).length > 0) && (
457
+ <div>
458
+ <FormattedMessage
459
+ id="No broken relations found."
460
+ defaultMessage="No broken relations found."
461
+ />
462
+ </div>
463
+ )}
464
+ {can_fix_relations ? (
465
+ <React.Fragment>
466
+ <Divider hidden />
467
+ <h2>
468
+ {capitalize(intl.formatMessage(messages.rebuildRelations))}
469
+ </h2>
463
470
 
464
- <Button.Group>
465
- <Button
466
- primary
467
- onClick={() => rebuildRelationsHandler(false)}
468
- content={intl.formatMessage(messages.rebuildRelations)}
469
- aria-label={intl.formatMessage(messages.rebuildRelations)}
470
- />
471
- </Button.Group>
471
+ <Button.Group>
472
+ <Button
473
+ primary
474
+ onClick={() => rebuildRelationsHandler(false)}
475
+ content={intl.formatMessage(messages.rebuildRelations)}
476
+ aria-label={intl.formatMessage(messages.rebuildRelations)}
477
+ />
478
+ </Button.Group>
472
479
 
473
- <Divider hidden />
474
- <h2>
475
- {capitalize(
476
- intl.formatMessage(messages.flushAndRebuildRelations),
480
+ <Divider hidden />
481
+ <h2>
482
+ {capitalize(
483
+ intl.formatMessage(messages.flushAndRebuildRelations),
484
+ )}
485
+ </h2>
486
+ <div
487
+ dangerouslySetInnerHTML={{
488
+ __html: intl.formatMessage(
489
+ messages.flushAndRebuildRelationsHints,
490
+ ),
491
+ }}
492
+ />
493
+ <Divider hidden />
494
+ <Button.Group>
495
+ <Button
496
+ secondary
497
+ color="red"
498
+ onClick={() => rebuildRelationsHandler(true)}
499
+ content={intl.formatMessage(
500
+ messages.flushAndRebuildRelations,
477
501
  )}
478
- </h2>
479
- <ul>
480
- <li>
481
- Regenerate intIds (tokens of relations in relation
482
- catalog)
483
- </li>
484
- <li>Rebuild relations</li>
485
- </ul>
486
- <p>Check the log for details!</p>
487
- <p>
488
- <b>Warning</b>: If you have add-ons relying on intIds, you
489
- should not flush them.
490
- </p>
491
- <Divider hidden />
492
- <Button.Group>
493
- <Button
494
- secondary
495
- color="red"
496
- onClick={() => rebuildRelationsHandler(true)}
497
- content={intl.formatMessage(
498
- messages.flushAndRebuildRelations,
499
- )}
500
- aria-label={intl.formatMessage(
501
- messages.flushAndRebuildRelations,
502
- )}
503
- />
504
- </Button.Group>
505
- </React.Fragment>
506
- ) : null}
507
- <BrokenRelations />
508
- </div>
509
- ) : (
510
- <div>
511
- <FormattedMessage
512
- id="No broken relations found."
513
- defaultMessage="No broken relations found."
514
- />
515
- </div>
516
- )}
502
+ aria-label={intl.formatMessage(
503
+ messages.flushAndRebuildRelations,
504
+ )}
505
+ />
506
+ </Button.Group>
507
+ </React.Fragment>
508
+ ) : null}
509
+ <BrokenRelations />
510
+ </div>
517
511
  </Tab.Pane>
518
512
  ),
519
513
  },
@@ -53,7 +53,7 @@ class RenderUsers extends Component {
53
53
  */
54
54
 
55
55
  onChange(event, { value }) {
56
- const [user, role] = value.split('.');
56
+ const [user, role] = value.split('&role=');
57
57
  this.props.updateUser(user, role);
58
58
  }
59
59
  /**
@@ -83,7 +83,7 @@ class RenderUsers extends Component {
83
83
  <Checkbox
84
84
  checked={this.props.user.roles.includes(role.id)}
85
85
  onChange={this.onChange}
86
- value={`${this.props.user.id}.${role.id}`}
86
+ value={`${this.props.user.id}&role=${role.id}`}
87
87
  />
88
88
  )}
89
89
  </Table.Cell>