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

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 (318) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +549 -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 +58 -5
  8. package/cypress/support/e2e.js +1 -2
  9. package/cypress/support/volto-slate.js +4 -5
  10. package/docker-compose.yml +1 -1
  11. package/locales/ca/LC_MESSAGES/volto.po +282 -7
  12. package/locales/ca.json +1 -1
  13. package/locales/de/LC_MESSAGES/volto.po +301 -26
  14. package/locales/de.json +1 -1
  15. package/locales/en/LC_MESSAGES/volto.po +281 -6
  16. package/locales/en.json +1 -1
  17. package/locales/es/LC_MESSAGES/volto.po +291 -16
  18. package/locales/es.json +1 -1
  19. package/locales/eu/LC_MESSAGES/volto.po +282 -7
  20. package/locales/eu.json +1 -1
  21. package/locales/fi/LC_MESSAGES/volto.po +4891 -0
  22. package/locales/fi.json +1 -1
  23. package/locales/fr/LC_MESSAGES/volto.po +282 -7
  24. package/locales/fr.json +1 -1
  25. package/locales/it/LC_MESSAGES/volto.po +284 -9
  26. package/locales/it.json +1 -1
  27. package/locales/ja/LC_MESSAGES/volto.po +282 -7
  28. package/locales/ja.json +1 -1
  29. package/locales/nl/LC_MESSAGES/volto.po +937 -650
  30. package/locales/nl.json +1 -1
  31. package/locales/pt/LC_MESSAGES/volto.po +282 -7
  32. package/locales/pt.json +1 -1
  33. package/locales/pt_BR/LC_MESSAGES/volto.po +291 -16
  34. package/locales/pt_BR.json +1 -1
  35. package/locales/ro/LC_MESSAGES/volto.po +282 -7
  36. package/locales/ro.json +1 -1
  37. package/locales/volto.pot +282 -7
  38. package/locales/zh_CN/LC_MESSAGES/volto.po +282 -7
  39. package/locales/zh_CN.json +1 -1
  40. package/package.json +8 -6
  41. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +1 -1
  42. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +1 -1
  43. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +1 -1
  44. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +1 -1
  45. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +1 -1
  46. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +1 -1
  47. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +1 -1
  48. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +1 -1
  49. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +1 -1
  50. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +1 -1
  51. package/packages/volto-slate/package.json +1 -1
  52. package/packages/volto-slate/src/actions/index.js +1 -1
  53. package/packages/volto-slate/src/blocks/Table/TableBlockEdit.jsx +21 -212
  54. package/packages/volto-slate/src/blocks/Table/schema.js +122 -0
  55. package/packages/volto-slate/src/blocks/Text/DefaultTextBlockEditor.jsx +8 -3
  56. package/packages/volto-slate/src/blocks/Text/SlashMenu.jsx +4 -3
  57. package/packages/volto-slate/src/blocks/Text/TextBlockView.jsx +20 -16
  58. package/packages/volto-slate/src/blocks/Text/extensions/withDeserializers.js +3 -1
  59. package/packages/volto-slate/src/blocks/Text/index.js +10 -2
  60. package/packages/volto-slate/src/editor/config.jsx +5 -4
  61. package/packages/volto-slate/src/editor/deserialize.js +0 -1
  62. package/packages/volto-slate/src/editor/index.js +4 -4
  63. package/packages/volto-slate/src/editor/less/slate.less +28 -0
  64. package/packages/volto-slate/src/editor/plugins/StyleMenu/StyleMenu.jsx +14 -4
  65. package/packages/volto-slate/src/editor/plugins/StyleMenu/utils.js +14 -5
  66. package/packages/volto-slate/src/editor/render.jsx +68 -8
  67. package/packages/volto-slate/src/editor/ui/SlateContextToolbar.jsx +2 -2
  68. package/packages/volto-slate/src/editor/ui/index.js +15 -15
  69. package/packages/volto-slate/src/index.js +2 -2
  70. package/packages/volto-slate/src/utils/blocks.js +7 -0
  71. package/packages/volto-slate/src/widgets/RichTextWidget.jsx +15 -8
  72. package/razzle.config.js +28 -0
  73. package/src/actions/index.js +6 -0
  74. package/src/actions/language/language.js +9 -8
  75. package/src/actions/querystringsearch/querystringsearch.js +20 -14
  76. package/src/actions/relations/rebuild.js +25 -0
  77. package/src/actions/relations/relations.js +86 -0
  78. package/src/actions/relations/relations.test.js +15 -0
  79. package/src/components/index.js +3 -0
  80. package/src/components/manage/Add/Add.jsx +2 -2
  81. package/src/components/manage/AnchorPlugin/index.jsx +2 -2
  82. package/src/components/manage/AnchorPlugin/utils/EditorUtils.js +3 -1
  83. package/src/components/manage/BlockChooser/BlockChooser.jsx +14 -5
  84. package/src/components/manage/BlockChooser/BlockChooser.test.jsx +5 -0
  85. package/src/components/manage/Blocks/Block/BlocksForm.jsx +19 -2
  86. package/src/components/manage/Blocks/Block/Edit.jsx +1 -1
  87. package/src/components/manage/Blocks/Block/Style.jsx +2 -2
  88. package/src/components/manage/Blocks/Container/Data.jsx +32 -0
  89. package/src/components/manage/Blocks/Container/Edit.jsx +174 -0
  90. package/src/components/manage/Blocks/Container/EditBlockWrapper.jsx +120 -0
  91. package/src/components/manage/Blocks/Container/NewBlockAddButton.jsx +84 -0
  92. package/src/components/manage/Blocks/Container/SimpleContainerToolbar.jsx +54 -0
  93. package/src/components/manage/Blocks/Grid/Edit.jsx +33 -0
  94. package/src/components/manage/Blocks/Grid/View.jsx +43 -0
  95. package/src/components/manage/Blocks/Grid/adapter.js +14 -0
  96. package/src/components/manage/Blocks/Grid/grid-1.svg +6 -0
  97. package/src/components/manage/Blocks/Grid/grid-2.svg +9 -0
  98. package/src/components/manage/Blocks/Grid/grid-3.svg +10 -0
  99. package/src/components/manage/Blocks/Grid/grid-4.svg +11 -0
  100. package/src/components/manage/Blocks/Grid/schema.js +35 -0
  101. package/src/components/manage/Blocks/Grid/templates.js +47 -0
  102. package/src/components/manage/Blocks/HeroImageLeft/Edit.jsx +6 -1
  103. package/src/components/manage/Blocks/Image/Edit.jsx +51 -12
  104. package/src/components/manage/Blocks/Image/Edit.test.jsx +2 -0
  105. package/src/components/manage/Blocks/Image/ImageSidebar.jsx +66 -16
  106. package/src/components/manage/Blocks/Image/View.jsx +25 -5
  107. package/src/components/manage/Blocks/Image/View.test.jsx +20 -0
  108. package/src/components/manage/Blocks/Image/schema.js +12 -9
  109. package/src/components/manage/Blocks/Image/utils.js +14 -0
  110. package/src/components/manage/Blocks/LeadImage/Edit.jsx +32 -10
  111. package/src/components/manage/Blocks/LeadImage/Edit.test.jsx +11 -1
  112. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.jsx +28 -9
  113. package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx +8 -2
  114. package/src/components/manage/Blocks/LeadImage/View.jsx +50 -38
  115. package/src/components/manage/Blocks/LeadImage/View.test.jsx +11 -1
  116. package/src/components/manage/Blocks/Listing/DefaultTemplate.jsx +18 -3
  117. package/src/components/manage/Blocks/Listing/Edit.jsx +0 -14
  118. package/src/components/manage/Blocks/Listing/ListingBody.jsx +30 -8
  119. package/src/components/manage/Blocks/Listing/ListingBody.test.jsx +20 -0
  120. package/src/components/manage/Blocks/Listing/SummaryTemplate.jsx +1 -1
  121. package/src/components/manage/Blocks/Listing/getAsyncData.js +9 -3
  122. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +26 -18
  123. package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +5 -4
  124. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +2 -1
  125. package/src/components/manage/Blocks/Search/components/DateRangeFacet.jsx +4 -1
  126. package/src/components/manage/Blocks/Search/components/Facets.jsx +64 -4
  127. package/src/components/manage/Blocks/Search/components/SearchInput.jsx +9 -2
  128. package/src/components/manage/Blocks/Search/components/index.js +13 -13
  129. package/src/components/manage/Blocks/Search/hocs/index.js +2 -2
  130. package/src/components/manage/Blocks/Search/hocs/withQueryString.jsx +2 -2
  131. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +43 -15
  132. package/src/components/manage/Blocks/Search/layout/LeftColumnFacets.jsx +17 -5
  133. package/src/components/manage/Blocks/Search/layout/RightColumnFacets.jsx +17 -5
  134. package/src/components/manage/Blocks/Search/layout/TopSideFacets.jsx +21 -5
  135. package/src/components/manage/Blocks/Search/schema.js +16 -1
  136. package/src/components/manage/Blocks/Teaser/Body.jsx +0 -1
  137. package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +5 -10
  138. package/src/components/manage/Blocks/Teaser/schema.js +5 -0
  139. package/src/components/manage/Blocks/Title/View.jsx +15 -5
  140. package/src/components/manage/Blocks/Title/View.test.jsx +16 -1
  141. package/src/components/manage/Blocks/ToC/Schema.jsx +5 -1
  142. package/src/components/manage/Blocks/ToC/View.jsx +8 -1
  143. package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +17 -4
  144. package/src/components/manage/Blocks/ToC/variations/HorizontalMenu.jsx +148 -10
  145. package/src/components/manage/Blocks/ToC/variations/index.js +3 -1
  146. package/src/components/manage/Contents/Contents.jsx +47 -32
  147. package/src/components/manage/Contents/ContentsItem.jsx +6 -0
  148. package/src/components/manage/Contents/ContentsUploadModal.jsx +10 -5
  149. package/src/components/manage/Controlpanels/AddonsControlpanel.jsx +3 -3
  150. package/src/components/manage/Controlpanels/Controlpanels.jsx +199 -224
  151. package/src/components/manage/Controlpanels/Controlpanels.test.jsx +46 -7
  152. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +66 -0
  153. package/src/components/manage/Controlpanels/Relations/Relations.jsx +114 -0
  154. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +479 -0
  155. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +531 -0
  156. package/src/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel.jsx +3 -3
  157. package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +51 -82
  158. package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +79 -75
  159. package/src/components/manage/DragDropList/DragDropList.jsx +18 -13
  160. package/src/components/manage/Form/Form.jsx +5 -3
  161. package/src/components/manage/Form/InlineForm.jsx +39 -9
  162. package/src/components/manage/Form/InlineFormState.js +8 -0
  163. package/src/components/manage/History/History.jsx +11 -1
  164. package/src/components/manage/LinksToItem/LinksToItem.jsx +209 -0
  165. package/src/components/manage/LinksToItem/LinksToItem.test.jsx +97 -0
  166. package/src/components/manage/Multilingual/CreateTranslation.jsx +2 -2
  167. package/src/components/manage/Multilingual/TranslationObject.jsx +4 -3
  168. package/src/components/manage/Preferences/ChangePassword.jsx +2 -2
  169. package/src/components/manage/Preferences/PersonalPreferences.jsx +2 -2
  170. package/src/components/manage/Sharing/Sharing.jsx +5 -1
  171. package/src/components/manage/Sidebar/AlignBlock.jsx +1 -1
  172. package/src/components/manage/TemplateChooser/TemplateChooser.jsx +38 -0
  173. package/src/components/manage/TemplateChooser/TemplateChooser.test.jsx +34 -0
  174. package/src/components/manage/TemplateChooser/template.svg +10 -0
  175. package/src/components/manage/Toast/Toast.jsx +2 -2
  176. package/src/components/manage/Toolbar/More.jsx +15 -0
  177. package/src/components/manage/Toolbar/Types.jsx +2 -2
  178. package/src/components/manage/UniversalLink/UniversalLink.jsx +2 -6
  179. package/src/components/manage/UniversalLink/UniversalLink.test.jsx +36 -0
  180. package/src/components/manage/Widgets/ColorPickerWidget.jsx +6 -1
  181. package/src/components/manage/Widgets/DatetimeWidget.jsx +9 -5
  182. package/src/components/manage/Widgets/FileWidget.jsx +2 -1
  183. package/src/components/manage/Widgets/ObjectListWidget.jsx +3 -8
  184. package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +2 -1
  185. package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +2 -1
  186. package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +2 -1
  187. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +8 -3
  188. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +2 -1
  189. package/src/components/manage/Widgets/SelectUtils.js +1 -1
  190. package/src/components/manage/Widgets/SelectWidget.jsx +1 -1
  191. package/src/components/theme/Anontools/Anontools.jsx +44 -72
  192. package/src/components/theme/Anontools/Anontools.stories.jsx +16 -6
  193. package/src/components/theme/Anontools/Anontools.test.jsx +16 -2
  194. package/src/components/theme/Breadcrumbs/Breadcrumbs.jsx +52 -99
  195. package/src/components/theme/Breadcrumbs/Breadcrumbs.stories.jsx +14 -13
  196. package/src/components/theme/Comments/CommentEditModal.jsx +63 -115
  197. package/src/components/theme/Component/Component.jsx +1 -1
  198. package/src/components/theme/ContactForm/ContactForm.jsx +108 -192
  199. package/src/components/theme/ContactForm/ContactForm.stories.jsx +1 -1
  200. package/src/components/theme/ContactForm/ContactForm.test.jsx +2 -3
  201. package/src/components/theme/Footer/Footer.jsx +2 -13
  202. package/src/components/theme/Header/Header.jsx +37 -63
  203. package/src/components/theme/Header/Header.test.jsx +18 -0
  204. package/src/components/theme/Icon/Icon.jsx +2 -2
  205. package/src/components/theme/Image/Image.jsx +96 -0
  206. package/src/components/theme/Image/Image.test.jsx +125 -0
  207. package/src/components/theme/LanguageSelector/LanguageSelector.js +8 -3
  208. package/src/components/theme/Login/Login.jsx +1 -0
  209. package/src/components/theme/Logo/Logo.jsx +4 -1
  210. package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +2 -2
  211. package/src/components/theme/Navigation/NavItem.jsx +4 -2
  212. package/src/components/theme/NotFound/NotFound.jsx +55 -41
  213. package/src/components/theme/PasswordReset/PasswordReset.jsx +7 -4
  214. package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +1 -1
  215. package/src/components/theme/PreviewImage/PreviewImage.jsx +25 -14
  216. package/src/components/theme/PreviewImage/PreviewImage.test.js +39 -16
  217. package/src/components/theme/SearchWidget/SearchWidget.jsx +38 -98
  218. package/src/components/theme/Sitemap/Sitemap.jsx +5 -3
  219. package/src/components/theme/View/AlbumView.jsx +20 -16
  220. package/src/components/theme/View/DefaultView.jsx +1 -1
  221. package/src/components/theme/View/EventDatesInfo.jsx +2 -1
  222. package/src/components/theme/View/EventView.jsx +36 -25
  223. package/src/components/theme/View/FileView.jsx +23 -18
  224. package/src/components/theme/View/ImageView.jsx +40 -32
  225. package/src/components/theme/View/ImageView.test.jsx +4 -0
  226. package/src/components/theme/View/LinkView.jsx +53 -78
  227. package/src/components/theme/View/ListingView.jsx +36 -28
  228. package/src/components/theme/View/NewsItemView.jsx +16 -17
  229. package/src/components/theme/View/RenderBlocks.jsx +56 -21
  230. package/src/components/theme/View/RenderEmptyBlock.jsx +5 -0
  231. package/src/components/theme/View/SummaryView.jsx +49 -39
  232. package/src/components/theme/View/TabularView.jsx +59 -53
  233. package/src/components/theme/Widgets/DateWidget.jsx +2 -1
  234. package/src/components/theme/Widgets/DatetimeWidget.jsx +2 -1
  235. package/src/components/theme/Widgets/RelationsWidget.jsx +13 -11
  236. package/src/config/Blocks.jsx +46 -0
  237. package/src/config/Components.jsx +2 -1
  238. package/src/config/ControlPanels.js +2 -0
  239. package/src/config/NonContentRoutes.jsx +1 -0
  240. package/src/config/RichTextEditor/Blocks.jsx +2 -2
  241. package/src/config/RichTextEditor/FromHTML.jsx +2 -2
  242. package/src/config/RichTextEditor/Styles.jsx +1 -1
  243. package/src/config/Widgets.jsx +2 -0
  244. package/src/config/index.js +24 -0
  245. package/src/config/server.js +2 -0
  246. package/src/constants/ActionTypes.js +4 -0
  247. package/src/constants/Indexes.js +3 -1
  248. package/src/constants/Languages.js +8 -4
  249. package/src/express-middleware/devproxy.js +1 -1
  250. package/src/express-middleware/files.js +3 -3
  251. package/src/express-middleware/images.js +4 -4
  252. package/src/express-middleware/ok.js +16 -0
  253. package/src/express-middleware/robotstxt.js +1 -1
  254. package/src/express-middleware/sitemap.js +37 -5
  255. package/src/express-middleware/static.js +3 -3
  256. package/src/helpers/Api/Api.js +1 -1
  257. package/src/helpers/Blocks/Blocks.js +48 -0
  258. package/src/helpers/Blocks/Blocks.test.js +79 -0
  259. package/src/helpers/Extensions/index.js +2 -1
  260. package/src/helpers/Extensions/withBlockSchemaEnhancer.js +15 -11
  261. package/src/helpers/Extensions/withBlockSchemaEnhancer.test.js +145 -0
  262. package/src/helpers/FormValidation/FormValidation.js +40 -2
  263. package/src/helpers/FormValidation/FormValidation.test.js +73 -0
  264. package/src/helpers/Html/Html.jsx +3 -1
  265. package/src/helpers/Html/Html.test.jsx +5 -0
  266. package/src/helpers/MessageLabels/MessageLabels.js +80 -0
  267. package/src/helpers/Robots/Robots.js +24 -6
  268. package/src/helpers/ScrollToTop/ScrollToTop.jsx +5 -3
  269. package/src/helpers/Sitemap/Sitemap.js +44 -2
  270. package/src/helpers/Url/Url.js +49 -7
  271. package/src/helpers/Url/Url.test.js +67 -0
  272. package/src/helpers/Utils/UseDetectClickOutside.stories.jsx +191 -0
  273. package/src/helpers/Utils/Utils.js +63 -13
  274. package/src/helpers/Utils/Utils.test.js +4 -4
  275. package/src/helpers/Utils/usePagination.js +67 -14
  276. package/src/helpers/Utils/usePagination.test.js +115 -0
  277. package/src/helpers/index.js +20 -10
  278. package/src/hooks/client/useClient.js +11 -0
  279. package/src/hooks/clipboard/useClipboard.js +26 -0
  280. package/src/hooks/index.js +2 -0
  281. package/src/icons/grid-block.svg +11 -0
  282. package/src/middleware/Api.test.js +54 -0
  283. package/src/middleware/api.js +24 -6
  284. package/src/middleware/index.js +2 -2
  285. package/src/reducers/actions/actions.js +8 -6
  286. package/src/reducers/actions/actions.test.js +70 -0
  287. package/src/reducers/breadcrumbs/breadcrumbs.js +1 -1
  288. package/src/reducers/index.js +2 -0
  289. package/src/reducers/navigation/navigation.js +1 -1
  290. package/src/reducers/relations/relations.js +173 -0
  291. package/src/reducers/types/types.js +1 -1
  292. package/src/routes.js +14 -0
  293. package/src/server.jsx +28 -23
  294. package/src/start-server.js +2 -2
  295. package/test-setup-config.js +11 -1
  296. package/theme/themes/pastanaga/extras/blocks.less +5 -1
  297. package/theme/themes/pastanaga/extras/contents.less +1 -0
  298. package/theme/themes/pastanaga/extras/grid.less +426 -0
  299. package/theme/themes/pastanaga/extras/main.less +8 -1
  300. package/theme/themes/pastanaga/extras/search.less +6 -0
  301. package/theme/themes/pastanaga/extras/sidebar.less +4 -0
  302. package/theme/themes/pastanaga/extras/toc.less +29 -0
  303. package/theme/themes/pastanaga/extras/userscontrolpanel.less +99 -76
  304. package/.changelog.draft +0 -31
  305. package/.editorconfig +0 -36
  306. package/.storybook/main.js +0 -127
  307. package/.storybook/manager.js +0 -15
  308. package/.storybook/preview.js +0 -21
  309. package/.storybook/static/previewImage.svg +0 -48
  310. package/.yarnrc.yml +0 -5
  311. package/jsdoc.json +0 -16
  312. package/netlify.toml +0 -5
  313. package/pyvenv.cfg +0 -3
  314. package/share/man/man1/ttx.1 +0 -225
  315. package/src/components/manage/Blocks/Teaser/utils.js +0 -44
  316. package/src/components/manage/Blocks/Teaser/utils.test.jsx +0 -229
  317. package/src/components/theme/Header/Header.md +0 -27
  318. package/towncrier.toml +0 -33
