@plone/volto 14.9.0 → 15.0.0-alpha.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.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Change Log
2
2
 
3
+ ## 15.0.0-alpha.2 (2022-02-10)
4
+
5
+ ### Breaking
6
+
7
+ - Language Switcher no longer takes care of the change of the language on the Redux Store. This responsability has been unified in the `MultilingualRedirector` @sneridagh
8
+
9
+ ### Bugfix
10
+
11
+ - Prevent the MultilingualRedirector to force 4 content load when switching the language @reebalazs
12
+
13
+ ### Documentation
14
+
15
+ - Upgrade Guide i18n: Make clear what's project, what add-on. @ksuess
16
+
17
+ ## 15.0.0-alpha.1 (2022-02-09)
18
+
19
+ ### Bugfix
20
+
21
+ - Fix the `null` error in SelectAutoComplete Widget @iFlameing
22
+
23
+ ## 15.0.0-alpha.0 (2022-02-09)
24
+
25
+ ### Breaking
26
+
27
+ - Upgrade `react-cookie` to latest version. @sneridagh @robgietema
28
+ See https://docs.voltocms.com/upgrade-guide/ for more information.
29
+
30
+ ## 14.10.0 (2022-02-08)
31
+
32
+ ### Feature
33
+
34
+ - Add Pluggable to toolbar user menu. @ksuess
35
+
3
36
  ## 14.9.0 (2022-02-08)
4
37
 
5
38
  ### Feature
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  }
10
10
  ],
11
11
  "license": "MIT",
