@plone/volto 17.0.0-alpha.0 → 17.0.0-alpha.2

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 (61) hide show
  1. package/.changelog.draft +21 -12
  2. package/.yarn/install-state.gz +0 -0
  3. package/CHANGELOG.md +118 -5
  4. package/README.md +4 -4
  5. package/cypress/support/commands.js +25 -0
  6. package/locales/ca/LC_MESSAGES/volto.po +5 -0
  7. package/locales/ca.json +1 -1
  8. package/locales/de/LC_MESSAGES/volto.po +5 -0
  9. package/locales/de.json +1 -1
  10. package/locales/en/LC_MESSAGES/volto.po +5 -0
  11. package/locales/en.json +1 -1
  12. package/locales/es/LC_MESSAGES/volto.po +5 -0
  13. package/locales/es.json +1 -1
  14. package/locales/eu/LC_MESSAGES/volto.po +5 -0
  15. package/locales/eu.json +1 -1
  16. package/locales/fi.json +1 -1
  17. package/locales/fr/LC_MESSAGES/volto.po +5 -0
  18. package/locales/fr.json +1 -1
  19. package/locales/it/LC_MESSAGES/volto.po +5 -0
  20. package/locales/it.json +1 -1
  21. package/locales/ja/LC_MESSAGES/volto.po +5 -0
  22. package/locales/ja.json +1 -1
  23. package/locales/nl/LC_MESSAGES/volto.po +5 -0
  24. package/locales/nl.json +1 -1
  25. package/locales/pt/LC_MESSAGES/volto.po +5 -0
  26. package/locales/pt.json +1 -1
  27. package/locales/pt_BR/LC_MESSAGES/volto.po +5 -0
  28. package/locales/pt_BR.json +1 -1
  29. package/locales/ro/LC_MESSAGES/volto.po +5 -0
  30. package/locales/ro.json +1 -1
  31. package/locales/volto.pot +5 -0
  32. package/locales/zh_CN/LC_MESSAGES/volto.po +5 -0
  33. package/locales/zh_CN.json +1 -1
  34. package/package-why.json +0 -1
  35. package/package.json +7 -7
  36. package/packages/volto-slate/package.json +1 -1
  37. package/packages/volto-slate/src/blocks/Table/TableBlockView.jsx +4 -4
  38. package/packages/volto-slate/src/blocks/Table/index.js +2 -0
  39. package/src/components/manage/BlockChooser/BlockChooserButton.jsx +63 -29
  40. package/src/components/manage/BlockChooser/BlockChooserSearch.jsx +0 -1
  41. package/src/components/manage/Blocks/Listing/Edit.jsx +0 -5
  42. package/src/components/manage/Blocks/Listing/ListingBody.jsx +77 -61
  43. package/src/components/manage/Blocks/Listing/View.jsx +0 -4
  44. package/src/components/manage/Blocks/ToC/Edit.jsx +1 -0
  45. package/src/components/manage/Contents/Contents.jsx +35 -8
  46. package/src/components/manage/Controlpanels/Controlpanels.jsx +1 -1
  47. package/src/components/manage/DragDropList/DragDropList.jsx +63 -42
  48. package/src/components/manage/Form/BlocksToolbar.jsx +5 -1
  49. package/src/components/manage/Form/Form.jsx +6 -2
  50. package/src/components/manage/History/History.jsx +35 -18
  51. package/src/components/theme/View/EventView.jsx +1 -1
  52. package/src/components/theme/View/NewsItemView.jsx +1 -1
  53. package/src/config/index.js +1 -0
  54. package/src/config/server.js +19 -0
  55. package/src/express-middleware/devproxy.js +4 -2
  56. package/src/express-middleware/static.js +32 -0
  57. package/src/server.jsx +1 -7
  58. package/src/start-server.js +4 -2
  59. package/theme/themes/pastanaga/extras/blocks.less +0 -9
  60. package/theme/themes/pastanaga/extras/contents.less +1 -0
  61. package/theme/themes/pastanaga/extras/main.less +80 -1
@@ -1,5 +1,6 @@
1
1
  import React, { createRef } from 'react';
2
2
  import { FormattedMessage, injectIntl } from 'react-intl';
3
+ import cx from 'classnames';
3
4
  import { Pagination, Dimmer, Loader } from 'semantic-ui-react';
