@plone/volto 16.26.1 → 16.28.0

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/.changelog.draft +5 -5
  2. package/.yarn/install-state.gz +0 -0
  3. package/CHANGELOG.md +17 -0
  4. package/README.md +3 -19
  5. package/locales/ca/LC_MESSAGES/volto.po +94 -30
  6. package/locales/ca.json +1 -1
  7. package/locales/de/LC_MESSAGES/volto.po +94 -30
  8. package/locales/de.json +1 -1
  9. package/locales/en/LC_MESSAGES/volto.po +95 -31
  10. package/locales/en.json +1 -1
  11. package/locales/es/LC_MESSAGES/volto.po +94 -30
  12. package/locales/es.json +1 -1
  13. package/locales/eu/LC_MESSAGES/volto.po +94 -30
  14. package/locales/eu.json +1 -1
  15. package/locales/fi/LC_MESSAGES/volto.po +94 -30
  16. package/locales/fi.json +1 -1
  17. package/locales/fr/LC_MESSAGES/volto.po +94 -30
  18. package/locales/fr.json +1 -1
  19. package/locales/it/LC_MESSAGES/volto.po +94 -30
  20. package/locales/it.json +1 -1
  21. package/locales/ja/LC_MESSAGES/volto.po +94 -30
  22. package/locales/ja.json +1 -1
  23. package/locales/nl/LC_MESSAGES/volto.po +94 -30
  24. package/locales/nl.json +1 -1
  25. package/locales/pt/LC_MESSAGES/volto.po +94 -30
  26. package/locales/pt.json +1 -1
  27. package/locales/pt_BR/LC_MESSAGES/volto.po +94 -30
  28. package/locales/pt_BR.json +1 -1
  29. package/locales/ro/LC_MESSAGES/volto.po +94 -30
  30. package/locales/ro.json +1 -1
  31. package/locales/volto.pot +95 -31
  32. package/locales/zh_CN/LC_MESSAGES/volto.po +94 -30
  33. package/locales/zh_CN.json +1 -1
  34. package/package.json +1 -1
  35. package/packages/volto-slate/package.json +1 -1
  36. package/src/actions/index.js +7 -0
  37. package/src/actions/relations/rebuild.js +25 -0
  38. package/src/actions/relations/relations.js +103 -0
  39. package/src/actions/relations/relations.test.js +15 -0
  40. package/src/components/index.js +1 -0
  41. package/src/components/manage/Contents/Contents.jsx +320 -110
  42. package/src/components/manage/LinksToItem/LinksToItem.jsx +185 -0
  43. package/src/components/manage/LinksToItem/LinksToItem.test.jsx +97 -0
  44. package/src/components/manage/Toolbar/More.jsx +15 -0
  45. package/src/components/theme/ContentMetadataTags/ContentMetadataTags.jsx +37 -6
  46. package/src/config/NonContentRoutes.jsx +1 -0
  47. package/src/config/index.js +2 -0
  48. package/src/constants/ActionTypes.js +5 -0
  49. package/src/reducers/index.js +2 -0
  50. package/src/reducers/relations/relations.js +201 -0
  51. package/src/routes.js +9 -0
  52. package/theme/themes/pastanaga/extras/contents.less +4 -0
  53. package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +0 -541
  54. package/.yarn/releases/yarn-3.6.3.cjs +0 -874
  55. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +0 -90
  56. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +0 -6
  57. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +0 -6
  58. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +0 -6
  59. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +0 -10
  60. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +0 -10
  61. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +0 -30
  62. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +0 -10
  63. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +0 -6
  64. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +0 -6
  65. package/share/man/man1/ttx.1 +0 -225
  66. package/styles/Microsoft/AMPM.yml +0 -9
  67. package/styles/Microsoft/Accessibility.yml +0 -25
  68. package/styles/Microsoft/Acronyms.yml +0 -64
  69. package/styles/Microsoft/Adverbs.yml +0 -270
  70. package/styles/Microsoft/Auto.yml +0 -11
  71. package/styles/Microsoft/Avoid.yml +0 -14
  72. package/styles/Microsoft/ComplexWords.yml +0 -120
  73. package/styles/Microsoft/Contractions.yml +0 -50
  74. package/styles/Microsoft/Dashes.yml +0 -13
  75. package/styles/Microsoft/DateFormat.yml +0 -8
  76. package/styles/Microsoft/DateNumbers.yml +0 -40
  77. package/styles/Microsoft/DateOrder.yml +0 -8
  78. package/styles/Microsoft/Ellipses.yml +0 -9
  79. package/styles/Microsoft/FirstPerson.yml +0 -16
  80. package/styles/Microsoft/Foreign.yml +0 -13
  81. package/styles/Microsoft/Gender.yml +0 -8
  82. package/styles/Microsoft/GenderBias.yml +0 -44
  83. package/styles/Microsoft/GeneralURL.yml +0 -11
  84. package/styles/Microsoft/HeadingAcronyms.yml +0 -7
  85. package/styles/Microsoft/HeadingColons.yml +0 -8
  86. package/styles/Microsoft/HeadingPunctuation.yml +0 -13
  87. package/styles/Microsoft/Headings.yml +0 -28
  88. package/styles/Microsoft/Hyphens.yml +0 -14
  89. package/styles/Microsoft/Negative.yml +0 -13
  90. package/styles/Microsoft/Ordinal.yml +0 -13
  91. package/styles/Microsoft/OxfordComma.yml +0 -8
  92. package/styles/Microsoft/Passive.yml +0 -183
  93. package/styles/Microsoft/Percentages.yml +0 -7
  94. package/styles/Microsoft/Quotes.yml +0 -7
  95. package/styles/Microsoft/RangeFormat.yml +0 -13
  96. package/styles/Microsoft/RangeTime.yml +0 -13
  97. package/styles/Microsoft/Ranges.yml +0 -7
  98. package/styles/Microsoft/Semicolon.yml +0 -8
  99. package/styles/Microsoft/SentenceLength.yml +0 -7
  100. package/styles/Microsoft/Spacing.yml +0 -8
  101. package/styles/Microsoft/Suspended.yml +0 -7
  102. package/styles/Microsoft/Terms.yml +0 -41
  103. package/styles/Microsoft/URLFormat.yml +0 -10
  104. package/styles/Microsoft/Units.yml +0 -16
  105. package/styles/Microsoft/Vocab.yml +0 -25
  106. package/styles/Microsoft/We.yml +0 -11
  107. package/styles/Microsoft/Wordiness.yml +0 -122
  108. package/styles/Microsoft/meta.json +0 -4
