diff --git a/geonode_mapstore_client/client/js/epics/gnsave.js b/geonode_mapstore_client/client/js/epics/gnsave.js
index daee701f1b..bcb253e97b 100644
--- a/geonode_mapstore_client/client/js/epics/gnsave.js
+++ b/geonode_mapstore_client/client/js/epics/gnsave.js
@@ -75,7 +75,8 @@ import {
} from '@js/api/geonode/security';
import {
STOP_ASYNC_PROCESS,
- startAsyncProcess
+ startAsyncProcess,
+ updateAsyncProcess
} from '@js/actions/resourceservice';
import {
ResourceTypes,
@@ -94,6 +95,7 @@ import { updateNode, updateSettingsParams } from '@mapstore/framework/actions/la
import { layersSelector, getSelectedLayer as getSelectedNode } from '@mapstore/framework/selectors/layers';
import { styleServiceSelector, getUpdatedLayer, selectedStyleSelector } from '@mapstore/framework/selectors/styleeditor';
import LayersAPI from '@mapstore/framework/api/geoserver/Layers';
+import { wrapStartStop } from '@mapstore/framework/observables/epics';
const RESOURCE_MANAGEMENT_PROPERTIES_KEYS = Object.keys({...RESOURCE_PUBLISHING_PROPERTIES, ...RESOURCE_OPTIONS_PROPERTIES});
@@ -418,10 +420,19 @@ export const gnWatchStopCopyProcessOnSave = (action$, store) =>
}
return Observable.defer(() => getResourceByUuid(newResourceUuid))
.switchMap((resource) => {
- window.location.href = parseDevHostname(resource?.detail_url);
- return Observable.empty();
+ const updatedPayload = {
+ ...action.payload,
+ clonedResourceUrl: parseDevHostname(resource?.detail_url)
+ };
+ return Observable.of(updateAsyncProcess(updatedPayload));
+ })
+ .catch(() => {
+ return Observable.of(loadingResourceConfig(false));
})
- .startWith(loadingResourceConfig(true));
+ .let(wrapStartStop(
+ loadingResourceConfig(true),
+ loadingResourceConfig(false)
+ ));
});
export default {
diff --git a/geonode_mapstore_client/client/js/plugins/ActionNavbar/buttons.jsx b/geonode_mapstore_client/client/js/plugins/ActionNavbar/buttons.jsx
index f20391352a..cce81eea56 100644
--- a/geonode_mapstore_client/client/js/plugins/ActionNavbar/buttons.jsx
+++ b/geonode_mapstore_client/client/js/plugins/ActionNavbar/buttons.jsx
@@ -31,6 +31,9 @@ import { exportDataResultsControlEnabledSelector, checkingExportDataEntriesSelec
import { currentLocaleSelector } from '@mapstore/framework/selectors/locale';
import { checkExportDataEntries, removeExportDataResult } from '@mapstore/framework/actions/layerdownload';
import ExportDataResultsComponent from '@mapstore/framework/components/data/download/ExportDataResultsComponent';
+import FlexBox from '@mapstore/framework/components/layout/FlexBox';
+import Spinner from '@mapstore/framework/components/layout/Spinner';
+import { getCurrentResourceCopyLoading, getCurrentResourceClonedUrl } from '@js/selectors/resourceservice';
// buttons override to use in ActionNavbar for plugin imported from mapstore
@@ -193,3 +196,30 @@ export const AddWidgetActionButton = connect(
);
});
+
+export const ResourceCloningIndicator = connect(
+ (state) => ({
+ isCopying: getCurrentResourceCopyLoading(state),
+ clonedResourceUrl: getCurrentResourceClonedUrl(state)
+ })
+)(({ isCopying, clonedResourceUrl }) => {
+ const className = 'text-primary ms-text _font-size-sm _strong';
+ if (isCopying) {
+ return (
+
+
+
+
+ );
+ }
+
+ if (clonedResourceUrl) {
+ return (
+
+
+
+ );
+ }
+
+ return null;
+});
diff --git a/geonode_mapstore_client/client/js/plugins/SaveAs.jsx b/geonode_mapstore_client/client/js/plugins/SaveAs.jsx
index 491be266a7..d63c1ec178 100644
--- a/geonode_mapstore_client/client/js/plugins/SaveAs.jsx
+++ b/geonode_mapstore_client/client/js/plugins/SaveAs.jsx
@@ -36,6 +36,7 @@ import { canCopyResource } from '@js/utils/ResourceUtils';
import { processResources } from '@js/actions/gnresource';
import { getCurrentResourceCopyLoading } from '@js/selectors/resourceservice';
import withPrompt from '@js/plugins/save/withPrompt';
+import { ResourceCloningIndicator } from './ActionNavbar/buttons';
function SaveAs({
resources,
@@ -214,10 +215,15 @@ const ConnectedMenuItem = connect(
export default createPlugin('SaveAs', {
component: SaveAsPlugin,
containers: {
- ActionNavbar: {
+ ActionNavbar: [{
name: 'SaveAs',
Component: ConnectedSaveAsButton
- },
+ }, {
+ name: 'ResourceCloningIndicator',
+ Component: ResourceCloningIndicator,
+ target: 'right-menu',
+ position: 1
+ }],
ResourcesGrid: {
name: ProcessTypes.COPY_RESOURCE,
target: 'card-options',
diff --git a/geonode_mapstore_client/client/js/selectors/__tests__/resourceservice-test.js b/geonode_mapstore_client/client/js/selectors/__tests__/resourceservice-test.js
index ae78bc1e01..e811c15229 100644
--- a/geonode_mapstore_client/client/js/selectors/__tests__/resourceservice-test.js
+++ b/geonode_mapstore_client/client/js/selectors/__tests__/resourceservice-test.js
@@ -7,8 +7,9 @@
*/
import expect from 'expect';
-import { getCurrentProcesses, processingDownload } from '../resourceservice';
+import { getCurrentProcesses, processingDownload, getCurrentResourceClonedUrl } from '../resourceservice';
import { ResourceTypes } from '@js/utils/ResourceUtils';
+import { ProcessTypes } from '@js/utils/ResourceServiceUtils';
describe('resourceservice selector', () => {
@@ -50,4 +51,110 @@ describe('resourceservice selector', () => {
};
expect(processingDownload(testState)).toEqual(true);
});
+
+ it('test getCurrentResourceClonedUrl when completed copy process has clonedResourceUrl', () => {
+ const testState = {
+ gnresource: {
+ data: {
+ pk: 1
+ }
+ },
+ resourceservice: {
+ processes: [{
+ resource: { pk: 1 },
+ processType: ProcessTypes.COPY_RESOURCE,
+ completed: true,
+ clonedResourceUrl: 'https://example.com/resource/123'
+ }]
+ }
+ };
+ expect(getCurrentResourceClonedUrl(testState)).toEqual('https://example.com/resource/123');
+ });
+
+ it('test getCurrentResourceClonedUrl when completed copy process has no clonedResourceUrl', () => {
+ const testState = {
+ gnresource: {
+ data: {
+ pk: 1
+ }
+ },
+ resourceservice: {
+ processes: [{
+ resource: { pk: 1 },
+ processType: ProcessTypes.COPY_RESOURCE,
+ completed: true
+ }]
+ }
+ };
+ expect(getCurrentResourceClonedUrl(testState)).toEqual(null);
+ });
+
+ it('test getCurrentResourceClonedUrl when copy process is not completed', () => {
+ const testState = {
+ gnresource: {
+ data: {
+ pk: 1
+ }
+ },
+ resourceservice: {
+ processes: [{
+ resource: { pk: 1 },
+ processType: ProcessTypes.COPY_RESOURCE,
+ completed: false,
+ clonedResourceUrl: 'https://example.com/resource/123'
+ }]
+ }
+ };
+ expect(getCurrentResourceClonedUrl(testState)).toEqual(null);
+ });
+
+ it('test getCurrentResourceClonedUrl when there is no copy process', () => {
+ const testState = {
+ gnresource: {
+ data: {
+ pk: 1
+ }
+ },
+ resourceservice: {
+ processes: []
+ }
+ };
+ expect(getCurrentResourceClonedUrl(testState)).toEqual(null);
+ });
+
+ it('test getCurrentResourceClonedUrl when there is no resource', () => {
+ const testState = {
+ gnresource: {
+ data: null
+ },
+ resourceservice: {
+ processes: [{
+ resource: { pk: 1 },
+ processType: ProcessTypes.COPY_RESOURCE,
+ completed: true,
+ clonedResourceUrl: 'https://example.com/resource/123'
+ }]
+ }
+ };
+ expect(getCurrentResourceClonedUrl(testState)).toEqual(null);
+ });
+
+ it('test getCurrentResourceClonedUrl when copy process is for different resource', () => {
+ const testState = {
+ gnresource: {
+ data: {
+ pk: 1
+ }
+ },
+ resourceservice: {
+ processes: [{
+ resource: { pk: 2 },
+ processType: ProcessTypes.COPY_RESOURCE,
+ completed: true,
+ clonedResourceUrl: 'https://example.com/resource/123'
+ }]
+ }
+ };
+ expect(getCurrentResourceClonedUrl(testState)).toEqual(null);
+ });
});
diff --git a/geonode_mapstore_client/client/js/selectors/resourceservice.js b/geonode_mapstore_client/client/js/selectors/resourceservice.js
index c4949de15d..6bfcce948b 100644
--- a/geonode_mapstore_client/client/js/selectors/resourceservice.js
+++ b/geonode_mapstore_client/client/js/selectors/resourceservice.js
@@ -51,6 +51,16 @@ export const getCurrentResourceCopyLoading = (state) => {
return isLoading;
};
+export const getCurrentResourceClonedUrl = (state) => {
+ const resource = getResourceData(state);
+ const copyProcess = resource && state?.resourceservice?.processes?.find(process =>
+ process?.resource?.pk === resource?.pk
+ && process?.processType === ProcessTypes.COPY_RESOURCE
+ && process?.completed === true
+ );
+ return copyProcess?.clonedResourceUrl || null;
+};
+
export const getCurrentResourceDeleteLoading = (state) => {
const resource = getResourceData(state);
const permissionsProcess = resource && state?.resourceservice?.processes?.find(process =>
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.de-DE.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.de-DE.json
index 82ebc67aeb..666970680d 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.de-DE.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.de-DE.json
@@ -226,6 +226,7 @@
"deleting": "Löschen...",
"cloning": "Klonen...",
"clone": "Klon",
+ "navigateToClonedResource": "Zum geklonten Ressource navigieren",
"stylesFirstClone": "An den Stilen vorgenommene Änderungen gelten nur für den aktuellen Layer in Ihrer Karte und nicht für den ursprünglichen Datensatz.",
"title": "Titel",
"titlePlaceholder": "Titel eingeben...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.en-US.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.en-US.json
index c0f732f461..c08a8fb1f1 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.en-US.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.en-US.json
@@ -226,6 +226,7 @@
"deleting": "Deleting...",
"cloning": "Cloning...",
"clone": "Clone",
+ "navigateToClonedResource": "Navigate to cloned resource",
"stylesFirstClone": "Changes made to the styles will only apply to the current layer in your map, and not the original dataset.",
"title": "Title",
"titlePlaceholder": "Type a title...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.es-ES.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.es-ES.json
index 979fa245b9..73dd908e89 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.es-ES.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.es-ES.json
@@ -226,6 +226,7 @@
"deleting": "Eliminando ...",
"cloning": "Clonación ...",
"clone": "Clonar",
+ "navigateToClonedResource": "Navegar al recurso clonado",
"stylesFirstClone": "Los cambios realizados en los estilos solo se aplicarán a la capa actual en su mapa, y no al dataset original.",
"title": "Título",
"titlePlaceholder": "Escriba un título...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.fi-FI.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.fi-FI.json
index bac10b5c11..5890d9bcbe 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.fi-FI.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.fi-FI.json
@@ -224,6 +224,7 @@
"deleting": "Deleting...",
"cloning": "Cloning...",
"clone": "Clone",
+ "navigateToClonedResource": "Navigate to cloned resource",
"stylesFirstClone": "Changes made to the styles will only apply to the current layer in your map, and not the original dataset.",
"title": "Title",
"titlePlaceholder": "Type a title...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.fr-FR.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.fr-FR.json
index 7164839b4a..f0708d4ec0 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.fr-FR.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.fr-FR.json
@@ -226,6 +226,7 @@
"deleting": "Suppression...",
"cloning": "Clonage...",
"clone": "Cloner",
+ "navigateToClonedResource": "Naviguer vers la ressource clonée",
"stylesFirstClone": "Les modifications apportées aux styles ne s'appliqueront qu'à la couche actuelle de votre carte, et non au jeu de données d'origine.",
"title": "Titre",
"titlePlaceholder": "Tapez un titre...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.hr-HR.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.hr-HR.json
index 24832d3c7f..4928734f9b 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.hr-HR.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.hr-HR.json
@@ -224,6 +224,7 @@
"deleting": "Deleting...",
"cloning": "Cloning...",
"clone": "Clone",
+ "navigateToClonedResource": "Navigate to cloned resource",
"stylesFirstClone": "Changes made to the styles will only apply to the current layer in your map, and not the original dataset.",
"title": "Title",
"titlePlaceholder": "Type a title...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.it-IT.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.it-IT.json
index 29a0bde245..de053c722c 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.it-IT.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.it-IT.json
@@ -228,6 +228,7 @@
"deleting": "Eliminazione in corso...",
"cloning": "Copia in corso...",
"clone": "Clona",
+ "navigateToClonedResource": "Naviga alla risorsa clonata",
"stylesFirstClone": "Le modifiche apportate agli stili verranno applicate solo al livello corrente nella mappa e non al set di dati originale.",
"title": "Titolo",
"titlePlaceholder": "Aggiungi un titolo...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.nl-NL.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.nl-NL.json
index 5ab0d3b04f..b698ce266b 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.nl-NL.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.nl-NL.json
@@ -224,6 +224,7 @@
"deleting": "Deleting...",
"cloning": "Cloning...",
"clone": "Clone",
+ "navigateToClonedResource": "Navigate to cloned resource",
"stylesFirstClone": "Changes made to the styles will only apply to the current layer in your map, and not the original dataset.",
"title": "Title",
"titlePlaceholder": "Type a title...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.pt-PT.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.pt-PT.json
index 23d06688eb..b3c7f57672 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.pt-PT.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.pt-PT.json
@@ -224,6 +224,7 @@
"deleting": "Deleting...",
"cloning": "Cloning...",
"clone": "Clone",
+ "navigateToClonedResource": "Navigate to cloned resource",
"stylesFirstClone": "Changes made to the styles will only apply to the current layer in your map, and not the original dataset.",
"title": "Title",
"titlePlaceholder": "Type a title...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.sk-SK.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.sk-SK.json
index f10330744d..4ee221e70e 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.sk-SK.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.sk-SK.json
@@ -224,6 +224,7 @@
"deleting": "Deleting...",
"cloning": "Cloning...",
"clone": "Clone",
+ "navigateToClonedResource": "Navigate to cloned resource",
"stylesFirstClone": "Changes made to the styles will only apply to the current layer in your map, and not the original dataset.",
"title": "Title",
"titlePlaceholder": "Type a title...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.sv-SE.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.sv-SE.json
index b4c76e07d9..12c8cdeebc 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.sv-SE.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.sv-SE.json
@@ -225,6 +225,7 @@
"deleting": "Deleting...",
"cloning": "Cloning...",
"clone": "Clone",
+ "navigateToClonedResource": "Navigate to cloned resource",
"stylesFirstClone": "Changes made to the styles will only apply to the current layer in your map, and not the original dataset.",
"title": "Title",
"titlePlaceholder": "Type a title...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.vi-VN.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.vi-VN.json
index 50e80c8095..0e133acf80 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.vi-VN.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.vi-VN.json
@@ -224,6 +224,7 @@
"deleting": "Deleting...",
"cloning": "Cloning...",
"clone": "Clone",
+ "navigateToClonedResource": "Navigate to cloned resource",
"stylesFirstClone": "Changes made to the styles will only apply to the current layer in your map, and not the original dataset.",
"title": "Title",
"titlePlaceholder": "Type a title...",
diff --git a/geonode_mapstore_client/static/mapstore/gn-translations/data.zh-ZH.json b/geonode_mapstore_client/static/mapstore/gn-translations/data.zh-ZH.json
index 55f81f1a7c..6d6f38b251 100644
--- a/geonode_mapstore_client/static/mapstore/gn-translations/data.zh-ZH.json
+++ b/geonode_mapstore_client/static/mapstore/gn-translations/data.zh-ZH.json
@@ -224,6 +224,7 @@
"deleting": "Deleting...",
"cloning": "Cloning...",
"clone": "Clone",
+ "navigateToClonedResource": "Navigate to cloned resource",
"stylesFirstClone": "Changes made to the styles will only apply to the current layer in your map, and not the original dataset.",
"title": "Title",
"titlePlaceholder": "Type a title...",