@eeacms/volto-cca-policy 0.1.1
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/.coverage.babel.config.js +9 -0
- package/.i18n.babel.config.js +1 -0
- package/.project.eslintrc.js +48 -0
- package/.release-it.json +17 -0
- package/CHANGELOG.md +30 -0
- package/DEVELOP.md +52 -0
- package/LICENSE.md +9 -0
- package/README.md +85 -0
- package/RELEASE.md +74 -0
- package/babel.config.js +17 -0
- package/bootstrap +41 -0
- package/cypress.config.js +26 -0
- package/jest-addon.config.js +36 -0
- package/locales/volto.pot +0 -0
- package/package.json +51 -0
- package/src/components/index.js +1 -0
- package/src/components/manage/Blocks/ContextNavigation/ContextNavigationEdit.jsx +31 -0
- package/src/components/manage/Blocks/ContextNavigation/ContextNavigationView.jsx +17 -0
- package/src/components/manage/Blocks/ContextNavigation/index.js +26 -0
- package/src/components/manage/Blocks/ContextNavigation/schema.js +81 -0
- package/src/components/manage/Blocks/LayoutSettings/LayoutSettingsEdit.jsx +32 -0
- package/src/components/manage/Blocks/LayoutSettings/LayoutSettingsView.jsx +15 -0
- package/src/components/manage/Blocks/LayoutSettings/edit.less +4 -0
- package/src/components/manage/Blocks/LayoutSettings/index.js +24 -0
- package/src/components/manage/Blocks/LayoutSettings/schema.js +32 -0
- package/src/components/manage/Blocks/Title/Edit.jsx +226 -0
- package/src/components/manage/Blocks/Title/View.jsx +35 -0
- package/src/components/manage/Blocks/Title/index.js +13 -0
- package/src/components/manage/Blocks/Title/schema.js +80 -0
- package/src/components/manage/Blocks/schema-utils.js +16 -0
- package/src/components/manage/Blocks/schema.js +52 -0
- package/src/components/theme/Banner/Banner.jsx +99 -0
- package/src/components/theme/Banner/View.jsx +241 -0
- package/src/components/theme/Banner/styles.less +20 -0
- package/src/components/theme/CustomCSS/CustomCSS.jsx +12 -0
- package/src/components/theme/DraftBackground/DraftBackground.jsx +16 -0
- package/src/components/theme/DraftBackground/draft.css +3 -0
- package/src/components/theme/DraftBackground/draft.png +0 -0
- package/src/components/theme/Homepage/HomePageInverseView.jsx +60 -0
- package/src/components/theme/Homepage/HomePageView.jsx +60 -0
- package/src/components/theme/Logo.jsx +34 -0
- package/src/components/theme/SubsiteClass.jsx +23 -0
- package/src/components/theme/Widgets/TokenWidget.jsx +16 -0
- package/src/config.js +307 -0
- package/src/customizations/@eeacms/volto-block-style/StyleWrapper/schema.js +44 -0
- package/src/customizations/@eeacms/volto-eea-design-system/ui/Header/HeaderSearchPopUp.js +80 -0
- package/src/customizations/@eeacms/volto-tabs-block/components/templates/default/schema.js +109 -0
- package/src/customizations/@eeacms/volto-tabs-block/components/templates/horizontal-responsive/schema.js +109 -0
- package/src/customizations/volto/components/manage/Form/Form.jsx +784 -0
- package/src/customizations/volto/components/manage/Form/ModalForm.jsx +326 -0
- package/src/customizations/volto/components/manage/Sharing/Sharing.jsx +495 -0
- package/src/customizations/volto/components/manage/Widgets/ObjectBrowserWidget.jsx +436 -0
- package/src/customizations/volto/components/theme/Breadcrumbs/Breadcrumbs.jsx +62 -0
- package/src/customizations/volto/components/theme/Comments/Comments.jsx +487 -0
- package/src/customizations/volto/components/theme/Footer/Footer.jsx +90 -0
- package/src/customizations/volto/components/theme/Header/Header.jsx +258 -0
- package/src/customizations/volto/components/theme/Tags/Tags.jsx +53 -0
- package/src/customizations/volto/components/theme/Unauthorized/Unauthorized.jsx +91 -0
- package/src/customizations/volto/components/theme/View/EventView.jsx +90 -0
- package/src/helpers/index.js +44 -0
- package/src/icons/content-box.svg +5 -0
- package/src/icons/image-narrow.svg +5 -0
- package/src/index.js +13 -0
- package/src/middleware/voltoCustom.js +37 -0
- package/src/policy.js +136 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header component.
|
|
3
|
+
* @module components/theme/Header/Header
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { Dropdown, Image } from 'semantic-ui-react';
|
|
8
|
+
import { connect, useDispatch, useSelector } from 'react-redux';
|
|
9
|
+
|
|
10
|
+
import { withRouter } from 'react-router-dom';
|
|
11
|
+
import { UniversalLink } from '@plone/volto/components';
|
|
12
|
+
import {
|
|
13
|
+
getBaseUrl,
|
|
14
|
+
hasApiExpander,
|
|
15
|
+
flattenToAppURL,
|
|
16
|
+
} from '@plone/volto/helpers';
|
|
17
|
+
import { getNavigation } from '@plone/volto/actions';
|
|
18
|
+
import { Header, Logo } from '@eeacms/volto-eea-design-system/ui';
|
|
19
|
+
import { usePrevious } from '@eeacms/volto-eea-design-system/helpers';
|
|
20
|
+
import { find } from 'lodash';
|
|
21
|
+
import globeIcon from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/global-line.svg';
|
|
22
|
+
import eeaFlag from '@eeacms/volto-eea-design-system/../theme/themes/eea/assets/images/Header/eea.png';
|
|
23
|
+
|
|
24
|
+
import config from '@plone/volto/registry';
|
|
25
|
+
import { compose } from 'recompose';
|
|
26
|
+
import { BodyClass } from '@plone/volto/helpers';
|
|
27
|
+
|
|
28
|
+
import cx from 'classnames';
|
|
29
|
+
|
|
30
|
+
function removeTrailingSlash(path) {
|
|
31
|
+
return path.replace(/\/+$/, '');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* EEA Specific Header component.
|
|
36
|
+
*/
|
|
37
|
+
const EEAHeader = ({ pathname, token, items, history, subsite }) => {
|
|
38
|
+
const currentLang = useSelector((state) => state.intl.locale);
|
|
39
|
+
const translations = useSelector(
|
|
40
|
+
(state) => state.content.data?.['@components']?.translations?.items,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const router_pathname = useSelector((state) => {
|
|
44
|
+
return removeTrailingSlash(state.router?.location?.pathname) || '';
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const isSubsite = subsite?.['@type'] === 'Subsite';
|
|
48
|
+
|
|
49
|
+
const isHomePageInverse = useSelector((state) => {
|
|
50
|
+
const layout = state.content?.data?.layout;
|
|
51
|
+
const has_home_layout =
|
|
52
|
+
layout === 'homepage_inverse_view' ||
|
|
53
|
+
(__CLIENT__ && document.body.classList.contains('homepage-inverse'));
|
|
54
|
+
return (
|
|
55
|
+
has_home_layout &&
|
|
56
|
+
(pathname === router_pathname || router_pathname.endsWith('/edit'))
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const { eea } = config.settings;
|
|
61
|
+
const headerOpts = eea.headerOpts || {};
|
|
62
|
+
const { logo, logoWhite } = headerOpts || {};
|
|
63
|
+
const width = useSelector((state) => state.screen?.width);
|
|
64
|
+
const dispatch = useDispatch();
|
|
65
|
+
const previousToken = usePrevious(token);
|
|
66
|
+
const [language, setLanguage] = React.useState(
|
|
67
|
+
currentLang || eea.defaultLanguage,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
React.useEffect(() => {
|
|
71
|
+
const { settings } = config;
|
|
72
|
+
const base_url = getBaseUrl(pathname);
|
|
73
|
+
if (!hasApiExpander('navigation', base_url)) {
|
|
74
|
+
dispatch(getNavigation(base_url, settings.navDepth));
|
|
75
|
+
}
|
|
76
|
+
}, [pathname, dispatch]);
|
|
77
|
+
|
|
78
|
+
React.useEffect(() => {
|
|
79
|
+
if (token !== previousToken) {
|
|
80
|
+
const { settings } = config;
|
|
81
|
+
const base = getBaseUrl(pathname);
|
|
82
|
+
if (!hasApiExpander('navigation', base)) {
|
|
83
|
+
dispatch(getNavigation(base, settings.navDepth));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}, [token, dispatch, pathname, previousToken]);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Header menuItems={items}>
|
|
90
|
+
{isHomePageInverse && <BodyClass className="homepage" />}
|
|
91
|
+
<Header.TopHeader>
|
|
92
|
+
<Header.TopItem className="official-union">
|
|
93
|
+
<Image src={eeaFlag} alt="eea flag"></Image>
|
|
94
|
+
<Header.TopDropdownMenu
|
|
95
|
+
text="An official website of the European Union | How do you know?"
|
|
96
|
+
tabletText="EEA information systems"
|
|
97
|
+
mobileText=" "
|
|
98
|
+
icon="chevron down"
|
|
99
|
+
aria-label="dropdown"
|
|
100
|
+
className=""
|
|
101
|
+
viewportWidth={width}
|
|
102
|
+
>
|
|
103
|
+
<div
|
|
104
|
+
className="content"
|
|
105
|
+
role="menu"
|
|
106
|
+
tabIndex="0"
|
|
107
|
+
onClick={(evt) => evt.stopPropagation()}
|
|
108
|
+
onKeyDown={(evt) => evt.stopPropagation()}
|
|
109
|
+
>
|
|
110
|
+
<p>
|
|
111
|
+
All official European Union website addresses are in the{' '}
|
|
112
|
+
<b>europa.eu</b> domain.
|
|
113
|
+
</p>
|
|
114
|
+
<a
|
|
115
|
+
href="https://europa.eu/european-union/contact/institutions-bodies_en"
|
|
116
|
+
target="_blank"
|
|
117
|
+
rel="noreferrer"
|
|
118
|
+
role="option"
|
|
119
|
+
aria-selected="false"
|
|
120
|
+
>
|
|
121
|
+
See all EU institutions and bodies
|
|
122
|
+
</a>
|
|
123
|
+
</div>
|
|
124
|
+
</Header.TopDropdownMenu>
|
|
125
|
+
</Header.TopItem>
|
|
126
|
+
|
|
127
|
+
{!!headerOpts.partnerLinks && (
|
|
128
|
+
<Header.TopItem>
|
|
129
|
+
<Header.TopDropdownMenu
|
|
130
|
+
id="theme-sites"
|
|
131
|
+
text={headerOpts.partnerLinks.title}
|
|
132
|
+
viewportWidth={width}
|
|
133
|
+
>
|
|
134
|
+
<div className="wrapper">
|
|
135
|
+
{headerOpts.partnerLinks.links.map((item, index) => (
|
|
136
|
+
<Dropdown.Item key={index}>
|
|
137
|
+
<a
|
|
138
|
+
href={item.href}
|
|
139
|
+
className="site"
|
|
140
|
+
target="_blank"
|
|
141
|
+
rel="noreferrer"
|
|
142
|
+
>
|
|
143
|
+
{item.title}
|
|
144
|
+
</a>
|
|
145
|
+
</Dropdown.Item>
|
|
146
|
+
))}
|
|
147
|
+
</div>
|
|
148
|
+
</Header.TopDropdownMenu>
|
|
149
|
+
</Header.TopItem>
|
|
150
|
+
)}
|
|
151
|
+
|
|
152
|
+
{config.settings.isMultilingual && (
|
|
153
|
+
<Header.TopDropdownMenu
|
|
154
|
+
id="language-switcher"
|
|
155
|
+
className="item"
|
|
156
|
+
text={`${language.toUpperCase()}`}
|
|
157
|
+
mobileText={`${language.toUpperCase()}`}
|
|
158
|
+
icon={
|
|
159
|
+
<Image src={globeIcon} alt="language dropdown globe icon"></Image>
|
|
160
|
+
}
|
|
161
|
+
viewportWidth={width}
|
|
162
|
+
>
|
|
163
|
+
<ul
|
|
164
|
+
className="wrapper language-list"
|
|
165
|
+
role="listbox"
|
|
166
|
+
aria-label="language switcher"
|
|
167
|
+
>
|
|
168
|
+
{eea.languages.map((item, index) => (
|
|
169
|
+
<Dropdown.Item
|
|
170
|
+
as="li"
|
|
171
|
+
key={index}
|
|
172
|
+
text={
|
|
173
|
+
<span>
|
|
174
|
+
{item.name}
|
|
175
|
+
<span className="country-code">
|
|
176
|
+
{item.code.toUpperCase()}
|
|
177
|
+
</span>
|
|
178
|
+
</span>
|
|
179
|
+
}
|
|
180
|
+
onClick={() => {
|
|
181
|
+
const translation = find(translations, {
|
|
182
|
+
language: item.code,
|
|
183
|
+
});
|
|
184
|
+
const to = translation
|
|
185
|
+
? flattenToAppURL(translation['@id'])
|
|
186
|
+
: `/${item.code}`;
|
|
187
|
+
setLanguage(item.code);
|
|
188
|
+
history.push(to);
|
|
189
|
+
}}
|
|
190
|
+
></Dropdown.Item>
|
|
191
|
+
))}
|
|
192
|
+
</ul>
|
|
193
|
+
</Header.TopDropdownMenu>
|
|
194
|
+
)}
|
|
195
|
+
</Header.TopHeader>
|
|
196
|
+
<Header.Main
|
|
197
|
+
pathname={pathname}
|
|
198
|
+
inverted={isHomePageInverse ? true : false}
|
|
199
|
+
transparency={isHomePageInverse ? true : false}
|
|
200
|
+
logo={
|
|
201
|
+
<div {...(isSubsite ? { className: 'logo-wrapper' } : {})}>
|
|
202
|
+
<Logo
|
|
203
|
+
src={isHomePageInverse ? logoWhite : logo}
|
|
204
|
+
title={eea.websiteTitle}
|
|
205
|
+
alt={eea.organisationName}
|
|
206
|
+
url={eea.logoTargetUrl}
|
|
207
|
+
/>
|
|
208
|
+
|
|
209
|
+
{!!subsite && subsite.title && (
|
|
210
|
+
<UniversalLink item={subsite} className="subsite-logo">
|
|
211
|
+
{subsite.title}
|
|
212
|
+
</UniversalLink>
|
|
213
|
+
)}
|
|
214
|
+
</div>
|
|
215
|
+
}
|
|
216
|
+
menuItems={items}
|
|
217
|
+
renderGlobalMenuItem={(item, { onClick }) => (
|
|
218
|
+
<a
|
|
219
|
+
href={item.url || '/'}
|
|
220
|
+
title={item.title}
|
|
221
|
+
onClick={(e) => {
|
|
222
|
+
e.preventDefault();
|
|
223
|
+
onClick(e, item);
|
|
224
|
+
}}
|
|
225
|
+
>
|
|
226
|
+
{item.title}
|
|
227
|
+
</a>
|
|
228
|
+
)}
|
|
229
|
+
renderMenuItem={(item, options, props) => (
|
|
230
|
+
<UniversalLink
|
|
231
|
+
href={item.url || '/'}
|
|
232
|
+
title={item.title}
|
|
233
|
+
{...(options || {})}
|
|
234
|
+
className={cx(options?.className, {
|
|
235
|
+
active: item.url === router_pathname,
|
|
236
|
+
})}
|
|
237
|
+
>
|
|
238
|
+
{props?.iconPosition !== 'right' && props?.children}
|
|
239
|
+
<span>{item.title}</span>
|
|
240
|
+
{props?.iconPosition === 'right' && props?.children}
|
|
241
|
+
</UniversalLink>
|
|
242
|
+
)}
|
|
243
|
+
></Header.Main>
|
|
244
|
+
</Header>
|
|
245
|
+
);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
export default compose(
|
|
249
|
+
withRouter,
|
|
250
|
+
connect(
|
|
251
|
+
(state) => ({
|
|
252
|
+
token: state.userSession.token,
|
|
253
|
+
items: state.navigation.items,
|
|
254
|
+
subsite: state.content.data?.['@components']?.subsite,
|
|
255
|
+
}),
|
|
256
|
+
{ getNavigation },
|
|
257
|
+
),
|
|
258
|
+
)(EEAHeader);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tags component.
|
|
3
|
+
* @module components/theme/Tags/Tags
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import PropTypes from 'prop-types';
|
|
8
|
+
import TagList from '@eeacms/volto-eea-design-system/ui/TagList/TagList';
|
|
9
|
+
import Tag from '@eeacms/volto-eea-design-system/ui/Tag/Tag';
|
|
10
|
+
import { Container } from 'semantic-ui-react';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Tags component class.
|
|
14
|
+
* @function Tags
|
|
15
|
+
* @param {array} tags Array of tags.
|
|
16
|
+
* @returns {string} Markup of the component.
|
|
17
|
+
*/
|
|
18
|
+
const Tags = ({ tags }) =>
|
|
19
|
+
tags && tags.length > 0 ? (
|
|
20
|
+
<Container className="eea">
|
|
21
|
+
<TagList className="right">
|
|
22
|
+
<TagList.Content>
|
|
23
|
+
{tags.map((tag) => (
|
|
24
|
+
<Tag href={`http://search.apps.eea.europa.eu/?q=${tag}`} key={tag}>
|
|
25
|
+
{tag}
|
|
26
|
+
</Tag>
|
|
27
|
+
))}
|
|
28
|
+
</TagList.Content>
|
|
29
|
+
</TagList>
|
|
30
|
+
</Container>
|
|
31
|
+
) : (
|
|
32
|
+
''
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Property types.
|
|
37
|
+
* @property {Object} propTypes Property types.
|
|
38
|
+
* @static
|
|
39
|
+
*/
|
|
40
|
+
Tags.propTypes = {
|
|
41
|
+
tags: PropTypes.arrayOf(PropTypes.string),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Default properties.
|
|
46
|
+
* @property {Object} defaultProps Default properties.
|
|
47
|
+
* @static
|
|
48
|
+
*/
|
|
49
|
+
Tags.defaultProps = {
|
|
50
|
+
tags: null,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default Tags;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module components/theme/Unauthorized/Unauthorized
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React, { useEffect } from 'react';
|
|
6
|
+
import { useLocation, Link, useHistory } from 'react-router-dom';
|
|
7
|
+
import { getBaseUrl } from '@plone/volto/helpers';
|
|
8
|
+
import { Container, Button } from 'semantic-ui-react';
|
|
9
|
+
|
|
10
|
+
import { FormattedMessage } from 'react-intl';
|
|
11
|
+
|
|
12
|
+
import { withServerErrorCode } from '@plone/volto/helpers/Utils/Utils';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* unauthorized function.
|
|
16
|
+
* @function Unauthorized
|
|
17
|
+
* @returns {string} Markup of the unauthorized page.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const Unauthorized = () => {
|
|
21
|
+
const location = useLocation();
|
|
22
|
+
const pathname = location.pathname;
|
|
23
|
+
const base_pathname = getBaseUrl(pathname);
|
|
24
|
+
const login = `${base_pathname}/login`;
|
|
25
|
+
const history = useHistory();
|
|
26
|
+
const [countdown, setCountdown] = React.useState(5);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const login_with_return = login + `?return_url=${pathname}`;
|
|
30
|
+
const timer = setTimeout(() => {
|
|
31
|
+
return history.push(login_with_return);
|
|
32
|
+
}, 5000);
|
|
33
|
+
return () => clearTimeout(timer);
|
|
34
|
+
}, [history, login, pathname]);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
const timer = setTimeout(() => {
|
|
38
|
+
if (countdown > 0) {
|
|
39
|
+
setCountdown(countdown - 1);
|
|
40
|
+
} else {
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
}
|
|
43
|
+
}, 1000);
|
|
44
|
+
// Clear timeout if the component is unmounted
|
|
45
|
+
return () => clearTimeout(timer);
|
|
46
|
+
}, [countdown]);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Container className="view-wrapper">
|
|
50
|
+
<h1>
|
|
51
|
+
<FormattedMessage id="Unauthorized" defaultMessage="Unauthorized" />
|
|
52
|
+
</h1>
|
|
53
|
+
<h3>
|
|
54
|
+
<FormattedMessage
|
|
55
|
+
id="Forbidden, you will be redirected to the login screen in "
|
|
56
|
+
defaultMessage="Forbidden, you will be redirected to the login screen in "
|
|
57
|
+
/>
|
|
58
|
+
<span id="redirect-countdown">{countdown}</span>.
|
|
59
|
+
</h3>
|
|
60
|
+
|
|
61
|
+
<p className="description">
|
|
62
|
+
<FormattedMessage id="Click" defaultMessage="Click" />{' '}
|
|
63
|
+
<Link
|
|
64
|
+
className={'ui mini compact button my-half unauthorized-buttons'}
|
|
65
|
+
to={login}
|
|
66
|
+
>
|
|
67
|
+
<FormattedMessage id="Log in" defaultMessage="Log in" />
|
|
68
|
+
</Link>
|
|
69
|
+
<FormattedMessage
|
|
70
|
+
id="to get to login screen or click"
|
|
71
|
+
defaultMessage="to get to login screen or click"
|
|
72
|
+
/>{' '}
|
|
73
|
+
<Button
|
|
74
|
+
size={'mini'}
|
|
75
|
+
compact
|
|
76
|
+
onClick={() => history.goBack()}
|
|
77
|
+
className={'my-half unauthorized-buttons'}
|
|
78
|
+
>
|
|
79
|
+
<FormattedMessage id="Get back" defaultMessage="Get back" />
|
|
80
|
+
</Button>
|
|
81
|
+
<FormattedMessage
|
|
82
|
+
id="to get to the previous location"
|
|
83
|
+
defaultMessage="to get to the previous location"
|
|
84
|
+
/>
|
|
85
|
+
.
|
|
86
|
+
</p>
|
|
87
|
+
</Container>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export default withServerErrorCode(401)(Unauthorized);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EventView view component.
|
|
3
|
+
* @module components/theme/View/EventView
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import PropTypes from 'prop-types';
|
|
8
|
+
import { hasBlocksData, flattenHTMLToAppURL } from '@plone/volto/helpers';
|
|
9
|
+
import { Image, Grid } from 'semantic-ui-react';
|
|
10
|
+
import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
|
|
11
|
+
import { EventDetails } from '@plone/volto/components';
|
|
12
|
+
|
|
13
|
+
const EventTextfieldView = ({ content }) => (
|
|
14
|
+
<React.Fragment>
|
|
15
|
+
{content.title && <h1 className="documentFirstHeading">{content.title}</h1>}
|
|
16
|
+
{content.description && (
|
|
17
|
+
<p className="documentDescription">{content.description}</p>
|
|
18
|
+
)}
|
|
19
|
+
{content.image && (
|
|
20
|
+
<Image
|
|
21
|
+
className="document-image"
|
|
22
|
+
src={content.image.scales.thumb.download}
|
|
23
|
+
floated="right"
|
|
24
|
+
/>
|
|
25
|
+
)}
|
|
26
|
+
{content.text && (
|
|
27
|
+
<div
|
|
28
|
+
dangerouslySetInnerHTML={{
|
|
29
|
+
__html: flattenHTMLToAppURL(content.text.data),
|
|
30
|
+
}}
|
|
31
|
+
/>
|
|
32
|
+
)}
|
|
33
|
+
</React.Fragment>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* EventView view component class.
|
|
38
|
+
* @function EventView
|
|
39
|
+
* @params {object} content Content object.
|
|
40
|
+
* @returns {string} Markup of the component.
|
|
41
|
+
*/
|
|
42
|
+
const EventView = (props) => {
|
|
43
|
+
const { content } = props;
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div id="page-document" className="ui container viewwrapper event-view">
|
|
47
|
+
<Grid>
|
|
48
|
+
<Grid.Column mobile={12} tablet={7} computer={7}>
|
|
49
|
+
{hasBlocksData(content) ? (
|
|
50
|
+
<RenderBlocks {...props} />
|
|
51
|
+
) : (
|
|
52
|
+
<EventTextfieldView {...props} />
|
|
53
|
+
)}
|
|
54
|
+
</Grid.Column>
|
|
55
|
+
<Grid.Column mobile={12} tablet={5} computer={5}>
|
|
56
|
+
<EventDetails content={content} />
|
|
57
|
+
</Grid.Column>
|
|
58
|
+
</Grid>
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Property types.
|
|
65
|
+
* @property {Object} propTypes Property types.
|
|
66
|
+
* @static
|
|
67
|
+
*/
|
|
68
|
+
EventView.propTypes = {
|
|
69
|
+
content: PropTypes.shape({
|
|
70
|
+
title: PropTypes.string,
|
|
71
|
+
description: PropTypes.string,
|
|
72
|
+
text: PropTypes.shape({
|
|
73
|
+
data: PropTypes.string,
|
|
74
|
+
}),
|
|
75
|
+
attendees: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
76
|
+
contact_email: PropTypes.string,
|
|
77
|
+
contact_name: PropTypes.string,
|
|
78
|
+
contact_phone: PropTypes.string,
|
|
79
|
+
end: PropTypes.string.isRequired,
|
|
80
|
+
event_url: PropTypes.string,
|
|
81
|
+
location: PropTypes.string,
|
|
82
|
+
open_end: PropTypes.bool,
|
|
83
|
+
recurrence: PropTypes.any,
|
|
84
|
+
start: PropTypes.string.isRequired,
|
|
85
|
+
subjects: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
86
|
+
whole_day: PropTypes.bool,
|
|
87
|
+
}).isRequired,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export default EventView;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import superagent from 'superagent';
|
|
2
|
+
import config from '@plone/volto/registry';
|
|
3
|
+
import { addHeadersFactory } from '@plone/volto/helpers/Proxy/Proxy';
|
|
4
|
+
import { isArray } from 'lodash';
|
|
5
|
+
import { serializeNodes } from '@plone/volto-slate/editor/render';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get a resource image/file with authenticated (if token exist) API headers
|
|
9
|
+
* @function getBackendResourceWithAuth
|
|
10
|
+
* @param {Object} req Request object
|
|
11
|
+
* @return {string} The response with the image
|
|
12
|
+
*/
|
|
13
|
+
export const getBackendResourceWithAuth = (req) =>
|
|
14
|
+
new Promise((resolve, reject) => {
|
|
15
|
+
const { settings } = config;
|
|
16
|
+
|
|
17
|
+
let apiPath = '';
|
|
18
|
+
if (settings.internalApiPath && __SERVER__) {
|
|
19
|
+
apiPath = settings.internalApiPath;
|
|
20
|
+
} else if (__DEVELOPMENT__ && settings.devProxyToApiPath) {
|
|
21
|
+
apiPath = settings.devProxyToApiPath;
|
|
22
|
+
} else {
|
|
23
|
+
apiPath = settings.apiPath;
|
|
24
|
+
}
|
|
25
|
+
const backendURL = `${apiPath}${req.path}`;
|
|
26
|
+
const request = superagent
|
|
27
|
+
.get(backendURL)
|
|
28
|
+
.maxResponseSize(settings.maxResponseSize)
|
|
29
|
+
.responseType('blob');
|
|
30
|
+
const authToken = req.universalCookies.get('auth_token');
|
|
31
|
+
if (authToken) {
|
|
32
|
+
request.set('Authorization', `Bearer ${authToken}`);
|
|
33
|
+
}
|
|
34
|
+
request.use(addHeadersFactory(req));
|
|
35
|
+
request.then(resolve).catch(reject);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const createSlateParagraph = (text) => {
|
|
39
|
+
return isArray(text) ? text : config.settings.slate.defaultValue();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const serializeText = (text) => {
|
|
43
|
+
return isArray(text) ? serializeNodes(text) : text;
|
|
44
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
3
|
+
<svg width="90mm" height="90mm" version="1.1" viewBox="0 0 90 90" xmlns="http://www.w3.org/2000/svg">
|
|
4
|
+
<text x="44.72715" y="38.463978" fill="#000000" font-family="sans-serif" font-size="16.933px" letter-spacing="0px" stroke-width=".26458" text-align="center" text-anchor="middle" word-spacing="0px" style="line-height:1.5" xml:space="preserve"><tspan x="44.72715" y="38.463978" text-align="center">Content</tspan><tspan x="44.72715" y="63.863926" text-align="center">Box</tspan></text>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg width="36" height="36" viewBox="0 0 36 36" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M10 13L11.9333 11V25L10 23H26L24.0667 25V11L26 13H10ZM26 11V18V21.5V23.5V25H10V11H26Z" fill="black"/>
|
|
3
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 31H29V29H7V31Z"/>
|
|
4
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 7H29V5H7V7Z"/>
|
|
5
|
+
</svg>
|
package/src/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const applyConfig = (config) => {
|
|
2
|
+
config.settings.dateLocale = 'en-gb';
|
|
3
|
+
config.settings.isMultilingual = true;
|
|
4
|
+
config.settings.defaultLanguage =
|
|
5
|
+
config.settings.eea?.defaultLanguage || 'en';
|
|
6
|
+
config.settings.supportedLanguages = config.settings.eea?.languages?.map(
|
|
7
|
+
(item) => item.code,
|
|
8
|
+
) || ['en'];
|
|
9
|
+
|
|
10
|
+
return config;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default applyConfig;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getBackendResourceWithAuth } from '@eeacms/volto-cca-policy/helpers';
|
|
2
|
+
|
|
3
|
+
const HEADERS = [
|
|
4
|
+
'Accept-Ranges',
|
|
5
|
+
'Cache-Control',
|
|
6
|
+
'Content-Disposition',
|
|
7
|
+
'Content-Range',
|
|
8
|
+
'Content-Type',
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
function voltoCustomMiddleware(req, res, next) {
|
|
12
|
+
getBackendResourceWithAuth(req)
|
|
13
|
+
.then((resource) => {
|
|
14
|
+
// Just forward the headers that we need
|
|
15
|
+
HEADERS.forEach((header) => {
|
|
16
|
+
if (resource?.get?.(header)) {
|
|
17
|
+
res.set(header, resource.get(header));
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
res.status(resource.statusCode);
|
|
21
|
+
res.send(resource.body);
|
|
22
|
+
})
|
|
23
|
+
.catch(() => {
|
|
24
|
+
res.set('Content-Type', 'text/css; charset=utf-8');
|
|
25
|
+
res.status(200);
|
|
26
|
+
res.send(
|
|
27
|
+
'/* Override this by adding a File called voltoCustom.css to backend at portal_skins/custom/manage_main */',
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default function (express) {
|
|
33
|
+
const middleware = express.Router();
|
|
34
|
+
middleware.all(['**/voltoCustom.css$'], voltoCustomMiddleware);
|
|
35
|
+
middleware.id = 'voltoCustom.css';
|
|
36
|
+
return middleware;
|
|
37
|
+
}
|