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

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 (297) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +530 -15
  3. package/CONTRIBUTING.md +1 -1
  4. package/README.md +11 -14
  5. package/addon-registry.js +34 -0
  6. package/create-theme-addons-loader.js +79 -0
  7. package/cypress/support/commands.js +56 -4
  8. package/cypress/support/volto-slate.js +4 -5
  9. package/docker-compose.yml +1 -1
  10. package/locales/ca/LC_MESSAGES/volto.po +272 -6
  11. package/locales/ca.json +1 -1
  12. package/locales/de/LC_MESSAGES/volto.po +291 -25
  13. package/locales/de.json +1 -1
  14. package/locales/en/LC_MESSAGES/volto.po +271 -5
  15. package/locales/en.json +1 -1
  16. package/locales/es/LC_MESSAGES/volto.po +281 -15
  17. package/locales/es.json +1 -1
  18. package/locales/eu/LC_MESSAGES/volto.po +272 -6
  19. package/locales/eu.json +1 -1
  20. package/locales/fi/LC_MESSAGES/volto.po +4882 -0
  21. package/locales/fi.json +1 -1
  22. package/locales/fr/LC_MESSAGES/volto.po +272 -6
  23. package/locales/fr.json +1 -1
  24. package/locales/it/LC_MESSAGES/volto.po +273 -7
  25. package/locales/it.json +1 -1
  26. package/locales/ja/LC_MESSAGES/volto.po +272 -6
  27. package/locales/ja.json +1 -1
  28. package/locales/nl/LC_MESSAGES/volto.po +927 -649
  29. package/locales/nl.json +1 -1
  30. package/locales/pt/LC_MESSAGES/volto.po +272 -6
  31. package/locales/pt.json +1 -1
  32. package/locales/pt_BR/LC_MESSAGES/volto.po +281 -15
  33. package/locales/pt_BR.json +1 -1
  34. package/locales/ro/LC_MESSAGES/volto.po +272 -6
  35. package/locales/ro.json +1 -1
  36. package/locales/volto.pot +272 -6
  37. package/locales/zh_CN/LC_MESSAGES/volto.po +272 -6
  38. package/locales/zh_CN.json +1 -1
  39. package/package.json +5 -3
  40. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +1 -1
  41. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +1 -1
  42. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +1 -1
  43. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +1 -1
  44. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +1 -1
  45. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +1 -1
  46. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +1 -1
  47. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +1 -1
  48. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +1 -1
  49. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +1 -1
  50. package/packages/volto-slate/package.json +1 -1
  51. package/packages/volto-slate/src/actions/index.js +1 -1
  52. package/packages/volto-slate/src/blocks/Table/TableBlockEdit.jsx +21 -212
  53. package/packages/volto-slate/src/blocks/Table/schema.js +122 -0
  54. package/packages/volto-slate/src/blocks/Text/DefaultTextBlockEditor.jsx +8 -3
  55. package/packages/volto-slate/src/blocks/Text/SlashMenu.jsx +4 -3
  56. package/packages/volto-slate/src/blocks/Text/TextBlockView.jsx +20 -16
  57. package/packages/volto-slate/src/blocks/Text/extensions/withDeserializers.js +3 -1
  58. package/packages/volto-slate/src/blocks/Text/index.js +10 -2
  59. package/packages/volto-slate/src/editor/config.jsx +5 -4
  60. package/packages/volto-slate/src/editor/deserialize.js +0 -1
  61. package/packages/volto-slate/src/editor/index.js +4 -4
  62. package/packages/volto-slate/src/editor/less/slate.less +28 -0
  63. package/packages/volto-slate/src/editor/plugins/StyleMenu/StyleMenu.jsx +14 -4
  64. package/packages/volto-slate/src/editor/plugins/StyleMenu/utils.js +14 -5
  65. package/packages/volto-slate/src/editor/render.jsx +68 -8
  66. package/packages/volto-slate/src/editor/ui/SlateContextToolbar.jsx +2 -2
  67. package/packages/volto-slate/src/editor/ui/index.js +15 -15
  68. package/packages/volto-slate/src/index.js +2 -2
  69. package/packages/volto-slate/src/utils/blocks.js +7 -0
  70. package/packages/volto-slate/src/widgets/RichTextWidget.jsx +15 -8
  71. package/razzle.config.js +28 -0
  72. package/src/actions/index.js +6 -0
  73. package/src/actions/language/language.js +9 -8
  74. package/src/actions/querystringsearch/querystringsearch.js +20 -14
  75. package/src/actions/relations/rebuild.js +25 -0
  76. package/src/actions/relations/relations.js +86 -0
  77. package/src/actions/relations/relations.test.js +15 -0
  78. package/src/components/index.js +2 -0
  79. package/src/components/manage/Add/Add.jsx +2 -2
  80. package/src/components/manage/AnchorPlugin/index.jsx +2 -2
  81. package/src/components/manage/AnchorPlugin/utils/EditorUtils.js +3 -1
  82. package/src/components/manage/BlockChooser/BlockChooser.jsx +14 -5
  83. package/src/components/manage/BlockChooser/BlockChooser.test.jsx +5 -0
  84. package/src/components/manage/Blocks/Block/BlocksForm.jsx +19 -2
  85. package/src/components/manage/Blocks/Block/Edit.jsx +1 -1
  86. package/src/components/manage/Blocks/Block/Style.jsx +2 -2
  87. package/src/components/manage/Blocks/Container/Data.jsx +32 -0
  88. package/src/components/manage/Blocks/Container/Edit.jsx +174 -0
  89. package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +120 -0
  90. package/src/components/manage/Blocks/Container/NewBlockAddButton.jsx +84 -0
  91. package/src/components/manage/Blocks/Container/SimpleContainerToolbar.jsx +54 -0
  92. package/src/components/manage/Blocks/Grid/Edit.jsx +33 -0
  93. package/src/components/manage/Blocks/Grid/View.jsx +43 -0
  94. package/src/components/manage/Blocks/Grid/adapter.js +14 -0
  95. package/src/components/manage/Blocks/Grid/grid-1.svg +6 -0
  96. package/src/components/manage/Blocks/Grid/grid-2.svg +9 -0
  97. package/src/components/manage/Blocks/Grid/grid-3.svg +10 -0
  98. package/src/components/manage/Blocks/Grid/grid-4.svg +11 -0
  99. package/src/components/manage/Blocks/Grid/schema.js +35 -0
  100. package/src/components/manage/Blocks/Grid/templates.js +47 -0
  101. package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +6 -1
  102. package/src/components/manage/Blocks/Image/Edit.jsx +11 -7
  103. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +2 -1
  104. package/src/components/manage/Blocks/Image/schema.js +11 -0
  105. package/src/components/manage/Blocks/Listing/DefaultTemplate.jsx +18 -3
  106. package/src/components/manage/Blocks/Listing/Edit.jsx +0 -14
  107. package/src/components/manage/Blocks/Listing/ListingBody.jsx +30 -8
  108. package/src/components/manage/Blocks/Listing/ListingBody.test.jsx +20 -0
  109. package/src/components/manage/Blocks/Listing/getAsyncData.js +9 -3
  110. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +26 -18
  111. package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +5 -4
  112. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +2 -1
  113. package/src/components/manage/Blocks/Search/components/DateRangeFacet.jsx +4 -1
  114. package/src/components/manage/Blocks/Search/components/Facets.jsx +64 -4
  115. package/src/components/manage/Blocks/Search/components/SearchInput.jsx +9 -2
  116. package/src/components/manage/Blocks/Search/components/index.js +13 -13
  117. package/src/components/manage/Blocks/Search/hocs/index.js +2 -2
  118. package/src/components/manage/Blocks/Search/hocs/withQueryString.jsx +2 -2
  119. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +43 -15
  120. package/src/components/manage/Blocks/Search/layout/LeftColumnFacets.jsx +17 -5
  121. package/src/components/manage/Blocks/Search/layout/RightColumnFacets.jsx +17 -5
  122. package/src/components/manage/Blocks/Search/layout/TopSideFacets.jsx +21 -5
  123. package/src/components/manage/Blocks/Search/schema.js +16 -1
  124. package/src/components/manage/Blocks/Teaser/Body.jsx +0 -1
  125. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +20 -15
  126. package/src/components/manage/Blocks/Teaser/schema.js +5 -0
  127. package/src/components/manage/Blocks/Title/View.jsx +15 -5
  128. package/src/components/manage/Blocks/Title/View.test.jsx +16 -1
  129. package/src/components/manage/Blocks/ToC/Schema.jsx +5 -1
  130. package/src/components/manage/Blocks/ToC/View.jsx +8 -1
  131. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +17 -4
  132. package/src/components/manage/Blocks/ToC/variations/HorizontalMenu.jsx +148 -10
  133. package/src/components/manage/Blocks/ToC/variations/index.js +3 -1
  134. package/src/components/manage/Contents/Contents.jsx +39 -26
  135. package/src/components/manage/Contents/ContentsItem.jsx +6 -0
  136. package/src/components/manage/Contents/ContentsUploadModal.jsx +10 -5
  137. package/src/components/manage/Controlpanels/AddonsControlpanel.jsx +3 -3
  138. package/src/components/manage/Controlpanels/Controlpanels.jsx +199 -224
  139. package/src/components/manage/Controlpanels/Controlpanels.test.jsx +46 -7
  140. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +66 -0
  141. package/src/components/manage/Controlpanels/Relations/Relations.jsx +114 -0
  142. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +479 -0
  143. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +531 -0
  144. package/src/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel.jsx +3 -3
  145. package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +51 -82
  146. package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +79 -75
  147. package/src/components/manage/DragDropList/DragDropList.jsx +18 -13
  148. package/src/components/manage/Form/Form.jsx +5 -3
  149. package/src/components/manage/Form/InlineForm.jsx +39 -9
  150. package/src/components/manage/Form/InlineFormState.js +8 -0
  151. package/src/components/manage/History/History.jsx +11 -1
  152. package/src/components/manage/LinksToItem/LinksToItem.jsx +209 -0
  153. package/src/components/manage/LinksToItem/LinksToItem.test.jsx +97 -0
  154. package/src/components/manage/Multilingual/CreateTranslation.jsx +2 -2
  155. package/src/components/manage/Multilingual/TranslationObject.jsx +4 -3
  156. package/src/components/manage/Preferences/ChangePassword.jsx +2 -2
  157. package/src/components/manage/Preferences/PersonalPreferences.jsx +2 -2
  158. package/src/components/manage/Sharing/Sharing.jsx +5 -1
  159. package/src/components/manage/TemplateChooser/TemplateChooser.jsx +38 -0
  160. package/src/components/manage/TemplateChooser/TemplateChooser.test.jsx +34 -0
  161. package/src/components/manage/TemplateChooser/template.svg +10 -0
  162. package/src/components/manage/Toast/Toast.jsx +2 -2
  163. package/src/components/manage/Toolbar/More.jsx +15 -0
  164. package/src/components/manage/Toolbar/Types.jsx +2 -2
  165. package/src/components/manage/UniversalLink/UniversalLink.jsx +2 -6
  166. package/src/components/manage/UniversalLink/UniversalLink.test.jsx +36 -0
  167. package/src/components/manage/Widgets/ColorPickerWidget.jsx +6 -1
  168. package/src/components/manage/Widgets/DatetimeWidget.jsx +9 -5
  169. package/src/components/manage/Widgets/FileWidget.jsx +2 -1
  170. package/src/components/manage/Widgets/ObjectListWidget.jsx +3 -8
  171. package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +2 -1
  172. package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +2 -1
  173. package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +2 -1
  174. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +8 -3
  175. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +2 -1
  176. package/src/components/manage/Widgets/SelectUtils.js +1 -1
  177. package/src/components/manage/Widgets/SelectWidget.jsx +1 -1
  178. package/src/components/theme/Anontools/Anontools.jsx +44 -72
  179. package/src/components/theme/Anontools/Anontools.stories.jsx +16 -6
  180. package/src/components/theme/Anontools/Anontools.test.jsx +16 -2
  181. package/src/components/theme/Breadcrumbs/Breadcrumbs.jsx +52 -99
  182. package/src/components/theme/Breadcrumbs/Breadcrumbs.stories.jsx +14 -13
  183. package/src/components/theme/Comments/CommentEditModal.jsx +63 -115
  184. package/src/components/theme/Component/Component.jsx +1 -1
  185. package/src/components/theme/ContactForm/ContactForm.jsx +108 -192
  186. package/src/components/theme/ContactForm/ContactForm.stories.jsx +1 -1
  187. package/src/components/theme/ContactForm/ContactForm.test.jsx +2 -3
  188. package/src/components/theme/Footer/Footer.jsx +2 -13
  189. package/src/components/theme/Header/Header.jsx +37 -63
  190. package/src/components/theme/Header/Header.test.jsx +18 -0
  191. package/src/components/theme/Icon/Icon.jsx +2 -2
  192. package/src/components/theme/LanguageSelector/LanguageSelector.js +8 -3
  193. package/src/components/theme/Login/Login.jsx +1 -0
  194. package/src/components/theme/Logo/Logo.jsx +2 -1
  195. package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +2 -2
  196. package/src/components/theme/Navigation/NavItem.jsx +4 -2
  197. package/src/components/theme/NotFound/NotFound.jsx +55 -41
  198. package/src/components/theme/PasswordReset/PasswordReset.jsx +7 -4
  199. package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +1 -1
  200. package/src/components/theme/SearchWidget/SearchWidget.jsx +38 -98
  201. package/src/components/theme/Sitemap/Sitemap.jsx +5 -3
  202. package/src/components/theme/View/AlbumView.jsx +9 -1
  203. package/src/components/theme/View/DefaultView.jsx +1 -1
  204. package/src/components/theme/View/EventDatesInfo.jsx +2 -1
  205. package/src/components/theme/View/EventView.jsx +6 -2
  206. package/src/components/theme/View/FileView.jsx +23 -18
  207. package/src/components/theme/View/ImageView.jsx +37 -32
  208. package/src/components/theme/View/LinkView.jsx +53 -78
  209. package/src/components/theme/View/ListingView.jsx +33 -27
  210. package/src/components/theme/View/NewsItemView.jsx +10 -5
  211. package/src/components/theme/View/RenderBlocks.jsx +56 -21
  212. package/src/components/theme/View/RenderEmptyBlock.jsx +5 -0
  213. package/src/components/theme/View/SummaryView.jsx +47 -38
  214. package/src/components/theme/View/TabularView.jsx +59 -53
  215. package/src/components/theme/Widgets/DateWidget.jsx +2 -1
  216. package/src/components/theme/Widgets/DatetimeWidget.jsx +2 -1
  217. package/src/components/theme/Widgets/RelationsWidget.jsx +13 -11
  218. package/src/config/Blocks.jsx +44 -0
  219. package/src/config/ControlPanels.js +2 -0
  220. package/src/config/NonContentRoutes.jsx +1 -0
  221. package/src/config/RichTextEditor/Blocks.jsx +2 -2
  222. package/src/config/RichTextEditor/FromHTML.jsx +2 -2
  223. package/src/config/RichTextEditor/Styles.jsx +1 -1
  224. package/src/config/Widgets.jsx +2 -0
  225. package/src/config/index.js +24 -0
  226. package/src/config/server.js +2 -0
  227. package/src/constants/ActionTypes.js +4 -0
  228. package/src/constants/Indexes.js +3 -1
  229. package/src/constants/Languages.js +8 -4
  230. package/src/express-middleware/devproxy.js +1 -1
  231. package/src/express-middleware/files.js +3 -3
  232. package/src/express-middleware/images.js +4 -4
  233. package/src/express-middleware/ok.js +16 -0
  234. package/src/express-middleware/robotstxt.js +1 -1
  235. package/src/express-middleware/sitemap.js +37 -5
  236. package/src/express-middleware/static.js +3 -3
  237. package/src/helpers/Api/Api.js +1 -1
  238. package/src/helpers/Blocks/Blocks.js +48 -0
  239. package/src/helpers/Blocks/Blocks.test.js +79 -0
  240. package/src/helpers/Extensions/index.js +2 -1
  241. package/src/helpers/Extensions/withBlockSchemaEnhancer.js +15 -11
  242. package/src/helpers/Extensions/withBlockSchemaEnhancer.test.js +145 -0
  243. package/src/helpers/FormValidation/FormValidation.js +40 -2
  244. package/src/helpers/FormValidation/FormValidation.test.js +73 -0
  245. package/src/helpers/Html/Html.jsx +3 -1
  246. package/src/helpers/Html/Html.test.jsx +5 -0
  247. package/src/helpers/MessageLabels/MessageLabels.js +80 -0
  248. package/src/helpers/Robots/Robots.js +24 -6
  249. package/src/helpers/ScrollToTop/ScrollToTop.jsx +5 -3
  250. package/src/helpers/Sitemap/Sitemap.js +44 -2
  251. package/src/helpers/Url/Url.js +27 -6
  252. package/src/helpers/Url/Url.test.js +26 -0
  253. package/src/helpers/Utils/UseDetectClickOutside.stories.jsx +191 -0
  254. package/src/helpers/Utils/Utils.js +63 -13
  255. package/src/helpers/Utils/Utils.test.js +4 -4
  256. package/src/helpers/Utils/usePagination.js +67 -14
  257. package/src/helpers/Utils/usePagination.test.js +115 -0
  258. package/src/helpers/index.js +20 -10
  259. package/src/hooks/client/useClient.js +11 -0
  260. package/src/hooks/clipboard/useClipboard.js +26 -0
  261. package/src/hooks/index.js +2 -0
  262. package/src/icons/grid-block.svg +11 -0
  263. package/src/middleware/Api.test.js +54 -0
  264. package/src/middleware/api.js +24 -6
  265. package/src/middleware/index.js +2 -2
  266. package/src/reducers/actions/actions.js +8 -6
  267. package/src/reducers/actions/actions.test.js +70 -0
  268. package/src/reducers/breadcrumbs/breadcrumbs.js +1 -1
  269. package/src/reducers/index.js +2 -0
  270. package/src/reducers/navigation/navigation.js +1 -1
  271. package/src/reducers/relations/relations.js +173 -0
  272. package/src/reducers/types/types.js +1 -1
  273. package/src/routes.js +14 -0
  274. package/src/server.jsx +28 -23
  275. package/src/start-server.js +2 -2
  276. package/test-setup-config.js +2 -0
  277. package/theme/themes/pastanaga/extras/blocks.less +3 -1
  278. package/theme/themes/pastanaga/extras/contents.less +1 -0
  279. package/theme/themes/pastanaga/extras/grid.less +426 -0
  280. package/theme/themes/pastanaga/extras/main.less +3 -1
  281. package/theme/themes/pastanaga/extras/search.less +6 -0
  282. package/theme/themes/pastanaga/extras/sidebar.less +4 -0
  283. package/theme/themes/pastanaga/extras/toc.less +29 -0
  284. package/theme/themes/pastanaga/extras/userscontrolpanel.less +99 -76
  285. package/.changelog.draft +0 -31
  286. package/.editorconfig +0 -36
  287. package/.storybook/main.js +0 -127
  288. package/.storybook/manager.js +0 -15
  289. package/.storybook/preview.js +0 -21
  290. package/.storybook/static/previewImage.svg +0 -48
  291. package/.yarnrc.yml +0 -5
  292. package/jsdoc.json +0 -16
  293. package/netlify.toml +0 -5
  294. package/pyvenv.cfg +0 -3
  295. package/share/man/man1/ttx.1 +0 -225
  296. package/src/components/theme/Header/Header.md +0 -27
  297. package/towncrier.toml +0 -33