4
5
  import { Icon } from '@plone/volto/components';
5
6
  import config from '@plone/volto/registry';
@@ -44,70 +45,85 @@ const ListingBody = withQuerystringResults((props) => {
44
45
  ? variation.noResultsComponent
45
46
  : config.blocks?.blocksConfig['listing'].noResultsComponent;
46
47
 
47
- return listingItems?.length > 0 ? (
48
- <div ref={listingRef}>
49
- <ListingBodyTemplate
50
- items={listingItems}
51
- isEditMode={isEditMode}
52
- {...data}
53
- {...variation}
54
- />
55
- {totalPages > 1 && (
56
- <div className="pagination-wrapper">
57
- <Pagination
58
- activePage={currentPage}
59
- totalPages={totalPages}
60
- onPageChange={(e, { activePage }) => {
61
- !isEditMode &&
62
- listingRef.current.scrollIntoView({ behavior: 'smooth' });
63
- onPaginationChange(e, { activePage });
64
- }}
65
- firstItem={null}
66
- lastItem={null}
67
- prevItem={{
68
- content: <Icon name={paginationLeftSVG} size="18px" />,
69
- icon: true,
70
- 'aria-disabled': !prevBatch,
71
- className: !prevBatch ? 'disabled' : null,
72
- }}
73
- nextItem={{
74
- content: <Icon name={paginationRightSVG} size="18px" />,
75
- icon: true,
76
- 'aria-disabled': !nextBatch,
77
- className: !nextBatch ? 'disabled' : null,
78
- }}
48
+ const HeadlineTag = data.headlineTag || 'h2';
49
+
50
+ return (
51
+ <>
52
+ {data.headline && (
53
+ <HeadlineTag
54
+ className={cx('headline', {
55
+ emptyListing: !listingItems?.length > 0,
56
+ })}
57
+ >
58
+ {data.headline}
59
+ </HeadlineTag>
60
+ )}
61
+ {listingItems?.length > 0 ? (
62
+ <div ref={listingRef}>
63
+ <ListingBodyTemplate
64
+ items={listingItems}
65
+ isEditMode={isEditMode}
66
+ {...data}
67
+ {...variation}
79
68
  />
69
+ {totalPages > 1 && (
70
+ <div className="pagination-wrapper">
71
+ <Pagination
72
+ activePage={currentPage}
73
+ totalPages={totalPages}
74
+ onPageChange={(e, { activePage }) => {
75
+ !isEditMode &&
76
+ listingRef.current.scrollIntoView({ behavior: 'smooth' });
77
+ onPaginationChange(e, { activePage });
78
+ }}
79
+ firstItem={null}
80
+ lastItem={null}
81
+ prevItem={{
82
+ content: <Icon name={paginationLeftSVG} size="18px" />,
83
+ icon: true,
84
+ 'aria-disabled': !prevBatch,
85
+ className: !prevBatch ? 'disabled' : null,
86
+ }}
87
+ nextItem={{
88
+ content: <Icon name={paginationRightSVG} size="18px" />,
89
+ icon: true,
90
+ 'aria-disabled': !nextBatch,
91
+ className: !nextBatch ? 'disabled' : null,
92
+ }}
93
+ />
94
+ </div>
95
+ )}
96
+ </div>
97
+ ) : isEditMode ? (
98
+ <div className="listing message" ref={listingRef}>
99
+ {isFolderContentsListing && (
100
+ <FormattedMessage
101
+ id="No items found in this container."
102
+ defaultMessage="No items found in this container."
103
+ />
104
+ )}
105
+ {hasLoaded && NoResults && (
106
+ <NoResults isEditMode={isEditMode} {...data} />
107
+ )}
108
+ <Dimmer active={!hasLoaded} inverted>
109
+ <Loader indeterminate size="small">
110
+ <FormattedMessage id="loading" defaultMessage="Loading" />
111
+ </Loader>
112
+ </Dimmer>
113
+ </div>
114
+ ) : (
115
+ <div className="emptyListing">
116
+ {hasLoaded && NoResults && (
117
+ <NoResults isEditMode={isEditMode} {...data} />
118
+ )}
119
+ <Dimmer active={!hasLoaded} inverted>
120
+ <Loader indeterminate size="small">
121
+ <FormattedMessage id="loading" defaultMessage="Loading" />
122
+ </Loader>
123
+ </Dimmer>
80
124
  </div>
81
125
  )}
82
- </div>
83
- ) : isEditMode ? (
84
- <div className="listing message" ref={listingRef}>
85
- {isFolderContentsListing && (
86
- <FormattedMessage
87
- id="No items found in this container."
88
- defaultMessage="No items found in this container."
89
- />
90
- )}
91
- {hasLoaded && NoResults && (
92
- <NoResults isEditMode={isEditMode} {...data} />
93
- )}
94
- <Dimmer active={!hasLoaded} inverted>
95
- <Loader indeterminate size="small">
96
- <FormattedMessage id="loading" defaultMessage="Loading" />
97
- </Loader>
98
- </Dimmer>
99
- </div>
100
- ) : (
101
- <div>
102
- {hasLoaded && NoResults && (
103
- <NoResults isEditMode={isEditMode} {...data} />
104
- )}
105
- <Dimmer active={!hasLoaded} inverted>
106
- <Loader indeterminate size="small">
107
- <FormattedMessage id="loading" defaultMessage="Loading" />
108
- </Loader>
109
- </Dimmer>
110
- </div>
126
+ </>
111
127
  );
112
128
  });
113
129
 
@@ -7,15 +7,11 @@ import { ListingBlockBody as ListingBody } from '@plone/volto/components';
7
7
 
8
8
  const View = (props) => {
9
9
  const { data, path, pathname, className } = props;
10
- const HeadlineTag = data.headlineTag || 'h2';
11
10
 
12
11
  return (
13
12
  <div
14
13
  className={cx('block listing', data.variation || 'default', className)}
15
14
  >
16
- {data.headline && (
17
- <HeadlineTag className="headline">{data.headline}</HeadlineTag>
18
- )}
19
15
  <ListingBody {...props} path={path ?? pathname} />
20
16
  </div>
21
17
  );
@@ -26,6 +26,7 @@ class Edit extends Component {
26
26
  }}
27
27
  onChangeBlock={this.props.onChangeBlock}
28
28
  formData={this.props.data}
29
+ block={this.props.block}
29
30
  />
30
31
  </SidebarPortal>
31
32
  </>
@@ -290,6 +290,10 @@ const messages = defineMessages({
290
290
  id: 'This Page is referenced by the following items:',
291
291
  defaultMessage: 'This Page is referenced by the following items:',
292
292
  },
293
+ deleteItemCountMessage: {
294
+ id: 'Total items to be deleted:',
295
+ defaultMessage: 'Total items to be deleted:',
296
+ },
293
297
  deleteItemMessage: {
294
298
  id: 'Items to be deleted:',
295
299
  defaultMessage: 'Items to be deleted:',
@@ -418,6 +422,8 @@ class Contents extends Component {
418
422
  this.paste = this.paste.bind(this);
419
423
  this.fetchContents = this.fetchContents.bind(this);
420
424
  this.orderTimeout = null;
425
+ this.deleteItemsToShowThreshold = 10;
426
+
421
427
  this.state = {
422
428
  selected: [],
423
429
  showDelete: false,
@@ -427,6 +433,7 @@ class Contents extends Component {
427
433
  showProperties: false,
428
434
  showWorkflow: false,
429
435
  itemsToDelete: [],
436
+ showAllItemsToDelete: true,
430
437
  items: this.props.items,
431
438
  filter: '',
432
439
  currentPage: 0,
@@ -456,7 +463,6 @@ class Contents extends Component {
456
463
  this.fetchContents();
457
464
  this.setState({ isClient: true });
458
465
  }
459
-
460
466
  async componentDidUpdate(_, prevState) {
461
467
  if (
462
468
  this.state.itemsToDelete !== prevState.itemsToDelete &&
@@ -468,6 +474,8 @@ class Contents extends Component {
468
474
  this.getFieldById(item, 'UID'),
469
475
  ),
470
476
  ),
477
+ showAllItemsToDelete:
478
+ this.state.itemsToDelete.length < this.deleteItemsToShowThreshold,
471
479
  });
472
480
  }
473
481
  }
@@ -1188,16 +1196,35 @@ class Contents extends Component {
1188
1196
  <div className="content">
1189
1197
  <h3>
1190
1198
  {this.props.intl.formatMessage(
1191
- messages.deleteItemMessage,
1192
- )}
1199
+ messages.deleteItemCountMessage,
1200
+ ) + ` ${this.state.itemsToDelete.length}`}
1193
1201
  </h3>
1194
1202
  <ul className="content">
1195
- {map(this.state.itemsToDelete, (item) => (
1196
- <li key={item}>
1197
- {this.getFieldById(item, 'title')}
1198
- </li>
1199
- ))}
1203
+ {map(
1204
+ this.state.showAllItemsToDelete
1205
+ ? this.state.itemsToDelete
1206
+ : this.state.itemsToDelete.slice(
1207
+ 0,
1208
+ this.deleteItemsToShowThreshold,
1209
+ ),
1210
+ (item) => (
1211
+ <li key={item}>
1212
+ {this.getFieldById(item, 'title')}
1213
+ </li>
1214
+ ),
1215
+ )}
1200
1216
  </ul>
1217
+ {!this.state.showAllItemsToDelete && (
1218
+ <Button
1219
+ onClick={() =>
1220
+ this.setState({
1221
+ showAllItemsToDelete: true,
1222
+ })
1223
+ }
1224
+ >
1225
+ Show all items
1226
+ </Button>
1227
+ )}
1201
1228
  {this.state.linkIntegrityBreakages.length > 0 ? (
1202
1229
  <div>
1203
1230
  <h3>
@@ -270,7 +270,7 @@ class Controlpanels extends Component {
270
270
  {group}
271
271
  </Segment>,
272
272
  <Segment key={`body-${group}`} attached>
273
- <Grid columns={6}>
273
+ <Grid doubling columns={6}>
274
274
  <Grid.Row>
275
275
  {map(filter(controlpanels, { group }), (controlpanel) => (
276
276
  <Grid.Column key={controlpanel.id}>
@@ -3,7 +3,7 @@ import { isEmpty } from 'lodash';
3
3
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
4
4
  import { v4 as uuid } from 'uuid';
5
5
 
6
- const getPlaceholder = (draggedDOM, sourceIndex, destinationIndex) => {
6
+ const getPlaceholder = (draggedDOM, sourceIndex, destinationIndex, uid) => {
7
7
  // Because of the margin rendering rules, there is no easy
8
8
  // way to calculate the offset of the placeholder.
9
9
  //
@@ -13,12 +13,16 @@ const getPlaceholder = (draggedDOM, sourceIndex, destinationIndex) => {
13
13
  //
14
14
  // To get a placeholder that looks good in all cases, we
15
15
  // fill up the space between the previous and the next element.
16
- const childrenArray = [...draggedDOM.parentNode.children];
16
+ const queryAttr = 'data-rbd-droppable-id';
17
+ const domQuery = `[${queryAttr}='${uid}']`;
18
+ const parentDOM = document.querySelector(domQuery);
19
+
20
+ const childrenArray = [...parentDOM.children];
17
21
  // Remove the source element
18
22
  childrenArray.splice(sourceIndex, 1);
19
23
  // Also remove the placeholder that the library always inserts at the end
20
24
  childrenArray.splice(-1, 1);
21
- const parentRect = draggedDOM.parentNode.getBoundingClientRect();
25
+ const parentRect = parentDOM.getBoundingClientRect();
22
26
  const prevNode = childrenArray[destinationIndex - 1];
23
27
  const nextNode = childrenArray[destinationIndex];
24
28
  let top, bottom;
@@ -40,9 +44,7 @@ const getPlaceholder = (draggedDOM, sourceIndex, destinationIndex) => {
40
44
  return {
41
45
  clientY: top,
42
46
  clientHeight: bottom - top,
43
- clientX: parseFloat(
44
- window.getComputedStyle(draggedDOM.parentNode).paddingLeft,
45
- ),
47
+ clientX: parseFloat(window.getComputedStyle(parentDOM).paddingLeft),
46
48
  clientWidth: draggedDOM.clientWidth,
47
49
  };
48
50
  };
@@ -63,17 +65,22 @@ const DragDropList = (props) => {
63
65
  // queueing timed action
64
66
  const timer = useRef(null);
65
67
 
66
- const onDragStart = React.useCallback((event) => {
67
- clearTimeout(timer.current);
68
- const queryAttr = 'data-rbd-draggable-id';
69
- const domQuery = `[${queryAttr}='${event.draggableId}']`;
70
- const draggedDOM = document.querySelector(domQuery);
71
- if (!draggedDOM) {
72
- return;
73
- }
74
- const sourceIndex = event.source.index;
75
- setPlaceholderProps(getPlaceholder(draggedDOM, sourceIndex, sourceIndex));
76
- }, []);
68
+ const onDragStart = React.useCallback(
69
+ (event) => {
70
+ clearTimeout(timer.current);
71
+ const queryAttr = 'data-rbd-draggable-id';
72
+ const domQuery = `[${queryAttr}='${event.draggableId}']`;
73
+ const draggedDOM = document.querySelector(domQuery);
74
+ if (!draggedDOM) {
75
+ return;
76
+ }
77
+ const sourceIndex = event.source.index;
78
+ setPlaceholderProps(
79
+ getPlaceholder(draggedDOM, sourceIndex, sourceIndex, uid),
80
+ );
81
+ },
82
+ [uid],
83
+ );
77
84
 
78
85
  const onDragEnd = React.useCallback(
79
86
  (result) => {
@@ -84,30 +91,33 @@ const DragDropList = (props) => {
84
91
  [onMoveItem],
85
92
  );
86
93
 
87
- const onDragUpdate = React.useCallback((update) => {
88
- clearTimeout(timer.current);
89
- setPlaceholderProps({});
90
- if (!update.destination) {
91
- return;
92
- }
93
- const draggableId = update.draggableId;
94
- const queryAttr = 'data-rbd-draggable-id';
95
- const domQuery = `[${queryAttr}='${draggableId}']`;
96
- const draggedDOM = document.querySelector(domQuery);
97
- if (!draggedDOM) {
98
- return;
99
- }
100
- const sourceIndex = update.source.index;
101
- const destinationIndex = update.destination.index;
102
- // Wait until the animations have finished, to make it look good.
103
- timer.current = setTimeout(
104
- () =>
105
- setPlaceholderProps(
106
- getPlaceholder(draggedDOM, sourceIndex, destinationIndex),
107
- ),
108
- 250,
109
- );
110
- }, []);
94
+ const onDragUpdate = React.useCallback(
95
+ (update) => {
96
+ clearTimeout(timer.current);
97
+ setPlaceholderProps({});
98
+ if (!update.destination) {
99
+ return;
100
+ }
101
+ const draggableId = update.draggableId;
102
+ const queryAttr = 'data-rbd-draggable-id';
103
+ const domQuery = `[${queryAttr}='${draggableId}']`;
104
+ const draggedDOM = document.querySelector(domQuery);
105
+ if (!draggedDOM) {
106
+ return;
107
+ }
108
+ const sourceIndex = update.source.index;
109
+ const destinationIndex = update.destination.index;
110
+ // Wait until the animations have finished, to make it look good.
111
+ timer.current = setTimeout(
112
+ () =>
113
+ setPlaceholderProps(
114
+ getPlaceholder(draggedDOM, sourceIndex, destinationIndex, uid),
115
+ ),
116
+ 250,
117
+ );
118
+ },
119
+ [uid],
120
+ );
111
121
 
112
122
  const AsDomComponent = as;
113
123
  return (
@@ -116,7 +126,18 @@ const DragDropList = (props) => {
116
126
  onDragUpdate={onDragUpdate}
117
127
  onDragEnd={onDragEnd}
118
128
  >
119
- <Droppable droppableId={uid}>
129
+ <Droppable
130
+ droppableId={uid}
131
+ renderClone={(provided, snapshot, rubric) => {
132
+ const index = rubric.source.index;
133
+ return children({
134
+ child: childList[index][1],
135
+ childId: childList[index][0],
136
+ index,
137
+ draginfo: provided,
138
+ });
139
+ }}
140
+ >
120
141
  {(provided, snapshot) => (
121
142
  <AsDomComponent
122
143
  ref={provided.innerRef}
@@ -177,7 +177,11 @@ export class BlocksToolbarComponent extends React.Component {
177
177
  ''
178
178
  )}
179
179
  {selectedBlock && (blocksClipboard?.cut || blocksClipboard?.copy) && (
180
- <Plug pluggable="main.toolbar.bottom" id="block-paste-btn">
180
+ <Plug
181
+ pluggable="main.toolbar.bottom"
182
+ id="block-paste-btn"
183
+ dependencies={[selectedBlock]}
184
+ >
181
185
  <button
182
186
  aria-label={intl.formatMessage(messages.pasteBlocks)}
183
187
  onClick={this.pasteBlocks}
@@ -258,7 +258,11 @@ class Form extends Component {
258
258
  * Tab selection is done only by setting activeIndex in state
259
259
  */
260
260
  onTabChange(e, { activeIndex }) {
261
- this.setState({ activeIndex });
261
+ const defaultFocus = this.props.schema.fieldsets[activeIndex].fields[0];
262
+ this.setState({
263
+ activeIndex,
264
+ ...(defaultFocus ? { inFocus: { [defaultFocus]: true } } : {}),
265
+ });
262
266
  }
263
267
 
264
268
  /**
@@ -686,7 +690,7 @@ class Form extends Component {
686
690
  id={field}
687
691
  formData={this.state.formData}
688
692
  fieldSet={item.title.toLowerCase()}
689
- focus={index === 0}
693
+ focus={this.state.inFocus[field]}
690
694
  value={this.state.formData?.[field]}
691
695
  required={schema.required.indexOf(field) !== -1}
692
696
  onChange={this.onChangeField}
@@ -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
  ) : (
@@ -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
@@ -2,6 +2,7 @@ import imagesMiddleware from '@plone/volto/express-middleware/images';
2
2
  import filesMiddleware from '@plone/volto/express-middleware/files';
3
3
  import robotstxtMiddleware from '@plone/volto/express-middleware/robotstxt';
4
4
  import sitemapMiddleware from '@plone/volto/express-middleware/sitemap';
5
+ import staticsMiddleware from '@plone/volto/express-middleware/static';
5
6
  import devProxyMiddleware from '@plone/volto/express-middleware/devproxy';
6
7
 
7
8
  const settings = {
@@ -11,10 +12,28 @@ const settings = {
11
12
  imagesMiddleware(),
12
13
  robotstxtMiddleware(),
13
14
  sitemapMiddleware(),
15
+ staticsMiddleware(),
14
16
  ],
15
17
  criticalCssPath: 'public/critical.css',
16
18
  readCriticalCss: null, // so it will be defaultReadCriticalCss
17
19
  extractScripts: { errorPages: false },
20
+ staticFiles: [
21
+ {
22
+ id: 'root_static',
23
+ match: /^\/static\/.*/,
24
+ headers: {
25
+ // stable resources never change. 31536000 seconds == 365 days
26
+ 'Cache-Control': 'public, max-age=31536000',
27
+ },
28
+ },
29
+ {
30
+ id: 'all',
31
+ match: /.*/,
32
+ headers: {
33
+ 'Cache-Control': 'public, max-age=60',
34
+ },
35
+ },
36
+ ],
18
37
  };
19
38
 
20
39
  export default settings;
@@ -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
  }),
@@ -0,0 +1,32 @@
1
+ import express from 'express';
2
+ import path from 'path';
3
+ import config from '@plone/volto/registry';
4
+
5
+ const staticMiddleware = express.static(
6
+ process.env.BUILD_DIR
7
+ ? path.join(process.env.BUILD_DIR, 'public')
8
+ : process.env.RAZZLE_PUBLIC_DIR,
9
+ {
10
+ setHeaders: function (res, path) {
11
+ const pathLib = require('path');
12
+ const base = pathLib.resolve(process.env.RAZZLE_PUBLIC_DIR);
13
+ const relpath = path.substr(base.length);
14
+ config.settings.serverConfig.staticFiles.some((elem) => {
15
+ if (relpath.match(elem.match)) {
16
+ for (const name in elem.headers) {
17
+ res.setHeader(name, elem.headers[name] || 'undefined');
18
+ }
19
+ return true;
20
+ }
21
+ return false;
22
+ });
23
+ },
24
+ },
25
+ );
26
+
27
+ export default function () {
28
+ const middleware = express.Router();
29
+ middleware.all('*', staticMiddleware);
30
+ middleware.id = 'staticResourcesProcessor';
31
+ return middleware;
32
+ }