Skip to content

Commit 5a7953b

Browse files
Fix geosolutions-it#11879 Add the CRS selector permission to access the quick selector and settings modal (geosolutions-it#11948)
1 parent 6e8e515 commit 5a7953b

9 files changed

Lines changed: 187 additions & 52 deletions

File tree

docs/developer-guide/mapstore-migration-guide.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,25 @@ As part of improving the authentication rules to make dynamic request configurat
7979
| `header` | `headers: { ... }` |
8080
| `browserWithCredentials` | `withCredentials: true` |
8181

82+
### Replace filterAllowedCRS and additionalCRS with availableProjections
83+
84+
As part of extending the functionalities of the CRS selector, we have deprecated the use of `filterAllowedCRS` and `additionalCRS` in favor of new configuration `availableProjections`. The new configuration provides the support to add both filterAllowedCRS and additionalCRS in a single configuration. The configuration in `localConfig.json` should be updated as follow:
85+
86+
```diff
87+
{
88+
"name": "CRSSelector",
89+
"cfg": {
90+
- "additionalCRS": {},
91+
- "filterAllowedCRS": ["EPSG:4326", "EPSG:3857"],
92+
+ "availableProjections": [
93+
+ { "value": "EPSG:4326", "label": "EPSG:4326" },
94+
+ { "value": "EPSG:3857", "label": "EPSG:3857" }
95+
+ ],
96+
"allowedRoles": ["ADMIN"]
97+
}
98+
}
99+
```
100+
82101
## Migration from 2025.01.01 to 2025.02.00
83102

84103
### Update authenticationRules in localConfig.json

web/client/actions/crsselector.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
export const CHANGE_CRS_INPUT_VALUE = 'CHANGE_CRS_INPUT_VALUE';
1010
export const SET_PROJECTIONS_CONFIG = 'SET_PROJECTIONS_CONFIG';
11+
export const SET_CAN_EDIT_PROJECTION = 'SET_CAN_EDIT_PROJECTION';
1112

1213
export function setInputValue(value) {
1314
return {
@@ -22,3 +23,10 @@ export function setProjectionsConfig(config) {
2223
config
2324
};
2425
}
26+
27+
export function setCanEditProjection(canEdit) {
28+
return {
29+
type: SET_CAN_EDIT_PROJECTION,
30+
canEdit
31+
};
32+
}

web/client/configs/pluginsConfig.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,10 @@
349349
"description": "plugins.CRSSelector.description",
350350
"dependencies": ["MapFooter"],
351351
"defaultConfig": {
352-
"additionalCRS": {},
353-
"filterAllowedCRS": ["EPSG:4326", "EPSG:3857"],
352+
"availableProjections": [
353+
{ "value": "EPSG:4326", "label": "EPSG:4326" },
354+
{ "value": "EPSG:3857", "label": "EPSG:3857" }
355+
],
354356
"allowedRoles": ["ADMIN"]
355357
}
356358
},

