@plone/volto 17.0.0-alpha.20 → 17.0.0-alpha.22

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 (108) hide show
  1. package/.gitignore~ +71 -0
  2. package/.yarn/install-state.gz +0 -0
  3. package/CHANGELOG.md +49 -0
  4. package/cypress/support/commands.js +2 -1
  5. package/cypress/support/e2e.js +1 -2
  6. package/locales/ca/LC_MESSAGES/volto.po +24 -5
  7. package/locales/ca.json +1 -1
  8. package/locales/de/LC_MESSAGES/volto.po +37 -18
  9. package/locales/de.json +1 -1
  10. package/locales/en/LC_MESSAGES/volto.po +25 -6
  11. package/locales/en.json +1 -1
  12. package/locales/es/LC_MESSAGES/volto.po +25 -6
  13. package/locales/es.json +1 -1
  14. package/locales/eu/LC_MESSAGES/volto.po +24 -5
  15. package/locales/eu.json +1 -1
  16. package/locales/fi/LC_MESSAGES/volto.po +24 -5
  17. package/locales/fi.json +1 -1
  18. package/locales/fr/LC_MESSAGES/volto.po +24 -5
  19. package/locales/fr.json +1 -1
  20. package/locales/it/LC_MESSAGES/volto.po +250 -231
  21. package/locales/it.json +1 -1
  22. package/locales/ja/LC_MESSAGES/volto.po +24 -5
  23. package/locales/ja.json +1 -1
  24. package/locales/nl/LC_MESSAGES/volto.po +24 -5
  25. package/locales/nl.json +1 -1
  26. package/locales/pt/LC_MESSAGES/volto.po +24 -5
  27. package/locales/pt.json +1 -1
  28. package/locales/pt_BR/LC_MESSAGES/volto.po +25 -6
  29. package/locales/pt_BR.json +1 -1
  30. package/locales/ro/LC_MESSAGES/volto.po +24 -5
  31. package/locales/ro.json +1 -1
  32. package/locales/volto.pot +25 -6
  33. package/locales/zh_CN/LC_MESSAGES/volto.po +24 -5
  34. package/locales/zh_CN.json +1 -1
  35. package/news/4547.breaking~ +1 -0
  36. package/package.json +6 -6
  37. package/packages/volto-slate/package.json +1 -1
  38. package/src/actions/relations/rebuild.js +7 -7
  39. package/src/components/index.js +1 -0
  40. package/src/components/manage/Actions/Actions.jsx +133 -243
  41. package/src/components/manage/Blocks/Container/Edit.jsx +4 -1
  42. package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +1 -0
  43. package/src/components/manage/Blocks/Grid/Edit.jsx +13 -1
  44. package/src/components/manage/Blocks/Image/Edit.jsx +40 -5
  45. package/src/components/manage/Blocks/Image/Edit.test.jsx +2 -0
  46. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +64 -15
  47. package/src/components/manage/Blocks/Image/View.jsx +26 -5
  48. package/src/components/manage/Blocks/Image/View.test.jsx +20 -0
  49. package/src/components/manage/Blocks/Image/schema.js +1 -9
  50. package/src/components/manage/Blocks/Image/utils.js +14 -0
  51. package/src/components/manage/Blocks/LeadImage/Edit.jsx +32 -10
  52. package/src/components/manage/Blocks/LeadImage/Edit.test.jsx +11 -1
  53. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.jsx +28 -9
  54. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx +8 -2
  55. package/src/components/manage/Blocks/LeadImage/View.jsx +50 -38
  56. package/src/components/manage/Blocks/LeadImage/View.test.jsx +11 -1
  57. package/src/components/manage/Blocks/Listing/SummaryTemplate.jsx +1 -1
  58. package/src/components/manage/Blocks/Maps/Edit.jsx +135 -209
  59. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +3 -2
  60. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +13 -23
  61. package/src/components/manage/Contents/Contents.jsx +8 -6
  62. package/src/components/manage/Contents/ContentsPropertiesModal.jsx +1 -13
  63. package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +2 -2
  64. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +30 -7
  65. package/src/components/manage/Controlpanels/Relations/Relations.jsx +2 -2
  66. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +53 -59
  67. package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +2 -2
  68. package/src/components/manage/Delete/Delete.jsx +96 -171
  69. package/src/components/manage/Sidebar/AlignBlock.jsx +1 -1
  70. package/src/components/manage/Widgets/SelectUtils.js +1 -1
  71. package/src/components/manage/Workflow/Workflow.jsx +75 -184
  72. package/src/components/theme/Image/Image.jsx +96 -0
  73. package/src/components/theme/Image/Image.test.jsx +125 -0
  74. package/src/components/theme/Logo/Logo.jsx +2 -0
  75. package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +95 -170
  76. package/src/components/theme/PreviewImage/PreviewImage.jsx +25 -14
  77. package/src/components/theme/PreviewImage/PreviewImage.test.js +39 -16
  78. package/src/components/theme/View/AlbumView.jsx +11 -15
  79. package/src/components/theme/View/EventView.jsx +30 -23
  80. package/src/components/theme/View/ImageView.jsx +5 -2
  81. package/src/components/theme/View/ImageView.test.jsx +4 -0
  82. package/src/components/theme/View/ListingView.jsx +5 -3
  83. package/src/components/theme/View/NewsItemView.jsx +7 -13
  84. package/src/components/theme/View/SummaryView.jsx +4 -3
  85. package/src/config/Blocks.jsx +2 -0
  86. package/src/config/Components.jsx +3 -1
  87. package/src/config/index.js~ +223 -0
  88. package/src/express-middleware/files.js +8 -6
  89. package/src/express-middleware/images.js +7 -1
  90. package/src/helpers/MessageLabels/MessageLabels.js +6 -0
  91. package/src/helpers/Url/Url.js +22 -1
  92. package/src/helpers/Url/Url.test.js +41 -0
  93. package/src/reducers/relations/relations.js +1 -1
  94. package/test-setup-config.js +9 -1
  95. package/theme/themes/pastanaga/extras/blocks.less +3 -1
  96. package/theme/themes/pastanaga/extras/main.less +5 -0
  97. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +0 -90
  98. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +0 -6
  99. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +0 -6
  100. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +0 -6
  101. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +0 -10
  102. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +0 -10
  103. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +0 -30
  104. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +0 -10
  105. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +0 -6
  106. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +0 -6
  107. package/src/components/manage/Blocks/Teaser/utils.js +0 -44
  108. package/src/components/manage/Blocks/Teaser/utils.test.jsx +0 -229
