@plone/volto 19.0.0-alpha.3 → 19.0.0-alpha.31
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.
- package/.eslintignore +2 -0
- package/.eslintrc +37 -6
- package/CHANGELOG.md +548 -2
- package/README.md +23 -21
- package/babel.js +1 -9
- package/cypress/docker/prefixed-rules.yml +26 -0
- package/cypress/docker/prefixed.yml +24 -0
- package/cypress/support/commands.js +12 -6
- package/cypress/support/guillotina.js +1 -0
- package/cypress.config.js +1 -0
- package/global-test-setup.js +1 -2
- package/locales/af/LC_MESSAGES/volto.po +5500 -0
- package/locales/af.json +1 -1
- package/locales/ar/LC_MESSAGES/volto.po +5500 -0
- package/locales/ar.json +1 -1
- package/locales/bg/LC_MESSAGES/volto.po +5500 -0
- package/locales/bg.json +1 -1
- package/locales/bn/LC_MESSAGES/volto.po +5500 -0
- package/locales/bn.json +1 -1
- package/locales/ca/LC_MESSAGES/volto.po +559 -349
- package/locales/ca.json +1 -1
- package/locales/cs/LC_MESSAGES/volto.po +5500 -0
- package/locales/cs.json +1 -1
- package/locales/cy/LC_MESSAGES/volto.po +5500 -0
- package/locales/cy.json +1 -1
- package/locales/da/LC_MESSAGES/volto.po +5500 -0
- package/locales/da.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +233 -22
- package/locales/de.json +1 -1
- package/locales/el/LC_MESSAGES/volto.po +5500 -0
- package/locales/el.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +226 -11
- package/locales/en.json +1 -1
- package/locales/en_AU/LC_MESSAGES/volto.po +5500 -0
- package/locales/en_AU.json +1 -1
- package/locales/en_GB/LC_MESSAGES/volto.po +5500 -0
- package/locales/en_GB.json +1 -1
- package/locales/eo/LC_MESSAGES/volto.po +5500 -0
- package/locales/eo.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +321 -111
- package/locales/es.json +1 -1
- package/locales/et/LC_MESSAGES/volto.po +5500 -0
- package/locales/et.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +404 -194
- package/locales/eu.json +1 -1
- package/locales/fa/LC_MESSAGES/volto.po +5500 -0
- package/locales/fa.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +221 -11
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +220 -10
- package/locales/fr.json +1 -1
- package/locales/fu/LC_MESSAGES/volto.po +5500 -0
- package/locales/fu.json +1 -1
- package/locales/gl/LC_MESSAGES/volto.po +5501 -0
- package/locales/gl.json +1 -1
- package/locales/he/LC_MESSAGES/volto.po +5500 -0
- package/locales/he.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +225 -10
- package/locales/hi.json +1 -1
- package/locales/hr/LC_MESSAGES/volto.po +5500 -0
- package/locales/hr.json +1 -1
- package/locales/hu/LC_MESSAGES/volto.po +5500 -0
- package/locales/hu.json +1 -1
- package/locales/hy/LC_MESSAGES/volto.po +5500 -0
- package/locales/hy.json +1 -1
- package/locales/id/LC_MESSAGES/volto.po +5500 -0
- package/locales/id.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +239 -24
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +264 -53
- package/locales/ja.json +1 -1
- package/locales/ka/LC_MESSAGES/volto.po +5500 -0
- package/locales/ka.json +1 -1
- package/locales/kn/LC_MESSAGES/volto.po +5500 -0
- package/locales/kn.json +1 -1
- package/locales/ko/LC_MESSAGES/volto.po +5500 -0
- package/locales/ko.json +1 -1
- package/locales/lt/LC_MESSAGES/volto.po +5500 -0
- package/locales/lt.json +1 -1
- package/locales/lv/LC_MESSAGES/volto.po +5500 -0
- package/locales/lv.json +1 -1
- package/locales/mi/LC_MESSAGES/volto.po +5500 -0
- package/locales/mi.json +1 -1
- package/locales/mk/LC_MESSAGES/volto.po +5500 -0
- package/locales/mk.json +1 -1
- package/locales/my/LC_MESSAGES/volto.po +5500 -0
- package/locales/my.json +1 -1
- package/locales/nb_NO/LC_MESSAGES/volto.po +5500 -0
- package/locales/nb_NO.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +243 -32
- package/locales/nl.json +1 -1
- package/locales/nn/LC_MESSAGES/volto.po +5500 -0
- package/locales/nn.json +1 -1
- package/locales/pl/LC_MESSAGES/volto.po +5500 -0
- package/locales/pl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +869 -659
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +229 -19
- package/locales/pt_BR.json +1 -1
- package/locales/rm/LC_MESSAGES/volto.po +5500 -0
- package/locales/rm.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +252 -42
- package/locales/ro.json +1 -1
- package/locales/ru/LC_MESSAGES/volto.po +220 -10
- package/locales/ru.json +1 -1
- package/locales/sk/LC_MESSAGES/volto.po +5500 -0
- package/locales/sk.json +1 -1
- package/locales/sl/LC_MESSAGES/volto.po +5500 -0
- package/locales/sl.json +1 -1
- package/locales/sm/LC_MESSAGES/volto.po +5500 -0
- package/locales/sm.json +1 -1
- package/locales/sq/LC_MESSAGES/volto.po +5500 -0
- package/locales/sq.json +1 -1
- package/locales/sr/LC_MESSAGES/volto.po +5500 -0
- package/locales/sr.json +1 -1
- package/locales/sr@cyrl/LC_MESSAGES/volto.po +5500 -0
- package/locales/sr@cyrl.json +1 -1
- package/locales/sr@latn/LC_MESSAGES/volto.po +5500 -0
- package/locales/sr@latn.json +1 -1
- package/locales/sv/LC_MESSAGES/volto.po +5500 -0
- package/locales/sv.json +1 -1
- package/locales/ta/LC_MESSAGES/volto.po +5501 -0
- package/locales/ta.json +1 -1
- package/locales/te/LC_MESSAGES/volto.po +5500 -0
- package/locales/te.json +1 -1
- package/locales/th/LC_MESSAGES/volto.po +5500 -0
- package/locales/th.json +1 -1
- package/locales/to/LC_MESSAGES/volto.po +5500 -0
- package/locales/to.json +1 -1
- package/locales/tr/LC_MESSAGES/volto.po +5501 -0
- package/locales/tr.json +1 -1
- package/locales/uk/LC_MESSAGES/volto.po +5500 -0
- package/locales/uk.json +1 -1
- package/locales/vi/LC_MESSAGES/volto.po +5500 -0
- package/locales/vi.json +1 -1
- package/locales/volto.pot +220 -10
- package/locales/zh_CN/LC_MESSAGES/volto.po +221 -10
- package/locales/zh_CN.json +1 -1
- package/locales/zh_Hant/LC_MESSAGES/volto.po +5500 -0
- package/locales/zh_Hant.json +1 -1
- package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +5500 -0
- package/locales/zh_Hant_HK.json +1 -1
- package/package.json +73 -146
- package/razzle.config.js +32 -25
- package/src/actions/blockTypes/blockTypes.ts +24 -0
- package/src/components/manage/Actions/Actions.test.jsx +1 -5
- package/src/components/manage/Add/Add.jsx +15 -10
- package/src/components/manage/BlockChooser/BlockChooser.jsx +1 -0
- package/src/components/manage/Blocks/Block/BlocksForm.jsx +10 -7
- package/src/components/manage/Blocks/Block/BlocksForm.test.jsx +3 -14
- package/src/components/manage/Blocks/Block/EditBlockWrapper.jsx +9 -4
- package/src/components/manage/Blocks/Block/Order/Item.jsx +27 -13
- package/src/components/manage/Blocks/Block/Order/Item.test.jsx +90 -0
- package/src/components/manage/Blocks/Block/Order/Order.jsx +116 -67
- package/src/components/manage/Blocks/Block/Order/utilities.js +28 -11
- package/src/components/manage/Blocks/Block/Settings.test.jsx +1 -5
- package/src/components/manage/Blocks/Grid/View.jsx +14 -11
- package/src/components/manage/Blocks/Grid/context.js +3 -0
- package/src/components/manage/Blocks/HTML/Edit.test.jsx +1 -5
- package/src/components/manage/Blocks/Image/Edit.jsx +5 -1
- package/src/components/manage/Blocks/Image/ImageSidebar.test.jsx +2 -5
- package/src/components/manage/Blocks/LeadImage/Edit.jsx +2 -2
- package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.jsx +1 -1
- package/src/components/manage/Blocks/LeadImage/LeadImageSidebar.test.jsx +2 -5
- package/src/components/manage/Blocks/Listing/Edit.jsx +1 -0
- package/src/components/manage/Blocks/Listing/ImageGallery.jsx +6 -4
- package/src/components/manage/Blocks/Listing/ListingBody.jsx +4 -0
- package/src/components/manage/Blocks/Maps/Edit.jsx +2 -1
- package/src/components/manage/Blocks/Maps/MapsSidebar.test.jsx +1 -5
- package/src/components/manage/Blocks/Search/SearchBlockView.jsx +21 -4
- package/src/components/manage/Blocks/Search/components/DateRangeFacet.test.jsx +1 -6
- package/src/components/manage/Blocks/Search/components/SelectFacet.jsx +22 -1
- package/src/components/manage/Blocks/Search/components/SelectFacet.test.jsx +1 -6
- package/src/components/manage/Blocks/Search/components/SortOn.jsx +8 -2
- package/src/components/manage/Blocks/Search/components/ToggleFacet.jsx +14 -0
- package/src/components/manage/Blocks/Teaser/Data.jsx +21 -7
- package/src/components/manage/Blocks/Teaser/DefaultBody.jsx +11 -3
- package/src/components/manage/Blocks/Teaser/View.jsx +0 -1
- package/src/components/manage/Blocks/Teaser/utils.js +13 -0
- package/src/components/manage/Blocks/Teaser/utils.test.js +34 -0
- package/src/components/manage/Blocks/Title/Edit.jsx +5 -0
- package/src/components/manage/Blocks/Video/Body.jsx +69 -43
- package/src/components/manage/Blocks/Video/Body.test.jsx +122 -5
- package/src/components/manage/Blocks/Video/Edit.jsx +22 -3
- package/src/components/manage/Blocks/Video/Edit.test.jsx +6 -0
- package/src/components/manage/Blocks/Video/VideoSidebar.test.jsx +1 -5
- package/src/components/manage/Blocks/Video/View.jsx +1 -0
- package/src/components/manage/Blocks/Video/View.test.jsx +29 -15
- package/src/components/manage/Blocks/Video/schema.js +14 -1
- package/src/components/manage/Contents/Contents.jsx +697 -659
- package/src/components/manage/Contents/Contents.test.jsx +1 -5
- package/src/components/manage/Contents/ContentsBreadcrumbs.jsx +6 -5
- package/src/components/manage/Contents/ContentsItem.jsx +1 -1
- package/src/components/manage/Contents/ContentsPropertiesModal.test.jsx +1 -5
- package/src/components/manage/Contents/ContentsRenameModal.test.jsx +1 -5
- package/src/components/manage/Contents/ContentsTagsModal.test.jsx +1 -5
- package/src/components/manage/Contents/ContentsWorkflowModal.test.jsx +1 -5
- package/src/components/manage/Contents/DropZoneContent.jsx +338 -0
- package/src/components/manage/Contents/__mocks__/index.tsx +2 -18
- package/src/components/manage/Controlpanels/AddonsControlpanel.jsx +7 -0
- package/src/components/manage/Controlpanels/Aliases.test.jsx +1 -5
- package/src/components/manage/Controlpanels/BlockType.tsx +166 -0
- package/src/components/manage/Controlpanels/BlockTypes.tsx +145 -0
- package/src/components/manage/Controlpanels/ContentType.jsx +1 -1
- package/src/components/manage/Controlpanels/ContentType.test.jsx +1 -5
- package/src/components/manage/Controlpanels/ContentTypeSchema.jsx +1 -1
- package/src/components/manage/Controlpanels/Controlpanels.jsx +28 -5
- package/src/components/manage/Controlpanels/Controlpanels.test.jsx +10 -0
- package/src/components/manage/Controlpanels/DatabaseInformation.jsx +9 -0
- package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +3 -2
- package/src/components/manage/Controlpanels/ModerateComments.jsx +8 -0
- package/src/components/manage/Controlpanels/Relations/Relations.jsx +1 -1
- package/src/components/manage/Controlpanels/Rules/AddRule.test.jsx +1 -5
- package/src/components/manage/Controlpanels/Rules/EditRule.test.jsx +1 -5
- package/src/components/manage/Controlpanels/UndoControlpanel.test.jsx +1 -5
- package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +156 -175
- package/src/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel.test.jsx +3 -0
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +575 -631
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.test.jsx +58 -11
- package/src/components/manage/Diff/Diff.jsx +201 -298
- package/src/components/manage/Diff/Diff.test.jsx +1 -6
- package/src/components/manage/Diff/DiffField.test.jsx +1 -6
- package/src/components/manage/Display/Display.test.jsx +2 -11
- package/src/components/manage/Edit/Edit.test.jsx +1 -5
- package/src/components/manage/Form/BlockDataForm.test.jsx +1 -5
- package/src/components/manage/Form/Form.jsx +3 -3
- package/src/components/manage/Form/Form.test.jsx +1 -5
- package/src/components/manage/Form/InlineForm.jsx +2 -2
- package/src/components/manage/Form/InlineForm.test.jsx +1 -5
- package/src/components/manage/Form/ModalForm.jsx +12 -10
- package/src/components/manage/Form/ModalForm.test.jsx +27 -5
- package/src/components/manage/Form/__mocks__/index.tsx +9 -27
- package/src/components/manage/Multilingual/CompareLanguages.jsx +6 -6
- package/src/components/manage/Multilingual/CreateTranslation.jsx +16 -13
- package/src/components/manage/Multilingual/ManageTranslations.jsx +5 -5
- package/src/components/manage/Multilingual/TranslationObject.jsx +11 -8
- package/src/components/manage/Preferences/ChangePassword.test.jsx +1 -5
- package/src/components/manage/Preferences/PersonalPreferences.jsx +8 -5
- package/src/components/manage/Preferences/PersonalPreferences.test.jsx +1 -17
- package/src/components/manage/Sharing/Sharing.jsx +21 -15
- package/src/components/manage/Sidebar/ObjectBrowser.jsx +3 -0
- package/src/components/manage/Sidebar/ObjectBrowserBody.jsx +18 -2
- package/src/components/manage/Sidebar/ObjectBrowserNav.jsx +2 -1
- package/src/components/manage/Sidebar/SidebarPortal.test.tsx +42 -0
- package/src/components/manage/Sidebar/SidebarPortal.tsx +48 -0
- package/src/components/manage/TemplateChooser/TemplateChooser.jsx +2 -1
- package/src/components/manage/TemplateChooser/TemplateChooser.test.jsx +1 -0
- package/src/components/manage/Toast/Toast.jsx +32 -0
- package/src/components/manage/Toast/Toast.test.jsx +9 -5
- package/src/components/manage/Toolbar/PersonalTools.jsx +2 -1
- package/src/components/manage/Toolbar/PersonalTools.test.jsx +15 -0
- package/src/components/manage/Toolbar/Toolbar.jsx +14 -4
- package/src/components/manage/Toolbar/Types.crash.test.jsx +48 -0
- package/src/components/manage/Toolbar/Types.jsx +6 -4
- package/src/components/manage/UniversalLink/UniversalLink.test.jsx +16 -0
- package/src/components/manage/UniversalLink/UniversalLink.tsx +2 -0
- package/src/components/manage/Widgets/AlignWidget.stories.jsx +9 -0
- package/src/components/manage/Widgets/AlignWidget.test.tsx +95 -0
- package/src/components/manage/Widgets/{AlignWidget.jsx → AlignWidget.tsx} +23 -7
- package/src/components/manage/Widgets/ArrayWidget.test.jsx +1 -6
- package/src/components/manage/Widgets/BlockAlignment.stories.tsx +104 -0
- package/src/components/manage/Widgets/BlockAlignment.test.tsx +104 -0
- package/src/components/manage/Widgets/BlockAlignment.tsx +88 -0
- package/src/components/manage/Widgets/BlockWidth.stories.tsx +69 -0
- package/src/components/manage/Widgets/BlockWidth.test.tsx +62 -0
- package/src/components/manage/Widgets/BlockWidth.tsx +101 -0
- package/src/components/manage/Widgets/ButtonsWidget.stories.jsx +61 -0
- package/src/components/manage/Widgets/ButtonsWidget.test.tsx +138 -0
- package/src/components/manage/Widgets/ButtonsWidget.tsx +195 -0
- package/src/components/manage/Widgets/CheckboxGroupWidget.test.jsx +1 -6
- package/src/components/manage/Widgets/DatetimeWidget.jsx +102 -53
- package/src/components/manage/Widgets/DatetimeWidget.test.jsx +56 -6
- package/src/components/manage/Widgets/FileWidget.jsx +21 -8
- package/src/components/manage/Widgets/FormFieldWrapper.jsx +146 -168
- package/src/components/manage/Widgets/ImageWidget.jsx +177 -38
- package/src/components/manage/Widgets/InternalUrlWidget.jsx +2 -0
- package/src/components/manage/Widgets/ObjectBrowserWidget.jsx +8 -0
- package/src/components/manage/Widgets/ObjectListWidget.test.jsx +2 -11
- package/src/components/manage/Widgets/ObjectWidget.test.jsx +1 -5
- package/src/components/manage/Widgets/QueryWidget.jsx +137 -9
- package/src/components/manage/Widgets/RadioGroupWidget.test.jsx +1 -6
- package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.test.jsx +1 -6
- package/src/components/manage/Widgets/RegistryImageWidget.jsx +1 -1
- package/src/components/manage/Widgets/RegistryImageWidget.test.jsx +4 -2
- package/src/components/manage/Widgets/SchemaWidget.test.jsx +1 -6
- package/src/components/manage/Widgets/SchemaWidgetFieldset.test.jsx +1 -6
- package/src/components/manage/Widgets/SelectAutoComplete.jsx +29 -12
- package/src/components/manage/Widgets/SelectAutoComplete.test.jsx +1 -6
- package/src/components/manage/Widgets/SelectWidget.test.jsx +1 -6
- package/src/components/manage/Widgets/Size.stories.tsx +69 -0
- package/src/components/manage/Widgets/Size.test.tsx +59 -0
- package/src/components/manage/Widgets/Size.tsx +78 -0
- package/src/components/manage/Widgets/TimeWidget.test.jsx +1 -6
- package/src/components/manage/Widgets/TokenWidget.test.jsx +1 -6
- package/src/components/manage/Widgets/UrlWidget.jsx +49 -18
- package/src/components/manage/Widgets/VocabularyTermsWidget.test.jsx +2 -11
- package/src/components/manage/Widgets/__mocks__/index.tsx +33 -51
- package/src/components/manage/Widgets/index.tsx +21 -0
- package/src/components/manage/Workflow/Workflow.test.jsx +2 -11
- package/src/components/theme/AlternateHrefLangs/AlternateHrefLangs.jsx +1 -0
- package/src/components/theme/AlternateHrefLangs/AlternateHrefLangs.test.jsx +30 -0
- package/src/components/theme/App/App.jsx +5 -1
- package/src/components/theme/Avatar/Avatar.jsx +2 -1
- package/src/components/theme/Comments/CommentEditModal.test.jsx +1 -5
- package/src/components/theme/Comments/Comments.test.jsx +2 -11
- package/src/components/theme/ConnectionRefused/ConnectionRefused.jsx +3 -2
- package/src/components/theme/ContactForm/ContactForm.test.jsx +1 -5
- package/src/components/theme/Image/Image.jsx +25 -13
- package/src/components/theme/Image/Image.test.jsx +247 -146
- package/src/components/theme/InjectPloneComponentsCSS/InjectPloneComponentsCSS.tsx +7 -0
- package/src/components/theme/LanguageSelector/LanguageSelector.tsx +89 -0
- package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +42 -12
- package/src/components/theme/PasswordReset/PasswordReset.jsx +108 -191
- package/src/components/theme/PasswordReset/RequestPasswordReset.test.jsx +1 -5
- package/src/components/theme/PreviewImage/PreviewImage.jsx +1 -1
- package/src/components/theme/Register/Register.test.jsx +1 -5
- package/src/components/theme/RequestTimeout/RequestTimeout.jsx +1 -1
- package/src/components/theme/Sitemap/Sitemap.stories.jsx +82 -0
- package/src/components/theme/SlotRenderer/SlotRenderer.tsx +12 -6
- package/src/components/theme/Unauthorized/Unauthorized.jsx +35 -25
- package/src/components/theme/Unauthorized/Unauthorized.test.jsx +28 -1
- package/src/components/theme/VideoEmbed/VideoEmbed.jsx +100 -0
- package/src/components/theme/View/EventDatesInfo.test.jsx +1 -6
- package/src/components/theme/View/EventView.stories.jsx +89 -0
- package/src/components/theme/View/EventView.test.jsx +1 -6
- package/src/components/theme/View/FileView.stories.jsx +50 -0
- package/src/components/theme/View/ImageView.jsx +2 -1
- package/src/components/theme/View/ImageView.test.jsx +3 -0
- package/src/components/theme/View/LinkView.stories.jsx +57 -0
- package/src/components/theme/View/ListingView.stories.jsx +70 -0
- package/src/components/theme/View/NewsItemView.stories.jsx +58 -0
- package/src/components/theme/View/RenderBlocks.jsx +8 -10
- package/src/components/theme/View/RenderBlocks.stories.jsx +112 -0
- package/src/components/theme/View/RenderBlocks.test.jsx +14 -4
- package/src/components/theme/View/SummaryView.stories.jsx +71 -0
- package/src/components/theme/View/TabularView.stories.jsx +66 -0
- package/src/components/theme/View/View.jsx +8 -1
- package/src/components/theme/Widgets/ImageWidget.jsx +2 -1
- package/src/components/theme/Widgets/ImageWidget.test.jsx +31 -11
- package/src/config/Blocks.jsx +3 -0
- package/src/config/ControlPanels.js +3 -0
- package/src/config/Widgets.jsx +7 -0
- package/src/config/index.js +19 -12
- package/src/config/server.js +0 -2
- package/src/config/slots.js +19 -0
- package/src/config/validation.ts +8 -0
- package/src/constants/ActionTypes.js +1 -0
- package/src/express-middleware/devproxy.js +22 -5
- package/src/express-middleware/files.js +1 -0
- package/src/express-middleware/files.test.js +59 -0
- package/src/express-middleware/images.js +1 -0
- package/src/express-middleware/images.test.js +50 -0
- package/src/helpers/Api/APIResourceWithAuth.js +8 -3
- package/src/helpers/Api/Api.js +7 -4
- package/src/helpers/AsyncConnect/ssr.js +4 -1
- package/src/helpers/AuthToken/AuthToken.js +1 -6
- package/src/helpers/Blocks/Blocks.js +113 -28
- package/src/helpers/Blocks/Blocks.test.js +100 -0
- package/src/helpers/Content/Content.js +23 -0
- package/src/helpers/Content/Content.test.js +39 -0
- package/src/helpers/Content/withClientSideContent.jsx +35 -0
- package/src/helpers/Extensions/withBlockSchemaEnhancer.jsx +4 -1
- package/src/helpers/FormValidation/FormValidation.test.js +31 -0
- package/src/helpers/FormValidation/validators.ts +52 -6
- package/src/helpers/Html/Html.jsx +13 -4
- package/src/helpers/Loadable/__mocks__/Loadable.jsx +7 -22
- package/src/helpers/MessageLabels/MessageLabels.js +10 -0
- package/src/helpers/Sitemap/Sitemap.js +4 -4
- package/src/helpers/Url/Url.js +33 -2
- package/src/helpers/Url/Url.test.js +62 -0
- package/src/helpers/Utils/Utils.jsx +17 -0
- package/src/helpers/Utils/Utils.test.jsx +39 -0
- package/src/hooks/user/useUser.js +1 -1
- package/src/internalChecks.test.ts +11 -0
- package/src/middleware/api.js +17 -8
- package/src/middleware/storeProtectLoadUtils.test.js +3 -3
- package/src/reducers/blockTypes/blockTypes.js +38 -0
- package/src/reducers/content/content.js +3 -18
- package/src/reducers/diff/diff.js +5 -1
- package/src/reducers/diff/diff.test.js +60 -4
- package/src/reducers/index.js +2 -0
- package/src/reducers/querystring/querystring.js +8 -1
- package/src/reducers/users/users.js +1 -1
- package/src/routes.js +13 -1
- package/src/server.jsx +47 -13
- package/src/start-client.jsx +30 -5
- package/src/start-server.js +9 -3
- package/test-addons-loader.js +3 -0
- package/test-setup-globals.js +56 -2
- package/theme/themes/default/elements/segment.variables +9 -16
- package/theme/themes/pastanaga/collections/form.overrides +1 -1
- package/theme/themes/pastanaga/elements/segment.variables +1 -4
- package/theme/themes/pastanaga/extras/block-types.less +17 -0
- package/theme/themes/pastanaga/extras/blocks.less +19 -0
- package/theme/themes/pastanaga/extras/contents.less +75 -0
- package/theme/themes/pastanaga/extras/main.less +20 -4
- package/theme/themes/pastanaga/extras/toolbar.less +10 -5
- package/theme/themes/pastanaga/extras/videoembed.less +22 -0
- package/theme/themes/pastanaga/extras/widgets.less +79 -0
- package/tsconfig.declarations.json +1 -1
- package/tsconfig.json +4 -5
- package/types/actions/blockTypes/blockTypes.d.ts +7 -0
- package/types/components/index.d.ts +1 -1
- package/types/components/manage/Blocks/Block/Order/Item.test.d.ts +1 -0
- package/types/components/manage/Blocks/Block/Order/utilities.d.ts +2 -1
- package/types/components/manage/Blocks/Grid/context.d.ts +1 -0
- package/types/components/manage/Blocks/Teaser/utils.d.ts +5 -0
- package/types/components/manage/Blocks/Video/Body.d.ts +4 -2
- package/types/components/manage/Blocks/Video/schema.d.ts +4 -0
- package/types/components/manage/Contents/DropZoneContent.d.ts +2 -0
- package/types/components/manage/Contents/__mocks__/index.d.ts +2 -2
- package/types/components/manage/Controlpanels/BlockType.d.ts +7 -0
- package/types/components/manage/Controlpanels/BlockTypes.d.ts +7 -0
- package/types/components/manage/Controlpanels/Relations/RelationsMatrix.d.ts +1 -1
- package/types/components/manage/Controlpanels/Users/RenderUsers.d.ts +18 -2
- package/types/components/manage/Controlpanels/Users/UsersControlpanel.d.ts +6 -2
- package/types/components/manage/Controlpanels/index.d.ts +2 -2
- package/types/components/manage/Diff/Diff.d.ts +7 -2
- package/types/components/manage/Form/__mocks__/index.d.ts +8 -8
- package/types/components/manage/Multilingual/ManageTranslations.d.ts +1 -1
- package/types/components/manage/Sidebar/ObjectBrowser.d.ts +1 -1
- package/types/components/manage/Sidebar/SidebarPortal.d.ts +7 -15
- package/types/components/manage/Toolbar/Types.crash.test.d.ts +1 -0
- package/types/components/manage/Widgets/AlignWidget.d.ts +8 -10
- package/types/components/manage/Widgets/AlignWidget.stories.d.ts +1 -0
- package/types/components/manage/Widgets/BlockAlignment.d.ts +7 -0
- package/types/components/manage/Widgets/BlockAlignment.stories.d.ts +8 -0
- package/types/components/manage/Widgets/BlockWidth.d.ts +7 -0
- package/types/components/manage/Widgets/BlockWidth.stories.d.ts +6 -0
- package/types/components/manage/Widgets/ButtonsWidget.d.ts +48 -1
- package/types/components/manage/Widgets/ButtonsWidget.stories.d.ts +3 -0
- package/types/components/manage/Widgets/FormFieldWrapper.d.ts +28 -5
- package/types/components/manage/Widgets/ImageWidget.d.ts +41 -1
- package/types/components/manage/Widgets/InternalUrlWidget.d.ts +1 -1
- package/types/components/manage/Widgets/ObjectBrowserWidget.d.ts +2 -0
- package/types/components/manage/Widgets/QueryWidget.d.ts +5 -2
- package/types/components/manage/Widgets/RecurrenceWidget/Utils.d.ts +12 -18
- package/types/components/manage/Widgets/Size.d.ts +7 -0
- package/types/components/manage/Widgets/Size.stories.d.ts +6 -0
- package/types/components/manage/Widgets/UrlWidget.d.ts +1 -1
- package/types/components/manage/Widgets/__mocks__/index.d.ts +33 -33
- package/types/components/manage/Widgets/index.d.ts +11 -6
- package/types/components/theme/ConnectionRefused/ConnectionRefused.d.ts +2 -2
- package/types/components/theme/InjectPloneComponentsCSS/InjectPloneComponentsCSS.d.ts +3 -0
- package/types/components/theme/LanguageSelector/LanguageSelector.d.ts +3 -10
- package/types/components/theme/PasswordReset/PasswordReset.d.ts +6 -2
- package/types/components/theme/Sitemap/Sitemap.stories.d.ts +13 -0
- package/types/components/theme/SlotRenderer/SlotRenderer.d.ts +4 -5
- package/types/components/theme/Unauthorized/Unauthorized.d.ts +2 -2
- package/types/components/theme/VideoEmbed/VideoEmbed.d.ts +2 -0
- package/types/components/theme/View/EventView.stories.d.ts +19 -0
- package/types/components/theme/View/FileView.stories.d.ts +18 -0
- package/types/components/theme/View/LinkView.stories.d.ts +18 -0
- package/types/components/theme/View/ListingView.stories.d.ts +24 -0
- package/types/components/theme/View/NewsItemView.stories.d.ts +23 -0
- package/types/components/theme/View/RenderBlocks.stories.d.ts +23 -0
- package/types/components/theme/View/SummaryView.stories.d.ts +23 -0
- package/types/components/theme/View/TabularView.stories.d.ts +23 -0
- package/types/config/ControlPanels.d.ts +1 -0
- package/types/config/Views.d.ts +1 -1
- package/types/config/Widgets.d.ts +6 -0
- package/types/config/slots.d.ts +7 -0
- package/types/constants/ActionTypes.d.ts +1 -0
- package/types/helpers/Blocks/Blocks.d.ts +4 -0
- package/types/helpers/Content/Content.d.ts +7 -0
- package/types/helpers/Content/withClientSideContent.d.ts +1 -0
- package/types/helpers/Extensions/withBlockSchemaEnhancer.d.ts +4 -5
- package/types/helpers/FormValidation/validators.d.ts +18 -1
- package/types/helpers/Helmet/Helmet.d.ts +1 -1
- package/types/helpers/Loadable/__mocks__/Loadable.d.ts +2 -2
- package/types/helpers/MessageLabels/MessageLabels.d.ts +105 -93
- package/types/helpers/Url/Url.d.ts +14 -0
- package/types/helpers/Url/bulkFlattenToAppURL.d.ts +5 -0
- package/types/helpers/Utils/Utils.d.ts +1 -0
- package/types/reducers/blockTypes/blockTypes.d.ts +16 -0
- package/types/reducers/index.d.ts +3 -0
- package/types/routes.d.ts +7 -5
- package/types/start-client.d.ts +0 -1
- package/vitest.config.mjs +84 -42
- package/webpack-plugins/webpack-less-plugin.js +1 -1
- package/webpack-plugins/webpack-scss-plugin.js +172 -0
- package/cypress/downloads/downloads.html +0 -0
- package/jest-addons-loader.js +0 -3
- package/jest-extender-plugin.js +0 -39
- package/jest-setup-afterenv.js +0 -2
- package/jest-svgsystem-transform.js +0 -10
- package/package-why.json +0 -34
- package/patches/patchit.sh +0 -2
- package/patches/razzle-jest.patch +0 -10
- package/src/components/manage/Contents/__mocks__/index.vitest.tsx +0 -5
- package/src/components/manage/Form/__mocks__/index.vitest.tsx +0 -73
- package/src/components/manage/Sidebar/SidebarPortal.jsx +0 -47
- package/src/components/manage/Sidebar/SidebarPortal.test.jsx +0 -26
- package/src/components/manage/Widgets/AlignWidget.test.jsx +0 -59
- package/src/components/manage/Widgets/ButtonsWidget.jsx +0 -41
- package/src/components/manage/Widgets/ButtonsWidget.test.jsx +0 -70
- package/src/components/manage/Widgets/__mocks__/index.vitest.tsx +0 -41
- package/src/components/theme/LanguageSelector/LanguageSelector.jsx +0 -79
- package/src/helpers/Loadable/__mocks__/Loadable.vitest.jsx +0 -39
- package/test-setup-globals-vitest.js +0 -46
- package/theme/themes/pastanaga/extras/utils.less +0 -63
- /package/src/components/theme/LanguageSelector/{LanguageSelector.test.jsx → LanguageSelector.test.tsx} +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import imagesMiddleware from './images';
|
|
2
|
+
import { getAPIResourceWithAuth } from '@plone/volto/helpers/Api/APIResourceWithAuth';
|
|
3
|
+
|
|
4
|
+
vi.mock('@plone/volto/helpers/Api/APIResourceWithAuth', () => ({
|
|
5
|
+
getAPIResourceWithAuth: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
describe('images middleware', () => {
|
|
9
|
+
it('handles requests containing @@images and forwards headers/body', async () => {
|
|
10
|
+
getAPIResourceWithAuth.mockResolvedValue({
|
|
11
|
+
headers: {
|
|
12
|
+
'content-type': 'image/png',
|
|
13
|
+
'cache-status': 'Souin; hit; ttl=664',
|
|
14
|
+
},
|
|
15
|
+
body: 'OK',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const mw = imagesMiddleware();
|
|
19
|
+
const layer = mw.stack.find(
|
|
20
|
+
(l) => l.regexp && l.regexp.source.includes('@@images'),
|
|
21
|
+
);
|
|
22
|
+
expect(layer).toBeTruthy();
|
|
23
|
+
|
|
24
|
+
const req = {
|
|
25
|
+
path: '/some/@@images/image',
|
|
26
|
+
method: 'GET',
|
|
27
|
+
app: { locals: {} },
|
|
28
|
+
};
|
|
29
|
+
const res = {
|
|
30
|
+
headers: {},
|
|
31
|
+
set(name, val) {
|
|
32
|
+
this.headers[name] = val;
|
|
33
|
+
},
|
|
34
|
+
send(body) {
|
|
35
|
+
this.body = body;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// invoke the middleware handler for that layer
|
|
40
|
+
layer.handle(req, res, (err) => {
|
|
41
|
+
if (err) throw err;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
await new Promise(process.nextTick);
|
|
45
|
+
|
|
46
|
+
expect(res.body).toBe('OK');
|
|
47
|
+
expect(res.headers['content-type']).toBe('image/png');
|
|
48
|
+
expect(res.headers['cache-status']).toBe('Souin; hit; ttl=664');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import superagent from 'superagent';
|
|
7
7
|
import config from '@plone/volto/registry';
|
|
8
8
|
import { addHeadersFactory } from '@plone/volto/helpers/Proxy/Proxy';
|
|
9
|
+
import { stripSubpathPrefix } from '@plone/volto/helpers/Url/Url';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Get a resource image/file with authenticated (if token exist) API headers
|
|
@@ -16,9 +17,9 @@ import { addHeadersFactory } from '@plone/volto/helpers/Proxy/Proxy';
|
|
|
16
17
|
export const getAPIResourceWithAuth = (req) =>
|
|
17
18
|
new Promise((resolve, reject) => {
|
|
18
19
|
const { settings } = config;
|
|
19
|
-
const
|
|
20
|
-
|
|
20
|
+
const apiSuffix = settings.legacyTraverse ? '' : '/++api++';
|
|
21
21
|
let apiPath = '';
|
|
22
|
+
|
|
22
23
|
if (settings.internalApiPath && __SERVER__) {
|
|
23
24
|
apiPath = settings.internalApiPath;
|
|
24
25
|
} else if (__DEVELOPMENT__ && settings.devProxyToApiPath) {
|
|
@@ -26,8 +27,12 @@ export const getAPIResourceWithAuth = (req) =>
|
|
|
26
27
|
} else {
|
|
27
28
|
apiPath = settings.apiPath;
|
|
28
29
|
}
|
|
30
|
+
|
|
31
|
+
//strip subpath if any
|
|
32
|
+
const contentPath = stripSubpathPrefix(req.path);
|
|
33
|
+
|
|
29
34
|
const request = superagent
|
|
30
|
-
.get(`${apiPath}${__DEVELOPMENT__ ? '' :
|
|
35
|
+
.get(`${apiPath}${__DEVELOPMENT__ ? '' : apiSuffix}${contentPath}`)
|
|
31
36
|
.maxResponseSize(settings.maxResponseSize)
|
|
32
37
|
.responseType('blob');
|
|
33
38
|
const authToken = req.universalCookies.get('auth_token');
|
package/src/helpers/Api/Api.js
CHANGED
|
@@ -7,7 +7,10 @@ import superagent from 'superagent';
|
|
|
7
7
|
import Cookies from 'universal-cookie';
|
|
8
8
|
import config from '@plone/volto/registry';
|
|
9
9
|
import { addHeadersFactory } from '@plone/volto/helpers/Proxy/Proxy';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
stripQuerystring,
|
|
12
|
+
stripSubpathPrefix,
|
|
13
|
+
} from '@plone/volto/helpers/Url/Url';
|
|
11
14
|
|
|
12
15
|
const methods = ['get', 'post', 'put', 'patch', 'del'];
|
|
13
16
|
|
|
@@ -19,11 +22,10 @@ const methods = ['get', 'post', 'put', 'patch', 'del'];
|
|
|
19
22
|
*/
|
|
20
23
|
export function formatUrl(path) {
|
|
21
24
|
const { settings } = config;
|
|
22
|
-
const
|
|
25
|
+
const apiSuffix = settings.legacyTraverse ? '' : '/++api++';
|
|
23
26
|
|
|
24
27
|
if (path.startsWith('http://') || path.startsWith('https://')) return path;
|
|
25
28
|
|
|
26
|
-
const adjustedPath = path[0] !== '/' ? `/${path}` : path;
|
|
27
29
|
let apiPath = '';
|
|
28
30
|
if (settings.internalApiPath && __SERVER__) {
|
|
29
31
|
apiPath = settings.internalApiPath;
|
|
@@ -31,7 +33,8 @@ export function formatUrl(path) {
|
|
|
31
33
|
apiPath = settings.apiPath;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
const contentPath = stripSubpathPrefix(path[0] !== '/' ? `/${path}` : path);
|
|
37
|
+
return `${apiPath}${apiSuffix}${contentPath}`;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { matchRoutes } from 'react-router-config';
|
|
2
2
|
import { mapSeries, isPromise } from './utils';
|
|
3
3
|
import { endGlobalLoad } from '@plone/volto/actions/asyncConnect/asyncConnect';
|
|
4
|
+
import { stripSubpathPrefix } from '@plone/volto/helpers/Url/Url';
|
|
4
5
|
|
|
5
6
|
export function filterComponents(branch) {
|
|
6
7
|
return branch.reduce((result, { route, match }) => {
|
|
@@ -18,7 +19,9 @@ export function loadAsyncConnect({
|
|
|
18
19
|
filter = () => true,
|
|
19
20
|
...rest
|
|
20
21
|
}) {
|
|
21
|
-
const layered = filterComponents(
|
|
22
|
+
const layered = filterComponents(
|
|
23
|
+
matchRoutes(routes, stripSubpathPrefix(location.pathname)),
|
|
24
|
+
);
|
|
22
25
|
|
|
23
26
|
if (layered.length === 0) {
|
|
24
27
|
return Promise.resolve();
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
import Cookies from 'universal-cookie';
|
|
7
7
|
import jwtDecode from 'jwt-decode';
|
|
8
|
-
|
|
9
8
|
import { loginRenew } from '@plone/volto/actions/userSession/userSession';
|
|
10
9
|
import { getCookieOptions } from '@plone/volto/helpers/Cookies/cookies';
|
|
11
10
|
import { push } from 'connected-react-router';
|
|
@@ -35,7 +34,6 @@ export function persistAuthToken(store, req) {
|
|
|
35
34
|
} else {
|
|
36
35
|
currentValue = cookies.get('auth_token');
|
|
37
36
|
}
|
|
38
|
-
|
|
39
37
|
/**
|
|
40
38
|
* handleChange method.
|
|
41
39
|
* @method handleChange
|
|
@@ -46,7 +44,6 @@ export function persistAuthToken(store, req) {
|
|
|
46
44
|
const previousValue = currentValue;
|
|
47
45
|
const state = store.getState();
|
|
48
46
|
currentValue = state.userSession.token;
|
|
49
|
-
|
|
50
47
|
if (
|
|
51
48
|
module.hot &&
|
|
52
49
|
module.hot.data &&
|
|
@@ -55,11 +52,11 @@ export function persistAuthToken(store, req) {
|
|
|
55
52
|
) {
|
|
56
53
|
currentValue = previousValue;
|
|
57
54
|
}
|
|
58
|
-
|
|
59
55
|
if (previousValue !== currentValue || initial) {
|
|
60
56
|
if (!currentValue) {
|
|
61
57
|
if (previousValue) {
|
|
62
58
|
cookies.remove('auth_token', { path: '/' });
|
|
59
|
+
cookies.remove('__ac', { path: '/' });
|
|
63
60
|
}
|
|
64
61
|
} else {
|
|
65
62
|
if (previousValue !== currentValue) {
|
|
@@ -97,11 +94,9 @@ export function persistAuthToken(store, req) {
|
|
|
97
94
|
}
|
|
98
95
|
}
|
|
99
96
|
}
|
|
100
|
-
|
|
101
97
|
store.subscribe(handleChange);
|
|
102
98
|
handleChange(true);
|
|
103
99
|
}
|
|
104
|
-
|
|
105
100
|
if (module?.hot) {
|
|
106
101
|
module.hot.dispose((data) => {
|
|
107
102
|
data.reloaded = true;
|
|
@@ -80,6 +80,14 @@ export function blockHasValue(data) {
|
|
|
80
80
|
return check(data);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Block id is valid (not undefined/null or the string "undefined" from object[undefined])
|
|
85
|
+
* @param {*} id Block id
|
|
86
|
+
* @return {boolean}
|
|
87
|
+
*/
|
|
88
|
+
const isValidBlockId = (id) =>
|
|
89
|
+
id != null && id !== 'undefined' && (typeof id !== 'string' || id.length > 0);
|
|
90
|
+
|
|
83
91
|
/**
|
|
84
92
|
* Get block pairs of [id, block] from content properties
|
|
85
93
|
* @function getBlocks
|
|
@@ -89,12 +97,26 @@ export function blockHasValue(data) {
|
|
|
89
97
|
export const getBlocks = (properties) => {
|
|
90
98
|
const blocksFieldName = getBlocksFieldname(properties);
|
|
91
99
|
const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
100
|
+
const blocks = properties?.[blocksFieldName];
|
|
101
|
+
const items = properties?.[blocksLayoutFieldname]?.items;
|
|
102
|
+
if (!items) return [];
|
|
103
|
+
return items
|
|
104
|
+
.filter((n) => isValidBlockId(n))
|
|
105
|
+
.map((n) => [n, blocks?.[n]])
|
|
106
|
+
.filter(([, block]) => block != null);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get layout item IDs that are valid but have no block data (orphaned refs).
|
|
111
|
+
* @param {Object} properties Content form properties
|
|
112
|
+
* @return {string[]} IDs that should be removed from layout
|
|
113
|
+
*/
|
|
114
|
+
export const getInvalidBlockLayoutIds = (properties) => {
|
|
115
|
+
const blocksFieldName = getBlocksFieldname(properties);
|
|
116
|
+
const blocksLayoutFieldName = getBlocksLayoutFieldname(properties);
|
|
117
|
+
const blocks = properties?.[blocksFieldName] ?? {};
|
|
118
|
+
const layoutItems = properties?.[blocksLayoutFieldName]?.items ?? [];
|
|
119
|
+
return layoutItems.filter((id) => isValidBlockId(id) && blocks[id] == null);
|
|
98
120
|
};
|
|
99
121
|
|
|
100
122
|
/**
|
|
@@ -129,12 +151,28 @@ export function deleteBlock(formData, blockId, intl) {
|
|
|
129
151
|
|
|
130
152
|
let newFormData = {
|
|
131
153
|
...formData,
|
|
132
|
-
[blocksLayoutFieldname]: {
|
|
133
|
-
items: without(formData[blocksLayoutFieldname].items, blockId),
|
|
134
|
-
},
|
|
135
|
-
[blocksFieldname]: omit(formData[blocksFieldname], [blockId]),
|
|
136
154
|
};
|
|
137
155
|
|
|
156
|
+
let container = findParent(newFormData, {
|
|
157
|
+
blockId,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (container) {
|
|
161
|
+
container[blocksLayoutFieldname].items = without(
|
|
162
|
+
container[blocksLayoutFieldname].items,
|
|
163
|
+
blockId,
|
|
164
|
+
);
|
|
165
|
+
container[blocksFieldname] = omit(container[blocksFieldname], [blockId]);
|
|
166
|
+
} else {
|
|
167
|
+
newFormData[blocksLayoutFieldname].items = without(
|
|
168
|
+
newFormData[blocksLayoutFieldname].items,
|
|
169
|
+
blockId,
|
|
170
|
+
);
|
|
171
|
+
newFormData[blocksFieldname] = omit(newFormData[blocksFieldname], [
|
|
172
|
+
blockId,
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
|
|
138
176
|
if (newFormData[blocksLayoutFieldname].items.length === 0) {
|
|
139
177
|
newFormData = addBlock(
|
|
140
178
|
newFormData,
|
|
@@ -812,12 +850,14 @@ export function findBlocks(blocks = {}, types, result = []) {
|
|
|
812
850
|
export const getBlocksHierarchy = (properties) => {
|
|
813
851
|
const blocksFieldName = getBlocksFieldname(properties);
|
|
814
852
|
const blocksLayoutFieldname = getBlocksLayoutFieldname(properties);
|
|
815
|
-
return properties[blocksLayoutFieldname]?.items?.map((n) => ({
|
|
853
|
+
return properties?.[blocksLayoutFieldname]?.items?.map((n) => ({
|
|
816
854
|
id: n,
|
|
817
|
-
title: properties[blocksFieldName][n]?.['@type'],
|
|
818
|
-
data: properties[blocksFieldName][n],
|
|
819
|
-
children: isBlockContainer(properties[blocksFieldName][n])
|
|
820
|
-
?
|
|
855
|
+
title: properties?.[blocksFieldName]?.[n]?.['@type'],
|
|
856
|
+
data: properties?.[blocksFieldName]?.[n],
|
|
857
|
+
children: isBlockContainer(properties?.[blocksFieldName]?.[n])
|
|
858
|
+
? properties?.[blocksFieldName]?.[n]?.data
|
|
859
|
+
? getBlocksHierarchy(properties?.[blocksFieldName]?.[n]?.data)
|
|
860
|
+
: getBlocksHierarchy(properties?.[blocksFieldName]?.[n])
|
|
821
861
|
: [],
|
|
822
862
|
}));
|
|
823
863
|
};
|
|
@@ -911,8 +951,13 @@ export function moveBlockEnhanced(formData, { source, destination }) {
|
|
|
911
951
|
const destinationContainer = findContainer(clonedFormData, {
|
|
912
952
|
containerId: destination.parent,
|
|
913
953
|
});
|
|
954
|
+
const sourceContainer = findContainer(clonedFormData, {
|
|
955
|
+
containerId: source.parent,
|
|
956
|
+
});
|
|
957
|
+
|
|
914
958
|
destinationContainer[blocksFieldName][source.id] =
|
|
915
|
-
|
|
959
|
+
sourceContainer[blocksFieldName]?.[source.id] ||
|
|
960
|
+
sourceContainer.data?.[blocksFieldName][source.id];
|
|
916
961
|
|
|
917
962
|
destinationContainer[blocksLayoutFieldname].items = insertInArray(
|
|
918
963
|
destinationContainer[blocksLayoutFieldname].items,
|
|
@@ -921,9 +966,6 @@ export function moveBlockEnhanced(formData, { source, destination }) {
|
|
|
921
966
|
);
|
|
922
967
|
|
|
923
968
|
// Remove the source block from the source parent
|
|
924
|
-
const sourceContainer = findContainer(clonedFormData, {
|
|
925
|
-
containerId: source.parent,
|
|
926
|
-
});
|
|
927
969
|
delete sourceContainer[blocksFieldName][source.id];
|
|
928
970
|
sourceContainer[blocksLayoutFieldname].items = removeFromArray(
|
|
929
971
|
sourceContainer[blocksLayoutFieldname].items,
|
|
@@ -957,23 +999,25 @@ export function moveBlockEnhanced(formData, { source, destination }) {
|
|
|
957
999
|
* @returns {object|undefined} - The container object if found, otherwise undefined.
|
|
958
1000
|
*/
|
|
959
1001
|
export const findContainer = (formData, { containerId }) => {
|
|
1002
|
+
const block =
|
|
1003
|
+
formData.blocks[containerId]?.data || formData.blocks[containerId];
|
|
960
1004
|
if (
|
|
961
|
-
|
|
962
|
-
Object.keys(
|
|
963
|
-
Object.keys(
|
|
1005
|
+
block &&
|
|
1006
|
+
Object.keys(block).includes('blocks') &&
|
|
1007
|
+
Object.keys(block).includes('blocks_layout')
|
|
964
1008
|
) {
|
|
965
|
-
return
|
|
1009
|
+
return block;
|
|
966
1010
|
}
|
|
967
1011
|
|
|
968
1012
|
let container;
|
|
969
1013
|
Object.keys(formData.blocks).every((blockId) => {
|
|
970
|
-
const
|
|
1014
|
+
const subBlock = formData.blocks[blockId].data || formData.blocks[blockId];
|
|
971
1015
|
if (
|
|
972
|
-
|
|
973
|
-
Object.keys(
|
|
974
|
-
Object.keys(
|
|
1016
|
+
subBlock &&
|
|
1017
|
+
Object.keys(subBlock).includes('blocks') &&
|
|
1018
|
+
Object.keys(subBlock).includes('blocks_layout')
|
|
975
1019
|
) {
|
|
976
|
-
container = findContainer(
|
|
1020
|
+
container = findContainer(subBlock, { containerId });
|
|
977
1021
|
}
|
|
978
1022
|
if (container) {
|
|
979
1023
|
return false;
|
|
@@ -985,6 +1029,47 @@ export const findContainer = (formData, { containerId }) => {
|
|
|
985
1029
|
return container;
|
|
986
1030
|
};
|
|
987
1031
|
|
|
1032
|
+
/**
|
|
1033
|
+
* Finds parent container of the specified blockId in the given formData.
|
|
1034
|
+
*
|
|
1035
|
+
* @param {object} formData - The form data object.
|
|
1036
|
+
* @param {object} options - The options object.
|
|
1037
|
+
* @param {string} options.blockId - The ID of the block to find.
|
|
1038
|
+
* @returns {object|undefined} - The container object if found, otherwise undefined.
|
|
1039
|
+
*/
|
|
1040
|
+
export const findParent = (formData, { blockId }) => {
|
|
1041
|
+
const block = formData.data || formData;
|
|
1042
|
+
|
|
1043
|
+
if (block && block.blocks && Object.keys(block.blocks).includes(blockId)) {
|
|
1044
|
+
return block;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
let found = false;
|
|
1048
|
+
|
|
1049
|
+
if (block && block.blocks) {
|
|
1050
|
+
Object.keys(block.blocks).every((subBlockId) => {
|
|
1051
|
+
const subBlock =
|
|
1052
|
+
block.blocks[subBlockId].data || block.blocks[subBlockId];
|
|
1053
|
+
if (subBlock && subBlock.blocks) {
|
|
1054
|
+
if (Object.keys(subBlock.blocks).includes(blockId)) {
|
|
1055
|
+
found = subBlock;
|
|
1056
|
+
}
|
|
1057
|
+
const parent = findParent(subBlock, { blockId });
|
|
1058
|
+
if (parent) {
|
|
1059
|
+
found = parent;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
if (found) {
|
|
1063
|
+
return false;
|
|
1064
|
+
} else {
|
|
1065
|
+
return true;
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
return found;
|
|
1071
|
+
};
|
|
1072
|
+
|
|
988
1073
|
const _dummyIntl = {
|
|
989
1074
|
formatMessage() {},
|
|
990
1075
|
};
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
getBlocks,
|
|
8
8
|
getBlocksFieldname,
|
|
9
9
|
getBlocksLayoutFieldname,
|
|
10
|
+
getInvalidBlockLayoutIds,
|
|
10
11
|
hasBlocksData,
|
|
11
12
|
insertBlock,
|
|
12
13
|
moveBlock,
|
|
@@ -482,6 +483,105 @@ describe('Blocks', () => {
|
|
|
482
483
|
['a', { value: 1 }],
|
|
483
484
|
]);
|
|
484
485
|
});
|
|
486
|
+
|
|
487
|
+
it('filters out invalid block IDs (null, undefined, string "undefined")', () => {
|
|
488
|
+
const validBlock = { '@type': 'search', value: 'test' };
|
|
489
|
+
const result = getBlocks({
|
|
490
|
+
blocks: {
|
|
491
|
+
'valid-id-123': validBlock,
|
|
492
|
+
// These shouldn't exist but test edge case
|
|
493
|
+
[null]: { '@type': 'invalid' },
|
|
494
|
+
[undefined]: { '@type': 'invalid' },
|
|
495
|
+
undefined: { '@type': 'invalid' },
|
|
496
|
+
},
|
|
497
|
+
blocks_layout: {
|
|
498
|
+
items: [
|
|
499
|
+
'valid-id-123',
|
|
500
|
+
null, // Invalid: null ID
|
|
501
|
+
undefined, // Invalid: undefined ID
|
|
502
|
+
'undefined', // Invalid: string "undefined"
|
|
503
|
+
'missing-block', // Valid ID but block doesn't exist (filtered by block !== undefined)
|
|
504
|
+
],
|
|
505
|
+
},
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// Should only return the valid block, filtering out:
|
|
509
|
+
// - null ID
|
|
510
|
+
// - undefined ID
|
|
511
|
+
// - string "undefined" ID
|
|
512
|
+
// - missing block (block is undefined)
|
|
513
|
+
expect(result).toStrictEqual([['valid-id-123', validBlock]]);
|
|
514
|
+
expect(result.length).toBe(1);
|
|
515
|
+
|
|
516
|
+
// Verify no invalid IDs in the result
|
|
517
|
+
const ids = result.map(([id]) => id);
|
|
518
|
+
expect(ids).not.toContain(null);
|
|
519
|
+
expect(ids).not.toContain(undefined);
|
|
520
|
+
expect(ids).not.toContain('undefined');
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('filters out invalid block IDs even when blocks object has invalid keys', () => {
|
|
524
|
+
// Simulate edge case where blocks object has invalid keys
|
|
525
|
+
const blocks = {
|
|
526
|
+
'valid-id': { '@type': 'search' },
|
|
527
|
+
};
|
|
528
|
+
// JavaScript allows this, creating string keys
|
|
529
|
+
blocks[null] = { '@type': 'invalid' };
|
|
530
|
+
blocks[undefined] = { '@type': 'invalid' };
|
|
531
|
+
|
|
532
|
+
const result = getBlocks({
|
|
533
|
+
blocks,
|
|
534
|
+
blocks_layout: {
|
|
535
|
+
items: ['valid-id', null, undefined, 'undefined'],
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// Should only return valid block
|
|
540
|
+
expect(result).toStrictEqual([['valid-id', { '@type': 'search' }]]);
|
|
541
|
+
expect(result.length).toBe(1);
|
|
542
|
+
});
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
describe('getInvalidBlockLayoutIds', () => {
|
|
546
|
+
it('returns layout IDs that are valid but have no block data', () => {
|
|
547
|
+
const result = getInvalidBlockLayoutIds({
|
|
548
|
+
blocks: {
|
|
549
|
+
a: { '@type': 'custom', text: 'a' },
|
|
550
|
+
b: { '@type': 'custom', text: 'b' },
|
|
551
|
+
},
|
|
552
|
+
blocks_layout: {
|
|
553
|
+
items: ['a', 'b', 'MISSING-1', 'MISSING-2'],
|
|
554
|
+
},
|
|
555
|
+
});
|
|
556
|
+
expect(result).toStrictEqual(['MISSING-1', 'MISSING-2']);
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
it('returns empty when all layout items have block data', () => {
|
|
560
|
+
const result = getInvalidBlockLayoutIds({
|
|
561
|
+
blocks: { a: { '@type': 'custom' }, b: { '@type': 'custom' } },
|
|
562
|
+
blocks_layout: { items: ['a', 'b'] },
|
|
563
|
+
});
|
|
564
|
+
expect(result).toStrictEqual([]);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
it('filters out invalid IDs (null, undefined, "undefined")', () => {
|
|
568
|
+
const result = getInvalidBlockLayoutIds({
|
|
569
|
+
blocks: {},
|
|
570
|
+
blocks_layout: {
|
|
571
|
+
items: [null, undefined, 'undefined', 'valid-missing'],
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
expect(result).toStrictEqual(['valid-missing']);
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
it('returns empty when items is missing or empty', () => {
|
|
578
|
+
expect(
|
|
579
|
+
getInvalidBlockLayoutIds({ blocks: {}, blocks_layout: {} }),
|
|
580
|
+
).toStrictEqual([]);
|
|
581
|
+
expect(
|
|
582
|
+
getInvalidBlockLayoutIds({ blocks: {}, blocks_layout: { items: [] } }),
|
|
583
|
+
).toStrictEqual([]);
|
|
584
|
+
});
|
|
485
585
|
});
|
|
486
586
|
|
|
487
587
|
describe('addBlock', () => {
|
|
@@ -11,6 +11,7 @@ import keys from 'lodash/keys';
|
|
|
11
11
|
import endsWith from 'lodash/endsWith';
|
|
12
12
|
import find from 'lodash/find';
|
|
13
13
|
import config from '@plone/volto/registry';
|
|
14
|
+
import omit from 'lodash/omit';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Nest content.
|
|
@@ -85,3 +86,25 @@ export function getLanguageIndependentFields(schema) {
|
|
|
85
86
|
properties[field]['multilingual_options']?.['language_independent'],
|
|
86
87
|
);
|
|
87
88
|
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Flattens static behaviors into the parent object with dot-notation keys.
|
|
92
|
+
* @function flattenStaticBehaviors
|
|
93
|
+
* @param {Object} result The result object containing static behaviors.
|
|
94
|
+
* @returns {Object} Result object with flattened static behaviors.
|
|
95
|
+
*/
|
|
96
|
+
export function flattenStaticBehaviors(result) {
|
|
97
|
+
if (!result['@static_behaviors']) {
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let flattened = Object.assign({}, result);
|
|
102
|
+
map(result['@static_behaviors'], (behavior) => {
|
|
103
|
+
flattened = {
|
|
104
|
+
...omit(flattened, behavior),
|
|
105
|
+
...mapKeys(flattened[behavior], (value, key) => `${behavior}.${key}`),
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return flattened;
|
|
110
|
+
}
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
nestContent,
|
|
3
3
|
getContentIcon,
|
|
4
4
|
getLanguageIndependentFields,
|
|
5
|
+
flattenStaticBehaviors,
|
|
5
6
|
} from './Content';
|
|
6
7
|
import contentExistingSVG from '@plone/volto/icons/content-existing.svg';
|
|
7
8
|
import linkSVG from '@plone/volto/icons/link.svg';
|
|
@@ -96,4 +97,42 @@ describe('Content', () => {
|
|
|
96
97
|
expect(getLanguageIndependentFields(schema)).toStrictEqual(['lif']);
|
|
97
98
|
});
|
|
98
99
|
});
|
|
100
|
+
|
|
101
|
+
describe('flattenStaticBehaviors', () => {
|
|
102
|
+
it('returns object unchanged when no @static_behaviors', () => {
|
|
103
|
+
const input = {
|
|
104
|
+
title: 'Example',
|
|
105
|
+
creator: 'admin',
|
|
106
|
+
};
|
|
107
|
+
expect(flattenStaticBehaviors(input)).toEqual(input);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('flattens static behaviors into dot-notation keys', () => {
|
|
111
|
+
const input = {
|
|
112
|
+
title: 'Example',
|
|
113
|
+
'@static_behaviors': [
|
|
114
|
+
'guillotina_cms.interfaces.blocks.IBlocks',
|
|
115
|
+
'guillotina_cms.interfaces.dublin_core.IDublinCore',
|
|
116
|
+
],
|
|
117
|
+
'guillotina_cms.interfaces.blocks.IBlocks': {
|
|
118
|
+
blocks: 'blocks',
|
|
119
|
+
blocks_layout: 'blocks_layout',
|
|
120
|
+
},
|
|
121
|
+
'guillotina_cms.interfaces.dublin_core.IDublinCore': {
|
|
122
|
+
creator: 'creator',
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
expect(flattenStaticBehaviors(input)).toEqual({
|
|
126
|
+
title: 'Example',
|
|
127
|
+
'@static_behaviors': [
|
|
128
|
+
'guillotina_cms.interfaces.blocks.IBlocks',
|
|
129
|
+
'guillotina_cms.interfaces.dublin_core.IDublinCore',
|
|
130
|
+
],
|
|
131
|
+
'guillotina_cms.interfaces.blocks.IBlocks.blocks': 'blocks',
|
|
132
|
+
'guillotina_cms.interfaces.blocks.IBlocks.blocks_layout':
|
|
133
|
+
'blocks_layout',
|
|
134
|
+
'guillotina_cms.interfaces.dublin_core.IDublinCore.creator': 'creator',
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
99
138
|
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This is a HOC for use with the volto Edit component.
|
|
3
|
+
It makes sure that we fetch the content from the API on the client side
|
|
4
|
+
when the Edit component is mounted, if there is an internalApiPath
|
|
5
|
+
that is different from the public API path. Otherwise we might end up
|
|
6
|
+
saving backend API paths back to the server.
|
|
7
|
+
|
|
8
|
+
It's admittedly a bit of a workaround.
|
|
9
|
+
Ideally the volto SSR should produce correct URLs
|
|
10
|
+
so that we don't have to do this on the client side.
|
|
11
|
+
But, that requires refactoring that won't happen quickly...
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { useEffect } from 'react';
|
|
15
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
16
|
+
import hoistNonReactStatics from 'hoist-non-react-statics';
|
|
17
|
+
import config from '@plone/volto/registry';
|
|
18
|
+
import { getContent } from '@plone/volto/actions/content/content';
|
|
19
|
+
|
|
20
|
+
export default function withClientSideContent(WrappedComponent) {
|
|
21
|
+
function WithClientSideContent(props) {
|
|
22
|
+
const { internalApiPath } = config.settings;
|
|
23
|
+
const dispatch = useDispatch();
|
|
24
|
+
const content = useSelector((state) => state.content);
|
|
25
|
+
const id = content.data?.['@id'];
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (internalApiPath && id?.startsWith(internalApiPath)) {
|
|
28
|
+
dispatch(getContent(id.substring(internalApiPath.length)));
|
|
29
|
+
}
|
|
30
|
+
}, [internalApiPath, dispatch, id]);
|
|
31
|
+
return <WrappedComponent {...props} />;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return hoistNonReactStatics(WithClientSideContent, WrappedComponent);
|
|
35
|
+
}
|
|
@@ -305,8 +305,11 @@ export const EMPTY_STYLES_SCHEMA = {
|
|
|
305
305
|
|
|
306
306
|
/**
|
|
307
307
|
* Adds the `styles` field and 'styling' fieldset in a given schema
|
|
308
|
+
* @param {object} params Helper params
|
|
309
|
+
* @param {import('@plone/types').JSONSchema} params.schema Schema to enhance
|
|
310
|
+
* @param {import('react-intl').IntlShape} params.intl intl helper for translations
|
|
308
311
|
*/
|
|
309
|
-
export const addStyling = ({ schema,
|
|
312
|
+
export const addStyling = ({ schema, intl }) => {
|
|
310
313
|
if (isEmpty(find(schema.fieldsets, { id: 'styling' }))) {
|
|
311
314
|
schema.fieldsets.push({
|
|
312
315
|
id: 'styling',
|
|
@@ -8,6 +8,12 @@ const schema = {
|
|
|
8
8
|
username: { title: 'Username', type: 'string', description: '' },
|
|
9
9
|
email: { title: 'Email', type: 'string', widget: 'email', description: '' },
|
|
10
10
|
url: { title: 'url', type: 'string', widget: 'url', description: '' },
|
|
11
|
+
file: {
|
|
12
|
+
title: 'file',
|
|
13
|
+
type: 'object',
|
|
14
|
+
description: '',
|
|
15
|
+
size: 5,
|
|
16
|
+
},
|
|
11
17
|
},
|
|
12
18
|
fieldsets: [
|
|
13
19
|
{ id: 'default', title: 'FIXME: User Data', fields: ['username'] },
|
|
@@ -181,6 +187,31 @@ describe('FormValidation', () => {
|
|
|
181
187
|
).toEqual({});
|
|
182
188
|
});
|
|
183
189
|
|
|
190
|
+
it('file - validates invalid size', () => {
|
|
191
|
+
const validationErrorMessages = [messages.maxSize.defaultMessage];
|
|
192
|
+
validationErrorMessages.title = 'file';
|
|
193
|
+
|
|
194
|
+
expect(
|
|
195
|
+
FormValidation.validateFieldsPerFieldset({
|
|
196
|
+
schema,
|
|
197
|
+
formData: { ...formData, file: { size: 10 } },
|
|
198
|
+
formatMessage,
|
|
199
|
+
}),
|
|
200
|
+
).toEqual({
|
|
201
|
+
file: validationErrorMessages,
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('file - validates size', () => {
|
|
206
|
+
expect(
|
|
207
|
+
FormValidation.validateFieldsPerFieldset({
|
|
208
|
+
schema,
|
|
209
|
+
formData: { ...formData, file: { size: 1 } },
|
|
210
|
+
formatMessage,
|
|
211
|
+
}),
|
|
212
|
+
).toEqual({});
|
|
213
|
+
});
|
|
214
|
+
|
|
184
215
|
it('widget - validator from block - Fails', () => {
|
|
185
216
|
let newSchema = {
|
|
186
217
|
properties: {
|