@plone/volto 18.32.1 → 18.32.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.
Files changed (167) hide show
  1. package/.eslintignore +1 -0
  2. package/CHANGELOG.md +40 -0
  3. package/README.md +0 -4
  4. package/locales/af/LC_MESSAGES/volto.po +20 -0
  5. package/locales/af.json +1 -1
  6. package/locales/ar/LC_MESSAGES/volto.po +20 -0
  7. package/locales/ar.json +1 -1
  8. package/locales/bg/LC_MESSAGES/volto.po +20 -0
  9. package/locales/bg.json +1 -1
  10. package/locales/bn/LC_MESSAGES/volto.po +20 -0
  11. package/locales/bn.json +1 -1
  12. package/locales/ca/LC_MESSAGES/volto.po +20 -0
  13. package/locales/ca.json +1 -1
  14. package/locales/cs/LC_MESSAGES/volto.po +20 -0
  15. package/locales/cs.json +1 -1
  16. package/locales/cy/LC_MESSAGES/volto.po +20 -0
  17. package/locales/cy.json +1 -1
  18. package/locales/da/LC_MESSAGES/volto.po +20 -0
  19. package/locales/da.json +1 -1
  20. package/locales/de/LC_MESSAGES/volto.po +20 -0
  21. package/locales/de.json +1 -1
  22. package/locales/el/LC_MESSAGES/volto.po +20 -0
  23. package/locales/el.json +1 -1
  24. package/locales/en/LC_MESSAGES/volto.po +20 -0
  25. package/locales/en.json +1 -1
  26. package/locales/en_AU/LC_MESSAGES/volto.po +20 -0
  27. package/locales/en_AU.json +1 -1
  28. package/locales/en_GB/LC_MESSAGES/volto.po +20 -0
  29. package/locales/en_GB.json +1 -1
  30. package/locales/eo/LC_MESSAGES/volto.po +20 -0
  31. package/locales/eo.json +1 -1
  32. package/locales/es/LC_MESSAGES/volto.po +20 -0
  33. package/locales/es.json +1 -1
  34. package/locales/et/LC_MESSAGES/volto.po +20 -0
  35. package/locales/et.json +1 -1
  36. package/locales/eu/LC_MESSAGES/volto.po +20 -0
  37. package/locales/eu.json +1 -1
  38. package/locales/fa/LC_MESSAGES/volto.po +20 -0
  39. package/locales/fa.json +1 -1
  40. package/locales/fi/LC_MESSAGES/volto.po +20 -0
  41. package/locales/fi.json +1 -1
  42. package/locales/fr/LC_MESSAGES/volto.po +20 -0
  43. package/locales/fr.json +1 -1
  44. package/locales/fu/LC_MESSAGES/volto.po +20 -0
  45. package/locales/fu.json +1 -1
  46. package/locales/gl/LC_MESSAGES/volto.po +20 -0
  47. package/locales/gl.json +1 -1
  48. package/locales/he/LC_MESSAGES/volto.po +20 -0
  49. package/locales/he.json +1 -1
  50. package/locales/hi/LC_MESSAGES/volto.po +20 -0
  51. package/locales/hi.json +1 -1
  52. package/locales/hr/LC_MESSAGES/volto.po +20 -0
  53. package/locales/hr.json +1 -1
  54. package/locales/hu/LC_MESSAGES/volto.po +20 -0
  55. package/locales/hu.json +1 -1
  56. package/locales/hy/LC_MESSAGES/volto.po +20 -0
  57. package/locales/hy.json +1 -1
  58. package/locales/id/LC_MESSAGES/volto.po +20 -0
  59. package/locales/id.json +1 -1
  60. package/locales/it/LC_MESSAGES/volto.po +31 -11
  61. package/locales/it.json +1 -1
  62. package/locales/ja/LC_MESSAGES/volto.po +20 -0
  63. package/locales/ja.json +1 -1
  64. package/locales/ka/LC_MESSAGES/volto.po +20 -0
  65. package/locales/ka.json +1 -1
  66. package/locales/kn/LC_MESSAGES/volto.po +20 -0
  67. package/locales/kn.json +1 -1
  68. package/locales/ko/LC_MESSAGES/volto.po +20 -0
  69. package/locales/ko.json +1 -1
  70. package/locales/lt/LC_MESSAGES/volto.po +20 -0
  71. package/locales/lt.json +1 -1
  72. package/locales/lv/LC_MESSAGES/volto.po +20 -0
  73. package/locales/lv.json +1 -1
  74. package/locales/mi/LC_MESSAGES/volto.po +20 -0
  75. package/locales/mi.json +1 -1
  76. package/locales/mk/LC_MESSAGES/volto.po +20 -0
  77. package/locales/mk.json +1 -1
  78. package/locales/my/LC_MESSAGES/volto.po +20 -0
  79. package/locales/my.json +1 -1
  80. package/locales/nb_NO/LC_MESSAGES/volto.po +20 -0
  81. package/locales/nb_NO.json +1 -1
  82. package/locales/nl/LC_MESSAGES/volto.po +20 -0
  83. package/locales/nl.json +1 -1
  84. package/locales/nn/LC_MESSAGES/volto.po +20 -0
  85. package/locales/nn.json +1 -1
  86. package/locales/pl/LC_MESSAGES/volto.po +20 -0
  87. package/locales/pl.json +1 -1
  88. package/locales/pt/LC_MESSAGES/volto.po +20 -0
  89. package/locales/pt.json +1 -1
  90. package/locales/pt_BR/LC_MESSAGES/volto.po +20 -0
  91. package/locales/pt_BR.json +1 -1
  92. package/locales/rm/LC_MESSAGES/volto.po +20 -0
  93. package/locales/rm.json +1 -1
  94. package/locales/ro/LC_MESSAGES/volto.po +20 -0
  95. package/locales/ro.json +1 -1
  96. package/locales/ru/LC_MESSAGES/volto.po +20 -0
  97. package/locales/ru.json +1 -1
  98. package/locales/sk/LC_MESSAGES/volto.po +20 -0
  99. package/locales/sk.json +1 -1
  100. package/locales/sl/LC_MESSAGES/volto.po +20 -0
  101. package/locales/sl.json +1 -1
  102. package/locales/sm/LC_MESSAGES/volto.po +20 -0
  103. package/locales/sm.json +1 -1
  104. package/locales/sq/LC_MESSAGES/volto.po +20 -0
  105. package/locales/sq.json +1 -1
  106. package/locales/sr/LC_MESSAGES/volto.po +20 -0
  107. package/locales/sr.json +1 -1
  108. package/locales/sr@cyrl/LC_MESSAGES/volto.po +20 -0
  109. package/locales/sr@cyrl.json +1 -1
  110. package/locales/sr@latn/LC_MESSAGES/volto.po +20 -0
  111. package/locales/sr@latn.json +1 -1
  112. package/locales/sv/LC_MESSAGES/volto.po +20 -0
  113. package/locales/sv.json +1 -1
  114. package/locales/ta/LC_MESSAGES/volto.po +20 -0
  115. package/locales/ta.json +1 -1
  116. package/locales/te/LC_MESSAGES/volto.po +20 -0
  117. package/locales/te.json +1 -1
  118. package/locales/th/LC_MESSAGES/volto.po +20 -0
  119. package/locales/th.json +1 -1
  120. package/locales/to/LC_MESSAGES/volto.po +20 -0
  121. package/locales/to.json +1 -1
  122. package/locales/tr/LC_MESSAGES/volto.po +20 -0
  123. package/locales/tr.json +1 -1
  124. package/locales/uk/LC_MESSAGES/volto.po +20 -0
  125. package/locales/uk.json +1 -1
  126. package/locales/vi/LC_MESSAGES/volto.po +20 -0
  127. package/locales/vi.json +1 -1
  128. package/locales/volto.pot +21 -1
  129. package/locales/zh_CN/LC_MESSAGES/volto.po +20 -0
  130. package/locales/zh_CN.json +1 -1
  131. package/locales/zh_Hant/LC_MESSAGES/volto.po +20 -0
  132. package/locales/zh_Hant.json +1 -1
  133. package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +20 -0
  134. package/locales/zh_Hant_HK.json +1 -1
  135. package/package.json +6 -5
  136. package/src/components/manage/Blocks/Block/BlocksForm.jsx +10 -7
  137. package/src/components/manage/Blocks/Block/BlocksForm.test.jsx +2 -9
  138. package/src/components/manage/Blocks/Block/EditBlockWrapper.jsx +10 -1
  139. package/src/components/manage/Blocks/Block/Order/Item.jsx +9 -3
  140. package/src/components/manage/Blocks/Block/Order/Order.jsx +116 -67
  141. package/src/components/manage/Blocks/Block/Order/utilities.js +28 -11
  142. package/src/components/manage/Blocks/Listing/Edit.jsx +1 -0
  143. package/src/components/manage/Controlpanels/ContentTypeSchema.jsx +1 -1
  144. package/src/components/manage/Sharing/Sharing.jsx +10 -12
  145. package/src/components/manage/Sidebar/ObjectBrowserBody.jsx +5 -1
  146. package/src/components/manage/UniversalLink/UniversalLink.test.jsx +16 -0
  147. package/src/components/manage/UniversalLink/UniversalLink.tsx +1 -0
  148. package/src/components/theme/App/App.jsx +3 -1
  149. package/src/components/theme/ConnectionRefused/ConnectionRefused.jsx +3 -2
  150. package/src/components/theme/PasswordReset/PasswordReset.jsx +108 -191
  151. package/src/components/theme/View/RenderBlocks.jsx +8 -10
  152. package/src/components/theme/View/RenderBlocks.test.jsx +14 -4
  153. package/src/config/index.js +1 -1
  154. package/src/config/validation.ts +8 -0
  155. package/src/helpers/Blocks/Blocks.js +109 -24
  156. package/src/helpers/Blocks/Blocks.test.js +100 -0
  157. package/src/helpers/FormValidation/FormValidation.test.js +47 -0
  158. package/src/helpers/FormValidation/validators.ts +37 -4
  159. package/src/helpers/MessageLabels/MessageLabels.js +5 -0
  160. package/types/components/manage/Blocks/Block/Order/utilities.d.ts +2 -1
  161. package/types/components/theme/ConnectionRefused/ConnectionRefused.d.ts +2 -2
  162. package/types/components/theme/PasswordReset/PasswordReset.d.ts +6 -2
  163. package/types/config/Views.d.ts +1 -1
  164. package/types/helpers/Blocks/Blocks.d.ts +4 -0
  165. package/types/helpers/FormValidation/validators.d.ts +18 -1
  166. package/types/helpers/MessageLabels/MessageLabels.d.ts +100 -94
  167. package/types/routes.d.ts +7 -5
