@plone/volto 17.0.0-alpha.5 → 17.0.0-alpha.6

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 (31) hide show
  1. package/.changelog.draft +26 -6
  2. package/.yarn/install-state.gz +0 -0
  3. package/CHANGELOG.md +84 -13
  4. package/CONTRIBUTING.md +1 -1
  5. package/README.md +4 -4
  6. package/locales/de/LC_MESSAGES/volto.po +17 -17
  7. package/locales/de.json +1 -1
  8. package/package.json +3 -2
  9. package/packages/volto-slate/package.json +1 -1
  10. package/src/components/manage/Blocks/Listing/Edit.jsx +0 -14
  11. package/src/components/manage/Blocks/Listing/getAsyncData.js +10 -2
  12. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +18 -13
  13. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +2 -1
  14. package/src/components/manage/Contents/Contents.jsx +16 -14
  15. package/src/components/manage/Controlpanels/Controlpanels.jsx +190 -224
  16. package/src/components/manage/Controlpanels/Controlpanels.test.jsx +46 -7
  17. package/src/components/manage/Form/InlineForm.jsx +39 -9
  18. package/src/components/manage/Form/InlineFormState.js +8 -0
  19. package/src/components/manage/Widgets/ObjectListWidget.jsx +3 -8
  20. package/src/components/theme/Icon/Icon.jsx +2 -2
  21. package/src/components/theme/Login/Login.jsx +1 -0
  22. package/src/config/index.js +1 -0
  23. package/src/helpers/Robots/Robots.js +16 -1
  24. package/src/helpers/Url/Url.js +8 -3
  25. package/src/helpers/Url/Url.test.js +14 -0
  26. package/src/helpers/Utils/Utils.js +17 -4
  27. package/src/helpers/index.js +2 -0
  28. package/src/middleware/Api.test.js +54 -0
  29. package/src/middleware/api.js +1 -1
  30. package/test-setup-config.js +1 -0
  31. package/theme/themes/pastanaga/extras/sidebar.less +4 -0
