@plone/volto 18.28.2 → 18.29.1
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/CHANGELOG.md +37 -0
- package/cypress/support/commands.js +5 -6
- package/cypress.config.js +1 -0
- package/locales/af/LC_MESSAGES/volto.po +5 -0
- package/locales/af.json +1 -1
- package/locales/ar/LC_MESSAGES/volto.po +5 -0
- package/locales/ar.json +1 -1
- package/locales/bg/LC_MESSAGES/volto.po +5 -0
- package/locales/bg.json +1 -1
- package/locales/bn/LC_MESSAGES/volto.po +5 -0
- package/locales/bn.json +1 -1
- package/locales/ca/LC_MESSAGES/volto.po +5 -0
- package/locales/ca.json +1 -1
- package/locales/cs/LC_MESSAGES/volto.po +5 -0
- package/locales/cs.json +1 -1
- package/locales/cy/LC_MESSAGES/volto.po +5 -0
- package/locales/cy.json +1 -1
- package/locales/da/LC_MESSAGES/volto.po +5 -0
- package/locales/da.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +5 -0
- package/locales/de.json +1 -1
- package/locales/el/LC_MESSAGES/volto.po +5 -0
- package/locales/el.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +5 -0
- package/locales/en.json +1 -1
- package/locales/en_AU/LC_MESSAGES/volto.po +5 -0
- package/locales/en_AU.json +1 -1
- package/locales/en_GB/LC_MESSAGES/volto.po +5 -0
- package/locales/en_GB.json +1 -1
- package/locales/eo/LC_MESSAGES/volto.po +5 -0
- package/locales/eo.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +18 -13
- package/locales/es.json +1 -1
- package/locales/et/LC_MESSAGES/volto.po +5 -0
- package/locales/et.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +7 -2
- package/locales/eu.json +1 -1
- package/locales/fa/LC_MESSAGES/volto.po +5 -0
- package/locales/fa.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +5 -0
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +5 -0
- package/locales/fr.json +1 -1
- package/locales/fu/LC_MESSAGES/volto.po +5 -0
- package/locales/fu.json +1 -1
- package/locales/gl/LC_MESSAGES/volto.po +5 -0
- package/locales/gl.json +1 -1
- package/locales/he/LC_MESSAGES/volto.po +5 -0
- package/locales/he.json +1 -1
- package/locales/hi/LC_MESSAGES/volto.po +5 -0
- package/locales/hi.json +1 -1
- package/locales/hr/LC_MESSAGES/volto.po +5 -0
- package/locales/hr.json +1 -1
- package/locales/hu/LC_MESSAGES/volto.po +5 -0
- package/locales/hu.json +1 -1
- package/locales/hy/LC_MESSAGES/volto.po +5 -0
- package/locales/hy.json +1 -1
- package/locales/id/LC_MESSAGES/volto.po +5 -0
- package/locales/id.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +5 -0
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +49 -44
- package/locales/ja.json +1 -1
- package/locales/ka/LC_MESSAGES/volto.po +5 -0
- package/locales/ka.json +1 -1
- package/locales/kn/LC_MESSAGES/volto.po +5 -0
- package/locales/kn.json +1 -1
- package/locales/ko/LC_MESSAGES/volto.po +5 -0
- package/locales/ko.json +1 -1
- package/locales/lt/LC_MESSAGES/volto.po +5 -0
- package/locales/lt.json +1 -1
- package/locales/lv/LC_MESSAGES/volto.po +5 -0
- package/locales/lv.json +1 -1
- package/locales/mi/LC_MESSAGES/volto.po +5 -0
- package/locales/mi.json +1 -1
- package/locales/mk/LC_MESSAGES/volto.po +5 -0
- package/locales/mk.json +1 -1
- package/locales/my/LC_MESSAGES/volto.po +5 -0
- package/locales/my.json +1 -1
- package/locales/nb_NO/LC_MESSAGES/volto.po +5 -0
- package/locales/nb_NO.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +7 -2
- package/locales/nl.json +1 -1
- package/locales/nn/LC_MESSAGES/volto.po +5 -0
- package/locales/nn.json +1 -1
- package/locales/pl/LC_MESSAGES/volto.po +5 -0
- package/locales/pl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +5 -0
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +5 -0
- package/locales/pt_BR.json +1 -1
- package/locales/rm/LC_MESSAGES/volto.po +5 -0
- package/locales/rm.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +5 -0
- package/locales/ro.json +1 -1
- package/locales/ru/LC_MESSAGES/volto.po +5 -0
- package/locales/ru.json +1 -1
- package/locales/sk/LC_MESSAGES/volto.po +5 -0
- package/locales/sk.json +1 -1
- package/locales/sl/LC_MESSAGES/volto.po +5 -0
- package/locales/sl.json +1 -1
- package/locales/sm/LC_MESSAGES/volto.po +5 -0
- package/locales/sm.json +1 -1
- package/locales/sq/LC_MESSAGES/volto.po +5 -0
- package/locales/sq.json +1 -1
- package/locales/sr/LC_MESSAGES/volto.po +5 -0
- package/locales/sr.json +1 -1
- package/locales/sr@cyrl/LC_MESSAGES/volto.po +5 -0
- package/locales/sr@cyrl.json +1 -1
- package/locales/sr@latn/LC_MESSAGES/volto.po +5 -0
- package/locales/sr@latn.json +1 -1
- package/locales/sv/LC_MESSAGES/volto.po +5 -0
- package/locales/sv.json +1 -1
- package/locales/ta/LC_MESSAGES/volto.po +58 -52
- package/locales/ta.json +1 -1
- package/locales/te/LC_MESSAGES/volto.po +5 -0
- package/locales/te.json +1 -1
- package/locales/th/LC_MESSAGES/volto.po +5 -0
- package/locales/th.json +1 -1
- package/locales/to/LC_MESSAGES/volto.po +5 -0
- package/locales/to.json +1 -1
- package/locales/tr/LC_MESSAGES/volto.po +5 -0
- package/locales/tr.json +1 -1
- package/locales/uk/LC_MESSAGES/volto.po +5 -0
- package/locales/uk.json +1 -1
- package/locales/vi/LC_MESSAGES/volto.po +5 -0
- package/locales/vi.json +1 -1
- package/locales/volto.pot +6 -1
- package/locales/zh_CN/LC_MESSAGES/volto.po +5 -0
- package/locales/zh_CN.json +1 -1
- package/locales/zh_Hant/LC_MESSAGES/volto.po +5 -0
- package/locales/zh_Hant.json +1 -1
- package/locales/zh_Hant_HK/LC_MESSAGES/volto.po +5 -0
- package/locales/zh_Hant_HK.json +1 -1
- package/news/7428.feat +1 -0
- package/package.json +14 -12
- package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +3 -2
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +575 -630
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.test.jsx +4 -3
- package/src/components/manage/Form/InlineForm.jsx +2 -2
- package/src/components/manage/Multilingual/CreateTranslation.jsx +8 -8
- package/src/components/manage/Widgets/ImageWidget.jsx +11 -4
- package/src/components/theme/LanguageSelector/{LanguageSelector.jsx → LanguageSelector.tsx} +36 -27
- package/src/components/theme/View/View.jsx +1 -1
- package/src/helpers/Content/Content.js +23 -0
- package/src/helpers/Content/Content.test.js +39 -0
- package/src/helpers/MessageLabels/MessageLabels.js +5 -0
- package/src/hooks/user/useUser.js +1 -1
- 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/theme/themes/pastanaga/extras/main.less +4 -0
- package/tsconfig.json +3 -4
- package/types/components/manage/Controlpanels/Users/UsersControlpanel.d.ts +6 -2
- package/types/components/manage/Controlpanels/index.d.ts +1 -1
- package/types/components/theme/LanguageSelector/LanguageSelector.d.ts +3 -10
- package/types/helpers/Content/Content.d.ts +7 -0
- package/types/helpers/MessageLabels/MessageLabels.d.ts +68 -62
- package/types/reducers/index.d.ts +1 -0
- /package/src/components/theme/LanguageSelector/{LanguageSelector.test.jsx → LanguageSelector.test.tsx} +0 -0
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { render, act } from '@testing-library/react';
|
|
3
3
|
import configureStore from 'redux-mock-store';
|
|
4
4
|
import { Provider } from 'react-intl-redux';
|
|
5
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
5
6
|
import jwt from 'jsonwebtoken';
|
|
6
7
|
|
|
7
8
|
import UsersControlpanel from './UsersControlpanel';
|
|
@@ -41,10 +42,10 @@ describe('UsersControlpanel', () => {
|
|
|
41
42
|
const { container } = await act(async () => {
|
|
42
43
|
return render(
|
|
43
44
|
<Provider store={store}>
|
|
44
|
-
|
|
45
|
-
<UsersControlpanel
|
|
45
|
+
<MemoryRouter initialEntries={['/controlpanel/users']}>
|
|
46
|
+
<UsersControlpanel />
|
|
46
47
|
<div id="toolbar"></div>
|
|
47
|
-
|
|
48
|
+
</MemoryRouter>
|
|
48
49
|
</Provider>,
|
|
49
50
|
);
|
|
50
51
|
});
|
|
@@ -158,7 +158,7 @@ const InlineForm = (props) => {
|
|
|
158
158
|
onChangeField(id, value, itemInfo);
|
|
159
159
|
}}
|
|
160
160
|
key={field}
|
|
161
|
-
error={errors?.[block]?.[field] ||
|
|
161
|
+
error={errors?.[block]?.[field] || []}
|
|
162
162
|
block={block}
|
|
163
163
|
/>
|
|
164
164
|
))}
|
|
@@ -199,7 +199,7 @@ const InlineForm = (props) => {
|
|
|
199
199
|
onChangeField(id, value);
|
|
200
200
|
}}
|
|
201
201
|
key={field}
|
|
202
|
-
error={errors?.[block]?.[field] ||
|
|
202
|
+
error={errors?.[block]?.[field] || []}
|
|
203
203
|
block={block}
|
|
204
204
|
/>
|
|
205
205
|
))}
|
|
@@ -12,9 +12,12 @@ const CreateTranslation = (props) => {
|
|
|
12
12
|
const dispatch = useDispatch();
|
|
13
13
|
const { language, translationOf } = props.location.state;
|
|
14
14
|
const [translationLocation, setTranslationLocation] = React.useState(null);
|
|
15
|
-
const [translationObject, setTranslationObject] = React.useState(null);
|
|
16
15
|
const languageFrom = useSelector((state) => state.intl.locale);
|
|
17
16
|
|
|
17
|
+
const translationObject = useSelector(
|
|
18
|
+
(state) => state.content.subrequests['translationObject'],
|
|
19
|
+
);
|
|
20
|
+
|
|
18
21
|
React.useEffect(() => {
|
|
19
22
|
// Only on mount, we dispatch the locator query
|
|
20
23
|
dispatch(getTranslationLocator(translationOf, language)).then((resp) => {
|
|
@@ -22,11 +25,7 @@ const CreateTranslation = (props) => {
|
|
|
22
25
|
});
|
|
23
26
|
|
|
24
27
|
//and we load the translationObject
|
|
25
|
-
dispatch(getContent(translationOf, null, 'translationObject'))
|
|
26
|
-
(resp) => {
|
|
27
|
-
setTranslationObject(resp);
|
|
28
|
-
},
|
|
29
|
-
);
|
|
28
|
+
dispatch(getContent(translationOf, null, 'translationObject'));
|
|
30
29
|
|
|
31
30
|
// On unmount we dispatch the language change
|
|
32
31
|
return () => {
|
|
@@ -46,7 +45,8 @@ const CreateTranslation = (props) => {
|
|
|
46
45
|
|
|
47
46
|
return (
|
|
48
47
|
translationLocation &&
|
|
49
|
-
translationObject &&
|
|
48
|
+
translationObject.data &&
|
|
49
|
+
translationObject.loaded > 0 && (
|
|
50
50
|
<Redirect
|
|
51
51
|
to={{
|
|
52
52
|
pathname: `${flattenToAppURL(translationLocation)}/add`,
|
|
@@ -54,7 +54,7 @@ const CreateTranslation = (props) => {
|
|
|
54
54
|
state: {
|
|
55
55
|
translationOf: props.location.state.translationOf,
|
|
56
56
|
language: props.location.state.language,
|
|
57
|
-
translationObject: translationObject,
|
|
57
|
+
translationObject: translationObject.data,
|
|
58
58
|
languageFrom,
|
|
59
59
|
},
|
|
60
60
|
}}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import { Button, Dimmer, Loader, Message } from 'semantic-ui-react';
|
|
3
3
|
import { useIntl, defineMessages } from 'react-intl';
|
|
4
|
-
import { useDispatch } from 'react-redux';
|
|
4
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
5
5
|
import { useLocation } from 'react-router-dom';
|
|
6
6
|
import loadable from '@loadable/component';
|
|
7
7
|
import { connect } from 'react-redux';
|
|
@@ -14,6 +14,7 @@ import config from '@plone/volto/registry';
|
|
|
14
14
|
import {
|
|
15
15
|
flattenToAppURL,
|
|
16
16
|
getBaseUrl,
|
|
17
|
+
getParentUrl,
|
|
17
18
|
isInternalURL,
|
|
18
19
|
normalizeUrl,
|
|
19
20
|
removeProtocol,
|
|
@@ -109,6 +110,9 @@ const UnconnectedImageInput = (props) => {
|
|
|
109
110
|
const linkEditor = useLinkEditor();
|
|
110
111
|
const location = useLocation();
|
|
111
112
|
const dispatch = useDispatch();
|
|
113
|
+
const isFolderish = useSelector(
|
|
114
|
+
(state) => state?.content?.data?.is_folderish,
|
|
115
|
+
);
|
|
112
116
|
const contextUrl = location.pathname;
|
|
113
117
|
|
|
114
118
|
const [uploading, setUploading] = React.useState(false);
|
|
@@ -156,6 +160,8 @@ const UnconnectedImageInput = (props) => {
|
|
|
156
160
|
|
|
157
161
|
const handleUpload = React.useCallback(
|
|
158
162
|
(eventOrFile) => {
|
|
163
|
+
let uploadUrl = getBaseUrl(contextUrl);
|
|
164
|
+
if (!isFolderish) uploadUrl = getParentUrl(uploadUrl);
|
|
159
165
|
if (restrictFileUpload === true) return;
|
|
160
166
|
eventOrFile.target && eventOrFile.stopPropagation();
|
|
161
167
|
|
|
@@ -171,7 +177,7 @@ const UnconnectedImageInput = (props) => {
|
|
|
171
177
|
const fields = fileData.match(/^data:(.*);(.*),(.*)$/);
|
|
172
178
|
dispatch(
|
|
173
179
|
createContent(
|
|
174
|
-
|
|
180
|
+
uploadUrl,
|
|
175
181
|
{
|
|
176
182
|
'@type': 'Image',
|
|
177
183
|
title: file.name,
|
|
@@ -188,11 +194,12 @@ const UnconnectedImageInput = (props) => {
|
|
|
188
194
|
});
|
|
189
195
|
},
|
|
190
196
|
[
|
|
197
|
+
contextUrl,
|
|
198
|
+
isFolderish,
|
|
191
199
|
restrictFileUpload,
|
|
192
200
|
intl.formatMessage,
|
|
193
201
|
dispatch,
|
|
194
|
-
props,
|
|
195
|
-
contextUrl,
|
|
202
|
+
props.block,
|
|
196
203
|
requestId,
|
|
197
204
|
],
|
|
198
205
|
);
|
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Language selector component.
|
|
3
|
-
* @module components/LanguageSelector/LanguageSelector
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React from 'react';
|
|
7
|
-
import PropTypes from 'prop-types';
|
|
8
1
|
import { Link } from 'react-router-dom';
|
|
9
2
|
|
|
10
3
|
import { useSelector } from 'react-redux';
|
|
11
4
|
import cx from 'classnames';
|
|
12
|
-
import find from 'lodash/find';
|
|
13
5
|
import map from 'lodash/map';
|
|
14
6
|
|
|
15
7
|
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
|
|
@@ -19,7 +11,12 @@ import { toReactIntlLang } from '@plone/volto/helpers/Utils/Utils';
|
|
|
19
11
|
|
|
20
12
|
import config from '@plone/volto/registry';
|
|
21
13
|
|
|
22
|
-
import { defineMessages, useIntl } from 'react-intl';
|
|
14
|
+
import { defineMessages, useIntl, type IntlShape } from 'react-intl';
|
|
15
|
+
import type {
|
|
16
|
+
Content,
|
|
17
|
+
GetSiteResponse,
|
|
18
|
+
GetTranslationResponse,
|
|
19
|
+
} from '@plone/types';
|
|
23
20
|
|
|
24
21
|
const messages = defineMessages({
|
|
25
22
|
switchLanguageTo: {
|
|
@@ -28,33 +25,53 @@ const messages = defineMessages({
|
|
|
28
25
|
},
|
|
29
26
|
});
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
type FormState = {
|
|
29
|
+
content: {
|
|
30
|
+
data: Content;
|
|
31
|
+
};
|
|
32
|
+
intl: IntlShape;
|
|
33
|
+
site: {
|
|
34
|
+
data: GetSiteResponse;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const LanguageSelector = ({
|
|
39
|
+
onClickAction = () => {},
|
|
40
|
+
}: {
|
|
41
|
+
onClickAction?: () => void;
|
|
42
|
+
}) => {
|
|
32
43
|
const intl = useIntl();
|
|
33
|
-
const currentLang = useSelector
|
|
34
|
-
|
|
35
|
-
(state) => state.content.data?.['@components']?.translations?.items,
|
|
44
|
+
const currentLang = useSelector<FormState, IntlShape['locale']>(
|
|
45
|
+
(state) => state.intl.locale,
|
|
36
46
|
);
|
|
47
|
+
const translations = useSelector<
|
|
48
|
+
FormState,
|
|
49
|
+
GetTranslationResponse['items'] | undefined
|
|
50
|
+
>((state) => state.content.data?.['@components']?.translations?.items);
|
|
37
51
|
|
|
38
52
|
const { settings } = config;
|
|
39
53
|
|
|
40
54
|
return settings.isMultilingual ? (
|
|
41
55
|
<div className="language-selector">
|
|
42
|
-
{map(settings.supportedLanguages, (lang) => {
|
|
43
|
-
const
|
|
56
|
+
{map(settings.supportedLanguages || [], (lang) => {
|
|
57
|
+
const langKey = lang as keyof typeof langmap;
|
|
58
|
+
const translation = translations?.find(
|
|
59
|
+
(t: { language: string }) => t.language === lang,
|
|
60
|
+
);
|
|
44
61
|
return (
|
|
45
62
|
<Link
|
|
46
63
|
aria-label={`${intl.formatMessage(
|
|
47
64
|
messages.switchLanguageTo,
|
|
48
|
-
)} ${langmap[
|
|
65
|
+
)} ${langmap[langKey].nativeName.toLowerCase()}`}
|
|
49
66
|
className={cx({ selected: toReactIntlLang(lang) === currentLang })}
|
|
50
67
|
to={translation ? flattenToAppURL(translation['@id']) : `/${lang}`}
|
|
51
|
-
title={langmap[
|
|
68
|
+
title={langmap[langKey].nativeName}
|
|
52
69
|
onClick={() => {
|
|
53
|
-
|
|
70
|
+
onClickAction();
|
|
54
71
|
}}
|
|
55
72
|
key={`language-selector-${lang}`}
|
|
56
73
|
>
|
|
57
|
-
{langmap[
|
|
74
|
+
{langmap[langKey].nativeName}
|
|
58
75
|
</Link>
|
|
59
76
|
);
|
|
60
77
|
})}
|
|
@@ -66,12 +83,4 @@ const LanguageSelector = (props) => {
|
|
|
66
83
|
);
|
|
67
84
|
};
|
|
68
85
|
|
|
69
|
-
LanguageSelector.propTypes = {
|
|
70
|
-
onClickAction: PropTypes.func,
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
LanguageSelector.defaultProps = {
|
|
74
|
-
onClickAction: () => {},
|
|
75
|
-
};
|
|
76
|
-
|
|
77
86
|
export default LanguageSelector;
|
|
@@ -246,7 +246,7 @@ class View extends Component {
|
|
|
246
246
|
/>
|
|
247
247
|
<SlotRenderer name="aboveContent" content={this.props.content} />
|
|
248
248
|
<RenderedView
|
|
249
|
-
key={this.props.content['@id']}
|
|
249
|
+
key={flattenToAppURL(this.props.content['@id'])}
|
|
250
250
|
content={this.props.content}
|
|
251
251
|
location={this.props.location}
|
|
252
252
|
token={this.props.token}
|
|
@@ -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
|
});
|
|
@@ -152,6 +152,11 @@ export const messages = defineMessages({
|
|
|
152
152
|
id: 'Groupname',
|
|
153
153
|
defaultMessage: 'Groupname',
|
|
154
154
|
},
|
|
155
|
+
addGroupsFormGroupNameDescription: {
|
|
156
|
+
id: 'A unique identifier for the group. Cannot be changed after creation. No spaces allowed.',
|
|
157
|
+
defaultMessage:
|
|
158
|
+
'A unique identifier for the group. Cannot be changed after creation. No spaces allowed.',
|
|
159
|
+
},
|
|
155
160
|
addGroupsFormDescriptionTitle: {
|
|
156
161
|
id: 'Description',
|
|
157
162
|
defaultMessage: 'Description',
|
|
@@ -12,7 +12,7 @@ const useUser = () => {
|
|
|
12
12
|
const dispatch = useDispatch();
|
|
13
13
|
|
|
14
14
|
useEffect(() => {
|
|
15
|
-
if (!user?.id && users?.get.loading === false) {
|
|
15
|
+
if (userId && !user?.id && users?.get.loading === false) {
|
|
16
16
|
dispatch(getUser(userId));
|
|
17
17
|
}
|
|
18
18
|
}, [dispatch, userId, user, users?.get.loading]);
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
* @module reducers/content/content
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import map from 'lodash/map';
|
|
7
|
-
import mapKeys from 'lodash/mapKeys';
|
|
8
6
|
import omit from 'lodash/omit';
|
|
9
7
|
|
|
10
8
|
import { flattenToAppURL } from '@plone/volto/helpers/Url/Url';
|
|
9
|
+
import { flattenStaticBehaviors } from '@plone/volto/helpers/Content/Content';
|
|
11
10
|
|
|
12
11
|
import {
|
|
13
12
|
CREATE_CONTENT,
|
|
@@ -131,14 +130,7 @@ export default function content(state = initialState, action = {}) {
|
|
|
131
130
|
},
|
|
132
131
|
};
|
|
133
132
|
case `${CREATE_CONTENT}_SUCCESS`:
|
|
134
|
-
|
|
135
|
-
map(result['@static_behaviors'], (behavior) => {
|
|
136
|
-
result = {
|
|
137
|
-
...omit(result, behavior),
|
|
138
|
-
...mapKeys(result[behavior], (value, key) => `${behavior}.${key}`),
|
|
139
|
-
};
|
|
140
|
-
});
|
|
141
|
-
}
|
|
133
|
+
result = flattenStaticBehaviors(result);
|
|
142
134
|
const data = action.subrequest
|
|
143
135
|
? Array.isArray(result)
|
|
144
136
|
? result.map((item) => ({
|
|
@@ -188,14 +180,7 @@ export default function content(state = initialState, action = {}) {
|
|
|
188
180
|
},
|
|
189
181
|
};
|
|
190
182
|
case `${GET_CONTENT}_SUCCESS`:
|
|
191
|
-
|
|
192
|
-
map(result['@static_behaviors'], (behavior) => {
|
|
193
|
-
result = {
|
|
194
|
-
...omit(result, behavior),
|
|
195
|
-
...mapKeys(result[behavior], (value, key) => `${behavior}.${key}`),
|
|
196
|
-
};
|
|
197
|
-
});
|
|
198
|
-
}
|
|
183
|
+
result = flattenStaticBehaviors(result);
|
|
199
184
|
|
|
200
185
|
const transforms = config.getUtilities({
|
|
201
186
|
type: 'transform',
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { GET_DIFF } from '@plone/volto/constants/ActionTypes';
|
|
7
|
+
import { flattenStaticBehaviors } from '@plone/volto/helpers/Content/Content';
|
|
7
8
|
|
|
8
9
|
const initialState = {
|
|
9
10
|
error: null,
|
|
@@ -29,10 +30,13 @@ export default function diff(state = initialState, action = {}) {
|
|
|
29
30
|
loading: true,
|
|
30
31
|
};
|
|
31
32
|
case `${GET_DIFF}_SUCCESS`:
|
|
33
|
+
const first_result = flattenStaticBehaviors(action.result[0]);
|
|
34
|
+
const second_result = flattenStaticBehaviors(action.result[1]);
|
|
35
|
+
|
|
32
36
|
return {
|
|
33
37
|
...state,
|
|
34
38
|
error: null,
|
|
35
|
-
data:
|
|
39
|
+
data: [first_result, second_result],
|
|
36
40
|
loaded: true,
|
|
37
41
|
loading: false,
|
|
38
42
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import diff from './diff';
|
|
2
1
|
import { GET_DIFF } from '@plone/volto/constants/ActionTypes';
|
|
2
|
+
import diff from './diff';
|
|
3
3
|
|
|
4
4
|
describe('Diff reducer', () => {
|
|
5
5
|
it('should return the initial state', () => {
|
|
@@ -24,15 +24,71 @@ describe('Diff reducer', () => {
|
|
|
24
24
|
});
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
it('should handle GET_DIFF_SUCCESS', () => {
|
|
27
|
+
it('should handle GET_DIFF_SUCCESS with array result with static behaviors', () => {
|
|
28
|
+
expect(
|
|
29
|
+
diff(undefined, {
|
|
30
|
+
type: `${GET_DIFF}_SUCCESS`,
|
|
31
|
+
result: [
|
|
32
|
+
{
|
|
33
|
+
'@static_behaviors': [
|
|
34
|
+
'guillotina.behaviors.dublincore.IDublinCore',
|
|
35
|
+
],
|
|
36
|
+
'guillotina.behaviors.dublincore.IDublinCore': {
|
|
37
|
+
title: 'test page',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
'@static_behaviors': [
|
|
42
|
+
'guillotina.behaviors.dublincore.IDublinCore',
|
|
43
|
+
],
|
|
44
|
+
'guillotina.behaviors.dublincore.IDublinCore': {
|
|
45
|
+
title: 'test second page',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
}),
|
|
50
|
+
).toEqual({
|
|
51
|
+
error: null,
|
|
52
|
+
data: [
|
|
53
|
+
{
|
|
54
|
+
'@static_behaviors': ['guillotina.behaviors.dublincore.IDublinCore'],
|
|
55
|
+
|
|
56
|
+
'guillotina.behaviors.dublincore.IDublinCore.title': 'test page',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
'@static_behaviors': ['guillotina.behaviors.dublincore.IDublinCore'],
|
|
60
|
+
'guillotina.behaviors.dublincore.IDublinCore.title':
|
|
61
|
+
'test second page',
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
loaded: true,
|
|
65
|
+
loading: false,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should handle GET_DIFF_SUCCESS with array result without static behaviors', () => {
|
|
70
|
+
const data = [
|
|
71
|
+
{
|
|
72
|
+
title: 'test page',
|
|
73
|
+
'guillotina.behaviors.dublincore.IDublinCore': {
|
|
74
|
+
title: 'test page',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
title: 'test second page',
|
|
79
|
+
'guillotina.behaviors.dublincore.IDublinCore': {
|
|
80
|
+
title: 'test second page',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
];
|
|
28
84
|
expect(
|
|
29
85
|
diff(undefined, {
|
|
30
86
|
type: `${GET_DIFF}_SUCCESS`,
|
|
31
|
-
result:
|
|
87
|
+
result: data,
|
|
32
88
|
}),
|
|
33
89
|
).toEqual({
|
|
34
90
|
error: null,
|
|
35
|
-
data:
|
|
91
|
+
data: data,
|
|
36
92
|
loaded: true,
|
|
37
93
|
loading: false,
|
|
38
94
|
});
|
|
@@ -440,6 +440,10 @@ fieldset.invisible {
|
|
|
440
440
|
padding: 0;
|
|
441
441
|
border: none;
|
|
442
442
|
margin: 0;
|
|
443
|
+
// Reset visibility, this 'invisible' is not meant to hide the fieldset
|
|
444
|
+
// This prevents issues when used along other CSS framworks that might
|
|
445
|
+
// have this utility class defined (e.g. TailwindCSS)
|
|
446
|
+
visibility: initial;
|
|
443
447
|
}
|
|
444
448
|
|
|
445
449
|
.vertical-form {
|
package/tsconfig.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"target": "
|
|
4
|
-
"lib": ["
|
|
3
|
+
"target": "es2022",
|
|
4
|
+
"lib": ["es2022", "dom", "dom.iterable"],
|
|
5
5
|
"types": ["vitest/globals", "jest"],
|
|
6
|
-
"module": "
|
|
6
|
+
"module": "preserve",
|
|
7
7
|
"allowJs": true,
|
|
8
8
|
"skipLibCheck": true,
|
|
9
9
|
"esModuleInterop": true,
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
"strict": true,
|
|
12
12
|
"forceConsistentCasingInFileNames": true,
|
|
13
13
|
"strictPropertyInitialization": false,
|
|
14
|
-
"moduleResolution": "Node",
|
|
15
14
|
"resolveJsonModule": true,
|
|
16
15
|
"isolatedModules": true,
|
|
17
16
|
"noEmit": true,
|
|
@@ -4,7 +4,7 @@ export declare const RulesControlpanel: import("@loadable/component").LoadableCl
|
|
|
4
4
|
export declare const AddRuleControlpanel: import("@loadable/component").LoadableClassComponent<any>;
|
|
5
5
|
export declare const EditRuleControlpanel: import("@loadable/component").LoadableClassComponent<any>;
|
|
6
6
|
export declare const ConfigureRuleControlpanel: import("@loadable/component").LoadableClassComponent<any>;
|
|
7
|
-
export declare const UsersControlpanel: import("@loadable/component").
|
|
7
|
+
export declare const UsersControlpanel: import("@loadable/component").LoadableComponent<unknown>;
|
|
8
8
|
export declare const RenderUsers: import("@loadable/component").LoadableComponent<any>;
|
|
9
9
|
export declare const UserGroupMembershipControlPanel: import("@loadable/component").LoadableComponent<unknown>;
|
|
10
10
|
export declare const GroupsControlpanel: import("@loadable/component").LoadableClassComponent<any>;
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
+
declare const LanguageSelector: ({ onClickAction, }: {
|
|
2
|
+
onClickAction?: () => void;
|
|
3
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
1
4
|
export default LanguageSelector;
|
|
2
|
-
declare function LanguageSelector(props: any): import("react/jsx-runtime").JSX.Element;
|
|
3
|
-
declare namespace LanguageSelector {
|
|
4
|
-
namespace propTypes {
|
|
5
|
-
let onClickAction: any;
|
|
6
|
-
}
|
|
7
|
-
namespace defaultProps {
|
|
8
|
-
export function onClickAction_1(): void;
|
|
9
|
-
export { onClickAction_1 as onClickAction };
|
|
10
|
-
}
|
|
11
|
-
}
|
|
@@ -29,3 +29,10 @@ export function getContentIcon(type: string, isFolderish: boolean): any;
|
|
|
29
29
|
* @returns {array} List of language independent fields
|
|
30
30
|
*/
|
|
31
31
|
export function getLanguageIndependentFields(schema: string): any[];
|
|
32
|
+
/**
|
|
33
|
+
* Flattens static behaviors into the parent object with dot-notation keys.
|
|
34
|
+
* @function flattenStaticBehaviors
|
|
35
|
+
* @param {Object} result The result object containing static behaviors.
|
|
36
|
+
* @returns {Object} Result object with flattened static behaviors.
|
|
37
|
+
*/
|
|
38
|
+
export function flattenStaticBehaviors(result: any): any;
|