@plone/volto 16.13.0 → 16.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/.changelog.draft +14 -11
  2. package/.yarn/install-state.gz +0 -0
  3. package/CHANGELOG.md +32 -0
  4. package/locales/ca/LC_MESSAGES/volto.po +30 -0
  5. package/locales/ca.json +1 -1
  6. package/locales/de/LC_MESSAGES/volto.po +30 -0
  7. package/locales/de.json +1 -1
  8. package/locales/en/LC_MESSAGES/volto.po +30 -0
  9. package/locales/en.json +1 -1
  10. package/locales/es/LC_MESSAGES/volto.po +30 -0
  11. package/locales/es.json +1 -1
  12. package/locales/eu/LC_MESSAGES/volto.po +30 -0
  13. package/locales/eu.json +1 -1
  14. package/locales/fi/LC_MESSAGES/volto.po +30 -0
  15. package/locales/fi.json +1 -1
  16. package/locales/fr/LC_MESSAGES/volto.po +30 -0
  17. package/locales/fr.json +1 -1
  18. package/locales/it/LC_MESSAGES/volto.po +30 -0
  19. package/locales/it.json +1 -1
  20. package/locales/ja/LC_MESSAGES/volto.po +30 -0
  21. package/locales/ja.json +1 -1
  22. package/locales/nl/LC_MESSAGES/volto.po +30 -0
  23. package/locales/nl.json +1 -1
  24. package/locales/pt/LC_MESSAGES/volto.po +30 -0
  25. package/locales/pt.json +1 -1
  26. package/locales/pt_BR/LC_MESSAGES/volto.po +30 -0
  27. package/locales/pt_BR.json +1 -1
  28. package/locales/ro/LC_MESSAGES/volto.po +30 -0
  29. package/locales/ro.json +1 -1
  30. package/locales/volto.pot +31 -1
  31. package/locales/zh_CN/LC_MESSAGES/volto.po +30 -0
  32. package/locales/zh_CN.json +1 -1
  33. package/package.json +1 -1
  34. package/packages/volto-slate/package.json +1 -1
  35. package/packages/volto-slate/src/blocks/Table/TableBlockView.jsx +4 -4
  36. package/src/components/manage/Blocks/Teaser/Body.jsx +30 -0
  37. package/src/components/manage/Blocks/Teaser/Data.jsx +71 -0
  38. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +89 -0
  39. package/src/components/manage/Blocks/Teaser/Edit.jsx +25 -0
  40. package/src/components/manage/Blocks/Teaser/View.jsx +9 -0
  41. package/src/components/manage/Blocks/Teaser/adapter.js +23 -0
  42. package/src/components/manage/Blocks/Teaser/schema.js +103 -0
  43. package/src/components/manage/Blocks/Teaser/utils.js +44 -0
  44. package/src/components/manage/Blocks/Teaser/utils.test.jsx +229 -0
  45. package/src/components/manage/History/History.jsx +35 -18
  46. package/src/components/theme/View/EventView.jsx +1 -1
  47. package/src/components/theme/View/NewsItemView.jsx +1 -1
  48. package/src/config/Blocks.jsx +28 -0
  49. package/src/config/index.js +1 -0
  50. package/src/express-middleware/devproxy.js +4 -2
  51. package/src/server.jsx +1 -0
  52. package/src/start-server.js +4 -2
  53. package/theme/themes/pastanaga/extras/blocks.less +2 -0
  54. package/theme/themes/pastanaga/extras/teaser.less +166 -0
