@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,21 +1,14 @@
1
- /**
2
- * Delete container.
3
- * @module components/manage/Delete/Delete
4
- */
5
-
6
- import React, { Component } from 'react';
7
- import PropTypes from 'prop-types';
8
- import { Helmet } from '@plone/volto/helpers';
9
- import { connect } from 'react-redux';
10
- import { compose } from 'redux';
11
- import { withRouter } from 'react-router-dom';
1
+ import { useEffect, useState } from 'react';
2
+ import { useDispatch, useSelector, shallowEqual } from 'react-redux';
3
+ import { useHistory, useLocation } from 'react-router-dom';
12
4
  import { Portal } from 'react-portal';
13
5
  import { Button, Container, List, Segment } from 'semantic-ui-react';
14
- import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
6
+ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
15
7
  import qs from 'query-string';
16
8
 
17
- import { deleteContent, getContent } from '@plone/volto/actions';
18
9
  import { Toolbar } from '@plone/volto/components';
10
+ import { Helmet, usePrevious } from '@plone/volto/helpers';
11
+ import { deleteContent, getContent } from '@plone/volto/actions';
19
12
 
20
13
  const messages = defineMessages({
21
14
  delete: {
@@ -32,172 +25,104 @@ const messages = defineMessages({
32
25
  },
33
26
  });
34
27
 
35
- /**
36
- * Delete container class.
37
- * @class Delete
38
- * @extends Component
39
- */
40
- class Delete extends Component {
41
- /**
42
- * Property types.
43
- * @property {Object} propTypes Property types.
44
- * @static
45
- */
46
- static propTypes = {
47
- deleteContent: PropTypes.func.isRequired,
48
- getContent: PropTypes.func.isRequired,
49
- deleteRequest: PropTypes.shape({
50
- loading: PropTypes.bool,
51
- loaded: PropTypes.bool,
52
- }).isRequired,
53
- pathname: PropTypes.string.isRequired,
54
- content: PropTypes.shape({
55
- title: PropTypes.string,
56
- }),
57
- returnUrl: PropTypes.string,
58
- };
28
+ const Delete = () => {
29
+ const dispatch = useDispatch();
30
+ const intl = useIntl();
31
+ const [isClient, setisClient] = useState(false);
32
+ const { pathname, search } = useLocation();
33
+ const history = useHistory();
34
+ const deleteRequest = useSelector((state) => state.content?.delete);
35
+ const content = useSelector((state) => state.content?.data, shallowEqual);
59
36
 
60
- /**
61
- * Default properties
62
- * @property {Object} defaultProps Default properties.
63
- * @static
64
- */
65
- static defaultProps = {
66
- content: null,
67
- returnUrl: null,
68
- };
37
+ const prevdeleteRequestLoading = usePrevious(deleteRequest.loading);
38
+ const returnUrl = qs.parse(search).return_url;
69
39
 
70
- /**
71
- * Constructor
72
- * @method constructor
73
- * @param {Object} props Component properties
74
- * @constructs WysiwygEditor
75
- */
76
- constructor(props) {
77
- super(props);
78
- this.onCancel = this.onCancel.bind(this);
79
- this.onSubmit = this.onSubmit.bind(this);
80
- this.state = { isClient: false };
81
- }
40
+ useEffect(() => {
41
+ setisClient(true);
42
+ }, []);
43
+
44
+ useEffect(() => {
45
+ dispatch(getContent(pathname.split('/delete')[0]));
46
+ }, [dispatch, pathname]);
82
47
 
83
- /**
84
- * Component will receive props
85
- * @method componentWillReceiveProps
86
- * @param {Object} nextProps Next properties
87
- * @returns {undefined}
88
- */
89
- UNSAFE_componentWillReceiveProps(nextProps) {
90
- if (this.props.deleteRequest.loading && nextProps.deleteRequest.loaded) {
91
- this.props.history.push(
92
- this.props.returnUrl ||
93
- this.props.pathname.replace('/delete', '').replace(/\/[^/]*$/, ''),
48
+ useEffect(() => {
49
+ if (prevdeleteRequestLoading && deleteRequest.loaded) {
50
+ history.push(
51
+ returnUrl || pathname.replace('/delete', '').replace(/\/[^/]*$/, ''),
94
52
  );
95
53
  }
96
- }
97
-
98
- /**
99
- * Component did mount
100
- * @method componentDidMount
101
- * @returns {undefined}
102
- */
103
- componentDidMount() {
104
- this.props.getContent(this.props.pathname.split('/delete')[0]);
105
- this.setState({ isClient: true });
106
- }
54
+ }, [
55
+ history,
56
+ pathname,
57
+ returnUrl,
58
+ prevdeleteRequestLoading,
59
+ deleteRequest.loaded,
60
+ ]);
107
61
 
108
- /**
109
- * Submit handler
110
- * @method onSubmit
111
- * @returns {undefined}
112
- */
113
- onSubmit() {
114
- this.props.deleteContent(this.props.pathname.replace('/delete', ''));
115
- }
62
+ const onSubmit = () => {
63
+ dispatch(deleteContent(pathname.replace('/delete', '')));
64
+ };
116
65
 
117
- /**
118
- * Cancel handler
119
- * @method onCancel
120
- * @returns {undefined}
121
- */
122
- onCancel() {
123
- this.props.history.push(this.props.pathname.replace('/delete', ''));
124
- }
66
+ const onCancel = () => {
67
+ history.push(pathname.replace('/delete', ''));
68
+ };
125
69
 
126
- /**
127
- * Render method.
128
- * @method render
129
- * @returns {string} Markup for the component.
130
- */
131
- render() {
132
- if (this.props.content) {
133
- return (
134
- <div id="page-delete">
135
- <Helmet title={this.props.intl.formatMessage(messages.delete)} />
136
- <Container>
137
- <Segment.Group raised>
138
- <Segment className="primary">
139
- <FormattedMessage
140
- id="Do you really want to delete this item?"
141
- defaultMessage="Do you really want to delete this item?"
142
- />
143
- </Segment>
144
- <Segment attached>
145
- <List bulleted>
146
- <List.Item>{this.props.content.title}</List.Item>
147
- </List>
148
- </Segment>
149
- <Segment className="actions" clearing>
150
- <Button
151
- basic
152
- circular
153
- primary
154
- floated="right"
155
- icon="arrow right"
156
- aria-label={this.props.intl.formatMessage(messages.ok)}
157
- title={this.props.intl.formatMessage(messages.ok)}
158
- size="big"
159
- onClick={this.onSubmit}
160
- />
161
- <Button
162
- basic
163
- circular
164
- secondary
165
- icon="remove"
166
- aria-label={this.props.intl.formatMessage(messages.cancel)}
167
- title={this.props.intl.formatMessage(messages.cancel)}
168
- floated="right"
169
- size="big"
170
- onClick={this.onCancel}
171
- />
172
- </Segment>
173
- </Segment.Group>
174
- </Container>
175
- {this.state.isClient && (
176
- <Portal node={document.getElementById('toolbar')}>
177
- <Toolbar
178
- pathname={this.props.pathname}
179
- hideDefaultViewButtons
180
- inner={<span />}
70
+ if (content) {
71
+ return (
72
+ <div id="page-delete">
73
+ <Helmet title={intl.formatMessage(messages.delete)} />
74
+ <Container>
75
+ <Segment.Group raised>
76
+ <Segment className="primary">
77
+ <FormattedMessage
78
+ id="Do you really want to delete this item?"
79
+ defaultMessage="Do you really want to delete this item?"
181
80
  />
182
- </Portal>
183
- )}
184
- </div>
185
- );
186
- }
187
- return <div />;
81
+ </Segment>
82
+ <Segment attached>
83
+ <List bulleted>
84
+ <List.Item>{content.title}</List.Item>
85
+ </List>
86
+ </Segment>
87
+ <Segment className="actions" clearing>
88
+ <Button
89
+ basic
90
+ circular
91
+ primary
92
+ floated="right"
93
+ icon="arrow right"
94
+ aria-label={intl.formatMessage(messages.ok)}
95
+ title={intl.formatMessage(messages.ok)}
96
+ size="big"
97
+ onClick={onSubmit}
98
+ />
99
+ <Button
100
+ basic
101
+ circular
102
+ secondary
103
+ icon="remove"
104
+ aria-label={intl.formatMessage(messages.cancel)}
105
+ title={intl.formatMessage(messages.cancel)}
106
+ floated="right"
107
+ size="big"
108
+ onClick={onCancel}
109
+ />
110
+ </Segment>
111
+ </Segment.Group>
112
+ </Container>
113
+ {isClient && (
114
+ <Portal node={document.getElementById('toolbar')}>
115
+ <Toolbar
116
+ pathname={pathname}
117
+ hideDefaultViewButtons
118
+ inner={<span />}
119
+ />
120
+ </Portal>
121
+ )}
122
+ </div>
123
+ );
188
124
  }
189
- }
125
+ return <div />;
126
+ };
190
127
 
191
- export default compose(
192
- withRouter,
193
- injectIntl,
194
- connect(
195
- (state, props) => ({
196
- content: state.content.data,
197
- deleteRequest: state.content.delete,
198
- pathname: props.location.pathname,
199
- returnUrl: qs.parse(props.location.search).return_url,
200
- }),
201
- { deleteContent, getContent },
202
- ),
203
- )(Delete);
128
+ export default Delete;
@@ -111,7 +111,7 @@ export function normalizeValue(choices, value, intl) {
111
111
 
112
112
  if (Array.isArray(value)) {
113
113
  // a list of values, like ['foo', 'bar'];
114
- return value.map((v) => normalizeValue(choices, v));
114
+ return value.map((v) => normalizeValue(choices, v, intl));
115
115
  }
116
116
 
117
117
  if (isObject(value)) {
@@ -1,22 +1,17 @@
1
- /**
2
- * Workflow component.
3
- * @module components/manage/Workflow/Workflow
4
- */
5
-
6
- import React, { Component, Fragment } from 'react';
1
+ import { useEffect } from 'react';
7
2
  import PropTypes from 'prop-types';
8
3
  import { compose } from 'redux';
9
- import { connect } from 'react-redux';
4
+ import { useDispatch, useSelector, shallowEqual } from 'react-redux';
10
5
  import { uniqBy } from 'lodash';
11
6
  import { toast } from 'react-toastify';
12
- import { defineMessages, injectIntl } from 'react-intl';
7
+ import { defineMessages, useIntl } from 'react-intl';
8
+
13
9
  import { FormFieldWrapper, Icon, Toast } from '@plone/volto/components';
14
10
  import {
15
11
  flattenToAppURL,
16
- getCurrentStateMapping,
17
12
  getWorkflowOptions,
13
+ getCurrentStateMapping,
18
14
  } from '@plone/volto/helpers';
19
-
20
15
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
21
16
 
22
17
  import {
@@ -24,7 +19,6 @@ import {
24
19
  getWorkflow,
25
20
  transitionWorkflow,
26
21
  } from '@plone/volto/actions';
27
-
28
22
  import downSVG from '@plone/volto/icons/down-key.svg';
29
23
  import upSVG from '@plone/volto/icons/up-key.svg';
30
24
  import checkSVG from '@plone/volto/icons/check.svg';
@@ -165,191 +159,88 @@ const customSelectStyles = {
165
159
  }),
166
160
  };
167
161
 
168
- /**
169
- * Workflow container class.
170
- * @class Workflow
171
- * @extends Component
172
- */
173
- class Workflow extends Component {
174
- /**
175
- * Property types.
176
- * @property {Object} propTypes Property types.
177
- * @static
178
- */
179
- static propTypes = {
180
- getContent: PropTypes.func.isRequired,
181
- getWorkflow: PropTypes.func.isRequired,
182
- transitionWorkflow: PropTypes.func.isRequired,
183
- loaded: PropTypes.bool.isRequired,
184
- pathname: PropTypes.string.isRequired,
185
- history: PropTypes.arrayOf(
186
- PropTypes.shape({
187
- review_state: PropTypes.string,
188
- }),
189
- ),
190
- transitions: PropTypes.arrayOf(
191
- PropTypes.shape({
192
- '@id': PropTypes.string,
193
- title: PropTypes.string,
194
- }),
195
- ),
196
- };
162
+ function useWorkflow() {
163
+ const history = useSelector((state) => state.workflow.history, shallowEqual);
164
+ const transitions = useSelector(
165
+ (state) => state.workflow.transitions,
166
+ shallowEqual,
167
+ );
168
+ const loaded = useSelector((state) => state.workflow.transition.loaded);
169
+ const currentStateValue = useSelector(
170
+ (state) => getCurrentStateMapping(state.workflow.currentState),
171
+ shallowEqual,
172
+ );
197
173
 
198
- /**
199
- * Default properties
200
- * @property {Object} defaultProps Default properties.
201
- * @static
202
- */
203
- static defaultProps = {
204
- history: [],
205
- transitions: [],
206
- };
174
+ return { loaded, history, transitions, currentStateValue };
175
+ }
207
176
 
208
- componentDidMount() {
209
- this.props.getWorkflow(this.props.pathname);
210
- }
177
+ const Workflow = (props) => {
178
+ const intl = useIntl();
179
+ const dispatch = useDispatch();
180
+ const { loaded, transitions, currentStateValue } = useWorkflow();
181
+ const content = useSelector((state) => state.content?.data, shallowEqual);
182
+ const { pathname } = props;
211
183
 
212
- /**
213
- * Component will receive props
214
- * @method componentWillReceiveProps
215
- * @param {Object} nextProps Next properties
216
- * @returns {undefined}
217
- */
218
- UNSAFE_componentWillReceiveProps(nextProps) {
219
- if (nextProps.pathname !== this.props.pathname) {
220
- this.props.getWorkflow(nextProps.pathname);
221
- }
222
- if (!this.props.loaded && nextProps.loaded) {
223
- this.props.getWorkflow(nextProps.pathname);
224
- this.props.getContent(nextProps.pathname);
225
- }
226
- }
184
+ useEffect(() => {
185
+ dispatch(getWorkflow(pathname));
186
+ dispatch(getContent(pathname));
187
+ }, [dispatch, pathname, loaded]);
227
188
 
228
- /**
229
- * On transition handler
230
- * @method transition
231
- * @param {string} event Event object
232
- * @returns {undefined}
233
- */
234
- transition = (selectedOption) => {
235
- this.props.transitionWorkflow(flattenToAppURL(selectedOption.url));
189
+ const transition = (selectedOption) => {
190
+ dispatch(transitionWorkflow(flattenToAppURL(selectedOption.url)));
236
191
  toast.success(
237
192
  <Toast
238
193
  success
239
- title={this.props.intl.formatMessage(messages.messageUpdated)}
194
+ title={intl.formatMessage(messages.messageUpdated)}
240
195
  content=""
241
196
  />,
242
197
  );
243
198
  };
244
199
 
245
- selectValue = (option) => {
246
- const stateDecorator = {
247
- marginLeft: '10px',
248
- marginRight: '10px',
249
- display: 'inline-block',
250
- backgroundColor: option.color || null,
251
- content: ' ',
252
- height: '10px',
253
- width: '10px',
254
- borderRadius: '50%',
255
- };
256
- return (
257
- <Fragment>
258
- <span style={stateDecorator} />
259
- <span className="Select-value-label">{option.label}</span>
260
- </Fragment>
261
- );
262
- };
263
-
264
- optionRenderer = (option) => {
265
- const stateDecorator = {
266
- marginLeft: '10px',
267
- marginRight: '10px',
268
- display: 'inline-block',
269
- backgroundColor:
270
- this.props.currentStateValue.value === option.value
271
- ? option.color
272
- : null,
273
- content: ' ',
274
- height: '10px',
275
- width: '10px',
276
- borderRadius: '50%',
277
- border:
278
- this.props.currentStateValue.value !== option.value
279
- ? `1px solid ${option.color}`
280
- : null,
281
- };
282
-
283
- return (
284
- <Fragment>
285
- <span style={stateDecorator} />
286
- <span style={{ marginRight: 'auto' }}>{option.label}</span>
287
- <Icon name={checkSVG} size="24px" />
288
- </Fragment>
289
- );
290
- };
200
+ const { Placeholder } = props.reactSelect.components;
201
+ const Select = props.reactSelect.default;
291
202
 
292
- render() {
293
- const { Placeholder } = this.props.reactSelect.components;
294
- const Select = this.props.reactSelect.default;
203
+ return (
204
+ <FormFieldWrapper
205
+ id="state-select"
206
+ title={intl.formatMessage(messages.state)}
207
+ intl={intl}
208
+ {...props}
209
+ >
210
+ <Select
211
+ name="state-select"
212
+ className="react-select-container"
213
+ classNamePrefix="react-select"
214
+ isDisabled={!content.review_state || transitions.length === 0}
215
+ options={uniqBy(
216
+ transitions.map((transition) => getWorkflowOptions(transition)),
217
+ 'label',
218
+ ).concat(currentStateValue)}
219
+ styles={customSelectStyles}
220
+ theme={selectTheme}
221
+ components={{
222
+ DropdownIndicator,
223
+ Placeholder,
224
+ Option,
225
+ SingleValue,
226
+ }}
227
+ onChange={transition}
228
+ value={
229
+ content.review_state
230
+ ? currentStateValue
231
+ : {
232
+ label: intl.formatMessage(messages.messageNoWorkflow),
233
+ value: 'noworkflow',
234
+ }
235
+ }
236
+ isSearchable={false}
237
+ />
238
+ </FormFieldWrapper>
239
+ );
240
+ };
295
241
 
296
- return (
297
- <FormFieldWrapper
298
- id="state-select"
299
- title={this.props.intl.formatMessage(messages.state)}
300
- {...this.props}
301
- >
302
- <Select
303
- name="state-select"
304
- className="react-select-container"
305
- classNamePrefix="react-select"
306
- isDisabled={
307
- !this.props.content.review_state ||
308
- this.props.transitions.length === 0
309
- }
310
- options={uniqBy(
311
- this.props.transitions.map((transition) =>
312
- getWorkflowOptions(transition),
313
- ),
314
- 'label',
315
- ).concat(this.props.currentStateValue)}
316
- styles={customSelectStyles}
317
- theme={selectTheme}
318
- components={{
319
- DropdownIndicator,
320
- Placeholder,
321
- Option,
322
- SingleValue,
323
- }}
324
- onChange={this.transition}
325
- value={
326
- this.props.content.review_state
327
- ? this.props.currentStateValue
328
- : {
329
- label: this.props.intl.formatMessage(
330
- messages.messageNoWorkflow,
331
- ),
332
- value: 'noworkflow',
333
- }
334
- }
335
- isSearchable={false}
336
- />
337
- </FormFieldWrapper>
338
- );
339
- }
340
- }
242
+ Workflow.propTypes = {
243
+ pathname: PropTypes.string.isRequired,
244
+ };
341
245
 
342
- export default compose(
343
- injectIntl,
344
- injectLazyLibs(['reactSelect']),
345
- connect(
346
- (state) => ({
347
- loaded: state.workflow.transition.loaded,
348
- content: state.content.data,
349
- history: state.workflow.history,
350
- transitions: state.workflow.transitions,
351
- currentStateValue: getCurrentStateMapping(state.workflow.currentState),
352
- }),
353
- { getContent, getWorkflow, transitionWorkflow },
354
- ),
355
- )(Workflow);
246
+ export default compose(injectLazyLibs(['reactSelect']))(Workflow);