@@ -19,6 +19,8 @@ import {
19
19
  buildStyleClassNamesFromData,
20
20
  buildStyleClassNamesExtenders,
21
21
  getPreviousNextBlock,
22
+ blocksFormGenerator,
23
+ findBlocks,
22
24
  } from './Blocks';
23
25
 
24
26
  import config from '@plone/volto/registry';
@@ -1275,4 +1277,81 @@ describe('Blocks', () => {
1275
1277
  ]);
1276
1278
  });
1277
1279
  });
1280
+
1281
+ describe('blocksFormGenerator', () => {
1282
+ it('Returns an empty blocks/blocks_layout pair', () => {
1283
+ expect(blocksFormGenerator(0, '')).toEqual({
1284
+ blocks: {},
1285
+ blocks_layout: { items: [] },
1286
+ });
1287
+ });
1288
+ it('Returns a filled blocks/blocks_layout pair with type block', () => {
1289
+ const result = blocksFormGenerator(2, 'teaser');
1290
+ expect(Object.keys(result.blocks).length).toEqual(2);
1291
+ expect(result.blocks_layout.items.length).toEqual(2);
1292
+ expect(result.blocks[result.blocks_layout.items[0]]['@type']).toEqual(
1293
+ 'teaser',
1294
+ );
1295
+ expect(result.blocks[result.blocks_layout.items[1]]['@type']).toEqual(
1296
+ 'teaser',
1297
+ );
1298
+ });
1299
+ });
1300
+ });
1301
+
1302
+ describe('findBlocks', () => {
1303
+ it('Get all blocks in the first level (main block container)', () => {
1304
+ const blocks = {
1305
+ '1': { title: 'title', '@type': 'title' },
1306
+ '2': { title: 'an image', '@type': 'image' },
1307
+ '3': { title: 'description', '@type': 'description' },
1308
+ '4': { title: 'a text', '@type': 'slate' },
1309
+ };
1310
+ const types = ['description'];
1311
+ expect(findBlocks(blocks, types)).toStrictEqual(['3']);
1312
+ });
1313
+
1314
+ it('Get all blocks in the first level (main block container) given a list', () => {
1315
+ const blocks = {
1316
+ '1': { title: 'title', '@type': 'title' },
1317
+ '2': { title: 'an image', '@type': 'image' },
1318
+ '3': { title: 'description', '@type': 'description' },
1319
+ '4': { title: 'a text', '@type': 'slate' },
1320
+ };
1321
+ const types = ['description', 'slate'];
1322
+ expect(findBlocks(blocks, types)).toStrictEqual(['3', '4']);
1323
+ });
1324
+
1325
+ it('Get all blocks in the first level (main block container) given a list', () => {
1326
+ const blocks = {
1327
+ '1': { title: 'title', '@type': 'title' },
1328
+ '2': { title: 'an image', '@type': 'image' },
1329
+ '3': { title: 'description', '@type': 'description' },
1330
+ '4': { title: 'a text', '@type': 'slate' },
1331
+ '5': { title: 'a text', '@type': 'slate' },
1332
+ };
1333
+ const types = ['description', 'slate'];
1334
+ expect(findBlocks(blocks, types)).toStrictEqual(['3', '4', '5']);
1335
+ });
1336
+
1337
+ it('Get all blocks, including containers, given a list', () => {
1338
+ const blocks = {
1339
+ '1': { title: 'title', '@type': 'title' },
1340
+ '2': { title: 'an image', '@type': 'image' },
1341
+ '3': { title: 'description', '@type': 'description' },
1342
+ '4': { title: 'a text', '@type': 'slate' },
1343
+ '5': {
1344
+ title: 'a container',
1345
+ '@type': 'gridBlock',
1346
+ blocks: {
1347
+ '6': { title: 'title', '@type': 'title' },
1348
+ '7': { title: 'an image', '@type': 'image' },
1349
+ '8': { title: 'description', '@type': 'description' },
1350
+ '9': { title: 'a text', '@type': 'slate' },
1351
+ },
1352
+ },
1353
+ };
1354
+ const types = ['description', 'slate'];
1355
+ expect(findBlocks(blocks, types)).toStrictEqual(['3', '4', '8', '9']);
1356
+ });
1278
1357
  });