@@ -5,12 +5,8 @@
5
5
 
6
6
  import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
- import { Container as SemanticContainer, Image } from 'semantic-ui-react';
9
- import {
10
- hasBlocksData,
11
- flattenToAppURL,
12
- flattenHTMLToAppURL,
13
- } from '@plone/volto/helpers';
8
+ import { Container as SemanticContainer } from 'semantic-ui-react';
9
+ import { hasBlocksData, flattenHTMLToAppURL } from '@plone/volto/helpers';
14
10
  import RenderBlocks from '@plone/volto/components/theme/View/RenderBlocks';
15
11
  import config from '@plone/volto/registry';
16
12
 
@@ -21,6 +17,7 @@ import config from '@plone/volto/registry';
21
17
  * @returns {string} Markup of the component.
22
18
  */
23
19
  const NewsItemView = ({ content }) => {
20
+ const Image = config.getComponent({ name: 'Image' }).component;
24
21
  const Container =
25
22
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
26
23
 
@@ -41,15 +38,12 @@ const NewsItemView = ({ content }) => {
41
38
  )}
42
39
  {content.image && (
43
40
  <Image
44
- className="documentImage"
41
+ className="documentImage ui right floated image"
45
42
  alt={content.title}
46
43
  title={content.title}
47
- src={
48
- content.image['content-type'] === 'image/svg+xml'
49
- ? flattenToAppURL(content.image.download)
50
- : flattenToAppURL(content.image.scales.mini.download)
51
- }
52
- floated="right"
44
+ item={content}
45
+ imageField="image"
46
+ responsive={true}
53
47
  />
54
48
  )}