@@ -7,6 +7,7 @@ import {
7
7
  getBlocks,
8
8
  getBlocksFieldname,
9
9
  getBlocksLayoutFieldname,
10
+ getInvalidBlockLayoutIds,
10
11
  hasBlocksData,
11
12
  insertBlock,
12
13
  moveBlock,
@@ -482,6 +483,105 @@ describe('Blocks', () => {
482
483
  ['a', { value: 1 }],
483
484
  ]);
484
485
  });
486
+
487
+ it('filters out invalid block IDs (null, undefined, string "undefined")', () => {
488
+ const validBlock = { '@type': 'search', value: 'test' };
489
+ const result = getBlocks({
490
+ blocks: {
491
+ 'valid-id-123': validBlock,
492
+ // These shouldn't exist but test edge case
493
+ [null]: { '@type': 'invalid' },
494
+ [undefined]: { '@type': 'invalid' },
495
+ undefined: { '@type': 'invalid' },
496
+ },
497
+ blocks_layout: {
498
+ items: [
499
+ 'valid-id-123',
500
+ null, // Invalid: null ID
501
+ undefined, // Invalid: undefined ID
502
+ 'undefined', // Invalid: string "undefined"
503
+ 'missing-block', // Valid ID but block doesn't exist (filtered by block !== undefined)
504
+ ],
505
+ },
506
+ });
507
+
508
+ // Should only return the valid block, filtering out:
509
+ // - null ID
510
+ // - undefined ID
511
+ // - string "undefined" ID
512
+ // - missing block (block is undefined)
513
+ expect(result).toStrictEqual([['valid-id-123', validBlock]]);
514
+ expect(result.length).toBe(1);
515
+
516
+ // Verify no invalid IDs in the result
517
+ const ids = result.map(([id]) => id);
518
+ expect(ids).not.toContain(null);
519
+ expect(ids).not.toContain(undefined);
520
+ expect(ids).not.toContain('undefined');
521
+ });
522
+
523
+ it('filters out invalid block IDs even when blocks object has invalid keys', () => {
524
+ // Simulate edge case where blocks object has invalid keys
525
+ const blocks = {
526
+ 'valid-id': { '@type': 'search' },
527
+ };
528
+ // JavaScript allows this, creating string keys
529
+ blocks[null] = { '@type': 'invalid' };
530
+ blocks[undefined] = { '@type': 'invalid' };
531
+
532
+ const result = getBlocks({
533
+ blocks,
534
+ blocks_layout: {
535
+ items: ['valid-id', null, undefined, 'undefined'],
536
+ },
537
+ });
538
+
539
+ // Should only return valid block
540
+ expect(result).toStrictEqual([['valid-id', { '@type': 'search' }]]);
541
+ expect(result.length).toBe(1);
542
+ });
543
+ });
544
+
545
+ describe('getInvalidBlockLayoutIds', () => {
546
+ it('returns layout IDs that are valid but have no block data', () => {
547
+ const result = getInvalidBlockLayoutIds({
548
+ blocks: {
549
+ a: { '@type': 'custom', text: 'a' },
550
+ b: { '@type': 'custom', text: 'b' },
551
+ },
552
+ blocks_layout: {
553
+ items: ['a', 'b', 'MISSING-1', 'MISSING-2'],
554
+ },
555
+ });
556
+ expect(result).toStrictEqual(['MISSING-1', 'MISSING-2']);
557
+ });
558
+
559
+ it('returns empty when all layout items have block data', () => {
560
+ const result = getInvalidBlockLayoutIds({
561
+ blocks: { a: { '@type': 'custom' }, b: { '@type': 'custom' } },
562
+ blocks_layout: { items: ['a', 'b'] },
563
+ });
564
+ expect(result).toStrictEqual([]);
565
+ });
566
+
567
+ it('filters out invalid IDs (null, undefined, "undefined")', () => {
568
+ const result = getInvalidBlockLayoutIds({
569
+ blocks: {},
570
+ blocks_layout: {
571
+ items: [null, undefined, 'undefined', 'valid-missing'],
572
+ },
573
+ });
574
+ expect(result).toStrictEqual(['valid-missing']);
575
+ });
576
+
577
+ it('returns empty when items is missing or empty', () => {
578
+ expect(
579
+ getInvalidBlockLayoutIds({ blocks: {}, blocks_layout: {} }),
580
+ ).toStrictEqual([]);
581
+ expect(
582
+ getInvalidBlockLayoutIds({ blocks: {}, blocks_layout: { items: [] } }),
583
+ ).toStrictEqual([]);
584
+ });
485
585
  });