@@ -1,5 +1,6 @@
1
1
  export * from './withBlockSchemaEnhancer';
2
- export withBlockExtensions, {
2
+ export {
3
+ default as withBlockExtensions,
3
4
  resolveExtension,
4
5
  resolveBlockExtensions,
5
6
  } from './withBlockExtensions';
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { defineMessages } from 'react-intl';
3
3
  import { useIntl } from 'react-intl';
4
+ import { find, isEmpty } from 'lodash';
4
5
  import config from '@plone/volto/registry';
5
6
  import { cloneDeepSchema } from '@plone/volto/helpers/Utils/Utils';
6
7
 
@@ -291,20 +292,23 @@ export const EMPTY_STYLES_SCHEMA = {
291
292
  };
292
293
 
293
294
  /**
294
- * Creates the `styles` field and fieldset in a schema
295
+ * Adds the `styles` field and 'styling' fieldset in a given schema
295
296
  */
296
297
  export const addStyling = ({ schema, formData, intl }) => {
297
- schema.fieldsets.push({
298
- id: 'styling',
299
- title: intl.formatMessage(messages.styling),
300
- fields: ['styles'],
301
- });
298
+ if (isEmpty(find(schema.fieldsets, { id: 'styling' }))) {
299
+ schema.fieldsets.push({
300
+ id: 'styling',
301
+ title: intl.formatMessage(messages.styling),
302
+ fields: ['styles'],
303
+ });
304
+
305
+ schema.properties.styles = {
306
+ widget: 'object',
307
+ title: intl.formatMessage(messages.styling),
308
+ schema: cloneDeepSchema(EMPTY_STYLES_SCHEMA),
309
+ };
310
+ }
302
311
 
303
- schema.properties.styles = {
304
- widget: 'object',
305
- title: intl.formatMessage(messages.styling),
306
- schema: EMPTY_STYLES_SCHEMA,
307
- };
308
312
  return schema;
309
313
  };
310
314
 
@@ -2,6 +2,7 @@ import {
2
2
  addExtensionFieldToSchema,
3
3
  applySchemaEnhancer,
4
4
  composeSchema,
5
+ addStyling,
5
6
  } from './withBlockSchemaEnhancer';
6
7
 
7
8
  import config from '@plone/volto/registry';
@@ -246,3 +247,147 @@ describe('composeSchema', () => {
246
247
  expect(res).toStrictEqual([6, 9]);
247
248
  });
248
249
  });
