@plone/volto 17.0.0-alpha.24 → 17.0.0-alpha.26
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/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +92 -4
- package/CONTRIBUTING.md +5 -1
- package/README.md +9 -7
- package/cypress/support/commands.js +12 -9
- package/cypress.config.js +1 -0
- package/locales/ca/LC_MESSAGES/volto.po +41 -15
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +41 -15
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +40 -14
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +69 -43
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +40 -14
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +40 -14
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +41 -15
- package/locales/fr.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +40 -14
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +40 -14
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +41 -15
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +41 -15
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +40 -14
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +41 -15
- package/locales/ro.json +1 -1
- package/locales/volto.pot +41 -15
- package/locales/zh_CN/LC_MESSAGES/volto.po +41 -15
- package/locales/zh_CN.json +1 -1
- package/package.json +4 -4
- package/packages/volto-slate/build/messages/src/blocks/Table/TableBlockEdit.json +90 -0
- package/packages/volto-slate/build/messages/src/blocks/Text/DefaultTextBlockEditor.json +6 -0
- package/packages/volto-slate/build/messages/src/blocks/Text/DetachedTextBlockEditor.json +6 -0
- package/packages/volto-slate/build/messages/src/blocks/Text/SlashMenu.json +6 -0
- package/packages/volto-slate/build/messages/src/editor/plugins/AdvancedLink/index.json +10 -0
- package/packages/volto-slate/build/messages/src/editor/plugins/Link/index.json +10 -0
- package/packages/volto-slate/build/messages/src/editor/plugins/Table/index.json +30 -0
- package/packages/volto-slate/build/messages/src/elementEditor/messages.json +10 -0
- package/packages/volto-slate/build/messages/src/widgets/HtmlSlateWidget.json +6 -0
- package/packages/volto-slate/build/messages/src/widgets/RichTextWidgetView.json +6 -0
- package/packages/volto-slate/package.json +1 -1
- package/packages/volto-slate/src/editor/render.jsx +2 -3
- package/src/actions/index.js +4 -0
- package/src/actions/navroot/navroot.js +16 -0
- package/src/actions/navroot/navroot.test.js +15 -0
- package/src/actions/relations/relations.js +17 -0
- package/src/actions/site/site.js +16 -0
- package/src/actions/site/site.test.js +15 -0
- package/src/actions/userSession/userSession.js +17 -1
- package/src/components/manage/Blocks/Block/Settings.jsx +2 -0
- package/src/components/manage/Blocks/Block/Settings.test.jsx +90 -0
- package/src/components/manage/Blocks/Image/schema.js +5 -1
- package/src/components/manage/Blocks/Listing/withQuerystringResults.jsx +18 -11
- package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +42 -25
- package/src/components/manage/Blocks/ToC/View.jsx +75 -13
- package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.jsx +4 -13
- package/src/components/manage/Blocks/ToC/variations/DefaultTocRenderer.test.jsx +44 -0
- package/src/components/manage/Contents/Contents.jsx +27 -0
- package/src/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +65 -38
- package/src/components/manage/Controlpanels/Relations/BrokenRelations.jsx +11 -9
- package/src/components/manage/Controlpanels/Relations/Relations.jsx +3 -3
- package/src/components/manage/Controlpanels/Relations/RelationsListing.jsx +8 -7
- package/src/components/manage/Controlpanels/Relations/RelationsMatrix.jsx +15 -9
- package/src/components/manage/Controlpanels/Rules/AddRule.jsx +1 -1
- package/src/components/manage/Controlpanels/Rules/EditRule.jsx +1 -1
- package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +95 -5
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +127 -99
- package/src/components/manage/Diff/DiffField.jsx +25 -1
- package/src/components/manage/Form/BlockDataForm.jsx +3 -2
- package/src/components/manage/Form/BlockDataForm.test.jsx +34 -2
- package/src/components/manage/LinksToItem/LinksToItem.jsx +1 -1
- package/src/components/manage/LinksToItem/LinksToItem.test.jsx +5 -2
- package/src/components/manage/Messages/Messages.jsx +32 -99
- package/src/components/manage/Messages/Messages.test.jsx +0 -1
- package/src/components/manage/Sharing/Sharing.jsx +50 -21
- package/src/components/manage/UniversalLink/UniversalLink.jsx +4 -6
- package/src/components/manage/Widgets/ArrayWidget.jsx +3 -1
- package/src/components/manage/Widgets/ArrayWidget.test.jsx +45 -1
- package/src/components/manage/Widgets/FormFieldWrapper.jsx +1 -1
- package/src/components/manage/Widgets/RegistryImageWidget.jsx +210 -0
- package/src/components/manage/Widgets/RegistryImageWidget.test.jsx +91 -0
- package/src/components/manage/Widgets/SelectWidget.jsx +15 -1
- package/src/components/manage/Widgets/SelectWidget.test.jsx +45 -1
- package/src/components/theme/Comments/Comment.stories.jsx +84 -0
- package/src/components/theme/Comments/Comments.jsx +273 -378
- package/src/components/theme/ContentMetadataTags/ContentMetadataTags.jsx +37 -3
- package/src/components/theme/Login/Login.jsx +159 -241
- package/src/components/theme/Logo/Logo.Multilingual.test.jsx +131 -1
- package/src/components/theme/Logo/Logo.jsx +35 -29
- package/src/components/theme/Logo/Logo.test.jsx +135 -1
- package/src/components/theme/Logout/Logout.jsx +36 -83
- package/src/components/theme/Navigation/Navigation.jsx +86 -171
- package/src/components/theme/Search/SearchTags.jsx +30 -60
- package/src/components/theme/SearchWidget/SearchWidget.jsx +15 -3
- package/src/components/theme/SearchWidget/SearchWidget.test.jsx +8 -0
- package/src/components/theme/Sitemap/Sitemap.jsx +24 -13
- package/src/components/theme/Sitemap/Sitemap.test.jsx +23 -2
- package/src/components/theme/View/View.jsx +2 -0
- package/src/config/ControlPanels.js +0 -1
- package/src/config/Widgets.jsx +2 -0
- package/src/config/index.js +15 -3
- package/src/constants/ActionTypes.js +4 -0
- package/src/express-middleware/images.js +1 -0
- package/src/helpers/MessageLabels/MessageLabels.js +26 -4
- package/src/helpers/Site/index.js +21 -0
- package/src/helpers/index.js +1 -0
- package/src/reducers/index.js +4 -0
- package/src/reducers/navroot/navroot.js +79 -0
- package/src/reducers/navroot/navroot.test.js +110 -0
- package/src/reducers/relations/relations.js +74 -46
- package/src/reducers/site/site.js +51 -0
- package/src/reducers/site/site.test.js +67 -0
- package/src/reducers/userSession/userSession.js +15 -1
- package/src/server.jsx +9 -0
- package/test-setup-config.js +1 -0
- package/theme/themes/pastanaga/collections/form.overrides +46 -0
- package/theme/themes/pastanaga/elements/input.overrides +10 -0
- package/theme/themes/pastanaga/elements/label.overrides +10 -0
- package/theme/themes/pastanaga/extras/login.less +3 -0
- package/webpack-plugins/webpack-less-plugin.js +19 -0
- package/.gitignore~ +0 -71
- package/news/4547.breaking~ +0 -1
- package/package.json~ +0 -444
- package/src/config/index.js~ +0 -223
|
@@ -1,71 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* @module components/theme/Search/SearchTags
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { Component } from 'react';
|
|
7
|
-
import PropTypes from 'prop-types';
|
|
8
|
-
import { connect } from 'react-redux';
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
|
9
3
|
import { Link } from 'react-router-dom';
|
|
10
4
|
|
|
11
5
|
import { getVocabulary } from '@plone/volto/actions';
|
|
12
6
|
|
|
13
7
|
const vocabulary = 'plone.app.vocabularies.Keywords';
|
|
14
8
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
* @class SearchTags
|
|
18
|
-
* @extends Component
|
|
19
|
-
*/
|
|
20
|
-
class SearchTags extends Component {
|
|
21
|
-
/**
|
|
22
|
-
* Property types.
|
|
23
|
-
* @property {Object} propTypes Property types.
|
|
24
|
-
* @static
|
|
25
|
-
*/
|
|
26
|
-
static propTypes = {
|
|
27
|
-
getVocabulary: PropTypes.func.isRequired,
|
|
28
|
-
items: PropTypes.arrayOf(
|
|
29
|
-
PropTypes.shape({
|
|
30
|
-
label: PropTypes.string,
|
|
31
|
-
}),
|
|
32
|
-
).isRequired,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
componentDidMount() {
|
|
36
|
-
this.props.getVocabulary({ vocabNameOrURL: vocabulary });
|
|
37
|
-
}
|
|
9
|
+
const SearchTags = () => {
|
|
10
|
+
const dispatch = useDispatch();
|
|
38
11
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
* @returns {string} Markup for the component.
|
|
43
|
-
*/
|
|
44
|
-
render() {
|
|
45
|
-
return this.props.items && this.props.items.length > 0 ? (
|
|
46
|
-
<div>
|
|
47
|
-
{this.props.items.map((item) => (
|
|
48
|
-
<Link
|
|
49
|
-
className="ui label"
|
|
50
|
-
to={`/search?Subject=${item.label}`}
|
|
51
|
-
key={item.label}
|
|
52
|
-
>
|
|
53
|
-
{item.label}
|
|
54
|
-
</Link>
|
|
55
|
-
))}
|
|
56
|
-
</div>
|
|
57
|
-
) : (
|
|
58
|
-
<span />
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
dispatch(getVocabulary({ vocabNameOrURL: vocabulary }));
|
|
14
|
+
}, [dispatch]);
|
|
62
15
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
items:
|
|
16
|
+
const items = useSelector(
|
|
17
|
+
(state) =>
|
|
66
18
|
state.vocabularies[vocabulary] && state.vocabularies[vocabulary].items
|
|
67
19
|
? state.vocabularies[vocabulary].items
|
|
68
20
|
: [],
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
21
|
+
shallowEqual,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return items && items.length > 0 ? (
|
|
25
|
+
<div>
|
|
26
|
+
{items.map((item) => (
|
|
27
|
+
<Link
|
|
28
|
+
className="ui label"
|
|
29
|
+
to={`/search?Subject=${item.label}`}
|
|
30
|
+
key={item.label}
|
|
31
|
+
>
|
|
32
|
+
{item.label}
|
|
33
|
+
</Link>
|
|
34
|
+
))}
|
|
35
|
+
</div>
|
|
36
|
+
) : (
|
|
37
|
+
<span />
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default SearchTags;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
2
|
import { useHistory } from 'react-router-dom';
|
|
3
3
|
import { Form, Input } from 'semantic-ui-react';
|
|
4
4
|
import { defineMessages, useIntl } from 'react-intl';
|
|
5
|
+
import { useDispatch, useSelector } from 'react-redux';
|
|
5
6
|
|
|
7
|
+
import { getNavroot } from '@plone/volto/actions';
|
|
8
|
+
import { hasApiExpander, getBaseUrl } from '@plone/volto/helpers';
|
|
6
9
|
import { Icon } from '@plone/volto/components';
|
|
7
10
|
import zoomSVG from '@plone/volto/icons/zoom.svg';
|
|
8
11
|
|
|
@@ -19,6 +22,8 @@ const messages = defineMessages({
|
|
|
19
22
|
|
|
20
23
|
const SearchWidget = (props) => {
|
|
21
24
|
const intl = useIntl();
|
|
25
|
+
const dispatch = useDispatch();
|
|
26
|
+
|
|
22
27
|
const [text, setText] = useState('');
|
|
23
28
|
const history = useHistory();
|
|
24
29
|
const onChangeText = (event, { value }) => {
|
|
@@ -29,14 +34,21 @@ const SearchWidget = (props) => {
|
|
|
29
34
|
const path =
|
|
30
35
|
pathname?.length > 0 ? `&path=${encodeURIComponent(pathname)}` : '';
|
|
31
36
|
|
|
32
|
-
history.push(
|
|
37
|
+
history.push(`./search?SearchableText=${encodeURIComponent(text)}${path}`);
|
|
33
38
|
// reset input value
|
|
34
39
|
setText('');
|
|
35
40
|
event.preventDefault();
|
|
36
41
|
};
|
|
37
42
|
|
|
43
|
+
const navroot = useSelector((state) => state.navroot?.data);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!hasApiExpander('navroot', getBaseUrl(pathname))) {
|
|
46
|
+
dispatch(getNavroot(getBaseUrl(pathname)));
|
|
47
|
+
}
|
|
48
|
+
}, [dispatch, pathname]);
|
|
49
|
+
|
|
38
50
|
return (
|
|
39
|
-
<Form action=
|
|
51
|
+
<Form action={`${navroot?.navroot?.['@id']}/search`} onSubmit={onSubmit}>
|
|
40
52
|
<Form.Field className="searchbox">
|
|
41
53
|
<Input
|
|
42
54
|
aria-label={intl.formatMessage(messages.search)}
|
|
@@ -15,6 +15,14 @@ describe('SearchWidget', () => {
|
|
|
15
15
|
locale: 'en',
|
|
16
16
|
messages: {},
|
|
17
17
|
},
|
|
18
|
+
navroot: {
|
|
19
|
+
data: {
|
|
20
|
+
'@id': 'http://localhost:3000/@navroot',
|
|
21
|
+
navroot: {
|
|
22
|
+
'@id': 'http://localhost:3000',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
18
26
|
});
|
|
19
27
|
const component = renderer.create(
|
|
20
28
|
<Provider store={store}>
|
|
@@ -22,6 +22,13 @@ const messages = defineMessages({
|
|
|
22
22
|
defaultMessage: 'Sitemap',
|
|
23
23
|
},
|
|
24
24
|
});
|
|
25
|
+
|
|
26
|
+
export function getSitemapPath(pathname = '', lang) {
|
|
27
|
+
const prefix = pathname.replace(/\/sitemap$/gm, '').replace(/^\//, '');
|
|
28
|
+
const path = prefix || lang || '';
|
|
29
|
+
return path;
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
/**
|
|
26
33
|
* Sitemap class.
|
|
27
34
|
* @class Sitemap
|
|
@@ -39,11 +46,13 @@ class Sitemap extends Component {
|
|
|
39
46
|
|
|
40
47
|
componentDidMount() {
|
|
41
48
|
const { settings } = config;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
|
|
50
|
+
const lang = settings.isMultilingual
|
|
51
|
+
? `${toBackendLang(this.props.lang)}`
|
|
52
|
+
: null;
|
|
53
|
+
|
|
54
|
+
const path = getSitemapPath(this.props.location.pathname, lang);
|
|
55
|
+
this.props.getNavigation(path, 4);
|
|
47
56
|
}
|
|
48
57
|
|
|
49
58
|
/**
|
|
@@ -105,15 +114,17 @@ export default compose(
|
|
|
105
114
|
{
|
|
106
115
|
key: 'navigation',
|
|
107
116
|
promise: ({ location, store: { dispatch, getState } }) => {
|
|
117
|
+
if (!__SERVER__) return;
|
|
108
118
|
const { settings } = config;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
|
|
120
|
+
const path = getSitemapPath(
|
|
121
|
+
location.pathname,
|
|
122
|
+
settings.isMultilingual
|
|
123
|
+
? toBackendLang(getState().intl.locale)
|
|
124
|
+
: null,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return dispatch(getNavigation(path, 4));
|
|
117
128
|
},
|
|
118
129
|
},
|
|
119
130
|
]),
|
|
@@ -4,7 +4,7 @@ import configureStore from 'redux-mock-store';
|
|
|
4
4
|
import { Provider } from 'react-intl-redux';
|
|
5
5
|
import { MemoryRouter } from 'react-router-dom';
|
|
6
6
|
|
|
7
|
-
import { __test__ as Sitemap } from './Sitemap';
|
|
7
|
+
import { __test__ as Sitemap, getSitemapPath } from './Sitemap';
|
|
8
8
|
|
|
9
9
|
const mockStore = configureStore();
|
|
10
10
|
|
|
@@ -46,7 +46,7 @@ describe('Sitemap', () => {
|
|
|
46
46
|
const component = renderer.create(
|
|
47
47
|
<Provider store={store}>
|
|
48
48
|
<MemoryRouter>
|
|
49
|
-
<Sitemap />
|
|
49
|
+
<Sitemap location={{ pathname: '/page-1' }} />
|
|
50
50
|
</MemoryRouter>
|
|
51
51
|
</Provider>,
|
|
52
52
|
);
|
|
@@ -54,3 +54,24 @@ describe('Sitemap', () => {
|
|
|
54
54
|
expect(json).toMatchSnapshot();
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
|
+
|
|
58
|
+
describe('getSitemapPath', () => {
|
|
59
|
+
it('accepts empty path', () => {
|
|
60
|
+
expect(getSitemapPath('', null)).toBe('');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('accepts a path', () => {
|
|
64
|
+
expect(getSitemapPath('/page-1/sitemap', null)).toBe('page-1');
|
|
65
|
+
});
|
|
66
|
+
it('accepts a path', () => {
|
|
67
|
+
expect(getSitemapPath('/page-1/sitemap', null)).toBe('page-1');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('uses a language as default root', () => {
|
|
71
|
+
expect(getSitemapPath('/', 'de')).toBe('de');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('accepts language-rooted paths', () => {
|
|
75
|
+
expect(getSitemapPath('/de/mission', 'de')).toBe('de/mission');
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -123,6 +123,7 @@ class View extends Component {
|
|
|
123
123
|
if (!hasApiExpander('actions', getBaseUrl(this.props.pathname))) {
|
|
124
124
|
this.props.listActions(getBaseUrl(this.props.pathname));
|
|
125
125
|
}
|
|
126
|
+
|
|
126
127
|
this.props.getContent(
|
|
127
128
|
getBaseUrl(this.props.pathname),
|
|
128
129
|
this.props.versionId,
|
|
@@ -142,6 +143,7 @@ class View extends Component {
|
|
|
142
143
|
if (!hasApiExpander('actions', getBaseUrl(nextProps.pathname))) {
|
|
143
144
|
this.props.listActions(getBaseUrl(nextProps.pathname));
|
|
144
145
|
}
|
|
146
|
+
|
|
145
147
|
this.props.getContent(
|
|
146
148
|
getBaseUrl(nextProps.pathname),
|
|
147
149
|
this.props.versionId,
|
package/src/config/Widgets.jsx
CHANGED
|
@@ -24,6 +24,7 @@ import InternalUrlWidget from '@plone/volto/components/manage/Widgets/InternalUr
|
|
|
24
24
|
import EmailWidget from '@plone/volto/components/manage/Widgets/EmailWidget';
|
|
25
25
|
import NumberWidget from '@plone/volto/components/manage/Widgets/NumberWidget';
|
|
26
26
|
import ImageSizeWidget from '@plone/volto/components/manage/Widgets/ImageSizeWidget';
|
|
27
|
+
import RegistryImageWidget from '@plone/volto/components/manage/Widgets/RegistryImageWidget';
|
|
27
28
|
|
|
28
29
|
import ReferenceWidget from '@plone/volto/components/manage/Widgets/ReferenceWidget';
|
|
29
30
|
import ObjectBrowserWidget from '@plone/volto/components/manage/Widgets/ObjectBrowserWidget';
|
|
@@ -72,6 +73,7 @@ export const widgetMapping = {
|
|
|
72
73
|
recurrence: RecurrenceWidget,
|
|
73
74
|
remoteUrl: UrlWidget,
|
|
74
75
|
id: IdWidget,
|
|
76
|
+
site_logo: RegistryImageWidget,
|
|
75
77
|
},
|
|
76
78
|
widget: {
|
|
77
79
|
richtext: WysiwygWidget,
|
package/src/config/index.js
CHANGED
|
@@ -37,6 +37,8 @@ import applyAddonConfiguration, { addonsInfo } from 'load-volto-addons';
|
|
|
37
37
|
|
|
38
38
|
import ConfigRegistry from '@plone/volto/registry';
|
|
39
39
|
|
|
40
|
+
import { getSiteAsyncPropExtender } from '@plone/volto/helpers';
|
|
41
|
+
|
|
40
42
|
const host = process.env.HOST || 'localhost';
|
|
41
43
|
const port = process.env.PORT || '3000';
|
|
42
44
|
|
|
@@ -83,7 +85,13 @@ let config = {
|
|
|
83
85
|
// https://6.docs.plone.org/volto/configuration/settings-reference.html#term-apiExpanders
|
|
84
86
|
// {
|
|
85
87
|
// match: '',
|
|
86
|
-
// GET_CONTENT: [
|
|
88
|
+
// GET_CONTENT: [
|
|
89
|
+
// 'breadcrumbs',
|
|
90
|
+
// 'navigation',
|
|
91
|
+
// 'actions',
|
|
92
|
+
// 'types',
|
|
93
|
+
// 'navroot',
|
|
94
|
+
// ],
|
|
87
95
|
// },
|
|
88
96
|
],
|
|
89
97
|
// Internal proxy to bypass CORS *while developing*. NOT intended for production use.
|
|
@@ -128,7 +136,7 @@ let config = {
|
|
|
128
136
|
useEmailAsLogin: false,
|
|
129
137
|
persistentReducers: ['blocksClipboard'],
|
|
130
138
|
initialReducersBlacklist: [], // reducers in this list won't be hydrated in windows.__data
|
|
131
|
-
asyncPropsExtenders: [], // per route asyncConnect customizers
|
|
139
|
+
asyncPropsExtenders: [getSiteAsyncPropExtender], // per route asyncConnect customizers
|
|
132
140
|
contentIcons: contentIcons,
|
|
133
141
|
loadables,
|
|
134
142
|
lazyBundles: {
|
|
@@ -188,6 +196,10 @@ let config = {
|
|
|
188
196
|
blockSettingsTabFieldsetsInitialStateOpen: true,
|
|
189
197
|
excludeLinksAndReferencesMenuItem: false,
|
|
190
198
|
containerBlockTypes: ['gridBlock'],
|
|
199
|
+
siteTitleFormat: {
|
|
200
|
+
includeSiteTitle: false,
|
|
201
|
+
titleAndSiteTitleSeparator: '-',
|
|
202
|
+
},
|
|
191
203
|
},
|
|
192
204
|
experimental: {
|
|
193
205
|
addBlockButton: {
|
|
@@ -223,7 +235,7 @@ config.settings.apiExpanders = [
|
|
|
223
235
|
...config.settings.apiExpanders,
|
|
224
236
|
{
|
|
225
237
|
match: '',
|
|
226
|
-
GET_CONTENT: ['breadcrumbs', 'actions', 'types'],
|
|
238
|
+
GET_CONTENT: ['breadcrumbs', 'actions', 'types', 'navroot'],
|
|
227
239
|
},
|
|
228
240
|
{
|
|
229
241
|
match: '',
|
|
@@ -39,6 +39,7 @@ export const GET_QUERYSTRING_RESULTS = 'GET_QUERYSTRING_RESULTS';
|
|
|
39
39
|
export const CREATE_RELATIONS = 'CREATE_RELATIONS';
|
|
40
40
|
export const DELETE_RELATIONS = 'DELETE_RELATIONS';
|
|
41
41
|
export const LIST_RELATIONS = 'LIST_RELATIONS';
|
|
42
|
+
export const STATS_RELATIONS = 'STATS_RELATIONS';
|
|
42
43
|
export const REBUILD_RELATIONS = 'REBUILD_RELATIONS';
|
|
43
44
|
export const GET_SCHEMA = 'GET_SCHEMA';
|
|
44
45
|
export const POST_SCHEMA = 'POST_SCHEMA';
|
|
@@ -137,3 +138,6 @@ export const REMOVE_ALIASES = 'REMOVE_ALIASES';
|
|
|
137
138
|
export const GET_USERSCHEMA = 'GET_USERSCHEMA';
|
|
138
139
|
export const GET_UPGRADE = 'GET_UPGRADE';
|
|
139
140
|
export const POST_UPGRADE = 'POST_UPGRADE';
|
|
141
|
+
export const RESET_LOGIN_REQUEST = 'RESET_LOGIN_REQUEST';
|
|
142
|
+
export const GET_SITE = 'GET_SITE';
|
|
143
|
+
export const GET_NAVROOT = 'GET_NAVROOT';
|
|
@@ -28,6 +28,7 @@ export default function imagesMiddleware() {
|
|
|
28
28
|
|
|
29
29
|
middleware.all(['**/@@images/*'], imageMiddlewareFn);
|
|
30
30
|
middleware.all(['/@portrait/*'], imageMiddlewareFn);
|
|
31
|
+
middleware.all(['/@@site-logo/*'], imageMiddlewareFn);
|
|
31
32
|
middleware.id = 'imageResourcesProcessor';
|
|
32
33
|
return middleware;
|
|
33
34
|
}
|
|
@@ -172,6 +172,28 @@ export const messages = defineMessages({
|
|
|
172
172
|
id: 'Roles',
|
|
173
173
|
defaultMessage: 'Roles',
|
|
174
174
|
},
|
|
175
|
+
addUserFormPasswordAndSendPasswordTogetherNotAllowed: {
|
|
176
|
+
id:
|
|
177
|
+
'It is not allowed to define both the password and to request sending the password reset message by e-mail. You need to select one of them.',
|
|
178
|
+
defaultMessage:
|
|
179
|
+
'It is not allowed to define both the password and to request sending the password reset message by e-mail. You need to select one of them.',
|
|
180
|
+
},
|
|
181
|
+
userSearchNoResults: {
|
|
182
|
+
id: 'There are no users with the searched criteria',
|
|
183
|
+
defaultMessage: 'There are no users with the searched criteria',
|
|
184
|
+
},
|
|
185
|
+
groupSearchNoResults: {
|
|
186
|
+
id: 'There are no groups with the searched criteria',
|
|
187
|
+
defaultMessage: 'There are no groups with the searched criteria',
|
|
188
|
+
},
|
|
189
|
+
updateUserFormTitle: {
|
|
190
|
+
id: 'Update User',
|
|
191
|
+
defaultMessage: 'Update User',
|
|
192
|
+
},
|
|
193
|
+
updateUserSuccess: {
|
|
194
|
+
id: 'User updated successfuly',
|
|
195
|
+
defaultMessage: 'User updated successfuly',
|
|
196
|
+
},
|
|
175
197
|
updateRoles: {
|
|
176
198
|
id: 'User roles updated',
|
|
177
199
|
defaultMessage: 'User roles updated',
|
|
@@ -234,19 +256,19 @@ export const messages = defineMessages({
|
|
|
234
256
|
},
|
|
235
257
|
copyBlocks: {
|
|
236
258
|
id: 'Copy blocks',
|
|
237
|
-
|
|
259
|
+
defaultMessage: 'Copy blocks',
|
|
238
260
|
},
|
|
239
261
|
cutBlocks: {
|
|
240
262
|
id: 'Cut blocks',
|
|
241
|
-
|
|
263
|
+
defaultMessage: 'Cut blocks',
|
|
242
264
|
},
|
|
243
265
|
pasteBlocks: {
|
|
244
266
|
id: 'Paste blocks',
|
|
245
|
-
|
|
267
|
+
defaultMessage: 'Paste blocks',
|
|
246
268
|
},
|
|
247
269
|
deleteBlocks: {
|
|
248
270
|
id: 'Delete blocks',
|
|
249
|
-
|
|
271
|
+
defaultMessage: 'Delete blocks',
|
|
250
272
|
},
|
|
251
273
|
showAllUserButton: {
|
|
252
274
|
id: 'Show All',
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { GET_SITE } from '@plone/volto/constants/ActionTypes';
|
|
2
|
+
import { getSite } from '@plone/volto/actions';
|
|
3
|
+
|
|
4
|
+
const getSiteAsyncPropExtender = {
|
|
5
|
+
path: '/',
|
|
6
|
+
extend: (dispatchActions) => {
|
|
7
|
+
if (
|
|
8
|
+
dispatchActions.filter((asyncAction) => asyncAction.key === GET_SITE)
|
|
9
|
+
.length === 0
|
|
10
|
+
) {
|
|
11
|
+
dispatchActions.push({
|
|
12
|
+
key: GET_SITE,
|
|
13
|
+
promise: ({ location, store: { dispatch } }) =>
|
|
14
|
+
__SERVER__ && dispatch(getSite()),
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return dispatchActions;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export { getSiteAsyncPropExtender };
|
package/src/helpers/index.js
CHANGED
package/src/reducers/index.js
CHANGED
|
@@ -50,6 +50,8 @@ import workingCopy from './workingcopy/workingcopy';
|
|
|
50
50
|
import transactions from './transactions/transactions';
|
|
51
51
|
import upgrade from './upgrade/upgrade';
|
|
52
52
|
import userschema from './userschema/userschema';
|
|
53
|
+
import site from './site/site';
|
|
54
|
+
import navroot from './navroot/navroot';
|
|
53
55
|
|
|
54
56
|
/**
|
|
55
57
|
* Root reducer.
|
|
@@ -105,6 +107,8 @@ const reducers = {
|
|
|
105
107
|
workingCopy,
|
|
106
108
|
transactions,
|
|
107
109
|
userschema,
|
|
110
|
+
site,
|
|
111
|
+
navroot,
|
|
108
112
|
};
|
|
109
113
|
|
|
110
114
|
export default reducers;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navroot reducer.
|
|
3
|
+
* @module reducers/navroot/navroot
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
flattenToAppURL,
|
|
7
|
+
getBaseUrl,
|
|
8
|
+
hasApiExpander,
|
|
9
|
+
} from '@plone/volto/helpers';
|
|
10
|
+
|
|
11
|
+
import { GET_NAVROOT, GET_CONTENT } from '@plone/volto/constants/ActionTypes';
|
|
12
|
+
|
|
13
|
+
const initialState = {
|
|
14
|
+
error: null,
|
|
15
|
+
loaded: false,
|
|
16
|
+
loading: false,
|
|
17
|
+
data: {},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* navroot reducer.
|
|
22
|
+
* @function navroot
|
|
23
|
+
* @param {Object} state Current state.
|
|
24
|
+
* @param {Object} action Action to be handled.
|
|
25
|
+
* @returns {Object} New state.
|
|
26
|
+
*/
|
|
27
|
+
export default function navroot(state = initialState, action = {}) {
|
|
28
|
+
let hasExpander;
|
|
29
|
+
switch (action.type) {
|
|
30
|
+
case `${GET_NAVROOT}_PENDING`:
|
|
31
|
+
return {
|
|
32
|
+
...state,
|
|
33
|
+
error: null,
|
|
34
|
+
loaded: false,
|
|
35
|
+
loading: true,
|
|
36
|
+
data: {},
|
|
37
|
+
};
|
|
38
|
+
case `${GET_CONTENT}_SUCCESS`:
|
|
39
|
+
hasExpander = hasApiExpander(
|
|
40
|
+
'navroot',
|
|
41
|
+
getBaseUrl(flattenToAppURL(action.result['@id'])),
|
|
42
|
+
);
|
|
43
|
+
if (hasExpander && !action.subrequest) {
|
|
44
|
+
return {
|
|
45
|
+
...state,
|
|
46
|
+
error: null,
|
|
47
|
+
data: action.result['@components'].navroot,
|
|
48
|
+
loaded: true,
|
|
49
|
+
loading: false,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return state;
|
|
53
|
+
case `${GET_NAVROOT}_SUCCESS`:
|
|
54
|
+
hasExpander = hasApiExpander(
|
|
55
|
+
'navroot',
|
|
56
|
+
getBaseUrl(flattenToAppURL(action.result['@id'])),
|
|
57
|
+
);
|
|
58
|
+
if (!hasExpander) {
|
|
59
|
+
return {
|
|
60
|
+
...state,
|
|
61
|
+
error: null,
|
|
62
|
+
loaded: true,
|
|
63
|
+
loading: false,
|
|
64
|
+
data: action.result,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return state;
|
|
68
|
+
case `${GET_NAVROOT}_FAIL`:
|
|
69
|
+
return {
|
|
70
|
+
...state,
|
|
71
|
+
error: action.result,
|
|
72
|
+
loaded: false,
|
|
73
|
+
loading: false,
|
|
74
|
+
data: {},
|
|
75
|
+
};
|
|
76
|
+
default:
|
|
77
|
+
return state;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { GET_CONTENT, GET_NAVROOT } from '@plone/volto/constants/ActionTypes';
|
|
2
|
+
import config from '@plone/volto/registry';
|
|
3
|
+
import navroot from './navroot';
|
|
4
|
+
|
|
5
|
+
const { settings } = config;
|
|
6
|
+
|
|
7
|
+
describe('Navroot reducer', () => {
|
|
8
|
+
it('should return the initial state', () => {
|
|
9
|
+
expect(navroot()).toEqual({
|
|
10
|
+
error: null,
|
|
11
|
+
data: {},
|
|
12
|
+
loaded: false,
|
|
13
|
+
loading: false,
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should handle GET_NAVROOT_PENDING', () => {
|
|
18
|
+
expect(
|
|
19
|
+
navroot(undefined, {
|
|
20
|
+
type: `${GET_NAVROOT}_PENDING`,
|
|
21
|
+
}),
|
|
22
|
+
).toEqual({
|
|
23
|
+
error: null,
|
|
24
|
+
data: {},
|
|
25
|
+
loaded: false,
|
|
26
|
+
loading: true,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should handle GET_NAVROOT_SUCCESS', () => {
|
|
31
|
+
expect(
|
|
32
|
+
navroot(undefined, {
|
|
33
|
+
type: `${GET_NAVROOT}_SUCCESS`,
|
|
34
|
+
result: {
|
|
35
|
+
title: 'Welcome to Plone!',
|
|
36
|
+
description:
|
|
37
|
+
'Congratulations! You have successfully installed Plone.',
|
|
38
|
+
'@id': `${settings.apiPath}/front-page`,
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
).toEqual({
|
|
42
|
+
error: null,
|
|
43
|
+
data: {
|
|
44
|
+
'@id': `${settings.apiPath}/front-page`,
|
|
45
|
+
title: 'Welcome to Plone!',
|
|
46
|
+
description: 'Congratulations! You have successfully installed Plone.',
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
loaded: true,
|
|
50
|
+
loading: false,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should handle GET_NAVROOT_FAIL', () => {
|
|
55
|
+
expect(
|
|
56
|
+
navroot(undefined, {
|
|
57
|
+
type: `${GET_NAVROOT}_FAIL`,
|
|
58
|
+
error: 'failed',
|
|
59
|
+
}),
|
|
60
|
+
).toEqual({
|
|
61
|
+
error: undefined,
|
|
62
|
+
data: {},
|
|
63
|
+
loaded: false,
|
|
64
|
+
loading: false,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('Navroot reducer (NAVROOT)GET_CONTENT', () => {
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
config.settings.apiExpanders = [
|
|
72
|
+
{
|
|
73
|
+
match: '',
|
|
74
|
+
GET_CONTENT: ['navroot'],
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should handle (NAVROOT)GET_CONTENT_SUCCESS', () => {
|
|
80
|
+
expect(
|
|
81
|
+
navroot(undefined, {
|
|
82
|
+
type: `${GET_CONTENT}_SUCCESS`,
|
|
83
|
+
result: {
|
|
84
|
+
'@components': {
|
|
85
|
+
navroot: {
|
|
86
|
+
navroot: {
|
|
87
|
+
title: 'Welcome to Plone!',
|
|
88
|
+
description:
|
|
89
|
+
'Congratulations! You have successfully installed Plone.',
|
|
90
|
+
'@id': `${settings.apiPath}/front-page`,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
).toEqual({
|
|
97
|
+
error: null,
|
|
98
|
+
data: {
|
|
99
|
+
navroot: {
|
|
100
|
+
'@id': `${settings.apiPath}/front-page`,
|
|
101
|
+
title: 'Welcome to Plone!',
|
|
102
|
+
description:
|
|
103
|
+
'Congratulations! You have successfully installed Plone.',
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
loaded: true,
|
|
107
|
+
loading: false,
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|