@plone/volto 16.22.0 → 16.22.2
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.draft +12 -22
- package/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +50 -1
- package/CONTRIBUTING.md +5 -1
- package/locales/ca/LC_MESSAGES/volto.po +37 -2
- package/locales/ca.json +1 -1
- package/locales/de/LC_MESSAGES/volto.po +38 -3
- package/locales/de.json +1 -1
- package/locales/en/LC_MESSAGES/volto.po +37 -2
- package/locales/en.json +1 -1
- package/locales/es/LC_MESSAGES/volto.po +37 -2
- package/locales/es.json +1 -1
- package/locales/eu/LC_MESSAGES/volto.po +37 -2
- package/locales/eu.json +1 -1
- package/locales/fi/LC_MESSAGES/volto.po +37 -2
- package/locales/fi.json +1 -1
- package/locales/fr/LC_MESSAGES/volto.po +37 -2
- package/locales/fr.json +1 -1
- package/locales/it/LC_MESSAGES/volto.po +228 -193
- package/locales/it.json +1 -1
- package/locales/ja/LC_MESSAGES/volto.po +37 -2
- package/locales/ja.json +1 -1
- package/locales/nl/LC_MESSAGES/volto.po +37 -2
- package/locales/nl.json +1 -1
- package/locales/pt/LC_MESSAGES/volto.po +37 -2
- package/locales/pt.json +1 -1
- package/locales/pt_BR/LC_MESSAGES/volto.po +37 -2
- package/locales/pt_BR.json +1 -1
- package/locales/ro/LC_MESSAGES/volto.po +37 -2
- package/locales/ro.json +1 -1
- package/locales/volto.pot +38 -3
- package/locales/zh_CN/LC_MESSAGES/volto.po +37 -2
- package/locales/zh_CN.json +1 -1
- package/package.json +1 -1
- package/packages/volto-slate/package.json +1 -1
- 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/components/SearchInput.jsx +9 -2
- package/src/components/manage/Blocks/Search/hocs/withSearch.jsx +12 -1
- package/src/components/manage/Blocks/ToC/Schema.jsx +36 -7
- package/src/components/manage/Contents/Contents.jsx +27 -0
- package/src/components/manage/Contents/ContentsPropertiesModal.jsx +1 -13
- package/src/components/manage/Controlpanels/Groups/RenderGroups.jsx +2 -2
- package/src/components/manage/Controlpanels/Users/RenderUsers.jsx +2 -2
- package/src/components/manage/Controlpanels/Users/UsersControlpanel.jsx +25 -10
- package/src/components/manage/Diff/DiffField.jsx +25 -1
- package/src/components/manage/Sharing/Sharing.jsx +11 -5
- package/src/components/manage/Widgets/FormFieldWrapper.jsx +1 -1
- package/src/components/manage/Widgets/SelectUtils.js +1 -1
- package/src/components/theme/Error/ServerError.jsx +29 -0
- package/src/components/theme/Sitemap/Sitemap.jsx +24 -13
- package/src/components/theme/Sitemap/Sitemap.test.jsx +23 -2
- package/src/config/Views.jsx +2 -0
- package/src/express-middleware/files.js +8 -6
- package/src/express-middleware/images.js +7 -1
- package/src/middleware/api.js +14 -2
- package/src/reducers/actions/actions.js +7 -5
- package/src/reducers/actions/actions.test.js +70 -0
- package/src/server.jsx +9 -0
- package/styles/Vocab/Plone/accept.txt +9 -2
- package/styles/Vocab/Plone/reject.txt +2 -5
- package/theme/themes/pastanaga/collections/form.overrides +46 -0
- package/theme/themes/pastanaga/elements/input.overrides +6 -0
- package/theme/themes/pastanaga/elements/label.overrides +10 -0
|
@@ -57,7 +57,7 @@ class RenderGroups extends Component {
|
|
|
57
57
|
* @memberof UsersControlpanelUser
|
|
58
58
|
*/
|
|
59
59
|
onChange(event, { value }) {
|
|
60
|
-
const [group, role] = value.split('
|
|
60
|
+
const [group, role] = value.split('&role=');
|
|
61
61
|
this.props.updateGroups(group, role);
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -97,7 +97,7 @@ class RenderGroups extends Component {
|
|
|
97
97
|
: this.props.group.roles.includes(role.id)
|
|
98
98
|
}
|
|
99
99
|
onChange={this.onChange}
|
|
100
|
-
value={`${this.props.group.id}
|
|
100
|
+
value={`${this.props.group.id}&role=${role.id}`}
|
|
101
101
|
/>
|
|
102
102
|
)}
|
|
103
103
|
</Table.Cell>
|
|
@@ -53,7 +53,7 @@ class RenderUsers extends Component {
|
|
|
53
53
|
*/
|
|
54
54
|
|
|
55
55
|
onChange(event, { value }) {
|
|
56
|
-
const [user, role] = value.split('
|
|
56
|
+
const [user, role] = value.split('&role=');
|
|
57
57
|
this.props.updateUser(user, role);
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
@@ -83,7 +83,7 @@ class RenderUsers extends Component {
|
|
|
83
83
|
<Checkbox
|
|
84
84
|
checked={this.props.user.roles.includes(role.id)}
|
|
85
85
|
onChange={this.onChange}
|
|
86
|
-
value={`${this.props.user.id}
|
|
86
|
+
value={`${this.props.user.id}&role=${role.id}`}
|
|
87
87
|
/>
|
|
88
88
|
)}
|
|
89
89
|
</Table.Cell>
|
|
@@ -107,6 +107,7 @@ class UsersControlpanel extends Component {
|
|
|
107
107
|
isClient: false,
|
|
108
108
|
currentPage: 0,
|
|
109
109
|
pageSize: 10,
|
|
110
|
+
loginUsingEmail: false,
|
|
110
111
|
};
|
|
111
112
|
}
|
|
112
113
|
|
|
@@ -122,6 +123,14 @@ class UsersControlpanel extends Component {
|
|
|
122
123
|
}
|
|
123
124
|
};
|
|
124
125
|
|
|
126
|
+
// Because username field needs to be disabled if email login is enabled!
|
|
127
|
+
checkLoginUsingEmailStatus = async () => {
|
|
128
|
+
await this.props.getControlpanel('security');
|
|
129
|
+
this.setState({
|
|
130
|
+
loginUsingEmail: this.props.controlPanelData?.data.use_email_as_login,
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
|
|
125
134
|
/**
|
|
126
135
|
* Component did mount
|
|
127
136
|
* @method componentDidMount
|
|
@@ -132,6 +141,7 @@ class UsersControlpanel extends Component {
|
|
|
132
141
|
isClient: true,
|
|
133
142
|
});
|
|
134
143
|
this.fetchData();
|
|
144
|
+
this.checkLoginUsingEmailStatus();
|
|
135
145
|
}
|
|
136
146
|
|
|
137
147
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
@@ -425,7 +435,7 @@ class UsersControlpanel extends Component {
|
|
|
425
435
|
id: 'default',
|
|
426
436
|
title: 'FIXME: User Data',
|
|
427
437
|
fields: [
|
|
428
|
-
'username',
|
|
438
|
+
...(!this.state.loginUsingEmail ? ['username'] : []),
|
|
429
439
|
'fullname',
|
|
430
440
|
'email',
|
|
431
441
|
'password',
|
|
@@ -436,15 +446,19 @@ class UsersControlpanel extends Component {
|
|
|
436
446
|
},
|
|
437
447
|
],
|
|
438
448
|
properties: {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
449
|
+
...(!this.state.loginUsingEmail
|
|
450
|
+
? {
|
|
451
|
+
username: {
|
|
452
|
+
title: this.props.intl.formatMessage(
|
|
453
|
+
messages.addUserFormUsernameTitle,
|
|
454
|
+
),
|
|
455
|
+
type: 'string',
|
|
456
|
+
description: this.props.intl.formatMessage(
|
|
457
|
+
messages.addUserFormUsernameDescription,
|
|
458
|
+
),
|
|
459
|
+
},
|
|
460
|
+
}
|
|
461
|
+
: {}),
|
|
448
462
|
fullname: {
|
|
449
463
|
title: this.props.intl.formatMessage(
|
|
450
464
|
messages.addUserFormFullnameTitle,
|
|
@@ -670,6 +684,7 @@ export default compose(
|
|
|
670
684
|
createRequest: state.users.create,
|
|
671
685
|
loadRolesRequest: state.roles,
|
|
672
686
|
inheritedRole: state.authRole.authenticatedRole,
|
|
687
|
+
controlPanelData: state.controlpanels?.controlpanel,
|
|
673
688
|
}),
|
|
674
689
|
(dispatch) =>
|
|
675
690
|
bindActionCreators(
|
|
@@ -17,6 +17,7 @@ import { useSelector } from 'react-redux';
|
|
|
17
17
|
import { Api } from '@plone/volto/helpers';
|
|
18
18
|
import configureStore from '@plone/volto/store';
|
|
19
19
|
import { DefaultView } from '@plone/volto/components/';
|
|
20
|
+
import { serializeNodes } from '@plone/volto-slate/editor/render';
|
|
20
21
|
|
|
21
22
|
import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
|
|
22
23
|
|
|
@@ -69,7 +70,7 @@ const DiffField = ({
|
|
|
69
70
|
.replace('\u202F', ' '),
|
|
70
71
|
);
|
|
71
72
|
break;
|
|
72
|
-
case 'json':
|
|
73
|
+
case 'json': {
|
|
73
74
|
const api = new Api();
|
|
74
75
|
const history = createBrowserHistory();
|
|
75
76
|
const store = configureStore(window.__data, history, api);
|
|
@@ -90,6 +91,29 @@ const DiffField = ({
|
|
|
90
91
|
),
|
|
91
92
|
);
|
|
92
93
|
break;
|
|
94
|
+
}
|
|
95
|
+
case 'slate': {
|
|
96
|
+
const api = new Api();
|
|
97
|
+
const history = createBrowserHistory();
|
|
98
|
+
const store = configureStore(window.__data, history, api);
|
|
99
|
+
parts = diffWords(
|
|
100
|
+
ReactDOMServer.renderToStaticMarkup(
|
|
101
|
+
<Provider store={store}>
|
|
102
|
+
<ConnectedRouter history={history}>
|
|
103
|
+
{serializeNodes(one)}
|
|
104
|
+
</ConnectedRouter>
|
|
105
|
+
</Provider>,
|
|
106
|
+
),
|
|
107
|
+
ReactDOMServer.renderToStaticMarkup(
|
|
108
|
+
<Provider store={store}>
|
|
109
|
+
<ConnectedRouter history={history}>
|
|
110
|
+
{serializeNodes(two)}
|
|
111
|
+
</ConnectedRouter>
|
|
112
|
+
</Provider>,
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
93
117
|
case 'textarea':
|
|
94
118
|
default:
|
|
95
119
|
parts = diffWords(one, two);
|
|
@@ -246,9 +246,9 @@ class SharingComponent extends Component {
|
|
|
246
246
|
* @returns {undefined}
|
|
247
247
|
*/
|
|
248
248
|
onToggleInherit() {
|
|
249
|
-
this.setState({
|
|
250
|
-
inherit: !
|
|
251
|
-
});
|
|
249
|
+
this.setState((state) => ({
|
|
250
|
+
inherit: !state.inherit,
|
|
251
|
+
}));
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
/**
|
|
@@ -404,9 +404,15 @@ class SharingComponent extends Component {
|
|
|
404
404
|
<Segment attached>
|
|
405
405
|
<Form.Field>
|
|
406
406
|
<Checkbox
|
|
407
|
-
|
|
407
|
+
id="inherit-permissions-checkbox"
|
|
408
|
+
name="inherit-permissions-checkbox"
|
|
409
|
+
defaultChecked={this.state.inherit}
|
|
408
410
|
onChange={this.onToggleInherit}
|
|
409
|
-
label={
|
|
411
|
+
label={
|
|
412
|
+
<label htmlFor="inherit-permissions-checkbox">
|
|
413
|
+
{this.props.intl.formatMessage(messages.inherit)}
|
|
414
|
+
</label>
|
|
415
|
+
}
|
|
410
416
|
/>
|
|
411
417
|
</Form.Field>
|
|
412
418
|
<p className="help">
|
|
@@ -96,7 +96,7 @@ class FormFieldWrapper extends Component {
|
|
|
96
96
|
{this.props.children}
|
|
97
97
|
|
|
98
98
|
{map(error, (message) => (
|
|
99
|
-
<Label key={message} basic color="red"
|
|
99
|
+
<Label key={message} basic color="red" className="form-error-label">
|
|
100
100
|
{message}
|
|
101
101
|
</Label>
|
|
102
102
|
))}
|
|
@@ -111,7 +111,7 @@ export function normalizeValue(choices, value, intl) {
|
|
|
111
111
|
|
|
112
112
|
if (Array.isArray(value)) {
|
|
113
113
|
// a list of values, like ['foo', 'bar'];
|
|
114
|
-
return value.map((v) => normalizeValue(choices, v));
|
|
114
|
+
return value.map((v) => normalizeValue(choices, v, intl));
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
if (isObject(value)) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module components/theme/Error/ServerError
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { FormattedMessage } from 'react-intl';
|
|
7
|
+
import { Container } from 'semantic-ui-react';
|
|
8
|
+
import { withServerErrorCode } from '@plone/volto/helpers/Utils/Utils';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* server error
|
|
12
|
+
* @function ServerError
|
|
13
|
+
* @returns {string} Markup of the server error page.
|
|
14
|
+
*/
|
|
15
|
+
const ServerError = () => (
|
|
16
|
+
<Container className="view-wrapper">
|
|
17
|
+
<h1>
|
|
18
|
+
<FormattedMessage id="Server Error" defaultMessage="Server Error" />
|
|
19
|
+
</h1>
|
|
20
|
+
<p className="description">
|
|
21
|
+
<FormattedMessage
|
|
22
|
+
id="We apologize for the inconvenience, but there was an unexpected error on the server."
|
|
23
|
+
defaultMessage="We apologize for the inconvenience, but there was an unexpected error on the server."
|
|
24
|
+
/>
|
|
25
|
+
</p>
|
|
26
|
+
</Container>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export default withServerErrorCode(500)(ServerError);
|
|
@@ -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
|
+
});
|
package/src/config/Views.jsx
CHANGED
|
@@ -16,6 +16,7 @@ import RequestTimeout from '@plone/volto/components/theme/RequestTimeout/Request
|
|
|
16
16
|
import AlbumView from '@plone/volto/components/theme/View/AlbumView';
|
|
17
17
|
import Unauthorized from '@plone/volto/components/theme/Unauthorized/Unauthorized';
|
|
18
18
|
import Forbidden from '@plone/volto/components/theme/Forbidden/Forbidden';
|
|
19
|
+
import ServerError from '@plone/volto/components/theme/Error/ServerError';
|
|
19
20
|
|
|
20
21
|
const EventView = loadable(() =>
|
|
21
22
|
import('@plone/volto/components/theme/View/EventView'),
|
|
@@ -114,6 +115,7 @@ export const errorViews = {
|
|
|
114
115
|
'401': Unauthorized,
|
|
115
116
|
'403': Forbidden,
|
|
116
117
|
'408': RequestTimeout,
|
|
118
|
+
'500': ServerError,
|
|
117
119
|
ECONNREFUSED: ConnectionRefused,
|
|
118
120
|
corsError: CorsError,
|
|
119
121
|
};
|
|
@@ -2,11 +2,13 @@ import express from 'express';
|
|
|
2
2
|
import { getAPIResourceWithAuth } from '@plone/volto/helpers';
|
|
3
3
|
|
|
4
4
|
const HEADERS = [
|
|
5
|
-
'
|
|
6
|
-
'
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'
|
|
5
|
+
'accept-ranges',
|
|
6
|
+
'cache-control',
|
|
7
|
+
'content-disposition',
|
|
8
|
+
'content-range',
|
|
9
|
+
'content-type',
|
|
10
|
+
'x-sendfile',
|
|
11
|
+
'x-accel-redirect',
|
|
10
12
|
];
|
|
11
13
|
|
|
12
14
|
function filesMiddlewareFn(req, res, next) {
|
|
@@ -14,7 +16,7 @@ function filesMiddlewareFn(req, res, next) {
|
|
|
14
16
|
.then((resource) => {
|
|
15
17
|
// Just forward the headers that we need
|
|
16
18
|
HEADERS.forEach((header) => {
|
|
17
|
-
if (resource.
|
|
19
|
+
if (resource.headers[header]) {
|
|
18
20
|
res.set(header, resource.get(header));
|
|
19
21
|
}
|
|
20
22
|
});
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
2
|
import { getAPIResourceWithAuth } from '@plone/volto/helpers';
|
|
3
3
|
|
|
4
|
-
const HEADERS = [
|
|
4
|
+
const HEADERS = [
|
|
5
|
+
'content-type',
|
|
6
|
+
'content-disposition',
|
|
7
|
+
'cache-control',
|
|
8
|
+
'x-sendfile',
|
|
9
|
+
'x-accel-redirect',
|
|
10
|
+
];
|
|
5
11
|
|
|
6
12
|
function imageMiddlewareFn(req, res, next) {
|
|
7
13
|
getAPIResourceWithAuth(req)
|
package/src/middleware/api.js
CHANGED
|
@@ -250,10 +250,22 @@ const apiMiddlewareFactory = (api) => ({ dispatch, getState }) => (next) => (
|
|
|
250
250
|
};
|
|
251
251
|
});
|
|
252
252
|
}
|
|
253
|
-
|
|
253
|
+
try {
|
|
254
|
+
return next({ ...rest, result, type: `${type}_SUCCESS` });
|
|
255
|
+
} catch (error) {
|
|
256
|
+
// There was an exception while processing reducers or downstream middleware.
|
|
257
|
+
next({
|
|
258
|
+
...rest,
|
|
259
|
+
error: { status: 500, error },
|
|
260
|
+
type: `${type}_FAIL`,
|
|
261
|
+
});
|
|
262
|
+
// Rethrow the original exception on the client side only,
|
|
263
|
+
// so it doesn't fall through to express on the server.
|
|
264
|
+
if (__CLIENT__) throw error;
|
|
265
|
+
}
|
|
254
266
|
},
|
|
255
267
|
(error) => {
|
|
256
|
-
// Only
|
|
268
|
+
// Only SSR can set ECONNREFUSED
|
|
257
269
|
if (error.code === 'ECONNREFUSED') {
|
|
258
270
|
next({
|
|
259
271
|
...rest,
|
|
@@ -57,11 +57,13 @@ export default function actions(state = initialState, action = {}) {
|
|
|
57
57
|
}
|
|
58
58
|
return state;
|
|
59
59
|
case `${LIST_ACTIONS}_SUCCESS`:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
)
|
|
64
|
-
|
|
60
|
+
// Even if the expander is set or not, if the LIST_ACTIONS is
|
|
61
|
+
// called, we want it to store the data if the actions data is
|
|
62
|
+
// not set in the expander data (['@components']) but in the "normal"
|
|
63
|
+
// action result (we look for the object property returned by the endpoint)
|
|
64
|
+
// Unfortunately, this endpoint returns all the actions in a plain object
|
|
65
|
+
// with no structure :(
|
|
66
|
+
if (action.result.object) {
|
|
65
67
|
return {
|
|
66
68
|
...state,
|
|
67
69
|
error: null,
|
|
@@ -207,4 +207,74 @@ describe('Actions reducer - (ACTIONS)GET_CONTENT', () => {
|
|
|
207
207
|
loading: false,
|
|
208
208
|
});
|
|
209
209
|
});
|
|
210
|
+
|
|
211
|
+
it('should handle (ACTIONS)LIST_ACTIONS (standalone with apiExpander enabled)', () => {
|
|
212
|
+
expect(
|
|
213
|
+
actions(undefined, {
|
|
214
|
+
type: `${LIST_ACTIONS}_SUCCESS`,
|
|
215
|
+
result: {
|
|
216
|
+
object: [],
|
|
217
|
+
object_buttons: [],
|
|
218
|
+
site_actions: [],
|
|
219
|
+
user: [
|
|
220
|
+
{
|
|
221
|
+
icon: '',
|
|
222
|
+
id: 'preferences',
|
|
223
|
+
title: 'Preferences',
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
icon: '',
|
|
227
|
+
id: 'dashboard',
|
|
228
|
+
title: 'Dashboard',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
icon: '',
|
|
232
|
+
id: 'plone_setup',
|
|
233
|
+
title: 'Site Setup',
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
icon: '',
|
|
237
|
+
id: 'logout',
|
|
238
|
+
title: 'Log out',
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
document_actions: [],
|
|
242
|
+
portal_tabs: [],
|
|
243
|
+
},
|
|
244
|
+
}),
|
|
245
|
+
).toEqual({
|
|
246
|
+
error: null,
|
|
247
|
+
actions: {
|
|
248
|
+
object: [],
|
|
249
|
+
object_buttons: [],
|
|
250
|
+
site_actions: [],
|
|
251
|
+
user: [
|
|
252
|
+
{
|
|
253
|
+
icon: '',
|
|
254
|
+
id: 'preferences',
|
|
255
|
+
title: 'Preferences',
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
icon: '',
|
|
259
|
+
id: 'dashboard',
|
|
260
|
+
title: 'Dashboard',
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
icon: '',
|
|
264
|
+
id: 'plone_setup',
|
|
265
|
+
title: 'Site Setup',
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
icon: '',
|
|
269
|
+
id: 'logout',
|
|
270
|
+
title: 'Log out',
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
document_actions: [],
|
|
274
|
+
portal_tabs: [],
|
|
275
|
+
},
|
|
276
|
+
loaded: true,
|
|
277
|
+
loading: false,
|
|
278
|
+
});
|
|
279
|
+
});
|
|
210
280
|
});
|
package/src/server.jsx
CHANGED
|
@@ -263,6 +263,15 @@ server.get('/*', (req, res) => {
|
|
|
263
263
|
const readCriticalCss =
|
|
264
264
|
config.settings.serverConfig.readCriticalCss || defaultReadCriticalCss;
|
|
265
265
|
|
|
266
|
+
// If we are showing an "old browser" warning,
|
|
267
|
+
// make sure it doesn't get cached in a shared cache
|
|
268
|
+
const browserdetect = store.getState().browserdetect;
|
|
269
|
+
if (config.settings.notSupportedBrowsers.includes(browserdetect?.name)) {
|
|
270
|
+
res.set({
|
|
271
|
+
'Cache-Control': 'private',
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
266
275
|
if (context.url) {
|
|
267
276
|
res.redirect(flattenToAppURL(context.url));
|
|
268
277
|
} else if (context.error_code) {
|
|
@@ -49,6 +49,52 @@
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
.ui.form .fields.error .field textarea,
|
|
53
|
+
.ui.form .fields.error .field select,
|
|
54
|
+
.ui.form .fields.error .field input:not([type]),
|
|
55
|
+
.ui.form .fields.error .field input[type='date'],
|
|
56
|
+
.ui.form .fields.error .field input[type='datetime-local'],
|
|
57
|
+
.ui.form .fields.error .field input[type='email'],
|
|
58
|
+
.ui.form .fields.error .field input[type='number'],
|
|
59
|
+
.ui.form .fields.error .field input[type='password'],
|
|
60
|
+
.ui.form .fields.error .field input[type='search'],
|
|
61
|
+
.ui.form .fields.error .field input[type='tel'],
|
|
62
|
+
.ui.form .fields.error .field input[type='time'],
|
|
63
|
+
.ui.form .fields.error .field input[type='text'],
|
|
64
|
+
.ui.form .fields.error .field input[type='file'],
|
|
65
|
+
.ui.form .fields.error .field input[type='url'],
|
|
66
|
+
.ui.form .field.error textarea,
|
|
67
|
+
.ui.form .field.error select,
|
|
68
|
+
.ui.form .field.error input:not([type]),
|
|
69
|
+
.ui.form .field.error input[type='date'],
|
|
70
|
+
.ui.form .field.error input[type='datetime-local'],
|
|
71
|
+
.ui.form .field.error input[type='email'],
|
|
72
|
+
.ui.form .field.error input[type='number'],
|
|
73
|
+
.ui.form .field.error input[type='password'],
|
|
74
|
+
.ui.form .field.error input[type='search'],
|
|
75
|
+
.ui.form .field.error input[type='tel'],
|
|
76
|
+
.ui.form .field.error input[type='time'],
|
|
77
|
+
.ui.form .field.error input[type='text'],
|
|
78
|
+
.ui.form .field.error input[type='file'],
|
|
79
|
+
.ui.form .field.error input[type='url'],
|
|
80
|
+
.ui.form .field.error textarea:focus,
|
|
81
|
+
.ui.form .field.error select:focus,
|
|
82
|
+
.ui.form .field.error input:not([type]):focus,
|
|
83
|
+
.ui.form .field.error input[type='date']:focus,
|
|
84
|
+
.ui.form .field.error input[type='datetime-local']:focus,
|
|
85
|
+
.ui.form .field.error input[type='email']:focus,
|
|
86
|
+
.ui.form .field.error input[type='number']:focus,
|
|
87
|
+
.ui.form .field.error input[type='password']:focus,
|
|
88
|
+
.ui.form .field.error input[type='search']:focus,
|
|
89
|
+
.ui.form .field.error input[type='tel']:focus,
|
|
90
|
+
.ui.form .field.error input[type='time']:focus,
|
|
91
|
+
.ui.form .field.error input[type='text']:focus,
|
|
92
|
+
.ui.form .field.error input[type='file']:focus,
|
|
93
|
+
.ui.form .field.error input[type='url']:focus {
|
|
94
|
+
border-color: #d01157;
|
|
95
|
+
background: none;
|
|
96
|
+
}
|
|
97
|
+
|
|
52
98
|
.ui.form .field > .selection.dropdown {
|
|
53
99
|
height: 60px;
|
|
54
100
|
}
|
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
font-weight: @inputFontWeight;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/* This aligns the height of the field name label to the other side in case
|
|
10
|
+
of an error is present, it overrides a default from SemanticUI grid definitions. */
|
|
11
|
+
.ui.grid > .stretched.row > .column > .wrapper {
|
|
12
|
+
flex-grow: 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
9
15
|
.inline.field {
|
|
10
16
|
.wrapper {
|
|
11
17
|
display: flex;
|