From bc9fba3b00c6076d8643fa98079b043fc1c13164 Mon Sep 17 00:00:00 2001 From: kappu Date: Tue, 20 Feb 2018 13:59:32 +0100 Subject: [PATCH 1/5] Closes geosolutions-it/austrocontrol-C125#45 --- web/client/actions/annotations.js | 10 +++++++++- .../mapcontrols/annotations/Annotations.jsx | 11 ++++++++++- web/client/epics/annotations.js | 15 +++++++++++++-- web/client/plugins/Annotations.jsx | 5 +++-- web/client/translations/data.de-DE | 1 + web/client/translations/data.en-US | 1 + web/client/translations/data.es-ES | 1 + web/client/translations/data.fr-FR | 1 + web/client/translations/data.it-IT | 1 + 9 files changed, 40 insertions(+), 6 deletions(-) diff --git a/web/client/actions/annotations.js b/web/client/actions/annotations.js index 68e08e0c49..73ee491e96 100644 --- a/web/client/actions/annotations.js +++ b/web/client/actions/annotations.js @@ -39,6 +39,13 @@ const SHOW_TEXT_AREA = 'ANNOTATIONS:SHOW_TEXT_AREA'; const ADD_TEXT = 'ANNOTATIONS:ADD_TEXT'; const CANCEL_CLOSE_TEXT = 'ANNOTATIONS:CANCEL_CLOSE_TEXT'; const SAVE_TEXT = 'ANNOTATIONS:SAVE_TEXT'; +const DONWLOAD = 'ANNOTATIONS:DONWLOAD'; + +function download() { + return { + type: DONWLOAD + }; +} const {head} = require('lodash'); @@ -289,5 +296,6 @@ module.exports = { filterAnnotations, closeAnnotations, confirmCloseAnnotations, - cancelCloseAnnotations + cancelCloseAnnotations, + DONWLOAD, download }; diff --git a/web/client/components/mapcontrols/annotations/Annotations.jsx b/web/client/components/mapcontrols/annotations/Annotations.jsx index 19fc8d0ff1..e6fb079dc7 100644 --- a/web/client/components/mapcontrols/annotations/Annotations.jsx +++ b/web/client/components/mapcontrols/annotations/Annotations.jsx @@ -71,6 +71,7 @@ const defaultConfig = require('./AnnotationsConfig'); * @prop {function} onDetail triggered when the user clicks on an annotation card * @prop {function} onFilter triggered when the user enters some text in the filtering widget * @prop {function} classNameSelector optional selector to assign custom a CSS class to annotations, based on + * @prop {function} onDownload triggered when the user clicks on the download annotations button * the annotation's attributes. */ class Annotations extends React.Component { @@ -104,7 +105,8 @@ class Annotations extends React.Component { filter: PropTypes.string, onFilter: PropTypes.func, classNameSelector: PropTypes.func, - width: PropTypes.number + width: PropTypes.number, + onDownload: React.PropTypes.func }; static contextTypes = { @@ -227,6 +229,13 @@ class Annotations extends React.Component { tooltip: , visible: this.props.mode === "list", onClick: () => { this.props.onAdd(); } + }, + { + glyph: 'download', + disabled: !(this.props.annotations && this.props.annotations.length > 0), + tooltip: , + visible: this.props.mode === "list", + onClick: () => { this.props.onDownload(); } } ]}/> diff --git a/web/client/epics/annotations.js b/web/client/epics/annotations.js index 47bf9a098d..4278720fef 100644 --- a/web/client/epics/annotations.js +++ b/web/client/epics/annotations.js @@ -7,6 +7,7 @@ */ const Rx = require('rxjs'); +const {saveAs} = require('file-saver'); const {MAP_CONFIG_LOADED} = require('../actions/config'); const {TOGGLE_CONTROL, toggleControl} = require('../actions/controls'); const {addLayer, updateNode, changeLayerProperties, removeLayer} = require('../actions/layers'); @@ -15,7 +16,7 @@ const {hideMapinfoMarker, purgeMapInfoResults} = require('../actions/mapInfo'); const {updateAnnotationGeometry, setStyle, toggleStyle, cleanHighlight, toggleAdd, showTextArea, CONFIRM_REMOVE_ANNOTATION, SAVE_ANNOTATION, EDIT_ANNOTATION, CANCEL_EDIT_ANNOTATION, TOGGLE_ADD, SET_STYLE, RESTORE_STYLE, HIGHLIGHT, CLEAN_HIGHLIGHT, CONFIRM_CLOSE_ANNOTATIONS, STOP_DRAWING, - CANCEL_CLOSE_TEXT, SAVE_TEXT} = require('../actions/annotations'); + CANCEL_CLOSE_TEXT, SAVE_TEXT, DONWLOAD} = require('../actions/annotations'); const {CLICK_ON_MAP} = require('../actions/map'); const {GEOMETRY_CHANGED} = require('../actions/draw'); @@ -27,6 +28,8 @@ const assign = require('object-assign'); const {annotationsLayerSelector} = require('../selectors/annotations'); // const {DEFAULT_ANNOTATIONS_STYLES} = require('../utils/AnnotationsUtils'); +const { mapNameSelector} = require('../selectors/map'); + const {changeDrawingStatus} = require('../actions/draw'); /** @@ -257,5 +260,13 @@ module.exports = (viewer) => ({ confirmCloseAnnotationsEpic: (action$, store) => action$.ofType(CONFIRM_CLOSE_ANNOTATIONS) .switchMap(() => { return Rx.Observable.from((store.getState().controls.annotations && store.getState().controls.annotations.enabled ? [toggleControl('annotations')] : []).concat([purgeMapInfoResults()])); - }) + }), + downloadAnnotaions: (action$, {getState}) => action$.ofType(DONWLOAD) + .switchMap(() => { + const annotations = annotationsLayerSelector(getState()); + const mapName = mapNameSelector(getState()); + saveAs(new Blob([JSON.stringify(annotations.features)], {type: "application/json;charset=utf-8"}), `${ mapName.length > 0 && mapName || "Annotations"}.json`); + return Rx.Observable.empty(); + }) + }); diff --git a/web/client/plugins/Annotations.jsx b/web/client/plugins/Annotations.jsx index adfe6fe5a2..13cb8ef91b 100644 --- a/web/client/plugins/Annotations.jsx +++ b/web/client/plugins/Annotations.jsx @@ -20,7 +20,7 @@ const {cancelRemoveAnnotation, confirmRemoveAnnotation, editAnnotation, newAnnot saveAnnotation, toggleAdd, validationError, removeAnnotationGeometry, toggleStyle, setStyle, restoreStyle, highlight, cleanHighlight, showAnnotation, cancelShowAnnotation, filterAnnotations, closeAnnotations, cancelCloseAnnotations, confirmCloseAnnotations, stopDrawing, changeStyler, setUnsavedChanges, toggleUnsavedChangesModal, changedProperties, - setUnsavedStyle, toggleUnsavedStyleModal, addText, cancelText, saveText} = + setUnsavedStyle, toggleUnsavedStyleModal, addText, cancelText, saveText, download} = require('../actions/annotations'); const { zoomToExtent } = require('../actions/map'); @@ -86,7 +86,8 @@ const Annotations = connect(panelSelector, { onHighlight: highlight, onCleanHighlight: cleanHighlight, onDetail: showAnnotation, - onFilter: filterAnnotations + onFilter: filterAnnotations, + onDownload: download })(require('../components/mapcontrols/annotations/Annotations')); const ContainerDimensions = require('react-container-dimensions').default; diff --git a/web/client/translations/data.de-DE b/web/client/translations/data.de-DE index 9ad4808b07..90d3e5ac2f 100644 --- a/web/client/translations/data.de-DE +++ b/web/client/translations/data.de-DE @@ -719,6 +719,7 @@ "add": "Neue", "filter": "Filtern die annotationen...", "undo": "Sind Sie sicher, dass Sie die Annotationsbearbeitungssitzung aufgeben möchten?", + "downloadtooltip": "Laden Sie Anmerkungen herunter", "zoomTo": "Zoom", "field": { "title": "Titel", diff --git a/web/client/translations/data.en-US b/web/client/translations/data.en-US index 5f2cd649e2..cdf1d6a7b6 100644 --- a/web/client/translations/data.en-US +++ b/web/client/translations/data.en-US @@ -720,6 +720,7 @@ "undo": "Are you sure you want to abandon the annotation editing session?", "title": "Annotations", "zoomTo": "Zoom", + "downloadtooltip": "Download annotations", "field": { "title": "Title", "description": "Description" diff --git a/web/client/translations/data.es-ES b/web/client/translations/data.es-ES index b83536f91d..b386701dfd 100644 --- a/web/client/translations/data.es-ES +++ b/web/client/translations/data.es-ES @@ -720,6 +720,7 @@ "filter": "Filtrar las anotaciones...", "undo": "¿Estás seguro de que quieres abandonar la sesión de edición de anotaciones?", "zoomTo": "Zoom", + "downloadtooltip": "Descargar anotaciones", "field": { "title": "Título", "description": "Descripción" diff --git a/web/client/translations/data.fr-FR b/web/client/translations/data.fr-FR index 23f69f53e3..b2e44d464b 100644 --- a/web/client/translations/data.fr-FR +++ b/web/client/translations/data.fr-FR @@ -694,6 +694,7 @@ "filter": "Filtrer les annotations...", "undo": "Êtes-vous sûr de vouloir abandonner la session d'édition d'annotation?", "zoomTo": "Zoom", + "downloadtooltip": "Télécharger les annotations", "field": { "title": "Titre", "description": "Description" diff --git a/web/client/translations/data.it-IT b/web/client/translations/data.it-IT index 24ee96d4e7..3a847d6b89 100644 --- a/web/client/translations/data.it-IT +++ b/web/client/translations/data.it-IT @@ -721,6 +721,7 @@ "undo": "Sei sicuro di voler abbandonare la modifica in corso?", "title": "Annotazioni", "zoomTo": "Zoom", + "downloadtooltip": "Scarica annotazioni", "field": { "title": "Titolo", "description": "Descrizione" From 75185765f1ad05f8114a464766bb12fc91ba4174 Mon Sep 17 00:00:00 2001 From: kappu Date: Tue, 20 Feb 2018 14:28:11 +0100 Subject: [PATCH 2/5] Fixed translation --- web/client/translations/data.it-IT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/client/translations/data.it-IT b/web/client/translations/data.it-IT index 3a847d6b89..bc6fcb762c 100644 --- a/web/client/translations/data.it-IT +++ b/web/client/translations/data.it-IT @@ -721,7 +721,7 @@ "undo": "Sei sicuro di voler abbandonare la modifica in corso?", "title": "Annotazioni", "zoomTo": "Zoom", - "downloadtooltip": "Scarica annotazioni", + "downloadtooltip": "Esporta annotazioni", "field": { "title": "Titolo", "description": "Descrizione" From faea35cb52c9fa3377da6b8d5578f6ee4f7f3d46 Mon Sep 17 00:00:00 2001 From: kappu Date: Tue, 20 Feb 2018 15:07:26 +0100 Subject: [PATCH 3/5] Added test and fixed typo --- web/client/actions/annotations.js | 6 ++--- .../epics/__tests__/annotations-test.js | 26 +++++++++++++++++-- web/client/epics/annotations.js | 4 +-- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/web/client/actions/annotations.js b/web/client/actions/annotations.js index 73ee491e96..4185321eac 100644 --- a/web/client/actions/annotations.js +++ b/web/client/actions/annotations.js @@ -39,11 +39,11 @@ const SHOW_TEXT_AREA = 'ANNOTATIONS:SHOW_TEXT_AREA'; const ADD_TEXT = 'ANNOTATIONS:ADD_TEXT'; const CANCEL_CLOSE_TEXT = 'ANNOTATIONS:CANCEL_CLOSE_TEXT'; const SAVE_TEXT = 'ANNOTATIONS:SAVE_TEXT'; -const DONWLOAD = 'ANNOTATIONS:DONWLOAD'; +const DOWNLOAD = 'ANNOTATIONS:DOWNLOAD'; function download() { return { - type: DONWLOAD + type: DOWNLOAD }; } @@ -297,5 +297,5 @@ module.exports = { closeAnnotations, confirmCloseAnnotations, cancelCloseAnnotations, - DONWLOAD, download + DOWNLOAD, download }; diff --git a/web/client/epics/__tests__/annotations-test.js b/web/client/epics/__tests__/annotations-test.js index 5c458107de..c92899e3ad 100644 --- a/web/client/epics/__tests__/annotations-test.js +++ b/web/client/epics/__tests__/annotations-test.js @@ -16,13 +16,13 @@ const {HIDE_MAPINFO_MARKER, PURGE_MAPINFO_RESULTS} = require('../../actions/mapI const {configureMap } = require('../../actions/config'); const {editAnnotation, confirmRemoveAnnotation, saveAnnotation, cancelEditAnnotation, setStyle, highlight, cleanHighlight, - toggleAdd, UPDATE_ANNOTATION_GEOMETRY, SHOW_TEXT_AREA, cancelText, stopDrawing + toggleAdd, UPDATE_ANNOTATION_GEOMETRY, SHOW_TEXT_AREA, cancelText, stopDrawing, download } = require('../../actions/annotations'); const {clickOnMap } = require('../../actions/map'); const {addAnnotationsLayerEpic, editAnnotationEpic, removeAnnotationEpic, saveAnnotationEpic, cancelEditAnnotationEpic, startDrawMarkerEpic, endDrawGeomEpic, setStyleEpic, restoreStyleEpic, highlighAnnotationEpic, - cleanHighlightAnnotationEpic, addTextEpic, cancelTextAnnotationsEpic, endDrawTextEpic, stopDrawingMultiGeomEpic + cleanHighlightAnnotationEpic, addTextEpic, cancelTextAnnotationsEpic, endDrawTextEpic, stopDrawingMultiGeomEpic, downloadAnnotations } = require('../annotations')({}); const rootEpic = combineEpics(addAnnotationsLayerEpic, editAnnotationEpic, removeAnnotationEpic, saveAnnotationEpic, setStyleEpic, cancelEditAnnotationEpic, startDrawMarkerEpic, endDrawGeomEpic, restoreStyleEpic, highlighAnnotationEpic, @@ -306,4 +306,26 @@ describe('annotations Epics', () => { }, state); }); + it('export annotation layer', (done) => { + const state = { + layers: { + flat: [{ + id: 'annotations', + features: [{ + properties: { + id: '1' + }, + geometry: { + type: "Point" + } + }] + }] + } + }; + testEpic(downloadAnnotations, 0, download(), actions => { + expect(actions.length).toBe(0); + done(); + }, state); + }); + }); diff --git a/web/client/epics/annotations.js b/web/client/epics/annotations.js index 4278720fef..911b6fe4c7 100644 --- a/web/client/epics/annotations.js +++ b/web/client/epics/annotations.js @@ -16,7 +16,7 @@ const {hideMapinfoMarker, purgeMapInfoResults} = require('../actions/mapInfo'); const {updateAnnotationGeometry, setStyle, toggleStyle, cleanHighlight, toggleAdd, showTextArea, CONFIRM_REMOVE_ANNOTATION, SAVE_ANNOTATION, EDIT_ANNOTATION, CANCEL_EDIT_ANNOTATION, TOGGLE_ADD, SET_STYLE, RESTORE_STYLE, HIGHLIGHT, CLEAN_HIGHLIGHT, CONFIRM_CLOSE_ANNOTATIONS, STOP_DRAWING, - CANCEL_CLOSE_TEXT, SAVE_TEXT, DONWLOAD} = require('../actions/annotations'); + CANCEL_CLOSE_TEXT, SAVE_TEXT, DOWNLOAD} = require('../actions/annotations'); const {CLICK_ON_MAP} = require('../actions/map'); const {GEOMETRY_CHANGED} = require('../actions/draw'); @@ -261,7 +261,7 @@ module.exports = (viewer) => ({ .switchMap(() => { return Rx.Observable.from((store.getState().controls.annotations && store.getState().controls.annotations.enabled ? [toggleControl('annotations')] : []).concat([purgeMapInfoResults()])); }), - downloadAnnotaions: (action$, {getState}) => action$.ofType(DONWLOAD) + downloadAnnotations: (action$, {getState}) => action$.ofType(DOWNLOAD) .switchMap(() => { const annotations = annotationsLayerSelector(getState()); const mapName = mapNameSelector(getState()); From 9b7b020a2a2cf4ba8f5dbfcd5076a84ed41c13ca Mon Sep 17 00:00:00 2001 From: kappu Date: Tue, 20 Feb 2018 15:17:23 +0100 Subject: [PATCH 4/5] Added action test --- web/client/actions/__tests__/annotations-test.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/web/client/actions/__tests__/annotations-test.js b/web/client/actions/__tests__/annotations-test.js index 3aaecb30e6..376520cb6a 100644 --- a/web/client/actions/__tests__/annotations-test.js +++ b/web/client/actions/__tests__/annotations-test.js @@ -64,7 +64,8 @@ const { filterAnnotations, closeAnnotations, confirmCloseAnnotations, - cancelCloseAnnotations + cancelCloseAnnotations, + DOWNLOAD, download } = require('../annotations'); describe('Test correctness of the annotations actions', () => { @@ -267,4 +268,8 @@ describe('Test correctness of the annotations actions', () => { const result = cancelCloseAnnotations(); expect(result.type).toEqual(CANCEL_CLOSE_ANNOTATIONS); }); + it('download annotations', () => { + const result = download(); + expect(result.type).toEqual(DOWNLOAD); + }); }); From 38a3c2f19bb7d61238f11552b2c75dfadfdd52c1 Mon Sep 17 00:00:00 2001 From: kappu Date: Tue, 20 Feb 2018 18:13:05 +0100 Subject: [PATCH 5/5] Fix on Mauro's comment --- .../epics/__tests__/annotations-test.js | 26 +++++++++---------- web/client/epics/annotations.js | 19 +++++++++++--- web/client/translations/data.de-DE | 1 + web/client/translations/data.en-US | 1 + web/client/translations/data.es-ES | 1 + web/client/translations/data.fr-FR | 1 + web/client/translations/data.it-IT | 1 + 7 files changed, 32 insertions(+), 18 deletions(-) diff --git a/web/client/epics/__tests__/annotations-test.js b/web/client/epics/__tests__/annotations-test.js index c92899e3ad..a78f9bdf0c 100644 --- a/web/client/epics/__tests__/annotations-test.js +++ b/web/client/epics/__tests__/annotations-test.js @@ -306,24 +306,22 @@ describe('annotations Epics', () => { }, state); }); - it('export annotation layer', (done) => { + it('export annotation fail', (done) => { const state = { layers: { - flat: [{ - id: 'annotations', - features: [{ - properties: { - id: '1' - }, - geometry: { - type: "Point" - } - }] - }] + flat: [] } }; - testEpic(downloadAnnotations, 0, download(), actions => { - expect(actions.length).toBe(0); + testEpic(downloadAnnotations, 1, download(), actions => { + expect(actions.length).toBe(1); + actions.map((action) => { + switch (action.type) { + case "SHOW_NOTIFICATION": + break; + default: + expect(false).toBe(true); + } + }); done(); }, state); }); diff --git a/web/client/epics/annotations.js b/web/client/epics/annotations.js index 911b6fe4c7..b367d0d277 100644 --- a/web/client/epics/annotations.js +++ b/web/client/epics/annotations.js @@ -13,6 +13,8 @@ const {TOGGLE_CONTROL, toggleControl} = require('../actions/controls'); const {addLayer, updateNode, changeLayerProperties, removeLayer} = require('../actions/layers'); const {hideMapinfoMarker, purgeMapInfoResults} = require('../actions/mapInfo'); +const {error} = require('../actions/notifications'); + const {updateAnnotationGeometry, setStyle, toggleStyle, cleanHighlight, toggleAdd, showTextArea, CONFIRM_REMOVE_ANNOTATION, SAVE_ANNOTATION, EDIT_ANNOTATION, CANCEL_EDIT_ANNOTATION, TOGGLE_ADD, SET_STYLE, RESTORE_STYLE, HIGHLIGHT, CLEAN_HIGHLIGHT, CONFIRM_CLOSE_ANNOTATIONS, STOP_DRAWING, @@ -263,10 +265,19 @@ module.exports = (viewer) => ({ }), downloadAnnotations: (action$, {getState}) => action$.ofType(DOWNLOAD) .switchMap(() => { - const annotations = annotationsLayerSelector(getState()); - const mapName = mapNameSelector(getState()); - saveAs(new Blob([JSON.stringify(annotations.features)], {type: "application/json;charset=utf-8"}), `${ mapName.length > 0 && mapName || "Annotations"}.json`); - return Rx.Observable.empty(); + try { + const annotations = annotationsLayerSelector(getState()); + const mapName = mapNameSelector(getState()); + saveAs(new Blob([JSON.stringify(annotations.features)], {type: "application/json;charset=utf-8"}), `${ mapName.length > 0 && mapName || "Annotations"}.json`); + return Rx.Observable.empty(); + }catch (e) { + return Rx.Observable.of(error({ + title: "annotations.title", + message: "annotations.downloadError", + autoDismiss: 5, + position: "tr" + })); + } }) }); diff --git a/web/client/translations/data.de-DE b/web/client/translations/data.de-DE index 90d3e5ac2f..e5a428fcf1 100644 --- a/web/client/translations/data.de-DE +++ b/web/client/translations/data.de-DE @@ -720,6 +720,7 @@ "filter": "Filtern die annotationen...", "undo": "Sind Sie sicher, dass Sie die Annotationsbearbeitungssitzung aufgeben möchten?", "downloadtooltip": "Laden Sie Anmerkungen herunter", + "downloadError": "Fehler beim Exportieren", "zoomTo": "Zoom", "field": { "title": "Titel", diff --git a/web/client/translations/data.en-US b/web/client/translations/data.en-US index cdf1d6a7b6..9001c7fbb2 100644 --- a/web/client/translations/data.en-US +++ b/web/client/translations/data.en-US @@ -721,6 +721,7 @@ "title": "Annotations", "zoomTo": "Zoom", "downloadtooltip": "Download annotations", + "downloadError": "Export error", "field": { "title": "Title", "description": "Description" diff --git a/web/client/translations/data.es-ES b/web/client/translations/data.es-ES index b386701dfd..508d5c4bc0 100644 --- a/web/client/translations/data.es-ES +++ b/web/client/translations/data.es-ES @@ -721,6 +721,7 @@ "undo": "¿Estás seguro de que quieres abandonar la sesión de edición de anotaciones?", "zoomTo": "Zoom", "downloadtooltip": "Descargar anotaciones", + "downloadError": "Error de exportación", "field": { "title": "Título", "description": "Descripción" diff --git a/web/client/translations/data.fr-FR b/web/client/translations/data.fr-FR index b2e44d464b..cb0c9fd830 100644 --- a/web/client/translations/data.fr-FR +++ b/web/client/translations/data.fr-FR @@ -695,6 +695,7 @@ "undo": "Êtes-vous sûr de vouloir abandonner la session d'édition d'annotation?", "zoomTo": "Zoom", "downloadtooltip": "Télécharger les annotations", + "downloadError": "Erreur d'exportation", "field": { "title": "Titre", "description": "Description" diff --git a/web/client/translations/data.it-IT b/web/client/translations/data.it-IT index bc6fcb762c..043df7ee94 100644 --- a/web/client/translations/data.it-IT +++ b/web/client/translations/data.it-IT @@ -722,6 +722,7 @@ "title": "Annotazioni", "zoomTo": "Zoom", "downloadtooltip": "Esporta annotazioni", + "downloadError": "Errore di esportazione", "field": { "title": "Titolo", "description": "Descrizione"