Skip to content

Commit 465b70e

Browse files
Fix #2373 Support for CRS selector (#2376)
1 parent 66368e0 commit 465b70e

File tree

6 files changed

+158
-12
lines changed

6 files changed

+158
-12
lines changed

geonode_mapstore_client/client/js/epics/__tests__/gnresource-test.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import {
1717
gnZoomToFitBounds,
1818
closeResourceDetailsOnMapInfoOpen,
1919
gnUpdateResourceExtent,
20-
gnUpdateBackgroundEditEpic
20+
gnUpdateBackgroundEditEpic,
21+
gnUpdateEditProjectionEpic
2122
} from '@js/epics/gnresource';
2223
import { SAVE_SUCCESS } from '@mapstore/framework/actions/featuregrid';
2324
import {
@@ -35,6 +36,7 @@ import {
3536
import { newMapInfoRequest } from '@mapstore/framework/actions/mapInfo';
3637
import { SET_SHOW_DETAILS } from '@mapstore/framework/plugins/ResourcesCatalog/actions/resources';
3738
import { CREATE_BACKGROUNDS_LIST } from '@mapstore/framework/actions/backgroundselector';
39+
import { MAP_CONFIG_LOADED } from '@mapstore/framework/actions/config';
3840

3941
let mockAxios;
4042

@@ -377,4 +379,33 @@ describe('gnresource epics', () => {
377379
testState
378380
);
379381
});
382+
it('should set canEdit projection false for MAP resource without change_resourcebase permission', (done) => {
383+
const NUM_ACTIONS = 1;
384+
const testState = {
385+
gnresource: {
386+
type: "map",
387+
data: {
388+
pk: 1,
389+
title: 'Test Map',
390+
perms: ['view_resourcebase']
391+
}
392+
}
393+
};
394+
395+
testEpic(
396+
gnUpdateEditProjectionEpic,
397+
NUM_ACTIONS,
398+
{ type: MAP_CONFIG_LOADED },
399+
(actions) => {
400+
try {
401+
expect(actions.length).toBe(1);
402+
expect(actions[0].canEdit).toBe(false);
403+
} catch (e) {
404+
done(e);
405+
}
406+
done();
407+
},
408+
testState
409+
);
410+
});
380411
});

geonode_mapstore_client/client/js/epics/gnresource.js

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
deleteResourceThumbnail,
3434
updateResourceExtent
3535
} from '@js/api/geonode/v2';
36-
import { configureMap } from '@mapstore/framework/actions/config';
36+
import { configureMap, MAP_CONFIG_LOADED } from '@mapstore/framework/actions/config';
3737
import { isMapInfoOpen } from '@mapstore/framework/selectors/mapInfo';
3838
import { isLoggedIn, userSelector } from '@mapstore/framework/selectors/security';
3939
import {
@@ -71,7 +71,8 @@ import {
7171
setSelectedLayer,
7272
UPDATE_RESOURCE_EXTENT,
7373
updateResourceExtentLoading,
74-
setDatasetEditPermissionsError
74+
setDatasetEditPermissionsError,
75+
LOADING_RESOURCE_CONFIG
7576
} from '@js/actions/gnresource';
7677

7778
import {
@@ -98,7 +99,8 @@ import {
9899
toMapStoreMapConfig,
99100
getCataloguePath,
100101
isDefaultDatasetSubtype,
101-
resourceHasPermission
102+
resourceHasPermission,
103+
canEditMap
102104
} from '@js/utils/ResourceUtils';
103105
import {
104106
canAddResource,
@@ -111,7 +113,7 @@ import {
111113
import { updateAdditionalLayer } from '@mapstore/framework/actions/additionallayers';
112114
import { STYLE_OWNER_NAME } from '@mapstore/framework/utils/StyleEditorUtils';
113115
import { initStyleService, resetStyleEditor } from '@mapstore/framework/actions/styleeditor';
114-
import { CLICK_ON_MAP, resizeMap, CHANGE_MAP_VIEW, zoomToExtent } from '@mapstore/framework/actions/map';
116+
import { CLICK_ON_MAP, resizeMap, CHANGE_MAP_VIEW, zoomToExtent, changeCRS } from '@mapstore/framework/actions/map';
115117
import { purgeMapInfoResults, closeIdentify, NEW_MAPINFO_REQUEST } from '@mapstore/framework/actions/mapInfo';
116118
import { saveError } from '@js/actions/gnsave';
117119
import {
@@ -140,6 +142,7 @@ import { forceUpdateMapLayout } from '@mapstore/framework/actions/maplayout';
140142
import { getShowDetails } from '@mapstore/framework/plugins/ResourcesCatalog/selectors/resources';
141143
import { searchSelector } from '@mapstore/framework/selectors/router';
142144
import { CREATE_BACKGROUNDS_LIST, allowBackgroundsDeletion } from '@mapstore/framework/actions/backgroundselector';
145+
import { setCanEditProjection, setProjectionsConfig } from '@mapstore/framework/actions/crsselector';
143146

144147
const FIT_BOUNDS_CONTROL = 'fitBounds';
145148

@@ -221,7 +224,9 @@ const resourceTypes = {
221224
: []),
222225
...(newLayer?.bboxError
223226
? [warningNotification({ title: "gnviewer.invalidBbox", message: "gnviewer.invalidBboxMsg" })]
224-
: [])
227+
: []),
228+
...(gnLayer?.data?.crsSelector ? [changeCRS(gnLayer?.data?.crsSelector?.currentProjection)] : []),
229+
...(gnLayer?.data?.crsSelector ? [setProjectionsConfig(gnLayer?.data?.crsSelector)] : [])
225230
);
226231
});
227232
}
@@ -890,15 +895,21 @@ export const gnUpdateBackgroundEditEpic = (action$, store) =>
890895
action$.ofType(CREATE_BACKGROUNDS_LIST)
891896
.switchMap(() => {
892897
const state = store.getState();
893-
const resource = state.gnresource?.data || {};
894-
const resourceType = state.gnresource?.type;
895-
const canEdit = resourceType === ResourceTypes.MAP && resource?.perms?.includes('change_resourcebase') ? true : false;
898+
const canEdit = canEditMap(state.gnresource);
896899
return Observable.of(
897900
setResourceContext({ canEdit }),
898901
...(canEdit ? [allowBackgroundsDeletion(true)] : [])
899902
);
900903
});
901904