@@ -0,0 +1,103 @@
1
+ import { defineMessages } from 'react-intl';
2
+ import { addStyling } from '@plone/volto/helpers/Extensions/withBlockSchemaEnhancer';
3
+
4
+ const messages = defineMessages({
5
+ Target: {
6
+ id: 'Target',
7
+ defaultMessage: 'Target',
8
+ },
9
+ imageOverride: {
10
+ id: 'Image override',
11
+ defaultMessage: 'Image override',
12
+ },
13
+ openLinkInNewTab: {
14
+ id: 'Open in a new tab',
15
+ defaultMessage: 'Open in a new tab',
16
+ },
17
+ title: {
18
+ id: 'Title',
19
+ defaultMessage: 'Title',
20
+ },
21
+ description: {
22
+ id: 'Description',
23
+ defaultMessage: 'Description',
24
+ },
25
+ head_title: {
26
+ id: 'head_title',
27
+ defaultMessage: 'Head title',
28
+ },
29
+ teaser: {
30
+ id: 'Teaser',
31
+ defaultMessage: 'Teaser',
32
+ },
33
+ align: {
34
+ id: 'Alignment',
35
+ defaultMessage: 'Alignment',
36
+ },
37
+ });
38
+
39
+ export const TeaserSchema = ({ intl }) => {
40
+ const schema = {
41
+ title: intl.formatMessage(messages.teaser),
42
+ fieldsets: [
43
+ {
44
+ id: 'default',
45
+ title: 'Default',
46
+ fields: ['href', 'title', 'head_title', 'description', 'preview_image'],
47
+ },
48
+ ],
49
+
50
+ properties: {
51
+ href: {
52
+ title: intl.formatMessage(messages.Target),
53
+ widget: 'object_browser',
54
+ mode: 'link',
55
+ selectedItemAttrs: [
56
+ 'Title',
57
+ 'head_title',
58
+ 'Description',
59
+ 'hasPreviewImage',
60
+ 'image_field',
61
+ 'image_scales',
62
+ '@type',
63
+ ],
64
+ allowExternals: true,
65
+ },
66
+ title: {
67
+ title: intl.formatMessage(messages.title),
68
+ },
69
+ head_title: {
70
+ title: intl.formatMessage(messages.head_title),
71
+ },
72
+ description: {
73
+ title: intl.formatMessage(messages.description),
74
+ widget: 'textarea',
75
+ },
76
+ preview_image: {
77
+ title: intl.formatMessage(messages.imageOverride),
78
+ widget: 'object_browser',
79
+ mode: 'image',
80
+ allowExternals: true,
81
+ selectedItemAttrs: ['image_field', 'image_scales'],
82
+ },
83
+ openLinkInNewTab: {
84
+ title: intl.formatMessage(messages.openLinkInNewTab),
85
+ type: 'boolean',
86
+ },
87
+ },
88
+ required: [],
89
+ };
90
+
91
+ addStyling({ schema, intl });
92
+
93
+ schema.properties.styles.schema.properties.align = {
94
+ widget: 'align',
95
+ title: intl.formatMessage(messages.align),
96
+ actions: ['left', 'right', 'center'],
97
+ default: 'left',
98
+ };
99
+
100
+ schema.properties.styles.schema.fieldsets[0].fields = ['align'];
101
+
102
+ return schema;
103
+ };
@@ -0,0 +1,44 @@
1
+ import { isInternalURL } from '@plone/volto/helpers';
2
+ import config from '@plone/volto/registry';
3
+
4
+ export function getTeaserImageURL({ href, image, align }) {
5
+ // The default scale used in teasers is the 'teaser' scale
6
+ // except if it's customized otherwise in the teaser block settings
7
+ // or if the teaser is center (top)
8
+ const imageScale =
9
+ align === 'center'
10
+ ? 'great'
11
+ : config.blocks.blocksConfig['teaser'].imageScale || 'teaser';
12
+
13
+ if (image) {
14
+ // If the image is overriden locally in the teaser block
15
+ if (isInternalURL(image['@id'])) {
16
+ // If it's internal check if image_scales catalog info is present
17
+ if (image?.image_scales?.[image?.image_field]) {
18
+ return `${image['@id']}/${
19
+ image.image_scales[image.image_field]?.[0].scales[imageScale]
20
+ ?.download || image.image_scales[image.image_field]?.[0].download
21
+ }`;
22
+ } else {
23
+ // If not, fallback to content scale URL shortcut
24
+ return `${image['@id']}/@@images/${image.image_field}/${imageScale}`;
25
+ }
26
+ } else {
27
+ // If it's external, return the plain URL
28
+ return image['@id'];
29
+ }
30
+ } else {
31
+ // If the image is not overriden
32
+ if (href?.image_scales?.[href?.image_field]) {
33
+ return `${href['@id']}/${
34
+ href.image_scales[href.image_field]?.[0].scales[imageScale]?.download ||
35
+ href.image_scales[href.image_field]?.[0].download
36
+ }`;
37
+ } else {
38
+ // If not, fallback to content scale URL shortcut
39
+ return `${href['@id']}/@@images/${
40
+ href.image_field || 'preview_image'
41
+ }/${imageScale}`;
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,229 @@
1
+ import { getTeaserImageURL } from './utils';
2
+ import config from '@plone/volto/registry';
3
+
4
+ beforeAll(() => {
5
+ config.blocks.blocksConfig.teaser = {};
6
+ config.blocks.blocksConfig.teaser.imageScale = 'teaser';
7
+ });
8
+
9
+ describe('getTeaserImageURL tests', () => {
10
+ it('getTeaserImageURL internal URL - no overriden', () => {
11
+ const align = 'left';
12
+ const href = {
13
+ '@id': '/document',
14
+ image_field: 'preview_image',
15
+ image_scales: {
16
+ preview_image: [
17
+ {
18
+ download: '@@images/default_original_URL',
19
+ scales: {
20
+ teaser: {
21
+ download: '@@images/teaser_scale_URL',
22
+ },
23
+ },
24
+ },
25
+ ],
26
+ },
27
+ };
28
+ const image = undefined;
29
+ expect(getTeaserImageURL({ href, image, align })).toBe(
30
+ '/document/@@images/teaser_scale_URL',
31
+ );
32
+ });
33
+
34
+ it('getTeaserImageURL internal URL - no overriden - no scale', () => {
35
+ const align = 'left';
36
+ const href = {
37
+ '@id': '/document',
38
+ image_field: 'preview_image',
39
+ image_scales: {
40
+ preview_image: [
41
+ {
42
+ download: '@@images/default_original_URL',
43
+ scales: {},
44
+ },
45
+ ],
46
+ },
47
+ };
48
+ const image = undefined;
49
+ expect(getTeaserImageURL({ href, image, align })).toBe(
50
+ '/document/@@images/default_original_URL',
51
+ );
52
+ });
53
+
54
+ it('getTeaserImageURL internal URL - no overriden - no catalog image info', () => {
55
+ const align = 'left';
56
+ const href = {
57
+ '@id': '/document',
58
+ image_field: 'image',
59
+ image_scales: {
60
+ preview_image: [
61
+ {
62
+ download: '@@images/default_original_URL',
63
+ scales: {},
64
+ },
65
+ ],
66
+ },
67
+ };
68
+ const image = undefined;
69
+ expect(getTeaserImageURL({ href, image, align })).toBe(
70
+ '/document/@@images/image/teaser',
71
+ );
72
+ });
73
+
74
+ it('getTeaserImageURL internal URL - no overriden - center', () => {
75
+ const align = 'center';
76
+ const href = {
77
+ '@id': '/document',
78
+ image_field: 'preview_image',
79
+ image_scales: {
80
+ preview_image: [
81
+ {
82
+ download: '@@images/default_original_URL',
83
+ scales: {
84
+ great: {
85
+ download: '@@images/great_scale_URL',
86
+ },
87
+ teaser: {
88
+ download: '@@images/teaser_scale_URL',
89
+ },
90
+ },
91
+ },
92
+ ],
93
+ },
94
+ };
95
+ const image = undefined;
96
+ expect(getTeaserImageURL({ href, image, align })).toBe(
97
+ '/document/@@images/great_scale_URL',
98
+ );
99
+ });
100
+
101
+ it('getTeaserImageURL internal URL - no overriden - center - no great scale', () => {
102
+ const align = 'center';
103
+ const href = {
104
+ '@id': '/document',
105
+ image_field: 'preview_image',
106
+ image_scales: {
107
+ preview_image: [
108
+ {
109
+ download: '@@images/default_original_URL',
110
+ scales: {
111
+ teaser: {
112
+ download: '@@images/teaser_scale_URL',
113
+ },
114
+ },
115
+ },
116
+ ],
117
+ },
118
+ };
119
+ const image = undefined;
120
+ expect(getTeaserImageURL({ href, image, align })).toBe(
121
+ '/document/@@images/default_original_URL',
122
+ );
123
+ });
124
+
125
+ it('getTeaserImageURL internal URL - image overriden', () => {
126
+ const align = 'left';
127
+ const href = {
128
+ '@id': '/document',
129
+ image_field: 'preview_image',
130
+ image_scales: {
131
+ preview_image: [
132
+ {
133
+ download: '@@images/default_original_URL',
134
+ scales: {
135
+ teaser: {
136
+ download: '@@images/teaser_scale_URL',
137
+ },
138
+ },
139
+ },
140
+ ],
141
+ },
142
+ };
143
+ const image = {
144
+ '@id': '/document/image',
145
+ image_field: 'image',
146
+ image_scales: {
147
+ image: [
148
+ {
149
+ download: '@@images/overriden_image_default_original_URL',
150
+ scales: {
151
+ teaser: {
152
+ download: '@@images/overriden_image_teaser_scale_URL',
153
+ },
154
+ },
155
+ },
156
+ ],
157
+ },
158
+ };
159
+ expect(getTeaserImageURL({ href, image, align })).toBe(
160
+ '/document/image/@@images/overriden_image_teaser_scale_URL',
161
+ );
162
+ });
163
+
164
+ it('getTeaserImageURL internal URL - image overriden - center - no great scale', () => {
165
+ const align = 'center';
166
+ const href = {
167
+ '@id': '/document',
168
+ image_field: 'preview_image',
169
+ image_scales: {
170
+ preview_image: [
171
+ {
172
+ download: '@@images/default_original_URL',
173
+ scales: {
174
+ teaser: {
175
+ download: '@@images/teaser_scale_URL',
176
+ },
177
+ },
178
+ },
179
+ ],
180
+ },
181
+ };
182
+ const image = {
183
+ '@id': '/document/image',
184
+ image_field: 'image',
185
+ image_scales: {
186
+ image: [
187
+ {
188
+ download: '@@images/overriden_image_default_original_URL',
189
+ scales: {
190
+ teaser: {
191
+ download: '@@images/overriden_image_teaser_scale_URL',
192
+ },
193
+ },
194
+ },
195
+ ],
196
+ },
197
+ };
198
+ expect(getTeaserImageURL({ href, image, align })).toBe(
199
+ '/document/image/@@images/overriden_image_default_original_URL',
200
+ );
201
+ });
202
+
203
+ it('getTeaserImageURL internal URL - image overriden - external', () => {
204
+ const align = 'left';
205
+ const href = {
206
+ '@id': '/document',
207
+ image_field: 'preview_image',
208
+ image_scales: {
209
+ preview_image: [
210
+ {
211
+ download: '@@images/default_original_URL',
212
+ scales: {
213
+ teaser: {
214
+ download: '@@images/teaser_scale_URL',
215
+ },
216
+ },
217
+ },
218
+ ],
219
+ },
220
+ };
221
+ const image = {
222
+ '@id': 'https://plone.org/document/image.png',
223
+ };
224
+
225
+ expect(getTeaserImageURL({ href, image, align })).toBe(
226
+ 'https://plone.org/document/image.png',
227
+ );
228
+ });
229
+ });
@@ -116,22 +116,37 @@ class History extends Component {
116
116
  this.props.revertHistory(getBaseUrl(this.props.pathname), value);
117
117
  }
118
118
 
119
- /**
120
- * Render method.
121
- * @method render
122
- * @returns {string} Markup for the component.
123
- */
124
- render() {
119
+ processHistoryEntries = () => {
120
+ // Getting the history entries from the props
121
+ // No clue why the reverse(concat()) is necessary
125
122
  const entries = reverse(concat(this.props.entries));
126
123
  let title = entries.length > 0 ? entries[0].state_title : '';
127
124
  for (let x = 1; x < entries.length; x += 1) {
128
125
  entries[x].prev_state_title = title;
129
126
  title = entries[x].state_title || title;
130
127
  }
128
+ // We reverse them again
131
129
  reverse(entries);
130
+
131
+ // We identify the latest 'versioning' entry and mark it
132
+ const current_version = find(entries, (item) => item.type === 'versioning');
133
+ if (current_version) {
134
+ current_version.is_current = true;
135
+ }
136
+ return entries;
137
+ };
138
+
139
+ /**
140
+ * Render method.
141
+ * @method render
142
+ * @returns {string} Markup for the component.
143
+ */
144
+ render() {
132
145
  const historyAction = find(this.props.objectActions, {
133
146
  id: 'history',
134
147
  });
148
+ const entries = this.processHistoryEntries();
149
+
135
150
  return !historyAction ? (
136
151
  <>
137
152
  {this.props.token ? (
@@ -266,18 +281,20 @@ class History extends Component {
266
281
  />
267
282
  </Link>
268
283
  )}
269
- {'version' in entry && (
270
- <Dropdown.Item
271
- value={entry.version}
272
- onClick={this.onRevert}
273
- >
274
- <Icon name="undo" />{' '}
275
- <FormattedMessage
276
- id="Revert to this revision"
277
- defaultMessage="Revert to this revision"
278
- />
279
- </Dropdown.Item>
280
- )}
284
+ {'version' in entry &&
285
+ entry.may_revert &&
286
+ !entry.is_current && (
287
+ <Dropdown.Item
288
+ value={entry.version}
289
+ onClick={this.onRevert}
290
+ >
291
+ <Icon name="undo" />{' '}
292
+ <FormattedMessage
293
+ id="Revert to this revision"
294
+ defaultMessage="Revert to this revision"
295
+ />
296
+ </Dropdown.Item>
297
+ )}
281
298
  </Dropdown.Menu>
282
299
  </Dropdown>
283
300
  )}
@@ -43,7 +43,7 @@ const EventView = (props) => {
43
43
  const { content } = props;
44
44
 
45
45
  return (
46
- <div id="page-document" className="ui container viewwrapper event-view">
46
+ <div id="page-document" className="ui container view-wrapper event-view">
47
47
  <Grid>
48
48
  <Grid.Column width={7} className="mobile hidden">
49
49
  {hasBlocksData(content) ? (
@@ -21,7 +21,7 @@ import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
21
21
  */
22
22
  const NewsItemView = ({ content }) =>
23
23
  hasBlocksData(content) ? (
24
- <div id="page-document" className="ui container viewwrapper event-view">
24
+ <div id="page-document" className="ui container view-wrapper newsitem-view">
25
25
  <RenderBlocks content={content} />
26
26
  </div>
27
27
  ) : (
@@ -42,6 +42,7 @@ import tableSVG from '@plone/volto/icons/table.svg';
42
42
  import listingBlockSVG from '@plone/volto/icons/content-listing.svg';
43
43
  import tocSVG from '@plone/volto/icons/list-bullet.svg';
44
44
  import searchSVG from '@plone/volto/icons/zoom.svg';
45
+ import imagesSVG from '@plone/volto/icons/images.svg';
45
46
 
46
47
  import ImageGalleryListingBlockTemplate from '@plone/volto/components/manage/Blocks/Listing/ImageGallery';
47
48
  import BlockSettingsSchema from '@plone/volto/components/manage/Blocks/Block/Schema';
@@ -73,6 +74,12 @@ import SearchBlockSchema from '@plone/volto/components/manage/Blocks/Search/sche
73
74
 
74
75
  import ToCVariations from '@plone/volto/components/manage/Blocks/ToC/variations';
75
76
 
77
+ import TeaserViewBlock from '@plone/volto/components/manage/Blocks/Teaser/View';
78
+ import TeaserEditBlock from '@plone/volto/components/manage/Blocks/Teaser/Edit';
79
+ import TeaserBlockDefaultBody from '@plone/volto/components/manage/Blocks/Teaser/DefaultBody';
80
+ import { TeaserSchema } from '@plone/volto/components/manage/Blocks/Teaser/schema';
81
+ import { TeaserBlockDataAdapter } from '@plone/volto/components/manage/Blocks/Teaser/adapter';
82
+
76
83
  defineMessages({
77
84
  title: {
78
85
  id: 'title',
@@ -451,6 +458,27 @@ const blocksConfig = {
451
458
  },
452
459
  },
453
460
  },
461
+ teaser: {
462
+ id: 'teaser',
463
+ title: 'Teaser',
464
+ icon: imagesSVG,
465
+ group: 'common',
466
+ view: TeaserViewBlock,
467
+ edit: TeaserEditBlock,
468
+ restricted: false,
469
+ mostUsed: true,
470
+ sidebarTab: 1,
471
+ blockSchema: TeaserSchema,
472
+ dataAdapter: TeaserBlockDataAdapter,
473
+ variations: [
474
+ {
475
+ id: 'default',
476
+ isDefault: true,
477
+ title: 'Default',
478
+ template: TeaserBlockDefaultBody,
479
+ },
480
+ ],
481
+ },
454
482
  };
455
483
 
456
484
  const requiredBlocks = ['title'];
@@ -89,6 +89,7 @@ let config = {
89
89
  // https://6.docs.plone.org/volto/deploying/seamless-mode.html
90
90
  devProxyToApiPath:
91
91
  process.env.RAZZLE_DEV_PROXY_API_PATH ||
92
+ process.env.RAZZLE_INTERNAL_API_PATH ||
92
93
  process.env.RAZZLE_API_PATH ||
93
94
  'http://localhost:8080/Plone', // Set it to '' for disabling the proxy
94
95
  // proxyRewriteTarget Set it for set a custom target for the proxy or overide the internal VHM rewrite
@@ -75,12 +75,14 @@ export default function () {
75
75
  const { apiPathURL, instancePath } = getEnv();
76
76
  const target =
77
77
  config.settings.proxyRewriteTarget ||
78
- `/VirtualHostBase/http/${apiPathURL.hostname}:${apiPathURL.port}${instancePath}/++api++/VirtualHostRoot`;
78
+ `/VirtualHostBase/${apiPathURL.protocol.slice(0, -1)}/${
79
+ apiPathURL.hostname
80
+ }:${apiPathURL.port}${instancePath}/++api++/VirtualHostRoot`;
79
81
 
80
82
  return `${target}${path.replace('/++api++', '')}`;
81
83
  },
82
84
  logLevel: process.env.DEBUG_HPM ? 'debug' : 'silent',
83
- ...(config.settings?.proxyRewriteTarget?.startsWith('https') && {
85
+ ...(process.env.RAZZLE_DEV_PROXY_INSECURE && {
84
86
  changeOrigin: true,
85
87
  secure: false,
86
88
  }),
package/src/server.jsx CHANGED
@@ -329,6 +329,7 @@ export const defaultReadCriticalCss = () => {
329
329
  // Exposed for the console bootstrap info messages
330
330
  server.apiPath = config.settings.apiPath;
331
331
  server.devProxyToApiPath = config.settings.devProxyToApiPath;
332
+ server.proxyRewriteTarget = config.settings.proxyRewriteTarget;
332
333
  server.publicURL = config.settings.publicURL;
333
334
 
334
335
  export default server;
@@ -19,9 +19,11 @@ export default () => {
19
19
  } else {
20
20
  console.log(`API server (API_PATH) is set to: ${app.apiPath}`);
21
21
  }
22
- if (__DEVELOPMENT__ && app.devProxyToApiPath)
22
+ if (app.devProxyToApiPath)
23
23
  console.log(
24
- `Using internal proxy: ${app.publicURL} -> ${app.devProxyToApiPath}`,
24
+ `Proxying API requests from ${app.publicURL}/++api++ to ${
25
+ app.devProxyToApiPath
26
+ }${app.proxyRewriteTarget || ''}`,
25
27
  );
26
28
  console.log(`🎭 Volto started at ${bind_address}:${port} 🚀`);
27
29
 
@@ -1176,3 +1176,5 @@ body.has-toolbar.has-sidebar-collapsed .ui.wrapper > .ui.inner.block.full {
1176
1176
  }
1177
1177
  }
1178
1178
  }
1179
+
1180
+ @import 'teaser';