250
+
251
+ describe('addStyling', () => {
252
+ it('returns an enhanced schema with the styling wrapper object on it', () => {
253
+ const intl = { formatMessage: () => 'Styling' };
254
+
255
+ const schema = {
256
+ fieldsets: [
257
+ {
258
+ id: 'default',
259
+ title: 'Default',
260
+ fields: [],
261
+ },
262
+ ],
263
+ properties: {},
264
+ required: [],
265
+ };
266
+
267
+ const result = addStyling({ schema, intl });
268
+
269
+ expect(result).toStrictEqual({
270
+ fieldsets: [
271
+ { id: 'default', title: 'Default', fields: [] },
272
+ { id: 'styling', title: 'Styling', fields: ['styles'] },
273
+ ],
274
+ properties: {
275
+ styles: {
276
+ widget: 'object',
277
+ title: 'Styling',
278
+ schema: {
279
+ fieldsets: [
280
+ {
281
+ fields: [],
282
+ id: 'default',
283
+ title: 'Default',
284
+ },
285
+ ],
286
+ properties: {},
287
+ required: [],
288
+ },
289
+ },
290
+ },
291
+ required: [],
292
+ });
293
+ });
294
+
295
+ it('multiple schema enhancers', () => {
296
+ const intl = { formatMessage: () => 'Styling' };
297
+
298
+ const schema1 = {
299
+ fieldsets: [
300
+ {
301
+ id: 'default',
302
+ title: 'Default',
303
+ fields: [],
304
+ },
305
+ ],
306
+ properties: {},
307
+ required: [],
308
+ };
309
+
310
+ const schema2 = {
311
+ fieldsets: [
312
+ {
313
+ id: 'default',
314
+ title: 'Default',
315
+ fields: [],
316
+ },
317
+ ],
318
+ properties: {},
319
+ required: [],
320
+ };
321
+
322
+ const result = addStyling({ schema: schema1, intl });
323
+
324
+ // We add some fields to the styling schema
325
+ result.properties.styles.schema.properties.align = {
326
+ widget: 'align',
327
+ title: 'align',
328
+ actions: ['left', 'right', 'center'],
329
+ default: 'left',
330
+ };
331
+
332
+ result.properties.styles.schema.fieldsets[0].fields = ['align'];
333
+
334
+ const result2 = addStyling({ schema: schema2, intl });
335
+
336
+ expect(result).toStrictEqual({
337
+ fieldsets: [
338
+ { id: 'default', title: 'Default', fields: [] },
339
+ { id: 'styling', title: 'Styling', fields: ['styles'] },
340
+ ],
341
+ properties: {
342
+ styles: {
343
+ widget: 'object',
344
+ title: 'Styling',
345
+ schema: {
346
+ fieldsets: [
347
+ {
348
+ fields: ['align'],
349
+ id: 'default',
350
+ title: 'Default',
351
+ },
352
+ ],
353
+ properties: {
354
+ align: {
355
+ widget: 'align',
356
+ title: 'align',
357
+ actions: ['left', 'right', 'center'],
358
+ default: 'left',
359
+ },
360
+ },
361
+ required: [],
362
+ },
363
+ },
364
+ },
365
+ required: [],
366
+ });
367
+
368
+ expect(result2).toStrictEqual({
369
+ fieldsets: [
370
+ { id: 'default', title: 'Default', fields: [] },
371
+ { id: 'styling', title: 'Styling', fields: ['styles'] },
372
+ ],
373
+ properties: {
374
+ styles: {
375
+ widget: 'object',
376
+ title: 'Styling',
377
+ schema: {
378
+ fieldsets: [
379
+ {
380
+ fields: [],
381
+ id: 'default',
382
+ title: 'Default',
383
+ },
384
+ ],
385
+ properties: {},
386
+ required: [],
387
+ },
388
+ },
389
+ },
390
+ required: [],
391
+ });
392
+ });
393
+ });
@@ -1,5 +1,8 @@
1
1
  import { map, uniq, keys, intersection, isEmpty } from 'lodash';