486
586
 
487
587
  describe('addBlock', () => {
@@ -8,6 +8,18 @@ const schema = {
8
8
  username: { title: 'Username', type: 'string', description: '' },
9
9
  email: { title: 'Email', type: 'string', widget: 'email', description: '' },
10
10
  url: { title: 'url', type: 'string', widget: 'url', description: '' },
11
+ file: {
12
+ title: 'file',
13
+ type: 'object',
14
+ description: '',
15
+ size: 5,
16
+ },
17
+ notafile: {
18
+ title: 'notafile',
19
+ type: 'object',
20
+ description: '',
21
+ whatever: 1,
22
+ },
11
23
  },
12
24
  fieldsets: [
13
25
  { id: 'default', title: 'FIXME: User Data', fields: ['username'] },
@@ -181,6 +193,41 @@ describe('FormValidation', () => {
181
193
  ).toEqual({});
182
194
  });
183
195
 
196
+ it('file - validates invalid size', () => {
197
+ const validationErrorMessages = [messages.maxSize.defaultMessage];
198
+ validationErrorMessages.title = 'file';
199
+
200
+ expect(
201
+ FormValidation.validateFieldsPerFieldset({
202
+ schema,
203
+ formData: { ...formData, file: { size: 10 } },
204
+ formatMessage,
205
+ }),
206
+ ).toEqual({
207
+ file: validationErrorMessages,
208
+ });
209
+ });
210
+
211
+ it('file - validates size', () => {
212
+ expect(
213
+ FormValidation.validateFieldsPerFieldset({
214
+ schema,
215
+ formData: { ...formData, file: { size: 1 } },
216
+ formatMessage,
217
+ }),
218
+ ).toEqual({});
219
+ });
220
+
221
+ it('notafile - the size validator does nothing', () => {
222
+ expect(
223
+ FormValidation.validateFieldsPerFieldset({
224
+ schema,
225
+ formData: { ...formData, notafile: { whatever: 1 } },
226
+ formatMessage,
227
+ }),
228
+ ).toEqual({});
229
+ });
230
+
184
231
  it('widget - validator from block - Fails', () => {
185
232
  let newSchema = {
186
233
  properties: {
@@ -1,5 +1,6 @@
1
1
  import { validationMessage } from '@plone/volto/helpers/FormValidation/FormValidation';
2
2
  import { messages } from '@plone/volto/helpers/MessageLabels/MessageLabels';
3
+ import config from '@plone/volto/registry';
3
4
 
4
5
  type MinMaxValidator = {
5
6
  value: string | number;
@@ -15,6 +16,24 @@ type Validator = {
15
16
  formatMessage: Function;
16
17
  };
17
18
 
19
+ type Choice = {
20
+ token: string;
21
+ label: string;
22
+ };
23
+ type ChoiceValidator = {
24
+ value: string | Choice;
25
+ field: Record<string, any>;
26
+ formData: any;
27
+ formatMessage: Function;
28
+ };
29
+
30
+ type FileValidator = {
31
+ value: Record<string, any>;
32
+ field: Record<string, any>;
33
+ formData: any;
34
+ formatMessage: Function;
35
+ };
36
+
18
37
  export const isMaxPropertyValid = ({
19
38
  value,
20
39
  fieldSpec,
@@ -211,12 +230,26 @@ export const defaultLanguageControlPanelValidator = ({
211
230
  value,
212
231
  formData,
213
232
  formatMessage,
214
- }: Validator) => {
233
+ }: ChoiceValidator) => {
234
+ const token = typeof value === 'object' ? value.token : value;
215
235
  const isValid =
216
- value &&
236
+ token &&
217
237
  (formData.available_languages.find(
218
- (lang: { token: string }) => lang.token === value,
238
+ (lang: { token: string }) => lang.token === token,
219
239
  ) ||
220
- formData.available_languages.includes(value));
240
+ formData.available_languages.includes(token));
221
241
  return !isValid ? formatMessage(messages.defaultLanguage) : null;
222
242
  };
243
+
244
+ export const sizeValidator = ({
245
+ value,
246
+ field,
247
+ formatMessage,
248
+ }: FileValidator) => {
249
+ const maxSize = field.size
250
+ ? parseInt(field.size, 10)
251
+ : config.settings.maxFileUploadSize;
252
+ return maxSize && value.size > maxSize
253
+ ? formatMessage(messages.maxSize, { maxSize, size: value.size })
254
+ : null;
255
+ };
@@ -25,6 +25,11 @@ export const messages = defineMessages({
25
25
  id: 'Maximum value is {len}.',
26
26
  defaultMessage: 'Maximum value is {len}.',
27
27
  },
28
+ maxSize: {
29
+ id: 'Maximum file size is {maxSize} bytes, but the uploaded file is {size} bytes.',
30
+ defaultMessage:
31
+ 'Maximum file size is {maxSize} bytes, but the uploaded file is {size} bytes.',
32
+ },
28
33
  uniqueItems: {
29
34
  id: 'Items must be unique.',
30
35
  defaultMessage: 'Items must be unique.',
@@ -1,8 +1,9 @@
1
1
  export function getProjection(items: any, activeId: any, overId: any, dragOffset: any, indentationWidth: any, arrayMove: any): {
2
+ parentId: any;
3
+ parentType: any;
2
4
  depth: any;
3
5
  maxDepth: any;
4
6
  minDepth: any;
5
- parentId: any;
6
7
  };
7
8
  export function flattenTree(items: any): any;
8
9
  export function findItem(items: any, itemId: any): any;
@@ -1,2 +1,2 @@
1
- export default ConnectionRefused;
2
- declare function ConnectionRefused(): import("react/jsx-runtime").JSX.Element;
1
+ declare const _default: (props: any) => import("react/jsx-runtime").JSX.Element;
2
+ export default _default;
@@ -1,2 +1,6 @@
1
- declare const _default: any;
2
- export default _default;
1
+ export default PasswordReset;
2
+ /**
3
+ * @function PasswordReset
4
+ * @returns {JSX.Element}
5
+ */
6
+ declare function PasswordReset(): JSX.Element;
@@ -42,7 +42,7 @@ export const errorViews: {
42
42
  403: (props: any) => import("react/jsx-runtime").JSX.Element;
43
43
  408: () => string;
44
44
  500: (props: any) => import("react/jsx-runtime").JSX.Element;
45
- ECONNREFUSED: () => import("react/jsx-runtime").JSX.Element;
45
+ ECONNREFUSED: (props: any) => import("react/jsx-runtime").JSX.Element;
46
46
  corsError: () => string;
47
47
  };
48
48
  export namespace layoutViewsNamesMapping {
@@ -176,6 +176,7 @@ export function findBlocks(blocks: {}, types: any, result?: any[]): any[];
176
176
  */
177
177
  export function moveBlockEnhanced(formData: any, { source, destination }: number): any;
178
178
  export function getBlocks(properties: any): any[];
179
+ export function getInvalidBlockLayoutIds(properties: any): string[];
179
180
  export function applyBlockInitialValue({ id, value, blocksConfig, formData, intl, }: {
180
181
  id: any;
181
182
  value: any;
@@ -193,3 +194,6 @@ export function getBlocksHierarchy(properties: any): any;
193
194
  export function findContainer(formData: object, { containerId }: {
194
195
  containerId: string;
195
196
  }): object | undefined;
197
+ export function findParent(formData: object, { blockId }: {
198
+ blockId: string;
199
+ }): object | undefined;
@@ -10,6 +10,22 @@ type Validator = {
10
10
  formData: any;
11
11
  formatMessage: Function;
12
12
  };
13
+ type Choice = {
14
+ token: string;
15
+ label: string;
16
+ };
17
+ type ChoiceValidator = {
18
+ value: string | Choice;
19
+ field: Record<string, any>;
20
+ formData: any;
21
+ formatMessage: Function;
22
+ };
23
+ type FileValidator = {
24
+ value: Record<string, any>;
25
+ field: Record<string, any>;
26
+ formData: any;
27
+ formatMessage: Function;
28
+ };
13
29
  export declare const isMaxPropertyValid: ({ value, fieldSpec, criterion, formatMessage, }: MinMaxValidator) => any;
14
30
  export declare const isMinPropertyValid: ({ value, fieldSpec, criterion, formatMessage, }: MinMaxValidator) => any;
15
31
  export declare const minLengthValidator: ({ value, field, formatMessage, }: Validator) => any;
@@ -26,5 +42,6 @@ export declare const endEventDateRangeValidator: ({ value, field, formData, form
26
42
  export declare const patternValidator: ({ value, field, formatMessage, }: Validator) => any;
27
43
  export declare const maxItemsValidator: ({ value, field, formatMessage, }: Validator) => any;
28
44
  export declare const minItemsValidator: ({ value, field, formatMessage, }: Validator) => any;
29
- export declare const defaultLanguageControlPanelValidator: ({ value, formData, formatMessage, }: Validator) => any;
45
+ export declare const defaultLanguageControlPanelValidator: ({ value, formData, formatMessage, }: ChoiceValidator) => any;
46
+ export declare const sizeValidator: ({ value, field, formatMessage, }: FileValidator) => any;
30
47
  export {};