@plone/volto 17.0.0-alpha.1 → 17.0.0-alpha.10

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 (179) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/CHANGELOG.md +396 -16
  3. package/CONTRIBUTING.md +1 -1
  4. package/README.md +12 -15
  5. package/addon-registry.js +34 -0
  6. package/create-theme-addons-loader.js +79 -0
  7. package/cypress/support/commands.js +25 -0
  8. package/locales/ca/LC_MESSAGES/volto.po +187 -6
  9. package/locales/ca.json +1 -1
  10. package/locales/de/LC_MESSAGES/volto.po +206 -25
  11. package/locales/de.json +1 -1
  12. package/locales/en/LC_MESSAGES/volto.po +186 -5
  13. package/locales/en.json +1 -1
  14. package/locales/es/LC_MESSAGES/volto.po +187 -6
  15. package/locales/es.json +1 -1
  16. package/locales/eu/LC_MESSAGES/volto.po +187 -6
  17. package/locales/eu.json +1 -1
  18. package/locales/fi/LC_MESSAGES/volto.po +4792 -0
  19. package/locales/fi.json +1 -1
  20. package/locales/fr/LC_MESSAGES/volto.po +187 -6
  21. package/locales/fr.json +1 -1
  22. package/locales/it/LC_MESSAGES/volto.po +187 -6
  23. package/locales/it.json +1 -1
  24. package/locales/ja/LC_MESSAGES/volto.po +187 -6
  25. package/locales/ja.json +1 -1
  26. package/locales/nl/LC_MESSAGES/volto.po +842 -649
  27. package/locales/nl.json +1 -1
  28. package/locales/pt/LC_MESSAGES/volto.po +187 -6
  29. package/locales/pt.json +1 -1
  30. package/locales/pt_BR/LC_MESSAGES/volto.po +195 -14
  31. package/locales/pt_BR.json +1 -1
  32. package/locales/ro/LC_MESSAGES/volto.po +187 -6
  33. package/locales/ro.json +1 -1
  34. package/locales/volto.pot +187 -6
  35. package/locales/zh_CN/LC_MESSAGES/volto.po +187 -6
  36. package/locales/zh_CN.json +1 -1
  37. package/package-why.json +0 -1
  38. package/package.json +9 -8
  39. package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +1 -1
  40. package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +1 -1
  41. package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +1 -1
  42. package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +1 -1
  43. package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +1 -1
  44. package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +1 -1
  45. package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +1 -1
  46. package/packages/volto-slate/build/messages/src/elementEditor/messages.json +1 -1
  47. package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +1 -1
  48. package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +1 -1
  49. package/packages/volto-slate/package.json +1 -1
  50. package/packages/volto-slate/src/blocks/Table/index.js +2 -0
  51. package/packages/volto-slate/src/blocks/Text/SlashMenu.jsx +4 -3
  52. package/packages/volto-slate/src/editor/deserialize.js +0 -1
  53. package/packages/volto-slate/src/editor/plugins/StyleMenu/StyleMenu.jsx +14 -4
  54. package/razzle.config.js +28 -0
  55. package/src/actions/index.js +6 -0
  56. package/src/actions/language/language.js +9 -8
  57. package/src/actions/querystringsearch/querystringsearch.js +20 -14
  58. package/src/actions/relations/rebuild.js +25 -0
  59. package/src/actions/relations/relations.js +86 -0
  60. package/src/actions/relations/relations.test.js +15 -0
  61. package/src/components/index.js +1 -0
  62. package/src/components/manage/Add/Add.jsx +2 -2
  63. package/src/components/manage/BlockChooser/BlockChooser.jsx +14 -5
  64. package/src/components/manage/BlockChooser/BlockChooser.test.jsx +5 -0
  65. package/src/components/manage/Blocks/Listing/Edit.jsx +0 -19
  66. package/src/components/manage/Blocks/Listing/ListingBody.jsx +77 -61
  67. package/src/components/manage/Blocks/Listing/View.jsx +0 -4
  68. package/src/components/manage/Blocks/Listing/getAsyncData.js +10 -2
  69. package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +18 -13
  70. package/src/components/manage/Blocks/Search/SearchBlockEdit.jsx +5 -4
  71. package/src/components/manage/Blocks/Search/SearchBlockView.jsx +2 -1
  72. package/src/components/manage/Blocks/Search/components/DateRangeFacet.jsx +4 -1
  73. package/src/components/manage/Blocks/Search/components/Facets.jsx +58 -2
  74. package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +24 -11
  75. package/src/components/manage/Blocks/Search/layout/LeftColumnFacets.jsx +17 -5
  76. package/src/components/manage/Blocks/Search/layout/RightColumnFacets.jsx +17 -5
  77. package/src/components/manage/Blocks/Search/layout/TopSideFacets.jsx +21 -5
  78. package/src/components/manage/Blocks/Search/schema.js +16 -1
  79. package/src/components/manage/Blocks/ToC/Edit.jsx +1 -0
  80. package/src/components/manage/Contents/Contents.jsx +69 -33
  81. package/src/components/manage/Contents/ContentsItem.jsx +6 -0
  82. package/src/components/manage/Controlpanels/AddonsControlpanel.jsx +3 -3
  83. package/src/components/manage/Controlpanels/Controlpanels.jsx +199 -224
  84. package/src/components/manage/Controlpanels/Controlpanels.test.jsx +46 -7
  85. package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +66 -0
  86. package/src/components/manage/Controlpanels/Relations/Relations.jsx +114 -0
  87. package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +479 -0
  88. package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +531 -0
  89. package/src/components/manage/Controlpanels/Users/UserGroupMembershipControlPanel.jsx +3 -3
  90. package/src/components/manage/Controlpanels/Users/UserGroupMembershipListing.jsx +51 -82
  91. package/src/components/manage/Controlpanels/Users/UserGroupMembershipMatrix.jsx +79 -75
  92. package/src/components/manage/DragDropList/DragDropList.jsx +63 -42
  93. package/src/components/manage/Form/BlocksToolbar.jsx +5 -1
  94. package/src/components/manage/Form/Form.jsx +11 -5
  95. package/src/components/manage/Form/InlineForm.jsx +39 -9
  96. package/src/components/manage/Form/InlineFormState.js +8 -0
  97. package/src/components/manage/Multilingual/CreateTranslation.jsx +2 -2
  98. package/src/components/manage/Multilingual/TranslationObject.jsx +4 -3
  99. package/src/components/manage/Preferences/ChangePassword.jsx +2 -2
  100. package/src/components/manage/Preferences/PersonalPreferences.jsx +2 -2
  101. package/src/components/manage/Toast/Toast.jsx +1 -1
  102. package/src/components/manage/Toolbar/Types.jsx +2 -2
  103. package/src/components/manage/Widgets/DatetimeWidget.jsx +9 -5
  104. package/src/components/manage/Widgets/ObjectListWidget.jsx +3 -8
  105. package/src/components/manage/Widgets/RecurrenceWidget/ByDayField.jsx +2 -1
  106. package/src/components/manage/Widgets/RecurrenceWidget/MonthOfTheYearField.jsx +2 -1
  107. package/src/components/manage/Widgets/RecurrenceWidget/Occurences.jsx +2 -1
  108. package/src/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +7 -2
  109. package/src/components/manage/Widgets/RecurrenceWidget/WeekdayOfTheMonthField.jsx +2 -1
  110. package/src/components/manage/Widgets/SelectUtils.js +1 -1
  111. package/src/components/manage/Widgets/SelectWidget.jsx +1 -1
  112. package/src/components/theme/Footer/Footer.jsx +2 -13
  113. package/src/components/theme/Header/Header.jsx +37 -63
  114. package/src/components/theme/Header/Header.test.jsx +18 -0
  115. package/src/components/theme/Icon/Icon.jsx +2 -2
  116. package/src/components/theme/LanguageSelector/LanguageSelector.js +8 -3
  117. package/src/components/theme/Login/Login.jsx +1 -0
  118. package/src/components/theme/Logo/Logo.jsx +2 -1
  119. package/src/components/theme/MultilingualRedirector/MultilingualRedirector.jsx +2 -2
  120. package/src/components/theme/Navigation/NavItem.jsx +4 -2
  121. package/src/components/theme/NotFound/NotFound.jsx +55 -41
  122. package/src/components/theme/PasswordReset/PasswordReset.jsx +7 -4
  123. package/src/components/theme/PasswordReset/RequestPasswordReset.jsx +1 -1
  124. package/src/components/theme/Sitemap/Sitemap.jsx +5 -3
  125. package/src/components/theme/View/DefaultView.jsx +1 -1
  126. package/src/components/theme/View/EventDatesInfo.jsx +2 -1
  127. package/src/components/theme/View/RenderBlocks.jsx +7 -1
  128. package/src/components/theme/Widgets/DateWidget.jsx +2 -1
  129. package/src/components/theme/Widgets/DatetimeWidget.jsx +2 -1
  130. package/src/components/theme/Widgets/RelationsWidget.jsx +13 -11
  131. package/src/config/ControlPanels.js +2 -0
  132. package/src/config/Widgets.jsx +1 -0
  133. package/src/config/index.js +2 -0
  134. package/src/constants/ActionTypes.js +4 -0
  135. package/src/constants/Languages.js +8 -4
  136. package/src/express-middleware/sitemap.js +36 -4
  137. package/src/helpers/Api/Api.js +1 -1
  138. package/src/helpers/FormValidation/FormValidation.js +11 -2
  139. package/src/helpers/FormValidation/FormValidation.test.js +73 -0
  140. package/src/helpers/Html/Html.jsx +3 -1
  141. package/src/helpers/Html/Html.test.jsx +5 -0
  142. package/src/helpers/MessageLabels/MessageLabels.js +72 -0
  143. package/src/helpers/Robots/Robots.js +24 -6
  144. package/src/helpers/Sitemap/Sitemap.js +44 -2
  145. package/src/helpers/Url/Url.js +27 -6
  146. package/src/helpers/Url/Url.test.js +26 -0
  147. package/src/helpers/Utils/Utils.js +38 -13
  148. package/src/helpers/Utils/Utils.test.js +4 -4
  149. package/src/helpers/index.js +7 -2
  150. package/src/hooks/userSession/useToken.js +5 -0
  151. package/src/middleware/Api.test.js +54 -0
  152. package/src/middleware/api.js +8 -4
  153. package/src/reducers/actions/actions.js +1 -1
  154. package/src/reducers/breadcrumbs/breadcrumbs.js +1 -1
  155. package/src/reducers/index.js +2 -0
  156. package/src/reducers/navigation/navigation.js +1 -1
  157. package/src/reducers/relations/relations.js +173 -0
  158. package/src/reducers/types/types.js +1 -1
  159. package/src/routes.js +5 -0
  160. package/src/server.jsx +28 -23
  161. package/test-setup-config.js +1 -0
  162. package/theme/themes/pastanaga/extras/contents.less +1 -0
  163. package/theme/themes/pastanaga/extras/main.less +80 -1
  164. package/theme/themes/pastanaga/extras/search.less +6 -0
  165. package/theme/themes/pastanaga/extras/sidebar.less +4 -0
  166. package/theme/themes/pastanaga/extras/userscontrolpanel.less +99 -76
  167. package/.changelog.draft +0 -28
  168. package/.editorconfig +0 -36
  169. package/.storybook/main.js +0 -127
  170. package/.storybook/manager.js +0 -15
  171. package/.storybook/preview.js +0 -21
  172. package/.storybook/static/previewImage.svg +0 -48
  173. package/.yarnrc.yml +0 -5
  174. package/jsdoc.json +0 -16
  175. package/netlify.toml +0 -5
  176. package/pyvenv.cfg +0 -3
  177. package/share/man/man1/ttx.1 +0 -225
  178. package/src/components/theme/Header/Header.md +0 -27
  179. package/towncrier.toml +0 -33