2
2
  import { messages } from '../MessageLabels/MessageLabels';
3
+ import config from '@plone/volto/registry';
4
+ import { toast } from 'react-toastify';
5
+ import Toast from '@plone/volto/components/manage/Toast/Toast';
3
6
 
4
7
  /**
5
8
  * Will return the intl message if invalid
@@ -65,7 +68,16 @@ const widgetValidation = {
65
68
  },
66
69
  url: {
67
70
  isValidURL: (urlValue, urlObj, intlFunc) => {
68
- const urlRegex = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?|^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([_.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?|^((http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/gm;
71
+ var urlRegex = new RegExp(
72
+ '^(https?:\\/\\/)?' + // validate protocol
73
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // validate domain name
74
+ '((\\d{1,3}\\.){3}\\d{1,3}))|' + // validate OR ip (v4) address
75
+ '(localhost)' + // validate OR localhost address
76
+ '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // validate port and path
77
+ '(\\?[;&a-z\\d%_.~+=-]*)?' + // validate query string
78
+ '(\\#[-a-z\\d_]*)?$', // validate fragment locator
79
+ 'i',
80
+ );
69
81
  const isValid = urlRegex.test(urlValue);
70
82
  return !isValid ? intlFunc(messages.isValidURL) : null;
71
83
  },
@@ -194,7 +206,7 @@ const validateRequiredFields = (
194
206
  const type = schema.properties[requiredField]?.type;
195
207
  const widget = schema.properties[requiredField]?.widget;
196
208
 
197
- let isEmpty = !formData[requiredField];
209
+ let isEmpty = !formData[requiredField] && formData[requiredField] !== 0;
198
210
  if (!isEmpty) {
199
211
  if (type === 'array') {
200
212
  isEmpty = formData[requiredField]
@@ -360,3 +372,29 @@ class FormValidation {
360
372
  }
361
373
 
362
374
  export default FormValidation;
375
+
376
+ /**
377
+ * Check if a file upload is within the maximum size limit.
378
+ * @param {File} file
379
+ * @param {Function} intlFunc
380
+ * @returns {Boolean}
381
+ */
382
+ export const validateFileUploadSize = (file, intlFunc) => {
383
+ const isValid =
384
+ !config.settings.maxFileUploadSize ||
385
+ file.size <= config.settings.maxFileUploadSize;
386
+ if (!isValid) {
387
+ toast.error(
388
+ <Toast
389
+ error
390
+ title={intlFunc(messages.error)}
391
+ content={intlFunc(messages.fileTooLarge, {
392
+ limit: `${Math.floor(
393
+ config.settings.maxFileUploadSize / 1024 / 1024,
394
+ )}MB`,
395
+ })}
396
+ />,
397
+ );
398
+ }
399
+ return isValid;
400
+ };
@@ -5,6 +5,7 @@ const schema = {
5
5
  properties: {
6
6
  username: { title: 'Username', type: 'string', description: '' },
7
7
  email: { title: 'Email', type: 'string', widget: 'email', description: '' },
8
+ url: { title: 'url', type: 'string', widget: 'url', description: '' },
8
9
  },
9
10
  fieldsets: [
10
11
  { id: 'default', title: 'FIXME: User Data', fields: ['username'] },
@@ -65,6 +66,38 @@ describe('FormValidation', () => {
65
66
  });
66
67
  });
