@plone/volto 16.20.7 → 16.21.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/.changelog.draft +12 -12
- package/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +31 -0
- package/locales/ca/LC_MESSAGES/volto.po +94 -3
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +94 -3
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +93 -2
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +94 -3
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +94 -3
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +94 -3
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +94 -3
- package/locales/fr.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +94 -3
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +94 -3
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +94 -3
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +94 -3
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +94 -3
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +94 -3
- package/locales/ro.json +1 -1
- package/locales/volto.pot +94 -3
- package/locales/zh_CN/LC_MESSAGES/volto.po +94 -3
- package/locales/zh_CN.json +1 -1
- package/package.json +1 -1
- package/packages/volto-slate/package.json +1 -1
- package/packages/volto-slate/src/blocks/Text/DefaultTextBlockEditor.jsx +8 -3
- package/packages/volto-slate/src/blocks/Text/extensions/withDeserializers.js +3 -1
- package/packages/volto-slate/src/editor/plugins/StyleMenu/StyleMenu.jsx +14 -4
- package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +6 -1
- package/src/components/manage/Blocks/Image/Edit.jsx +11 -7
- package/src/components/manage/Contents/Contents.jsx +5 -1
- package/src/components/manage/Contents/ContentsUploadModal.jsx +10 -5
- package/src/components/manage/Form/Form.jsx +5 -3
- package/src/components/manage/History/History.jsx +11 -1
- package/src/components/manage/Preferences/ChangePassword.jsx +2 -2
- package/src/components/manage/Sharing/Sharing.jsx +5 -1
- package/src/components/manage/Toast/Toast.jsx +1 -1
- package/src/components/manage/Widgets/ColorPickerWidget.jsx +6 -1
- package/src/components/manage/Widgets/FileWidget.jsx +2 -1
- package/src/components/theme/NotFound/NotFound.jsx +55 -41
- package/src/components/theme/PasswordReset/PasswordReset.jsx +6 -3
- package/src/components/theme/View/NewsItemView.jsx +10 -5
- package/src/components/theme/View/RenderBlocks.jsx +7 -1
- package/src/config/index.js +1 -0
- package/src/helpers/Api/Api.js +1 -1
- package/src/helpers/Extensions/withBlockSchemaEnhancer.js +15 -11
- package/src/helpers/Extensions/withBlockSchemaEnhancer.test.js +145 -0
- package/src/helpers/FormValidation/FormValidation.js +30 -1
- package/src/helpers/FormValidation/FormValidation.test.js +32 -0
- package/src/helpers/MessageLabels/MessageLabels.js +76 -0
- package/src/helpers/index.js +3 -1
- package/theme/themes/pastanaga/extras/contents.less +1 -0
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
flattenToAppURL,
|
|
22
22
|
getBaseUrl,
|
|
23
23
|
isInternalURL,
|
|
24
|
+
validateFileUploadSize,
|
|
24
25
|
} from '@plone/volto/helpers';
|
|
25
26
|
|
|
26
27
|
import imageBlockSVG from '@plone/volto/components/manage/Blocks/Image/block-image.svg';
|
|
@@ -125,6 +126,7 @@ class Edit extends Component {
|
|
|
125
126
|
onUploadImage = (e) => {
|
|
126
127
|
e.stopPropagation();
|
|
127
128
|
const file = e.target.files[0];
|
|
129
|
+
if (!validateFileUploadSize(file, this.props.intl.formatMessage)) return;
|
|
128
130
|
this.setState({
|
|
129
131
|
uploading: true,
|
|
130
132
|
});
|
|
@@ -178,23 +180,25 @@ class Edit extends Component {
|
|
|
178
180
|
* @param {array} files File objects
|
|
179
181
|
* @returns {undefined}
|
|
180
182
|
*/
|
|
181
|
-
onDrop = (
|
|
182
|
-
this.
|
|
183
|
-
|
|
184
|
-
|
|
183
|
+
onDrop = (files) => {
|
|
184
|
+
if (!validateFileUploadSize(files[0], this.props.intl.formatMessage)) {
|
|
185
|
+
this.setState({ dragging: false });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
this.setState({ uploading: true });
|
|
185
189
|
|
|
186
|
-
readAsDataURL(
|
|
190
|
+
readAsDataURL(files[0]).then((data) => {
|
|
187
191
|
const fields = data.match(/^data:(.*);(.*),(.*)$/);
|
|
188
192
|
this.props.createContent(
|
|
189
193
|
getBaseUrl(this.props.pathname),
|
|
190
194
|
{
|
|
191
195
|
'@type': 'Image',
|
|
192
|
-
title:
|
|
196
|
+
title: files[0].name,
|
|
193
197
|
image: {
|
|
194
198
|
data: fields[3],
|
|
195
199
|
encoding: fields[2],
|
|
196
200
|
'content-type': fields[1],
|
|
197
|
-
filename:
|
|
201
|
+
filename: files[0].name,
|
|
198
202
|
},
|
|
199
203
|
},
|
|
200
204
|
this.props.block,
|
|
@@ -12,7 +12,7 @@ import { Link } from 'react-router-dom';
|
|
|
12
12
|
import {
|
|
13
13
|
Button,
|
|
14
14
|
Confirm,
|
|
15
|
-
Container,
|
|
15
|
+
Container as SemanticContainer,
|
|
16
16
|
Divider,
|
|
17
17
|
Dropdown,
|
|
18
18
|
Menu,
|
|
@@ -70,6 +70,7 @@ import {
|
|
|
70
70
|
|
|
71
71
|
import { Helmet, getBaseUrl } from '@plone/volto/helpers';
|
|
72
72
|
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
73
|
+
import config from '@plone/volto/registry';
|
|
73
74
|
|
|
74
75
|
import backSVG from '@plone/volto/icons/back.svg';
|
|
75
76
|
import cutSVG from '@plone/volto/icons/cut.svg';
|
|
@@ -1177,6 +1178,9 @@ class Contents extends Component {
|
|
|
1177
1178
|
(this.props.orderRequest?.loading && !this.props.orderRequest?.error) ||
|
|
1178
1179
|
(this.props.searchRequest?.loading && !this.props.searchRequest?.error);
|
|
1179
1180
|
|
|
1181
|
+
const Container =
|
|
1182
|
+
config.getComponent({ name: 'Container' }).component || SemanticContainer;
|
|
1183
|
+
|
|
1180
1184
|
return this.props.token && this.props.objectActions?.length > 0 ? (
|
|
1181
1185
|
<>
|
|
1182
1186
|
{folderContentsAction ? (
|
|
@@ -25,6 +25,7 @@ import { readAsDataURL } from 'promise-file-reader';
|
|
|
25
25
|
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
|
26
26
|
import { FormattedRelativeDate } from '@plone/volto/components';
|
|
27
27
|
import { createContent } from '@plone/volto/actions';
|
|
28
|
+
import { validateFileUploadSize } from '@plone/volto/helpers';
|
|
28
29
|
|
|
29
30
|
const Dropzone = loadable(() => import('react-dropzone'));
|
|
30
31
|
|
|
@@ -121,14 +122,18 @@ class ContentsUploadModal extends Component {
|
|
|
121
122
|
* @returns {undefined}
|
|
122
123
|
*/
|
|
123
124
|
onDrop = async (files) => {
|
|
125
|
+
const validFiles = [];
|
|
124
126
|
for (let i = 0; i < files.length; i++) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
if (validateFileUploadSize(files[i], this.props.intl.formatMessage)) {
|
|
128
|
+
await readAsDataURL(files[i]).then((data) => {
|
|
129
|
+
const fields = data.match(/^data:(.*);(.*),(.*)$/);
|
|
130
|
+
files[i].preview = fields[0];
|
|
131
|
+
});
|
|
132
|
+
validFiles.push(files[i]);
|
|
133
|
+
}
|
|
129
134
|
}
|
|
130
135
|
this.setState({
|
|
131
|
-
files: concat(this.state.files,
|
|
136
|
+
files: concat(this.state.files, validFiles),
|
|
132
137
|
});
|
|
133
138
|
};
|
|
134
139
|
|
|
@@ -31,7 +31,7 @@ import { Portal } from 'react-portal';
|
|
|
31
31
|
import { connect } from 'react-redux';
|
|
32
32
|
import {
|
|
33
33
|
Button,
|
|
34
|
-
Container,
|
|
34
|
+
Container as SemanticContainer,
|
|
35
35
|
Form as UiForm,
|
|
36
36
|
Message,
|
|
37
37
|
Segment,
|
|
@@ -545,12 +545,14 @@ class Form extends Component {
|
|
|
545
545
|
const { schema: originalSchema, onCancel, onSubmit } = this.props;
|
|
546
546
|
const { formData } = this.state;
|
|
547
547
|
const schema = this.removeBlocksLayoutFields(originalSchema);
|
|
548
|
+
const Container =
|
|
549
|
+
config.getComponent({ name: 'Container' }).component || SemanticContainer;
|
|
548
550
|
|
|
549
551
|
return this.props.visual ? (
|
|
550
552
|
// Removing this from SSR is important, since react-beautiful-dnd supports SSR,
|
|
551
553
|
// but draftJS don't like it much and the hydration gets messed up
|
|
552
554
|
this.state.isClient && (
|
|
553
|
-
<
|
|
555
|
+
<Container>
|
|
554
556
|
<BlocksToolbar
|
|
555
557
|
formData={this.state.formData}
|
|
556
558
|
selectedBlock={this.state.selected}
|
|
@@ -639,7 +641,7 @@ class Form extends Component {
|
|
|
639
641
|
</UiForm>
|
|
640
642
|
</Portal>
|
|
641
643
|
)}
|
|
642
|
-
</
|
|
644
|
+
</Container>
|
|
643
645
|
)
|
|
644
646
|
) : (
|
|
645
647
|
<Container>
|
|
@@ -9,7 +9,13 @@ import { Helmet } from '@plone/volto/helpers';
|
|
|
9
9
|
import { Link } from 'react-router-dom';
|
|
10
10
|
import { connect } from 'react-redux';
|
|
11
11
|
import { compose } from 'redux';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
Container as SemanticContainer,
|
|
14
|
+
Dropdown,
|
|
15
|
+
Icon,
|
|
16
|
+
Segment,
|
|
17
|
+
Table,
|
|
18
|
+
} from 'semantic-ui-react';
|
|
13
19
|
import { concat, map, reverse, find } from 'lodash';
|
|
14
20
|
import { Portal } from 'react-portal';
|
|
15
21
|
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
|
@@ -24,6 +30,7 @@ import {
|
|
|
24
30
|
} from '@plone/volto/components';
|
|
25
31
|
import { getHistory, revertHistory, listActions } from '@plone/volto/actions';
|
|
26
32
|
import { getBaseUrl } from '@plone/volto/helpers';
|
|
33
|
+
import config from '@plone/volto/registry';
|
|
27
34
|
|
|
28
35
|
import backSVG from '@plone/volto/icons/back.svg';
|
|
29
36
|
|
|
@@ -147,6 +154,9 @@ class History extends Component {
|
|
|
147
154
|
});
|
|
148
155
|
const entries = this.processHistoryEntries();
|
|
149
156
|
|
|
157
|
+
const Container =
|
|
158
|
+
config.getComponent({ name: 'Container' }).component || SemanticContainer;
|
|
159
|
+
|
|
150
160
|
return !historyAction ? (
|
|
151
161
|
<>
|
|
152
162
|
{this.props.token ? (
|
|
@@ -43,8 +43,8 @@ const messages = defineMessages({
|
|
|
43
43
|
defaultMessage: 'New password',
|
|
44
44
|
},
|
|
45
45
|
newPasswordDescription: {
|
|
46
|
-
id: 'Enter your new password. Minimum
|
|
47
|
-
defaultMessage: 'Enter your new password. Minimum
|
|
46
|
+
id: 'Enter your new password. Minimum 8 characters.',
|
|
47
|
+
defaultMessage: 'Enter your new password. Minimum 8 characters.',
|
|
48
48
|
},
|
|
49
49
|
newPasswordRepeatTitle: {
|
|
50
50
|
id: 'Confirm password',
|
|
@@ -14,7 +14,7 @@ import { Portal } from 'react-portal';
|
|
|
14
14
|
import {
|
|
15
15
|
Button,
|
|
16
16
|
Checkbox,
|
|
17
|
-
Container,
|
|
17
|
+
Container as SemanticContainer,
|
|
18
18
|
Form,
|
|
19
19
|
Icon as IconOld,
|
|
20
20
|
Input,
|
|
@@ -28,6 +28,7 @@ import { updateSharing, getSharing } from '@plone/volto/actions';
|
|
|
28
28
|
import { getBaseUrl } from '@plone/volto/helpers';
|
|
29
29
|
import { Icon, Toolbar, Toast } from '@plone/volto/components';
|
|
30
30
|
import { toast } from 'react-toastify';
|
|
31
|
+
import config from '@plone/volto/registry';
|
|
31
32
|
|
|
32
33
|
import aheadSVG from '@plone/volto/icons/ahead.svg';
|
|
33
34
|
import clearSVG from '@plone/volto/icons/clear.svg';
|
|
@@ -288,6 +289,9 @@ class SharingComponent extends Component {
|
|
|
288
289
|
* @returns {string} Markup for the component.
|
|
289
290
|
*/
|
|
290
291
|
render() {
|
|
292
|
+
const Container =
|
|
293
|
+
config.getComponent({ name: 'Container' }).component || SemanticContainer;
|
|
294
|
+
|
|
291
295
|
return (
|
|
292
296
|
<Container id="page-sharing">
|
|
293
297
|
<Helmet title={this.props.intl.formatMessage(messages.sharing)} />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import
|
|
3
|
+
import Icon from '@plone/volto/components/theme/Icon/Icon';
|
|
4
4
|
|
|
5
5
|
import successSVG from '@plone/volto/icons/ready.svg';
|
|
6
6
|
import infoSVG from '@plone/volto/icons/info.svg';
|
|
@@ -51,7 +51,12 @@ const ColorPickerWidget = (props) => {
|
|
|
51
51
|
onClick={(e) => {
|
|
52
52
|
e.preventDefault();
|
|
53
53
|
e.stopPropagation();
|
|
54
|
-
onChange(
|
|
54
|
+
onChange(
|
|
55
|
+
id,
|
|
56
|
+
value === color.name
|
|
57
|
+
? props.missing_value
|
|
58
|
+
: color.name,
|
|
59
|
+
);
|
|
55
60
|
}}
|
|
56
61
|
active={value === color.name}
|
|
57
62
|
circular
|
|
@@ -11,7 +11,7 @@ import { injectIntl } from 'react-intl';
|
|
|
11
11
|
import deleteSVG from '@plone/volto/icons/delete.svg';
|
|
12
12
|
import { Icon, FormFieldWrapper } from '@plone/volto/components';
|
|
13
13
|
import loadable from '@loadable/component';
|
|
14
|
-
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
14
|
+
import { flattenToAppURL, validateFileUploadSize } from '@plone/volto/helpers';
|
|
15
15
|
import { defineMessages, useIntl } from 'react-intl';
|
|
16
16
|
|
|
17
17
|
const imageMimetypes = [
|
|
@@ -95,6 +95,7 @@ const FileWidget = (props) => {
|
|
|
95
95
|
*/
|
|
96
96
|
const onDrop = (files) => {
|
|
97
97
|
const file = files[0];
|
|
98
|
+
if (!validateFileUploadSize(file, intl.formatMessage)) return;
|
|
98
99
|
readAsDataURL(file).then((data) => {
|
|
99
100
|
const fields = data.match(/^data:(.*);(.*),(.*)$/);
|
|
100
101
|
onChange(id, {
|
|
@@ -1,53 +1,67 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* @module components/theme/NotFound/NotFound
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from 'react';
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { BodyClass, toBackendLang } from '@plone/volto/helpers';
|
|
7
3
|
import { FormattedMessage } from 'react-intl';
|
|
8
4
|
import { Link } from 'react-router-dom';
|
|
9
5
|
import { Container } from 'semantic-ui-react';
|
|
10
6
|
import { withServerErrorCode } from '@plone/volto/helpers/Utils/Utils';
|
|
7
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
8
|
+
import { getNavigation } from '@plone/volto/actions';
|
|
9
|
+
import config from '@plone/volto/registry';
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Not found function.
|
|
14
13
|
* @function NotFound
|
|
15
14
|
* @returns {string} Markup of the not found page.
|
|
16
15
|
*/
|
|
17
|
-
const NotFound = () =>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
16
|
+
const NotFound = () => {
|
|
17
|
+
const dispatch = useDispatch();
|
|
18
|
+
const lang = useSelector((state) => state.intl.locale);
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
dispatch(
|
|
22
|
+
getNavigation(
|
|
23
|
+
config.settings.isMultilingual ? `/${toBackendLang(lang)}` : '/',
|
|
24
|
+
config.settings.navDepth,
|
|
25
|
+
),
|
|
26
|
+
);
|
|
27
|
+
}, [dispatch, lang]);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<Container className="view-wrapper">
|
|
31
|
+
<BodyClass className="page-not-found" />
|
|
32
|
+
<h1>
|
|
33
|
+
<FormattedMessage
|
|
34
|
+
id="This page does not seem to exist…"
|
|
35
|
+
defaultMessage="This page does not seem to exist…"
|
|
36
|
+
/>
|
|
37
|
+
</h1>
|
|
38
|
+
<p className="description">
|
|
39
|
+
<FormattedMessage
|
|
40
|
+
id="We apologize for the inconvenience, but the page you were trying to access is not at this address. You can use the links below to help you find what you are looking for."
|
|
41
|
+
defaultMessage="We apologize for the inconvenience, but the page you were trying to access is not at this address. You can use the links below to help you find what you are looking for."
|
|
42
|
+
/>
|
|
43
|
+
</p>
|
|
44
|
+
<p>
|
|
45
|
+
<FormattedMessage
|
|
46
|
+
id="If you are certain you have the correct web address but are encountering an error, please contact the {site_admin}."
|
|
47
|
+
defaultMessage="If you are certain you have the correct web address but are encountering an error, please contact the {site_admin}."
|
|
48
|
+
values={{
|
|
49
|
+
site_admin: (
|
|
50
|
+
<Link to="/contact-form">
|
|
51
|
+
<FormattedMessage
|
|
52
|
+
id="Site Administration"
|
|
53
|
+
defaultMessage="Site Administration"
|
|
54
|
+
/>
|
|
55
|
+
</Link>
|
|
56
|
+
),
|
|
57
|
+
}}
|
|
58
|
+
/>
|
|
59
|
+
</p>
|
|
60
|
+
<p>
|
|
61
|
+
<FormattedMessage id="Thank you." defaultMessage="Thank you." />
|
|
62
|
+
</p>
|
|
63
|
+
</Container>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
52
66
|
|
|
53
67
|
export default withServerErrorCode(404)(NotFound);
|
|
@@ -50,8 +50,8 @@ const messages = defineMessages({
|
|
|
50
50
|
defaultMessage: 'New password',
|
|
51
51
|
},
|
|
52
52
|
passwordDescription: {
|
|
53
|
-
id: 'Enter your new password. Minimum
|
|
54
|
-
defaultMessage: 'Enter your new password. Minimum
|
|
53
|
+
id: 'Enter your new password. Minimum 8 characters.',
|
|
54
|
+
defaultMessage: 'Enter your new password. Minimum 8 characters.',
|
|
55
55
|
},
|
|
56
56
|
passwordRepeatTitle: {
|
|
57
57
|
id: 'Confirm password',
|
|
@@ -227,6 +227,9 @@ class PasswordReset extends Component {
|
|
|
227
227
|
);
|
|
228
228
|
}
|
|
229
229
|
if (this.props.token) {
|
|
230
|
+
const errmsg = this.props.error
|
|
231
|
+
? this.props.error.response.body.error
|
|
232
|
+
: null;
|
|
230
233
|
return (
|
|
231
234
|
<div id="page-password-reset">
|
|
232
235
|
<Helmet
|
|
@@ -238,7 +241,7 @@ class PasswordReset extends Component {
|
|
|
238
241
|
description={this.props.intl.formatMessage(messages.description)}
|
|
239
242
|
onSubmit={this.onSubmit}
|
|
240
243
|
onCancel={this.onCancel}
|
|
241
|
-
error={this.state.error ||
|
|
244
|
+
error={this.state.error || errmsg}
|
|
242
245
|
schema={{
|
|
243
246
|
fieldsets: [
|
|
244
247
|
{
|
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import { Container, Image } from 'semantic-ui-react';
|
|
8
|
+
import { Container as SemanticContainer, Image } from 'semantic-ui-react';
|
|
9
9
|
import {
|
|
10
10
|
hasBlocksData,
|
|
11
11
|
flattenToAppURL,
|
|
12
12
|
flattenHTMLToAppURL,
|
|
13
13
|
} from '@plone/volto/helpers';
|
|
14
14
|
import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
|
|
15
|
+
import config from '@plone/volto/registry';
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* NewsItemView view component class.
|
|
@@ -19,11 +20,14 @@ import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
|
|
|
19
20
|
* @params {object} content Content object.
|
|
20
21
|
* @returns {string} Markup of the component.
|
|
21
22
|
*/
|
|
22
|
-
const NewsItemView = ({ content }) =>
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
const NewsItemView = ({ content }) => {
|
|
24
|
+
const Container =
|
|
25
|
+
config.getComponent({ name: 'Container' }).component || SemanticContainer;
|
|
26
|
+
|
|
27
|
+
return hasBlocksData(content) ? (
|
|
28
|
+
<Container id="page-document" className="view-wrapper newsitem-view">
|
|
25
29
|
<RenderBlocks content={content} />
|
|
26
|
-
</
|
|
30
|
+
</Container>
|
|
27
31
|
) : (
|
|
28
32
|
<Container className="view-wrapper">
|
|
29
33
|
{content.title && (
|
|
@@ -57,6 +61,7 @@ const NewsItemView = ({ content }) =>
|
|
|
57
61
|
)}
|
|
58
62
|
</Container>
|
|
59
63
|
);
|
|
64
|
+
};
|
|
60
65
|
|
|
61
66
|
/**
|
|
62
67
|
* Property types.
|
|
@@ -44,7 +44,13 @@ const RenderBlocks = (props) => {
|
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
return Block ? (
|
|
47
|
-
<StyleWrapper
|
|
47
|
+
<StyleWrapper
|
|
48
|
+
key={block}
|
|
49
|
+
{...props}
|
|
50
|
+
id={block}
|
|
51
|
+
block={block}
|
|
52
|
+
data={blockData}
|
|
53
|
+
>
|
|
48
54
|
<Block
|
|
49
55
|
id={block}
|
|
50
56
|
metadata={metadata}
|
package/src/config/index.js
CHANGED
package/src/helpers/Api/Api.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { defineMessages } from 'react-intl';
|
|
3
3
|
import { useIntl } from 'react-intl';
|
|
4
|
+
import { find, isEmpty } from 'lodash';
|
|
4
5
|
import config from '@plone/volto/registry';
|
|
5
6
|
import { cloneDeepSchema } from '@plone/volto/helpers/Utils/Utils';
|
|
6
7
|
|
|
@@ -291,20 +292,23 @@ export const EMPTY_STYLES_SCHEMA = {
|
|
|
291
292
|
};
|
|
292
293
|
|
|
293
294
|
/**
|
|
294
|
-
*
|
|
295
|
+
* Adds the `styles` field and 'styling' fieldset in a given schema
|
|
295
296
|
*/
|
|
296
297
|
export const addStyling = ({ schema, formData, intl }) => {
|
|
297
|
-
schema.fieldsets
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
298
|
+
if (isEmpty(find(schema.fieldsets, { id: 'styling' }))) {
|
|
299
|
+
schema.fieldsets.push({
|
|
300
|
+
id: 'styling',
|
|
301
|
+
title: intl.formatMessage(messages.styling),
|
|
302
|
+
fields: ['styles'],
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
schema.properties.styles = {
|
|
306
|
+
widget: 'object',
|
|
307
|
+
title: intl.formatMessage(messages.styling),
|
|
308
|
+
schema: cloneDeepSchema(EMPTY_STYLES_SCHEMA),
|
|
309
|
+
};
|
|
310
|
+
}
|
|
302
311
|
|
|
303
|
-
schema.properties.styles = {
|
|
304
|
-
widget: 'object',
|
|
305
|
-
title: intl.formatMessage(messages.styling),
|
|
306
|
-
schema: EMPTY_STYLES_SCHEMA,
|
|
307
|
-
};
|
|
308
312
|
return schema;
|
|
309
313
|
};
|
|
310
314
|
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
addExtensionFieldToSchema,
|
|
3
3
|
applySchemaEnhancer,
|
|
4
4
|
composeSchema,
|
|
5
|
+
addStyling,
|
|
5
6
|
} from './withBlockSchemaEnhancer';
|
|
6
7
|
|
|
7
8
|
import config from '@plone/volto/registry';
|
|
@@ -246,3 +247,147 @@ describe('composeSchema', () => {
|
|
|
246
247
|
expect(res).toStrictEqual([6, 9]);
|
|
247
248
|
});
|
|
248
249
|
});
|
|
250
|
+
|
|
251
|
+
describe('addStyling', () => {
|
|
252
|
+
it('returns an enhanced schema with the styling wrapper object on it', () => {
|
|
253
|
+
const intl = { formatMessage: () => 'Styling' };
|
|
254
|
+
|
|
255
|
+
const schema = {
|
|
256
|
+
fieldsets: [
|
|
257
|
+
{
|
|
258
|
+
id: 'default',
|
|
259
|
+
title: 'Default',
|
|
260
|
+
fields: [],
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
properties: {},
|
|
264
|
+
required: [],
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const result = addStyling({ schema, intl });
|
|
268
|
+
|
|
269
|
+
expect(result).toStrictEqual({
|
|
270
|
+
fieldsets: [
|
|
271
|
+
{ id: 'default', title: 'Default', fields: [] },
|
|
272
|
+
{ id: 'styling', title: 'Styling', fields: ['styles'] },
|
|
273
|
+
],
|
|
274
|
+
properties: {
|
|
275
|
+
styles: {
|
|
276
|
+
widget: 'object',
|
|
277
|
+
title: 'Styling',
|
|
278
|
+
schema: {
|
|
279
|
+
fieldsets: [
|
|
280
|
+
{
|
|
281
|
+
fields: [],
|
|
282
|
+
id: 'default',
|
|
283
|
+
title: 'Default',
|
|
284
|
+
},
|
|
285
|
+
],
|
|
286
|
+
properties: {},
|
|
287
|
+
required: [],
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
required: [],
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('multiple schema enhancers', () => {
|
|
296
|
+
const intl = { formatMessage: () => 'Styling' };
|
|
297
|
+
|
|
298
|
+
const schema1 = {
|
|
299
|
+
fieldsets: [
|
|
300
|
+
{
|
|
301
|
+
id: 'default',
|
|
302
|
+
title: 'Default',
|
|
303
|
+
fields: [],
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
properties: {},
|
|
307
|
+
required: [],
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const schema2 = {
|
|
311
|
+
fieldsets: [
|
|
312
|
+
{
|
|
313
|
+
id: 'default',
|
|
314
|
+
title: 'Default',
|
|
315
|
+
fields: [],
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
properties: {},
|
|
319
|
+
required: [],
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const result = addStyling({ schema: schema1, intl });
|
|
323
|
+
|
|
324
|
+
// We add some fields to the styling schema
|
|
325
|
+
result.properties.styles.schema.properties.align = {
|
|
326
|
+
widget: 'align',
|
|
327
|
+
title: 'align',
|
|
328
|
+
actions: ['left', 'right', 'center'],
|
|
329
|
+
default: 'left',
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
result.properties.styles.schema.fieldsets[0].fields = ['align'];
|
|
333
|
+
|
|
334
|
+
const result2 = addStyling({ schema: schema2, intl });
|
|
335
|
+
|
|
336
|
+
expect(result).toStrictEqual({
|
|
337
|
+
fieldsets: [
|
|
338
|
+
{ id: 'default', title: 'Default', fields: [] },
|
|
339
|
+
{ id: 'styling', title: 'Styling', fields: ['styles'] },
|
|
340
|
+
],
|
|
341
|
+
properties: {
|
|
342
|
+
styles: {
|
|
343
|
+
widget: 'object',
|
|
344
|
+
title: 'Styling',
|
|
345
|
+
schema: {
|
|
346
|
+
fieldsets: [
|
|
347
|
+
{
|
|
348
|
+
fields: ['align'],
|
|
349
|
+
id: 'default',
|
|
350
|
+
title: 'Default',
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
properties: {
|
|
354
|
+
align: {
|
|
355
|
+
widget: 'align',
|
|
356
|
+
title: 'align',
|
|
357
|
+
actions: ['left', 'right', 'center'],
|
|
358
|
+
default: 'left',
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
required: [],
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
required: [],
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
expect(result2).toStrictEqual({
|
|
369
|
+
fieldsets: [
|
|
370
|
+
{ id: 'default', title: 'Default', fields: [] },
|
|
371
|
+
{ id: 'styling', title: 'Styling', fields: ['styles'] },
|
|
372
|
+
],
|
|
373
|
+
properties: {
|
|
374
|
+
styles: {
|
|
375
|
+
widget: 'object',
|
|
376
|
+
title: 'Styling',
|
|
377
|
+
schema: {
|
|
378
|
+
fieldsets: [
|
|
379
|
+
{
|
|
380
|
+
fields: [],
|
|
381
|
+
id: 'default',
|
|
382
|
+
title: 'Default',
|
|
383
|
+
},
|
|
384
|
+
],
|
|
385
|
+
properties: {},
|
|
386
|
+
required: [],
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
required: [],
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
});
|