905+
export const gnUpdateEditProjectionEpic = (action$, store) =>
906+
action$.ofType(MAP_CONFIG_LOADED, LOADING_RESOURCE_CONFIG)
907+
.switchMap(() => {
908+
const state = store.getState();
909+
const canEdit = canEditMap(state.gnresource, { isNewCheck: true, resourceTypes: [ResourceTypes.MAP, ResourceTypes.DATASET] });
910+
return Observable.of(setCanEditProjection(canEdit));
911+
});
912+
902913
export default {
903914
gnViewerRequestNewResourceConfig,
904915
gnViewerRequestResourceConfig,
@@ -911,5 +922,6 @@ export default {
911922
gnManageLinkedResource,
912923
gnZoomToFitBounds,
913924
gnSelectResourceEpic,
914-
gnUpdateResourceExtent
925+
gnUpdateResourceExtent,
926+
gnUpdateEditProjectionEpic
915927
};

geonode_mapstore_client/client/js/selectors/resource.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,13 @@ export const getDataPayload = (state, resourceType) => {
179179
const selectedLayer = getSelectedNode(state);
180180
const omitKeys = ['extendedParams', 'availableStyles', 'infoFormats', 'style'];
181181
const data = saveLayer(selectedLayer ?? {});
182+
const crsSelector = state?.crsselector?.config;
183+
const currentProjection = mapSelector(state)?.projection;
182184
return omit({
183185
...data,
184186
...currentLayerSettings,
185-
...(selectedLayer && {fields: selectedLayer?.fields ?? {}})
187+
...(selectedLayer && {fields: selectedLayer?.fields ?? {}}),
188+
...(crsSelector && {crsSelector: {...crsSelector, currentProjection}})
186189
}, omitKeys);
187190
}
188191
default:

geonode_mapstore_client/client/js/utils/ResourceUtils.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,3 +993,16 @@ export const canAccessPermissions = (resource) => {
993993
const { perms } = resource || {};
994994
return perms?.includes('change_resourcebase_permissions');
995995
};
996+
997+
/**
998+
* Check if the resource can be edited (for map resources)
999+
* @param {Object} gnresource - The state of the resource
1000+
* @param {Object} options - The options for the check
1001+
* @param {boolean} options.isNewCheck - True if the check should be done for a new resource, false otherwise
1002+
* @returns {boolean} - True if the resource can be edited, false otherwise
1003+
*/
1004+
export const canEditMap = (gnresource, { resourceTypes = [ResourceTypes.MAP], isNewCheck = false } = {}) => {
1005+
const { data = {}, type, isNew } = gnresource;
1006+
const hasEditPermission = data?.perms?.includes('change_resourcebase');
1007+
return type && resourceTypes.includes(type) && (hasEditPermission || (isNewCheck && isNew)) ? true : false;
1008+
};

geonode_mapstore_client/client/js/utils/__tests__/ResourceUtils-test.js

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ import {
3737
canManageResourceOptions,
3838
canManageResourceSettings,
3939
canAccessPermissions,
40-
formatResourceLinkUrl
40+
formatResourceLinkUrl,
41+
canEditMap
4142
} from '../ResourceUtils';
4243

4344
describe('Test Resource Utils', () => {
@@ -1118,4 +1119,72 @@ describe('Test Resource Utils', () => {
11181119
expect(formatResourceLinkUrl({ uuid: '123' })).toContain('/catalogue/uuid/123');
11191120
expect(formatResourceLinkUrl({ pk: '123' })).toNotContain('/catalogue/uuid/123');
11201121
});
1122+
describe('canEditMap', () => {
1123+
it('existing map with edit permission', () => {
1124+
const gnresourceState = {
1125+
type: ResourceTypes.MAP,
1126+
data: {
1127+
perms: ['view_resourcebase', 'change_resourcebase']
1128+
},
1129+
isNew: false
1130+
};
1131+
const result = canEditMap(gnresourceState, { isNewCheck: false });
1132+
expect(result).toBeTruthy();
1133+
});
1134+
it('new map without edit permission but marked as new', () => {
1135+
const gnresourceState = {
1136+
type: ResourceTypes.MAP,
1137+
data: {
1138+
perms: ['view_resourcebase']
1139+
},
1140+
isNew: true
1141+
};
1142+
const result = canEditMap(gnresourceState, { isNewCheck: true });
1143+
expect(result).toBeTruthy();
1144+
});
1145+
it('map without edit permission and not new', () => {
1146+
const gnresourceState = {
1147+
type: ResourceTypes.MAP,
1148+
data: {
1149+
perms: ['view_resourcebase']
1150+
},
1151+
isNew: false
1152+
};
1153+
const result = canEditMap(gnresourceState, { isNewCheck: false });
1154+
expect(result).toBeFalsy();
1155+
});
1156+
it('non map type should not be editable when only MAP is allowed', () => {
1157+
const gnresourceState = {
1158+
type: ResourceTypes.DATASET,
1159+
data: {
1160+
perms: ['change_resourcebase']
1161+
},
1162+
isNew: true
1163+
};
1164+
const result = canEditMap(gnresourceState, { isNewCheck: true });
1165+
expect(result).toBeFalsy();
1166+
});
1167+
it('dataset should be editable when included in resourceType and has edit permission', () => {
1168+
const gnresourceState = {
1169+
type: ResourceTypes.DATASET,
1170+
data: {
1171+
perms: ['view_resourcebase', 'change_resourcebase']
1172+
},
1173+
isNew: false
1174+
};
1175+
const result = canEditMap(gnresourceState, { isNewCheck: false, resourceTypes: [ResourceTypes.MAP, ResourceTypes.DATASET] });
1176+
expect(result).toBeTruthy();
1177+
});
1178+
it('new dataset without edit permission but marked as new and included in resourceType', () => {
1179+
const gnresourceState = {
1180+
type: ResourceTypes.DATASET,
1181+
data: {
1182+
perms: ['view_resourcebase']
1183+
},
1184+
isNew: true
1185+
};
1186+
const result = canEditMap(gnresourceState, { isNewCheck: true, resourceTypes: [ResourceTypes.MAP, ResourceTypes.DATASET] });
1187+
expect(result).toBeTruthy();
1188+
});
1189+
});
11211190
});

geonode_mapstore_client/static/mapstore/configs/localConfig.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,15 @@
12561256
"cfg": {
12571257
"containerPosition": "header"
12581258
}
1259+
},
1260+
{
1261+
"name": "CRSSelector",
1262+
"cfg": {
1263+
"availableProjections": [
1264+
{ "value": "EPSG:4326", "label": "EPSG:4326" },
1265+
{ "value": "EPSG:3857", "label": "EPSG:3857" }
1266+
]
1267+
}
12591268
}
12601269
],
12611270
"dataset_edit_data_viewer": [
@@ -2272,6 +2281,15 @@
22722281
}
22732282
]
22742283
}
2284+
},
2285+
{
2286+
"name": "CRSSelector",
2287+
"cfg": {
2288+
"availableProjections": [
2289+
{ "value": "EPSG:4326", "label": "EPSG:4326" },
2290+
{ "value": "EPSG:3857", "label": "EPSG:3857" }
2291+
]
2292+
}
22752293
}
22762294
],
22772295
"geostory_viewer": [

0 commit comments

Comments
 (0)