67
68
 
69
+ it('do not treat 0 as missing required value', () => {
70
+ let newSchema = {
71
+ ...schema,
72
+ properties: {
73
+ ...schema.properties,
74
+ age: {
75
+ title: 'age',
76
+ type: 'integer',
77
+ widget: 'number',
78
+ description: '',
79
+ },
80
+ },
81
+ required: ['age'],
82
+ };
83
+ expect(
84
+ FormValidation.validateFieldsPerFieldset({
85
+ schema: newSchema,
86
+ formData: { username: 'test username', age: null },
87
+ formatMessage,
88
+ }),
89
+ ).toEqual({
90
+ age: [messages.required.defaultMessage],
91
+ });
92
+ expect(
93
+ FormValidation.validateFieldsPerFieldset({
94
+ schema: newSchema,
95
+ formData: { username: 'test username', age: 0 },
96
+ formatMessage,
97
+ }),
98
+ ).toEqual({});
99
+ });
100
+
68
101
  it('validates incorrect email', () => {
69
102
  expect(
70
103
  FormValidation.validateFieldsPerFieldset({
@@ -87,5 +120,45 @@ describe('FormValidation', () => {
87
120
  }),
88
121
  ).toEqual({});
89
122
  });
123
+ it('validates incorrect url', () => {
124
+ formData.url = 'foo';
125
+ expect(
126
+ FormValidation.validateFieldsPerFieldset({
127
+ schema,
128
+ formData,
129
+ formatMessage,
130
+ }),
131
+ ).toEqual({ url: [messages.isValidURL.defaultMessage] });
132
+ });
133
+ it('validates url', () => {
134
+ formData.url = 'https://plone.org/';
135
+ expect(
136
+ FormValidation.validateFieldsPerFieldset({
137
+ schema,
138
+ formData,
139
+ formatMessage,
140
+ }),
141
+ ).toEqual({});
142
+ });
143
+ it('validates url with ip', () => {
144
+ formData.url = 'http://127.0.0.1:8080/Plone';
145
+ expect(
146
+ FormValidation.validateFieldsPerFieldset({
147
+ schema,
148
+ formData,
149
+ formatMessage,
150
+ }),
151
+ ).toEqual({});
152
+ });
153
+ it('validates url with localhost', () => {
154
+ formData.url = 'http://localhost:8080/Plone';
155
+ expect(
156
+ FormValidation.validateFieldsPerFieldset({
157
+ schema,
158
+ formData,
159
+ formatMessage,
160
+ }),
161
+ ).toEqual({});
162
+ });
90
163
  });