@@ -0,0 +1,209 @@
1
+ /**
2
+ * LinksToItem component
3
+ * @module components/manage/LinksToItem/LinksToItem
4
+ */
5
+ import { useEffect } from 'react';
6
+ import { find } from 'lodash';
7
+ import { Helmet } from '@plone/volto/helpers';
8
+ import { Link } from 'react-router-dom';
9
+ import { Portal } from 'react-portal';
10
+ import { Container, Segment, Table } from 'semantic-ui-react';
11
+ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
12
+ import { useDispatch, useSelector } from 'react-redux';
13
+ import { getContent, queryRelations } from '@plone/volto/actions';
14
+ import {
15
+ Icon as IconNext,
16
+ Toolbar,
17
+ UniversalLink,
18
+ } from '@plone/volto/components';
19
+
20
+ import { getBaseUrl } from '@plone/volto/helpers';
21
+ import backSVG from '@plone/volto/icons/back.svg';
22
+ import settingsSVG from '@plone/volto/icons/settings.svg';
23
+
24
+ const messages = defineMessages({
25
+ back: {
26
+ id: 'Back',
27
+ defaultMessage: 'Back',
28
+ },
29
+ linkstoitem: {
30
+ id: 'Links and references',
31
+ defaultMessage: 'Links and references',
32
+ },
33
+ helpLinkRelationsControlPanel: {
34
+ id: 'Overview of relations of all content items',
35
+ defaultMessage: 'Overview of relations of all content items',
36
+ },
37
+ });
38
+
39
+ const LinksToItem = (props) => {
40
+ const intl = useIntl();
41
+ const dispatch = useDispatch();
42
+ const pathname = props.location.pathname;
43
+ const itempath = getBaseUrl(pathname);
44
+
45
+ const title = useSelector((state) => state.content.data?.title || '');
46
+ const myrelations = useSelector(
47
+ (state) => state.relations.subrequests[itempath]?.relations,
48
+ );
49
+ const actions = useSelector((state) => state.actions?.actions ?? {});
50
+ const ploneSetupAction = find(actions.user, {
51
+ id: 'plone_setup',
52
+ });
53
+
54
+ useEffect(() => {
55
+ dispatch(queryRelations(null, false, itempath, null, [itempath]));
56
+ }, [dispatch, itempath]);
57
+
58
+ useEffect(() => {
59
+ if (!title) dispatch(getContent(itempath));
60
+ }, [dispatch, itempath, title]);
61
+
62
+ let links = {};
63
+ if (myrelations) {
64
+ Object.keys(myrelations).forEach((relationtype) => {
65
+ links[relationtype] = {};
66
+ myrelations[relationtype].items.forEach((item) => {
67
+ links[relationtype][item.source.UID] = item.source;
68
+ });
69
+ });
70
+ }
71
+
72
+ let links_ordered = {};
73
+ Object.keys(links).forEach((relationtype) => {
74
+ links_ordered[relationtype] = Object.values(links[relationtype]).sort(
75
+ (link) => link['@id'],
76
+ );
77
+ });
78
+
79
+ const relations_found = Object.keys(links_ordered).length > 0;
80
+ return (
81
+ <Container id="linkstoitem">
82
+ <Helmet title={intl.formatMessage(messages.linkstoitem)} />
83
+ <Segment.Group raised>
84
+ <Segment className="primary">
85
+ <FormattedMessage
86
+ id="Content that links to or references {title}"
87
+ defaultMessage="Content that links to or references {title}"
88
+ values={{ title: <q>{title}</q> }}
89
+ />
90
+ </Segment>
91
+ {relations_found ? (
92
+ <Table selectable compact singleLine attached>
93
+ {
94
+ <Table.Body>
95
+ {Object.keys(links_ordered).map((relationtype) => {
96
+ return [].concat(
97
+ [
98
+ <Table.Row>
99
+ <Table.HeaderCell>
100
+ {relationtype === 'isReferencing' ? (
101
+ <FormattedMessage
102
+ id="Linking this item with hyperlink in text"
103
+ defaultMessage="Linking this item with hyperlink in text"
104
+ />
105
+ ) : relationtype === 'relatedItems' ? (
106
+ <FormattedMessage
107
+ id="Referencing this item as related item"
108
+ defaultMessage="Referencing this item as related item"
109
+ />
110
+ ) : (
111
+ <>
112
+ <FormattedMessage
113
+ id="Referencing this item with {relationship}"
114
+ defaultMessage="Referencing this item with {relationship}"
115
+ values={{ relationship: <q>{relationtype}</q> }}
116
+ />
117
+ </>
118
+ )}
119
+ </Table.HeaderCell>
120
+ <Table.HeaderCell>
121
+ <FormattedMessage
122
+ id="Review state"
123
+ defaultMessage="Review state"
124
+ />
125
+ </Table.HeaderCell>
126
+ <Table.HeaderCell>
127
+ <FormattedMessage id="Type" defaultMessage="Type" />
128
+ </Table.HeaderCell>
129
+ </Table.Row>,
130
+ ],
131
+ links_ordered[relationtype].map((link) => {
132
+ return (
133
+ <Table.Row key={link['@id']}>
134
+ <Table.Cell>
135
+ <UniversalLink
136
+ href={link['@id']}
137
+ className={`source ${link.review_state}`}
138
+ >
139
+ <span className="label" title={link.type_title}>
140
+ {link.title}
141
+ </span>
142
+ </UniversalLink>
143
+ </Table.Cell>
144
+ <Table.Cell>
145
+ <span>{link.review_state}</span>
146
+ </Table.Cell>
147
+ <Table.Cell>
148
+ <span>{link.type_title || ''}</span>
149
+ </Table.Cell>
150
+ </Table.Row>
151
+ );
152
+ }),
153
+ );
154
+ })}
155
+ </Table.Body>
156
+ }
157
+ </Table>
158
+ ) : (
159
+ <Segment secondary>
160
+ <FormattedMessage
161
+ id="No links to this item found."
162
+ defaultMessage="No links to this item found."
163
+ />
164
+ </Segment>
165
+ )}
166
+ </Segment.Group>
167
+ {__CLIENT__ && (
168
+ <Portal node={document.getElementById('toolbar')}>
169
+ <Toolbar
170
+ pathname={pathname}
171
+ hideDefaultViewButtons
172
+ inner={
173
+ <>
174
+ <Link to={itempath} className="item">
175
+ <IconNext
176
+ name={backSVG}
177
+ className="contents circled"
178
+ size="30px"
179
+ title={intl.formatMessage(messages.back)}
180
+ />
181
+ </Link>
182
+
183
+ <>
184
+ {ploneSetupAction ? (
185
+ <Link to="/controlpanel/relations" className="relations">
186
+ <IconNext
187
+ name={settingsSVG}
188
+ className="circled"
189
+ aria-label={intl.formatMessage(
190
+ messages.helpLinkRelationsControlPanel,
191
+ )}
192
+ size="30px"
193
+ title={intl.formatMessage(
194
+ messages.helpLinkRelationsControlPanel,
195
+ )}
196
+ />
197
+ </Link>
198
+ ) : null}
199
+ </>
200
+ </>
201
+ }
202
+ />
203
+ </Portal>
204
+ )}
205
+ </Container>
206
+ );
207
+ };
208
+
209
+ export default LinksToItem;
@@ -0,0 +1,97 @@
1
+ import React from 'react';
2
+ import renderer from 'react-test-renderer';
3
+ import { Provider } from 'react-intl-redux';
4
+ import configureMockStore from 'redux-mock-store';
5
+ import thunk from 'redux-thunk';
6
+
7
+ import LinksToItem from './LinksToItem';
8
+
9
+ const middlewares = [thunk];
10
+ const mockStore = configureMockStore(middlewares);
11
+
12
+ jest.mock('react-portal', () => ({
13
+ Portal: jest.fn(() => <div id="Portal" />),
14
+ }));
15
+ jest.mock('../Toolbar/More', () => jest.fn(() => <div className="More" />));
16
+
17
+ describe('LinksToItem', () => {
18
+ it('renders "links and references" view', () => {
19
+ const store = mockStore({
20
+ relations: {
21
+ subrequests: {
22
+ '/page-1': {
23
+ relations: {
24
+ isReferencing: {
25
+ items: [
26
+ {
27
+ source: {
28
+ '@id': 'http://localhost:3000/page-basil',
29
+ '@type': 'Document',
30
+ UID: 'SOMEUID008',
31
+ description: '',
32
+ review_state: 'published',
33
+ title: 'Basil',
34
+ type_title: 'Document',
35
+ },
36
+ target: {
37
+ '@id': 'http://localhost:3000/page-tomato',
38
+ '@type': 'Document',
39
+ UID: 'SOMEUID007',
40
+ description: '',
41
+ review_state: 'published',
42
+ title: 'Tomato',
43
+ type_title: 'Document',
44
+ },
45
+ },
46
+ ],
47
+ items_total: 1,
48
+ },
49
+ relatedItems: {
50
+ items: [
51
+ {
52
+ source: {
53
+ '@id': 'http://localhost:3000/page-cucumber',
54
+ '@type': 'Document',
55
+ UID: 'SOMEUID008',
56
+ description: '',
57
+ review_state: 'published',
58
+ title: 'Cucumber',
59
+ type_title: 'Document',
60
+ },
61
+ target: {
62
+ '@id': 'http://localhost:3000/page-tomato',
63
+ '@type': 'Document',
64
+ UID: 'SOMEUID007',
65
+ description: '',
66
+ review_state: 'published',
67
+ title: 'Tomato',
68
+ type_title: 'Document',
69
+ },
70
+ },
71
+ ],
72
+ items_total: 1,
73
+ },
74
+ },
75
+ },
76
+ },
77
+ },
78
+ content: {
79
+ data: {
80
+ UID: 'SOMEUID007',
81
+ title: 'page #1',
82
+ },
83
+ },
84
+ intl: {
85
+ locale: 'en',
86
+ messages: {},
87
+ },
88
+ });
89
+ const component = renderer.create(
90
+ <Provider store={store}>
91
+ <LinksToItem location={{ pathname: '/page-1/links-to-item' }} />
92
+ </Provider>,
93
+ );
94
+ const json = component.toJSON();
95
+ expect(json).toMatchSnapshot();
96
+ });
97
+ });
@@ -6,7 +6,7 @@ import {
6
6
  getTranslationLocator,
7
7
  getContent,
8
8
  } from '@plone/volto/actions';