@@ -34,7 +34,7 @@ import {
34
34
  } from 'lodash';
35
35
  import move from 'lodash-move';
36
36
  import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
37
- import { asyncConnect } from '@plone/volto/helpers';
37
+ import { asyncConnect, flattenToAppURL } from '@plone/volto/helpers';
38
38
 
39
39
  import {
40
40
  searchContent,
@@ -118,9 +118,13 @@ const messages = defineMessages({
118
118
  id: 'Delete',
119
119
  defaultMessage: 'Delete',
120
120
  },
121
- deleteConfirm: {
122
- id: 'Do you really want to delete the following items?',
123
- defaultMessage: 'Do you really want to delete the following items?',
121
+ deleteConfirmSingleItem: {
122
+ id: 'Delete this item?',
123
+ defaultMessage: 'Delete this item?',
124
+ },
125
+ deleteConfirmMultipleItems: {
126
+ id: 'Delete selected items?',
127
+ defaultMessage: 'Delete selected items?',
124
128
  },
125
129
  deleteError: {
126
130
  id: 'The item could not be deleted.',
@@ -270,35 +274,6 @@ const messages = defineMessages({
270
274
  id: 'All',
271
275
  defaultMessage: 'All',
272
276
  },
273
- linkIntegrityMessageHeader: {
274
- id: 'Potential link breakage',
275
- defaultMessage: 'Potential link breakage',
276
- },
277
- linkIntegrityMessageBody: {
278
- id:
279
- 'By deleting this item, you will break ' +
280
- 'links that exist in the items listed below. ' +
281
- 'If this is indeed what you want to do, ' +
282
- 'we recommend that remove these references first.',
283
- defaultMessage:
284
- 'By deleting this item, ' +
285
- 'you will break links that exist in the items ' +
286
- 'listed below. If this is indeed what you ' +
287
- 'want to do, we recommend that remove ' +
288
- 'these references first.',
289
- },
290
- linkIntegrityMessageExtra: {
291
- id: 'This Page is referenced by the following items:',
292
- defaultMessage: 'This Page is referenced by the following items:',
293
- },
294
- deleteItemCountMessage: {
295
- id: 'Total items to be deleted:',
296
- defaultMessage: 'Total items to be deleted:',
297
- },
298
- deleteItemMessage: {
299
- id: 'Items to be deleted:',
300
- defaultMessage: 'Items to be deleted:',
301
- },
302
277
  });
303
278
 
304
279
  /**
@@ -434,6 +409,9 @@ class Contents extends Component {
434
409
  showProperties: false,
435
410
  showWorkflow: false,
436
411
  itemsToDelete: [],
412
+ containedItemsToDelete: [],
413
+ brokenReferences: 0,
414
+ breaches: [],
437
415
  showAllItemsToDelete: true,
438
416
  items: this.props.items,
439
417
  filter: '',
@@ -469,12 +447,40 @@ class Contents extends Component {
469
447
  this.state.itemsToDelete !== prevState.itemsToDelete &&
470
448
  this.state.itemsToDelete.length > 0
471
449
  ) {
450
+ const linkintegrityInfo = await this.props.linkIntegrityCheck(
451
+ map(this.state.itemsToDelete, (item) => this.getFieldById(item, 'UID')),
452
+ );
453
+ const containedItems = linkintegrityInfo
454
+ .map((result) => result.items_total ?? 0)
455
+ .reduce((acc, value) => acc + value, 0);
456
+ const breaches = linkintegrityInfo.flatMap((result) =>
457
+ result.breaches.map((source) => ({
458
+ source: source,
459
+ target: result,
460
+ })),
461
+ );
462
+ const source_by_uid = breaches.reduce(
463
+ (acc, value) => acc.set(value.source.uid, value.source),
464
+ new Map(),
465
+ );
466
+ const by_source = breaches.reduce((acc, value) => {
467
+ if (acc.get(value.source.uid) === undefined) {
468
+ acc.set(value.source.uid, new Set());
469
+ }
470
+ acc.get(value.source.uid).add(value.target);
471
+ return acc;
472
+ }, new Map());
473
+
472
474
  this.setState({
473
- linkIntegrityBreakages: await this.props.linkIntegrityCheck(
474
- map(this.state.itemsToDelete, (item) =>
475
- this.getFieldById(item, 'UID'),
476
- ),
477
- ),
475
+ containedItemsToDelete: containedItems,
476
+ brokenReferences: by_source.size,
477
+ linksAndReferencesViewLink: linkintegrityInfo.length
478
+ ? linkintegrityInfo[0]['@id'] + '/links-to-item'
479
+ : null,
480
+ breaches: Array.from(by_source, (entry) => ({
481
+ source: source_by_uid.get(entry[0]),
482
+ targets: Array.from(entry[1]),
483
+ })),
478
484
  showAllItemsToDelete:
479
485
  this.state.itemsToDelete.length < this.deleteItemsToShowThreshold,
480
486
  });
@@ -1199,85 +1205,289 @@ class Contents extends Component {
1199
1205
  <article id="content">
1200
1206
  <Confirm
1201
1207
  open={this.state.showDelete}
1202
- confirmButton="Delete"
1203
- header={this.props.intl.formatMessage(
1204
- messages.deleteConfirm,
1205
- )}
1208
+ confirmButton={
1209
+ this.state.brokenReferences === 0
1210
+ ? 'Delete'
1211
+ : 'Delete item and break links'
1212
+ }
1213
+ header={
1214
+ this.state.itemsToDelete.length === 1
1215
+ ? this.props.intl.formatMessage(
1216
+ messages.deleteConfirmSingleItem,
1217
+ )
1218
+ : this.props.intl.formatMessage(
1219
+ messages.deleteConfirmMultipleItems,
1220
+ )
1221
+ }
1206
1222
  content={
1207
1223
  <div className="content">
1208
- <h3>
1209
- {this.props.intl.formatMessage(
1210
- messages.deleteItemCountMessage,
1211
- ) + ` ${this.state.itemsToDelete.length}`}
1212
- </h3>
1213
- <ul className="content">
1214
- {map(
1215
- this.state.showAllItemsToDelete
1216
- ? this.state.itemsToDelete
1217
- : this.state.itemsToDelete.slice(
1218
- 0,
1219
- this.deleteItemsToShowThreshold,
1220
- ),
1221
- (item) => (
1222
- <li key={item}>
1223
- {this.getFieldById(item, 'title')}
1224
- </li>
1225
- ),
1226
- )}
1227
- </ul>
1228
- {!this.state.showAllItemsToDelete && (
1229
- <Button
1230
- onClick={() =>
1231
- this.setState({
1232
- showAllItemsToDelete: true,
1233
- })
1234
- }
1235
- >
1236
- Show all items
1237
- </Button>
1238
- )}
1239
- {this.state.linkIntegrityBreakages.reduce(
1240
- (a, b) => a + b.breaches.length,
1241
- 0,
1242
- ) ? (
1243
- <div>
1244
- <h3>
1245
- {this.props.intl.formatMessage(
1246
- messages.linkIntegrityMessageHeader,
1224
+ {this.state.itemsToDelete.length > 1 ? (
1225
+ this.state.containedItemsToDelete > 0 ? (
1226
+ <>
1227
+ <FormattedMessage
1228
+ id="Some items are also a folder. By deleting them you will delete {containedItemsToDelete} {variation} inside the folders."
1229
+ defaultMessage="Some items are also a folder. By deleting them you will delete {containedItemsToDelete} {variation} inside the folders."
1230
+ values={{
1231
+ containedItemsToDelete: (
1232
+ <span>
1233
+ {this.state.containedItemsToDelete}
1234
+ </span>
1235
+ ),
1236
+ variation: (
1237
+ <span>
1238
+ {this.state.containedItemsToDelete ===
1239
+ 1 ? (
1240
+ <FormattedMessage
1241
+ id="item"
1242
+ defaultMessage="item"
1243
+ />
1244
+ ) : (
1245
+ <FormattedMessage
1246
+ id="items"
1247
+ defaultMessage="items"
1248
+ />
1249
+ )}
1250
+ </span>
1251
+ ),
1252
+ }}
1253
+ />
1254
+ {this.state.brokenReferences > 0 && (
1255
+ <>
1256
+ <br />
1257
+ <FormattedMessage
1258
+ id="Some items are referenced by other contents. By deleting them {brokenReferences} {variation} will be broken."
1259
+ defaultMessage="Some items are referenced by other contents. By deleting them {brokenReferences} {variation} will be broken."
1260
+ values={{
1261
+ brokenReferences: (
1262
+ <span>
1263
+ {this.state.brokenReferences}
1264
+ </span>
1265
+ ),
1266
+ variation: (
1267
+ <span>
1268
+ {this.state.brokenReferences === 1 ? (
1269
+ <FormattedMessage
1270
+ id="reference"
1271
+ defaultMessage="reference"
1272
+ />
1273
+ ) : (
1274
+ <FormattedMessage
1275
+ id="references"
1276
+ defaultMessage="references"
1277
+ />
1278
+ )}
1279
+ </span>
1280
+ ),
1281
+ }}
1282
+ />
1283
+ </>
1247
1284
  )}
1248
- </h3>
1249
- <p>
1250
- {this.props.intl.formatMessage(
1251
- messages.linkIntegrityMessageBody,
1285
+ </>
1286
+ ) : (
1287
+ <>
1288
+ {this.state.brokenReferences > 0 && (
1289
+ <>
1290
+ <FormattedMessage
1291
+ id="Some items are referenced by other contents. By deleting them {brokenReferences} {variation} will be broken."
1292
+ defaultMessage="Some items are referenced by other contents. By deleting them {brokenReferences} {variation} will be broken."
1293
+ values={{
1294
+ brokenReferences: (
1295
+ <span>
1296
+ {this.state.brokenReferences}
1297
+ </span>
1298
+ ),
1299
+ variation: (
1300
+ <span>
1301
+ {this.state.brokenReferences === 1 ? (
1302
+ <FormattedMessage
1303
+ id="reference"
1304
+ defaultMessage="reference"
1305
+ />
1306
+ ) : (
1307
+ <FormattedMessage
1308
+ id="references"
1309
+ defaultMessage="references"
1310
+ />
1311
+ )}
1312
+ </span>
1313
+ ),
1314
+ }}
1315
+ />
1316
+ </>
1252
1317
  )}
1253
- </p>
1254
- <ul className="content">
1255
- {map(this.state.linkIntegrityBreakages, (item) =>
1256
- item.breaches.length ? (
1257
- <li key={item['@id']}>
1258
- <a href={item['@id']}>{item.title}</a>
1259
- <p>
1260
- {this.props.intl.formatMessage(
1261
- messages.linkIntegrityMessageExtra,
1318
+ </>
1319
+ )
1320
+ ) : this.state.containedItemsToDelete > 0 ? (
1321
+ <>
1322
+ <FormattedMessage
1323
+ id="This item is also a folder. By deleting it you will delete {containedItemsToDelete} {variation} inside the folder."
1324
+ defaultMessage="This item is also a folder. By deleting it you will delete {containedItemsToDelete} {variation} inside the folder."
1325
+ values={{
1326
+ containedItemsToDelete: (
1327
+ <span>
1328
+ {this.state.containedItemsToDelete}
1329
+ </span>
1330
+ ),
1331
+ variation: (
1332
+ <span>
1333
+ {this.state.containedItemsToDelete === 1 ? (
1334
+ <FormattedMessage
1335
+ id="item"
1336
+ defaultMessage="item"
1337
+ />
1338
+ ) : (
1339
+ <FormattedMessage
1340
+ id="items"
1341
+ defaultMessage="items"
1342
+ />
1343
+ )}
1344
+ </span>
1345
+ ),
1346
+ }}
1347
+ />
1348
+ {this.state.brokenReferences > 0 && (
1349
+ <>
1350
+ <br />
1351
+ <FormattedMessage
1352
+ id="Deleting this item breaks {brokenReferences} {variation}."
1353
+ defaultMessage="Deleting this item breaks {brokenReferences} {variation}."
1354
+ values={{
1355
+ brokenReferences: (
1356
+ <span>{this.state.brokenReferences}</span>
1357
+ ),
1358
+ variation: (
1359
+ <span>
1360
+ {this.state.brokenReferences === 1 ? (
1361
+ <FormattedMessage
1362
+ id="reference"
1363
+ defaultMessage="reference"
1364
+ />
1365
+ ) : (
1366
+ <FormattedMessage
1367
+ id="references"
1368
+ defaultMessage="references"
1369
+ />
1370
+ )}
1371
+ </span>
1372
+ ),
1373
+ }}
1374
+ />
1375
+ <div className="broken-links-list">
1376
+ <FormattedMessage id="These items will have broken links" />
1377
+ <ul>
1378
+ {this.state.breaches.map((breach) => (
1379
+ <li key={breach.source['@id']}>
1380
+ <Link
1381
+ to={flattenToAppURL(
1382
+ breach.source['@id'],
1383
+ )}
1384
+ title="Navigate to this item"
1385
+ >
1386
+ {breach.source.title}
1387
+ </Link>{' '}
1388
+ refers to{' '}
1389
+ {breach.targets
1390
+ .map((target) => (
1391
+ <Link
1392
+ to={flattenToAppURL(
1393
+ target['@id'],
1394
+ )}
1395
+ title="Navigate to this item"
1396
+ >
1397
+ {target.title}
1398
+ </Link>
1399
+ ))
1400
+ .reduce((result, item) => (
1401
+ <>
1402
+ {result}, {item}
1403
+ </>
1404
+ ))}
1405
+ </li>
1406
+ ))}
1407
+ </ul>
1408
+ {this.state.linksAndReferencesViewLink && (
1409
+ <Link
1410
+ to={flattenToAppURL(
1411
+ this.state.linksAndReferencesViewLink,
1262
1412
  )}
1263
- </p>
1264
- <ul className="content">
1265
- {map(item.breaches, (breach) => (
1266
- <li key={breach['@id']}>
1267
- <a href={breach['@id']}>
1268
- {breach.title}
1269
- </a>
1270
- </li>
1413
+ >
1414
+ <FormattedMessage
1415
+ id="View links and references to this item"
1416
+ defaultMessage="View links and references to this item"
1417
+ />
1418
+ </Link>
1419
+ )}
1420
+ </div>
1421
+ </>
1422
+ )}
1423
+ </>
1424
+ ) : this.state.brokenReferences > 0 ? (
1425
+ <>
1426
+ <FormattedMessage
1427
+ id="Deleting this item breaks {brokenReferences} {variation}."
1428
+ defaultMessage="Deleting this item breaks {brokenReferences} {variation}."
1429
+ values={{
1430
+ brokenReferences: (
1431
+ <span>{this.state.brokenReferences}</span>
1432
+ ),
1433
+ variation: (
1434
+ <span>
1435
+ {this.state.brokenReferences === 1 ? (
1436
+ <FormattedMessage
1437
+ id="reference"
1438
+ defaultMessage="reference"
1439
+ />
1440
+ ) : (
1441
+ <FormattedMessage id="references" />
1442
+ )}
1443
+ </span>
1444
+ ),
1445
+ }}
1446
+ />
1447
+ <div className="broken-links-list">
1448
+ <FormattedMessage id="These items will have broken links" />
1449
+ <ul>
1450
+ {this.state.breaches.map((breach) => (
1451
+ <li key={breach.source['@id']}>
1452
+ <Link
1453
+ to={flattenToAppURL(breach.source['@id'])}
1454
+ title="Navigate to this item"
1455
+ >
1456
+ {breach.source.title}
1457
+ </Link>{' '}
1458
+ refers to{' '}
1459
+ {breach.targets
1460
+ .map((target) => (
1461
+ <Link
1462
+ to={flattenToAppURL(target['@id'])}
1463
+ title="Navigate to this item"
1464
+ >
1465
+ {target.title}
1466
+ </Link>
1467
+ ))
1468
+ .reduce((result, item) => (
1469
+ <>
1470
+ {result}, {item}
1471
+ </>
1271
1472
  ))}
1272
- </ul>
1273
1473
  </li>
1274
- ) : null,
1474
+ ))}
1475
+ </ul>
1476
+ {this.state.linksAndReferencesViewLink && (
1477
+ <Link
1478
+ to={flattenToAppURL(
1479
+ this.state.linksAndReferencesViewLink,
1480
+ )}
1481
+ >
1482
+ <FormattedMessage
1483
+ id="View links and references to this item"
1484
+ defaultMessage="View links and references to this item"
1485
+ />
1486
+ </Link>
1275
1487
  )}
1276
- </ul>
1277
- </div>
1278
- ) : (
1279
- <div></div>
1280
- )}
1488
+ </div>
1489
+ </>
1490
+ ) : null}
1281
1491
  </div>
1282
1492
  }
1283
1493
  onCancel={this.onDeleteCancel}
@@ -2079,7 +2289,7 @@ export default compose(
2079
2289
  asyncConnect([
2080
2290
  {
2081
2291
  key: 'actions',
2082
- // Dispatch async/await to make the operation syncronous, otherwise it returns
2292
+ // Dispatch async/await to make the operation synchronous, otherwise it returns
2083
2293
  // before the promise is resolved
2084
2294
  promise: async ({ location, store: { dispatch } }) =>
2085
2295
  await dispatch(listActions(getBaseUrl(location.pathname))),
@@ -0,0 +1,185 @@
1
+ /**
2
+ * LinksToItem component
3
+ * @module components/manage/LinksToItem/LinksToItem
4
+ */
5
+ import { useEffect } from 'react';
6
+ import { Helmet } from '@plone/volto/helpers';
7
+ import { Link } from 'react-router-dom';
8
+ import { Portal } from 'react-portal';
9
+ import { Container, Segment, Table } from 'semantic-ui-react';
10
+ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
11
+ import { useDispatch, useSelector } from 'react-redux';
12
+ import { getContent, queryRelations } from '@plone/volto/actions';
13
+ import {
14
+ Icon as IconNext,
15
+ Toolbar,
16
+ UniversalLink,
17
+ } from '@plone/volto/components';
18
+
19
+ import { getBaseUrl } from '@plone/volto/helpers';
20
+ import backSVG from '@plone/volto/icons/back.svg';
21
+
22
+ const messages = defineMessages({
23
+ back: {
24
+ id: 'Back',
25
+ defaultMessage: 'Back',
26
+ },
27
+ linkstoitem: {
28
+ id: 'Links and references',
29
+ defaultMessage: 'Links and references',
30
+ },
31
+ helpLinkRelationsControlPanel: {
32
+ id: 'Overview of relations of all content items',
33
+ defaultMessage: 'Overview of relations of all content items',
34
+ },
35
+ });
36
+
37
+ const LinksToItem = (props) => {
38
+ const intl = useIntl();
39
+ const dispatch = useDispatch();
40
+ const pathname = props.location.pathname;
41
+ const itempath = getBaseUrl(pathname);
42
+
43
+ const title = useSelector((state) => state.content.data?.title || '');
44
+ const myrelations = useSelector(
45
+ (state) => state.relations.subrequests[itempath]?.data,
46
+ );
47
+
48
+ useEffect(() => {
49
+ dispatch(queryRelations(null, false, itempath, null, [itempath]));
50
+ }, [dispatch, itempath]);
51
+
52
+ useEffect(() => {
53
+ if (!title) dispatch(getContent(itempath));
54
+ }, [dispatch, itempath, title]);
55
+
56
+ let links = {};
57
+ if (myrelations) {
58
+ Object.keys(myrelations).forEach((relationtype) => {
59
+ links[relationtype] = {};
60
+ myrelations[relationtype].items.forEach((item) => {
61
+ links[relationtype][item.source.UID] = item.source;
62
+ });
63
+ });
64
+ }
65
+
66
+ let links_ordered = {};
67
+ Object.keys(links).forEach((relationtype) => {
68
+ links_ordered[relationtype] = Object.values(links[relationtype]).sort(
69
+ (link) => link['@id'],
70
+ );
71
+ });
72
+
73
+ const relations_found = Object.keys(links_ordered).length > 0;
74
+ return (
75
+ <Container id="linkstoitem">
76
+ <Helmet title={intl.formatMessage(messages.linkstoitem)} />
77
+ <Segment.Group raised>
78
+ <Segment className="primary">
79
+ <FormattedMessage
80
+ id="Content that links to or references {title}"
81
+ defaultMessage="Content that links to or references {title}"
82
+ values={{ title: <q>{title}</q> }}
83
+ />
84
+ </Segment>
85
+ {relations_found ? (
86
+ <Table selectable compact singleLine attached>
87
+ {
88
+ <Table.Body>
89
+ {Object.keys(links_ordered).map((relationtype) => {
90
+ return [].concat(
91
+ [
92
+ <Table.Row>
93
+ <Table.HeaderCell>
94
+ {relationtype === 'isReferencing' ? (
95
+ <FormattedMessage
96
+ id="Linking this item with hyperlink in text"
97
+ defaultMessage="Linking this item with hyperlink in text"
98
+ />
99
+ ) : relationtype === 'relatedItems' ? (
100
+ <FormattedMessage
101
+ id="Referencing this item as related item"
102
+ defaultMessage="Referencing this item as related item"
103
+ />
104
+ ) : (
105
+ <>
106
+ <FormattedMessage
107
+ id="Referencing this item with {relationship}"
108
+ defaultMessage="Referencing this item with {relationship}"
109
+ values={{ relationship: <q>{relationtype}</q> }}
110
+ />
111
+ </>
112
+ )}
113
+ </Table.HeaderCell>
114
+ <Table.HeaderCell>
115
+ <FormattedMessage
116
+ id="Review state"
117
+ defaultMessage="Review state"
118
+ />
119
+ </Table.HeaderCell>
120
+ <Table.HeaderCell>
121
+ <FormattedMessage id="Type" defaultMessage="Type" />
122
+ </Table.HeaderCell>
123
+ </Table.Row>,
124
+ ],
125
+ links_ordered[relationtype].map((link) => {
126
+ return (
127
+ <Table.Row key={link['@id']}>
128
+ <Table.Cell>
129
+ <UniversalLink
130
+ href={link['@id']}
131
+ className={`source ${link.review_state}`}
132
+ >
133
+ <span className="label" title={link.type_title}>
134
+ {link.title}
135
+ </span>
136
+ </UniversalLink>
137
+ </Table.Cell>
138
+ <Table.Cell>
139
+ <span>{link.review_state}</span>
140
+ </Table.Cell>
141
+ <Table.Cell>
142
+ <span>{link.type_title || ''}</span>
143
+ </Table.Cell>
144
+ </Table.Row>
145
+ );
146
+ }),
147
+ );
148
+ })}
149
+ </Table.Body>
150
+ }
151
+ </Table>
152
+ ) : (
153
+ <Segment secondary>
154
+ <FormattedMessage
155
+ id="No links to this item found."
156
+ defaultMessage="No links to this item found."
157
+ />
158
+ </Segment>
159
+ )}
160
+ </Segment.Group>
161
+ {__CLIENT__ && (
162
+ <Portal node={document.getElementById('toolbar')}>
163
+ <Toolbar
164
+ pathname={pathname}
165
+ hideDefaultViewButtons
166
+ inner={
167
+ <>
168
+ <Link to={itempath} className="item">
169
+ <IconNext
170
+ name={backSVG}
171
+ className="contents circled"
172
+ size="30px"
173
+ title={intl.formatMessage(messages.back)}
174
+ />
175
+ </Link>
176
+ </>
177
+ }
178
+ />
179
+ </Portal>
180
+ )}
181
+ </Container>
182
+ );
183
+ };
184
+
185
+ export default LinksToItem;