Skip to content

Commit f825b9d

Browse files
committed
#1947: Harmonization of download actions inside the GeoNode client
1 parent c0f8177 commit f825b9d

23 files changed

+196
-28
lines changed

geonode_mapstore_client/client/js/components/Menu/DropdownList.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ const itemElement = ({ labelId, href, badge, target }) => (
2828

2929
const itemsList = (items) => (items && items.map((item) => {
3030

31-
const { labelId, href, badge, target, type, Component, className } = item;
31+
const { labelId, href, badge, target, type, Component, className, cfg } = item;
3232

3333
if (type === 'plugin' && Component) {
34-
return (<li><Component variant="default" className={className} showMessage /></li>);
34+
return (<li><Component {...cfg} variant="default" className={className} showMessage /></li>);
3535
}
3636

3737
return itemElement({ labelId, href, badge, target });
@@ -97,7 +97,7 @@ const DropdownList = ({
9797
.map((itm, idx) => {
9898

9999
if (itm.type === 'plugin' && itm.Component) {
100-
return (<li><itm.Component variant="default" className={itm.className} showMessage /></li>);
100+
return (<li><itm.Component {...itm.cfg} variant="default" className={itm.className} showMessage /></li>);
101101
}
102102
if (itm.type === 'divider') {
103103
return <Dropdown.Divider key={idx} />;

geonode_mapstore_client/client/js/epics/index.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -68,23 +68,23 @@ export const gnFetchMissingLayerData = (action$, { getState } = {}) =>
6868
const layerResourceId = layer?.extendedParams?.pk;
6969
const layerResourceDataset = state.gnresource.data?.maplayers?.find(mapLayer => mapLayer.dataset?.pk === parseInt(layerResourceId, 10))?.dataset;
7070
return layerResourceDataset
71-
? isEmpty(layerResourceDataset?.linkedResources)
72-
? Rx.Observable.defer(() =>
73-
getDatasetByPk(layerResourceId)
74-
.then((layerDataset) => layerDataset)
75-
.catch(() => [])
76-
).switchMap((layerDataset) =>
77-
Rx.Observable.of(
78-
updateLayerDataset(layerDataset),
71+
? isEmpty(layerResourceDataset?.linkedResources)
72+
? Rx.Observable.defer(() =>
73+
getDatasetByPk(layerResourceId)
74+
.then((layerDataset) => layerDataset)
75+
.catch(() => [])
76+
).switchMap((layerDataset) =>
77+
Rx.Observable.of(
78+
updateLayerDataset(layerDataset),
79+
setLayerDataset(layerResourceId)
80+
)
81+
).startWith(setLayerDataset())
82+
: Rx.Observable.of(
7983
setLayerDataset(layerResourceId)
8084
)
81-
)
8285
: Rx.Observable.of(
83-
setLayerDataset(layerResourceId)
84-
)
85-
: Rx.Observable.of(
86-
setLayerDataset()
87-
)
86+
setLayerDataset()
87+
);
8888
});
8989

9090

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2025, GeoSolutions Sas.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
import React from "react";
9+
import { connect } from 'react-redux';
10+
import { createSelector } from 'reselect';
11+
12+
import { createPlugin } from "@mapstore/framework/utils/PluginsUtils";
13+
14+
import Dropdown from '@js/components/Dropdown';
15+
import FaIcon from '@js/components/FaIcon/FaIcon';
16+
import { getSelectedLayerDataset } from '@js/selectors/resource';
17+
import { SOURCE_TYPES } from "@js/utils/ResourceUtils";
18+
19+
const DatasetDownload = connect(
20+
createSelector([
21+
getSelectedLayerDataset
22+
], (resource) => ({
23+
resource
24+
}))
25+
)(({
26+
items,
27+
selectedNodes,
28+
status,
29+
statusTypes,
30+
resource, // the selected layer's dataset resource
31+
allowedSources = [SOURCE_TYPES.LOCAL] // allowed sources for download
32+
}) => {
33+
const toolbarItems = items?.filter(item => item.target === "toolbar");
34+
const layer = selectedNodes?.[0]?.node;
35+
if ([statusTypes?.LAYER].includes(status) && layer && !layer?.error && toolbarItems.length > 0) {
36+
return (
37+
<div className="gn-layer-download">
38+
<Dropdown className={"download-dropdown"}>
39+
<Dropdown.Toggle
40+
id={ `gn-toggle-dropdown-layer-download`}
41+
bsStyle={"primary"}
42+
noCaret
43+
>
44+
<FaIcon name={"download"} />
45+
</Dropdown.Toggle>
46+
<Dropdown.Menu>
47+
{toolbarItems.map(({Component, name}, idx) => (
48+
<Dropdown.Item eventKey={idx}>
49+
<Component
50+
key={`${name}-item-${idx}`}
51+
resource={resource}
52+
allowedSources={allowedSources}
53+
variant="default"
54+
/>
55+
</Dropdown.Item>
56+
))}
57+
</Dropdown.Menu>
58+
</Dropdown>
59+
</div>
60+
);
61+
}
62+
return null;
63+
});
64+
65+
/**
66+
* Plugin for downloading the dataset resource
67+
* via direct download or export the data associated
68+
* @name DatasetDownload
69+
* @example
70+
* {
71+
* "name": "DatasetDownload"
72+
* }
73+
*/
74+
export default createPlugin('DatasetDownload', {
75+
component: DatasetDownload,
76+
containers: {
77+
TOC: {
78+
doNotHide: true,
79+
name: "DatasetDownload",
80+
target: 'toolbar',
81+
Component: DatasetDownload,
82+
position: 11
83+
}
84+
},
85+
epics: {},
86+
reducers: {}
87+
});

geonode_mapstore_client/client/js/plugins/DownloadResource.jsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { connect } from 'react-redux';
1111
import { createSelector } from 'reselect';
1212
import isEmpty from 'lodash/isEmpty';
1313
import { createPlugin } from '@mapstore/framework/utils/PluginsUtils';
14-
import { getDownloadUrlInfo, isDocumentExternalSource, GXP_PTYPES } from '@js/utils/ResourceUtils';
14+
import { getDownloadUrlInfo, isDocumentExternalSource, GXP_PTYPES, SOURCE_TYPES } from '@js/utils/ResourceUtils';
1515
import Message from '@mapstore/framework/components/I18N/Message';
1616
import Button from '@js/components/Button';
1717
import tooltip from '@mapstore/framework/components/misc/enhancers/tooltip';
@@ -38,6 +38,8 @@ const DownloadButton = ({
3838
onAction = () => {},
3939
renderType = "button",
4040
showIcon,
41+
downloadMsgId = "gnviewer.download",
42+
allowedSources = [SOURCE_TYPES.LOCAL, SOURCE_TYPES.REMOTE],
4143
downloading
4244
}) => {
4345
const Component = RENDER_TYPE[renderType];
@@ -51,6 +53,7 @@ const DownloadButton = ({
5153
|| !_resource?.perms?.includes('download_resourcebase')
5254
|| (!isButton && isNotAjaxSafe)
5355
|| [GXP_PTYPES.REST_MAP, GXP_PTYPES.REST_IMG].includes(_resource?.ptype) // exclude arcgis remote layers from direct download
56+
|| !allowedSources.includes(_resource?.sourcetype)
5457
) {
5558
return null;
5659
}
@@ -59,15 +62,15 @@ const DownloadButton = ({
5962
return downloadInfo.url ? (
6063
<Component
6164
{...isButton && { variant, size }}
62-
{...showIcon && { tooltipId: "gnviewer.download" }}
65+
{...showIcon && { tooltipId: downloadMsgId }}
6366
download
6467
href={ downloadInfo.url }
6568
target="_blank"
6669
rel="noopener noreferrer"
6770
>
6871
{showIcon
6972
? <FaIcon name={isExternal ? "external-link" : "download"} />
70-
: <Message msgId="gnviewer.download" />
73+
: <Message msgId={downloadMsgId} />
7174
}
7275
</Component>
7376
) : null;
@@ -78,11 +81,11 @@ const DownloadButton = ({
7881
disabled={!!downloading}
7982
onClick={() => downloading ? null : onAction(_resource)}
8083
{...isButton && { variant, size}}
81-
{...showIcon && { tooltipId: "gnviewer.download" }}
84+
{...showIcon && { tooltipId: downloadMsgId }}
8285
>
8386
{showIcon
8487
? <FaIcon name="download" />
85-
: <Message msgId="gnviewer.download" />
88+
: <Message msgId={downloadMsgId} />
8689
}
8790
</Component>
8891
);
@@ -133,6 +136,12 @@ export default createPlugin('DownloadResource', {
133136
target: 'toolbar',
134137
Component: DownloadResource,
135138
priority: 1
139+
},
140+
DatasetDownload: {
141+
name: 'DownloadResource',
142+
target: 'toolbar',
143+
Component: DownloadResource,
144+
priority: 1
136145
}
137146
},
138147
epics: {},

geonode_mapstore_client/client/js/plugins/VisualStyleEditor.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import tooltip from '@mapstore/framework/components/misc/enhancers/tooltip';
5252
import { getSelectedLayer, layersSelector } from '@mapstore/framework/selectors/layers';
5353
import useLocalStorage from '@js/hooks/useLocalStorage';
5454
import TemplateSelector from '@js/plugins/visualstyleeditor/TemplateSelector';
55-
import { isDefaultDatasetSubtype } from '@js/utils/ResourceUtils';
55+
import { isDefaultDatasetSubtype, SOURCE_TYPES } from '@js/utils/ResourceUtils';
5656

5757
const Button = tooltip(GNButton);
5858

@@ -318,7 +318,7 @@ function StyleEditorTocButton({
318318
if (hide
319319
|| status !== statusTypes.LAYER
320320
|| !mapLayer?.dataset
321-
|| mapLayer?.dataset?.sourcetype === 'REMOTE'
321+
|| mapLayer?.dataset?.sourcetype === SOURCE_TYPES.REMOTE
322322
|| !changeResource
323323
|| isNew
324324
|| !isDefaultDatasetSubtype(mapLayer?.dataset?.subtype)) {

geonode_mapstore_client/client/js/plugins/actionnavbar/buttons.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export const LayerDownloadActionButton = connect(
7878
size={size}
7979
onClick={() => onClick()}
8080
>
81-
<Message msgId="gnhome.dataset" />
81+
<Message msgId="gnviewer.exportData" />
8282
</Button>
8383
);
8484
});

geonode_mapstore_client/client/js/plugins/index.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,25 @@ export const plugins = {
6969
OperationPlugin,
7070
MetadataEditorPlugin,
7171
MetadataViewerPlugin,
72+
DatasetDownloadPlugin: toModulePlugin(
73+
'DatasetDownload',
74+
() => import(/* webpackChunkName: 'plugins/dataset-download' */ '@js/plugins/DatasetDownload')
75+
),
7276
LayerDownloadPlugin: toModulePlugin(
7377
'LayerDownload',
7478
() => import(/* webpackChunkName: 'plugins/layer-download' */ '@mapstore/framework/plugins/LayerDownload'),
7579
{
7680
overrides: {
7781
containers: {
82+
TOC: {},
7883
ActionNavbar: {
7984
name: 'LayerDownload',
8085
Component: LayerDownloadActionButton
86+
},
87+
DatasetDownload: {
88+
name: 'LayerDownload',
89+
Component: LayerDownloadActionButton,
90+
target: 'toolbar'
8191
}
8292
}
8393
}

geonode_mapstore_client/client/js/utils/ResourceUtils.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ export const isDefaultDatasetSubtype = (subtype) => !subtype || ['vector', 'rast
8484

8585
export const FEATURE_INFO_FORMAT = 'TEMPLATE';
8686

87+
export const SOURCE_TYPES = {
88+
LOCAL: 'LOCAL',
89+
REMOTE: 'REMOTE'
90+
};
91+
8792
const datasetAttributeSetToFields = ({ attribute_set: attributeSet = [] }) => {
8893
return attributeSet
8994
.filter(({ attribute_type: type }) => !type.includes('gml:'))
@@ -221,7 +226,7 @@ export const resourceToLayerConfig = (resource) => {
221226
...(dimensions.length > 0 && ({ dimensions })),
222227
extendedParams,
223228
...(fields && { fields }),
224-
...(sourcetype === 'REMOTE' && !wmsUrl.includes('/geoserver/') && {
229+
...(sourcetype === SOURCE_TYPES.REMOTE && !wmsUrl.includes('/geoserver/') && {
225230
serverType: ServerTypes.NO_VENDOR
226231
})
227232
};
@@ -327,7 +332,7 @@ export const ResourceTypes = {
327332
};
328333

329334
export const isDocumentExternalSource = (resource) => {
330-
return resource && resource.resource_type === ResourceTypes.DOCUMENT && resource.sourcetype === 'REMOTE';
335+
return resource && resource.resource_type === ResourceTypes.DOCUMENT && resource.sourcetype === SOURCE_TYPES.REMOTE;
331336
};
332337

333338
export const getResourceTypesInfo = () => ({

geonode_mapstore_client/client/themes/geonode/less/_layer-settings.less

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,31 @@
6666
}
6767

6868
}
69+
70+
.ms-toc-container {
71+
.ms-toc-toolbar-content {
72+
.gn-layer-download {
73+
width: 30px;
74+
.download-dropdown {
75+
position: absolute;
76+
top: 0;
77+
width: inherit;
78+
button.dropdown-toggle{
79+
width: inherit;
80+
}
81+
ul.dropdown-menu {
82+
width: 120px;
83+
li {
84+
a[role="menuitem"] {
85+
padding: 0;
86+
}
87+
button {
88+
width: 100%;
89+
text-align: left;
90+
}
91+
}
92+
}
93+
}
94+
}
95+
}
96+
}

geonode_mapstore_client/static/mapstore/configs/localConfig.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,14 @@
629629
"disableIf": "{!state('selectedLayerPermissions').includes('download_resourcebase') || context.isDocumentExternalSource(state('gnResourceData'))}",
630630
"type": "dropdown",
631631
"items": [
632+
{
633+
"type": "plugin",
634+
"name": "DownloadResource",
635+
"cfg": {
636+
"downloadMsgId": "gnhome.dataset",
637+
"allowedSources": ["LOCAL"]
638+
}
639+
},
632640
{
633641
"type": "plugin",
634642
"name": "LayerDownload"
@@ -1610,6 +1618,14 @@
16101618
}
16111619
}
16121620
},
1621+
{
1622+
"name": "DatasetDownload",
1623+
"mandatory": true
1624+
},
1625+
{
1626+
"name": "DownloadResource",
1627+
"mandatory": true
1628+
},
16131629
{
16141630
"name": "MapThumbnail"
16151631
},

geonode_mapstore_client/static/mapstore/gn-translations/data.de-DE.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@
175175
},
176176
"gnviewer": {
177177
"edit": "Bearbeiten",
178-
"export": "Export",
178+
"export": "Exportieren",
179+
"exportData": "Daten exportieren",
179180
"editInfo": "Informationen anzeigen",
180181
"editMetadata": "Metadaten bearbeiten",
181182
"editStyle": "Stil bearbeiten",

geonode_mapstore_client/static/mapstore/gn-translations/data.en-US.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@
176176
"gnviewer": {
177177
"edit": "Edit",
178178
"export": "Export",
179+
"exportData": "Export Data",
179180
"editInfo": "Edit Info",
180181
"editMetadata": "Edit Metadata",
181182
"editStyle": "Edit Style",

geonode_mapstore_client/static/mapstore/gn-translations/data.es-ES.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@
176176
"gnviewer": {
177177
"edit": "Editar",
178178
"export": "Exportar",
179+
"exportData": "Exportar datos",
179180
"editInfo": "Editar información",
180181
"editMetadata": "Editar Metadatos",
181182
"editStyle": "Editar Estilo",

geonode_mapstore_client/static/mapstore/gn-translations/data.fi-FI.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@
174174
"gnviewer": {
175175
"edit": "Edit",
176176
"export": "Export",
177+
"exportData": "Export Data",
177178
"editInfo": "Edit Info",
178179
"editMetadata": "Edit Metadata",
179180
"editStyle": "Edit Style",

0 commit comments

Comments
 (0)