web/client/plugins/CRSSelector.jsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ import crsselectorReducers from '../reducers/crsselector';
2222
import annotationsReducers from './Annotations/reducers/annotations';
2323
import { editingSelector } from '../plugins/Annotations/selectors/annotations';
2424
import { measureSelector, printSelector, queryPanelSelector } from '../selectors/controls';
25-
import { crsInputValueSelector, crsProjectionsConfigSelector } from '../selectors/crsselector';
25+
import { canEditProjectionSelector, crsInputValueSelector, crsProjectionsConfigSelector } from '../selectors/crsselector';
2626
import { modeSelector } from '../selectors/featuregrid';
2727
import { currentBackgroundSelector } from '../selectors/layers';
2828
import { projectionDefsSelector, projectionSelector } from '../selectors/map';
2929
import { bottomPanelOpenSelector } from '../selectors/maplayout';
3030
import { isCesium } from '../selectors/maptype';
31-
import { isLoggedIn, userRoleSelector } from '../selectors/security';
31+
import { userRoleSelector } from '../selectors/security';
3232
import { getAvailableCRS, normalizeSRS } from '../utils/CoordinatesUtils';
3333
import { getAvailableProjectionsFromConfig } from '../utils/ProjectionUtils';
3434
import ButtonRB from '../components/misc/Button';
@@ -60,9 +60,9 @@ const Selector = ({
6060
currentRole,
6161
projectionsConfig = {},
6262
setConfig = () => {},
63-
userLoggedIn,
6463
currentBackground,
65-
onError = () => {}
64+
onError = () => {},
65+
canEditProjection = true
6666
}) => {
6767
const [toggled, setToggled] = useState(false);
6868
const [openAvailableProjections, setOpenAvailableProjections] = useState(false);
@@ -113,8 +113,7 @@ const Selector = ({
113113
return normalizeSRS(selected, availableProjections.map(p => p.value));
114114
}, [availableProjections, selected]);
115115

116-
const isAllowedToChange = includes(allowedRoles, "ALL") || includes(allowedRoles, currentRole);
117-
const isAllowedToSwitch = userLoggedIn;
116+
const isAllowedToSwitch = includes(allowedRoles, "ALL") || includes(allowedRoles, currentRole);
118117

119118
if (!enabled) {
120119
return null;
@@ -180,7 +179,7 @@ const Selector = ({
180179
</Dropdown>
181180
</div>
182181
</FlexBox>
183-
{isAllowedToChange && (
182+
{isAllowedToSwitch && canEditProjection && (
184183
<>
185184
<Button
186185
bsStyle="link"
@@ -224,7 +223,7 @@ Selector.propTypes = {
224223
availableProjections: PropTypes.array,
225224
projectionsConfig: PropTypes.object,
226225
setConfig: PropTypes.func,
227-
userLoggedIn: PropTypes.bool
226+
canEditProjection: PropTypes.bool
228227
};
229228

230229
const crsSelector = connect(
@@ -242,16 +241,16 @@ const crsSelector = connect(
242241
printSelector,
243242
editingSelector,
244243
crsProjectionsConfigSelector,
245-
isLoggedIn,
246-
( currentRole, currentBackground, selected, projectionDefs, value, mode, cesium, bottomPanel, measureEnabled, queryPanelEnabled, printEnabled, editingAnnotations, projectionsConfig, userLoggedIn) => ({
244+
canEditProjectionSelector,
245+
( currentRole, currentBackground, selected, projectionDefs, value, mode, cesium, bottomPanel, measureEnabled, queryPanelEnabled, printEnabled, editingAnnotations, projectionsConfig, canEditProjection) => ({
247246
currentRole,
248247
currentBackground,
249248
selected,
250249
projectionDefs,
251250
value,
252251
enabled: (mode !== 'EDIT') && !cesium && !bottomPanel && !measureEnabled && !queryPanelEnabled && !printEnabled && !editingAnnotations,
253252
projectionsConfig,
254-
userLoggedIn
253+
canEditProjection
255254
})
256255
), {
257256
typeInput: setInputValue,

web/client/plugins/__tests__/CRSSelector-test.jsx

Lines changed: 104 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import { getPluginForTest } from './pluginsTestUtils';
77
import security from '../../reducers/security';
88
import ReactTestUtils from 'react-dom/test-utils';
99

10+
const defaultAvailableProjections = [
11+
{ value: "EPSG:4326", label: "EPSG:4326" },
12+
{ value: "EPSG:3857", label: "EPSG:3857" }
13+
];
14+
1015
describe('CRSSelector Plugin', () => {
1116
beforeEach((done) => {
1217
document.body.innerHTML = '<div id="container"></div>';
@@ -42,7 +47,7 @@ describe('CRSSelector Plugin', () => {
4247
}
4348
});
4449

45-
ReactDOM.render(<Plugin filterAllowedCRS={["EPSG:4326", "EPSG:3857"]} additionalCRS={{}}/>, document.getElementById("container"));
50+
ReactDOM.render(<Plugin pluginCfg={{ availableProjections: defaultAvailableProjections }}/>, document.getElementById("container"));
4651
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(1);
4752
});
4853
it('render the plugin for role ADMIN with allowedRoles ADMIN, USER', () => {
@@ -61,9 +66,7 @@ describe('CRSSelector Plugin', () => {
6166
});
6267

6368
ReactDOM.render(<Plugin
64-
filterAllowedCRS={["EPSG:4326", "EPSG:3857"]}
65-
additionalCRS={{}}
66-
allowedRoles={["ADMIN", "USER"]}
69+
pluginCfg={{ availableProjections: defaultAvailableProjections, allowedRoles: ["ADMIN", "USER"] }}
6770
/>, document.getElementById("container"));
6871
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(1);
6972
});
@@ -83,9 +86,7 @@ describe('CRSSelector Plugin', () => {
8386
});
8487

8588
ReactDOM.render(<Plugin
86-
filterAllowedCRS={["EPSG:4326", "EPSG:3857"]}
87-
additionalCRS={{}}
88-
allowedRoles={["ADMIN", "USER"]}
89+
pluginCfg={{ availableProjections: defaultAvailableProjections, allowedRoles: ["ADMIN", "USER"] }}
8990
/>, document.getElementById("container"));
9091
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(1);
9192
});
@@ -105,9 +106,7 @@ describe('CRSSelector Plugin', () => {
105106
});
106107

107108
ReactDOM.render(<Plugin
108-
filterAllowedCRS={["EPSG:4326", "EPSG:3857"]}
109-
additionalCRS={{}}
110-
allowedRoles={["ALL"]}
109+
pluginCfg={{ availableProjections: defaultAvailableProjections, allowedRoles: ["ALL"] }}
111110
/>, document.getElementById("container"));
112111
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(1);
113112
});
@@ -127,16 +126,41 @@ describe('CRSSelector Plugin', () => {
127126
});
128127

129128
ReactDOM.render(<Plugin
130-
filterAllowedCRS={["EPSG:4326", "EPSG:3857"]}
131-
additionalCRS={{}}
132-
allowedRoles={["ADMIN", "USER"]}
129+
pluginCfg={{ availableProjections: defaultAvailableProjections, allowedRoles: ["ADMIN", "USER"] }}
133130
/>, document.getElementById("container"));
134131
// plugin is rendered because user is logged in
135132
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(1);
136133
// settings (cog) button is hidden because role is not allowed
137134
expect(document.getElementsByClassName('ms-crs-settings-button').length).toBe(0);
138135
});
139136

137+
it('renders with deprecated filterAllowedCRS and additionalCRS for backward compatibility', () => {
138+
const { Plugin } = getPluginForTest(CRSPluginCustomized, {
139+
map: {
140+
projection: "EPSG:900913"
141+
},
142+
localConfig: {
143+
projectionDefs: []
144+
},
145+
security: {
146+
user: {
147+
role: "USER"
148+
}
149+
}
150+
});
151+
152+
ReactDOM.render(<Plugin
153+
pluginCfg={{
154+
filterAllowedCRS: ["EPSG:4326", "EPSG:3857"],
155+
additionalCRS: { "EPSG:3003": { "label": "EPSG:3003" } },
156+
allowedRoles: ["ALL"]
157+
}}
158+
/>, document.getElementById("container"));
159+
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(1);
160+
// Projections from filterAllowedCRS and additionalCRS should be available
161+
const dropdown = document.querySelector('.ms-crs-dropdown');
162+
expect(dropdown).toExist();
163+
});
140164

141165
it('CRSSelector is not rendered when Print Panel is enabled', () => {
142166
const { Plugin } = getPluginForTest(CRSPluginCustomized, {
@@ -158,7 +182,7 @@ describe('CRSSelector Plugin', () => {
158182
}
159183
});
160184

161-
ReactDOM.render(<Plugin filterAllowedCRS={["EPSG:4326", "EPSG:3857"]} additionalCRS={{}}/>, document.getElementById("container"));
185+
ReactDOM.render(<Plugin pluginCfg={{ availableProjections: defaultAvailableProjections }}/>, document.getElementById("container"));
162186
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(0);
163187
});
164188

@@ -182,7 +206,7 @@ describe('CRSSelector Plugin', () => {
182206
}
183207
});
184208

185-
ReactDOM.render(<Plugin filterAllowedCRS={["EPSG:4326", "EPSG:3857"]} additionalCRS={{}}/>, document.getElementById("container"));
209+
ReactDOM.render(<Plugin pluginCfg={{ availableProjections: defaultAvailableProjections }}/>, document.getElementById("container"));
186210
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(0);
187211
});
188212

@@ -206,7 +230,7 @@ describe('CRSSelector Plugin', () => {
206230
}
207231
});
208232

209-
ReactDOM.render(<Plugin filterAllowedCRS={["EPSG:4326", "EPSG:3857"]} additionalCRS={{}}/>, document.getElementById("container"));
233+
ReactDOM.render(<Plugin pluginCfg={{ availableProjections: defaultAvailableProjections }}/>, document.getElementById("container"));
210234
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(0);
211235
});
212236

@@ -228,7 +252,7 @@ describe('CRSSelector Plugin', () => {
228252
}
229253
});
230254

231-
ReactDOM.render(<Plugin filterAllowedCRS={["EPSG:4326", "EPSG:3857"]} additionalCRS={{}}/>, document.getElementById("container"));
255+
ReactDOM.render(<Plugin pluginCfg={{ availableProjections: defaultAvailableProjections }}/>, document.getElementById("container"));
232256
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(0);
233257
});
234258

