@eeacms/volto-eea-website-policy 1.13.0 → 1.14.0

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/.eslintrc.js ADDED
@@ -0,0 +1,65 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const projectRootPath = fs.realpathSync(__dirname + '/../../../');
4
+
5
+ let voltoPath = path.join(projectRootPath, 'node_modules/@plone/volto');
6
+ let configFile;
7
+ if (fs.existsSync(`${projectRootPath}/tsconfig.json`))
8
+ configFile = `${projectRootPath}/tsconfig.json`;
9
+ else if (fs.existsSync(`${projectRootPath}/jsconfig.json`))
10
+ configFile = `${projectRootPath}/jsconfig.json`;
11
+
12
+ if (configFile) {
13
+ const jsConfig = require(configFile).compilerOptions;
14
+ const pathsConfig = jsConfig.paths;
15
+ if (pathsConfig['@plone/volto'])
16
+ voltoPath = `./${jsConfig.baseUrl}/${pathsConfig['@plone/volto'][0]}`;
17
+ }
18
+
19
+ const AddonConfigurationRegistry = require(`${voltoPath}/addon-registry.js`);
20
+ const reg = new AddonConfigurationRegistry(projectRootPath);
21
+
22
+ // Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
23
+ const addonAliases = Object.keys(reg.packages).map((o) => [
24
+ o,
25
+ reg.packages[o].modulePath,
26
+ ]);
27
+
28
+ const addonExtenders = reg.getEslintExtenders().map((m) => require(m));
29
+
30
+ const defaultConfig = {
31
+ extends: `${voltoPath}/.eslintrc`,
32
+ settings: {
33
+ 'import/resolver': {
34
+ alias: {
35
+ map: [
36
+ ['@plone/volto', '@plone/volto/src'],
37
+ ['@plone/volto-slate', '@plone/volto/packages/volto-slate/src'],
38
+ ...addonAliases,
39
+ ['@package', `${__dirname}/src`],
40
+ ['@root', `${__dirname}/src`],
41
+ ['~', `${__dirname}/src`],
42
+ ],
43
+ extensions: ['.js', '.jsx', '.json'],
44
+ },
45
+ 'babel-plugin-root-import': {
46
+ rootPathSuffix: 'src',
47
+ },
48
+ },
49
+ },
50
+ rules: {
51
+ 'react/jsx-no-target-blank': [
52
+ 'error',
53
+ {
54
+ allowReferrer: true,
55
+ },
56
+ ],
57
+ }
58
+ };
59
+
60
+ const config = addonExtenders.reduce(
61
+ (acc, extender) => extender.modify(acc),
62
+ defaultConfig,
63
+ );
64
+
65
+ module.exports = config;
package/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ ### [1.14.0](https://github.com/eea/volto-eea-website-policy/compare/1.13.1...1.14.0) - 16 April 2024
8
+
9
+ #### :rocket: New Features
10
+
11
+ - feat: Default view layout restriction to Manager - refs #268249 [ichim-david - [`1840c1b`](https://github.com/eea/volto-eea-website-policy/commit/1840c1bd9aae6ce18a23800ce618f8bbe47525ab)]
12
+ - feat: Volto 17 - refs #264527 [EEA Jenkins - [`253a322`](https://github.com/eea/volto-eea-website-policy/commit/253a3221e36500b4f0f3b160bb2455a066ff3ebd)]
13
+
14
+ #### :hammer_and_wrench: Others
15
+
16
+ - Update Jenkinsfile [Alexandru Ghica - [`c962b11`](https://github.com/eea/volto-eea-website-policy/commit/c962b118d4e2dbe6f5fe938cb8aa1fe610a09d3a)]
17
+ ### [1.13.1](https://github.com/eea/volto-eea-website-policy/compare/1.13.0...1.13.1) - 28 March 2024
18
+
19
+ #### :bug: Bug Fixes
20
+
21
+ - fix: make sure that the content type is sent in workflow change - refs #267108 [dobri1408 - [`934352d`](https://github.com/eea/volto-eea-website-policy/commit/934352dc16248a7f6a13499ed99910ca5f67d577)]
22
+
7
23
  ### [1.13.0](https://github.com/eea/volto-eea-website-policy/compare/1.13.0-beta.1...1.13.0) - 19 February 2024
8
24
 
9
25
  #### :rocket: New Features
@@ -1,3 +1,5 @@
1
+ require('dotenv').config({ path: __dirname + '/.env' })
2
+
1
3
  module.exports = {
2
4
  testMatch: ['**/src/addons/**/?(*.)+(spec|test).[jt]s?(x)'],
3
5
  collectCoverageFrom: [
@@ -9,18 +11,26 @@ module.exports = {
9
11
  '@plone/volto/cypress': '<rootDir>/node_modules/@plone/volto/cypress',
10
12
  '@plone/volto/babel': '<rootDir>/node_modules/@plone/volto/babel',
11
13
  '@plone/volto/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
12
- '@package/(.*)$': '<rootDir>/src/$1',
13
- '@root/(.*)$': '<rootDir>/src/$1',
14
+ '@package/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
15
+ '@root/(.*)$': '<rootDir>/node_modules/@plone/volto/src/$1',
14
16
  '@plone/volto-quanta/(.*)$': '<rootDir>/src/addons/volto-quanta/src/$1',
17
+ '@eeacms/search/(.*)$': '<rootDir>/src/addons/volto-searchlib/searchlib/$1',
18
+ '@eeacms/search': '<rootDir>/src/addons/volto-searchlib/searchlib',
15
19
  '@eeacms/(.*?)/(.*)$': '<rootDir>/node_modules/@eeacms/$1/src/$2',
16
- '@plone/volto-slate':
20
+ '@plone/volto-slate$':
17
21
  '<rootDir>/node_modules/@plone/volto/packages/volto-slate/src',
22
+ '@plone/volto-slate/(.*)$':
23
+ '<rootDir>/node_modules/@plone/volto/packages/volto-slate/src/$1',
18
24
  '~/(.*)$': '<rootDir>/src/$1',
19
25
  'load-volto-addons':
20
26
  '<rootDir>/node_modules/@plone/volto/jest-addons-loader.js',
21
27
  },
28
+ transformIgnorePatterns: [
29
+ '/node_modules/(?!(@plone|@root|@package|@eeacms)/).*/',
30
+ ],
22
31
  transform: {
23
32
  '^.+\\.js(x)?$': 'babel-jest',
33
+ '^.+\\.ts(x)?$': 'babel-jest',
24
34
  '^.+\\.(png)$': 'jest-file',
25
35
  '^.+\\.(jpg)$': 'jest-file',
26
36
  '^.+\\.(svg)$': './node_modules/@plone/volto/jest-svgsystem-transform.js',
@@ -33,4 +43,9 @@ module.exports = {
33
43
  statements: 5,
34
44
  },
35
45
  },
36
- };
46
+ ...(process.env.JEST_USE_SETUP === 'ON' && {
47
+ setupFilesAfterEnv: [
48
+ '<rootDir>/node_modules/@eeacms/volto-eea-website-policy/jest.setup.js',
49
+ ],
50
+ }),
51
+ }
package/jest.setup.js ADDED
@@ -0,0 +1,65 @@
1
+ import { jest } from '@jest/globals';
2
+ import configureStore from 'redux-mock-store';
3
+ import thunk from 'redux-thunk';
4
+ import { blocksConfig } from '@plone/volto/config/Blocks';
5
+ import installSlate from '@plone/volto-slate/index';
6
+
7
+ var mockSemanticComponents = jest.requireActual('semantic-ui-react');
8
+ var mockComponents = jest.requireActual('@plone/volto/components');
9
+ var config = jest.requireActual('@plone/volto/registry').default;
10
+
11
+ config.blocks.blocksConfig = {
12
+ ...blocksConfig,
13
+ ...config.blocks.blocksConfig,
14
+ };
15
+
16
+ jest.doMock('semantic-ui-react', () => ({
17
+ __esModule: true,
18
+ ...mockSemanticComponents,
19
+ Popup: ({ content, trigger }) => {
20
+ return (
21
+ <div className="popup">
22
+ <div className="trigger">{trigger}</div>
23
+ <div className="content">{content}</div>
24
+ </div>
25
+ );
26
+ },
27
+ }));
28
+
29
+ jest.doMock('@plone/volto/components', () => {
30
+ return {
31
+ __esModule: true,
32
+ ...mockComponents,
33
+ SidebarPortal: ({ children }) => <div id="sidebar">{children}</div>,
34
+ };
35
+ });
36
+
37
+ jest.doMock('@plone/volto/registry', () =>
38
+ [installSlate].reduce((acc, apply) => apply(acc), config),
39
+ );
40
+
41
+ const mockStore = configureStore([thunk]);
42
+
43
+ global.fetch = jest.fn(() =>
44
+ Promise.resolve({
45
+ json: () => Promise.resolve({}),
46
+ }),
47
+ );
48
+
49
+ global.store = mockStore({
50
+ intl: {
51
+ locale: 'en',
52
+ messages: {},
53
+ formatMessage: jest.fn(),
54
+ },
55
+ content: {
56
+ create: {},
57
+ subrequests: [],
58
+ },
59
+ connected_data_parameters: {},
60
+ screen: {
61
+ page: {
62
+ width: 768,
63
+ },
64
+ },
65
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-eea-website-policy",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "description": "@eeacms/volto-eea-website-policy: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: IDM2 A-Team",
@@ -26,6 +26,7 @@
26
26
  "@cypress/code-coverage": "^3.10.0",
27
27
  "@plone/scripts": "*",
28
28
  "babel-plugin-transform-class-properties": "^6.24.1",
29
+ "dotenv": "^16.3.2",
29
30
  "husky": "^8.0.3",
30
31
  "lint-staged": "^14.0.1",
31
32
  "md5": "^2.3.0"
@@ -3,7 +3,7 @@ import ContextNavigationEdit from './ContextNavigationEdit';
3
3
  import ContextNavigationView from './ContextNavigationView';
4
4
  import BlockSettingsSchema from '@plone/volto/components/manage/Blocks/Block/Schema';
5
5
 
6
- export default (config) => {
6
+ const applyConfig = (config) => {
7
7
  config.blocks.blocksConfig.contextNavigation = {
8
8
  id: 'contextNavigation',
9
9
  title: 'Navigation',
@@ -24,3 +24,5 @@ export default (config) => {
24
24
 
25
25
  return config;
26
26
  };
27
+
28
+ export default applyConfig;
@@ -0,0 +1,306 @@
1
+ import React, { Component, Fragment } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { connect } from 'react-redux';
4
+ import { compose } from 'redux';
5
+ import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
6
+
7
+ import jwtDecode from 'jwt-decode';
8
+ import {
9
+ getSchema,
10
+ getUser,
11
+ updateContent,
12
+ getContent,
13
+ } from '@plone/volto/actions';
14
+ import { getLayoutFieldname } from '@plone/volto/helpers';
15
+ import { FormFieldWrapper, Icon } from '@plone/volto/components';
16
+ import { defineMessages, injectIntl } from 'react-intl';
17
+ import config from '@plone/volto/registry';
18
+
19
+ import downSVG from '@plone/volto/icons/down-key.svg';
20
+ import upSVG from '@plone/volto/icons/up-key.svg';
21
+ import checkSVG from '@plone/volto/icons/check.svg';
22
+
23
+ const messages = defineMessages({
24
+ Viewmode: {
25
+ id: 'Viewmode',
26
+ defaultMessage: 'View',
27
+ },
28
+ });
29
+
30
+ const Option = injectLazyLibs('reactSelect')((props) => {
31
+ const { Option } = props.reactSelect.components;
32
+ return (
33
+ <Option {...props}>
34
+ <div>{props.label}</div>
35
+ {props.isFocused && !props.isSelected && (
36
+ <Icon name={checkSVG} size="18px" color="#b8c6c8" />
37
+ )}
38
+ {props.isSelected && <Icon name={checkSVG} size="18px" color="#007bc1" />}
39
+ </Option>
40
+ );
41
+ });
42
+
43
+ const DropdownIndicator = injectLazyLibs('reactSelect')((props) => {
44
+ const { DropdownIndicator } = props.reactSelect.components;
45
+ return (
46
+ <DropdownIndicator {...props}>
47
+ {props.selectProps.menuIsOpen ? (
48
+ <Icon name={upSVG} size="24px" color="#007bc1" />
49
+ ) : (
50
+ <Icon name={downSVG} size="24px" color="#007bc1" />
51
+ )}
52
+ </DropdownIndicator>
53
+ );
54
+ });
55
+
56
+ const selectTheme = (theme) => ({
57
+ ...theme,
58
+ borderRadius: 0,
59
+ colors: {
60
+ ...theme.colors,
61
+ primary25: 'hotpink',
62
+ primary: '#b8c6c8',
63
+ },
64
+ });
65
+
66
+ const customSelectStyles = {
67
+ control: (styles, state) => ({
68
+ ...styles,
69
+ border: 'none',
70
+ borderBottom: '2px solid #b8c6c8',
71
+ boxShadow: 'none',
72
+ borderBottomStyle: state.menuIsOpen ? 'dotted' : 'solid',
73
+ }),
74
+ menu: (styles, state) => ({
75
+ ...styles,
76
+ top: null,
77
+ marginTop: 0,
78
+ boxShadow: 'none',
79
+ borderBottom: '2px solid #b8c6c8',
80
+ }),
81
+ menuList: (styles, state) => ({
82
+ ...styles,
83
+ maxHeight: '400px',
84
+ }),
85
+ indicatorSeparator: (styles) => ({
86
+ ...styles,
87
+ width: null,
88
+ }),
89
+ valueContainer: (styles) => ({
90
+ ...styles,
91
+ padding: 0,
92
+ }),
93
+ option: (styles, state) => ({
94
+ ...styles,
95
+ backgroundColor: null,
96
+ minHeight: '50px',
97
+ display: 'flex',
98
+ justifyContent: 'space-between',
99
+ alignItems: 'center',
100
+ padding: '12px 12px',
101
+ color: state.isSelected
102
+ ? '#007bc1'
103
+ : state.isFocused
104
+ ? '#4a4a4a'
105
+ : 'inherit',
106
+ ':active': {
107
+ backgroundColor: null,
108
+ },
109
+ span: {
110
+ flex: '0 0 auto',
111
+ },
112
+ svg: {
113
+ flex: '0 0 auto',
114
+ },
115
+ }),
116
+ };
117
+
118
+ /**
119
+ * Display container class.
120
+ * @class Display
121
+ * @extends Component
122
+ */
123
+ class DisplaySelect extends Component {
124
+ /**
125
+ * Property types.
126
+ * @property {Object} propTypes Property types.
127
+ * @static
128
+ */
129
+ static propTypes = {
130
+ getSchema: PropTypes.func.isRequired,
131
+ updateContent: PropTypes.func.isRequired,
132
+ getContent: PropTypes.func.isRequired,
133
+ loaded: PropTypes.bool.isRequired,
134
+ pathname: PropTypes.string.isRequired,
135
+ layouts: PropTypes.arrayOf(PropTypes.string),
136
+ layout: PropTypes.string,
137
+ type: PropTypes.string.isRequired,
138
+ };
139
+
140
+ /**
141
+ * Default properties
142
+ * @property {Object} defaultProps Default properties.
143
+ * @static
144
+ */
145
+ static defaultProps = {
146
+ layouts: [],
147
+ layout: '',
148
+ rolesWhoCanChangeLayout: [],
149
+ };
150
+
151
+ state = {
152
+ hasMatchingRole: false,
153
+ selectedOption: {
154
+ value: this.props.layout,
155
+ label: config.views.layoutViewsNamesMapping?.[this.props.layout]
156
+ ? this.props.intl.formatMessage({
157
+ id: config.views.layoutViewsNamesMapping?.[this.props.layout],
158
+ defaultMessage:
159
+ config.views.layoutViewsNamesMapping?.[this.props.layout],
160
+ })
161
+ : this.props.layout,
162
+ },
163
+ };
164
+
165
+ componentDidMount() {
166
+ this.props.getSchema(this.props.type);
167
+ }
168
+
169
+ UNSAFE_componentWillMount() {
170
+ if (!this.props.rolesWhoCanChangeLayout.length) {
171
+ this.props.rolesWhoCanChangeLayout.push(
172
+ ...(config?.settings?.eea?.rolesWhoCanChangeLayout || []),
173
+ );
174
+ }
175
+ if (!this.props.layouts.length) {
176
+ this.props.getSchema(this.props.type);
177
+ }
178
+ if (Object.keys(this.props.user).length === 0) {
179
+ this.props.getUser(this.props.userId);
180
+ } else {
181
+ const hasMatchingRole = this.props.user.roles.some((role) =>
182
+ this.props.rolesWhoCanChangeLayout.includes(role),
183
+ );
184
+ if (hasMatchingRole !== this.state.hasMatchingRole) {
185
+ this.setState({ hasMatchingRole });
186
+ }
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Component will receive props
192
+ * @method componentWillReceiveProps
193
+ * @param {Object} nextProps Next properties
194
+ * @returns {undefined}
195
+ */
196
+ UNSAFE_componentWillReceiveProps(nextProps) {
197
+ if (nextProps.pathname !== this.props.pathname) {
198
+ this.props.getSchema(nextProps.type);
199
+ }
200
+ if (!this.props.loaded && nextProps.loaded) {
201
+ this.props.getContent(nextProps.pathname);
202
+ }
203
+
204
+ if (Object.keys(nextProps.user).length !== 0) {
205
+ const hasMatchingRole = nextProps.user.roles.some((role) =>
206
+ this.props.rolesWhoCanChangeLayout.includes(role),
207
+ );
208
+ if (hasMatchingRole !== this.state.hasMatchingRole) {
209
+ this.setState({ hasMatchingRole });
210
+ }
211
+ }
212
+ }
213
+
214
+ /**
215
+ * On set layout handler
216
+ * @method setLayout
217
+ * @param {Object} event Event object
218
+ * @returns {undefined}
219
+ */
220
+ setLayout = (selectedOption) => {
221
+ this.props.updateContent(this.props.pathname, {
222
+ layout: selectedOption.value,
223
+ });
224
+ this.setState({ selectedOption });
225
+ };
226
+
227
+ selectValue = (option) => (
228
+ <Fragment>
229
+ <span className="Select-value-label">{option.label}</span>
230
+ </Fragment>
231
+ );
232
+
233
+ optionRenderer = (option) => (
234
+ <Fragment>
235
+ <span style={{ marginRight: 'auto' }}>{option.label}</span>
236
+ <Icon name={checkSVG} size="24px" />
237
+ </Fragment>
238
+ );
239
+
240
+ render() {
241
+ if (!this.state.hasMatchingRole) {
242
+ return null;
243
+ }
244
+ const { selectedOption } = this.state;
245
+ const Select = this.props.reactSelect.default;
246
+ const layoutsNames = config.views.layoutViewsNamesMapping;
247
+ const layoutOptions = this.props.layouts
248
+ .filter(
249
+ (layout) =>
250
+ Object.keys(config.views.contentTypesViews).includes(layout) ||
251
+ Object.keys(config.views.layoutViews).includes(layout),
252
+ )
253
+ .map((item) => ({
254
+ value: item,
255
+ label:
256
+ this.props.intl.formatMessage({
257
+ id: layoutsNames[item],
258
+ defaultMessage: layoutsNames[item],
259
+ }) || item,
260
+ }));
261
+
262
+ return layoutOptions?.length > 1 ? (
263
+ <FormFieldWrapper
264
+ id="display-select"
265
+ title={this.props.intl.formatMessage(messages.Viewmode)}
266
+ {...this.props}
267
+ >
268
+ <Select
269
+ name="display-select"
270
+ className="react-select-container"
271
+ classNamePrefix="react-select"
272
+ options={layoutOptions}
273
+ styles={customSelectStyles}
274
+ theme={selectTheme}
275
+ components={{ DropdownIndicator, Option }}
276
+ onChange={this.setLayout}
277
+ defaultValue={selectedOption}
278
+ isSearchable={false}
279
+ />
280
+ </FormFieldWrapper>
281
+ ) : null;
282
+ }
283
+ }
284
+
285
+ export default compose(
286
+ injectIntl,
287
+ injectLazyLibs('reactSelect'),
288
+ connect(
289
+ (state) => ({
290
+ loaded: state.content.update.loaded,
291
+ layouts: state.schema.schema ? state.schema.schema.layouts : [],
292
+ layout: state.content.data
293
+ ? state.content.data[getLayoutFieldname(state.content.data)]
294
+ : '',
295
+ layout_fieldname: state.content.data
296
+ ? getLayoutFieldname(state.content.data)
297
+ : '',
298
+ type: state.content.data ? state.content.data['@type'] : '',
299
+ user: state.users.user,
300
+ userId: state.userSession.token
301
+ ? jwtDecode(state.userSession.token).sub
302
+ : '',
303
+ }),
304
+ { getSchema, getUser, updateContent, getContent },
305
+ ),
306
+ )(DisplaySelect);
@@ -0,0 +1 @@
1
+ Added customization to condition the display of the layout options based on the user's roles.
@@ -82,8 +82,7 @@ const messages = defineMessages({
82
82
  defaultMessage: 'Changes applied',
83
83
  },
84
84
  workingCopyAppliedBy: {
85
- id:
86
- 'Made by {creator} on {date}. This is not a working copy anymore, but the main content.',
85
+ id: 'Made by {creator} on {date}. This is not a working copy anymore, but the main content.',
87
86
  defaultMessage:
88
87
  'Made by {creator} on {date}. This is not a working copy anymore, but the main content.',
89
88
  },
@@ -342,7 +341,7 @@ class More extends Component {
342
341
  <div className="pastanaga-menu-list">
343
342
  <ul>
344
343
  {pluggables.map((p) => (
345
- <>{p()}</>
344
+ <React.Fragment key={p.id}>{p()}</React.Fragment>
346
345
  ))}
347
346
  </ul>
348
347
  </div>
@@ -3,20 +3,20 @@
3
3
  * @module components/manage/Workflow/Workflow
4
4
  */
5
5
 
6
- import React, { Component, Fragment } from 'react';
7
- import PropTypes from 'prop-types';
8
- import { withRouter } from 'react-router-dom';
9
- import { compose } from 'redux';
10
- import { connect } from 'react-redux';
11
- import { uniqBy } from 'lodash';
12
- import { toast } from 'react-toastify';
13
- import { defineMessages, injectIntl } from 'react-intl';
14
6
  import { FormFieldWrapper, Icon, Toast } from '@plone/volto/components';
15
7
  import {
16
8
  flattenToAppURL,
17
9
  getCurrentStateMapping,
18
10
  getWorkflowOptions,
19
11
  } from '@plone/volto/helpers';
12
+ import { uniqBy } from 'lodash';
13
+ import PropTypes from 'prop-types';
14
+ import { Component, Fragment } from 'react';
15
+ import { defineMessages, injectIntl } from 'react-intl';
16
+ import { connect } from 'react-redux';
17
+ import { withRouter } from 'react-router-dom';
18
+ import { toast } from 'react-toastify';
19
+ import { compose } from 'redux';
20
20
 
21
21
  import { injectLazyLibs } from '@plone/volto/helpers/Loadable/Loadable';
22
22
 
@@ -26,9 +26,9 @@ import {
26
26
  transitionWorkflow,
27
27
  } from '@plone/volto/actions';
28
28
 
29
+ import checkSVG from '@plone/volto/icons/check.svg';
29
30
  import downSVG from '@plone/volto/icons/down-key.svg';
30
31
  import upSVG from '@plone/volto/icons/up-key.svg';
31
- import checkSVG from '@plone/volto/icons/check.svg';
32
32
 
33
33
  const messages = defineMessages({
34
34
  messageUpdated: {
@@ -200,7 +200,7 @@ class Workflow extends Component {
200
200
  getContent: PropTypes.func.isRequired,
201
201
  getWorkflow: PropTypes.func.isRequired,
202
202
  transitionWorkflow: PropTypes.func.isRequired,
203
- workflowLoaded: PropTypes.func,
203
+ workflowLoaded: PropTypes.bool,
204
204
  loaded: PropTypes.bool.isRequired,
205
205
  pathname: PropTypes.string.isRequired,
206
206
 
@@ -265,7 +265,12 @@ class Workflow extends Component {
265
265
  * @returns {undefined}
266
266
  */
267
267
  transition = (selectedOption) => {
268
- if (filter_remaining_steps(this.props.editingProgressSteps).length === 0) {
268
+ if (
269
+ filter_remaining_steps(
270
+ this.props.editingProgressSteps,
271
+ this.props?.content?.review_state || '',
272
+ ).length === 0
273
+ ) {
269
274
  this.props.transitionWorkflow(flattenToAppURL(selectedOption.url));
270
275
  this.setState({ selectedOption });
271
276
  toast.success(
package/src/index.js CHANGED
@@ -39,7 +39,8 @@ const overrideBlocks = {
39
39
 
40
40
  const applyConfig = (config) => {
41
41
  // #158717#note-25 any path that isn't static, en or controlpanel is treated as external
42
- const notInEN = /^(?!(#|\/en|\/login-authomatic|\/fallback_login|\/static|\/controlpanel|\/cypress|\/login|\/logout|\/contact-form|\/passwordreset)).*$/;
42
+ const notInEN =
43
+ /^(?!(#|\/en|\/login-authomatic|\/fallback_login|\/static|\/controlpanel|\/cypress|\/login|\/logout|\/contact-form|\/passwordreset)).*$/;
43
44
  config.settings.externalRoutes = [
44
45
  ...(config.settings.externalRoutes || []),
45
46
  {
@@ -92,6 +93,12 @@ const applyConfig = (config) => {
92
93
  if (config.settings.eea) {
93
94
  config.settings.eea.logoTargetUrl = '/en';
94
95
  }
96
+ // #268249 - only allow managers to change layout from more menu
97
+ config.settings.eea = {
98
+ ...config.settings.eea,
99
+ rolesWhoCanChangeLayout: ['Manager'],
100
+ };
101
+ // config.settings.eea.rolesWhoCanChangeLayout = ['Manager'];
95
102
 
96
103
  // Custom blocks
97
104
  // context navigation
@@ -1,48 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- const projectRootPath = fs.existsSync('./project')
5
- ? fs.realpathSync('./project')
6
- : fs.realpathSync('./../../../');
7
- const packageJson = require(path.join(projectRootPath, 'package.json'));
8
- const jsConfig = require(path.join(projectRootPath, 'jsconfig.json')).compilerOptions;
9
-
10
- const pathsConfig = jsConfig.paths;
11
-
12
- let voltoPath = path.join(projectRootPath, 'node_modules/@plone/volto');
13
-
14
- Object.keys(pathsConfig).forEach(pkg => {
15
- if (pkg === '@plone/volto') {
16
- voltoPath = `./${jsConfig.baseUrl}/${pathsConfig[pkg][0]}`;
17
- }
18
- });
19
- const AddonConfigurationRegistry = require(`${voltoPath}/addon-registry.js`);
20
- const reg = new AddonConfigurationRegistry(projectRootPath);
21
-
22
- // Extends ESlint configuration for adding the aliases to `src` directories in Volto addons
23
- const addonAliases = Object.keys(reg.packages).map(o => [
24
- o,
25
- reg.packages[o].modulePath,
26
- ]);
27
-
28
-
29
- module.exports = {
30
- extends: `${projectRootPath}/node_modules/@plone/volto/.eslintrc`,
31
- settings: {
32
- 'import/resolver': {
33
- alias: {
34
- map: [
35
- ['@plone/volto', '@plone/volto/src'],
36
- ...addonAliases,
37
- ['@package', `${__dirname}/src`],
38
- ['~', `${__dirname}/src`],
39
- ],
40
- extensions: ['.js', '.jsx', '.json'],
41
- },
42
- 'babel-plugin-root-import': {
43
- rootPathSuffix: 'src',
44
- },
45
- },
46
- },
47
- };
48
-