@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 +65 -0
- package/CHANGELOG.md +16 -0
- package/jest-addon.config.js +19 -4
- package/jest.setup.js +65 -0
- package/package.json +2 -1
- package/src/components/Blocks/ContextNavigation/index.js +3 -1
- package/src/customizations/volto/components/manage/Display/Display.jsx +306 -0
- package/src/customizations/volto/components/manage/Display/Readme.md +1 -0
- package/src/customizations/volto/components/manage/Toolbar/More.jsx +2 -3
- package/src/customizations/volto/components/manage/Workflow/Workflow.jsx +16 -11
- package/src/index.js +8 -1
- package/.project.eslintrc.js +0 -48
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
|
package/jest-addon.config.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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 (
|
|
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 =
|
|
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
|
package/.project.eslintrc.js
DELETED
|
@@ -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
|
-
|