@@ -264,13 +288,13 @@ describe('CRSSelector Plugin', () => {
264288

265289
ReactDOM.render(<Plugin
266290
pluginCfg={{
267-
onError: actions.onError
268-
}}
269-
filterAllowedCRS={["EPSG:4326", "EPSG:3857"]}
270-
additionalCRS={{
271-
"EPSG:3003": { "label": "EPSG:3003" }
291+
onError: actions.onError,
292+
availableProjections: [
293+
...defaultAvailableProjections,
294+
{ value: "EPSG:3003", label: "EPSG:3003" }
295+
],
296+
allowedRoles: ["ALL"]
272297
}}
273-
allowedRoles={["ALL"]}
274298
/>, document.getElementById("container"));
275299
const dropdown = document.querySelector('.ms-crs-dropdown');
276300
expect(dropdown).toExist();
@@ -323,13 +347,13 @@ describe('CRSSelector Plugin', () => {
323347
ReactDOM.render(<Plugin
324348
pluginCfg={{
325349
onError: actions.onError,
326-
setCrs: actions.setCrs
327-
}}
328-
filterAllowedCRS={["EPSG:4326", "EPSG:3857"]}
329-
additionalCRS={{
330-
"EPSG:3003": { "label": "EPSG:3003" }
350+
setCrs: actions.setCrs,
351+
availableProjections: [
352+
...defaultAvailableProjections,
353+
{ value: "EPSG:3003", label: "EPSG:3003" }
354+
],
355+
allowedRoles: ["ALL"]
331356
}}
332-
allowedRoles={["ALL"]}
333357
/>, document.getElementById("container"));
334358
const dropdown = document.querySelector('.ms-crs-dropdown');
335359
expect(dropdown).toExist();
@@ -350,4 +374,55 @@ describe('CRSSelector Plugin', () => {
350374
}
351375
}, 100);
352376
});
377+
378+
it('does not show settings when canEditProjection is false', () => {
379+
const { Plugin } = getPluginForTest(CRSPluginCustomized, {
380+
map: {
381+
projection: "EPSG:900913"
382+
},
383+
localConfig: {
384+
projectionDefs: []
385+
},
386+
security: {
387+
user: {
388+
role: "ADMIN"
389+
}
390+
},
391+
crsselector: {
392+
canEdit: false
393+
}
394+
});
395+
396+
ReactDOM.render(<Plugin
397+
pluginCfg={{ availableProjections: defaultAvailableProjections, allowedRoles: ["ADMIN", "USER"] }}
398+
/>, document.getElementById("container"));
399+
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(1);
400+
// settings (cog) button is hidden because canEditProjection is false
401+
expect(document.getElementsByClassName('ms-crs-settings-button').length).toBe(0);
402+
});
403+
404+
it('shows settings when canEditProjection is true and user has allowed role', () => {
405+
const { Plugin } = getPluginForTest(CRSPluginCustomized, {
406+
map: {
407+
projection: "EPSG:900913"
408+
},
409+
localConfig: {
410+
projectionDefs: []
411+
},
412+
security: {
413+
user: {
414+
role: "ADMIN"
415+
}
416+
},
417+
crsselector: {
418+
canEdit: true
419+
}
420+
});
421+
422+
ReactDOM.render(<Plugin
423+
pluginCfg={{ availableProjections: defaultAvailableProjections, allowedRoles: ["ADMIN", "USER"] }}
424+
/>, document.getElementById("container"));
425+
expect(document.getElementsByClassName('ms-crs-selector-container').length).toBe(1);
426+
expect(document.getElementsByClassName('ms-crs-settings-button').length).toBe(1);
427+
});
353428
});