9
- import { flattenToAppURL, normalizeLanguageName } from '@plone/volto/helpers';
9
+ import { flattenToAppURL, toGettextLang } from '@plone/volto/helpers';
10
10
  import config from '@plone/volto/registry';
11
11
 
12
12
  const CreateTranslation = (props) => {
@@ -33,7 +33,7 @@ const CreateTranslation = (props) => {
33
33
  return () => {
34
34
  // We change the interface language
35
35
  if (config.settings.supportedLanguages.includes(language)) {
36
- const langFileName = normalizeLanguageName(language);
36
+ const langFileName = toGettextLang(language);
37
37
  import('@root/../locales/' + langFileName + '.json').then((locale) => {
38
38
  dispatch(changeLanguage(language, locale.default));
39
39
  });
@@ -15,7 +15,8 @@ import {
15
15
  Api,
16
16
  flattenToAppURL,
17
17
  langmap,
18
- normalizeLanguageName,
18
+ toGettextLang,
19
+ toReactIntlLang,
19
20
  } from '@plone/volto/helpers';
20
21
  import { createBrowserHistory } from 'history';
21
22
  const messages = defineMessages({
@@ -55,9 +56,9 @@ const TranslationObject = ({
55
56
  setLoadingLocale(true);
56
57
  let lang =
57
58
  config.settings.supportedLanguages[Object.keys(locales).length];
58
- const langFileName = normalizeLanguageName(lang);
59
+ const langFileName = toGettextLang(lang);
59
60
  import('@root/../locales/' + langFileName + '.json').then((locale) => {
60
- setLocales({ ...locales, [lang]: locale.default });
61
+ setLocales({ ...locales, [toReactIntlLang(lang)]: locale.default });
61
62
  setLoadingLocale(false);
62
63
  });
63
64
  }
@@ -43,8 +43,8 @@ const messages = defineMessages({
43
43
  defaultMessage: 'New password',
44
44
  },
45
45
  newPasswordDescription: {
46
- id: 'Enter your new password. Minimum 5 characters.',
47
- defaultMessage: 'Enter your new password. Minimum 5 characters.',
46
+ id: 'Enter your new password. Minimum 8 characters.',
47
+ defaultMessage: 'Enter your new password. Minimum 8 characters.',
48
48
  },
49
49
  newPasswordRepeatTitle: {
50
50
  id: 'Confirm password',
@@ -15,7 +15,7 @@ import { toast } from 'react-toastify';
15
15
  import { Form, Toast } from '@plone/volto/components';
16
16
  import languages from '@plone/volto/constants/Languages';
17
17
  import { changeLanguage } from '@plone/volto/actions';
18
- import { normalizeLanguageName } from '@plone/volto/helpers';
18
+ import { toGettextLang } from '@plone/volto/helpers';
19
19
  import config from '@plone/volto/registry';
20
20
 
21
21
  const messages = defineMessages({
@@ -86,7 +86,7 @@ class PersonalPreferences extends Component {
86
86
  onSubmit(data) {
87
87
  let language = data.language || 'en';
88
88
  if (config.settings.supportedLanguages.includes(language)) {
89
- const langFileName = normalizeLanguageName(language);
89
+ const langFileName = toGettextLang(language);
90
90
  import('@root/../locales/' + langFileName + '.json').then((locale) => {
91
91
  this.props.changeLanguage(language, locale.default);
92
92
  });
@@ -14,7 +14,7 @@ import { Portal } from 'react-portal';
14
14
  import {
15
15
  Button,
16
16
  Checkbox,
17
- Container,
17
+ Container as SemanticContainer,
18
18
  Form,
19
19
  Icon as IconOld,
20
20
  Input,
@@ -28,6 +28,7 @@ import { updateSharing, getSharing } from '@plone/volto/actions';
28
28
  import { getBaseUrl } from '@plone/volto/helpers';
29
29
  import { Icon, Toolbar, Toast } from '@plone/volto/components';
30
30
  import { toast } from 'react-toastify';
31
+ import config from '@plone/volto/registry';
31
32
 
32
33
  import aheadSVG from '@plone/volto/icons/ahead.svg';
33
34
  import clearSVG from '@plone/volto/icons/clear.svg';
@@ -288,6 +289,9 @@ class SharingComponent extends Component {
288
289
  * @returns {string} Markup for the component.
289
290
  */
290
291
  render() {
292
+ const Container =
293
+ config.getComponent({ name: 'Container' }).component || SemanticContainer;
294
+
291
295
  return (
292
296
  <Container id="page-sharing">
293
297
  <Helmet title={this.props.intl.formatMessage(messages.sharing)} />
@@ -0,0 +1,38 @@
1
+ import PropTypes from 'prop-types';
2
+ import { useIntl } from 'react-intl';
3
+ import { Button, Grid, Message } from 'semantic-ui-react';
4
+
5
+ const TemplateChooser = ({ templates, onSelectTemplate }) => {
6
+ const intl = useIntl();
7
+ return (
8
+ <div className="template-chooser">
9
+ <Message>
10
+ <Grid columns={templates(intl).length}>
11
+ {templates(intl).map((template, index) => (
12
+ <Grid.Column key={template.id}>
13
+ <Button
14
+ className="template-chooser-item"
15
+ onClick={() => onSelectTemplate(index)}
16
+ >
17
+ <img src={template.image} alt="" />
18
+ <div className="template-chooser-title">
19
+ {intl.formatMessage({
20
+ id: template.id,
21
+ defaultMessage: template.title,
22
+ })}
23
+ </div>
24
+ </Button>
25
+ </Grid.Column>
26
+ ))}
27
+ </Grid>
28
+ </Message>
29
+ </div>
30
+ );
31
+ };
32
+
33
+ TemplateChooser.propTypes = {
34
+ templates: PropTypes.func.isRequired,
35
+ onSelectTemplate: PropTypes.func.isRequired,
36
+ };
37
+
38
+ export default TemplateChooser;
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+ import renderer from 'react-test-renderer';
3
+ import configureStore from 'redux-mock-store';
4
+ import { Provider } from 'react-intl-redux';
5
+ import TemplateChooser from './TemplateChooser';
6
+ import templateSVG from './template.svg';
7
+
8
+ const mockStore = configureStore();
9
+
10
+ test('renders a TemplateChooser component', () => {
11
+ const store = mockStore({
12
+ intl: {
13
+ locale: 'en',
14
+ messages: { templateid: 'Template default translation' },
15
+ },
16
+ });
17
+
18
+ const component = renderer.create(
19
+ <Provider store={store}>
20
+ <TemplateChooser
21
+ templates={() => [
22
+ {
23
+ image: templateSVG,
24
+ id: 'templateid',
25
+ defaultMessage: 'Template default translation',
26
+ },
27
+ ]}
28
+ onSelectTemplate={() => {}}
29
+ />
30
+ </Provider>,
31
+ );
32
+ const json = component.toJSON();
33
+ expect(json).toMatchSnapshot();
34
+ });
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 96 96">
2
+ <g fill="none" fill-rule="evenodd">
3
+ <rect width="96" height="96" fill="#9FD1E5" rx="3"/>
4
+ <g fill="#FFF" opacity=".9" transform="translate(9 21)">
5
+ <rect width="78" height="29"/>
6
+ <rect width="36" height="8" y="33"/>
7
+ <rect width="77" height="10" y="45"/>
8
+ </g>
9
+ </g>
10
+ </svg>
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { Icon } from '@plone/volto/components';
3
+ import Icon from '@plone/volto/components/theme/Icon/Icon';
4
4
 
5
5
  import successSVG from '@plone/volto/icons/ready.svg';
6
6
  import infoSVG from '@plone/volto/icons/info.svg';
@@ -15,7 +15,7 @@ const Toast = (props) => {
15
15
  return successSVG;
16
16
  } else if (props.error) {
17
17
  return errorSVG;
18
- } else if (props.error) {
18
+ } else if (props.warning) {
19
19
  return warningSVG;
20
20
  } else {
21
21
  return successSVG;
@@ -53,6 +53,10 @@ const messages = defineMessages({
53
53
  id: 'URL Management',
54
54
  defaultMessage: 'URL Management',
55
55
  },
56
+ linkstoitem: {
57
+ id: 'Links and references',
58
+ defaultMessage: 'Links and references',
59
+ },
56
60
  ManageTranslations: {
57
61
  id: 'Manage Translations',
58
62
  defaultMessage: 'Manage Translations',
@@ -227,6 +231,7 @@ class More extends Component {
227
231
  const aliasesAction = find(this.props.actions.object_buttons, {
228
232
  id: 'redirection',
229
233
  });
234
+
230
235
  const { content, intl } = this.props;
231
236
 
232
237
  const dateOptions = {
@@ -317,6 +322,16 @@ class More extends Component {
317
322
  </li>
318
323
  )}
319
324
  </Plug>
325
+ {path !== '' && !config.settings.excludeLinksAndReferencesMenuItem && (
326
+ <Plug pluggable="toolbar-more-menu-list" id="linkstoitems">
327
+ <li>
328
+ <Link to={`${path}/links-to-item`}>
329
+ {this.props.intl.formatMessage(messages.linkstoitem)}
330
+ <Icon name={rightArrowSVG} size="24px" />
331
+ </Link>
332
+ </li>
333
+ </Plug>
334
+ )}
320
335
  <Plug pluggable="toolbar-more-menu-list" id="rules">
321
336
  {rulesAction && (
322
337
  <li>
@@ -4,7 +4,7 @@ import { connect } from 'react-redux';
4
4
  import { Link } from 'react-router-dom';
5
5
  import { filter, find, isEmpty, map } from 'lodash';
6
6
  import { FormattedMessage } from 'react-intl';
7
- import { flattenToAppURL, langmap } from '@plone/volto/helpers';
7
+ import { flattenToAppURL, langmap, toBackendLang } from '@plone/volto/helpers';
8
8
  import config from '@plone/volto/registry';
9
9
 
10
10
  const Types = ({ types, pathname, content, currentLanguage }) => {
@@ -59,7 +59,7 @@ const Types = ({ types, pathname, content, currentLanguage }) => {
59
59
  find(content['@components'].translations.items, {
60
60
  language: lang,
61
61
  }),
62
- ) && currentLanguage !== lang,
62
+ ) && toBackendLang(currentLanguage) !== lang,
63
63
  );
64
64
 
65
65
  return (
@@ -12,7 +12,6 @@ import {
12
12
  isInternalURL,
13
13
  URLUtils,
14
14
  } from '@plone/volto/helpers/Url/Url';
15
- import { matchPath } from 'react-router';
16
15
 
17
16
  import config from '@plone/volto/registry';
18
17
 
@@ -66,11 +65,8 @@ const UniversalLink = ({
66
65
  }
67
66
  }
68
67
 
69
- const isBlacklisted =
70
- (config.settings.externalRoutes ?? []).find((route) =>
71
- matchPath(flattenToAppURL(url), route.match),
72
- )?.length > 0;
73
- const isExternal = !isInternalURL(url) || isBlacklisted;
68
+ const isExternal = !isInternalURL(url);
69
+
74
70
  const isDownload = (!isExternal && url.includes('@@download')) || download;
75
71
  const isDisplayFile =
76
72
  (!isExternal && url.includes('@@display-file')) || false;
@@ -157,6 +157,42 @@ describe('UniversalLink', () => {
157
157
  );
158
158
  });
159
159
 
160
+ it('UniversalLink renders external link where link is blacklisted', () => {
161
+ const notInEN = /^(?!.*(#|\/en|\/static|\/controlpanel|\/cypress|\/login|\/logout|\/contact-form)).*$/;
162
+ config.settings.externalRoutes = [
163
+ {
164
+ match: {
165
+ path: notInEN,
166
+ exact: false,
167
+ strict: false,
168
+ },
169
+ url(payload) {
170
+ return payload.location.pathname;
171
+ },
172
+ },
173
+ ];
174
+
175
+ const { getByTitle } = render(
176
+ <Provider store={store}>
177
+ <MemoryRouter>
178
+ <UniversalLink
179
+ href="http://localhost:3000/blacklisted-app"
180
+ title="External blacklisted app"
181
+ >
182
+ <h1>Title</h1>
183
+ </UniversalLink>
184
+ </MemoryRouter>
185
+ </Provider>,
186
+ );
187
+
188
+ expect(getByTitle('External blacklisted app').getAttribute('target')).toBe(
189
+ '_blank',
190
+ );
191
+ expect(getByTitle('External blacklisted app').getAttribute('rel')).toBe(
192
+ 'noopener noreferrer',
193
+ );
194
+ });
195
+
160
196
  it('check UniversalLink does not break with error in item', () => {
161
197
  const component = renderer.create(
162
198
  <Provider store={store}>
@@ -51,7 +51,12 @@ const ColorPickerWidget = (props) => {
51
51
  onClick={(e) => {
52
52
  e.preventDefault();
53
53
  e.stopPropagation();
54
- onChange(id, color.name);
54
+ onChange(
55
+ id,
56
+ value === color.name
57
+ ? props.missing_value
58
+ : color.name,
59
+ );
55
60
  }}
56
61
  active={value === color.name}
57
62
  circular