@plone/volto 18.0.0-alpha.2 → 18.0.0-alpha.3
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.md +15 -0
- package/apps/plone/node_modules/.bin/acorn +17 -0
- package/apps/plone/node_modules/.bin/addon +17 -0
- package/apps/plone/node_modules/.bin/autoprefixer +17 -0
- package/apps/plone/node_modules/.bin/browserslist +17 -0
- package/apps/plone/node_modules/.bin/build-storybook +17 -0
- package/apps/plone/node_modules/.bin/changelogupdater +17 -0
- package/apps/plone/node_modules/.bin/eslint +17 -0
- package/apps/plone/node_modules/.bin/eslint-config-prettier +17 -0
- package/apps/plone/node_modules/.bin/i18n +17 -0
- package/apps/plone/node_modules/.bin/jest +17 -0
- package/apps/plone/node_modules/.bin/lessc +17 -0
- package/apps/plone/node_modules/.bin/missdev +17 -0
- package/apps/plone/node_modules/.bin/prettier +17 -0
- package/apps/plone/node_modules/.bin/razzle +17 -0
- package/apps/plone/node_modules/.bin/server-test +17 -0
- package/apps/plone/node_modules/.bin/start-server-and-test +17 -0
- package/apps/plone/node_modules/.bin/start-storybook +17 -0
- package/apps/plone/node_modules/.bin/start-test +17 -0
- package/apps/plone/node_modules/.bin/storybook-server +17 -0
- package/apps/plone/node_modules/.bin/stylelint +17 -0
- package/apps/plone/node_modules/.bin/tlds +17 -0
- package/apps/plone/node_modules/.bin/ts-jest +17 -0
- package/apps/plone/node_modules/.bin/tsc +17 -0
- package/apps/plone/node_modules/.bin/tsserver +17 -0
- package/apps/plone/node_modules/.bin/uuid +17 -0
- package/apps/plone/node_modules/.bin/webpack +17 -0
- package/apps/plone/node_modules/.bin/webpack-dev-server +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/acorn +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/addon +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/browserslist +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/changelogupdater +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/eslint +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/eslint-config-prettier +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/i18n +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/prettier +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/release-it +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/stylelint +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/tsc +17 -0
- package/apps/plone/src/addons/volto-volto-project/node_modules/.bin/tsserver +17 -0
- package/locales/ca/LC_MESSAGES/volto.po +1 -0
- package/locales/de/LC_MESSAGES/volto.po +1 -0
- package/locales/en/LC_MESSAGES/volto.po +1 -0
- package/locales/es/LC_MESSAGES/volto.po +1 -0
- package/locales/eu/LC_MESSAGES/volto.po +1 -0
- package/locales/fi/LC_MESSAGES/volto.po +1 -0
- package/locales/fr/LC_MESSAGES/volto.po +1 -0
- package/locales/it/LC_MESSAGES/volto.po +1 -0
- package/locales/ja/LC_MESSAGES/volto.po +1 -0
- package/locales/nl/LC_MESSAGES/volto.po +1 -0
- package/locales/pt/LC_MESSAGES/volto.po +1 -0
- package/locales/pt_BR/LC_MESSAGES/volto.po +1 -0
- package/locales/ro/LC_MESSAGES/volto.po +1 -0
- package/locales/volto.pot +2 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +1 -0
- package/package.json +2 -2
- package/packages/registry/node_modules/.bin/browserslist +17 -0
- package/packages/registry/node_modules/.bin/parcel +17 -0
- package/packages/registry/node_modules/.bin/release-it +17 -0
- package/packages/registry/node_modules/.bin/tsc +17 -0
- package/packages/registry/node_modules/.bin/tsserver +17 -0
- package/packages/registry/node_modules/.bin/vitest +17 -0
- package/packages/types/node_modules/.bin/browserslist +17 -0
- package/packages/types/node_modules/.bin/parcel +17 -0
- package/packages/types/node_modules/.bin/release-it +17 -0
- package/packages/types/node_modules/.bin/tsc +17 -0
- package/packages/types/node_modules/.bin/tsserver +17 -0
- package/packages/volto-slate/package.json +1 -1
- package/src/components/manage/Blocks/Video/Body.jsx +52 -22
- package/src/components/manage/Blocks/Video/Body.test.jsx +167 -0
- package/src/components/theme/Breadcrumbs/Breadcrumbs.jsx +19 -2
- package/src/components/theme/ContentMetadataTags/ContentMetadataTags.jsx +46 -39
- package/types/components/manage/Blocks/Video/Body.d.ts +5 -0
- package/types/components/manage/Blocks/Video/Body.test.d.ts +1 -0
|
@@ -5,39 +5,68 @@ import { Embed, Message } from 'semantic-ui-react';
|
|
|
5
5
|
import cx from 'classnames';
|
|
6
6
|
import { isInternalURL, flattenToAppURL } from '@plone/volto/helpers';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
? isInternalURL(data.preview_image)
|
|
11
|
-
? `${flattenToAppURL(data.preview_image)}/@@images/image`
|
|
12
|
-
: data.preview_image
|
|
13
|
-
: null;
|
|
14
|
-
|
|
8
|
+
//Extracting videoID, listID and thumbnailURL from the video URL
|
|
9
|
+
const getVideoIDAndPlaceholder = (url) => {
|
|
15
10
|
let videoID = null;
|
|
16
11
|
let listID = null;
|
|
12
|
+
let thumbnailURL = null;
|
|
17
13
|
|
|
18
|
-
if (
|
|
19
|
-
if (
|
|
20
|
-
if (
|
|
21
|
-
const matches =
|
|
14
|
+
if (url) {
|
|
15
|
+
if (url.match('youtu')) {
|
|
16
|
+
if (url.match('list')) {
|
|
17
|
+
const matches = url.match(/^.*\?list=(.*)|^.*&list=(.*)$/);
|
|
22
18
|
listID = matches[1] || matches[2];
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
|
|
20
|
+
let thumbnailID = null;
|
|
21
|
+
if (url.match(/\?v=(.*)&list/)) {
|
|
22
|
+
thumbnailID = url.match(/^.*\?v=(.*)&list(.*)/)[1];
|
|
23
|
+
}
|
|
24
|
+
if (url.match(/\?v=(.*)\?list/)) {
|
|
25
|
+
thumbnailID = url.match(/^.*\?v=(.*)\?list(.*)/)[1];
|
|
26
|
+
}
|
|
27
|
+
thumbnailURL =
|
|
28
|
+
'https://img.youtube.com/vi/' + thumbnailID + '/sddefault.jpg';
|
|
29
|
+
} else if (url.match('live')) {
|
|
30
|
+
videoID = url.match(/^.*\/live\/(.*)/)[1];
|
|
31
|
+
} else if (url.match(/\.be\//)) {
|
|
32
|
+
videoID = url.match(/^.*\.be\/(.*)/)[1];
|
|
33
|
+
} else if (url.match(/\?v=/)) {
|
|
34
|
+
videoID = url.match(/^.*\?v=(.*)$/)[1];
|
|
27
35
|
}
|
|
28
36
|
|
|
29
|
-
if (
|
|
37
|
+
if (videoID) {
|
|
38
|
+
let thumbnailID = videoID;
|
|
39
|
+
if (videoID.match(/\?si=/)) {
|
|
40
|
+
thumbnailID = videoID.match(/(.*)\?si=(.*)/)[1];
|
|
41
|
+
}
|
|
30
42
|
//load video preview image from youtube
|
|
31
|
-
|
|
32
|
-
'https://img.youtube.com/vi/' +
|
|
43
|
+
thumbnailURL =
|
|
44
|
+
'https://img.youtube.com/vi/' + thumbnailID + '/sddefault.jpg';
|
|
33
45
|
}
|
|
34
|
-
} else if (
|
|
35
|
-
videoID =
|
|
36
|
-
if (
|
|
37
|
-
|
|
46
|
+
} else if (url.match('vimeo')) {
|
|
47
|
+
videoID = url.match(/^.*\.com\/(.*)/)[1];
|
|
48
|
+
if (videoID) {
|
|
49
|
+
let thumbnailID = videoID;
|
|
50
|
+
if (videoID.match(/\?si=/)) {
|
|
51
|
+
thumbnailID = videoID.match(/(.*)\?si=(.*)/)[1];
|
|
52
|
+
}
|
|
53
|
+
thumbnailURL = 'https://vumbnail.com/' + thumbnailID + '.jpg';
|
|
38
54
|
}
|
|
39
55
|
}
|
|
40
56
|
}
|
|
57
|
+
return { videoID, listID, thumbnailURL };
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const Body = ({ data, isEditMode }) => {
|
|
61
|
+
let placeholder = data.preview_image
|
|
62
|
+
? isInternalURL(data.preview_image)
|
|
63
|
+
? `${flattenToAppURL(data.preview_image)}/@@images/image`
|
|
64
|
+
: data.preview_image
|
|
65
|
+
: null;
|
|
66
|
+
|
|
67
|
+
const { videoID, listID, thumbnailURL } = getVideoIDAndPlaceholder(data.url);
|
|
68
|
+
|
|
69
|
+
placeholder = !placeholder ? thumbnailURL : placeholder;
|
|
41
70
|
|
|
42
71
|
const ref = React.createRef();
|
|
43
72
|
const onKeyDown = (e) => {
|
|
@@ -130,3 +159,4 @@ Body.propTypes = {
|
|
|
130
159
|
};
|
|
131
160
|
|
|
132
161
|
export default Body;
|
|
162
|
+
export { getVideoIDAndPlaceholder };
|
|
@@ -0,0 +1,167 @@
|
|
|
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 Body from './Body';
|
|
6
|
+
import { getVideoIDAndPlaceholder } from './Body';
|
|
7
|
+
import config from '@plone/volto/registry';
|
|
8
|
+
|
|
9
|
+
config.blocks.blocksConfig = {
|
|
10
|
+
video: {
|
|
11
|
+
id: 'video',
|
|
12
|
+
title: 'Video',
|
|
13
|
+
group: 'media',
|
|
14
|
+
extensions: {},
|
|
15
|
+
variations: [],
|
|
16
|
+
restricted: false,
|
|
17
|
+
mostUsed: true,
|
|
18
|
+
sidebarTab: 1,
|
|
19
|
+
security: {
|
|
20
|
+
addPermission: [],
|
|
21
|
+
view: [],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const mockStore = configureStore();
|
|
27
|
+
|
|
28
|
+
test('renders a youtube video component with "list" in its url', () => {
|
|
29
|
+
const url =
|
|
30
|
+
'https://www.youtube.com/watch?v=KwRSRRyuk-Q&list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&index=1';
|
|
31
|
+
const videoDetails = getVideoIDAndPlaceholder(url);
|
|
32
|
+
expect(videoDetails).toEqual({
|
|
33
|
+
videoID: null,
|
|
34
|
+
listID: 'PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&index=1',
|
|
35
|
+
thumbnailURL: 'https://img.youtube.com/vi/KwRSRRyuk-Q/sddefault.jpg',
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('extracts video details from a youtube video with "/live/" in its url', () => {
|
|
40
|
+
const url = 'https://www.youtube.com/live/ISdHvS6Ck3k?si=COeVakmC1lI6jQy3';
|
|
41
|
+
const videoDetails = getVideoIDAndPlaceholder(url);
|
|
42
|
+
expect(videoDetails).toEqual({
|
|
43
|
+
videoID: 'ISdHvS6Ck3k?si=COeVakmC1lI6jQy3',
|
|
44
|
+
listID: null,
|
|
45
|
+
thumbnailURL: 'https://img.youtube.com/vi/ISdHvS6Ck3k/sddefault.jpg',
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('extracts video details from a youtube video with ".be/" in its url', () => {
|
|
50
|
+
const url = 'https://youtu.be/P9j-xYdWT28?si=zZ2putStJbPBLCdt';
|
|
51
|
+
const videoDetails = getVideoIDAndPlaceholder(url);
|
|
52
|
+
expect(videoDetails).toEqual({
|
|
53
|
+
videoID: 'P9j-xYdWT28?si=zZ2putStJbPBLCdt',
|
|
54
|
+
listID: null,
|
|
55
|
+
thumbnailURL: 'https://img.youtube.com/vi/P9j-xYdWT28/sddefault.jpg',
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('extracts video details from a youtube video with "?v=" in its url', () => {
|
|
60
|
+
const url = 'https://www.youtube.com/watch?v=KUd6e105u_I';
|
|
61
|
+
const videoDetails = getVideoIDAndPlaceholder(url);
|
|
62
|
+
expect(videoDetails).toEqual({
|
|
63
|
+
videoID: 'KUd6e105u_I',
|
|
64
|
+
listID: null,
|
|
65
|
+
thumbnailURL: 'https://img.youtube.com/vi/KUd6e105u_I/sddefault.jpg',
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('extracts video details from a vimeo video url', () => {
|
|
70
|
+
const url = 'https://vimeo.com/639449679';
|
|
71
|
+
const videoDetails = getVideoIDAndPlaceholder(url);
|
|
72
|
+
expect(videoDetails).toEqual({
|
|
73
|
+
videoID: '639449679',
|
|
74
|
+
listID: null,
|
|
75
|
+
thumbnailURL: 'https://vumbnail.com/639449679.jpg',
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('renders a youtube video body component', () => {
|
|
80
|
+
const store = mockStore({
|
|
81
|
+
intl: {
|
|
82
|
+
locale: 'en',
|
|
83
|
+
messages: {},
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const component = renderer.create(
|
|
88
|
+
<Provider store={store}>
|
|
89
|
+
<Body
|
|
90
|
+
data={{
|
|
91
|
+
'@type': 'video',
|
|
92
|
+
url: 'https://www.youtube.com/watch?v=KwRSRRyuk-Q&list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&index=1',
|
|
93
|
+
}}
|
|
94
|
+
/>
|
|
95
|
+
</Provider>,
|
|
96
|
+
);
|
|
97
|
+
const json = component.toJSON();
|
|
98
|
+
expect(json).toMatchSnapshot();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('renders a youtube video body component in edit mode', () => {
|
|
102
|
+
const store = mockStore({
|
|
103
|
+
intl: {
|
|
104
|
+
locale: 'en',
|
|
105
|
+
messages: {},
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const component = renderer.create(
|
|
110
|
+
<Provider store={store}>
|
|
111
|
+
<Body
|
|
112
|
+
data={{
|
|
113
|
+
'@type': 'video',
|
|
114
|
+
url: 'https://www.youtube.com/watch?v=KwRSRRyuk-Q&list=PLGN9BI-OAQkQmEqf6O8jeyoFY1b2hD1uL&index=1',
|
|
115
|
+
}}
|
|
116
|
+
isEditMode={true}
|
|
117
|
+
/>
|
|
118
|
+
</Provider>,
|
|
119
|
+
);
|
|
120
|
+
const json = component.toJSON();
|
|
121
|
+
expect(json).toMatchSnapshot();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('renders invalid video body component with invalid URL', () => {
|
|
125
|
+
const store = mockStore({
|
|
126
|
+
intl: {
|
|
127
|
+
locale: 'en',
|
|
128
|
+
messages: {},
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const component = renderer.create(
|
|
133
|
+
<Provider store={store}>
|
|
134
|
+
<Body
|
|
135
|
+
data={{
|
|
136
|
+
'@type': 'video',
|
|
137
|
+
url: 'https://www.google.com',
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
140
|
+
</Provider>,
|
|
141
|
+
);
|
|
142
|
+
const json = component.toJSON();
|
|
143
|
+
expect(json).toMatchSnapshot();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('renders a error message for invalid video URL in edit mode', () => {
|
|
147
|
+
const store = mockStore({
|
|
148
|
+
intl: {
|
|
149
|
+
locale: 'en',
|
|
150
|
+
messages: {},
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const component = renderer.create(
|
|
155
|
+
<Provider store={store}>
|
|
156
|
+
<Body
|
|
157
|
+
data={{
|
|
158
|
+
'@type': 'video',
|
|
159
|
+
url: 'https://www.google.com',
|
|
160
|
+
}}
|
|
161
|
+
isEditMode={true}
|
|
162
|
+
/>
|
|
163
|
+
</Provider>,
|
|
164
|
+
);
|
|
165
|
+
const json = component.toJSON();
|
|
166
|
+
expect(json).toMatchSnapshot();
|
|
167
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import { Link } from 'react-router-dom';
|
|
3
|
+
import { Link, useLocation } from 'react-router-dom';
|
|
4
4
|
import { Breadcrumb, Container, Segment } from 'semantic-ui-react';
|
|
5
5
|
import { defineMessages, useIntl } from 'react-intl';
|
|
6
6
|
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
|
@@ -19,13 +19,30 @@ const messages = defineMessages({
|
|
|
19
19
|
id: 'Breadcrumbs',
|
|
20
20
|
defaultMessage: 'Breadcrumbs',
|
|
21
21
|
},
|
|
22
|
+
controlpanel: {
|
|
23
|
+
id: 'Site Setup',
|
|
24
|
+
defaultMessage: 'Site Setup',
|
|
25
|
+
},
|
|
22
26
|
});
|
|
23
27
|
|
|
24
28
|
const BreadcrumbsComponent = ({ pathname }) => {
|
|
25
29
|
const intl = useIntl();
|
|
26
30
|
const dispatch = useDispatch();
|
|
31
|
+
const { pathname: realPath } = useLocation();
|
|
32
|
+
const controlpanelItems = [
|
|
33
|
+
{
|
|
34
|
+
url: '/controlpanel',
|
|
35
|
+
title: intl.formatMessage(messages.controlpanel),
|
|
36
|
+
},
|
|
37
|
+
];
|
|
27
38
|
|
|
28
|
-
const items = useSelector(
|
|
39
|
+
const items = useSelector(
|
|
40
|
+
(state) =>
|
|
41
|
+
realPath.startsWith('/controlpanel')
|
|
42
|
+
? controlpanelItems
|
|
43
|
+
: state.breadcrumbs.items,
|
|
44
|
+
shallowEqual,
|
|
45
|
+
);
|
|
29
46
|
const root = useSelector((state) => state.breadcrumbs.root);
|
|
30
47
|
|
|
31
48
|
useEffect(() => {
|
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
toPublicURL,
|
|
4
|
+
Helmet,
|
|
5
|
+
hasApiExpander,
|
|
6
|
+
getBaseUrl,
|
|
7
|
+
} from '@plone/volto/helpers';
|
|
8
|
+
import { getNavroot } from '@plone/volto/actions';
|
|
2
9
|
import config from '@plone/volto/registry';
|
|
10
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
3
11
|
|
|
4
12
|
const ContentMetadataTags = (props) => {
|
|
5
13
|
const {
|
|
@@ -13,23 +21,21 @@ const ContentMetadataTags = (props) => {
|
|
|
13
21
|
description,
|
|
14
22
|
} = props.content;
|
|
15
23
|
|
|
24
|
+
const dispatch = useDispatch();
|
|
25
|
+
const pathname = useSelector((state) => state.router.location.pathname);
|
|
26
|
+
const navroot = useSelector((state) => state.navroot?.data?.navroot);
|
|
27
|
+
const site = useSelector((state) => state.site?.data);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (pathname && !hasApiExpander('navroot', getBaseUrl(pathname))) {
|
|
31
|
+
dispatch(getNavroot(getBaseUrl(pathname)));
|
|
32
|
+
}
|
|
33
|
+
}, [dispatch, pathname]);
|
|
34
|
+
|
|
16
35
|
const getContentImageInfo = () => {
|
|
17
36
|
const { contentMetadataTagsImageField } = config.settings;
|
|
18
|
-
const
|
|
19
|
-
const preview_image = props.content.preview_image;
|
|
20
|
-
const preview_image_link = props.content.preview_image_link;
|
|
37
|
+
const image = props.content[contentMetadataTagsImageField];
|
|
21
38
|
const { opengraph_image } = props.content;
|
|
22
|
-
let image = undefined;
|
|
23
|
-
|
|
24
|
-
if (opengraph_image !== undefined && opengraph_image) {
|
|
25
|
-
image = opengraph_image;
|
|
26
|
-
} else if (preview_image_link !== undefined && preview_image_link) {
|
|
27
|
-
image = preview_image_link[contentMetadataTagsImageField];
|
|
28
|
-
} else if (preview_image !== undefined && preview_image) {
|
|
29
|
-
image = preview_image;
|
|
30
|
-
} else if (image_field !== undefined && image_field) {
|
|
31
|
-
image = image_field;
|
|
32
|
-
}
|
|
33
39
|
|
|
34
40
|
const contentImageInfo = {
|
|
35
41
|
contentHasImage: false,
|
|
@@ -37,7 +43,10 @@ const ContentMetadataTags = (props) => {
|
|
|
37
43
|
height: null,
|
|
38
44
|
width: null,
|
|
39
45
|
};
|
|
40
|
-
contentImageInfo.contentHasImage =
|
|
46
|
+
contentImageInfo.contentHasImage =
|
|
47
|
+
opengraph_image?.scales?.large?.download ||
|
|
48
|
+
image?.scales?.large?.download ||
|
|
49
|
+
false;
|
|
41
50
|
|
|
42
51
|
if (contentImageInfo.contentHasImage && opengraph_image?.scales?.large) {
|
|
43
52
|
contentImageInfo.url = opengraph_image.scales.large.download;
|
|
@@ -54,16 +63,35 @@ const ContentMetadataTags = (props) => {
|
|
|
54
63
|
|
|
55
64
|
const contentImageInfo = getContentImageInfo();
|
|
56
65
|
|
|
66
|
+
const getTitle = () => {
|
|
67
|
+
const includeSiteTitle =
|
|
68
|
+
config?.settings?.siteTitleFormat?.includeSiteTitle || false;
|
|
69
|
+
const titleAndSiteTitleSeparator =
|
|
70
|
+
config?.settings?.titleAndSiteTitleSeparator || '-';
|
|
71
|
+
const navRootTitle = navroot?.title;
|
|
72
|
+
const siteRootTitle = site?.['plone.site_title'];
|
|
73
|
+
const titlePart = navRootTitle || siteRootTitle;
|
|
74
|
+
|
|
75
|
+
if (includeSiteTitle && titlePart && titlePart !== title) {
|
|
76
|
+
return seo_title || `${title} ${titleAndSiteTitleSeparator} ${titlePart}`;
|
|
77
|
+
} else {
|
|
78
|
+
return seo_title || title;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
57
82
|
return (
|
|
58
83
|
<>
|
|
59
84
|
<Helmet>
|
|
60
|
-
<title>{(
|
|
85
|
+
<title>{getTitle()?.replace(/\u00AD/g, '')}</title>
|
|
86
|
+
<link
|
|
87
|
+
rel="canonical"
|
|
88
|
+
href={seo_canonical_url || toPublicURL(props.content['@id'])}
|
|
89
|
+
/>
|
|
61
90
|
<meta name="description" content={seo_description || description} />
|
|
62
91
|
<meta
|
|
63
92
|
property="og:title"
|
|
64
93
|
content={opengraph_title || seo_title || title}
|
|
65
94
|
/>
|
|
66
|
-
<meta property="og:type" content={'website'} />
|
|
67
95
|
<meta
|
|
68
96
|
property="og:url"
|
|
69
97
|
content={seo_canonical_url || toPublicURL(props.content['@id'])}
|
|
@@ -75,12 +103,6 @@ const ContentMetadataTags = (props) => {
|
|
|
75
103
|
content={toPublicURL(contentImageInfo.url)}
|
|
76
104
|
/>
|
|
77
105
|
)}
|
|
78
|
-
{contentImageInfo.contentHasImage && (
|
|
79
|
-
<meta
|
|
80
|
-
property="twitter:image"
|
|
81
|
-
content={toPublicURL(contentImageInfo.url)}
|
|
82
|
-
/>
|
|
83
|
-
)}
|
|
84
106
|
{contentImageInfo.contentHasImage && (
|
|
85
107
|
<meta property="og:image:width" content={contentImageInfo.width} />
|
|
86
108
|
)}
|
|
@@ -94,21 +116,6 @@ const ContentMetadataTags = (props) => {
|
|
|
94
116
|
/>
|
|
95
117
|
)}
|
|
96
118
|
<meta name="twitter:card" content="summary_large_image" />
|
|
97
|
-
<meta
|
|
98
|
-
property="twitter:url"
|
|
99
|
-
content={seo_canonical_url || toPublicURL(props.content['@id'])}
|
|
100
|
-
/>
|
|
101
|
-
{/* TODO: Improve SEO backend metadata providers by adding the twitter handler */}
|
|
102
|
-
{/* <meta property="twitter:site" content={'@my_twitter_handler'} /> */}
|
|
103
|
-
<meta
|
|
104
|
-
property="twitter:title"
|
|
105
|
-
content={opengraph_title || seo_title || title}
|
|
106
|
-
/>
|
|
107
|
-
<meta
|
|
108
|
-
property="twitter:description"
|
|
109
|
-
content={seo_description || description}
|
|
110
|
-
/>
|
|
111
|
-
<meta property="twitter:domain" content={config.settings.publicURL} />
|
|
112
119
|
</Helmet>
|
|
113
120
|
</>
|
|
114
121
|
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|