web/client/plugins/mapEditor/DefaultConfiguration.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,9 @@ export default (overridePluginsConfig = []) => {
103103
}, {
104104
"name": "CRSSelector",
105105
"cfg": {
106-
"additionalCRS": {
107-
108-
},
109-
"filterAllowedCRS": [
110-
"EPSG:4326",
111-
"EPSG:3857"
106+
"availableProjections": [
107+
{ "value": "EPSG:4326", "label": "EPSG:4326" },
108+
{ "value": "EPSG:3857", "label": "EPSG:3857" }
112109
],
113110
"allowedRoles": [
114111
"ADMIN"

web/client/reducers/crsselector.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { CHANGE_CRS_INPUT_VALUE, SET_PROJECTIONS_CONFIG } from '../actions/crsselector';
2-
function crsselector(state = {projections: [], config: {}}, action) {
1+
import { CHANGE_CRS_INPUT_VALUE, SET_CAN_EDIT_PROJECTION, SET_PROJECTIONS_CONFIG } from '../actions/crsselector';
2+
function crsselector(state = {projections: [], config: {}, canEdit: undefined}, action) {
33
switch (action.type) {
44
case CHANGE_CRS_INPUT_VALUE:
55
return Object.assign({}, state, {
@@ -9,6 +9,10 @@ function crsselector(state = {projections: [], config: {}}, action) {
99
return Object.assign({}, state, {
1010
config: { ...state.config, ...action.config }
1111
});
12+
case SET_CAN_EDIT_PROJECTION:
13+
return Object.assign({}, state, {
14+
canEdit: action.canEdit
15+
});
1216
default:
1317
return state;
1418
}

0 commit comments

Comments
 (0)