@plone/volto 19.0.0-alpha.2 → 19.0.0-alpha.4

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 (153) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +0 -2
  3. package/cypress/downloads/downloads.html +0 -0
  4. package/cypress/support/commands.js +5 -0
  5. package/locales/af.json +1 -0
  6. package/locales/ar.json +1 -0
  7. package/locales/bg.json +1 -0
  8. package/locales/bn.json +1 -0
  9. package/locales/ca/LC_MESSAGES/volto.po +44 -10
  10. package/locales/ca.json +1 -1
  11. package/locales/cs.json +1 -0
  12. package/locales/cy.json +1 -0
  13. package/locales/da.json +1 -0
  14. package/locales/de/LC_MESSAGES/volto.po +52 -18
  15. package/locales/de.json +1 -1
  16. package/locales/el.json +1 -0
  17. package/locales/en/LC_MESSAGES/volto.po +45 -11
  18. package/locales/en.json +1 -1
  19. package/locales/en_AU.json +1 -0
  20. package/locales/en_GB.json +1 -0
  21. package/locales/eo.json +1 -0
  22. package/locales/es/LC_MESSAGES/volto.po +45 -11
  23. package/locales/es.json +1 -1
  24. package/locales/et.json +1 -0
  25. package/locales/eu/LC_MESSAGES/volto.po +44 -10
  26. package/locales/eu.json +1 -1
  27. package/locales/fa.json +1 -0
  28. package/locales/fi/LC_MESSAGES/volto.po +44 -10
  29. package/locales/fi.json +1 -1
  30. package/locales/fr/LC_MESSAGES/volto.po +45 -11
  31. package/locales/fr.json +1 -1
  32. package/locales/fu.json +1 -0
  33. package/locales/gl.json +1 -0
  34. package/locales/he.json +1 -0
  35. package/locales/hi/LC_MESSAGES/volto.po +48 -14
  36. package/locales/hi.json +1 -1
  37. package/locales/hr.json +1 -0
  38. package/locales/hu.json +1 -0
  39. package/locales/hy.json +1 -0
  40. package/locales/id.json +1 -0
  41. package/locales/it/LC_MESSAGES/volto.po +48 -14
  42. package/locales/it.json +1 -1
  43. package/locales/ja/LC_MESSAGES/volto.po +44 -10
  44. package/locales/ja.json +1 -1
  45. package/locales/ka.json +1 -0
  46. package/locales/kn.json +1 -0
  47. package/locales/ko.json +1 -0
  48. package/locales/lt.json +1 -0
  49. package/locales/lv.json +1 -0
  50. package/locales/mi.json +1 -0
  51. package/locales/mk.json +1 -0
  52. package/locales/my.json +1 -0
  53. package/locales/nb_NO.json +1 -0
  54. package/locales/nl/LC_MESSAGES/volto.po +48 -14
  55. package/locales/nl.json +1 -1
  56. package/locales/nn.json +1 -0
  57. package/locales/pl.json +1 -0
  58. package/locales/pt/LC_MESSAGES/volto.po +44 -10
  59. package/locales/pt.json +1 -1
  60. package/locales/pt_BR/LC_MESSAGES/volto.po +59 -25
  61. package/locales/pt_BR.json +1 -1
  62. package/locales/rm.json +1 -0
  63. package/locales/ro/LC_MESSAGES/volto.po +48 -14
  64. package/locales/ro.json +1 -1
  65. package/locales/ru/LC_MESSAGES/volto.po +48 -14
  66. package/locales/ru.json +1 -1
  67. package/locales/sk.json +1 -0
  68. package/locales/sl.json +1 -0
  69. package/locales/sm.json +1 -0
  70. package/locales/sq.json +1 -0
  71. package/locales/sr.json +1 -0
  72. package/locales/sr@cyrl.json +1 -0
  73. package/locales/sr@latn.json +1 -0
  74. package/locales/sv.json +1 -1
  75. package/locales/ta.json +1 -0
  76. package/locales/te.json +1 -0
  77. package/locales/th.json +1 -0
  78. package/locales/to.json +1 -0
  79. package/locales/tr.json +1 -0
  80. package/locales/uk.json +1 -0
  81. package/locales/vi.json +1 -0
  82. package/locales/volto.pot +45 -11
  83. package/locales/zh_CN/LC_MESSAGES/volto.po +44 -10
  84. package/locales/zh_CN.json +1 -1
  85. package/locales/zh_Hant.json +1 -0
  86. package/locales/zh_Hant_HK.json +1 -0
  87. package/package.json +9 -9
  88. package/src/actions/content/content.js +0 -1
  89. package/src/actions/controlpanels/controlpanels.js +13 -7
  90. package/src/actions/controlpanels/controlpanels.test.js +11 -5
  91. package/src/actions/users/users.js +2 -2
  92. package/src/components/manage/Add/Add.jsx +5 -6
  93. package/src/components/manage/Blocks/Block/Edit.jsx +1 -0
  94. package/src/components/manage/Blocks/Teaser/schema.js +8 -3
  95. package/src/components/manage/Contents/Contents.jsx +3 -0
  96. package/src/components/manage/Contents/Contents.test.jsx +7 -0
  97. package/src/components/manage/Contents/ContentsBreadcrumbs.Multilingual.test.jsx +18 -5
  98. package/src/components/manage/Contents/ContentsBreadcrumbs.jsx +20 -26
  99. package/src/components/manage/Contents/ContentsBreadcrumbs.test.jsx +14 -0
  100. package/src/components/manage/Contents/ContentsDeleteModal.jsx +258 -206
  101. package/src/components/manage/Contents/ContentsDeleteModal.stories.jsx +26 -8
  102. package/src/components/manage/Contents/ContentsItem.jsx +10 -2
  103. package/src/components/manage/Contents/ContentsUploadModal.test.jsx +13 -22
  104. package/src/components/manage/Edit/Edit.jsx +2 -3
  105. package/src/components/manage/Multilingual/CompareLanguages.jsx +2 -5
  106. package/src/components/manage/Multilingual/ManageTranslations.jsx +4 -2
  107. package/src/components/manage/Multilingual/ManageTranslations.test.jsx +5 -1
  108. package/src/components/manage/Multilingual/TranslationObject.jsx +1 -1
  109. package/src/components/manage/Toolbar/More.jsx +4 -1
  110. package/src/components/manage/Toolbar/More.test.jsx +3 -0
  111. package/src/components/manage/Toolbar/Toolbar.jsx +3 -4
  112. package/src/components/manage/Toolbar/Types.jsx +7 -7
  113. package/src/components/manage/UniversalLink/UniversalLink.tsx +1 -0
  114. package/src/components/manage/Widgets/RegistryImageWidget.test.jsx +1 -1
  115. package/src/components/manage/Widgets/SelectAutoComplete.jsx +29 -12
  116. package/src/components/theme/AlternateHrefLangs/AlternateHrefLangs.jsx +1 -3
  117. package/src/components/theme/AlternateHrefLangs/AlternateHrefLangs.test.jsx +0 -4
  118. package/src/components/theme/App/App.jsx +3 -1
  119. package/src/components/theme/App/App.test.jsx +1 -0
  120. package/src/components/theme/FormattedDate/FormattedDate.stories.jsx +20 -2
  121. package/src/components/theme/LanguageSelector/LanguageSelector.jsx +9 -7
  122. package/src/components/theme/LanguageSelector/LanguageSelector.test.jsx +6 -6
  123. package/src/components/theme/Logo/Logo.Multilingual.test.jsx +0 -5
  124. package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +8 -12
  125. package/src/components/theme/MultilingualRedirector/MultilingualRedirector.test.jsx +3 -5
  126. package/src/components/theme/Navigation/NavItem.jsx +1 -5
  127. package/src/components/theme/Navigation/Navigation.Multilingual.test.jsx +0 -5
  128. package/src/components/theme/NotFound/NotFound.jsx +5 -2
  129. package/src/components/theme/NotFound/NotFound.test.jsx +3 -0
  130. package/src/components/theme/Sitemap/Sitemap.jsx +6 -5
  131. package/src/components/theme/Sitemap/Sitemap.test.jsx +0 -1
  132. package/src/components/theme/View/FileView.jsx +9 -1
  133. package/src/config/ControlPanels.js +1 -0
  134. package/src/config/index.js +6 -15
  135. package/src/express-middleware/devproxy.js +7 -2
  136. package/src/helpers/Html/Html.jsx +9 -4
  137. package/src/helpers/LanguageMap/LanguageMap.js +115 -8
  138. package/src/helpers/Url/bulkFlattenToAppURL.test.ts +122 -0
  139. package/src/helpers/Url/bulkFlattenToAppURL.ts +24 -0
  140. package/src/middleware/Api.test.js +4 -0
  141. package/src/middleware/api.js +74 -25
  142. package/src/routes.js +1 -1
  143. package/src/server.jsx +5 -6
  144. package/src/start-client.jsx +0 -4
  145. package/test-setup-config.jsx +0 -2
  146. package/theme/themes/pastanaga/extras/blocks.less +7 -0
  147. package/theme/themes/pastanaga/extras/contents.less +5 -5
  148. package/types/components/manage/Blocks/Teaser/schema.d.ts +1 -0
  149. package/types/components/theme/FormattedDate/FormattedDate.stories.d.ts +1 -1
  150. package/types/helpers/LanguageMap/LanguageMap.d.ts +428 -4
  151. package/types/helpers/Url/bulkFlattenToAppURL.d.ts +5 -0
  152. package/types/middleware/api.d.ts +6 -9
  153. package/src/actions/content/content.multilingual.test.js +0 -17
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from 'react';
1
+ import React, { useState, useEffect, useMemo } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useDispatch, useSelector } from 'react-redux';
4
4
  import { Link } from 'react-router-dom';
