@eeacms/volto-eea-website-theme 1.26.2 → 1.27.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/{.project.eslintrc.js → .eslintrc.js} +8 -0
- package/CHANGELOG.md +17 -0
- package/package.json +1 -1
- package/src/components/manage/Blocks/Title/schema.js +1 -1
- package/src/customizations/volto/components/manage/Blocks/Image/schema.js +2 -2
- package/src/customizations/volto/components/manage/Blocks/LeadImage/schema.js +1 -1
- package/src/customizations/volto/components/manage/Diff/DiffField.jsx +351 -0
- package/src/customizations/volto/components/manage/UniversalLink/UniversalLink.jsx +154 -0
- package/src/customizations/volto/components/theme/AppExtras/AppExtras.jsx +27 -0
- package/src/customizations/volto/components/theme/Header/Header.jsx +2 -2
- package/src/index.js +39 -6
- package/src/index.test.js +2 -2
- package/src/reducers/index.js +1 -0
package/CHANGELOG.md
CHANGED
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file. Dates are d
|
|
4
4
|
|
5
5
|
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
6
6
|
|
7
|
+
### [1.27.1](https://github.com/eea/volto-eea-website-theme/compare/1.27.0...1.27.1) - 18 January 2024
|
8
|
+
|
9
|
+
#### :bug: Bug Fixes
|
10
|
+
|
11
|
+
- fix: remove norefferer for seo purposes - refs #263008 [Gabriel - [`fab8094`](https://github.com/eea/volto-eea-website-theme/commit/fab80944fa3267ee4c584d2c4f77480d2270242c)]
|
12
|
+
|
13
|
+
### [1.27.0](https://github.com/eea/volto-eea-website-theme/compare/1.26.2...1.27.0) - 17 January 2024
|
14
|
+
|
15
|
+
#### :rocket: New Features
|
16
|
+
|
17
|
+
- feat: Put cloneData function for slate block, in order to change the uuids of fragments - refs #261770 [dobri1408 - [`069bffc`](https://github.com/eea/volto-eea-website-theme/commit/069bffcf114a886690408bbc2c2909c298b5e1aa)]
|
18
|
+
|
19
|
+
#### :bug: Bug Fixes
|
20
|
+
|
21
|
+
- fix: noreferrer on links - refs #263008 [ichim-david - [`d8f81e0`](https://github.com/eea/volto-eea-website-theme/commit/d8f81e01d9d2666e834ac92b2f34976f25156ea2)]
|
22
|
+
- fix: History diff page overlap - refs #262216 [dobri1408 - [`f580b16`](https://github.com/eea/volto-eea-website-theme/commit/f580b16f24faacb9318402699a1b616814840d63)]
|
23
|
+
|
7
24
|
### [1.26.2](https://github.com/eea/volto-eea-website-theme/compare/1.26.1...1.26.2) - 4 January 2024
|
8
25
|
|
9
26
|
### [1.26.1](https://github.com/eea/volto-eea-website-theme/compare/1.26.0...1.26.1) - 14 December 2023
|
package/package.json
CHANGED
@@ -133,7 +133,7 @@ export default {
|
|
133
133
|
Ex. ri-copyright-line. See{' '}
|
134
134
|
<a
|
135
135
|
target="_blank"
|
136
|
-
rel="noopener
|
136
|
+
rel="noopener"
|
137
137
|
href="https://eea.github.io/volto-eea-design-system/docs/webdev/Guidelines/iconography/#icon-set"
|
138
138
|
>
|
139
139
|
Remix Icon set
|
@@ -80,7 +80,7 @@ export function ImageSchema({ formData, intl }) {
|
|
80
80
|
href="https://www.w3.org/WAI/tutorials/images/decision-tree/"
|
81
81
|
title={intl.formatMessage(messages.openLinkInNewTab)}
|
82
82
|
target="_blank"
|
83
|
-
rel="noopener
|
83
|
+
rel="noopener"
|
84
84
|
>
|
85
85
|
{intl.formatMessage(messages.AltTextHintLinkText)}
|
86
86
|
</a>{' '}
|
@@ -117,7 +117,7 @@ export function ImageSchema({ formData, intl }) {
|
|
117
117
|
Ex. ri-copyright-line. See{' '}
|
118
118
|
<a
|
119
119
|
target="_blank"
|
120
|
-
rel="noopener
|
120
|
+
rel="noopener"
|
121
121
|
href="https://eea.github.io/volto-eea-design-system/docs/webdev/Guidelines/iconography/#icon-set"
|
122
122
|
>
|
123
123
|
Remix Icon set
|
@@ -95,7 +95,7 @@ export function LeadImageSchema({ formData, intl }) {
|
|
95
95
|
Ex. ri-copyright-line. See{' '}
|
96
96
|
<a
|
97
97
|
target="_blank"
|
98
|
-
rel="noopener
|
98
|
+
rel="noopener"
|
99
99
|
href="https://eea.github.io/volto-eea-design-system/docs/webdev/Guidelines/iconography/#icon-set"
|
100
100
|
>
|
101
101
|
Remix Icon set
|
@@ -0,0 +1,351 @@
|
|
1
|
+
/**
|
2
|
+
* Diff field component.
|
3
|
+
* @module components/manage/Diff/DiffField
|
4
|
+
*/
|
5
|
+
|
6
|
+
import React from 'react';
|
7
|
+
// import { diffWords as dWords } from 'diff';
|
8
|
+
import { join, map } from 'lodash';
|
9
|
+
import PropTypes from 'prop-types';
|
10
|
+
import { Grid } from 'semantic-ui-react';
|
11
|
+
import ReactDOMServer from 'react-dom/server';
|
12
|
+
import { Provider } from 'react-intl-redux';
|
13
|
+
import { createBrowserHistory } from 'history';
|
14
|
+
import { ConnectedRouter } from 'connected-react-router';
|
15
|
+
import { useSelector } from 'react-redux';
|
16
|
+
|
17
|
+
import { Api } from '@plone/volto/helpers';
|
18
|
+
import configureStore from '@plone/volto/store';
|
19
|
+
import { DefaultView } from '@plone/volto/components/';
|
20
|
+
import { serializeNodes } from '@plone/volto-slate/editor/render';
|
21
|
+
|
22
|
+
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Enhanced diff words utility
|
26
|
+
* @function diffWords
|
27
|
+
* @param oneStr Field one
|
28
|
+
* @param twoStr Field two
|
29
|
+
*/
|
30
|
+
|
31
|
+
/**
|
32
|
+
* Diff field component.
|
33
|
+
* @function DiffField
|
34
|
+
* @param {*} one Field one
|
35
|
+
* @param {*} two Field two
|
36
|
+
* @param {Object} schema Field schema
|
37
|
+
* @returns {string} Markup of the component.
|
38
|
+
*/
|
39
|
+
const DiffField = ({
|
40
|
+
one,
|
41
|
+
two,
|
42
|
+
contentOne,
|
43
|
+
contentTwo,
|
44
|
+
view,
|
45
|
+
schema,
|
46
|
+
diffLib,
|
47
|
+
}) => {
|
48
|
+
const language = useSelector((state) => state.intl.locale);
|
49
|
+
const readable_date_format = {
|
50
|
+
dateStyle: 'full',
|
51
|
+
timeStyle: 'short',
|
52
|
+
};
|
53
|
+
const splitWords = (str) => {
|
54
|
+
if (!str) return [];
|
55
|
+
const splitedArray = [];
|
56
|
+
let elementCurent = '';
|
57
|
+
let insideTag = false;
|
58
|
+
for (let i = 0; i < str.length; i++)
|
59
|
+
if (str[i] === '<') {
|
60
|
+
if (elementCurent) splitedArray.push(elementCurent);
|
61
|
+
elementCurent = '<';
|
62
|
+
insideTag = true;
|
63
|
+
} else if (str[i] === '>') {
|
64
|
+
elementCurent += '>';
|
65
|
+
splitedArray.push(elementCurent);
|
66
|
+
elementCurent = '';
|
67
|
+
insideTag = false;
|
68
|
+
} else if (str[i] === ' ' && insideTag === false) {
|
69
|
+
elementCurent += ' ';
|
70
|
+
splitedArray.push(elementCurent);
|
71
|
+
elementCurent = '';
|
72
|
+
} else elementCurent += str[i];
|
73
|
+
if (elementCurent) splitedArray.push(elementCurent);
|
74
|
+
return splitedArray;
|
75
|
+
};
|
76
|
+
|
77
|
+
const diffWords = (oneStr, twoStr) => {
|
78
|
+
return diffLib.diffArrays(splitWords(oneStr), splitWords(twoStr));
|
79
|
+
};
|
80
|
+
|
81
|
+
let parts, oneArray, twoArray;
|
82
|
+
if (schema.widget && schema.title !== 'Data sources and providers') {
|
83
|
+
switch (schema.widget) {
|
84
|
+
case 'richtext':
|
85
|
+
parts = diffWords(one?.data, two?.data);
|
86
|
+
break;
|
87
|
+
case 'datetime':
|
88
|
+
parts = diffWords(
|
89
|
+
new Intl.DateTimeFormat(language, readable_date_format)
|
90
|
+
.format(new Date(one))
|
91
|
+
.replace('\u202F', ' '),
|
92
|
+
new Intl.DateTimeFormat(language, readable_date_format)
|
93
|
+
.format(new Date(two))
|
94
|
+
.replace('\u202F', ' '),
|
95
|
+
);
|
96
|
+
break;
|
97
|
+
case 'json': {
|
98
|
+
const api = new Api();
|
99
|
+
const history = createBrowserHistory();
|
100
|
+
const store = configureStore(window.__data, history, api);
|
101
|
+
parts = diffWords(
|
102
|
+
ReactDOMServer.renderToStaticMarkup(
|
103
|
+
<Provider store={store}>
|
104
|
+
<ConnectedRouter history={history}>
|
105
|
+
<DefaultView content={contentOne} />
|
106
|
+
</ConnectedRouter>
|
107
|
+
</Provider>,
|
108
|
+
),
|
109
|
+
ReactDOMServer.renderToStaticMarkup(
|
110
|
+
<Provider store={store}>
|
111
|
+
<ConnectedRouter history={history}>
|
112
|
+
<DefaultView content={contentTwo} />
|
113
|
+
</ConnectedRouter>
|
114
|
+
</Provider>,
|
115
|
+
),
|
116
|
+
);
|
117
|
+
break;
|
118
|
+
}
|
119
|
+
case 'slate': {
|
120
|
+
const api = new Api();
|
121
|
+
const history = createBrowserHistory();
|
122
|
+
const store = configureStore(window.__data, history, api);
|
123
|
+
parts = diffWords(
|
124
|
+
ReactDOMServer.renderToStaticMarkup(
|
125
|
+
<Provider store={store}>
|
126
|
+
<ConnectedRouter history={history}>
|
127
|
+
{serializeNodes(one)}
|
128
|
+
</ConnectedRouter>
|
129
|
+
</Provider>,
|
130
|
+
),
|
131
|
+
ReactDOMServer.renderToStaticMarkup(
|
132
|
+
<Provider store={store}>
|
133
|
+
<ConnectedRouter history={history}>
|
134
|
+
{serializeNodes(two)}
|
135
|
+
</ConnectedRouter>
|
136
|
+
</Provider>,
|
137
|
+
),
|
138
|
+
);
|
139
|
+
break;
|
140
|
+
}
|
141
|
+
case 'temporal': {
|
142
|
+
if (one?.temporal?.length > 0 && two.temporal?.length > 0) {
|
143
|
+
let firstString = one.temporal.reduce((acc, cur) => {
|
144
|
+
return acc + cur?.label + ', ';
|
145
|
+
}, '');
|
146
|
+
firstString = firstString.substring(0, firstString.length - 2);
|
147
|
+
let secondString = two.temporal.reduce((acc, cur) => {
|
148
|
+
return acc + cur?.label + ', ';
|
149
|
+
}, '');
|
150
|
+
secondString = secondString.substring(0, secondString.length - 2);
|
151
|
+
parts = diffWords(firstString, secondString);
|
152
|
+
}
|
153
|
+
break;
|
154
|
+
}
|
155
|
+
case 'geolocation': {
|
156
|
+
if (one?.geolocation?.length > 0 && two.geolocation?.length > 0) {
|
157
|
+
let firstString = one.geolocation.reduce((acc, cur) => {
|
158
|
+
return acc + cur?.label + ', ';
|
159
|
+
}, '');
|
160
|
+
firstString = firstString.substring(0, firstString.length - 2);
|
161
|
+
let secondString = two.geolocation.reduce((acc, cur) => {
|
162
|
+
return acc + cur?.label + ', ';
|
163
|
+
}, '');
|
164
|
+
secondString = secondString.substring(0, secondString.length - 2);
|
165
|
+
parts = diffWords(firstString, secondString);
|
166
|
+
}
|
167
|
+
break;
|
168
|
+
}
|
169
|
+
case 'textarea':
|
170
|
+
default:
|
171
|
+
parts = diffWords(one, two);
|
172
|
+
break;
|
173
|
+
}
|
174
|
+
} else if (schema.title === 'Data sources and providers') {
|
175
|
+
if (one?.data?.length > 0 && two.data?.length > 0) {
|
176
|
+
let firstString = one.data.reduce((acc, cur) => {
|
177
|
+
return acc + cur?.title + ', ' + cur?.organisation + '<br/>';
|
178
|
+
}, '');
|
179
|
+
firstString = firstString.substring(0, firstString.length - 2);
|
180
|
+
let secondString = two.data.reduce((acc, cur) => {
|
181
|
+
return acc + cur?.title + ', ' + cur?.organisation + '<br/>';
|
182
|
+
}, '');
|
183
|
+
secondString = secondString.substring(0, secondString.length - 2);
|
184
|
+
parts = diffWords(firstString, secondString);
|
185
|
+
}
|
186
|
+
} else if (schema.type === 'object') {
|
187
|
+
parts = diffWords(one?.filename || one, two?.filename || two);
|
188
|
+
} else if (schema.type === 'array') {
|
189
|
+
oneArray = (one || []).map((i) => i?.title || i);
|
190
|
+
twoArray = (two || []).map((j) => j?.title || j);
|
191
|
+
parts = diffWords(oneArray, twoArray);
|
192
|
+
} else {
|
193
|
+
parts = diffWords(one?.title || one, two?.title || two);
|
194
|
+
}
|
195
|
+
|
196
|
+
return (
|
197
|
+
<Grid data-testid="DiffField">
|
198
|
+
<Grid.Row>
|
199
|
+
<Grid.Column width={12}>{schema.title}</Grid.Column>
|
200
|
+
</Grid.Row>
|
201
|
+
|
202
|
+
{view === 'split' && (
|
203
|
+
<Grid.Row>
|
204
|
+
<Grid.Column width={6} verticalAlign="top">
|
205
|
+
<span
|
206
|
+
dangerouslySetInnerHTML={{
|
207
|
+
__html: join(
|
208
|
+
map(parts, (part) => {
|
209
|
+
let combined = (part.value || []).reduce((acc, value) => {
|
210
|
+
if (
|
211
|
+
part.removed &&
|
212
|
+
!value.includes('<') &&
|
213
|
+
!value.includes('>') &&
|
214
|
+
!value.includes('>') &&
|
215
|
+
!value.includes('</') &&
|
216
|
+
!value.includes('"') &&
|
217
|
+
!value.includes('src') &&
|
218
|
+
!value.includes('href') &&
|
219
|
+
!value.includes('=')
|
220
|
+
)
|
221
|
+
return acc + `<span class="deletion">${value}</span>`;
|
222
|
+
if (
|
223
|
+
part.added &&
|
224
|
+
!value.includes('<') &&
|
225
|
+
!value.includes('>') &&
|
226
|
+
!value.includes('>') &&
|
227
|
+
!value.includes('</') &&
|
228
|
+
!value.includes('"') &&
|
229
|
+
!value.includes('src') &&
|
230
|
+
!value.includes('href') &&
|
231
|
+
!value.includes('=')
|
232
|
+
)
|
233
|
+
return acc;
|
234
|
+
return acc + value;
|
235
|
+
}, '');
|
236
|
+
return combined;
|
237
|
+
}),
|
238
|
+
'',
|
239
|
+
),
|
240
|
+
}}
|
241
|
+
/>
|
242
|
+
</Grid.Column>
|
243
|
+
<Grid.Column width={6} verticalAlign="top">
|
244
|
+
<span
|
245
|
+
dangerouslySetInnerHTML={{
|
246
|
+
__html: join(
|
247
|
+
map(parts, (part) => {
|
248
|
+
let combined = (part.value || []).reduce((acc, value) => {
|
249
|
+
if (
|
250
|
+
part.added &&
|
251
|
+
!value.includes('<') &&
|
252
|
+
!value.includes('>') &&
|
253
|
+
!value.includes('>') &&
|
254
|
+
!value.includes('</') &&
|
255
|
+
!value.includes('"') &&
|
256
|
+
!value.includes('src') &&
|
257
|
+
!value.includes('href') &&
|
258
|
+
!value.includes('=')
|
259
|
+
)
|
260
|
+
return acc + `<span class="addition">${value}</span>`;
|
261
|
+
if (
|
262
|
+
part.removed &&
|
263
|
+
!value.includes('<') &&
|
264
|
+
!value.includes('>') &&
|
265
|
+
!value.includes('>') &&
|
266
|
+
!value.includes('</') &&
|
267
|
+
!value.includes('"') &&
|
268
|
+
!value.includes('src') &&
|
269
|
+
!value.includes('href') &&
|
270
|
+
!value.includes('=')
|
271
|
+
)
|
272
|
+
return acc;
|
273
|
+
return acc + value;
|
274
|
+
}, '');
|
275
|
+
return combined;
|
276
|
+
}),
|
277
|
+
'',
|
278
|
+
),
|
279
|
+
}}
|
280
|
+
/>
|
281
|
+
</Grid.Column>
|
282
|
+
</Grid.Row>
|
283
|
+
)}
|
284
|
+
{view === 'unified' && (
|
285
|
+
<Grid.Row>
|
286
|
+
<Grid.Column width={16} verticalAlign="top">
|
287
|
+
<span
|
288
|
+
dangerouslySetInnerHTML={{
|
289
|
+
__html: join(
|
290
|
+
map(parts, (part) => {
|
291
|
+
let combined = (part.value || []).reduce((acc, value) => {
|
292
|
+
if (
|
293
|
+
part.removed &&
|
294
|
+
!value.includes('<') &&
|
295
|
+
!value.includes('>') &&
|
296
|
+
!value.includes('>') &&
|
297
|
+
!value.includes('</') &&
|
298
|
+
!value.includes('"') &&
|
299
|
+
!value.includes('src') &&
|
300
|
+
!value.includes('href') &&
|
301
|
+
!value.includes('=')
|
302
|
+
)
|
303
|
+
return acc + `<span class="deletion">${value}</span>`;
|
304
|
+
|
305
|
+
if (
|
306
|
+
part.added &&
|
307
|
+
!value.includes('<') &&
|
308
|
+
!value.includes('>') &&
|
309
|
+
!value.includes('>') &&
|
310
|
+
!value.includes('</') &&
|
311
|
+
!value.includes('"') &&
|
312
|
+
!value.includes('src') &&
|
313
|
+
!value.includes('href') &&
|
314
|
+
!value.includes('=')
|
315
|
+
)
|
316
|
+
return acc + `<span class="addition">${value}</span>`;
|
317
|
+
|
318
|
+
return acc + value;
|
319
|
+
}, '');
|
320
|
+
return combined;
|
321
|
+
}),
|
322
|
+
'',
|
323
|
+
),
|
324
|
+
}}
|
325
|
+
/>
|
326
|
+
</Grid.Column>
|
327
|
+
</Grid.Row>
|
328
|
+
)}
|
329
|
+
</Grid>
|
330
|
+
);
|
331
|
+
};
|
332
|
+
|
333
|
+
/**
|
334
|
+
* Property types.
|
335
|
+
* @property {Object} propTypes Property types.
|
336
|
+
* @static
|
337
|
+
*/
|
338
|
+
DiffField.propTypes = {
|
339
|
+
one: PropTypes.any.isRequired,
|
340
|
+
two: PropTypes.any.isRequired,
|
341
|
+
contentOne: PropTypes.any,
|
342
|
+
contentTwo: PropTypes.any,
|
343
|
+
view: PropTypes.string.isRequired,
|
344
|
+
schema: PropTypes.shape({
|
345
|
+
widget: PropTypes.string,
|
346
|
+
type: PropTypes.string,
|
347
|
+
title: PropTypes.string,
|
348
|
+
}).isRequired,
|
349
|
+
};
|
350
|
+
|
351
|
+
export default injectLazyLibs('diffLib')(DiffField);
|
@@ -0,0 +1,154 @@
|
|
1
|
+
/*
|
2
|
+
* UniversalLink
|
3
|
+
* @module components/UniversalLink
|
4
|
+
* Removed noreferrer from rel attribute
|
5
|
+
*/
|
6
|
+
|
7
|
+
import React from 'react';
|
8
|
+
import PropTypes from 'prop-types';
|
9
|
+
import { HashLink as Link } from 'react-router-hash-link';
|
10
|
+
import { useSelector } from 'react-redux';
|
11
|
+
import {
|
12
|
+
flattenToAppURL,
|
13
|
+
isInternalURL,
|
14
|
+
URLUtils,
|
15
|
+
} from '@plone/volto/helpers/Url/Url';
|
16
|
+
|
17
|
+
import config from '@plone/volto/registry';
|
18
|
+
|
19
|
+
const UniversalLink = ({
|
20
|
+
href,
|
21
|
+
item = null,
|
22
|
+
openLinkInNewTab,
|
23
|
+
download = false,
|
24
|
+
children,
|
25
|
+
className = null,
|
26
|
+
title = null,
|
27
|
+
...props
|
28
|
+
}) => {
|
29
|
+
const token = useSelector((state) => state.userSession?.token);
|
30
|
+
|
31
|
+
let url = href;
|
32
|
+
if (!href && item) {
|
33
|
+
if (!item['@id']) {
|
34
|
+
// eslint-disable-next-line no-console
|
35
|
+
console.error(
|
36
|
+
'Invalid item passed to UniversalLink',
|
37
|
+
item,
|
38
|
+
props,
|
39
|
+
children,
|
40
|
+
);
|
41
|
+
url = '#';
|
42
|
+
} else {
|
43
|
+
//case: generic item
|
44
|
+
url = flattenToAppURL(item['@id']);
|
45
|
+
|
46
|
+
//case: item like a Link
|
47
|
+
let remoteUrl = item.remoteUrl || item.getRemoteUrl;
|
48
|
+
if (!token && remoteUrl) {
|
49
|
+
url = remoteUrl;
|
50
|
+
}
|
51
|
+
|
52
|
+
//case: item of type 'File'
|
53
|
+
if (
|
54
|
+
!token &&
|
55
|
+
config.settings.downloadableObjects.includes(item['@type'])
|
56
|
+
) {
|
57
|
+
url = `${url}/@@download/file`;
|
58
|
+
}
|
59
|
+
|
60
|
+
if (
|
61
|
+
!token &&
|
62
|
+
config.settings.viewableInBrowserObjects.includes(item['@type'])
|
63
|
+
) {
|
64
|
+
url = `${url}/@@display-file/file`;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
const isExternal = !isInternalURL(url);
|
70
|
+
|
71
|
+
const isDownload = (!isExternal && url.includes('@@download')) || download;
|
72
|
+
const isDisplayFile =
|
73
|
+
(!isExternal && url.includes('@@display-file')) || false;
|
74
|
+
|
75
|
+
const checkedURL = URLUtils.checkAndNormalizeUrl(url);
|
76
|
+
|
77
|
+
url = checkedURL.url;
|
78
|
+
let tag = (
|
79
|
+
<Link
|
80
|
+
to={flattenToAppURL(url)}
|
81
|
+
target={openLinkInNewTab ?? false ? '_blank' : null}
|
82
|
+
title={title}
|
83
|
+
className={className}
|
84
|
+
smooth={config.settings.hashLinkSmoothScroll}
|
85
|
+
{...props}
|
86
|
+
>
|
87
|
+
{children}
|
88
|
+
</Link>
|
89
|
+
);
|
90
|
+
|
91
|
+
if (isExternal) {
|
92
|
+
tag = (
|
93
|
+
<a
|
94
|
+
href={url}
|
95
|
+
title={title}
|
96
|
+
target={
|
97
|
+
!checkedURL.isMail &&
|
98
|
+
!checkedURL.isTelephone &&
|
99
|
+
!(openLinkInNewTab === false)
|
100
|
+
? '_blank'
|
101
|
+
: null
|
102
|
+
}
|
103
|
+
rel="noopener"
|
104
|
+
className={className}
|
105
|
+
{...props}
|
106
|
+
>
|
107
|
+
{children}
|
108
|
+
</a>
|
109
|
+
);
|
110
|
+
} else if (isDownload) {
|
111
|
+
tag = (
|
112
|
+
<a
|
113
|
+
href={flattenToAppURL(url)}
|
114
|
+
download
|
115
|
+
title={title}
|
116
|
+
className={className}
|
117
|
+
{...props}
|
118
|
+
>
|
119
|
+
{children}
|
120
|
+
</a>
|
121
|
+
);
|
122
|
+
} else if (isDisplayFile) {
|
123
|
+
tag = (
|
124
|
+
<a
|
125
|
+
href={flattenToAppURL(url)}
|
126
|
+
title={title}
|
127
|
+
rel="noopener"
|
128
|
+
className={className}
|
129
|
+
{...props}
|
130
|
+
>
|
131
|
+
{children}
|
132
|
+
</a>
|
133
|
+
);
|
134
|
+
}
|
135
|
+
return tag;
|
136
|
+
};
|
137
|
+
|
138
|
+
UniversalLink.propTypes = {
|
139
|
+
href: PropTypes.string,
|
140
|
+
openLinkInNewTab: PropTypes.bool,
|
141
|
+
download: PropTypes.bool,
|
142
|
+
className: PropTypes.string,
|
143
|
+
title: PropTypes.string,
|
144
|
+
item: PropTypes.shape({
|
145
|
+
'@id': PropTypes.string.isRequired,
|
146
|
+
remoteUrl: PropTypes.string, //of plone @type 'Link'
|
147
|
+
}),
|
148
|
+
children: PropTypes.oneOfType([
|
149
|
+
PropTypes.arrayOf(PropTypes.node),
|
150
|
+
PropTypes.node,
|
151
|
+
]),
|
152
|
+
};
|
153
|
+
|
154
|
+
export default UniversalLink;
|
@@ -0,0 +1,27 @@
|
|
1
|
+
//this should be deleted when upgraded to a volto version that supports App Extras exceptions
|
2
|
+
import React from 'react';
|
3
|
+
import { matchPath } from 'react-router';
|
4
|
+
import config from '@plone/volto/registry';
|
5
|
+
|
6
|
+
const AppExtras = (props) => {
|
7
|
+
const { settings } = config;
|
8
|
+
const { appExtras = [] } = settings;
|
9
|
+
const { pathname } = props;
|
10
|
+
const active = appExtras
|
11
|
+
.map((reg) => {
|
12
|
+
const excluded = matchPath(pathname, reg.exclude);
|
13
|
+
if (excluded) return null;
|
14
|
+
const match = matchPath(pathname, reg.match);
|
15
|
+
return match ? { reg, match } : null;
|
16
|
+
})
|
17
|
+
.filter((reg) => reg);
|
18
|
+
|
19
|
+
return active.map(({ reg: { component, props: extraProps }, match }, i) => {
|
20
|
+
const Insert = component;
|
21
|
+
return (
|
22
|
+
<Insert key={`appextra-${i}`} match={match} {...props} {...extraProps} />
|
23
|
+
);
|
24
|
+
});
|
25
|
+
};
|
26
|
+
|
27
|
+
export default AppExtras;
|
@@ -116,7 +116,7 @@ const EEAHeader = ({ pathname, token, items, history, subsite }) => {
|
|
116
116
|
<a
|
117
117
|
href="https://europa.eu/european-union/contact/institutions-bodies_en"
|
118
118
|
target="_blank"
|
119
|
-
rel="
|
119
|
+
rel="noopener"
|
120
120
|
onKeyDown={(evt) => evt.stopPropagation()}
|
121
121
|
>
|
122
122
|
See all EU institutions and bodies
|
@@ -140,7 +140,7 @@ const EEAHeader = ({ pathname, token, items, history, subsite }) => {
|
|
140
140
|
href={item.href}
|
141
141
|
className="site"
|
142
142
|
target="_blank"
|
143
|
-
rel="
|
143
|
+
rel="noopener"
|
144
144
|
onKeyDown={(evt) => evt.stopPropagation()}
|
145
145
|
>
|
146
146
|
{item.title}
|
package/src/index.js
CHANGED
@@ -29,6 +29,9 @@ import installSlate from './slate';
|
|
29
29
|
import installReducers from './reducers';
|
30
30
|
import installMiddlewares from './middleware';
|
31
31
|
|
32
|
+
import { nanoid } from '@plone/volto-slate/utils';
|
33
|
+
import { v4 as uuid } from 'uuid';
|
34
|
+
|
32
35
|
import * as eea from './config';
|
33
36
|
import React from 'react';
|
34
37
|
|
@@ -85,11 +88,7 @@ function tabVariationCustomization(tabs_block_variations, config) {
|
|
85
88
|
innerSchema.properties.icon.description = (
|
86
89
|
<>
|
87
90
|
Ex. ri-home-line. See{' '}
|
88
|
-
<a
|
89
|
-
target="_blank"
|
90
|
-
rel="noopener noreferrer"
|
91
|
-
href="https://remixicon.com/"
|
92
|
-
>
|
91
|
+
<a target="_blank" rel="noopener" href="https://remixicon.com/">
|
93
92
|
Remix Icon set
|
94
93
|
</a>
|
95
94
|
</>
|
@@ -98,6 +97,7 @@ function tabVariationCustomization(tabs_block_variations, config) {
|
|
98
97
|
};
|
99
98
|
return schema;
|
100
99
|
};
|
100
|
+
|
101
101
|
const oldDefaultSchemaEnhancer = defaultVariation.schemaEnhancer;
|
102
102
|
defaultVariation.schemaEnhancer = (props) => {
|
103
103
|
const newSchema = oldDefaultSchemaEnhancer(props);
|
@@ -110,7 +110,6 @@ function tabVariationCustomization(tabs_block_variations, config) {
|
|
110
110
|
];
|
111
111
|
return newSchema;
|
112
112
|
};
|
113
|
-
|
114
113
|
const oldHorizontalSchemaEnhancer = horizontalVariation.schemaEnhancer;
|
115
114
|
horizontalVariation.schemaEnhancer = (props) => {
|
116
115
|
const newSchema = oldHorizontalSchemaEnhancer(props);
|
@@ -139,6 +138,40 @@ const applyConfig = (config) => {
|
|
139
138
|
if (config.settings?.serverConfig?.extractScripts) {
|
140
139
|
config.settings.serverConfig.extractScripts.errorPages = true;
|
141
140
|
}
|
141
|
+
// Set cloneData function for slate block, in order to change the uuids of fragments in the copy process
|
142
|
+
if (config.blocks?.blocksConfig?.slate) {
|
143
|
+
config.blocks.blocksConfig.slate.cloneData = (data) => {
|
144
|
+
const replaceAllUidsWithNewOnes = (value) => {
|
145
|
+
if (value?.children?.length > 0) {
|
146
|
+
const newChildren = value.children.map((childrenData) => {
|
147
|
+
if (childrenData?.data?.uid) {
|
148
|
+
return {
|
149
|
+
...childrenData,
|
150
|
+
data: { ...childrenData.data, uid: nanoid(5) },
|
151
|
+
};
|
152
|
+
}
|
153
|
+
return childrenData;
|
154
|
+
});
|
155
|
+
return {
|
156
|
+
...value,
|
157
|
+
children: newChildren,
|
158
|
+
};
|
159
|
+
}
|
160
|
+
return value;
|
161
|
+
};
|
162
|
+
return [
|
163
|
+
uuid(),
|
164
|
+
{
|
165
|
+
...data,
|
166
|
+
value: [
|
167
|
+
...(data?.value || []).map((value) =>
|
168
|
+
replaceAllUidsWithNewOnes(value),
|
169
|
+
),
|
170
|
+
],
|
171
|
+
},
|
172
|
+
];
|
173
|
+
};
|
174
|
+
}
|
142
175
|
|
143
176
|
// Disable tags on View
|
144
177
|
config.settings.showTags = false;
|
package/src/index.test.js
CHANGED
@@ -144,7 +144,7 @@ describe('applyConfig', () => {
|
|
144
144
|
{ match: '', component: 'MockedDraftBackground' },
|
145
145
|
{ match: '', component: 'MockedSubsiteClass' },
|
146
146
|
{ match: '', component: BaseTag },
|
147
|
-
{ match: '*', component: 'MockedRemoveSchema' },
|
147
|
+
{ match: '*', exclude: '/**/diff', component: 'MockedRemoveSchema' },
|
148
148
|
]);
|
149
149
|
expect(config.settings.available_colors).toEqual(eea.colors);
|
150
150
|
expect(config.settings.hasLanguageDropdown).toBe(false);
|
@@ -303,7 +303,7 @@ describe('applyConfig', () => {
|
|
303
303
|
{ match: '', component: 'MockedDraftBackground' },
|
304
304
|
{ match: '', component: 'MockedSubsiteClass' },
|
305
305
|
{ match: '', component: BaseTag },
|
306
|
-
{ match: '*', component: 'MockedRemoveSchema' },
|
306
|
+
{ match: '*', exclude: '/**/diff', component: 'MockedRemoveSchema' },
|
307
307
|
]);
|
308
308
|
expect(config.settings.available_colors).toEqual(eea.colors);
|
309
309
|
expect(config.settings.hasLanguageDropdown).toBe(false);
|