91
164
  });
@@ -97,8 +97,10 @@ class Html extends Component {
97
97
  } = this.props;
98
98
  const head = Helmet.rewind();
99
99
  const bodyClass = join(BodyClass.rewind(), ' ');
100
+ const htmlAttributes = head.htmlAttributes.toComponent();
101
+
100
102
  return (
101
- <html lang="en">
103
+ <html lang={htmlAttributes.lang}>
102
104
  <head>
103
105
  <meta charSet="utf-8" />
104
106
  {head.base.toComponent()}
@@ -20,6 +20,11 @@ jest.mock('../Helmet/Helmet', () => ({
20
20
  script: {
21
21
  toComponent: () => '',
22
22
  },
23
+ htmlAttributes: {
24
+ toComponent: () => ({
25
+ lang: 'en',
26
+ }),
27
+ },
23
28
  }),
24
29
  }));
25
30
 
@@ -260,4 +260,84 @@ export const messages = defineMessages({
260
260
  id: 'Show groups of users below',
261
261
  defaultMessage: 'Show groups of users below',
262
262
  },
263
+ urlClipboardCopy: {
264
+ id: 'Link copied to clipboard',
265
+ defaultMessage: 'Link copied to clipboard',
266
+ },
267
+ inspectRelations: {
268
+ id: 'Inspect relations',
269
+ defaultMessage: 'Inspect relations',
270
+ },
271
+ relations: {
272
+ id: 'Relations',
273
+ defaultMessage: 'Relations',
274
+ },
275
+ fixRelations: {
276
+ id: 'Fix relations',
277
+ defaultMessage: 'Fix relations',
278
+ },
279
+ searchRelationSource: {
280
+ id: 'Search sources by title or path',
281
+ defaultMessage: 'Search sources by title or path',
282
+ },
283
+ searchRelationTarget: {
284
+ id: 'Search targets by title or path',
285
+ defaultMessage: 'Search targets by title or path',
286
+ },
287
+ createOrDeleteRelationsToTarget: {
288
+ id: 'Create or delete relations to target',
289
+ defaultMessage: 'Create or delete relations to target',
290
+ },
291
+ relationName: {
292
+ id: 'Relation name',
293
+ defaultMessage: 'relation',
294
+ },
295
+ selectRelation: {
296
+ id: 'Select relation',
297
+ defaultMessage: 'Select relation',
298
+ },
299
+ norelationfound: {
300
+ id: 'No relation found',
301
+ defaultMessage: 'No relation found',
302
+ },
303
+ toomanyrelationsfound: {
304
+ id: 'Many relations found. Please search.',
305
+ defaultMessage: 'Many relations found. Please search.',
306
+ },
307
+ rebuildRelations: {
308
+ id: 'rebuild relations',
309
+ defaultMessage: 'rebuild relations',
310
+ },
311
+ flushAndRebuildRelations: {
312
+ id: 'flush intIds and rebuild relations',
313
+ defaultMessage: 'flush intIds and rebuild relations',
314
+ },
315
+ addPotentialTargetsPath: {
316
+ id: 'target path',
317
+ defaultMessage: 'target path',
318
+ },
319
+ addPotentialSourcesPath: {
320
+ id: 'sources path',
321
+ defaultMessage: 'sources path',
322
+ },
323
+ relationsUpdated: {
324
+ id: 'Relations updated',
325
+ defaultMessage: 'Relations updated',
326
+ },
327
+ select: {
328
+ id: 'Select',
329
+ defaultMessage: 'Select',
330
+ },
331
+ selected: {
332
+ id: 'Selected',
333
+ defaultMessage: 'Selected',
334
+ },
335
+ filter: {
336
+ id: 'Filter',
337
+ defaultMessage: 'Filter',
338
+ },
339
+ fileTooLarge: {
340
+ id: 'fileTooLarge',
341
+ defaultMessage: 'This website does not accept files larger than {limit}',
342
+ },
263
343
  });