12
- "version": "14.9.0",
12
+ "version": "15.0.0-alpha.2",
13
13
  "repository": {
14
14
  "type": "git",
15
15
  "url": "git@github.com:plone/volto.git"
@@ -350,7 +350,7 @@
350
350
  "react-anchor-link-smooth-scroll": "1.0.12",
351
351
  "react-animate-height": "2.0.17",
352
352
  "react-beautiful-dnd": "13.0.0",
353
- "react-cookie": "1.0.5",
353
+ "react-cookie": "4.1.1",
354
354
  "react-dates": "21.5.1",
355
355
  "react-detect-click-outside": "1.1.1",
356
356
  "react-dnd": "5.0.0",
@@ -401,6 +401,7 @@
401
401
  "svgo-loader": "2.2.1",
402
402
  "tlds": "1.203.1",
403
403
  "undoo": "0.5.0",
404
+ "universal-cookie-express": "4.0.3",
404
405
  "use-deep-compare-effect": "1.6.1",
405
406
  "xmlrpc": "1.3.2",
406
407
  "yarn-deduplicate": "3.1.0",
@@ -1,20 +1,22 @@
1
- import cookie from 'react-cookie';
2
1
  import { updateIntl } from 'react-intl-redux';
3
2
  import { normalizeLanguageName } from '@plone/volto/helpers';
3
+ import Cookies from 'universal-cookie';
4
4
 
5
5
  export function changeLanguageCookies(language) {
6
- cookie.save('lang', normalizeLanguageName(language), {
6
+ const cookies = new Cookies();
7
+
8
+ cookies.set('lang', normalizeLanguageName(language), {
7
9
  expires: new Date((2 ** 31 - 1) * 1000),
8
10
  path: '/',
9
11
  });
10
- cookie.save('I18N_LANGUAGE', normalizeLanguageName(language) || '', {
12
+ cookies.set('I18N_LANGUAGE', normalizeLanguageName(language) || '', {
11
13
  expires: new Date((2 ** 31 - 1) * 1000),
12
14
  path: '/',
13
15
  });
14
16
  }
15
17
 
16
18
  /**
17
- * Changes current language using react-intl-redux action and setting the cookie.
19
+ * Changes current language using react-intl-redux action and setting the cookie
18
20
  * @function changeLanguage
19
21
  * @param {string} language target language.
20
22
  * @param {number} locale set of locales corresponding the target language.
@@ -8,7 +8,7 @@ import PropTypes from 'prop-types';
8
8
  import { connect } from 'react-redux';
9
9
  import { compose } from 'redux';
10
10
  import { map, keys } from 'lodash';
11
- import cookie from 'react-cookie';
11
+ import { withCookies } from 'react-cookie';
12
12
  import { defineMessages, injectIntl } from 'react-intl';
13
13
  import { toast } from 'react-toastify';
14
14
 
@@ -112,9 +112,10 @@ class PersonalPreferences extends Component {
112
112
  * @returns {string} Markup for the component.
113
113
  */
114
114
  render() {
115
+ const { cookies } = this.props;
115
116
  return (
116
117
  <Form
117
- formData={{ language: cookie.load('I18N_LANGUAGE') || '' }}
118
+ formData={{ language: cookies.get('I18N_LANGUAGE') || '' }}
118
119
  schema={{
119
120
  fieldsets: [
120
121
  {
@@ -144,5 +145,6 @@ class PersonalPreferences extends Component {
144
145
 
145
146
  export default compose(
146
147
  injectIntl,
148
+ withCookies,
147
149
  connect(null, { changeLanguage }),
148
150
  )(PersonalPreferences);
@@ -8,7 +8,7 @@ import PropTypes from 'prop-types';
8
8
  import { Button, Tab } from 'semantic-ui-react';
9
9
  import { connect } from 'react-redux';
10
10
  import { compose } from 'redux';
11
- import cookie from 'react-cookie';
11
+ import { withCookies } from 'react-cookie';
12
12
  import { defineMessages, injectIntl } from 'react-intl';
13
13
  import cx from 'classnames';
14
14
  import { BodyClass } from '@plone/volto/helpers';
@@ -77,11 +77,12 @@ class Sidebar extends Component {
77
77
  */
78
78
  constructor(props) {
79
79
  super(props);
80
+ const { cookies } = props;
80
81
  this.onToggleExpanded = this.onToggleExpanded.bind(this);
81
82
  this.onToggleFullSize = this.onToggleFullSize.bind(this);
82
83
  this.onTabChange = this.onTabChange.bind(this);
83
84
  this.state = {
84
- expanded: cookie.load('sidebar_expanded') !== 'false',
85
+ expanded: cookies.get('sidebar_expanded') !== 'false',
85
86
  size: 0,
86
87
  showFull: true,
87
88
  showFullToolbarExpanded: true,
@@ -95,7 +96,8 @@ class Sidebar extends Component {
95
96
  * @returns {undefined}
96
97
  */
97
98
  onToggleExpanded() {
98
- cookie.save('sidebar_expanded', !this.state.expanded, {
99
+ const { cookies } = this.props;
100
+ cookies.set('sidebar_expanded', !this.state.expanded, {
99
101
  expires: new Date((2 ** 31 - 1) * 1000),
100
102
  path: '/',
101
103
  });
@@ -269,6 +271,7 @@ class Sidebar extends Component {
269
271
 
270
272
  export default compose(
271
273
  injectIntl,
274
+ withCookies,
272
275
  connect(
273
276
  (state) => ({
274
277
  tab: state.sidebar.tab,
@@ -11,6 +11,7 @@ import cx from 'classnames';
11
11
  import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
12
12
  import { Icon } from '@plone/volto/components';
13
13
  import { getUser } from '@plone/volto/actions';
14
+ import { Pluggable } from '@plone/volto/components/manage/Pluggable';
14
15
  import { userHasRoles } from '@plone/volto/helpers';
15
16
 
16
17
  import logoutSVG from '@plone/volto/icons/log-out.svg';
@@ -172,6 +173,7 @@ class PersonalTools extends Component {
172
173
  </Link>
173
174
  </li>
174
175
  )}
176
+ <Pluggable name="toolbar-user-menu" />
175
177
  </ul>
176
178
  </div>
177
179
  </div>
@@ -4,6 +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
  import jwt from 'jsonwebtoken';
7
+ import { PluggablesProvider } from '@plone/volto/components/manage/Pluggable';
7
8
 
8
9
  import PersonalTools from './PersonalTools';
9
10
 
@@ -35,14 +36,16 @@ describe('Toolbar Personal Tools component', () => {
35
36
  });
36
37
  const component = renderer.create(
37
38
  <Provider store={store}>
38
- <MemoryRouter>
39
- <PersonalTools
40
- loadComponent={() => {}}
41
- theToolbar={{
42
- current: { getBoundingClientRect: () => ({ width: '320' }) },
43
- }}
44
- />
45
- </MemoryRouter>
39
+ <PluggablesProvider>
40
+ <MemoryRouter>
41
+ <PersonalTools
42
+ loadComponent={() => {}}
43
+ theToolbar={{
44
+ current: { getBoundingClientRect: () => ({ width: '320' }) },
45
+ }}
46
+ />
47
+ </MemoryRouter>
48
+ </PluggablesProvider>
46
49
  </Provider>,
47
50
  );
48
51
  const json = component.toJSON();
@@ -11,7 +11,7 @@ import jwtDecode from 'jwt-decode';
11
11
  import { connect } from 'react-redux';
12
12
  import { compose } from 'redux';
13
13
  import { doesNodeContainClick } from 'semantic-ui-react/dist/commonjs/lib';
14
- import cookie from 'react-cookie';
14
+ import { withCookies } from 'react-cookie';
15
15
  import { filter, find } from 'lodash';
16
16
  import cx from 'classnames';
17
17
  import config from '@plone/volto/registry';
@@ -175,17 +175,21 @@ class Toolbar extends Component {
175
175
  types: [],
176
176
  };
177
177
 
178
- state = {
179
- expanded: cookie.load('toolbar_expanded') !== 'false',
180
- showMenu: false,
181
- menuStyle: {},
182
- menuComponents: [],
183
- loadedComponents: [],
184
- hideToolbarBody: false,
185
- };
186
-
187
178
  toolbarWindow = React.createRef();
188
179
 
180
+ constructor(props) {
181
+ super(props);
182
+ const { cookies } = props;
183
+ this.state = {
184
+ expanded: cookies.get('toolbar_expanded') !== 'false',
185
+ showMenu: false,
186
+ menuStyle: {},
187
+ menuComponents: [],
188
+ loadedComponents: [],
189
+ hideToolbarBody: false,
190
+ };
191
+ }
192
+
189
193
  /**
190
194
  * Component will mount
191
195
  * @method componentDidMount
@@ -226,7 +230,8 @@ class Toolbar extends Component {
226
230
  }
227
231
 
228
232
  handleShrink = () => {
229
- cookie.save('toolbar_expanded', !this.state.expanded, {
233
+ const { cookies } = this.props;
234
+ cookies.set('toolbar_expanded', !this.state.expanded, {
230
235
  expires: new Date((2 ** 31 - 1) * 1000),
231
236
  path: '/',
232
237
  });
@@ -569,6 +574,7 @@ class Toolbar extends Component {
569
574
 
570
575
  export default compose(
571
576
  injectIntl,
577
+ withCookies,
572
578
  connect(
573
579
  (state, props) => ({
574
580
  actions: state.actions.actions,
@@ -135,11 +135,11 @@ class SelectAutoComplete extends Component {
135
135
  }
136
136
 
137
137
  componentDidUpdate(prevProps, prevState) {
138
- const { value, choices = [] } = this.props;
138
+ const { value, choices } = this.props;
139
139
  if (
140
140
  this.state.termsPairsCache.length === 0 &&
141
141
  value?.length > 0 &&
142
- choices.length > 0
142
+ choices?.length > 0
143
143
  ) {
144
144
  this.setState((state) => ({
145
145
  termsPairsCache: [...state.termsPairsCache, ...choices],
@@ -7,21 +7,14 @@ import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import { Link } from 'react-router-dom';
9
9
 
10
- import { useSelector, useDispatch } from 'react-redux';
10
+ import { useSelector } from 'react-redux';
11
11
  import cx from 'classnames';
12
12
  import { find, map } from 'lodash';
13
13
 
14
- import {
15
- Helmet,
16
- langmap,
17
- flattenToAppURL,
18
- normalizeLanguageName,
19
- } from '@plone/volto/helpers';
14
+ import { Helmet, langmap, flattenToAppURL } from '@plone/volto/helpers';
20
15
 
21
16
  import config from '@plone/volto/registry';
22
17
 
23
- import { changeLanguage } from '@plone/volto/actions';
24
-
25
18
  import { defineMessages, useIntl } from 'react-intl';
26
19
 
27
20
  const messages = defineMessages({
@@ -33,7 +26,6 @@ const messages = defineMessages({
33
26
 
34
27
  const LanguageSelector = (props) => {
35
28
  const intl = useIntl();
36
- const dispatch = useDispatch();
37
29
  const currentLang = useSelector((state) => state.intl.locale);
38
30
  const translations = useSelector(
39
31
  (state) => state.content.data?.['@components']?.translations?.items,
@@ -55,14 +47,6 @@ const LanguageSelector = (props) => {
55
47
  title={langmap[lang].nativeName}
56
48
  onClick={() => {
57
49
  props.onClickAction();
58
- if (config.settings.supportedLanguages.includes(lang)) {
59
- const langFileName = normalizeLanguageName(lang);
60
- import('~/../locales/' + langFileName + '.json').then(
61
- (locale) => {
62
- dispatch(changeLanguage(lang, locale.default));
63
- },
64
- );
65
- }
66
50
  }}
67
51
  key={`language-selector-${lang}`}
68
52
  >
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Redirect } from 'react-router-dom';
3
3
  import { useDispatch } from 'react-redux';
4
- import cookie from 'react-cookie';
4
+ import { useCookies } from 'react-cookie';
5
5
  import config from '@plone/volto/registry';
6
6
  import { changeLanguage } from '@plone/volto/actions';
7
7
  import { normalizeLanguageName } from '@plone/volto/helpers';
@@ -9,8 +9,8 @@ import { normalizeLanguageName } from '@plone/volto/helpers';
9
9
  const MultilingualRedirector = (props) => {
10
10
  const { settings } = config;
11
11
  const { pathname, contentLanguage, children } = props;
12
- const currentLanguage =
13
- cookie.load('I18N_LANGUAGE') || settings.defaultLanguage;
12
+ const [cookies] = useCookies(['name']);
13
+ const currentLanguage = cookies['I18N_LANGUAGE'] || settings.defaultLanguage;
14
14
  const redirectToLanguage = settings.supportedLanguages.includes(
15
15
  currentLanguage,
16
16
  )
@@ -23,10 +23,14 @@ const MultilingualRedirector = (props) => {
23
23
  // UI are the same. Otherwise, there are inconsistencies between the UI main elements
24
24
  // eg. Home link in breadcrumbs, other i18n dependant literals from the main UI and
25
25
  // the current content language.
26
+ // This variable makes sure that the async operation in the lazy load import does not
27
+ // happen twice (when switching language)
28
+ let mounted = true;
26
29
  if (
27
30
  contentLanguage &&
28
31
  currentLanguage !== contentLanguage &&
29
32
  pathname &&
33
+ pathname !== '/' &&
30
34
  // We don't want to trigger it in Babel View, since Babel view already takes care
31
35
  // of it
32
36
  !pathname.endsWith('/add') &&
@@ -34,9 +38,14 @@ const MultilingualRedirector = (props) => {
34
38
  ) {
35
39
  const langFileName = normalizeLanguageName(contentLanguage);
36
40
  import('~/../locales/' + langFileName + '.json').then((locale) => {
37
- dispatch(changeLanguage(contentLanguage, locale.default));
41
+ if (mounted) {
42
+ dispatch(changeLanguage(contentLanguage, locale.default));
43
+ }
38
44
  });
39
45
  }
46
+ return () => {
47
+ mounted = false;
48
+ };
40
49
  }, [
41
50
  pathname,
42
51
  dispatch,
@@ -48,12 +57,18 @@ const MultilingualRedirector = (props) => {
48
57
  React.useEffect(() => {
49
58
  // ToDo: Add means to support language negotiation (with config)
50
59
  // const detectedLang = (navigator.language || navigator.userLanguage).substring(0, 2);
60
+ let mounted = true;
51
61
  if (settings.isMultilingual && pathname === '/') {
52
62
  const langFileName = normalizeLanguageName(redirectToLanguage);
53
63
  import('~/../locales/' + langFileName + '.json').then((locale) => {
54
- dispatch(changeLanguage(redirectToLanguage, locale.default));
64
+ if (mounted) {
65
+ dispatch(changeLanguage(redirectToLanguage, locale.default));
66
+ }
55
67
  });
56
68
  }
69
+ return () => {
70
+ mounted = false;
71
+ };
57
72
  }, [pathname, dispatch, redirectToLanguage, settings.isMultilingual]);
58
73
 
59
74
  return pathname === '/' && settings.isMultilingual ? (
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  import superagent from 'superagent';
7
- import cookie from 'react-cookie';
8
7
  import config from '@plone/volto/registry';
9
8
 
10
9
  /**
@@ -30,7 +29,7 @@ export const getAPIResourceWithAuth = (req) =>
30
29
  .get(`${apiPath}${APISUFIX}${req.path}`)
31
30
  .maxResponseSize(settings.maxResponseSize)
32
31
  .responseType('blob');
33
- const authToken = cookie.load('auth_token');
32
+ const authToken = req.universalCookies.get('auth_token');
34
33
  if (authToken) {
35
34
  request.set('Authorization', `Bearer ${authToken}`);
36
35
  }
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import superagent from 'superagent';
7
- import cookie from 'react-cookie';
7
+ import Cookies from 'universal-cookie';
8
8
  import config from '@plone/volto/registry';
9
9
 
10
10
  const methods = ['get', 'post', 'put', 'patch', 'del'];
@@ -42,7 +42,9 @@ class Api {
42
42
  * @method constructor
43
43
  * @constructs Api
44
44
  */
45
- constructor() {
45
+ constructor(req) {
46
+ const cookies = new Cookies();
47
+
46
48
  methods.forEach((method) => {
47
49
  this[method] = (path, { params, data, type, headers = {} } = {}) => {
48
50
  let request;
@@ -53,10 +55,15 @@ class Api {
53
55
  request.query(params);
54
56
  }
55
57
 
56
- const authToken = cookie.load('auth_token');
58
+ let authToken;
59
+ if (req) {
60
+ // We are in SSR
61
+ authToken = req.universalCookies.get('auth_token');
62
+ } else {
63
+ authToken = cookies.get('auth_token');
64
+ }
57
65
  if (authToken) {
58
66
  request.set('Authorization', `Bearer ${authToken}`);
59
- if (__SERVER__) request.set('Cookie', `auth_token=${authToken}`);
60
67
  }
61
68
 
62
69
  request.set('Accept', 'application/json');
@@ -3,19 +3,20 @@
3
3
  * @module helpers/AuthToken
4
4
  */
5
5
 
6
- import cookie from 'react-cookie';
6
+ import Cookies from 'universal-cookie';
7
7
  import jwtDecode from 'jwt-decode';
8
8
 
9
9
  import { loginRenew } from '@plone/volto/actions';
10
10
  import { push } from 'connected-react-router';
11
11
 
12
12
  /**
13
- * Get auth token method.
13
+ * Get auth token method (does not work in SSR)
14
14
  * @method getAuthToken
15
15
  * @returns {undefined}
16
16
  */
17
17
  export function getAuthToken() {
18
- return cookie.load('auth_token');
18
+ const cookies = new Cookies();
19
+ return cookies.get('auth_token');
19
20
  }
20
21
 
21
22
  /**
@@ -24,8 +25,15 @@ export function getAuthToken() {
24
25
  * @param {object} store Redux store.
25
26
  * @returns {undefined}
26
27
  */
27
- export function persistAuthToken(store) {
28
- let currentValue = getAuthToken();
28
+ export function persistAuthToken(store, req) {
29
+ const cookies = new Cookies();
30
+ let currentValue;
31
+ if (req) {
32
+ // We are in SSR
33
+ currentValue = req.universalCookies.get('auth_token');
34
+ } else {
35
+ currentValue = cookies.get('auth_token');
36
+ }
29
37
 
30
38
  /**
31
39
  * handleChange method.
@@ -50,11 +58,11 @@ export function persistAuthToken(store) {
50
58
  if (previousValue !== currentValue || initial) {
51
59
  if (!currentValue) {
52
60
  if (previousValue) {
53
- cookie.remove('auth_token', { path: '/' });
61
+ cookies.remove('auth_token', { path: '/' });
54
62
  }
55
63
  } else {
56
64
  if (previousValue !== currentValue) {
57
- cookie.save('auth_token', currentValue, {
65
+ cookies.set('auth_token', currentValue, {
58
66
  path: '/',
59
67
  expires: new Date(jwtDecode(currentValue).exp * 1000),
60
68
  });
@@ -1,20 +1,24 @@
1
- import cookie from 'react-cookie';
1
+ import Cookies from 'universal-cookie';
2
2
  import jwt from 'jsonwebtoken';
3
3
  import jwtDecode from 'jwt-decode';
4
4
 
5
5
  import { getAuthToken, persistAuthToken } from './AuthToken';
6
6
 
7
- jest.mock('react-cookie', () => ({
8
- load: jest
9
- .fn(() => require('jsonwebtoken').sign({ exp: 1 }, 'secret')) // eslint-disable-line global-require
10
- .mockImplementationOnce(() => null), // the first call is for anonymous, no auth_token cookie
11
- remove: jest.fn(),
12
- save: jest.fn(),
13
- }));
7
+ jest.mock('universal-cookie', () => {
8
+ const mCookie = {
9
+ get: jest
10
+ .fn(() => require('jsonwebtoken').sign({ exp: 1 }, 'secret')) // eslint-disable-line global-require
11
+ .mockImplementationOnce(() => null), // the first call is for anonymous, no auth_token cookie
12
+ remove: jest.fn(),
13
+ set: jest.fn(),
14
+ };
15
+ return jest.fn(() => mCookie);
16
+ });
14
17
 
15
18
  describe('AuthToken', () => {
16
19
  describe('anonymousAuthToken', () => {
17
20
  it('avoid unnecessary removing auth token', () => {
21
+ const cookies = new Cookies();
18
22
  const store = {
19
23
  subscribe: jest.fn(),
20
24
  getState: jest.fn(() => ({
@@ -24,19 +28,21 @@ describe('AuthToken', () => {
24
28
  })),
25
29
  };
26
30
  persistAuthToken(store);
27
- expect(cookie.remove).not.toBeCalledWith('auth_token', { path: '/' });
31
+ expect(cookies.remove).not.toBeCalledWith('auth_token', { path: '/' });
28
32
  });
29
33
  });
30
34
 
31
35
  describe('getAuthToken', () => {
32
36
  it('can get the auth token', () => {
37
+ const cookies = new Cookies();
33
38
  getAuthToken();
34
- expect(cookie.load).toBeCalledWith('auth_token');
39
+ expect(cookies.get).toBeCalledWith('auth_token');
35
40
  });
36
41
  });
37
42
 
38
43
  describe('persistAuthToken', () => {
39
44
  it('can set a new auth token', () => {
45
+ const cookies = new Cookies();
40
46
  const store = {
41
47
  subscribe: jest.fn(),
42
48
  getState: jest.fn(() => ({
@@ -48,13 +54,14 @@ describe('AuthToken', () => {
48
54
  const { token } = store.getState().userSession;
49
55
 
50
56
  persistAuthToken(store);
51
- expect(cookie.save).toBeCalledWith('auth_token', token, {
57
+ expect(cookies.set).toBeCalledWith('auth_token', token, {
52
58
  path: '/',
53
59
  expires: new Date(jwtDecode(token).exp * 1000),
54
60
  });
55
61
  });
56
62
 
57
63
  it('can remove an auth token', () => {
64
+ const cookies = new Cookies();
58
65
  const store = {
59
66
  subscribe: jest.fn(),
60
67
  getState: jest.fn(() => ({
@@ -65,7 +72,7 @@ describe('AuthToken', () => {
65
72
  };
66
73
 
67
74
  persistAuthToken(store);
68
- expect(cookie.remove).toBeCalledWith('auth_token', { path: '/' });
75
+ expect(cookies.remove).toBeCalledWith('auth_token', { path: '/' });
69
76
  });
70
77
  });
71
78
  });
@@ -4,7 +4,6 @@
4
4
  */
5
5
 
6
6
  import superagent from 'superagent';
7
- import cookie from 'react-cookie';
8
7
  import config from '@plone/volto/registry';
9
8
 
10
9
  /**
@@ -23,7 +22,7 @@ export const generateRobots = (req) =>
23
22
  );
24
23
  request.set('Accept', 'text/plain');
25
24
 
26
- const authToken = cookie.load('auth_token');
25
+ const authToken = req.universalCookies.get('auth_token');
27
26
  if (authToken) {
28
27
  request.set('Authorization', `Bearer ${authToken}`);
29
28
  }
@@ -5,7 +5,6 @@
5
5
 
6
6
  import superagent from 'superagent';
7
7
  import { map } from 'lodash';
8
- import cookie from 'react-cookie';
9
8
  import zlib from 'zlib';
10
9
  import { toPublicURL } from '@plone/volto/helpers';
11
10
 
@@ -24,7 +23,7 @@ export const generateSitemap = (_req) =>
24
23
  `${settings.apiPath}/@search?metadata_fields=modified&b_size=100000000&use_site_search_settings=1`,
25
24
  );
26
25
  request.set('Accept', 'application/json');
27
- const authToken = cookie.load('auth_token');
26
+ const authToken = _req.universalCookies.get('auth_token');
28
27
  if (authToken) {
29
28
  request.set('Authorization', `Bearer ${authToken}`);
30
29
  }
@@ -35,7 +34,7 @@ export const generateSitemap = (_req) =>
35
34
  const items = map(
36
35
  body.items,
37
36
  (item) =>
38
- ` <url>\n <loc>${toPublicURL(item['@id'])}</loc>\n
37
+ ` <url>\n <loc>${toPublicURL(item['@id'])}</loc>\n
39
38
  <lastmod>${item.modified}</lastmod>\n </url>`,
40
39
  );
41
40
  const result = `<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">\n${items.join(
@@ -3,7 +3,7 @@
3
3
  * @module middleware/api
4
4
  */
5
5
 
6
- import cookie from 'react-cookie';
6
+ import Cookies from 'universal-cookie';
7
7
  import jwtDecode from 'jwt-decode';
8
8
  import { compact, flatten, union } from 'lodash';
9
9
  import { matchPath } from 'react-router';
@@ -174,7 +174,8 @@ export default (api) => ({ dispatch, getState }) => (next) => (action) => {
174
174
  });
175
175
  }
176
176
  if (type === LOGIN && settings.websockets) {
177
- cookie.save('auth_token', result.token, {
177
+ const cookies = new Cookies();
178
+ cookies.set('auth_token', result.token, {
178
179
  path: '/',
179
180
  expires: new Date(jwtDecode(result.token).exp * 1000),
180
181
  });
package/src/server.jsx CHANGED
@@ -9,12 +9,13 @@ import { renderToString } from 'react-dom/server';
9
9
  import { createMemoryHistory } from 'history';
10
10
  import { parse as parseUrl } from 'url';
11
11
  import { keys } from 'lodash';
12
- import cookie, { plugToRequest } from 'react-cookie';
13
12
  import locale from 'locale';
14
13
  import { detect } from 'detect-browser';
15
14
  import path from 'path';
16
15
  import { ChunkExtractor, ChunkExtractorManager } from '@loadable/server';
17
16
  import { resetServerContext } from 'react-beautiful-dnd';
17
+ import { CookiesProvider } from 'react-cookie';
18
+ import cookiesMiddleware from 'universal-cookie-express';
18
19
  import debug from 'debug';
19
20
 
20
21
  import routes from '~/routes';
@@ -62,10 +63,7 @@ const server = express()
62
63
  // Support for HEAD requests. Required by start-test utility in CI.
63
64
  res.send('');
64
65
  })
65
- .all('*', (req, res, next) => {
66
- plugToRequest(req, res);
67
- next();
68
- });
66
+ .use(cookiesMiddleware());
69
67
 
70
68
  const middleware = (config.settings.expressMiddleware || []).filter((m) => m);
71
69
 
@@ -106,14 +104,14 @@ function setupServer(req, res, next) {
106
104
  const browserdetect = detect(req.headers['user-agent']);
107
105
 
108
106
  const lang = new locale.Locales(
109
- cookie.load('I18N_LANGUAGE') ||
107
+ req.universalCookies.get('I18N_LANGUAGE') ||
110
108
  config.settings.defaultLanguage ||
111
109
  req.headers['accept-language'],
112
110
  )
113
111
  .best(supported)
114
112
  .toString();
115
113
 
116
- const authToken = cookie.load('auth_token');
114
+ const authToken = req.universalCookies.get('auth_token');
117
115
  const initialState = {
118
116
  userSession: { ...userSession(), token: authToken },
119
117
  form: req.body,
@@ -132,7 +130,7 @@ function setupServer(req, res, next) {
132
130
  // Create a new Redux store instance
133
131
  const store = configureStore(initialState, history, api);
134
132
 
135
- persistAuthToken(store);
133
+ persistAuthToken(store, req);
136
134
 
137
135
  function errorHandler(error) {
138
136
  const errorPage = (
@@ -204,11 +202,13 @@ server.get('/*', (req, res) => {
204
202
  resetServerContext();
205
203
  const markup = renderToString(
206
204
  <ChunkExtractorManager extractor={extractor}>
207
- <Provider store={store} onError={reactIntlErrorHandler}>
208
- <StaticRouter context={context} location={req.url}>
209
- <ReduxAsyncConnect routes={routes} helpers={api} />
210
- </StaticRouter>
211
- </Provider>
205
+ <CookiesProvider cookies={req.universalCookies}>
206
+ <Provider store={store} onError={reactIntlErrorHandler}>
207
+ <StaticRouter context={context} location={req.url}>
208
+ <ReduxAsyncConnect routes={routes} helpers={api} />
209
+ </StaticRouter>
210
+ </Provider>
211
+ </CookiesProvider>
212
212
  </ChunkExtractorManager>,
213
213
  );
214
214
 
@@ -8,6 +8,7 @@ import { ConnectedRouter } from 'connected-react-router';
8
8
  import { createBrowserHistory } from 'history';
9
9
  import { ReduxAsyncConnect } from '@plone/volto/helpers/AsyncConnect';
10
10
  import { loadableReady } from '@loadable/component';
11
+ import { CookiesProvider } from 'react-cookie';
11
12
  import debug from 'debug';
12
13
  import routes from '~/routes';
13
14
  import config from '@plone/volto/registry';
@@ -55,15 +56,17 @@ export default () => {
55
56
 
56
57
  loadableReady(() => {
57
58
  hydrate(
58
- <Provider store={store}>
59
- <IntlProvider onError={reactIntlErrorHandler}>
60
- <ConnectedRouter history={history}>
61
- <ScrollToTop>
62
- <ReduxAsyncConnect routes={routes} helpers={api} />
63
- </ScrollToTop>
64
- </ConnectedRouter>
65
- </IntlProvider>
66
- </Provider>,
59
+ <CookiesProvider>
60
+ <Provider store={store}>
61
+ <IntlProvider onError={reactIntlErrorHandler}>
62
+ <ConnectedRouter history={history}>
63
+ <ScrollToTop>
64
+ <ReduxAsyncConnect routes={routes} helpers={api} />
65
+ </ScrollToTop>
66
+ </ConnectedRouter>
67
+ </IntlProvider>
68
+ </Provider>
69
+ </CookiesProvider>,
67
70
  document.getElementById('main'),
68
71
  );
69
72
  });
package/pyvenv.cfg DELETED
@@ -1,3 +0,0 @@
1
- home = /opt/homebrew/Cellar/python@3.8/3.8.12_1/bin
2
- include-system-site-packages = false
3
- version = 3.8.12