55
49
  {content.text && (
@@ -8,7 +8,6 @@ import PropTypes from 'prop-types';
8
8
  import { UniversalLink } from '@plone/volto/components';
9
9
  import { Container as SemanticContainer } from 'semantic-ui-react';
10
10
  import { FormattedMessage } from 'react-intl';
11
- import PreviewImage from '../PreviewImage/PreviewImage';
12
11
  import config from '@plone/volto/registry';
13
12
 
14
13
  /**
@@ -20,6 +19,7 @@ import config from '@plone/volto/registry';
20
19
  const SummaryView = ({ content }) => {
21
20
  const Container =
22
21
  config.getComponent({ name: 'Container' }).component || SemanticContainer;
22
+ const PreviewImage = config.getComponent({ name: 'PreviewImage' }).component;
23
23
 
24
24
  return (
25
25
  <Container className="view-wrapper summary-view">
@@ -41,9 +41,10 @@ const SummaryView = ({ content }) => {
41
41
  {item.image_field && (
42
42
  <PreviewImage
43
43
  item={item}
44
- alt={item.image_caption ? item.image_caption : item.title}
45
- size="thumb"
44
+ alt={item.image_caption}
46
45
  className="ui image floated right clear"
46
+ responsive={true}
47
+ loading="lazy"
47
48
  />
48
49
  )}
49
50
  {item.description && <p>{item.description}</p>}
@@ -75,6 +75,7 @@ import {
75
75
  DateRangeFacetFilterListEntry,
76
76
  } from '@plone/volto/components/manage/Blocks/Search/components';
77
77
  import getListingBlockAsyncData from '@plone/volto/components/manage/Blocks/Listing/getAsyncData';
78
+ import { getImageBlockSizes } from '@plone/volto/components/manage/Blocks/Image/utils';
78
79
 
79
80
  // block sidebar schemas (not the Dexterity Layout block settings schemas)
80
81
  import HeroImageLeftBlockSchema from '@plone/volto/components/manage/Blocks/HeroImageLeft/schema';
@@ -258,6 +259,7 @@ const blocksConfig = {
258
259
  restricted: false,
259
260
  mostUsed: true,
260
261
  sidebarTab: 1,
262
+ getSizes: getImageBlockSizes,
261
263
  },
262
264
  leadimage: {
263
265
  id: 'leadimage',
@@ -1,6 +1,8 @@
1
- import { App, PreviewImage } from '@plone/volto/components';
1
+ import { App, PreviewImage, Image } from '@plone/volto/components';
2
2
 
3
+ // Register components.
3
4
  export const components = {
4
5
  PreviewImage: { component: PreviewImage },
5
6
  App: { component: App },
7
+ Image: { component: Image },
6
8
  };
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Config.
3
+ * @module config
4
+ */
5
+ import { parse as parseUrl } from 'url';
6
+ import { defaultWidget, widgetMapping } from './Widgets';
7
+ import {
8
+ layoutViews,
9
+ contentTypesViews,
10
+ defaultView,
11
+ errorViews,
12
+ layoutViewsNamesMapping,
13
+ } from './Views';
14
+ import { nonContentRoutes } from './NonContentRoutes';
15
+ import {
16
+ groupBlocksOrder,
17
+ requiredBlocks,
18
+ blocksConfig,
19
+ initialBlocks,
20
+ initialBlocksFocus,
21
+ } from './Blocks';
22
+ import { components } from './Components';
23
+ import { loadables } from './Loadables';
24
+ import { workflowMapping } from './Workflows';
25
+
26
+ import { contentIcons } from './ContentIcons';
27
+ import { styleClassNameConverters, styleClassNameExtenders } from './Style';
28
+ import {
29
+ controlPanelsIcons,
30
+ filterControlPanels,
31
+ filterControlPanelsSchema,
32
+ } from './ControlPanels';
33
+
34
+ import { richtextEditorSettings, richtextViewSettings } from './RichTextEditor';
35
+
36
+ import applyAddonConfiguration, { addonsInfo } from 'load-volto-addons';
37
+
38
+ import ConfigRegistry from '@plone/volto/registry';
39
+
40
+ const host = process.env.HOST || 'localhost';
41
+ const port = process.env.PORT || '3000';
42
+
43
+ const apiPath =
44
+ process.env.RAZZLE_API_PATH ||
45
+ (__DEVELOPMENT__ ? `http://${host}:${port}` : '');
46
+
47
+ const getServerURL = (url) => {
48
+ if (!url) return;
49
+ const apiPathURL = parseUrl(url);
50
+ return `${apiPathURL.protocol}//${apiPathURL.hostname}${
51
+ apiPathURL.port ? `:${apiPathURL.port}` : ''
52
+ }`;
53
+ };
54
+
55
+ // Sensible defaults for publicURL
56
+ // if RAZZLE_PUBLIC_URL is present, use it
57
+ // if in DEV, use the host/port combination by default
58
+ // if in PROD, assume it's RAZZLE_API_PATH server name (no /api or alikes) or fallback
59
+ // to DEV settings if RAZZLE_API_PATH is not present
60
+ const publicURL =
61
+ process.env.RAZZLE_PUBLIC_URL ||
62
+ (__DEVELOPMENT__
63
+ ? `http://${host}:${port}`
64
+ : getServerURL(process.env.RAZZLE_API_PATH) || `http://${host}:${port}`);
65
+
66
+ const serverConfig =
67
+ typeof __SERVER__ !== 'undefined' && __SERVER__
68
+ ? require('./server').default
69
+ : {};
70
+
71
+ let config = {
72
+ settings: {
73
+ host,
74
+ port,
75
+ // The URL Volto is going to be served (see sensible defaults above)
76
+ publicURL,
77
+ apiPath,
78
+ apiExpanders: [
79
+ // Add the following expanders for only issuing a single request.
80
+ // https://6.docs.plone.org/volto/configuration/settings-reference.html#term-apiExpanders
81
+ // {
82
+ // match: '',
83
+ // GET_CONTENT: ['breadcrumbs', 'navigation', 'actions', 'types'],
84
+ // },
85
+ ],
86
+ // Internal proxy to bypass CORS *while developing*. NOT intended for production use.
87
+ // In production is recommended you use a Seamless mode deployment using a web server in
88
+ // front of both the frontend and the backend so you can bypass CORS safely.
89
+ // https://6.docs.plone.org/volto/deploying/seamless-mode.html
90
+ devProxyToApiPath:
91
+ process.env.RAZZLE_DEV_PROXY_API_PATH ||
92
+ process.env.RAZZLE_INTERNAL_API_PATH ||
93
+ process.env.RAZZLE_API_PATH ||
94
+ 'http://localhost:8080/Plone', // Set it to '' for disabling the proxy
95
+ // proxyRewriteTarget Set it for set a custom target for the proxy or overide the internal VHM rewrite
96
+ // proxyRewriteTarget: '/VirtualHostBase/http/localhost:8080/Plone/VirtualHostRoot/_vh_api'
97
+ // proxyRewriteTarget: 'https://myvoltositeinproduction.com'
98
+ proxyRewriteTarget: process.env.RAZZLE_PROXY_REWRITE_TARGET || undefined,
99
+ // apiPath: process.env.RAZZLE_API_PATH || 'http://localhost:8000', // for Volto reference
100
+ // apiPath: process.env.RAZZLE_API_PATH || 'http://localhost:8081/db/web', // for guillotina
101
+ actions_raising_api_errors: ['GET_CONTENT', 'UPDATE_CONTENT'],
102
+ internalApiPath: process.env.RAZZLE_INTERNAL_API_PATH || undefined,
103
+ websockets: process.env.RAZZLE_WEBSOCKETS || false,
104
+ // TODO: legacyTraverse to be removed when the use of the legacy traverse is deprecated.
105
+ legacyTraverse: process.env.RAZZLE_LEGACY_TRAVERSE || false,
106
+ cookieExpires: 15552000, //in seconds. Default is 6 month (15552000)
107
+ nonContentRoutes,
108
+ richtextEditorSettings, // Part of draftjs support, to be removed
109
+ richtextViewSettings, // Part of draftjs support, to be removed
110
+ imageObjects: ['Image'],
111
+ reservedIds: ['login', 'layout', 'plone', 'zip', 'properties'],
112
+ downloadableObjects: ['File'], //list of content-types for which the direct download of the file will be carried out if the user is not authenticated
113
+ viewableInBrowserObjects: [], //ex: ['File']. List of content-types for which the file will be displayed in browser if the user is not authenticated
114
+ listingPreviewImageField: 'image', // deprecated from Volto 14 onwards
115
+ openExternalLinkInNewTab: false,
116
+ notSupportedBrowsers: ['ie'],
117
+ defaultPageSize: 25,
118
+ isMultilingual: false,
119
+ supportedLanguages: ['en'],
120
+ defaultLanguage: 'en',
121
+ navDepth: 1,
122
+ expressMiddleware: serverConfig.expressMiddleware, // BBB
123
+ defaultBlockType: 'slate',
124
+ verticalFormTabs: false,
125
+ useEmailAsLogin: false,
126
+ persistentReducers: ['blocksClipboard'],
127
+ initialReducersBlacklist: [], // reducers in this list won't be hydrated in windows.__data
128
+ asyncPropsExtenders: [], // per route asyncConnect customizers
129
+ contentIcons: contentIcons,
130
+ loadables,
131
+ lazyBundles: {
132
+ cms: [
133
+ 'prettierStandalone',
134
+ 'prettierParserHtml',
135
+ 'prismCore',
136
+ 'toastify',
137
+ 'reactSelect',
138
+ 'reactBeautifulDnd',
139
+ // 'diffLib',
140
+ ],
141
+ draftEditor: [
142
+ 'immutableLib',
143
+ 'draftJs',
144
+ 'draftJsLibIsSoftNewlineEvent',
145
+ 'draftJsFilters',
146
+ 'draftJsInlineToolbarPlugin',
147
+ 'draftJsImportHtml',
148
+ 'draftJsBlockBreakoutPlugin',
149
+ ],
150
+ },
151
+ appExtras: [],
152
+ maxResponseSize: 2000000000, // This is superagent default (200 mb)
153
+ serverConfig,
154
+ storeExtenders: [],
155
+ showTags: true,
156
+ controlpanels: [],
157
+ controlPanelsIcons,
158
+ filterControlPanels,
159
+ filterControlPanelsSchema,
160
+ externalRoutes: [
161
+ // URL to be considered as external
162
+ // {
163
+ // match: {
164
+ // path: '/news',
165
+ // exact: false,
166
+ // strict: false,
167
+ // },
168
+ // url(payload) {
169
+ // return payload.location.pathname;
170
+ // },
171
+ // },
172
+ ],
173
+ showSelfRegistration: false,
174
+ contentMetadataTagsImageField: 'image',
175
+ hasWorkingCopySupport: false,
176
+ maxUndoLevels: 200, // undo history size for the main form
177
+ addonsInfo: addonsInfo,
178
+ workflowMapping,
179
+ errorHandlers: [], // callables for unhandled errors
180
+ styleClassNameConverters,
181
+ hashLinkSmoothScroll: false,
182
+ styleClassNameExtenders,
183
+ querystringSearchGet: false,
184
+ },
185
+ experimental: {
186
+ addBlockButton: {
187
+ enabled: false,
188
+ },
189
+ },
190
+ widgets: {
191
+ ...widgetMapping,
192
+ default: defaultWidget,
193
+ },
194
+ views: {
195
+ layoutViews,
196
+ contentTypesViews,
197
+ defaultView,
198
+ errorViews,
199
+ layoutViewsNamesMapping,
200
+ },
201
+ blocks: {
202
+ requiredBlocks,
203
+ blocksConfig,
204
+ groupBlocksOrder,
205
+ initialBlocks,
206
+ initialBlocksFocus,
207
+ showEditBlocksInBabelView: false,
208
+ },
209
+ addonRoutes: [],
210
+ addonReducers: {},
211
+ components,
212
+ };
213
+
214
+ ConfigRegistry.settings = config.settings;
215
+ ConfigRegistry.experimental = config.experimental;
216
+ ConfigRegistry.blocks = config.blocks;
217
+ ConfigRegistry.views = config.views;
218
+ ConfigRegistry.widgets = config.widgets;
219
+ ConfigRegistry.addonRoutes = config.addonRoutes;
220
+ ConfigRegistry.addonReducers = config.addonReducers;
221
+ ConfigRegistry.components = config.components;
222
+
223
+ applyAddonConfiguration(ConfigRegistry);
@@ -2,11 +2,13 @@ import express from 'express';
2
2
  import { getAPIResourceWithAuth } from '@plone/volto/helpers';
3
3
 
4
4
  const HEADERS = [
5
- 'Accept-Ranges',
6
- 'Cache-Control',
7
- 'Content-Disposition',
8
- 'Content-Range',
9
- 'Content-Type',
5
+ 'accept-ranges',
6
+ 'cache-control',
7
+ 'content-disposition',
8
+ 'content-range',
9
+ 'content-type',
10
+ 'x-sendfile',
11
+ 'x-accel-redirect',
10
12
  ];
11
13
 
12
14
  function filesMiddlewareFn(req, res, next) {
@@ -14,7 +16,7 @@ function filesMiddlewareFn(req, res, next) {
14
16
  .then((resource) => {
15
17
  // Just forward the headers that we need
16
18
  HEADERS.forEach((header) => {
17
- if (resource.get(header)) {
19
+ if (resource.headers[header]) {
18
20
  res.set(header, resource.get(header));
19
21
  }
20
22
  });
@@ -1,7 +1,13 @@
1
1
  import express from 'express';
2
2
  import { getAPIResourceWithAuth } from '@plone/volto/helpers';
3
3
 
4
- const HEADERS = ['content-type', 'content-disposition', 'cache-control'];
4
+ const HEADERS = [
5
+ 'content-type',
6
+ 'content-disposition',
7
+ 'cache-control',
8
+ 'x-sendfile',
9
+ 'x-accel-redirect',
10
+ ];
5
11
 
6
12
  function imageMiddlewareFn(req, res, next) {
7
13
  getAPIResourceWithAuth(req)
@@ -312,6 +312,12 @@ export const messages = defineMessages({
312
312
  id: 'flush intIds and rebuild relations',
313
313
  defaultMessage: 'flush intIds and rebuild relations',
314
314
  },
315
+ flushAndRebuildRelationsHints: {
316
+ id: 'flushAndRebuildRelationsHints',
317
+ defaultMessage:
318
+ '<ul><li>Regenerate intIds (tokens of relations in relation catalog)</li><li>Rebuild relations</li></ul><p>Check the log for details!</p><p><b>Warning</b>: If you have add-ons relying on intIds, you should not flush them.</p>',
319
+ },
320
+
315
321
  addPotentialTargetsPath: {
316
322
  id: 'target path',
317
323
  defaultMessage: 'target path',
@@ -3,7 +3,7 @@
3
3
  * @module helpers/Url
4
4
  */
5
5
 
6
- import { last, memoize } from 'lodash';
6
+ import { last, memoize, isArray, isObject, isString } from 'lodash';
7
7
  import { urlRegex, telRegex, mailRegex } from './urlRegex';
8
8
  import prependHttp from 'prepend-http';
9
9
  import config from '@plone/volto/registry';
@@ -251,6 +251,27 @@ export function isUrl(url) {
251
251
  return urlRegex().test(url);
252
252
  }
253
253
 
254
+ /**
255
+ * Get field url
256
+ * @method getFieldURL
257
+ * @param {object} data
258
+ * @returns {string | any} URL string value if field is of url type or any.
259
+ */
260
+ export const getFieldURL = (data) => {
261
+ let url = data;
262
+ const _isObject = data && isObject(data) && !isArray(data);
263
+ if (_isObject && data['@type'] === 'URL') {
264
+ url = data['value'] ?? data['url'] ?? data['href'] ?? data;
265
+ } else if (_isObject) {
266
+ url = data['@id'] ?? data['url'] ?? data['href'] ?? data;
267
+ }
268
+ if (isArray(data)) {
269
+ url = data.map((item) => getFieldURL(item));
270
+ }
271
+ if (isString(url) && isInternalURL(url)) return flattenToAppURL(url);
272
+ return url;
273
+ };
274
+
254
275
  /**
255
276
  * Normalize URL, adds protocol (if required eg. user has not entered the protocol)
256
277
  * @method normalizeUrl
@@ -10,6 +10,7 @@ import {
10
10
  isCmsUi,
11
11
  isInternalURL,
12
12
  isUrl,
13
+ getFieldURL,
13
14
  normalizeUrl,
14
15
  removeProtocol,
15
16
  addAppURL,
@@ -260,6 +261,46 @@ describe('Url', () => {
260
261
  expect(isUrl(href)).toBe(false);
261
262
  });
262
263
  });
264
+ describe('getFieldURL', () => {
265
+ it('returns app URL if the field is a string', () => {
266
+ const field = `${settings.apiPath}/foo/bar`;
267
+ expect(getFieldURL(field)).toBe('/foo/bar');
268
+ });
269
+ it('returns app URL if the field is an object with "@id"', () => {
270
+ const field = { '@id': '/foo/bar' };
271
+ expect(getFieldURL(field)).toBe('/foo/bar');
272
+ });
273
+ it('returns app URL if the field is an object with type URL', () => {
274
+ const field = { '@type': 'URL', value: '/foo/bar' };
275
+ expect(getFieldURL(field)).toBe('/foo/bar');
276
+ });
277
+ it('returns app URL if the field is an object with url or href properties', () => {
278
+ const fieldUrl = { url: '/foo/bar' };
279
+ const fieldHref = { href: '/foo/bar' };
280
+ expect(getFieldURL(fieldUrl)).toBe('/foo/bar');
281
+ expect(getFieldURL(fieldHref)).toBe('/foo/bar');
282
+ });
283
+ it('returns array of app URL if the field is an array of strings', () => {
284
+ const field = [
285
+ `${settings.apiPath}/foo/bar/1`,
286
+ `${settings.apiPath}/foo/bar/2`,
287
+ ];
288
+ expect(getFieldURL(field)).toStrictEqual(['/foo/bar/1', '/foo/bar/2']);
289
+ });
290
+ it('returns array of app URL if the field is an array of objects', () => {
291
+ const field = [
292
+ {
293
+ '@type': 'URL',
294
+ value: `${settings.apiPath}/foo/bar/1`,
295
+ },
296
+ {
297
+ '@type': 'URL',
298
+ value: `${settings.apiPath}/foo/bar/2`,
299
+ },
300
+ ];
301
+ expect(getFieldURL(field)).toStrictEqual(['/foo/bar/1', '/foo/bar/2']);
302
+ });
303
+ });
263
304
  describe('normalizeUrl', () => {
264
305
  it('normalizeUrl test', () => {
265
306
  const href = `www.example.com`;
@@ -103,7 +103,7 @@ export default function relations(state = initialState, action = {}) {
103
103
  error: null,
104
104
  relations: action.result.relations
105
105
  ? action.result.relations
106
- : state.relations,
106
+ : [],
107
107
  stats: null,
108
108
  },
109
109
  },
@@ -173,7 +173,15 @@ config.set('widgets', {
173
173
  default: BaseWidget('default'),
174
174
  });
175
175
 
176
- config.set('components', {});
176
+ config.set('components', {
177
+ PreviewImage: {
178
+ component: (props) => <img alt="PreviewImage component mock" {...props} />,
179
+ },
180
+ Image: {
181
+ // eslint-disable-next-line jsx-a11y/img-redundant-alt
182
+ component: (props) => <img alt="Image component mock" {...props} />,
183
+ },
184
+ });
177
185
  config.set('experimental', {
178
186
  addBlockButton: {
179
187
  enabled: false,
@@ -22,7 +22,7 @@
22
22
 
23
23
  .block .block:not(.inner)::before {
24
24
  position: absolute;
25
- z-index: -1;
25
+ z-index: auto;
26
26
  top: -9px;
27
27
  left: -9px;
28
28
  width: ~'calc(100% + 18px)';
@@ -811,6 +811,7 @@ body.has-toolbar.has-sidebar-collapsed .ui.wrapper > .ui.inner.block.full {
811
811
  .listing-item a {
812
812
  display: flex;
813
813
  width: 100%;
814
+ align-items: flex-start;
814
815
 
815
816
  @media only screen and (max-width: (@largestMobileScreen)) {
816
817
  flex-direction: column;
@@ -822,6 +823,7 @@ body.has-toolbar.has-sidebar-collapsed .ui.wrapper > .ui.inner.block.full {
822
823
 
823
824
  img {
824
825
  width: 15%;
826
+ height: auto;
825
827
  margin-right: 20px;
826
828
 
827
829
  @media only screen and (max-width: (@largestMobileScreen)) {
@@ -645,6 +645,11 @@ body.has-toolbar-collapsed .mobile-menu {
645
645
  transition: transform 0.5s cubic-bezier(0.09, 0.11, 0.24, 0.91);
646
646
  }
647
647
 
648
+ img.responsive {
649
+ max-width: 100%;
650
+ height: auto;
651
+ }
652
+
648
653
  // Deprecated as per https://github.com/plone/volto/issues/1265
649
654
  // @import 'utils';
650
655
  @import (multiple) '../extras/fonts';
@@ -1,90 +0,0 @@
1
- [
2
- {
3
- "id": "Insert row before",
4
- "defaultMessage": "Insert row before"
5
- },
6
- {
7
- "id": "Insert row after",
8
- "defaultMessage": "Insert row after"
9
- },
10
- {
11
- "id": "Delete row",
12
- "defaultMessage": "Delete row"
13
- },
14
- {
15
- "id": "Insert col before",
16
- "defaultMessage": "Insert col before"
17
- },
18
- {
19
- "id": "Insert col after",
20
- "defaultMessage": "Insert col after"
21
- },
22
- {
23
- "id": "Delete col",
24
- "defaultMessage": "Delete col"
25
- },
26
- {
27
- "id": "Hide headers",
28
- "defaultMessage": "Hide headers"
29
- },
30
- {
31
- "id": "Make the table sortable",
32
- "defaultMessage": "Make the table sortable"
33
- },
34
- {
35
- "id": "Visible only in view mode",
36
- "defaultMessage": "Visible only in view mode"
37
- },
38
- {
39
- "id": "Fixed width table cells",
40
- "defaultMessage": "Fixed width table cells"
41
- },
42
- {
43
- "id": "Make the table compact",
44
- "defaultMessage": "Make the table compact"
45
- },
46
- {
47
- "id": "Reduce complexity",
48
- "defaultMessage": "Reduce complexity"
49
- },
50
- {
51
- "id": "Divide each row into separate cells",
52
- "defaultMessage": "Divide each row into separate cells"
53
- },
54
- {
55
- "id": "Table color inverted",
56
- "defaultMessage": "Table color inverted"
57
- },
58
- {
59
- "id": "Stripe alternate rows with color",
60
- "defaultMessage": "Stripe alternate rows with color"
61
- },
62
- {
63
- "id": "Left",
64
- "defaultMessage": "Left"
65
- },
66
- {
67
- "id": "Center",
68
- "defaultMessage": "Center"
69
- },
70
- {
71
- "id": "Right",
72
- "defaultMessage": "Right"
73
- },
74
- {
75
- "id": "Bottom",
76
- "defaultMessage": "Bottom"
77
- },
78
- {
79
- "id": "Middle",
80
- "defaultMessage": "Middle"
81
- },
82
- {
83
- "id": "Top",
84
- "defaultMessage": "Top"
85
- },
86
- {
87
- "id": "Table",
88
- "defaultMessage": "Table"
89
- }
90
- ]
@@ -1,6 +0,0 @@
1
- [
2
- {
3
- "id": "Type text…",
4
- "defaultMessage": "Type text…"
5
- }
6
- ]
@@ -1,6 +0,0 @@
1
- [
2
- {
3
- "id": "Type text…",
4
- "defaultMessage": "Type text…"
5
- }
6
- ]
@@ -1,6 +0,0 @@
1
- [
2
- {
3
- "id": "No matching blocks",
4
- "defaultMessage": "No matching blocks"
5
- }
6
- ]
@@ -1,10 +0,0 @@
1
- [
2
- {
3
- "id": "Edit link",
4
- "defaultMessage": "Edit link"
5
- },
6
- {
7
- "id": "Remove link",
8
- "defaultMessage": "Remove link"
9
- }
10
- ]
@@ -1,10 +0,0 @@
1
- [
2
- {
3
- "id": "Add link",
4
- "defaultMessage": "Add link"
5
- },
6
- {
7
- "id": "Edit link",
8
- "defaultMessage": "Edit link"
9
- }
10
- ]