@@ -1,53 +1,67 @@
1
- /**
2
- * Home container.
3
- * @module components/theme/NotFound/NotFound
4
- */
5
-
6
- import React from 'react';
1
+ import { useEffect } from 'react';
2
+ import { BodyClass, toBackendLang } from '@plone/volto/helpers';
7
3
  import { FormattedMessage } from 'react-intl';
8
4
  import { Link } from 'react-router-dom';
9
5
  import { Container } from 'semantic-ui-react';
10
6
  import { withServerErrorCode } from '@plone/volto/helpers/Utils/Utils';
7
+ import { useDispatch, useSelector } from 'react-redux';
8
+ import { getNavigation } from '@plone/volto/actions';
9
+ import config from '@plone/volto/registry';
11
10
 
12
11
  /**
13
12
  * Not found function.
14
13
  * @function NotFound
15
14
  * @returns {string} Markup of the not found page.
16
15
  */
17
- const NotFound = () => (
18
- <Container className="view-wrapper">
19
- <h1>
20
- <FormattedMessage
21
- id="This page does not seem to exist…"
22
- defaultMessage="This page does not seem to exist…"
23
- />
24
- </h1>
25
- <p className="description">
26
- <FormattedMessage
27
- id="We apologize for the inconvenience, but the page you were trying to access is not at this address. You can use the links below to help you find what you are looking for."
28
- defaultMessage="We apologize for the inconvenience, but the page you were trying to access is not at this address. You can use the links below to help you find what you are looking for."
29
- />
30
- </p>
31
- <p>
32
- <FormattedMessage
33
- id="If you are certain you have the correct web address but are encountering an error, please contact the {site_admin}."
34
- defaultMessage="If you are certain you have the correct web address but are encountering an error, please contact the {site_admin}."
35
- values={{
36
- site_admin: (
37
- <Link to="/contact-form">
38
- <FormattedMessage
39
- id="Site Administration"
40
- defaultMessage="Site Administration"
41
- />
42
- </Link>
43
- ),
44
- }}
45
- />
46
- </p>
47
- <p>
48
- <FormattedMessage id="Thank you." defaultMessage="Thank you." />
49
- </p>
50
- </Container>
51
- );
16
+ const NotFound = () => {
17
+ const dispatch = useDispatch();
18
+ const lang = useSelector((state) => state.intl.locale);
19
+
20
+ useEffect(() => {
21
+ dispatch(
22
+ getNavigation(
23
+ config.settings.isMultilingual ? `/${toBackendLang(lang)}` : '/',
24
+ config.settings.navDepth,
25
+ ),
26
+ );
27
+ }, [dispatch, lang]);
28
+
29
+ return (
30
+ <Container className="view-wrapper">
31
+ <BodyClass className="page-not-found" />
32
+ <h1>
33
+ <FormattedMessage
34
+ id="This page does not seem to exist…"
35
+ defaultMessage="This page does not seem to exist…"
36
+ />
37
+ </h1>
38
+ <p className="description">
39
+ <FormattedMessage
40
+ id="We apologize for the inconvenience, but the page you were trying to access is not at this address. You can use the links below to help you find what you are looking for."
41
+ defaultMessage="We apologize for the inconvenience, but the page you were trying to access is not at this address. You can use the links below to help you find what you are looking for."
42
+ />
43
+ </p>
44
+ <p>
45
+ <FormattedMessage
46
+ id="If you are certain you have the correct web address but are encountering an error, please contact the {site_admin}."
47
+ defaultMessage="If you are certain you have the correct web address but are encountering an error, please contact the {site_admin}."
48
+ values={{
49
+ site_admin: (
50
+ <Link to="/contact-form">
51
+ <FormattedMessage
52
+ id="Site Administration"
53
+ defaultMessage="Site Administration"
54
+ />
55
+ </Link>
56
+ ),
57
+ }}
58
+ />
59
+ </p>
60
+ <p>
61
+ <FormattedMessage id="Thank you." defaultMessage="Thank you." />
62
+ </p>
63
+ </Container>
64
+ );
65
+ };
52
66
 
