@plone/volto 17.0.0-alpha.15 → 17.0.0-alpha.17
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/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +30 -0
- package/cypress/support/commands.js +18 -0
- package/locales/ca/LC_MESSAGES/volto.po +41 -0
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +41 -0
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +41 -0
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +41 -0
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +41 -0
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +41 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +41 -0
- package/locales/fr.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +41 -0
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +41 -0
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +41 -0
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +41 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +41 -0
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +41 -0
- package/locales/ro.json +1 -1
- package/locales/volto.pot +42 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +41 -0
- package/locales/zh_CN.json +1 -1
- package/news/4351.bugfix +1 -0
- package/news/4725.bugfix +1 -0
- package/news/4932.bugfix +1 -0
- package/news/4941.documentation +1 -0
- package/news/4951.breaking +1 -0
- package/news/4962.feature +1 -0
- package/news/4964.bugfix +1 -0
- package/package.json +1 -1
- package/packages/volto-slate/package.json +1 -1
- package/packages/volto-slate/src/blocks/Text/index.js +8 -0
- package/src/components/manage/Blocks/Block/BlocksForm.jsx +19 -2
- package/src/components/manage/Blocks/Block/Edit.jsx +1 -1
- package/src/components/manage/Blocks/Container/Data.jsx +32 -0
- package/src/components/manage/Blocks/Container/Edit.jsx +174 -0
- package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +120 -0
- package/src/components/manage/Blocks/Container/NewBlockAddButton.jsx +84 -0
- package/src/components/manage/Blocks/Container/SimpleContainerToolbar.jsx +54 -0
- package/src/components/manage/Blocks/Grid/Edit.jsx +33 -0
- package/src/components/manage/Blocks/Grid/View.jsx +43 -0
- package/src/components/manage/Blocks/Grid/adapter.js +14 -0
- package/src/components/manage/Blocks/Grid/grid-1.svg +6 -0
- package/src/components/manage/Blocks/Grid/grid-2.svg +9 -0
- package/src/components/manage/Blocks/Grid/grid-3.svg +10 -0
- package/src/components/manage/Blocks/Grid/grid-4.svg +11 -0
- package/src/components/manage/Blocks/Grid/schema.js +35 -0
- package/src/components/manage/Blocks/Grid/templates.js +47 -0
- package/src/components/manage/Blocks/Image/ImageSidebar.jsx +2 -1
- package/src/components/manage/Blocks/Image/schema.js +11 -0
- package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +3 -4
- package/src/components/manage/Blocks/Teaser/Body.jsx +0 -1
- package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +20 -15
- package/src/components/manage/Blocks/Teaser/schema.js +5 -0
- package/src/components/manage/DragDropList/DragDropList.jsx +18 -13
- package/src/components/manage/TemplateChooser/TemplateChooser.jsx +38 -0
- package/src/components/manage/TemplateChooser/TemplateChooser.test.jsx +34 -0
- package/src/components/manage/TemplateChooser/template.svg +10 -0
- package/src/components/manage/UniversalLink/UniversalLink.jsx +2 -6
- package/src/components/manage/UniversalLink/UniversalLink.test.jsx +36 -0
- package/src/components/theme/Anontools/Anontools.jsx +3 -4
- package/src/components/theme/Component/Component.jsx +1 -1
- package/src/components/theme/Header/Header.jsx +2 -2
- package/src/components/theme/View/AlbumView.jsx +9 -1
- package/src/components/theme/View/EventView.jsx +6 -2
- package/src/components/theme/View/FileView.jsx +23 -18
- package/src/components/theme/View/ImageView.jsx +37 -32
- package/src/components/theme/View/LinkView.jsx +4 -1
- package/src/components/theme/View/ListingView.jsx +33 -27
- package/src/components/theme/View/RenderBlocks.jsx +56 -27
- package/src/components/theme/View/RenderEmptyBlock.jsx +5 -0
- package/src/components/theme/View/SummaryView.jsx +47 -38
- package/src/components/theme/View/TabularView.jsx +59 -53
- package/src/config/Blocks.jsx +44 -0
- package/src/helpers/Blocks/Blocks.js +26 -0
- package/src/helpers/Blocks/Blocks.test.js +21 -0
- package/src/helpers/Utils/Utils.js +25 -0
- package/src/helpers/index.js +2 -0
- package/src/hooks/index.js +0 -1
- package/src/icons/grid-block.svg +11 -0
- package/theme/themes/pastanaga/extras/grid.less +426 -0
- package/theme/themes/pastanaga/extras/main.less +1 -0
- package/src/hooks/content/useContent.js +0 -31
- package/src/hooks/userSession/useToken.js +0 -5
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import PropTypes from 'prop-types';
|
|
2
|
+
import { useIntl } from 'react-intl';
|
|
3
|
+
import { Button, Grid, Message } from 'semantic-ui-react';
|
|
4
|
+
|
|
5
|
+
const TemplateChooser = ({ templates, onSelectTemplate }) => {
|
|
6
|
+
const intl = useIntl();
|
|
7
|
+
return (
|
|
8
|
+
<div className="template-chooser">
|
|
9
|
+
<Message>
|
|
10
|
+
<Grid columns={templates(intl).length}>
|
|
11
|
+
{templates(intl).map((template, index) => (
|
|
12
|
+
<Grid.Column key={template.id}>
|
|
13
|
+
<Button
|
|
14
|
+
className="template-chooser-item"
|
|
15
|
+
onClick={() => onSelectTemplate(index)}
|
|
16
|
+
>
|
|
17
|
+
<img src={template.image} alt="" />
|
|
18
|
+
<div className="template-chooser-title">
|
|
19
|
+
{intl.formatMessage({
|
|
20
|
+
id: template.id,
|
|
21
|
+
defaultMessage: template.title,
|
|
22
|
+
})}
|
|
23
|
+
</div>
|
|
24
|
+
</Button>
|
|
25
|
+
</Grid.Column>
|
|
26
|
+
))}
|
|
27
|
+
</Grid>
|
|
28
|
+
</Message>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
TemplateChooser.propTypes = {
|
|
34
|
+
templates: PropTypes.func.isRequired,
|
|
35
|
+
onSelectTemplate: PropTypes.func.isRequired,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default TemplateChooser;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import renderer from 'react-test-renderer';
|
|
3
|
+
import configureStore from 'redux-mock-store';
|
|
4
|
+
import { Provider } from 'react-intl-redux';
|
|
5
|
+
import TemplateChooser from './TemplateChooser';
|
|
6
|
+
import templateSVG from './template.svg';
|
|
7
|
+
|
|
8
|
+
const mockStore = configureStore();
|
|
9
|
+
|
|
10
|
+
test('renders a TemplateChooser component', () => {
|
|
11
|
+
const store = mockStore({
|
|
12
|
+
intl: {
|
|
13
|
+
locale: 'en',
|
|
14
|
+
messages: { templateid: 'Template default translation' },
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const component = renderer.create(
|
|
19
|
+
<Provider store={store}>
|
|
20
|
+
<TemplateChooser
|
|
21
|
+
templates={() => [
|
|
22
|
+
{
|
|
23
|
+
image: templateSVG,
|
|
24
|
+
id: 'templateid',
|
|
25
|
+
defaultMessage: 'Template default translation',
|
|
26
|
+
},
|
|
27
|
+
]}
|
|
28
|
+
onSelectTemplate={() => {}}
|
|
29
|
+
/>
|
|
30
|
+
</Provider>,
|
|
31
|
+
);
|
|
32
|
+
const json = component.toJSON();
|
|
33
|
+
expect(json).toMatchSnapshot();
|
|
34
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96">
|
|
2
|
+
<g fill="none" fill-rule="evenodd">
|
|
3
|
+
<rect width="96" height="96" fill="#9FD1E5" rx="3"/>
|
|
4
|
+
<g fill="#FFF" opacity=".9" transform="translate(9 21)">
|
|
5
|
+
<rect width="78" height="29"/>
|
|
6
|
+
<rect width="36" height="8" y="33"/>
|
|
7
|
+
<rect width="77" height="10" y="45"/>
|
|
8
|
+
</g>
|
|
9
|
+
</g>
|
|
10
|
+
</svg>
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
isInternalURL,
|
|
13
13
|
URLUtils,
|
|
14
14
|
} from '@plone/volto/helpers/Url/Url';
|
|
15
|
-
import { matchPath } from 'react-router';
|
|
16
15
|
|
|
17
16
|
import config from '@plone/volto/registry';
|
|
18
17
|
|
|
@@ -66,11 +65,8 @@ const UniversalLink = ({
|
|
|
66
65
|
}
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
matchPath(flattenToAppURL(url), route.match),
|
|
72
|
-
)?.length > 0;
|
|
73
|
-
const isExternal = !isInternalURL(url) || isBlacklisted;
|
|
68
|
+
const isExternal = !isInternalURL(url);
|
|
69
|
+
|
|
74
70
|
const isDownload = (!isExternal && url.includes('@@download')) || download;
|
|
75
71
|
const isDisplayFile =
|
|
76
72
|
(!isExternal && url.includes('@@display-file')) || false;
|
|
@@ -157,6 +157,42 @@ describe('UniversalLink', () => {
|
|
|
157
157
|
);
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
+
it('UniversalLink renders external link where link is blacklisted', () => {
|
|
161
|
+
const notInEN = /^(?!.*(#|\/en|\/static|\/controlpanel|\/cypress|\/login|\/logout|\/contact-form)).*$/;
|
|
162
|
+
config.settings.externalRoutes = [
|
|
163
|
+
{
|
|
164
|
+
match: {
|
|
165
|
+
path: notInEN,
|
|
166
|
+
exact: false,
|
|
167
|
+
strict: false,
|
|
168
|
+
},
|
|
169
|
+
url(payload) {
|
|
170
|
+
return payload.location.pathname;
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
const { getByTitle } = render(
|
|
176
|
+
<Provider store={store}>
|
|
177
|
+
<MemoryRouter>
|
|
178
|
+
<UniversalLink
|
|
179
|
+
href="http://localhost:3000/blacklisted-app"
|
|
180
|
+
title="External blacklisted app"
|
|
181
|
+
>
|
|
182
|
+
<h1>Title</h1>
|
|
183
|
+
</UniversalLink>
|
|
184
|
+
</MemoryRouter>
|
|
185
|
+
</Provider>,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
expect(getByTitle('External blacklisted app').getAttribute('target')).toBe(
|
|
189
|
+
'_blank',
|
|
190
|
+
);
|
|
191
|
+
expect(getByTitle('External blacklisted app').getAttribute('rel')).toBe(
|
|
192
|
+
'noopener noreferrer',
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
|
|
160
196
|
it('check UniversalLink does not break with error in item', () => {
|
|
161
197
|
const component = renderer.create(
|
|
162
198
|
<Provider store={store}>
|
|
@@ -3,13 +3,12 @@ import { Link } from 'react-router-dom';
|
|
|
3
3
|
import { Menu } from 'semantic-ui-react';
|
|
4
4
|
import { FormattedMessage } from 'react-intl';
|
|
5
5
|
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
6
|
-
import { useToken } from '@plone/volto/hooks/userSession/useToken';
|
|
7
|
-
import { useContent } from '@plone/volto/hooks/content/useContent';
|
|
8
6
|
import config from '@plone/volto/registry';
|
|
7
|
+
import { useSelector, shallowEqual } from 'react-redux';
|
|
9
8
|
|
|
10
9
|
const Anontools = () => {
|
|
11
|
-
const token =
|
|
12
|
-
const
|
|
10
|
+
const token = useSelector((state) => state.userSession.token, shallowEqual);
|
|
11
|
+
const content = useSelector((state) => state.content.data, shallowEqual);
|
|
13
12
|
|
|
14
13
|
const { settings } = config;
|
|
15
14
|
return (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import registry from '@plone/volto/registry';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* A component that can
|
|
4
|
+
* A component that can automatically look up its implementation from the
|
|
5
5
|
* registry based on the provided component `componentName`
|
|
6
6
|
*/
|
|
7
7
|
const Component = ({ componentName, dependencies, ...rest }) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Container, Segment } from 'semantic-ui-react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import {
|
|
3
|
+
import { useSelector, shallowEqual } from 'react-redux';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
Anontools,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from '@plone/volto/components';
|
|
12
12
|
|
|
13
13
|
const Header = ({ pathname }) => {
|
|
14
|
-
const token =
|
|
14
|
+
const token = useSelector((state) => state.userSession.token, shallowEqual);
|
|
15
15
|
|
|
16
16
|
return (
|
|
17
17
|
<Segment basic className="header-wrapper" role="banner">
|
|
@@ -5,9 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
import React, { Component } from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Container as SemanticContainer,
|
|
10
|
+
GridColumn,
|
|
11
|
+
Segment,
|
|
12
|
+
} from 'semantic-ui-react';
|
|
9
13
|
import { Button, Modal, Grid } from 'semantic-ui-react';
|
|
10
14
|
import { Icon, UniversalLink, PreviewImage } from '@plone/volto/components';
|
|
15
|
+
import config from '@plone/volto/registry';
|
|
11
16
|
|
|
12
17
|
import openSVG from '@plone/volto/icons/open.svg';
|
|
13
18
|
import aheadSVG from '@plone/volto/icons/ahead.svg';
|
|
@@ -56,6 +61,9 @@ class AlbumView extends Component {
|
|
|
56
61
|
|
|
57
62
|
render() {
|
|
58
63
|
const { content } = this.props;
|
|
64
|
+
const Container =
|
|
65
|
+
config.getComponent({ name: 'Container' }).component || SemanticContainer;
|
|
66
|
+
|
|
59
67
|
return (
|
|
60
68
|
<Container className="view-wrapper">
|
|
61
69
|
<article id="content">
|
|
@@ -9,6 +9,8 @@ import { hasBlocksData, flattenHTMLToAppURL } from '@plone/volto/helpers';
|
|
|
9
9
|
import { Image, Grid } from 'semantic-ui-react';
|
|
10
10
|
import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
|
|
11
11
|
import { EventDetails } from '@plone/volto/components';
|
|
12
|
+
import { Container as SemanticContainer } from 'semantic-ui-react';
|
|
13
|
+
import config from '@plone/volto/registry';
|
|
12
14
|
|
|
13
15
|
const EventTextfieldView = ({ content }) => (
|
|
14
16
|
<React.Fragment>
|
|
@@ -41,9 +43,11 @@ const EventTextfieldView = ({ content }) => (
|
|
|
41
43
|
*/
|
|
42
44
|
const EventView = (props) => {
|
|
43
45
|
const { content } = props;
|
|
46
|
+
const Container =
|
|
47
|
+
config.getComponent({ name: 'Container' }).component || SemanticContainer;
|
|
44
48
|
|
|
45
49
|
return (
|
|
46
|
-
<
|
|
50
|
+
<Container id="page-document" className="view-wrapper event-view">
|
|
47
51
|
<Grid>
|
|
48
52
|
<Grid.Column width={7} className="mobile hidden">
|
|
49
53
|
{hasBlocksData(content) ? (
|
|
@@ -83,7 +87,7 @@ const EventView = (props) => {
|
|
|
83
87
|
)}
|
|
84
88
|
</Grid.Column>
|
|
85
89
|
</Grid>
|
|
86
|
-
</
|
|
90
|
+
</Container>
|
|
87
91
|
);
|
|
88
92
|
};
|
|
89
93
|
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import { Container } from 'semantic-ui-react';
|
|
9
|
-
|
|
8
|
+
import { Container as SemanticContainer } from 'semantic-ui-react';
|
|
10
9
|
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
10
|
+
import config from '@plone/volto/registry';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* File view component class.
|
|
@@ -15,22 +15,27 @@ import { flattenToAppURL } from '@plone/volto/helpers';
|
|
|
15
15
|
* @params {object} content Content object.
|
|
16
16
|
* @returns {string} Markup of the component.
|
|
17
17
|
*/
|
|
18
|
-
const FileView = ({ content }) =>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
{content.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
18
|
+
const FileView = ({ content }) => {
|
|
19
|
+
const Container =
|
|
20
|
+
config.getComponent({ name: 'Container' }).component || SemanticContainer;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Container className="view-wrapper">
|
|
24
|
+
<h1 className="documentFirstHeading">
|
|
25
|
+
{content.title}
|
|
26
|
+
{content.subtitle && ` - ${content.subtitle}`}
|
|
27
|
+
</h1>
|
|
28
|
+
{content.description && (
|
|
29
|
+
<p className="documentDescription">{content.description}</p>
|
|
30
|
+
)}
|
|
31
|
+
{content.file?.download && (
|
|
32
|
+
<a href={flattenToAppURL(content.file.download)}>
|
|
33
|
+
{content.file.filename}
|
|
34
|
+
</a>
|
|
35
|
+
)}
|
|
36
|
+
</Container>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
34
39
|
|
|
35
40
|
/**
|
|
36
41
|
* Property types.
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import { Container } from 'semantic-ui-react';
|
|
8
|
+
import { Container as SemanticContainer } from 'semantic-ui-react';
|
|
9
9
|
import { FormattedMessage } from 'react-intl';
|
|
10
10
|
import prettybytes from 'pretty-bytes';
|
|
11
|
-
|
|
12
11
|
import { flattenToAppURL } from '@plone/volto/helpers';
|
|
12
|
+
import config from '@plone/volto/registry';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Image view component class.
|
|
@@ -17,37 +17,42 @@ import { flattenToAppURL } from '@plone/volto/helpers';
|
|
|
17
17
|
* @params {object} content Content object.
|
|
18
18
|
* @returns {string} Markup of the component.
|
|
19
19
|
*/
|
|
20
|
-
const ImageView = ({ content }) =>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
defaultMessage="Size: {size}"
|
|
39
|
-
values={{ size: prettybytes(content.image.size) }}
|
|
40
|
-
/>
|
|
41
|
-
—
|
|
42
|
-
<FormattedMessage
|
|
43
|
-
id="Click to download full sized image"
|
|
44
|
-
defaultMessage="Click to download full sized image"
|
|
20
|
+
const ImageView = ({ content }) => {
|
|
21
|
+
const Container =
|
|
22
|
+
config.getComponent({ name: 'Container' }).component || SemanticContainer;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Container className="view-wrapper">
|
|
26
|
+
<h1 className="documentFirstHeading">
|
|
27
|
+
{content.title}
|
|
28
|
+
{content.subtitle && ` - ${content.subtitle}`}
|
|
29
|
+
</h1>
|
|
30
|
+
{content.description && (
|
|
31
|
+
<p className="documentDescription">{content.description}</p>
|
|
32
|
+
)}
|
|
33
|
+
{content?.image?.download && (
|
|
34
|
+
<a href={flattenToAppURL(content.image.download)}>
|
|
35
|
+
<img
|
|
36
|
+
alt={content.title}
|
|
37
|
+
src={flattenToAppURL(content.image.scales.preview.download)}
|
|
45
38
|
/>
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
)
|
|
39
|
+
<figcaption>
|
|
40
|
+
<FormattedMessage
|
|
41
|
+
id="Size: {size}"
|
|
42
|
+
defaultMessage="Size: {size}"
|
|
43
|
+
values={{ size: prettybytes(content.image.size) }}
|
|
44
|
+
/>
|
|
45
|
+
—
|
|
46
|
+
<FormattedMessage
|
|
47
|
+
id="Click to download full sized image"
|
|
48
|
+
defaultMessage="Click to download full sized image"
|
|
49
|
+
/>
|
|
50
|
+
</figcaption>
|
|
51
|
+
</a>
|
|
52
|
+
)}
|
|
53
|
+
</Container>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
51
56
|
|
|
52
57
|
/**
|
|
53
58
|
* Property types.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import React, { Component } from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
8
|
import { isInternalURL, flattenToAppURL } from '@plone/volto/helpers';
|
|
9
|
-
import { Container } from 'semantic-ui-react';
|
|
9
|
+
import { Container as SemanticContainer } from 'semantic-ui-react';
|
|
10
10
|
import { UniversalLink } from '@plone/volto/components';
|
|
11
11
|
import { FormattedMessage } from 'react-intl';
|
|
12
12
|
import config from '@plone/volto/registry';
|
|
@@ -60,6 +60,9 @@ class LinkView extends Component {
|
|
|
60
60
|
render() {
|
|
61
61
|
const { remoteUrl } = this.props.content;
|
|
62
62
|
const { openExternalLinkInNewTab } = config.settings;
|
|
63
|
+
const Container =
|
|
64
|
+
config.getComponent({ name: 'Container' }).component || SemanticContainer;
|
|
65
|
+
|
|
63
66
|
return (
|
|
64
67
|
<Container id="page-document">
|
|
65
68
|
<h1 className="documentFirstHeading">{this.props.content.title}</h1>
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
import PropTypes from 'prop-types';
|
|
8
|
-
import { Segment, Container } from 'semantic-ui-react';
|
|
8
|
+
import { Segment, Container as SemanticContainer } from 'semantic-ui-react';
|
|
9
9
|
import { UniversalLink, PreviewImage } from '@plone/volto/components';
|
|
10
|
+
import config from '@plone/volto/registry';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* List view component class.
|
|
@@ -14,32 +15,37 @@ import { UniversalLink, PreviewImage } from '@plone/volto/components';
|
|
|
14
15
|
* @params {object} content Content object.
|
|
15
16
|
* @returns {string} Markup of the component.
|
|
16
17
|
*/
|
|
17
|
-
const ListingView = ({ content }) =>
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
item
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
18
|
+
const ListingView = ({ content }) => {
|
|
19
|
+
const Container =
|
|
20
|
+
config.getComponent({ name: 'Container' }).component || SemanticContainer;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Container id="page-home">
|
|
24
|
+
<section id="content-core">
|
|
25
|
+
{content.items.map((item) => (
|
|
26
|
+
<Segment key={item.url} className="listing-item">
|
|
27
|
+
<Container>
|
|
28
|
+
<h2>
|
|
29
|
+
<UniversalLink item={item} title={item['@type']}>
|
|
30
|
+
{item.title}
|
|
31
|
+
</UniversalLink>
|
|
32
|
+
</h2>
|
|
33
|
+
{item.description && <p>{item.description}</p>}
|
|
34
|
+
</Container>
|
|
35
|
+
{item.image_field && (
|
|
36
|
+
<PreviewImage
|
|
37
|
+
item={item}
|
|
38
|
+
size="thumb"
|
|
39
|
+
alt={item.image_caption ? item.image_caption : item.title}
|
|
40
|
+
className="ui image"
|
|
41
|
+
/>
|
|
42
|
+
)}
|
|
43
|
+
</Segment>
|
|
44
|
+
))}
|
|
45
|
+
</section>
|
|
46
|
+
</Container>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
43
49
|
|
|
44
50
|
/**
|
|
45
51
|
* Property types.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { getBaseUrl, applyBlockDefaults } from '@plone/volto/helpers';
|
|
3
|
-
import { defineMessages,
|
|
3
|
+
import { defineMessages, useIntl } from 'react-intl';
|
|
4
4
|
import { map } from 'lodash';
|
|
5
|
+
import { MaybeWrap } from '@plone/volto/components';
|
|
5
6
|
import {
|
|
6
7
|
getBlocksFieldname,
|
|
7
8
|
getBlocksLayoutFieldname,
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
import StyleWrapper from '@plone/volto/components/manage/Blocks/Block/StyleWrapper';
|
|
11
12
|
import config from '@plone/volto/registry';
|
|
12
13
|
import { ViewDefaultBlock } from '@plone/volto/components';
|
|
14
|
+
import RenderEmptyBlock from './RenderEmptyBlock';
|
|
13
15
|
|
|
14
16
|
const messages = defineMessages({
|
|
15
17
|
unknownBlock: {
|
|
@@ -23,7 +25,8 @@ const messages = defineMessages({
|
|
|
23
25
|
});
|
|
24
26
|
|
|
25
27
|
const RenderBlocks = (props) => {
|
|
26
|
-
const { content,
|
|
28
|
+
const { content, location, metadata, blockWrapperTag } = props;
|
|
29
|
+
const intl = useIntl();
|
|
27
30
|
const blocksFieldname = getBlocksFieldname(content);
|
|
28
31
|
const blocksLayoutFieldname = getBlocksLayoutFieldname(content);
|
|
29
32
|
const blocksConfig = props.blocksConfig || config.blocks.blocksConfig;
|
|
@@ -43,30 +46,56 @@ const RenderBlocks = (props) => {
|
|
|
43
46
|
properties: content,
|
|
44
47
|
});
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
49
|
+
if (content[blocksFieldname]?.[block]?.['@type'] === 'empty') {
|
|
50
|
+
return (
|
|
51
|
+
<MaybeWrap
|
|
52
|
+
key={block}
|
|
53
|
+
condition={blockWrapperTag}
|
|
54
|
+
as={blockWrapperTag}
|
|
55
|
+
>
|
|
56
|
+
<RenderEmptyBlock />
|
|
57
|
+
</MaybeWrap>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (Block) {
|
|
62
|
+
return (
|
|
63
|
+
<MaybeWrap
|
|
64
|
+
key={block}
|
|
65
|
+
condition={blockWrapperTag}
|
|
66
|
+
as={blockWrapperTag}
|
|
67
|
+
>
|
|
68
|
+
<StyleWrapper
|
|
69
|
+
key={block}
|
|
70
|
+
{...props}
|
|
71
|
+
id={block}
|
|
72
|
+
block={block}
|
|
73
|
+
data={blockData}
|
|
74
|
+
>
|
|
75
|
+
<Block
|
|
76
|
+
id={block}
|
|
77
|
+
metadata={metadata}
|
|
78
|
+
properties={content}
|
|
79
|
+
data={blockData}
|
|
80
|
+
path={getBaseUrl(location?.pathname || '')}
|
|
81
|
+
blocksConfig={blocksConfig}
|
|
82
|
+
/>
|
|
83
|
+
</StyleWrapper>
|
|
84
|
+
</MaybeWrap>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (blockData) {
|
|
89
|
+
return (
|
|
90
|
+
<div key={block}>
|
|
91
|
+
{intl.formatMessage(messages.unknownBlock, {
|
|
92
|
+
block: content[blocksFieldname]?.[block]?.['@type'],
|
|
93
|
+
})}
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return (
|
|
70
99
|
<div key={block}>{intl.formatMessage(messages.invalidBlock)}</div>
|
|
71
100
|
);
|
|
72
101
|
})}
|
|
@@ -76,4 +105,4 @@ const RenderBlocks = (props) => {
|
|
|
76
105
|
);
|
|
77
106
|
};
|
|
78
107
|
|
|
79
|
-
export default
|
|
108
|
+
export default RenderBlocks;
|