@@ -239,6 +239,7 @@ class Login extends Component {
239
239
  <Input
240
240
  type="password"
241
241
  id="password"
242
+ autocomplete="current-password"
242
243
  name="password"
243
244
  placeholder={this.props.intl.formatMessage(
244
245
  messages.password,
@@ -181,6 +181,7 @@ let config = {
181
181
  hashLinkSmoothScroll: false,
182
182
  styleClassNameExtenders,
183
183
  querystringSearchGet: false,
184
+ blockSettingsTabFieldsetsInitialStateOpen: true,
184
185
  },
185
186
  experimental: {
186
187
  addBlockButton: {
@@ -28,9 +28,24 @@ export const generateRobots = (req) =>
28
28
  if (error) {
29
29
  resolve(text || error);
30
30
  } else {
31
+ // It appears that express does not take the x-forwarded headers into
32
+ // consideration, so we do it ourselves.
33
+ const {
34
+ 'x-forwarded-proto': forwardedProto,
35
+ 'x-forwarded-host': forwardedHost,
36
+ 'x-forwarded-port': forwardedPort,
37
+ } = req.headers;
38
+ const proto = forwardedProto ?? req.protocol;
39
+ const host = forwardedHost ?? req.get('Host');
40
+ const portNum = forwardedPort ?? req.get('Port');
41
+ const port =
42
+ (proto === 'https' && '' + portNum === '443') ||
43
+ (proto === 'http' && '' + portNum === '80')
44
+ ? ''
45
+ : `:${portNum}`;
31
46
  // Plone has probably returned the sitemap link with the internal url.
32
47
  // If so, let's replace it with the current one.
33
- const url = `${req.protocol}://${req.get('Host')}`;
48
+ const url = `${proto}://${host}${port}`;
34
49
  text = text.replace(internalUrl, url);
35
50
  // Replace the sitemap with the sitemap index.
36
51
  text = text.replace('sitemap.xml.gz', 'sitemap-index.xml');
@@ -280,14 +280,14 @@ export function isTelephone(text) {
280
280
  }
281
281
 
282
282
  export function normaliseMail(email) {
283
- if (email.toLowerCase().startsWith('mailto:')) {
283
+ if (email?.toLowerCase()?.startsWith('mailto:')) {
284
284
  return email;
285
285
  }
286
286
  return `mailto:${email}`;
287
287
  }
288
288
 
289
289
  export function normalizeTelephone(tel) {
290
- if (tel.toLowerCase().startsWith('tel:')) {
290
+ if (tel?.toLowerCase()?.startsWith('tel:')) {
291
291
  return tel;
292
292
  }
293
293
  return `tel:${tel}`;
@@ -310,12 +310,17 @@ export function checkAndNormalizeUrl(url) {
310
310
  res.url = URLUtils.normalizeTelephone(url);
311
311
  } else {
312
312
  //url
313
- if (!res.url.startsWith('/') && !res.url.startsWith('#')) {
313
+ if (
314
+ res.url?.length >= 0 &&
315
+ !res.url.startsWith('/') &&
316
+ !res.url.startsWith('#')
317
+ ) {
314
318
  res.url = URLUtils.normalizeUrl(url);
315
319
  if (!URLUtils.isUrl(res.url)) {
316
320
  res.isValid = false;
317
321
  }
318
322
  }
323
+ if (res.url === undefined || res.url === null) res.isValid = false;
319
324
  }
320
325
  return res;
321
326
  }
@@ -14,6 +14,9 @@ import {
14
14
  removeProtocol,
15
15
  addAppURL,
16
16
  expandToBackendURL,
17
+ checkAndNormalizeUrl,
18
+ normaliseMail,
19
+ normalizeTelephone,
17
20
  } from './Url';
18
21
 
19
22
  beforeEach(() => {
@@ -61,6 +64,17 @@ describe('Url', () => {
61
64
  it('return empty string if no url is empty string', () => {
62
65
  expect(getBaseUrl('')).toBe('');
63
66
  });
67
+ it('return a null/undefined mailto adress ', () => {
68
+ expect(normaliseMail(null)).toBe('mailto:null');
69
+ expect(normaliseMail(undefined)).toBe('mailto:undefined');
70
+ });
71
+ it('return a null/undefined telephone number', () => {
72
+ expect(normalizeTelephone(null)).toBe('tel:null');
73
+ expect(normalizeTelephone(undefined)).toBe('tel:undefined');
74
+ });
75
+ it('null returns an invalid link', () => {
76
+ expect(checkAndNormalizeUrl(null).isValid).toBe(false);
77
+ });
64
78
  });
65
79
 
66
80
  describe('getView', () => {
@@ -258,11 +258,11 @@ export const removeFromArray = (array, index) => {
258
258
  };
259
259
 
260
260
  /**
261
- * Reorder array
261
+ * Moves an item from origin to target inside an array in an immutable way
262
262
  * @param {Array} array Array with data
263
- * @param {number} origin Index of item to be reordered
264
- * @param {number} target Index of item to be reordered to
265
- * @returns {Array} Array with reordered elements
263
+ * @param {number} origin Index of item to be moved from
264
+ * @param {number} target Index of item to be moved to
265
+ * @returns {Array} Resultant array
266
266
  */
267
267
  export const reorderArray = (array, origin, target) => {
268
268
  const result = Array.from(array);
@@ -299,3 +299,16 @@ export const cloneDeepSchema = (object) => {
299
299
  }
300
300
  });
301
301
  };
302
+
303
+ /**
304
+ * Creates an array given a range of numbers
305
+ * @param {number} start start number from
306
+ * @param {number} stop stop number at
307
+ * @param {number} step step every each number in the sequence
308
+ * @returns {array} The result, eg. [0, 1, 2, 3, 4]
309
+ */
310
+ export const arrayRange = (start, stop, step) =>
311
+ Array.from(
312
+ { length: (stop - start) / step + 1 },
313
+ (value, index) => start + index * step,
314
+ );
@@ -85,6 +85,8 @@ export {
85
85
  hasApiExpander,
86
86
  replaceItemOfArray,
87
87
  cloneDeepSchema,
88
+ arrayRange,
89
+ reorderArray,
88
90
  } from '@plone/volto/helpers/Utils/Utils';
89
91
  export { messages } from './MessageLabels/MessageLabels';
90
92
  export {
@@ -53,6 +53,60 @@ describe('api middleware helpers', () => {
53
53
  );
54
54
  expect(result).toEqual('/de/mypage/@navigation?expand.navigation.depth=3');
55
55
  });
56
+ it('addExpandersToPath - Path matching, preserve query', () => {
57
+ config.settings.apiExpanders = [
58
+ {
59
+ match: '/de/mypage',
60
+ GET_CONTENT: ['mycustomexpander', 'mycustomexpander2'],
61
+ },
62
+ ];
63
+
64
+ const result = addExpandersToPath(
65
+ '/de/mypage/@navigation?expand.navigation.depth=3',
66
+ GET_CONTENT,
67
+ );
68
+ expect(result).toEqual(
69
+ '/de/mypage/@navigation?expand=mycustomexpander,mycustomexpander2&expand.navigation.depth=3',
70
+ );
71
+ });
72
+ it('addExpandersToPath - Path matching, preserve query with multiple', () => {
73
+ config.settings.apiExpanders = [
74
+ {
75
+ match: '/de/mypage',
76
+ GET_CONTENT: ['mycustomexpander', 'mycustomexpander2'],
77
+ },
78
+ ];
79
+
80
+ const result = addExpandersToPath(
81
+ '/de/mypage/@navigation?expand.navigation.depth=3&expand.other=2',
82
+ GET_CONTENT,
83
+ );
84
+ expect(result).toEqual(
85
+ '/de/mypage/@navigation?expand=mycustomexpander,mycustomexpander2&expand.navigation.depth=3&expand.other=2',
86
+ );
87
+ });
88
+ it('addExpandersToPath - Path not matching, preserve encoded query', () => {
89
+ config.settings.apiExpanders = [
90
+ {
91
+ match: '/de/otherpath',
92
+ GET_CONTENT: ['mycustomexpander'],
93
+ },
94
+ ];
95
+
96
+ const result = addExpandersToPath('/de/mypage?query=a%26b', GET_CONTENT);
97
+ expect(result).toEqual('/de/mypage?query=a%26b');
98
+ });
99
+ it('addExpandersToPath - Path matching, preserve encoded query', () => {
100
+ config.settings.apiExpanders = [
101
+ {
102
+ match: '/de/mypage',
103
+ GET_CONTENT: ['mycustomexpander'],
104
+ },
105
+ ];
106
+
107
+ const result = addExpandersToPath('/de/mypage?query=a%26b', GET_CONTENT);
108
+ expect(result).toEqual('/de/mypage?expand=mycustomexpander&query=a%26b');
109
+ });
56
110
  it('addExpandersToPath - Two custom expanders from settings', () => {
57
111
  config.settings.apiExpanders = [
58
112
  {
@@ -43,7 +43,7 @@ export function addExpandersToPath(path, type, isAnonymous) {
43
43
  const {
44
44
  url,
45
45
  query: { expand, ...query },
46
- } = qs.parseUrl(path);
46
+ } = qs.parseUrl(path, { decode: false });
47
47
 
48
48
  const expandersFromConfig = apiExpanders
49
49
  .filter((expand) => matchPath(url, expand.match) && expand[type])
@@ -82,6 +82,7 @@ config.set('settings', {
82
82
  viewableInBrowserObjects: [],
83
83
  styleClassNameConverters,
84
84
  styleClassNameExtenders,
85
+ blockSettingsTabFieldsetsInitialStateOpen: true,
85
86
  });
86
87
  config.set('blocks', {
87
88
  blocksConfig: {
@@ -489,3 +489,7 @@
489
489
  margin-left: 5px;
490
490
  }
491
491
  }
492
+
493
+ .formtabs {
494
+ flex-wrap: wrap;
495
+ }