@@ -15,12 +15,9 @@ import { addHeadersFactory } from '@plone/volto/helpers/Proxy/Proxy';
15
15
  */
16
16
  export const generateRobots = (req) =>
17
17
  new Promise((resolve) => {
18
- //const url = `${req.protocol}://${req.get('Host')}`;
19
- const request = superagent.get(
20
- `${
21
- config.settings.internalApiPath ?? config.settings.apiPath
22
- }/robots.txt`,
23
- );
18
+ const internalUrl =
19
+ config.settings.internalApiPath ?? config.settings.apiPath;
20
+ const request = superagent.get(`${internalUrl}/robots.txt`);
24
21
  request.set('Accept', 'text/plain');
25
22
  const authToken = req.universalCookies.get('auth_token');
26
23
  if (authToken) {
@@ -31,6 +28,27 @@ export const generateRobots = (req) =>
31
28
  if (error) {
32
29
  resolve(text || error);
33
30
  } else {
31
+ // It appears that express does not take the x-forwarded headers into
32
+ // consideration, so we do it ourselves.
33
+ const {
34
+ 'x-forwarded-proto': forwardedProto,
35
+ 'x-forwarded-host': forwardedHost,
36
+ 'x-forwarded-port': forwardedPort,
37
+ } = req.headers;
38
+ const proto = forwardedProto ?? req.protocol;
39
+ const host = forwardedHost ?? req.get('Host');
40
+ const portNum = forwardedPort ?? req.get('Port');
41
+ const port =
42
+ (proto === 'https' && '' + portNum === '443') ||
43
+ (proto === 'http' && '' + portNum === '80')
44
+ ? ''
45
+ : `:${portNum}`;
46
+ // Plone has probably returned the sitemap link with the internal url.
47
+ // If so, let's replace it with the current one.
48
+ const url = `${proto}://${host}${port}`;
49
+ text = text.replace(internalUrl, url);
50
+ // Replace the sitemap with the sitemap index.
51
+ text = text.replace('sitemap.xml.gz', 'sitemap-index.xml');
34
52
  resolve(text);
35
53
  }
36
54
  });
@@ -28,15 +28,17 @@ class ScrollToTop extends React.Component {
28
28
  * @memberof ScrollToTop
29
29
  */
30
30
  componentDidUpdate(prevProps) {
31
+ const { location } = this.props;
31
32
  const noInitialBlocksFocus = // Do not scroll on /edit
32
33
  config.blocks?.initialBlocksFocus === null
33
34
  ? this.props.location?.pathname.slice(-5) !== '/edit'
34
35
  : true;
36
+
37
+ const isHash = location?.hash || location?.pathname.hash;
35
38
  if (
36
- !this.props.location?.hash &&
37
- !this.props.location?.pathname.hash &&
39
+ !isHash &&
38
40
  noInitialBlocksFocus &&
39
- this.props.location?.pathname !== prevProps.location?.pathname
41
+ location?.pathname !== prevProps.location?.pathname
40
42
  ) {
41
43
  window.scrollTo(0, 0);
42
44
  }