53
67
  export default withServerErrorCode(404)(NotFound);
@@ -31,7 +31,7 @@ const messages = defineMessages({
31
31
  },
32
32
  usernameTitle: {
33
33
  id: 'My username is',
34
- defaultMessage: 'My username is',
34
+ defaultMessage: 'My user name is',
35
35
  },
36
36
  emailTitle: {
37
37
  id: 'My email is',
@@ -50,8 +50,8 @@ const messages = defineMessages({
50
50
  defaultMessage: 'New password',
51
51
  },
52
52
  passwordDescription: {
53
- id: 'Enter your new password. Minimum 5 characters.',
54
- defaultMessage: 'Enter your new password. Minimum 5 characters.',
53
+ id: 'Enter your new password. Minimum 8 characters.',
54
+ defaultMessage: 'Enter your new password. Minimum 8 characters.',
55
55
  },
56
56
  passwordRepeatTitle: {
57
57
  id: 'Confirm password',
@@ -227,6 +227,9 @@ class PasswordReset extends Component {
227
227
  );
228
228
  }
229
229
  if (this.props.token) {
230
+ const errmsg = this.props.error
231
+ ? this.props.error.response.body.error
232
+ : null;
230
233
  return (
231
234
  <div id="page-password-reset">
232
235
  <Helmet
@@ -238,7 +241,7 @@ class PasswordReset extends Component {
238
241
  description={this.props.intl.formatMessage(messages.description)}
239
242
  onSubmit={this.onSubmit}
240
243
  onCancel={this.onCancel}
241
- error={this.state.error || this.props.error}
244
+ error={this.state.error || errmsg}
242
245
  schema={{
243
246
  fieldsets: [
244
247
  {
@@ -32,7 +32,7 @@ const messages = defineMessages({
32
32
  },
33
33
  usernameTitle: {
34
34
  id: 'label_my_username_is',
35
- defaultMessage: 'My username is',
35
+ defaultMessage: 'My user name is',
36
36
  },
37
37
  emailTitle: {
38
38
  id: 'label_my_email_is',
@@ -10,7 +10,7 @@ import { connect } from 'react-redux';
10
10
  import { asyncConnect } from '@plone/volto/helpers';
11
11
  import { defineMessages, injectIntl } from 'react-intl';
12
12
  import { Container } from 'semantic-ui-react';
13
- import { Helmet } from '@plone/volto/helpers';
13
+ import { Helmet, toBackendLang } from '@plone/volto/helpers';
14
14
  import { Link } from 'react-router-dom';
15
15
  import config from '@plone/volto/registry';
16
16
 
@@ -40,7 +40,7 @@ class Sitemap extends Component {
40
40
  componentDidMount() {
41
41
  const { settings } = config;
42
42
  if (settings.isMultilingual) {
43
- this.props.getNavigation(`${this.props.lang}`, 4);
43
+ this.props.getNavigation(`${toBackendLang(this.props.lang)}`, 4);
44
44
  } else {
45
45
  this.props.getNavigation('', 4);
46
46
  }
@@ -108,7 +108,9 @@ export default compose(
108
108
  const { settings } = config;
109
109
  const lang = getState().intl.locale;
110
110
  if (settings.isMultilingual) {
111
- return __SERVER__ && dispatch(getNavigation(`${lang}`, 4));
111
+ return (
112
+ __SERVER__ && dispatch(getNavigation(`${toBackendLang(lang)}`, 4))
113
+ );
112
114
  } else {
113
115
  return __SERVER__ && dispatch(getNavigation('', 4));
114
116
  }
@@ -87,7 +87,7 @@ const DefaultView = (props) => {
87
87
  return f !== 'title' ? (
88
88
  <Grid celled="internally" key={key}>
89
89
  <Grid.Row>
90
- <Label>{field.title}:</Label>
90
+ <Label title={field.id}>{field.title}:</Label>
91
91
  </Grid.Row>
92
92
  <Grid.Row>
93
93
  <Segment basic>
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import { List } from 'semantic-ui-react';
4
4
  import cx from 'classnames';
5
5
 
6
+ import { toBackendLang } from '@plone/volto/helpers';
6
7
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
7
8
  import { useSelector } from 'react-redux';
8
9
 
@@ -28,7 +29,7 @@ const When_ = ({ start, end, whole_day, open_end, moment: momentlib }) => {
28
29
  const lang = useSelector((state) => state.intl.locale);
29
30
 
30
31
  const moment = momentlib.default;
31
- moment.locale(lang);
32
+ moment.locale(toBackendLang(lang));
32
33
 
33
34
  const datesInfo = datesForDisplay(start, end, moment);
34
35
  if (!datesInfo) {
@@ -44,7 +44,13 @@ const RenderBlocks = (props) => {
44
44
  });
45
45
 
46
46
  return Block ? (
47
- <StyleWrapper key={block} {...props} id={block} data={blockData}>
47
+ <StyleWrapper
48
+ key={block}
49
+ {...props}
50
+ id={block}
51
+ block={block}
52
+ data={blockData}
53
+ >
48
54
  <Block
49
55
  id={block}
50
56
  metadata={metadata}
@@ -2,10 +2,11 @@ import React from 'react';
2
2
  import cx from 'classnames';
3
3
  import moment from 'moment';
4
4
  import { useSelector } from 'react-redux';
5
+ import { toBackendLang } from '@plone/volto/helpers';
5
6
 
6
7
  const DateWidget = ({ value, children, className, format = 'll' }) => {
7
8
  const lang = useSelector((state) => state.intl.locale);
8
- moment.locale(lang);
9
+ moment.locale(toBackendLang(lang));
9
10
  return value ? (
10
11
  <span className={cx(className, 'date', 'widget')}>
11
12
  {children
@@ -2,10 +2,11 @@ import React from 'react';
2
2
  import cx from 'classnames';
3
3
  import moment from 'moment';
4
4
  import { useSelector } from 'react-redux';
5
+ import { toBackendLang } from '@plone/volto/helpers';
5
6
 
6
7
  const DatetimeWidget = ({ value, children, className, format = 'lll' }) => {
7
8
  const lang = useSelector((state) => state.intl.locale);
8
- moment.locale(lang);
9
+ moment.locale(toBackendLang(lang));
9
10
  return value ? (
10
11
  <span className={cx(className, 'datetime', 'widget')}>
11
12
  {children
@@ -5,17 +5,19 @@ import RelationWidget from './RelationWidget';
5
5
  const RelationsWidget = ({ value, children, className }) =>
6
6
  value ? (
7
7
  <ul className={cx(className, 'relations', 'widget')}>
8
- {value.map((item, key) => (
9
- <li key={key}>
10
- <RelationWidget
11
- value={item}
12
- className={className}
13
- key={item.token || item.title || item}
14
- >
15
- {children}
16
- </RelationWidget>
17
- </li>
18
- ))}
8
+ {value.map((item, key) => {
9
+ return item ? (
10
+ <li key={key}>
11
+ <RelationWidget
12
+ value={item || `relation target not found '${key}'`}
13
+ className={className}
14
+ key={item.token || item.title || item}
15
+ >
16
+ {children}
17
+ </RelationWidget>
18
+ </li>
19
+ ) : null;
20
+ })}
19
21
  </ul>
20
22
  ) : (
21
23
  ''
@@ -18,6 +18,7 @@ import settingsSVG from '@plone/volto/icons/settings.svg';
18
18
  import rulesSVG from '@plone/volto/icons/content-existing.svg';
19
19
  import undoControlPanelSVG from '@plone/volto/icons/undo-control-panel.svg';
20
20
  import linkSVG from '@plone/volto/icons/link.svg';
21
+ import relationsSVG from '@plone/volto/icons/ahead.svg';
21
22
 
22
23
  export const controlPanelsIcons = {
23
24
  default: settingsSVG,
@@ -40,6 +41,7 @@ export const controlPanelsIcons = {
40
41
  rules: rulesSVG,
41
42
  undo: undoControlPanelSVG,
42
43
  aliases: linkSVG,
44
+ relations: relationsSVG,
43
45
  };
44
46
 
45
47
  export const filterControlPanels = (controlpanels) => {
@@ -98,6 +98,7 @@ export const widgetMapping = {
98
98
  select_querystring_field: SelectMetadataWidget,
99
99
  autocomplete: SelectAutoComplete,
100
100
  color_picker: ColorPickerWidget,
101
+ select: SelectWidget,
101
102
  },
102
103
  vocabulary: {
103
104
  'plone.app.vocabularies.Catalog': ObjectBrowserWidget,
@@ -180,6 +180,8 @@ let config = {
180
180
  styleClassNameConverters,
181
181
  hashLinkSmoothScroll: false,
182
182
  styleClassNameExtenders,
183
+ querystringSearchGet: false,
184
+ blockSettingsTabFieldsetsInitialStateOpen: true,
183
185
  },
184
186
  experimental: {
185
187
  addBlockButton: {
@@ -36,6 +36,10 @@ export const GET_NAVIGATION = 'GET_NAVIGATION';
36
36
  export const GET_PRINCIPALS = 'GET_PRINCIPALS';
37
37
  export const GET_QUERYSTRING = 'GET_QUERYSTRING';
38
38
  export const GET_QUERYSTRING_RESULTS = 'GET_QUERYSTRING_RESULTS';
39
+ export const CREATE_RELATIONS = 'CREATE_RELATIONS';
40
+ export const DELETE_RELATIONS = 'DELETE_RELATIONS';
41
+ export const LIST_RELATIONS = 'LIST_RELATIONS';
42
+ export const REBUILD_RELATIONS = 'REBUILD_RELATIONS';
39
43
  export const GET_SCHEMA = 'GET_SCHEMA';
40
44
  export const POST_SCHEMA = 'POST_SCHEMA';
41
45
  export const PUT_SCHEMA = 'PUT_SCHEMA';
@@ -4,14 +4,18 @@
4
4
  */
5
5
 
6
6
  module.exports = {
7
- en: 'English',
7
+ ca: 'Català',
8
8
  de: 'Deutsch',
9
+ en: 'English',
10
+ es: 'Español',
11
+ eu: 'Euskara',
12
+ fi: 'Suomi',
13
+ fr: 'Français',
14
+ it: 'Italiano',
9
15
  nl: 'Nederlands',
10
16
  ro: 'Română',
11
17
  ja: '日本語',
12
18
  pt: 'Português',
13
19
  pt_BR: 'Português (Brasil)',
14
- es: 'Spanish',
15
- it: 'Italian',
16
- eu: 'Euskara',
20
+ zh_CN: '中文',
17
21
  };
@@ -1,12 +1,34 @@
1
1
  import express from 'express';
2
- import { generateSitemap } from '@plone/volto/helpers/Sitemap/Sitemap';
2
+ import {
3
+ generateSitemap,
4
+ generateSitemapIndex,
5
+ SITEMAP_BATCH_SIZE,
6
+ } from '@plone/volto/helpers/Sitemap/Sitemap';
3
7
 
4
8
  export const sitemap = function (req, res, next) {
5
- generateSitemap(req).then((sitemap) => {
9
+ let start = 0;
10
+ let size = undefined;
11
+ const { batch: batchStr } = req.params;
12
+ if (batchStr !== undefined) {
13
+ const batch = parseInt(batchStr);
14
+ if (isNaN(batch) || batch === 0 || '' + batch !== batchStr) {
15
+ res.status(404);
16
+ // Some data, such as the internal API address, may be sensitive to be published
17
+ res.send(
18
+ `Invalid sitemap name, use sitemap.xml.gz, or batched sitemapN.xml.gz where N is a positive integer.`,
19
+ );
20
+ return;
21
+ }
22
+ start = SITEMAP_BATCH_SIZE * (batch - 1);
23
+ size = SITEMAP_BATCH_SIZE;
24
+ }
25
+ generateSitemap(req, start, size).then((sitemap) => {
6
26
  if (Buffer.isBuffer(sitemap)) {
7
27
  res.set('Content-Type', 'application/x-gzip');
8
- res.set('Content-Encoding', 'gzip');
9
- res.set('Content-Disposition', 'attachment; filename="sitemap.xml.gz"');
28
+ res.set(
29
+ 'Content-Disposition',
30
+ `attachment; filename="sitemap${batchStr || ''}.xml.gz"`,
31
+ );
10
32
  res.send(sitemap);
11
33
  } else {
12
34
  // {"errno":-111, "code":"ECONNREFUSED", "host": ...}
@@ -17,10 +39,20 @@ export const sitemap = function (req, res, next) {
17
39
  });
18
40
  };
19
41
 
42
+ export const sitemapIndex = function (req, res, next) {
43
+ generateSitemapIndex(req).then((sitemapIndex) => {
44
+ res.set('Content-Type', 'application/xml');
45
+ res.set('Content-Disposition', 'attachment; filename="sitemap-index.xml"');
46
+ res.send(sitemapIndex);
47
+ });
48
+ };
49
+
20
50
  export default function () {
21
51
  const middleware = express.Router();
22
52
 
23
53
  middleware.all('**/sitemap.xml.gz', sitemap);
54
+ middleware.all('**/sitemap:batch.xml.gz', sitemap);
55
+ middleware.all('**/sitemap-index.xml', sitemapIndex);
24
56
  middleware.id = 'sitemap.xml.gz';
25
57
  return middleware;
26
58
  }
@@ -89,7 +89,7 @@ class Api {
89
89
  checkUrl &&
90
90
  request.url &&
91
91
  request.xhr &&
92
- stripQuerystring(request.url) !==
92
+ encodeURI(stripQuerystring(request.url)) !==
93
93
  stripQuerystring(request.xhr.responseURL)
94
94
  ) {
95
95
  if (request.xhr.responseURL?.length === 0) {
@@ -65,7 +65,16 @@ const widgetValidation = {
65
65
  },
66
66
  url: {
67
67
  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;
68
+ var urlRegex = new RegExp(
69
+ '^(https?:\\/\\/)?' + // validate protocol
70
+ '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // validate domain name
71
+ '((\\d{1,3}\\.){3}\\d{1,3}))|' + // validate OR ip (v4) address
72
+ '(localhost)' + // validate OR localhost address
73
+ '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // validate port and path
74
+ '(\\?[;&a-z\\d%_.~+=-]*)?' + // validate query string
75
+ '(\\#[-a-z\\d_]*)?$', // validate fragment locator
76
+ 'i',
77
+ );
69
78
  const isValid = urlRegex.test(urlValue);
70
79
  return !isValid ? intlFunc(messages.isValidURL) : null;
71
80
  },
@@ -194,7 +203,7 @@ const validateRequiredFields = (
194
203
  const type = schema.properties[requiredField]?.type;
195
204
  const widget = schema.properties[requiredField]?.widget;
196
205
 
197
- let isEmpty = !formData[requiredField];
206
+ let isEmpty = !formData[requiredField] && formData[requiredField] !== 0;
198
207
  if (!isEmpty) {
199
208
  if (type === 'array') {
200
209
  isEmpty = formData[requiredField]
@@ -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,76 @@ export const messages = defineMessages({
260
260
  id: 'Show groups of users below',
261
261
  defaultMessage: 'Show groups of users below',
262
262
  },
263
+ inspectRelations: {
264
+ id: 'Inspect relations',
265
+ defaultMessage: 'Inspect relations',
266
+ },
267
+ relations: {
268
+ id: 'Relations',
269
+ defaultMessage: 'Relations',
270
+ },
271
+ fixRelations: {
272
+ id: 'Fix relations',
273
+ defaultMessage: 'Fix relations',
274
+ },
275
+ searchRelationSource: {
276
+ id: 'Search sources by title or path',
277
+ defaultMessage: 'Search sources by title or path',
278
+ },
279
+ searchRelationTarget: {
280
+ id: 'Search targets by title or path',
281
+ defaultMessage: 'Search targets by title or path',
282
+ },
283
+ createOrDeleteRelationsToTarget: {
284
+ id: 'Create or delete relations to target',
285
+ defaultMessage: 'Create or delete relations to target',
286
+ },
287
+ relationName: {
288
+ id: 'Relation name',
289
+ defaultMessage: 'relation',
290
+ },
291
+ selectRelation: {
292
+ id: 'Select relation',
293
+ defaultMessage: 'Select relation',
294
+ },
295
+ norelationfound: {
296
+ id: 'No relation found',
297
+ defaultMessage: 'No relation found',
298
+ },
299
+ toomanyrelationsfound: {
300
+ id: 'Many relations found. Please search.',
301
+ defaultMessage: 'Many relations found. Please search.',
302
+ },
303
+ rebuildRelations: {
304
+ id: 'rebuild relations',
305
+ defaultMessage: 'rebuild relations',
306
+ },
307
+ flushAndRebuildRelations: {
308
+ id: 'flush intIds and rebuild relations',
309
+ defaultMessage: 'flush intIds and rebuild relations',
310
+ },
311
+ addPotentialTargetsPath: {
312
+ id: 'target path',
313
+ defaultMessage: 'target path',
314
+ },
315
+ addPotentialSourcesPath: {
316
+ id: 'sources path',
317
+ defaultMessage: 'sources path',
318
+ },
319
+ relationsUpdated: {
320
+ id: 'Relations updated',
321
+ defaultMessage: 'Relations updated',
322
+ },
323
+ select: {
324
+ id: 'Select',
325
+ defaultMessage: 'Select',
326
+ },
327
+ selected: {
328
+ id: 'Selected',
329
+ defaultMessage: 'Selected',
330
+ },
331
+ filter: {
332
+ id: 'Filter',
333
+ defaultMessage: 'Filter',
334
+ },
263
335
  });