@@ -10,6 +10,8 @@ import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
10
10
 
11
11
  import { Confirm, Dimmer, Loader, Table } from 'semantic-ui-react';
12
12
 
13
+ const MAX_LINK_INTEGRITY_BREACHES_TO_SHOW = 5;
14
+
13
15
  const messages = defineMessages({
14
16
  deleteConfirmSingleItem: {
15
17
  id: 'Delete this item?',
@@ -39,22 +41,168 @@ const messages = defineMessages({
39
41
  id: 'Cancel',
40
42
  defaultMessage: 'Cancel',
41
43
  },
44
+ item: {
45
+ id: 'item',
46
+ defaultMessage: 'item',
47
+ },
48
+ items: {
49
+ id: 'items',
50
+ defaultMessage: 'items',
51
+ },
52
+ reference: {
53
+ id: 'reference',
54
+ defaultMessage: 'reference',
55
+ },
56
+ references: {
57
+ id: 'references',
58
+ defaultMessage: 'references',
59
+ },
60
+ folderDeletionSingle: {
61
+ id: 'This item contains subitems. Deleting it will also delete its {containedItemsToDelete} {variation} inside.',
62
+ defaultMessage:
63
+ 'This item contains subitems. Deleting it will also delete its {containedItemsToDelete} {variation} inside.',
64
+ },
65
+ folderDeletionMultiple: {
66
+ id: 'Some items contain subitems. Deleting them will also delete their {containedItemsToDelete} {variation} inside.',
67
+ defaultMessage:
68
+ 'Some items contain subitems. Deleting them will also delete their {containedItemsToDelete} {variation} inside.',
69
+ },
70
+ deleteAllItemsPaginated: {
71
+ id: 'You are about to delete all items in the current pagination of this folder.',
72
+ defaultMessage:
73
+ 'You are about to delete all items in the current pagination of this folder.',
74
+ },
75
+ deleteAllItems: {
76
+ id: 'You are about to delete all items and all its subitems.',
77
+ defaultMessage: 'You are about to delete all items and all its subitems.',
78
+ },
79
+ brokenReferencesMultiple: {
80
+ id: 'Some items are referenced by other contents. Deleting them will break {brokenReferences} {variation}.',
81
+ defaultMessage:
82
+ 'Some items are referenced by other contents. Deleting them will break {brokenReferences} {variation}.',
83
+ },
84
+ brokenReferencesSingle: {
85
+ id: 'Deleting this item will break {brokenReferences} {variation}.',
86
+ defaultMessage:
87
+ 'Deleting this item will break {brokenReferences} {variation}.',
88
+ },
42
89
  });
43
90
 
91
+ const safeUrl = (url) => {
92
+ if (!url) return '#';
93
+ return typeof url === 'string' ? flattenToAppURL(url) : '#';
94
+ };
95
+
96
+ const DeleteItemsList = ({ itemsToDelete, titlesToDelete }) => (
97
+ <ul>
98
+ {itemsToDelete.map((id) => (
99
+ <li key={id}>
100
+ <Link to={safeUrl(id)} target="_blank">
101
+ {titlesToDelete[id] || id}
102
+ </Link>
103
+ </li>
104
+ ))}
105
+ </ul>
106
+ );
107
+
108
+ const VariationMessage = ({ count, singularId, pluralId }) => (
109
+ <span>
110
+ {count === 1 ? (
111
+ <FormattedMessage {...messages[singularId]} />
112
+ ) : (
113
+ <FormattedMessage {...messages[pluralId]} />
114
+ )}
115
+ </span>
116
+ );
117
+
118
+ const DeleteAllMessage = ({ hasMultiplePages }) => (
119
+ <p>
120
+ <FormattedMessage
121
+ {...messages[
122
+ hasMultiplePages ? 'deleteAllItemsPaginated' : 'deleteAllItems'
123
+ ]}
124
+ />
125
+ </p>
126
+ );
127
+
128
+ const DeleteMessage = ({
129
+ isMultiple,
130
+ containedItemsToDelete,
131
+ brokenReferences,
132
+ breaches,
133
+ itemsToDelete,
134
+ linksAndReferencesViewLink,
135
+ }) => {
136
+ const intl = useIntl();
137
+ const showFolderMessage = containedItemsToDelete > 0;
138
+ const showBreachesMessage = brokenReferences > 0;
139
+
140
+ return (
141
+ <>
142
+ {showFolderMessage && (
143
+ <p>
144
+ <FormattedMessage
145
+ {...messages[
146
+ isMultiple ? 'folderDeletionMultiple' : 'folderDeletionSingle'
147
+ ]}
148
+ values={{
149
+ containedItemsToDelete: <span>{containedItemsToDelete}</span>,
150
+ variation: (
151
+ <VariationMessage
152
+ count={containedItemsToDelete}
153
+ singularId="item"
154
+ pluralId="items"
155
+ />
156
+ ),
157
+ }}
158
+ />
159
+ </p>
160
+ )}
161
+ {showBreachesMessage && (
162
+ <BrokenLinksList
163
+ intl={intl}
164
+ breaches={breaches}
165
+ brokenReferences={brokenReferences}
166
+ isMultiple={isMultiple}
167
+ itemsToDelete={itemsToDelete}
168
+ linksAndReferencesViewLink={!isMultiple && linksAndReferencesViewLink}
169
+ />
170
+ )}
171
+ </>
172
+ );
173
+ };
174
+
44
175
  const ContentsDeleteModal = (props) => {
45
- const { itemsToDelete = [], open, onCancel, onOk, items } = props;
176
+ const {
177
+ itemsToDelete = [],
178
+ open,
179
+ onCancel,
180
+ onOk,
181
+ items,
182
+ hasMultiplePages,
183
+ } = props;
46
184
  const intl = useIntl();
47
185
  const dispatch = useDispatch();
48
186
  const linkintegrityInfo = useSelector((state) => state.linkIntegrity?.result);
49
187
  const loading = useSelector((state) => state.linkIntegrity?.loading);
50
188
 
51
189
  const [brokenReferences, setBrokenReferences] = useState(0);
52
- const [containedItemsToDelete, setContainedItemsToDelete] = useState([]);
190
+ const [containedItemsToDelete, setContainedItemsToDelete] = useState(0);
53
191
  const [breaches, setBreaches] = useState([]);
54
192
 
55
193
  const [linksAndReferencesViewLink, setLinkAndReferencesViewLink] =
56
194
  useState(null);
57
195
 
196
+ const titlesToDelete = useMemo(
197
+ () =>
198
+ itemsToDelete.reduce((acc, id) => {
199
+ const item = items.find((item) => item['@id'] === id);
200
+ acc[id] = item ? item.Title : null;
201
+ return acc;
202
+ }, {}),
203
+ [itemsToDelete, items],
204
+ );
205
+
58
206
  useEffect(() => {
59
207
  const getFieldById = (id, field) => {
60
208
  const item = find(items, { '@id': id });
@@ -72,20 +220,34 @@ const ContentsDeleteModal = (props) => {
72
220
 
73
221
  useEffect(() => {
74
222
  if (linkintegrityInfo) {
75
- const containedItems = linkintegrityInfo
223
+ // Always set the total number of contained items, regardless of breaches
224
+ const totalContainedItems = linkintegrityInfo
76
225
  .map((result) => result.items_total ?? 0)
77
226
  .reduce((acc, value) => acc + value, 0);
227
+ setContainedItemsToDelete(totalContainedItems); // <-- always set
78
228
  const breaches = linkintegrityInfo.flatMap((result) =>
79
229
  result.breaches.map((source) => ({
80
230
  source: source,
81
231
  target: result,
82
232
  })),
83
233
  );
84
- const source_by_uid = breaches.reduce(
234
+ // Filter out breaches where breach.source is to be deleted
235
+ const filteredBreaches = breaches.filter(
236
+ (breach) =>
237
+ !itemsToDelete.some((item) => breach.source['@id'].endsWith(item)),
238
+ );
239
+ // If no breaches are found, return early
240
+ if (filteredBreaches.length === 0) {
241
+ setBrokenReferences(0);
242
+ setLinkAndReferencesViewLink(null);
243
+ setBreaches([]);
244
+ return;
245
+ }
246
+ const source_by_uid = filteredBreaches.reduce(
85
247
  (acc, value) => acc.set(value.source.uid, value.source),
86
248
  new Map(),
87
249
  );
88
- const by_source = breaches.reduce((acc, value) => {
250
+ const by_source = filteredBreaches.reduce((acc, value) => {
89
251
  if (acc.get(value.source.uid) === undefined) {
90
252
  acc.set(value.source.uid, new Set());
91
253
  }
@@ -93,7 +255,6 @@ const ContentsDeleteModal = (props) => {
93
255
  return acc;
94
256
  }, new Map());
95
257
 
96
- setContainedItemsToDelete(containedItems);
97
258
  setBrokenReferences(by_source.size);
98
259
  setLinkAndReferencesViewLink(
99
260
  linkintegrityInfo.length
@@ -107,12 +268,12 @@ const ContentsDeleteModal = (props) => {
107
268
  })),
108
269
  );
109
270
  } else {
110
- setContainedItemsToDelete([]);
271
+ setContainedItemsToDelete(0);
111
272
  setBrokenReferences(0);
112
273
  setLinkAndReferencesViewLink(null);
113
274
  setBreaches([]);
114
275
  }
115
- }, [linkintegrityInfo]);
276
+ }, [itemsToDelete, linkintegrityInfo]);
116
277
 
117
278
  return (
118
279
  open && (
@@ -137,172 +298,24 @@ const ContentsDeleteModal = (props) => {
137
298
  </Loader>
138
299
  </Dimmer>
139
300
 
140
- {itemsToDelete.length > 1 ? (
141
- containedItemsToDelete > 0 ? (
142
- <>
143
- <FormattedMessage
144
- id="Some items are also a folder. By deleting them you will delete {containedItemsToDelete} {variation} inside the folders."
145
- defaultMessage="Some items are also a folder. By deleting them you will delete {containedItemsToDelete} {variation} inside the folders."
146
- values={{
147
- containedItemsToDelete: (
148
- <span>{containedItemsToDelete}</span>
149
- ),
150
- variation: (
151
- <span>
152
- {containedItemsToDelete === 1 ? (
153
- <FormattedMessage id="item" defaultMessage="item" />
154
- ) : (
155
- <FormattedMessage
156
- id="items"
157
- defaultMessage="items"
158
- />
159
- )}
160
- </span>
161
- ),
162
- }}
163
- />
164
- {brokenReferences > 0 && (
165
- <>
166
- <br />
167
- <FormattedMessage
168
- id="Some items are referenced by other contents. By deleting them {brokenReferences} {variation} will be broken."
169
- defaultMessage="Some items are referenced by other contents. By deleting them {brokenReferences} {variation} will be broken."
170
- values={{
171
- brokenReferences: <span>{brokenReferences}</span>,
172
- variation: (
173
- <span>
174
- {brokenReferences === 1 ? (
175
- <FormattedMessage
176
- id="reference"
177
- defaultMessage="reference"
178
- />
179
- ) : (
180
- <FormattedMessage
181
- id="references"
182
- defaultMessage="references"
183
- />
184
- )}
185
- </span>
186
- ),
187
- }}
188
- />
189
- </>
190
- )}
191
- </>
192
- ) : (
193
- <>
194
- {brokenReferences > 0 && (
195
- <>
196
- <FormattedMessage
197
- id="Some items are referenced by other contents. By deleting them {brokenReferences} {variation} will be broken."
198
- defaultMessage="Some items are referenced by other contents. By deleting them {brokenReferences} {variation} will be broken."
199
- values={{
200
- brokenReferences: <span>{brokenReferences}</span>,
201
- variation: (
202
- <span>
203
- {brokenReferences === 1 ? (
204
- <FormattedMessage
205
- id="reference"
206
- defaultMessage="reference"
207
- />
208
- ) : (
209
- <FormattedMessage
210
- id="references"
211
- defaultMessage="references"
212
- />
213
- )}
214
- </span>
215
- ),
216
- }}
217
- />
218
- </>
219
- )}
220
- </>
221
- )
222
- ) : containedItemsToDelete > 0 ? (
223
- <>
224
- <FormattedMessage
225
- id="This item is also a folder. By deleting it you will delete {containedItemsToDelete} {variation} inside the folder."
226
- defaultMessage="This item is also a folder. By deleting it you will delete {containedItemsToDelete} {variation} inside the folder."
227
- values={{
228
- containedItemsToDelete: (
229
- <span>{containedItemsToDelete}</span>
230
- ),
231
- variation: (
232
- <span>
233
- {containedItemsToDelete === 1 ? (
234
- <FormattedMessage id="item" defaultMessage="item" />
235
- ) : (
236
- <FormattedMessage id="items" defaultMessage="items" />
237
- )}
238
- </span>
239
- ),
240
- }}
241
- />
242
- {brokenReferences > 0 && (
243
- <>
244
- <br />
245
- <FormattedMessage
246
- id="Deleting this item breaks {brokenReferences} {variation}."
247
- defaultMessage="Deleting this item breaks {brokenReferences} {variation}."
248
- values={{
249
- brokenReferences: <span>{brokenReferences}</span>,
250
- variation: (
251
- <span>
252
- {brokenReferences === 1 ? (
253
- <FormattedMessage
254
- id="reference"
255
- defaultMessage="reference"
256
- />
257
- ) : (
258
- <FormattedMessage
259
- id="references"
260
- defaultMessage="references"
261
- />
262
- )}
263
- </span>
264
- ),
265
- }}
266
- />
267
- <BrokenLinksList
268
- intl={intl}
269
- breaches={breaches}
270
- linksAndReferencesViewLink={linksAndReferencesViewLink}
271
- />
272
- </>
273
- )}
274
- </>
275
- ) : brokenReferences > 0 ? (
276
- <>
277
- <FormattedMessage
278
- id="Deleting this item breaks {brokenReferences} {variation}."
279
- defaultMessage="Deleting this item breaks {brokenReferences} {variation}."
280
- values={{
281
- brokenReferences: <span>{brokenReferences}</span>,
282
- variation: (
283
- <span>
284
- {brokenReferences === 1 ? (
285
- <FormattedMessage
286
- id="reference"
287
- defaultMessage="reference"
288
- />
289
- ) : (
290
- <FormattedMessage
291
- id="references"
292
- defaultMessage="references"
293
- />
294
- )}
295
- </span>
296
- ),
297
- }}
298
- />
299
- <BrokenLinksList
300
- intl={intl}
301
- breaches={breaches}
302
- linksAndReferencesViewLink={linksAndReferencesViewLink}
303
- />
304
- </>
305
- ) : null}
301
+ {itemsToDelete.length > 1 &&
302
+ items.length === itemsToDelete.length ? (
303
+ <DeleteAllMessage hasMultiplePages={hasMultiplePages} />
304
+ ) : (
305
+ <DeleteItemsList
306
+ itemsToDelete={itemsToDelete}
307
+ titlesToDelete={titlesToDelete}
308
+ />
309
+ )}
310
+
311
+ <DeleteMessage
312
+ isMultiple={itemsToDelete.length > 1}
313
+ containedItemsToDelete={containedItemsToDelete}
314
+ brokenReferences={brokenReferences}
315
+ breaches={breaches}
316
+ itemsToDelete={itemsToDelete}
317
+ linksAndReferencesViewLink={linksAndReferencesViewLink}
318
+ />
306
319
  </div>
307
320
  }
308
321
  onCancel={onCancel}
@@ -313,47 +326,86 @@ const ContentsDeleteModal = (props) => {
313
326
  );
314
327
  };
315
328
 
316
- const BrokenLinksList = ({ intl, breaches, linksAndReferencesViewLink }) => {
329
+ const BrokenLinksList = ({
330
+ intl,
331
+ breaches,
332
+ brokenReferences,
333
+ isMultiple,
334
+ itemsToDelete,
335
+ linksAndReferencesViewLink,
336
+ }) => {
317
337
  return (
318
338
  <div className="broken-links-list">
339
+ <FormattedMessage
340
+ {...messages[
341
+ isMultiple ? 'brokenReferencesMultiple' : 'brokenReferencesSingle'
342
+ ]}
343
+ values={{
344
+ brokenReferences: <span>{brokenReferences}</span>,
345
+ variation: (
346
+ <VariationMessage
347
+ count={brokenReferences}
348
+ singularId="reference"
349
+ pluralId="references"
350
+ />
351
+ ),
352
+ }}
353
+ />
354
+ <br />
319
355
  <FormattedMessage
320
356
  id="These items will have broken links"
321
357
  defaultMessage="These items will have broken links"
322
358
  />
323
- :
324
359
  <Table compact>
325
360
  <Table.Body>
326
- {breaches.map((breach) => (
327
- <Table.Row key={breach.source['@id']} verticalAlign="top">
328
- <Table.Cell>
329
- <Link
330
- to={flattenToAppURL(breach.source['@id'])}
331
- title={intl.formatMessage(messages.navigate_to_this_item)}
332
- >
333
- {breach.source.title}
334
- </Link>
335
- </Table.Cell>
336
- <Table.Cell style={{ minWidth: '140px' }}>
337
- <FormattedMessage id="refers to" defaultMessage="refers to" />:
338
- </Table.Cell>
339
- <Table.Cell>
340
- <ul style={{ margin: 0 }}>
341
- {breach.targets.map((target) => (
342
- <li key={target['@id']}>
343
- <Link
344
- to={flattenToAppURL(target['@id'])}
345
- title={intl.formatMessage(
346
- messages.navigate_to_this_item,
347
- )}
348
- >
349
- {target.title}
350
- </Link>
351
- </li>
352
- ))}
353
- </ul>
361
+ {breaches
362
+ .slice(0, MAX_LINK_INTEGRITY_BREACHES_TO_SHOW)
363
+ .map((breach) => (
364
+ <Table.Row key={breach.source['@id']} verticalAlign="top">
365
+ <Table.Cell>
366
+ <Link
367
+ to={flattenToAppURL(breach.source['@id'])}
368
+ title={intl.formatMessage(messages.navigate_to_this_item)}
369
+ >
370
+ {breach.source.title}
371
+ </Link>
372
+ </Table.Cell>
373
+ <Table.Cell style={{ minWidth: '140px' }}>
374
+ <FormattedMessage id="refers to" defaultMessage="refers to" />
375
+ :
376
+ </Table.Cell>
377
+ <Table.Cell>
378
+ <ul style={{ margin: 0 }}>
379
+ {breach.targets.map((target) => (
380
+ <li key={target['@id']}>
381
+ <Link
382
+ to={flattenToAppURL(target['@id'])}
383
+ title={intl.formatMessage(
384
+ messages.navigate_to_this_item,
385
+ )}
386
+ >
387
+ {target.title}
388
+ </Link>
389
+ </li>
390
+ ))}
391
+ </ul>
392
+ </Table.Cell>
393
+ </Table.Row>
394
+ ))}
395
+ {breaches.length > MAX_LINK_INTEGRITY_BREACHES_TO_SHOW && (
396
+ <Table.Row>
397
+ <Table.Cell colSpan="3">
398
+ <FormattedMessage
399
+ id="and {count} more…"
400
+ defaultMessage="and {count} more…"
401
+ values={{
402
+ count:
403
+ breaches.length - MAX_LINK_INTEGRITY_BREACHES_TO_SHOW,
404
+ }}
405
+ />
354
406
  </Table.Cell>
355
407
  </Table.Row>
356
- ))}
408
+ )}
357
409
  </Table.Body>
358
410
  </Table>
359
411
  {linksAndReferencesViewLink && (
@@ -36,11 +36,11 @@ Default.args = {
36
36
  locale: 'en',
37
37
  open: true,
38
38
  items: [
39
- { '@id': '/news', UID: '123', title: 'News' },
40
- { '@id': '/blog', UID: '456', title: 'Blog' },
41
- { '@id': '/extra', UID: '789', title: 'Extra' },
39
+ { '@id': '/news', UID: '123', Title: 'News' },
40
+ { '@id': '/blog', UID: '456', Title: 'Blog' },
41
+ { '@id': '/extra', UID: '789', Title: 'Extra' },
42
42
  ],
43
- itemsToDelete: [{ UID: '456' }],
43
+ itemsToDelete: ['/blog'],
44
44
  linkIntegrityResult: [
45
45
  {
46
46
  '@id': '/blog',
@@ -57,7 +57,7 @@ Default.args = {
57
57
  export const DeleteMoreThanOne = StoryComponent.bind({});
58
58
  DeleteMoreThanOne.args = {
59
59
  ...Default.args,
60
- itemsToDelete: [{ UID: '456' }, { UID: '789' }],
60
+ itemsToDelete: ['/blog', '/extra'],
61
61
  };
62
62
 
63
63
  export const NoContainedItems = StoryComponent.bind({});
@@ -76,7 +76,7 @@ NoContainedItems.args = {
76
76
  export const MultipleLinkIntegrityResults = StoryComponent.bind({});
77
77
  MultipleLinkIntegrityResults.args = {
78
78
  ...Default.args,
79
- itemsToDelete: [{ UID: '456' }, { UID: '789' }],
79
+ itemsToDelete: ['/blog', '/extra'],
80
80
  linkIntegrityResult: [
81
81
  {
82
82
  '@id': '/blog',
@@ -96,7 +96,7 @@ MultipleLinkIntegrityResults.args = {
96
96
  export const MultipleBreaches = StoryComponent.bind({});
97
97
  MultipleBreaches.args = {
98
98
  ...Default.args,
99
- itemsToDelete: [{ UID: '789' }],
99
+ itemsToDelete: ['/extra'],
100
100
  linkIntegrityResult: [
101
101
  {
102
102
  '@id': '/extra',
@@ -128,7 +128,25 @@ export default {
128
128
  ],
129
129
  argTypes: {
130
130
  locale: {
131
- options: ['en', 'fr', 'de', 'it', 'eu'],
131
+ options: [
132
+ 'ca',
133
+ 'de',
134
+ 'en',
135
+ 'es',
136
+ 'eu',
137
+ 'fi',
138
+ 'fr',
139
+ 'hi',
140
+ 'it',
141
+ 'ja',
142
+ 'nl',
143
+ 'pt',
144
+ 'pt-BR',
145
+ 'ro',
146
+ 'ru',
147
+ 'sv',
148
+ 'zh-CN',
149
+ ],
132
150
  control: {
133
151
  type: 'radio',
134
152
  },
@@ -55,7 +55,7 @@ const messages = defineMessages({
55
55
  defaultMessage: 'No workflow state',
56
56
  },
57
57
  none: {
58
- id: 'None',
58
+ id: 'Not available',
59
59
  defaultMessage: 'None',
60
60
  },
61
61
  });
@@ -168,7 +168,7 @@ export const ContentsItemComponent = ({
168
168
  {item.ExpirationDate !== 'None' &&
169
169
  new Date(item.ExpirationDate).getTime() <
170
170
  new Date().getTime() && (
171
- <Button className="button-margin" size="mini">
171
+ <Button className="button-margin expired-past" size="mini">
172
172
  <FormattedMessage id="Expired" defaultMessage="Expired" />
173
173
  </Button>
174
174
  )}
@@ -178,6 +178,14 @@ export const ContentsItemComponent = ({
178
178
  <FormattedMessage id="Scheduled" defaultMessage="Scheduled" />
179
179
  </Button>
180
180
  )}
181
+ {item.is_working_copy && (
182
+ <Button className="button-margin working-copy" size="mini">
183
+ <FormattedMessage
184
+ id="Working copy"
185
+ defaultMessage="Working copy"
186
+ />
187
+ </Button>
188
+ )}
181
189
  </Link>
182
190
  </Table.Cell>
183
191
  {map(indexes, (index) => (