diff --git a/.github/ISSUE_TEMPLATE/release_steps.md b/.github/ISSUE_TEMPLATE/release_steps.md index bafe2c04cb..94df9a215e 100644 --- a/.github/ISSUE_TEMPLATE/release_steps.md +++ b/.github/ISSUE_TEMPLATE/release_steps.md @@ -16,6 +16,7 @@ This steps have to be followed always when preparing a new release. - [ ] for `geostore`, check if [here](https://maven.geo-solutions.it/it/geosolutions/geostore/geostore-webapp/) is present the version specified. Release procedure is specified in the [Readme.md](https://github.com/geosolutions-it/geostore) file of the project. - [ ] for `http_proxy`, check if [here](https://maven.geo-solutions.it/proxy/http_proxy/) is present the version specified. Release procedure is specified in the [Readme.md](https://github.com/geosolutions-it/http-proxy) file of the project. - [ ] for `mapfish-print` check if [here](https://maven.geo-solutions.it/proxy/http_proxy/) is present the version specified. Release procedure is specified in the [Readme.md](https://github.com/geosolutions-it/mapfish-print) file of the project. +- [ ] Check if dependencies of MapStore libraries and geostore libraries are the same (compare `src/pom.xml` in geostore e `pom.xml` in MapStore). ## New stable branch creation @@ -42,7 +43,11 @@ This steps have to be followed always when preparing a new release. - [ ] Check `pom.xml` dependencies are all in fixed stable versions ( no `-SNAPSHOT` usage release). If not, You use the action [`Update dependencies versions`](https://github.com/geosolutions-it/MapStore2/actions/workflows/update_dependencies_versions.yml) to fix them, setting: - [ ] the branch to `YYYY.XX.xx` - [ ] the of geostore, http_proxy and mapfish-print versions accordingly with the [MapStore release calendar](https://github.com/geosolutions-it/MapStore2/wiki/MapStore-Release-Calendars) -- [ ] Check that [MapStoreExtension](https://github.com/geosolutions-it/MapStoreExtension) repository is aligned and working +- [ ] Check that [MapStoreExtension](https://github.com/geosolutions-it/MapStoreExtension) repository is aligned and working. +- [ ] Run the [Submodule Update](https://github.com/geosolutions-it/MapStoreExtension/actions/workflows/submodules_update.yml) of [MapStoreExtension](https://github.com/geosolutions-it/MapStoreExtension) to generate the `SampleExtension.zip` to use for testing. + - [ ] Use workflow from `YYYY.XX.xx` branch + - [ ] Wait for completition + - [ ] Download the `Artifacts.zip` that contains `SampleExtension.zip` from the execution of the `checks` action on the branch `YYYY.XX.xx`. This can be used for tests. - [ ] Test on QA [https://qa-mapstore.geosolutionsgroup.com/mapstore/](https://qa-mapstore.geosolutionsgroup.com/mapstore/) - [ ] Test **everything**, not only the new features - [ ] Test the creation of a standard project starting in from the stable branch and with the internal backend, so: diff --git a/CHANGELOG.md b/CHANGELOG.md index cb164ca733..488f8fc3c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## [2024.02.00](https://github.com/geosolutions-it/MapStore2/tree/v2024.02.00) (2024-10-7) + +- **[Full Changelog](https://github.com/geosolutions-it/MapStore2/compare/v2024.01.02...v2024.02.00)** +- **[Implemented enhancements](https://github.com/geosolutions-it/MapStore2/issues?q=is%3Aissue+milestone%3A%222024.02.00%22+is%3Aclosed+label%3Aenhancement)** +- **[Fixed bugs](https://github.com/geosolutions-it/MapStore2/issues?q=is%3Aissue+milestone%3A%222024.02.00%22+is%3Aclosed+label%3Abug)** +- **[Closed issues](https://github.com/geosolutions-it/MapStore2/issues?q=is%3Aissue+milestone%3A%222024.02.00%22+is%3Aclosed)** + ## [2024.01.02](https://github.com/geosolutions-it/MapStore2/tree/v2024.01.02) (2024-7-10) - **[Full Changelog](https://github.com/geosolutions-it/MapStore2/compare/v2024.01.01...v2024.01.02)** diff --git a/build/docma-config.json b/build/docma-config.json index 2ee8061022..c5aedcca7b 100644 --- a/build/docma-config.json +++ b/build/docma-config.json @@ -120,12 +120,9 @@ "web/client/components/data/featuregrid/editors/NumberEditor.jsx", "web/client/components/data/identify/GeocodeViewer.jsx", "web/client/components/data/identify/IdentifyContainer.jsx", - "web/client/components/data/query/AutocompleteField.jsx", + "web/client/components/data/query/AutocompleteFieldHOC.jsx", "web/client/components/map/leaflet/DrawSupport.jsx", "web/client/components/plugins/PluginsContainer.jsx", - "web/client/components/mapcontrols/annotations/Annotations.jsx", - "web/client/components/mapcontrols/annotations/AnnotationsEditor.jsx", - "web/client/components/mapcontrols/annotations/AnnotationsConfig.js", "web/client/components/misc/cardgrids/SideCard.jsx", "web/client/components/misc/cardgrids/SideGrid.jsx", "web/client/components/misc/enhancers/emptyState.jsx", @@ -135,7 +132,6 @@ "web/client/components/misc/panels/DockablePanel.jsx", "web/client/components/misc/panels/DockPanel.jsx", "web/client/components/misc/panels/PanelHeader.jsx", - "web/client/components/misc/sidebar/Sidebar.jsx", "web/client/components/misc/toolbar/Toolbar.jsx", "web/client/components/misc/transfer/Transfer.jsx", "web/client/components/misc/EmptyView.jsx", @@ -152,7 +148,6 @@ "web/client/actions/index.jsdoc", "web/client/actions/controls.js", "web/client/actions/fullscreen.js", - "web/client/actions/globeswitcher.js", "web/client/actions/maplayout.js", "web/client/actions/map.js", "web/client/actions/maps.js", @@ -171,7 +166,6 @@ "web/client/reducers/index.jsdoc", "web/client/reducers/controls.js", "web/client/reducers/featuregrid.js", - "web/client/reducers/globeswitcher.js", "web/client/reducers/mapInfo.js", "web/client/reducers/maps.js", "web/client/reducers/maptype.js", @@ -186,7 +180,6 @@ "web/client/epics/featuregrid.js", "web/client/epics/feedbackMask.js", "web/client/epics/fullscreen.js", - "web/client/epics/globeswitcher.js", "web/client/epics/maplayout.js", "web/client/epics/maptype.js", "web/client/epics/notifications.js", @@ -270,8 +263,6 @@ "web/client/plugins/MapTemplates.jsx", "web/client/plugins/MapViews.jsx", "web/client/plugins/Measure.jsx", - "web/client/plugins/MeasurePanel.jsx", - "web/client/plugins/MeasureResults.jsx", "web/client/plugins/MediaEditor.jsx", "web/client/plugins/MetadataExplorer.jsx", "web/client/plugins/MetadataInfo/index.jsx", @@ -306,7 +297,6 @@ "web/client/plugins/Tutorial.jsx", "web/client/plugins/UserExtensions.jsx", "web/client/plugins/UserSession.jsx", - "web/client/plugins/Version.jsx", "web/client/plugins/LayerDownload.jsx", "web/client/plugins/Widgets.jsx", "web/client/plugins/WidgetsBuilder.jsx", diff --git a/docs/developer-guide/integrations/users/openId.md b/docs/developer-guide/integrations/users/openId.md index fe7f0c08a9..2424c262f3 100644 --- a/docs/developer-guide/integrations/users/openId.md +++ b/docs/developer-guide/integrations/users/openId.md @@ -96,7 +96,7 @@ oidcOAuth2Config.internalRedirectUri=http://localhost:8080/mapstore - `oidcOAuth2Config.globalLogoutEnabled`: (*optional*): if true (and the server supports it) invokes global logout on MapStore logout !!! note - The `rolesClaim` and `groupsClaim` are optional. If you don't need to map roles or groups, you can omit them. At the moment, there is no mapping for roles and groups for the generic OIDC provider. If you need to map roles and groups, you can use the `keycloak` provider. + The only mandatory claim is the `email` or what you indicated in `oidcOAuth2Config.principalKey`. The `rolesClaim` and `groupsClaim` configurations are optional. If you don't need to map roles or groups, you can omit them. At the moment, there is no mapping for roles and groups for the generic OIDC provider. If you need to map roles and groups, you can use the `keycloak` provider. #### Configure the MapStore front-end diff --git a/package.json b/package.json index 5376dbd648..f20805aa9f 100644 --- a/package.json +++ b/package.json @@ -151,8 +151,6 @@ "@turf/point-on-surface": "4.1.0", "@turf/polygon-to-linestring": "4.1.0", "@znemz/cesium-navigation": "4.0.0", - "ag-grid-community": "20.2.0", - "ag-grid-react": "20.2.0", "assert": "2.0.0", "axios": "0.28.1", "b64-to-blob": "1.2.19", @@ -166,7 +164,6 @@ "chroma-js": "1.3.7", "classnames": "2.2.5", "codemirror": "5.65.16", - "colorbrewer": "1.0.0", "concurrently": "6.4.0", "connected-react-router": "6.3.2", "d3-format": "3.1.0", @@ -197,10 +194,8 @@ "intersection-observer": "0.7.0", "intl": "1.2.2", "ismobilejs": "0.5.0", - "istanbul-instrumenter-loader": "3.0.1", "json-2-csv": "5.5.1", "jsonlint-mod": "1.7.5", - "jsonpath": "1.1.1", "jszip": "3.10.1", "leaflet": "1.3.1", "leaflet-draw": "1.0.2", diff --git a/pom.xml b/pom.xml index 271f649b11..fe669a6e54 100644 --- a/pom.xml +++ b/pom.xml @@ -22,12 +22,12 @@ 2.0.4 1.5.4 2.3.1 - 5.6.12 + 5.7.12 2.19.0 2.16.1 1.12 2.4.2-geoserver - 2.7 + 2.14.0 2.1.3 diff --git a/project/standard/static/karma.conf.single-run.js b/project/standard/static/karma.conf.single-run.js index f4069c30d5..50d3f10426 100644 --- a/project/standard/static/karma.conf.single-run.js +++ b/project/standard/static/karma.conf.single-run.js @@ -15,16 +15,5 @@ module.exports = function karmaConfig(config) { "@js": path.resolve(__dirname, "js") } }); - testConfig.webpack.module.rules = [{ - test: /\.jsx?$/, - exclude: /(__tests__|node_modules|legacy|libs\\Cesium|libs\\html2canvas)\\|(__tests__|node_modules|legacy|libs\/Cesium|libs\/html2canvas)\/|webpack\.js|utils\/(openlayers|leaflet)/, - enforce: "post", - use: [ - { - loader: 'istanbul-instrumenter-loader', - options: { esModules: true } - } - ] - }, ...testConfig.webpack.module.rules]; config.set(testConfig); }; diff --git a/web/client/actions/__tests__/importer-test.js b/web/client/actions/__tests__/importer-test.js deleted file mode 100644 index 8a1d78e058..0000000000 --- a/web/client/actions/__tests__/importer-test.js +++ /dev/null @@ -1,315 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import expect from 'expect'; - -import { - IMPORTS_LOADING, - loadImports, - IMPORTS_LIST_LOADED, - loadImport, - IMPORT_LOADED, - createImport, - IMPORT_CREATED, - deleteImport, - IMPORT_DELETE, - runImport, - IMPORT_RUN_SUCCESS, - loadLayer, - LAYER_LOADED, - updateLayer, - loadTask, - IMPORTS_TASK_LOADED, - updateTask, - IMPORTS_TASK_UPDATED, - deleteTask, - IMPORTS_TASK_DELETE, - loadTransform, - IMPORTS_TRANSFORM_LOAD, - updateTransform, - IMPORTS_TRANSFORM_UPDATED, - editTransform, - IMPORTS_TRANSFORM_CHANGE, - deleteTransform, - IMPORTS_TRANSFORM_DELETE, - loadStylerTool, - selectWorkSpace, - loadWorkspaces, - createWorkspace, - IMPORTER_WORKSPACE_LOADED, - IMPORTER_WORKSPACE_SELECTED, - IMPORTER_WORKSPACE_CREATED, - dismissWorkspaceCreationStatus, - IMPORTER_WORKSPACE_STATUS_CHANGE -} from '../importer'; - -import { MAP_CONFIG_LOADED } from '../config'; - -/* This utility function runs a serie of test on an action creator - with multiple dispatch inside - You have to pass an array of functions that tests the action dispatched by - the action creator. TODO evaulate to put in a TestUtils. -*/ -const runAsyncTest = (url, action, tests, done, params = [], state) => { - let count = 0; - action(url, ...params)((actionResult) => { - try { - tests[count](actionResult); - count++; - if (count === tests.length - 1) { - done(); - } - } catch (e) { - done(e); - } - - - }, () => state); -}; -// NOTE use # to skip parameters by the API -describe('Test correctness of the importer actions', () => { - // most common test - const testLoading = (actionResult) => { - expect(actionResult.type).toBe(IMPORTS_LOADING); - }; - it('load imports list loading', (done) => { - const testImports = (actionResult) => { - let imports = actionResult && actionResult.imports; - expect(actionResult.type).toBe(IMPORTS_LIST_LOADED); - expect(imports).toExist(); - expect(imports.length).toBe(2); - }; - let url = 'base/web/client/test-resources/importer/imports.json#'; - let tests = [testLoading, testImports, testLoading ]; - runAsyncTest(url, loadImports, tests, done); - }); - it('single import load', (done) => { - const testImportCreation = (actionResult) => { - expect(actionResult.type).toBe(IMPORT_LOADED); - }; - let url = 'base/web/client/test-resources/importer/import.json#'; - let tests = [testLoading, testImportCreation ]; - runAsyncTest(url, loadImport, tests, done); - }); - it('import creation', (done) => { - const testImportCreation = (actionResult) => { - expect(actionResult.type).toBe(IMPORT_CREATED); - }; - let url = 'base/web/client/test-resources/importer/import.json#'; - let tests = [testLoading, testImportCreation ]; - runAsyncTest(url, createImport, tests, done, [1]); - }); - it('import delete', (done) => { - const testDeleteImport = (actionResult) => { - expect(actionResult.type).toBe(IMPORT_DELETE); - expect(actionResult.id).toBe(1); - }; - let url = 'base/web/client/test-resources/importer/import.json#'; - let tests = [testLoading, testDeleteImport, testLoading ]; - runAsyncTest(url, deleteImport, tests, done, [1]); - }); - it('import run', (done) => { - const testRunImport = (actionResult) => { - expect(actionResult.type).toBe(IMPORT_RUN_SUCCESS); - expect(actionResult.importId).toBe(1); - }; - let url = 'base/web/client/test-resources/importer/import.json#'; - let tests = [testLoading, testRunImport, testLoading ]; - runAsyncTest(url, runImport, tests, done, [1]); - }); - - // layer - it('layer load', (done) => { - const testLoadLayer = (actionResult) => { - expect(actionResult.type).toBe(LAYER_LOADED); - expect(actionResult.importId).toBe(1); - expect(actionResult.taskId).toBe(2); - expect(actionResult.layer).toExist(); - }; - let url = 'base/web/client/test-resources/importer/layer.json#'; - let tests = [testLoading, testLoadLayer, testLoading ]; - runAsyncTest(url, loadLayer, tests, done, [1, 2]); - }); - - it('layer update', (done) => { - const testLoadLayer = (actionResult) => { - expect(actionResult.type).toBe(LAYER_LOADED); - expect(actionResult.importId).toBe(1); - expect(actionResult.taskId).toBe(2); - }; - let url = 'base/web/client/test-resources/importer/task.json#'; - let tests = [testLoading, testLoadLayer, testLoading ]; - runAsyncTest(url, updateLayer, tests, done, [1, 2]); - }); - - // task - it('task load', (done) => { - const testLoadLayer = () => { - - }; - const testLoadTask = (actionResult) => { - expect(actionResult.type).toBe(IMPORTS_TASK_LOADED); - expect(actionResult.task).toExist(); - }; - let url = 'base/web/client/test-resources/importer/task.json#'; - let tests = [testLoading, testLoadTask, testLoadLayer, testLoading ]; - runAsyncTest(url, loadTask, tests, done); - }); - // task - it('task update', (done) => { - const testLoadLayer = () => { - - }; - const testLoadTask = (actionResult) => { - expect(actionResult.type).toBe(IMPORTS_TASK_UPDATED); - expect(actionResult.importId).toBe(1); - expect(actionResult.taskId).toBe(2); - expect(actionResult.task).toExist(); - }; - const testUpdateUI = (fun) => { - expect(fun).toExist(); - fun('base/web/client/test-resources/importer/task.json#', 1, 2); - }; - let url = 'base/web/client/test-resources/importer/task.json#'; - let state = { - importer: { - selectedImport: { - id: 1, - targetWorkspace: { - workspace: { - name: "TEST" - } - } - } - } - }; - let tests = [testLoading, testLoadTask, testLoadLayer, testUpdateUI]; - runAsyncTest(url, updateTask, tests, done, [1, 2, {}], state ); - - }); - - it('task element update', (done) => { - const testLoadLayer = () => { - - }; - const testLoadTask = (actionResult) => { - expect(actionResult.type).toBe(IMPORTS_TASK_UPDATED); - expect(actionResult.importId).toBe(1); - expect(actionResult.taskId).toBe(2); - expect(actionResult.task).toExist(); - }; - let url = 'base/web/client/test-resources/importer/task.json#'; - let tests = [testLoading, testLoadTask, testLoadLayer, testLoading ]; - runAsyncTest(url, updateTask, tests, done, [1, 2, {}, "target"]); - }); - it('task delete', (done) => { - const testdeleteTask = (actionResult) => { - expect(actionResult.type).toBe(IMPORTS_TASK_DELETE); - expect(actionResult.importId).toBe(1); - expect(actionResult.taskId).toBe(2); - }; - let url = 'base/web/client/test-resources/importer/task.json#'; - let tests = [testLoading, testdeleteTask, testLoading ]; - runAsyncTest(url, deleteTask, tests, done, [1, 2, {}]); - }); - - // transform - it('transform load', (done) => { - const testLoadTransform = (actionResult) => { - expect(actionResult.type).toBe(IMPORTS_TRANSFORM_LOAD); - expect(actionResult.importId).toBe(1); - expect(actionResult.taskId).toBe(2); - expect(actionResult.transformId).toBe(3); - expect(actionResult.transform).toExist(); - }; - let url = 'base/web/client/test-resources/importer/transform.json#'; - let tests = [testLoading, testLoadTransform, testLoading ]; - runAsyncTest(url, loadTransform, tests, done, [1, 2, 3]); - }); - // transform - it('transform update', (done) => { - const testUpdateTransform = (actionResult) => { - expect(actionResult.type).toBe(IMPORTS_TRANSFORM_UPDATED); - expect(actionResult.importId).toBe(1); - expect(actionResult.taskId).toBe(2); - expect(actionResult.transformId).toBe(3); - }; - let url = 'base/web/client/test-resources/importer/transform.json#'; - let tests = [testLoading, testUpdateTransform, testLoading ]; - runAsyncTest(url, updateTransform, tests, done, [1, 2, 3]); - }); - // transform - it('transform delete', (done) => { - const testdeleteTransform = (actionResult) => { - expect(actionResult.type).toBe(IMPORTS_TRANSFORM_DELETE); - expect(actionResult.importId).toBe(1); - expect(actionResult.taskId).toBe(2); - expect(actionResult.transformId).toBe(3); - }; - let url = 'base/web/client/test-resources/importer/transform.json#'; - let tests = [testLoading, testdeleteTransform, testLoading ]; - runAsyncTest(url, deleteTransform, tests, done, [1, 2, 3]); - }); - - it('transform edit', () => { - let transform = {options: [1, 2, 3]}; - const result = editTransform(transform); - expect(result.type).toBe(IMPORTS_TRANSFORM_CHANGE); - expect(result.transform).toBe(transform); - }); - // load styler - it('load styler tool', (done) => { - const testConfigureMap = (actionResult) => { - expect(actionResult.type).toBe(MAP_CONFIG_LOADED); - expect(actionResult.config).toExist(); - expect(actionResult.config.map).toExist(); - expect(actionResult.config.map.layers).toExist(); - expect(actionResult.config.map.layers.length).toBe(2); - done(); - }; - let url = 'base/web/client/test-resources/importer/layer.json#'; - let tests = [testConfigureMap]; - runAsyncTest(url, loadStylerTool, tests, done, [], {importer: {selectedImport: {targetWorkspace: { workspace: {name: "TEST"}}}}}); - }); - // select workspace - it('load styler tool', () => { - const wsName = "worskpace_name"; - let res = selectWorkSpace(wsName); - expect(res).toExist(); - expect(res.type).toBe(IMPORTER_WORKSPACE_SELECTED); - expect(res.workspace).toBe(wsName); - }); - - // load workspaces - it('load workspaces', (done) => { - const testWorkspaces = (actionResult) => { - expect(actionResult.type).toBe(IMPORTER_WORKSPACE_LOADED); - done(); - }; - let url = 'base/web/client/test-resources/geoserver/rest/workspaces.json#'; - let tests = [testWorkspaces]; - runAsyncTest(url, loadWorkspaces, tests, done, []); - }); - // load workspaces - it('create workspace', (done) => { - const testWorkspaceCreated = (actionResult) => { - expect(actionResult.type).toBe(IMPORTER_WORKSPACE_CREATED); - done(); - }; - let url = 'base/web/client/test-resources/geoserver/rest/workspaces.json#'; - let tests = [testLoading, testWorkspaceCreated]; - runAsyncTest(url, createWorkspace, tests, done, []); - }); - // load workspaces - it('update workspace creation status', () => { - let res = dismissWorkspaceCreationStatus(); - expect(res).toExist(); - expect(res.type).toBe(IMPORTER_WORKSPACE_STATUS_CHANGE); - }); - -}); diff --git a/web/client/actions/__tests__/mapPopups-test.js b/web/client/actions/__tests__/mapPopups-test.js index 18296feba6..1af4ecc32a 100644 --- a/web/client/actions/__tests__/mapPopups-test.js +++ b/web/client/actions/__tests__/mapPopups-test.js @@ -26,5 +26,9 @@ describe('test map popups action creators', () => { const action = POPUP.cleanPopups(); expect(action.type).toEqual(POPUP.CLEAN_MAP_POPUPS); }); + it('enable hide empty popup option', () => { + const action = POPUP.enableHideEmptyPopupOption(); + expect(action.type).toEqual(POPUP.ENABLE_HIDE_EMPTY_POPUP); + }); }); diff --git a/web/client/actions/__tests__/rasterstyler-test.js b/web/client/actions/__tests__/rasterstyler-test.js deleted file mode 100644 index 2abeb2ee40..0000000000 --- a/web/client/actions/__tests__/rasterstyler-test.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import expect from 'expect'; - -import { - SET_RASTERSTYLE_PARAMETER, - SET_RASTER_LAYER, - setRasterStyleParameter, - setRasterLayer -} from '../rasterstyler'; - -describe('Test correctness of the rasterstyle actions', () => { - - it('setRasterStyleParameter', () => { - const retVal = setRasterStyleParameter('cmp', 'property', 'val'); - expect(retVal).toExist(); - expect(retVal.type).toBe(SET_RASTERSTYLE_PARAMETER); - expect(retVal.component).toBe('cmp'); - expect(retVal.property).toBe('property'); - expect(retVal.value).toBe('val'); - }); - it('setRasterLayer', () => { - const retVal = setRasterLayer('layer'); - expect(retVal).toExist(); - expect(retVal.type).toBe(SET_RASTER_LAYER); - expect(retVal.layer).toBe('layer'); - }); -}); diff --git a/web/client/actions/__tests__/styler-test.js b/web/client/actions/__tests__/styler-test.js deleted file mode 100644 index f4565831de..0000000000 --- a/web/client/actions/__tests__/styler-test.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright 20167, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import expect from 'expect'; - -import { STYLER_RESET, SET_STYLER_LAYER, setStylerLayer, reset } from '../styler'; - -describe('Test correctness of the rasterstyle actions', () => { - - it('setStylerLayer', () => { - const layer = {name: "TEST"}; - const retVal = setStylerLayer(layer); - expect(retVal).toExist(); - expect(retVal.type).toBe(SET_STYLER_LAYER); - expect(retVal.layer).toBe(layer); - }); - it('setRasterLayer', () => { - const layer = {name: "TEST"}; - const retVal = reset(layer); - expect(retVal).toExist(); - expect(retVal.type).toBe(STYLER_RESET); - expect(retVal.layer).toBe(layer); - }); -}); diff --git a/web/client/actions/__tests__/tasks-test.js b/web/client/actions/__tests__/tasks-test.js deleted file mode 100644 index 951b7917ae..0000000000 --- a/web/client/actions/__tests__/tasks-test.js +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright 2015, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import expect from 'expect'; - -import { - taskSuccess, - taskStarted, - taskError, - startTask, - TASK_STARTED, - TASK_SUCCESS, - TASK_ERROR -} from '../tasks'; - -describe('Test correctness of the tasks actions', () => { - it('test taskSuccess action', () => { - let result = {value: "true"}; - let name = "myName"; - let actionPayload = null; - const retVal = taskSuccess(result, name, actionPayload); - expect(retVal).toExist(); - expect(retVal.type).toBe(TASK_SUCCESS); - expect(retVal.result).toBe(result); - expect(retVal.name).toBe(name); - expect(retVal.actionPayload).toBe(null); - }); - - it('test taskError action', () => { - let error = {value: "true"}; - let name = "myName"; - let actionPayload = null; - const retVal = taskError(error, name, actionPayload); - expect(retVal).toExist(); - expect(retVal.type).toBe(TASK_ERROR); - expect(retVal.error).toBe(error); - expect(retVal.name).toBe(name); - expect(retVal.actionPayload).toBe(null); - }); - - - it('test taskStarted action', () => { - let name = "myName"; - const retVal = taskStarted(name); - expect(retVal).toExist(); - expect(retVal.type).toBe(TASK_STARTED); - expect(retVal.name).toBe(name); - }); - - it('startTask', done => { - let started = false; - let executed = false; - let dispatchable = startTask((payload, callback) => {executed = true; expect(payload).toBe("payload"); callback(); }, "payload", "task", "actionPayload"); - dispatchable((action) => { - if (action.type === TASK_STARTED) { - expect(action.name).toBe("task"); - started = true; - } - if (action.type === TASK_SUCCESS) { - expect(action.actionPayload).toBe("actionPayload"); - expect(started).toBe(true); - expect(executed).toBe(true); - done(); - } - }); - }); -}); diff --git a/web/client/actions/importer.js b/web/client/actions/importer.js deleted file mode 100644 index 6218eaa211..0000000000 --- a/web/client/actions/importer.js +++ /dev/null @@ -1,721 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import API from '../api/geoserver/Importer'; - -import Workspaces from '../api/geoserver/Workspaces'; -import { configureMap } from './config'; -import { isString } from 'lodash'; -import assign from 'object-assign'; -export const IMPORTS_LOADING = 'IMPORTS_LOADING'; -export const IMPORTS_CREATION_ERROR = 'IMPORTS_CREATION_ERROR'; -export const IMPORT_CREATED = 'IMPORT_CREATED'; - -export const IMPORTS_TASK_CREATED = 'IMPORTS_TASK_CREATED'; -export const IMPORTS_TASK_LOADED = 'IMPORTS_TASK_LOADED'; -export const IMPORTS_TASK_LOAD_ERROR = 'IMPORTS_TASK_LOAD_ERROR'; -export const IMPORTS_TASK_UPDATED = 'IMPORTS_TASK_UPDATED'; -export const IMPORTS_TASK_DELETE = 'IMPORTS_TASK_DELETE'; -export const IMPORTS_TASK_CREATION_ERROR = 'IMPORTS_TASK_CREATION_ERROR'; - -export const LAYER_LOADED = 'LAYER_LOADED'; -export const LAYER_UPDATED = 'LAYER_UPDATED'; - -export const IMPORTS_TRANSFORM_LOAD = 'IMPORTS_TRANSFORM_LOAD'; -export const IMPORTS_TRANSFORM_LOAD_ERROR = 'IMPORTS_TRANSFORM_LOAD_ERROR'; -export const IMPORTS_TRANSFORM_CHANGE = 'IMPORTS_TRANSFORM_CHANGE'; - -export const IMPORTS_TRANSFORM_DELETE = 'IMPORTS_TRANSFORM_DELETE'; -export const IMPORTS_TRANSFORM_UPDATED = 'IMPORTS_TRANSFORM_UPDATED'; - -export const IMPORTS_FILE_UPLOADED = 'IMPORTS_FILE_UPLOADED'; -export const IMPORTS_UPLOAD_PROGRESS = 'IMPORTS_UPLOAD_PROGRESS'; - -export const IMPORTS_LIST_LOADED = 'IMPORTS_LIST_LOADED'; -export const IMPORTS_LIST_LOAD_ERROR = 'IMPORTS_LIST_LOAD_ERROR'; -export const TASK_PROGRESS_UPDATED = 'TASK_PROGRESS_UPDATED'; - -export const IMPORT_LOADED = 'IMPORT_LOADED'; -export const IMPORT_LOAD_ERROR = 'IMPORT_LOAD_ERROR'; -export const IMPORT_RUN_SUCCESS = 'IMPORT_RUN_SUCCESS'; -export const IMPORT_RUN_ERROR = 'IMPORT_RUN_ERROR'; -export const IMPORT_DELETE = 'IMPORT_DELETE'; -export const IMPORT_DELETE_ERROR = 'IMPORT_DELETE_ERROR'; - -export const IMPORTER_WORKSPACE_SELECTED = 'IMPORTER_WORKSPACE_SELECTED'; -export const IMPORTER_WORKSPACE_LOADED = 'IMPORTER_WORKSPACE_LOADED'; -export const IMPORTER_WORKSPACE_CREATED = 'IMPORTER_WORKSPACE_CREATED'; -export const IMPORTER_WORKSPACE_CREATION_ERROR = 'IMPORTER_WORKSPACE_CREATION_ERROR'; -export const IMPORTER_WORKSPACE_STATUS_CHANGE = 'IMPORTER_WORKSPACE_STATUS_CHANGE'; -/** *****************/ -/* UTILITY */ -/** *****************/ - -/** - * Check if task matches with the preset. - * The match is by state, data.file.format and data.file.name - * (also regex allowed for file name). - */ -export const matchPreset = function(preset, task) { - if (preset.state && preset.state !== task.state) { - return false; - } - if (preset.data && task.data) { - if (preset.data.format && preset.data.format !== task.data.format) { - return false; - } - if (preset.data.file && preset.data.file !== task.data.file) { - try { - let patt = new RegExp(preset.data.file); - if (!patt.test(task.data.file)) { - return false; - } - } catch (e) { - return false; - } - - } - } - return true; -}; -// for the moment supports only dataStore.name with only one change -export const applyPlaceholders = function(preset, model) { - - let replaceTargetWorkspace = function(el) { - if (isString(el)) { - return el.replace("{targetWorkspace}", model.targetWorkspace && model.targetWorkspace.workspace && model.targetWorkspace.workspace.name); - } - return el; - }; - if (preset && preset.changes && preset.changes.target && preset.changes.target.dataStore && preset.changes.target.dataStore.name) { - return assign({}, preset, { - changes: assign({}, preset.changes, { - target: assign({}, preset.changes.target, { - dataStore: assign({}, preset.changes.target.dataStore, { - name: replaceTargetWorkspace(preset.changes.target.dataStore.name) - }) - }) - }) - }); - } - return preset; - -}; -/** *****************/ -/* ACTION CREATORS */ -/** *****************/ - -export function loading(details, isLoading = true) { - return { - type: IMPORTS_LOADING, - loading: isLoading, - details: details - }; -} -export function loadError(e) { - return { - type: IMPORTS_LIST_LOAD_ERROR, - error: e - }; -} - -/** IMPORT **/ -export function importCretationError(e) { - return { - type: IMPORTS_CREATION_ERROR, - error: e - }; -} -export function importCreated(importObj) { - return { - type: IMPORT_CREATED, - "import": importObj - }; -} - -export function importTaskCreated(importId, tasks) { - return { - type: IMPORTS_TASK_CREATED, - importId: importId, - tasks: tasks - }; -} - -export function importTaskUpdated(task, importId, taskId) { - return { - type: IMPORTS_TASK_UPDATED, - task, - importId, - taskId - }; -} - -export function importTaskDeleted(importId, taskId) { - return { - type: IMPORTS_TASK_DELETE, - importId, - taskId - }; -} - -export function importsLoaded(imports) { - return { - type: IMPORTS_LIST_LOADED, - imports: imports - }; -} - -export function importLoaded(importObj) { - return { - type: IMPORT_LOADED, - "import": importObj - }; -} -export function importLoadError(e) { - return { - type: IMPORT_LOAD_ERROR, - "error": e - }; -} - -export function importRunSuccess(importId) { - return { - type: IMPORT_RUN_SUCCESS, - importId - }; -} - -export function importRunError(importId, error) { - return { - type: IMPORT_RUN_ERROR, - importId, - error - }; -} -export function importDeleted(id) { - return { - type: IMPORT_DELETE, - id: id - }; -} - -export function importDeleteError(id, e) { - return { - type: IMPORT_DELETE_ERROR, - error: e, - id - }; -} - -/** TASKS **/ - -export function importTaskLoaded(task) { - return { - type: IMPORTS_TASK_LOADED, - task: task - }; -} -export function importTaskLoadError(e) { - return { - type: IMPORTS_TASK_LOAD_ERROR, - task: e - }; -} -export function importTaskCreationError(e) { - return { - type: IMPORTS_TASK_CREATION_ERROR, - error: e - }; -} - -export function layerLoaded(importId, taskId, layer) { - return { - type: LAYER_LOADED, - importId, - taskId, - layer - }; -} - -export function layerUpdated(importId, taskId, layer) { - return { - type: LAYER_LOADED, - importId, - taskId, - layer - }; -} -export function taskProgressUpdated(importId, taskId, info) { - return { - type: TASK_PROGRESS_UPDATED, - importId, - taskId, - info - }; -} -/** TRANSFORMS **/ -export function transformLoaded(importId, taskId, transformId, transform) { - return { - type: IMPORTS_TRANSFORM_LOAD, - importId, - taskId, - transformId, - transform - }; -} - -export function editTransform(transform) { - return { - type: IMPORTS_TRANSFORM_CHANGE, - transform - }; -} - -export function transformLoadError(importId, taskId, transformId, error) { - return { - type: IMPORTS_TRANSFORM_LOAD_ERROR, - importId, - taskId, - transformId, - error - }; -} -export function transformDeleted(importId, taskId, transformId) { - return { - type: IMPORTS_TRANSFORM_DELETE, - importId, - taskId, - transformId - }; -} -export function transformUpdated(importId, taskId, transformId, transform) { - return { - type: IMPORTS_TRANSFORM_UPDATED, - importId, - taskId, - transformId, - transform - }; -} -/** FILE UPLOAD **/ -export function fileUploaded(files) { - return { - type: IMPORTS_FILE_UPLOADED, - "files": files - }; -} - -export function uploadProgress(progress) { - return { - type: IMPORTS_UPLOAD_PROGRESS, - progress: progress - }; -} - -/** WORKSPACES **/ -export function selectWorkSpace(workspace) { - return { - type: IMPORTER_WORKSPACE_SELECTED, - workspace: workspace - }; -} -export function workspacesLoaded(workspaces) { - return { - type: IMPORTER_WORKSPACE_LOADED, - workspaces: workspaces - }; -} - -export function workspaceCreated(name) { - return { - type: IMPORTER_WORKSPACE_CREATED, - name - }; -} -export function workspaceCreationError(name, error) { - return { - type: IMPORTER_WORKSPACE_CREATION_ERROR, - name, - error - }; -} -/** *****************/ -/* DISPATCHERS */ -/** *****************/ - -/** IMPORT **/ -export function createImport(geoserverRestURL, body = {}) { - return (dispatch) => { - dispatch(loading()); - let options = { - headers: { - 'Content-Type': 'application/json' - } - }; - API.createImport(geoserverRestURL, body, options).then((response) => { - dispatch(importCreated(response && response.data && response.data.import)); - dispatch(loading(null, false)); - }).catch((e) => { - dispatch(importCretationError(e)); - dispatch(loading(null, false)); - }); - }; -} -export function loadImports(geoserverRestURL) { - return (dispatch) => { - dispatch(loading()); - API.getImports(geoserverRestURL).then((response) => { - dispatch(importsLoaded(response && response.data && response.data.imports)); - dispatch(loading(null, false)); - }).catch((e) => { - dispatch(loadError(e)); - dispatch(loading(null, false)); - }); - }; -} - -export function loadImport(geoserverRestURL, importId) { - return (dispatch) => { - dispatch(loading({importId: importId})); - API.loadImport(geoserverRestURL, importId).then((response) => { - dispatch(importLoaded(response && response.data && response.data.import)); - dispatch(loading({importId: importId}, false)); - }).catch((e) => { - dispatch(importLoadError(e)); - dispatch(loading({importId: importId}, false)); - }); - }; -} -export function deleteImport(geoserverRestURL, importId) { - return (dispatch) => { - dispatch(loading({importId: importId, message: "deleting"})); - API.deleteImport(geoserverRestURL, importId).then(() => { - dispatch(importDeleted(importId)); - dispatch(loading({importId: importId, message: "deleting"}, false)); - }).catch((e) => { - dispatch(importDeleteError(importId, e)); - dispatch(loading({importId: importId, message: "deleting"}, false)); - }); - }; -} - -export function runImport(geoserverRestURL, importId) { - return (dispatch, getState) => { - dispatch(loading({importId})); - API.runImport(geoserverRestURL, importId).then(() => { - dispatch(importRunSuccess(importId)); - if (getState && getState().selectedImport && getState().selectedImport.id === importId) { - dispatch(loading({importId}, false)); - dispatch(loadImport(geoserverRestURL, importId)); - - } else { - dispatch(loading({importId}, false)); - dispatch(loadImports(geoserverRestURL)); - - } - }).catch((e) => {importRunError(importId, e); }); - }; -} -/** LAYER **/ - -export function loadLayer(geoserverRestURL, importId, taskId) { - return (dispatch) => { - dispatch(loading({importId: importId, taskId: taskId, element: "layer", message: "loadinglayer"})); - return API.loadLayer(geoserverRestURL, importId, taskId).then((response) => { - dispatch(layerLoaded(importId, taskId, response && response.data && response.data.layer)); - dispatch(loading({importId: importId, taskId: taskId, element: "layer", message: "loadinglayer"}, false)); - }); - }; -} -export function updateLayer(geoserverRestURL, importId, taskId, layer) { - return (dispatch) => { - dispatch(loading({importId: importId, taskId: taskId, element: "layer", message: "loadinglayer"})); - - return API.updateLayer(geoserverRestURL, importId, taskId, layer).then((response) => { - dispatch(layerUpdated(importId, taskId, response && response.data && response.data.layer)); - dispatch(loading({importId: importId, taskId: taskId, element: "layer", message: "loadinglayer"}, false)); - }); - }; -} -/** TASKS **/ -export function loadTask(geoserverRestURL, importId, taskId) { - return (dispatch) => { - dispatch(loading({importId: importId, taskId: taskId})); - API.loadTask(geoserverRestURL, importId, taskId).then((response) => { - dispatch(importTaskLoaded(response && response.data && response.data.task)); - dispatch(loadLayer(geoserverRestURL, importId, taskId)); - dispatch(loading({importId: importId, taskId: taskId}, false)); - }).catch((e) => { - dispatch(importTaskLoadError(e)); - dispatch(loading({importId: importId, taskId: taskId}, false)); - }); - }; -} -export function updateUI(geoserverRestURL, importId, taskId) { - return (dispatch, getState) => { - let state = getState && getState() && getState().importer; - if (state && state.selectedImport && state.selectedImport.id === importId && state.selectedTask && state.selectedTask.id === taskId) { - dispatch(loadTask(geoserverRestURL, importId, taskId)); - dispatch(loading({importId, taskId}, false)); - } else if (state && state.selectedImport && state.selectedImport.id === importId) { - dispatch(loadImport(geoserverRestURL, importId)); - dispatch(loading({importId}, false)); - } else { - dispatch(loadImports(geoserverRestURL)); - dispatch(loading({importId}, false)); - } - }; -} -export function updateTask(geoserverRestURL, importId, taskId, body, element, message = "updating") { - return (dispatch) => { - let opts = { - headers: { - 'Content-Type': 'application/json' - } - }; - - dispatch(loading({importId: importId, taskId: taskId, message})); - return API.updateTask(geoserverRestURL, importId, taskId, element, body, opts).then((response) => { - dispatch(importTaskUpdated(response && response.data && response.data.task, importId, taskId)); - dispatch(loading({importId: importId, taskId: taskId}, false)); - dispatch(updateUI(geoserverRestURL, importId, taskId)); - }); - }; -} - -export function deleteTask(geoserverRestURL, importId, taskId) { - return (dispatch) => { - dispatch(loading({importId: importId, taskId: taskId, message: "deleting"})); - return API.deleteTask(geoserverRestURL, importId, taskId).then(() => { - dispatch(importTaskDeleted(importId, taskId)); - dispatch(loading({importId: importId, taskId: taskId, message: "deleting"}, false)); - dispatch(loading({importId: importId, message: "deleting"}, false)); - }); - }; -} - -export function updateProgress(geoserverRestURL, importId, taskId) { - return (dispatch) => { - return API.getTaskProgress(geoserverRestURL, importId, taskId).then((response) => { - dispatch(taskProgressUpdated(importId, taskId, response.data)); - }); - }; -} -/** TRANFORMS **/ -export function loadTransform(geoserverRestURL, importId, taskId, transformId) { - return (dispatch) => { - dispatch(loading({importId: importId, taskId: taskId, transformId: transformId, message: "loading"})); - return API.loadTransform(geoserverRestURL, importId, taskId, transformId).then((response) => { - let transform = response && response.data; - dispatch(transformLoaded(importId, taskId, transformId, transform)); - dispatch(loading({importId: importId, taskId: taskId, transformId: transformId, message: "loading"}, false)); - }).catch((e) => {transformLoadError(importId, taskId, transformId, e); }); - }; -} -export function deleteTransform(geoserverRestURL, importId, taskId, transformId) { - return (dispatch, getState) => { - dispatch(loading({importId: importId, taskId: taskId, transformId: transformId, message: "loading"})); - return API.deleteTransform(geoserverRestURL, importId, taskId, transformId).then(() => { - dispatch(transformDeleted(importId, taskId, transformId)); - dispatch(loading({importId: importId, taskId: taskId, transformId: transformId, message: "loading"}, false)); - let state = getState().importer; - if (state.selectedTask && state.selectedTask.id === taskId) { - dispatch(loadTask(geoserverRestURL, importId, taskId)); - dispatch(loading({importId: importId, taskId: taskId, transformId: transformId, message: "loading"}, false)); - } - }).catch((e) => {transformLoadError(importId, taskId, transformId, e); }); // TODO transform delete error - }; -} - -export function updateTransform(geoserverRestURL, importId, taskId, transformId, transform) { - return (dispatch) => { - dispatch(loading({importId: importId, taskId: taskId, transformId: transformId, message: "loading"})); - return API.updateTransform(geoserverRestURL, importId, taskId, transformId, transform).then((response) => { - dispatch(transformUpdated(importId, taskId, transformId, response && response.data)); - dispatch(loading({importId: importId, taskId: taskId, transformId: transformId, message: "loading"}, false)); - }).catch((e) => { - transformLoadError(importId, taskId, transformId, e); // TODO transform update error - dispatch(loading({importId: importId, taskId: taskId, transformId: transformId, message: "loading"}, false)); - }); - }; -} -/** PRESETS **/ -export function applyPreset(geoserverRestURL, importId, task, preset) { - - return (dispatch) => { - const applyChange = (element, change) => { // TODO better as an action - dispatch(updateTask(geoserverRestURL, importId, task.id, change, element, "applyPresets")); - }; - if (preset.changes) { - // update target, layer - Object.keys(preset.changes).forEach((element) => { - let values = preset.changes[element]; - if (Array.isArray(values)) { - values.forEach(applyChange.bind(null, element)); - } else { - applyChange(element, values); - } - }); - } - if (preset.transforms) { - preset.transforms.forEach( (transform) => { - dispatch(loading({importId: importId, taskId: task.id, message: "applyPresets"})); - API.addTransform(geoserverRestURL, importId, task.id, transform).then(() => { - dispatch(loading({importId: importId, taskId: task.id, message: "applyPresets"}, false)); - }); - }); - } - }; -} -export function applyPresets(geoserverRestURL, importId, tasks, presets) { - return (dispatch) => { - if (tasks) { - tasks.forEach( (task) => { - presets.forEach( (preset) => { - if (task.data) { - if (matchPreset(preset, task)) { - dispatch(applyPreset(geoserverRestURL, importId, task, preset)); - } - } else { - dispatch(loading({importId: importId, taskId: task.id, message: "analyzing"})); - API.loadTask(geoserverRestURL, importId, task.id).then((response) => { - dispatch(loading({importId: importId, taskId: task.id}, false)); - let completeTask = response && response.data && response.data.task; - if (matchPreset(preset, completeTask)) { - dispatch(applyPreset(geoserverRestURL, importId, completeTask, preset)); - } - }); - } - - }); - }); - } - }; -} - -export function loadWorkspaces(geoserverRestURL) { - return (dispatch) => { - Workspaces.getWorkspaces(geoserverRestURL).then((result) => { - dispatch(workspacesLoaded(result.workspaces.workspace)); - }); - }; -} - -export function createWorkspace(geoserverRestURL, name, datastores = []) { - return (dispatch) => { - dispatch(loading()); - Workspaces.createWorkspace(geoserverRestURL, name).then(() => { - dispatch(workspaceCreated(name)); - - let count = datastores.length; - if (count === 0) { - dispatch(loading(null, false)); - } else { - datastores.forEach((ds) => { - // replace placeholder (this is required because in the importer the datastore name have to be unique, out of workspace) - let datastore = ds; - let dsname = ds.dataStore && ds.dataStore.name; - let datastoreobj = assign({}, ds.dataStore, {name: dsname.replace("{workspace}", name)}); - datastore = assign({}, ds, {dataStore: datastoreobj}); - - Workspaces.createDataStore(geoserverRestURL, name, datastore).then(() => { - count--; - if (count === 0) { - dispatch(loading(null, false)); - } - }); - }); - } - }).catch((error) => { - dispatch(workspaceCreationError(name, error)); - dispatch(loading(null, false)); - - }); - }; -} - -export function dismissWorkspaceCreationStatus() { - return { - type: IMPORTER_WORKSPACE_STATUS_CHANGE, - state: null - }; -} - -/** UPLOAD **/ -export function uploadImportFiles(geoserverRestURL, importId, files, presets) { - return (dispatch, getState) => { - dispatch(loading({importId: importId, uploadingFiles: files})); - let progressOpts = { - progress: (progressEvent) => { - dispatch(uploadProgress(progressEvent.loaded / progressEvent.total)); - } - }; - API.uploadImportFiles(geoserverRestURL, importId, files, progressOpts).then((response) => { - let tasks = response && response.data && response.data.tasks || response && response.data && [response.data.task]; - dispatch(fileUploaded(files)); - dispatch(importTaskCreated(importId, tasks)); - let state = getState(); - let impState = state.importer; - if (impState && impState.selectedImport && impState.selectedImport.id === importId && tasks && tasks.length > 1) { - dispatch(loadImport(geoserverRestURL, importId)); - } - if (presets) { - let newPreset = presets.map((preset) => applyPlaceholders(preset, state && state.importer && state.importer.selectedImport)); - dispatch(applyPresets(geoserverRestURL, importId, tasks, newPreset)); - } - dispatch(loading({importId: importId}, false)); - }).catch((e) => { - dispatch(importTaskCreationError(e)); - dispatch(loading({importId: importId}, false)); - }); - }; -} - -/** STYLER **/ -export function loadStylerTool(geoserverRestURL, importId, taskId) { - return (dispatch, getState) => { - return API.loadLayer(geoserverRestURL, importId, taskId).then((layerResponse) => { - let layer = layerResponse && layerResponse.data && layerResponse.data.layer; - - let importObj = getState && getState().importer && getState().importer.selectedImport; - let workspace = importObj.targetWorkspace && importObj.targetWorkspace.workspace.name; - let stylerMapConfig = { - "version": 2, - "map": { - "projection": "EPSG:3857", - "units": "m", - "center": {"x": 0, "y": 0, "crs": "EPSG:3857"}, - "zoom": 2, - "maxExtent": [ - -20037508.34, -20037508.34, - 20037508.34, 20037508.34 - ], - "layers": [{ - "type": "osm", - "title": "Open Street Map", - "name": "mapnik", - "source": "osm", - "group": "background", - "visibility": true - }] - } - }; - let config = stylerMapConfig; - config.map.layers = (config.map.layers || []).concat({ - "type": "wms", - "url": "/geoserver/wms", - "visibility": true, - "title": layer.title, - "name": workspace + ":" + layer.name, - "group": "Styler", - "format": "image/png8" - }); - dispatch(configureMap(config)); - }); - - }; -} diff --git a/web/client/actions/mapPopups.js b/web/client/actions/mapPopups.js index ed001897fc..66ab51c315 100644 --- a/web/client/actions/mapPopups.js +++ b/web/client/actions/mapPopups.js @@ -10,6 +10,7 @@ export const ADD_MAP_POPUP = 'MAP:ADD_POPUP'; export const REMOVE_MAP_POPUP = 'MAP:REMOVE_POPUP'; export const CLEAN_MAP_POPUPS = 'MAP:CLEAN_POPUPS'; +export const ENABLE_HIDE_EMPTY_POPUP = 'MAP:ENABLE_HIDE_EMPTY_POPUP'; export const addPopup = (id, options, single = true) => ({ type: ADD_MAP_POPUP, @@ -26,3 +27,7 @@ export const removePopup = (id) => ({ export const cleanPopups = () => ({ type: CLEAN_MAP_POPUPS }); + +export const enableHideEmptyPopupOption = () => ({ + type: ENABLE_HIDE_EMPTY_POPUP +}); diff --git a/web/client/actions/rasterstyler.js b/web/client/actions/rasterstyler.js deleted file mode 100644 index cbca1700c1..0000000000 --- a/web/client/actions/rasterstyler.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -export const SET_RASTERSTYLE_PARAMETER = 'SET_RASTERSTYLE_PARAMETER'; -export const SET_RASTER_LAYER = 'SET_RASTER_LAYER'; - -export function setRasterStyleParameter(component, property, value) { - return { - type: SET_RASTERSTYLE_PARAMETER, - component, - property, - value - }; -} -export function setRasterLayer(layer) { - return { - type: SET_RASTER_LAYER, - layer - }; -} diff --git a/web/client/actions/share.js b/web/client/actions/share.js deleted file mode 100644 index 079294dad2..0000000000 --- a/web/client/actions/share.js +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Copyright 2015, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -export const CHANGE_SHARE_STATE = 'CHANGE_SHARE_STATE'; - -export function changeShareState(enabled) { - return { - type: CHANGE_SHARE_STATE, - enabled: enabled - }; -} diff --git a/web/client/actions/styler.js b/web/client/actions/styler.js deleted file mode 100644 index 673f4dbd2a..0000000000 --- a/web/client/actions/styler.js +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -export const STYLE_SAVED = 'STYLER_STYLE_SAVED'; -export const SET_STYLER_LAYER = 'SET_STYLER_LAYER'; -export const STYLE_SAVE_ERROR = 'STYLE_SAVE_ERROR'; -export const STYLER_RESET = 'STYLER_RESET'; - -import Layers from '../api/geoserver/Layers'; -import { saveStyle } from '../api/geoserver/Styles'; - -export function setStylerLayer(layer) { - return { - type: SET_STYLER_LAYER, - layer - }; -} -export function styleSaved(name, style) { - return { - type: STYLE_SAVED, - name, - style - }; -} -export function styleSaveError(layer, style, error) { - return { - type: STYLE_SAVE_ERROR, - layer, - style, - error - }; -} -export function reset(layer) { - return { - type: STYLER_RESET, - layer - }; -} -export function saveLayerDefaultStyle(geoserverBaseUrl, layerName, style) { - return (dispatch) => { - return Layers.getLayer(geoserverBaseUrl, layerName).then((layer) => { - saveStyle(geoserverBaseUrl, layer.defaultStyle && layer.defaultStyle.name, style).then(()=> { - dispatch(styleSaved(layer.defaultStyle.name, style)); - }).catch((e) => {styleSaveError(layerName, layer.defaultStyle, e); }); - - }).catch((e) => {styleSaveError(layerName, null, e); }); - - }; -} diff --git a/web/client/actions/tasks.js b/web/client/actions/tasks.js deleted file mode 100644 index ec41369929..0000000000 --- a/web/client/actions/tasks.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -export const TASK_STARTED = 'TASK_STARTED'; -export const TASK_SUCCESS = 'TASK_SUCCESS'; -export const TASK_ERROR = 'TASK_ERROR'; - -export function taskSuccess(result, name, actionPayload) { - return { - type: TASK_SUCCESS, - result, - name, - actionPayload - }; -} - -export function taskStarted(name) { - return { - type: TASK_STARTED, - name - }; -} - -export function taskError(error, name, actionPayload) { - return { - type: TASK_ERROR, - error, - name, - actionPayload - }; -} - -export function startTask(task, taskPayload, name, actionPayload) { - return (dispatch) => { - dispatch(taskStarted(name)); - task(taskPayload, (result) => { - dispatch(taskSuccess(result, name, actionPayload)); - }, (error) => { - dispatch(taskError(error, name, actionPayload)); - }); - }; -} - diff --git a/web/client/actions/vectorstyler.js b/web/client/actions/vectorstyler.js deleted file mode 100644 index 44bea603ee..0000000000 --- a/web/client/actions/vectorstyler.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import uuid from 'uuid'; - -export const SET_VECTOR_RULE_PARAMETER = 'SET_VECTOR_RULE_PARAMETER'; -export const NEW_VECTOR_RULE = 'NEW_VECTOR_RULE'; -export const REMOVE_VECTOR_RULE = 'REMOVE_VECTOR_RULE'; -export const SELECT_VECTOR_RULE = 'SELECT_VECTOR_RULE'; -export const SET_VECTORSTYLE_PARAMETER = 'SET_VECTORSTYLE_PARAMETER'; -export const SET_VECTOR_LAYER = 'SET_VECTOR_LAYER'; - -export function setVectorStyleParameter(component, property, value) { - return { - type: SET_VECTORSTYLE_PARAMETER, - component, - property, - value - }; -} -export function setVectorRuleParameter(property, value) { - return { - type: SET_VECTOR_RULE_PARAMETER, - property, - value - }; -} -export function setVectorLayer(layer) { - return { - type: SET_VECTOR_LAYER, - layer - }; -} -export function newVectorRule() { - return { - type: NEW_VECTOR_RULE, - id: uuid.v1() - }; -} -export function removeVectorRule(id) { - return { - type: REMOVE_VECTOR_RULE, - id - }; -} -export function selectVectorRule(id) { - return { - type: SELECT_VECTOR_RULE, - id - }; -} diff --git a/web/client/api/CORS.js b/web/client/api/CORS.js new file mode 100644 index 0000000000..c6b00adbff --- /dev/null +++ b/web/client/api/CORS.js @@ -0,0 +1,67 @@ +/* + * Copyright 2024, GeoSolutions Sas. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. + */ + +import url from 'url'; +import { needProxy } from '../utils/ProxyUtils'; + +let proxyCache = {}; + + +const getBaseUrl = (uri) => { + const urlParts = url.parse(uri); + return urlParts.protocol + "//" + urlParts.host + urlParts.pathname; +}; +/** + * Set the proxy value for cached uri + * @param {string} uri - uri string to test + * @param {boolean} value - value to cache + * @returns the passed value + */ +export const setProxyCacheByUrl = (uri, value)=>{ + const baseUrl = getBaseUrl(uri); + proxyCache[baseUrl] = value; + return value; +}; +/** + * Get the proxy value for cached uri + * @param {string} uri - uri string to test + * @returns true, false or undefined, if undefined means the value has not been stored + */ +export const getProxyCacheByUrl = (uri)=>{ + const baseUrl = getBaseUrl(uri); + return proxyCache[baseUrl]; +}; +/** + * Perform a fetch request to test if a service support CORS + * @param {string} uri - uri string to test + * @returns true if the proxy is required + */ +export const testCors = (uri) => { + const proxy = getProxyCacheByUrl(uri); + if (needProxy(uri) === false) { + setProxyCacheByUrl(uri, false); + return Promise.resolve(false); + } + if (proxy !== undefined) { + return Promise.resolve(proxy); + } + return fetch(uri, { + method: 'GET', + mode: 'cors' + }) + .then((response) => { + if (!response.ok) { + return false; + } + return setProxyCacheByUrl(uri, false); + }) + .catch(() => { + // in server side error it goes to response(then) anyway, so we can assume that if we get here we have a cors error with no previewable response + return setProxyCacheByUrl(uri, true); + }); +}; diff --git a/web/client/api/MapConfigDAO.js b/web/client/api/MapConfigDAO.js deleted file mode 100644 index b3b49be551..0000000000 --- a/web/client/api/MapConfigDAO.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2015, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import axios from '../libs/ajax'; - -import ConfigUtils from '../utils/ConfigUtils'; -/** - * API for local config - */ -var Api = { - get: function(url) { - return axios.get(url).then((response) => { - return response.data; - }); - }, - - /** - * Returns Merged configurations from base url and GeoStore - */ - getMergedConfig: function(baseConfigURL, mapId, geoStoreBase) { - var url = ( geoStoreBase || "/mapstore/rest/geostore/" ) + "data/" + mapId; - if (!mapId) { - return Api.get(baseConfigURL); - } - - return axios.all([axios.get(baseConfigURL), axios.get(url)]) - .then( (args) => { - var baseConfig = args[0].data; - var mapConfig = args[1].data; - return ConfigUtils.mergeConfigs(baseConfig, mapConfig); - }).catch( () => { - return Api.get(baseConfigURL); - }); - } -}; - -export default Api; diff --git a/web/client/api/WFS3.js b/web/client/api/WFS3.js deleted file mode 100644 index b3d5aff87d..0000000000 --- a/web/client/api/WFS3.js +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2019, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import axios from 'axios'; -import head from 'lodash/head'; -import isString from 'lodash/isString'; -import ConfigUtils from '../utils/ConfigUtils'; - -const capabilitiesCache = {}; - -const collectionToLayer = (collection) => { - const { name, title, extent, links } = collection; - const spatial = extent && extent.spatial || [-180, -90, 180, 90]; - const { href: layerUrl, type: format } = head((links || []).filter(({ rel }) => rel === 'tiles')) || {}; - const { href: tilingSchemes } = head((links || []).filter(({ rel }) => rel === 'tilingSchemes')) || {}; - const { href: tilingScheme } = head((links || []).filter(({ rel }) => rel === 'tilingScheme')) || {}; - return { - name, - title, - type: 'wfs3', - visibility: true, - url: layerUrl, - format, - tilingScheme, - tilingSchemes, - bbox: { - crs: 'EPSG:4326', - bounds: { - minx: spatial[0], - miny: spatial[1], - maxx: spatial[2], - maxy: spatial[3] - } - } - }; -}; - -const searchAndPaginate = (json = {}, startPosition, maxRecords, text, url) => { - const { collections } = json; - const filteredLayers = collections - .filter((layer = {}) => !text - || layer.name && layer.name.toLowerCase().indexOf(text.toLowerCase()) !== -1 - || layer.title && layer.title.toLowerCase().indexOf(text.toLowerCase()) !== -1); - return { - numberOfRecordsMatched: filteredLayers.length, - numberOfRecordsReturned: Math.min(maxRecords, filteredLayers.length), - nextRecord: startPosition + Math.min(maxRecords, filteredLayers.length) + 1, - records: filteredLayers - .filter((layer, index) => index >= startPosition - 1 && index < startPosition - 1 + maxRecords) - .map((collection) => ({ - ...collection, - ...collectionToLayer(collection), - capabilitiesUrl: url - })) - }; -}; - -const parseUrl = function(url) { - const serviceUrl = (url || '').split(/\/wfs3\//)[0]; - return `${serviceUrl}/wfs3/collections`; -}; - -export const getRecords = function(url, startPosition, maxRecords, text) { - const cached = capabilitiesCache[url]; - if (cached && new Date().getTime() < cached.timestamp + (ConfigUtils.getConfigProp('cacheExpire') || 60) * 1000) { - return new Promise((resolve) => { - resolve(searchAndPaginate(cached.data, startPosition, maxRecords, text, url)); - }); - } - return axios.get(parseUrl(url)) - .then(({ data }) => { - capabilitiesCache[url] = { - timestamp: new Date().getTime(), - data - }; - return searchAndPaginate(data, startPosition, maxRecords, text, url); - }); -}; - -export const textSearch = function(url, startPosition, maxRecords, text) { - return getRecords(url, startPosition, maxRecords, text); -}; - -export const getTilingSchemes = (layer) => { - const { tilingSchemes, tilingScheme } = layer; - if (isString(tilingSchemes)) { - return axios.get(tilingSchemes) - .then(({ data }) => { - return data && data.tilingSchemes && data.tilingSchemes.length > 0 - ? axios.all( - data.tilingSchemes.map((tilingSchemeId) => - axios.get(tilingScheme.replace('{tilingSchemeId}', tilingSchemeId)) - .then(({ data: scheme }) => scheme) - .catch(() => null) - ) - ) - .then((schemes) => ({ - tilingSchemes: { - url: tilingSchemes, - schemes: schemes.filter(scheme => scheme) - }, - allowedSRS: schemes - .filter(scheme => scheme) - .reduce((acc, { supportedCRS }) => { - return { - ...acc, - [supportedCRS]: true - }; - }, {}) - })) - : { - tilingSchemes: { - url: tilingSchemes, - schemes: null - }, - allowedSRS: {} - }; - }); - } - return new Promise((resolve) => resolve(tilingSchemes)); -}; - -export const getLayerFromId = (serviceUrl, collectionId) => { - return axios.get(`${parseUrl(serviceUrl)}/${collectionId}`) - .then(({ data: collection }) => { - const layer = collectionToLayer(collection); - return getTilingSchemes(layer) - .then((params) => ({ - ...layer, - ...params - })); - }); -}; - -export const reset = () => { - Object.keys(capabilitiesCache).forEach(key => { - delete capabilitiesCache[key]; - }); -}; diff --git a/web/client/api/__tests__/WFS3-test.js b/web/client/api/__tests__/WFS3-test.js deleted file mode 100644 index c3b28b5ba3..0000000000 --- a/web/client/api/__tests__/WFS3-test.js +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright 2019, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import expect from 'expect'; -import { - getTilingSchemes, - getLayerFromId, - textSearch, - reset -} from '../WFS3'; -import MockAdapter from 'axios-mock-adapter'; -import axios from '../../libs/ajax'; - -let mockAxios; - -describe('Test WFS3 API', () => { - - beforeEach(done => { - mockAxios = new MockAdapter(axios); - setTimeout(done); - }); - - afterEach(done => { - mockAxios.restore(); - setTimeout(done); - reset(); - }); - - it('test getTilingSchemes', (done) => { - - const TILING_SCHEMES_URL = '/geoserver/wfs3/collections/layer_name/tiles'; - const TILING_SCHEMES_ID = 'GoogleMapsCompatible'; - const TILING_SCHEME = { - type: 'TileMatrixSet', - identifier: 'GoogleMapsCompatible', - title: 'GoogleMapsCompatible', - supportedCRS: 'EPSG:3857', - tileMatrix: [{ - matrixHeight: 1, - matrixWidth: 1, - tileHeight: 256, - tileWidth: 256, - identifier: '0', - scaleDenominator: 559082263.9508929, - topLeftCorner: [ - -20037508.34, - 20037508 - ], - type: 'TileMatrix' - }], - boundingBox: { - crs: 'http://www.opengis.net/def/crs/EPSG/0/3857', - lowerCorner: [ - -20037508.34, - -20037508.34 - ], - upperCorner: [ - 20037508.34, - 20037508.34 - ], - type: 'BoundingBox' - }, - wellKnownScaleSet: 'http://www.opengis.net/def/wkss/OGC/1.0/GoogleMapsCompatible' - }; - - mockAxios.onGet(TILING_SCHEMES_URL).reply(() => { - return [ 200, { tilingSchemes: [ TILING_SCHEMES_ID ] }]; - }); - - mockAxios.onGet(`${TILING_SCHEMES_URL}/${TILING_SCHEMES_ID}`).reply(() => { - return [ 200, TILING_SCHEME]; - }); - - const layer = { - tilingScheme: `${TILING_SCHEMES_URL}/{tilingSchemeId}`, - tilingSchemes: TILING_SCHEMES_URL - }; - - getTilingSchemes(layer) - .then(({ tilingSchemes, allowedSRS }) => { - expect(allowedSRS).toEqual({ 'EPSG:3857': true }); - expect(tilingSchemes).toEqual({ - url: TILING_SCHEMES_URL, - schemes: [ TILING_SCHEME ] - }); - done(); - }); - }); - - it('test getLayerFromId', (done) => { - const SERVICE_URL = '/geoserver/wfs3/collections/'; - const COLLECTIONS_ID = 'layer_name'; - const TILING_SCHEMES_URL = '/geoserver/wfs3/collections/layer_name/tiles'; - const TILING_SCHEMES_ID = 'GoogleMapsCompatible'; - const COLLECTION = { - name: COLLECTIONS_ID, - title: 'Layer Title', - extent: { - spatial: [-180, -90, 180, 90] - }, - links: [ - { - href: '/geoserver/wfs3/collections/layer_name/tiles/{tilingSchemeId}/{level}/{row}/{col}', - rel: 'tiles', - type: 'application/vnd.mapbox-vector-tile' - }, - { - href: '/geoserver/wfs3/collections/layer_name/tiles/{tilingSchemeId}', - rel: 'tilingScheme', - type: 'application/json', - title: '...' - }, - { - href: TILING_SCHEMES_URL, - rel: 'tilingSchemes', - type: 'application/json', - title: '...' - } - ] - }; - - const TILING_SCHEME = { - type: 'TileMatrixSet', - identifier: 'GoogleMapsCompatible', - title: 'GoogleMapsCompatible', - supportedCRS: 'EPSG:3857', - tileMatrix: [{ - matrixHeight: 1, - matrixWidth: 1, - tileHeight: 256, - tileWidth: 256, - identifier: '0', - scaleDenominator: 559082263.9508929, - topLeftCorner: [ - -20037508.34, - 20037508 - ], - type: 'TileMatrix' - }], - boundingBox: { - crs: 'http://www.opengis.net/def/crs/EPSG/0/3857', - lowerCorner: [ - -20037508.34, - -20037508.34 - ], - upperCorner: [ - 20037508.34, - 20037508.34 - ], - type: 'BoundingBox' - }, - wellKnownScaleSet: 'http://www.opengis.net/def/wkss/OGC/1.0/GoogleMapsCompatible' - }; - - mockAxios.onGet(`${SERVICE_URL}${COLLECTIONS_ID}`).reply(() => { - return [ 200, COLLECTION]; - }); - - mockAxios.onGet(TILING_SCHEMES_URL).reply(() => { - return [ 200, { tilingSchemes: [ TILING_SCHEMES_ID ] }]; - }); - - mockAxios.onGet(`${TILING_SCHEMES_URL}/${TILING_SCHEMES_ID}`).reply(() => { - return [ 200, TILING_SCHEME]; - }); - - getLayerFromId(SERVICE_URL, COLLECTIONS_ID) - .then((layer) => { - expect(layer).toEqual({ - name: COLLECTIONS_ID, - title: 'Layer Title', - type: 'wfs3', - visibility: true, - url: '/geoserver/wfs3/collections/layer_name/tiles/{tilingSchemeId}/{level}/{row}/{col}', - format: 'application/vnd.mapbox-vector-tile', - tilingScheme: '/geoserver/wfs3/collections/layer_name/tiles/{tilingSchemeId}', - tilingSchemes: { - url: TILING_SCHEMES_URL, - schemes: [ TILING_SCHEME ] - }, - allowedSRS: { 'EPSG:3857': true }, - bbox: { - crs: 'EPSG:4326', - bounds: { - minx: -180, - miny: -90, - maxx: 180, - maxy: 90 - } - }}); - done(); - }); - }); - it('test textSearch', (done) => { - const TILING_SCHEMES_URL = '/geoserver/wfs3/collections'; - const START_POSITION = 1; - const MAX_RECORDS = 1; - const TEXT = ''; - const COLLECTIONS = [ - { - name: 'layer_name_01', - title: 'layer title 01', - extent: { - spatial: [-180, -90, 180, 90] - }, - links: [] - }, - { - name: 'layer_name_02', - title: 'layer title 02', - extent: { - spatial: [-180, -90, 180, 90] - }, - links: [] - } - ]; - - mockAxios.onGet(TILING_SCHEMES_URL) - .reply(() => { - return [ 200, { collections: COLLECTIONS }]; - }); - textSearch(TILING_SCHEMES_URL, START_POSITION, MAX_RECORDS, TEXT) - .then((res) => { - expect(res).toEqual({ - numberOfRecordsMatched: 2, - numberOfRecordsReturned: 1, - nextRecord: 3, - records: [{ - name: 'layer_name_01', - title: 'layer title 01', - extent: { spatial: [ -180, -90, 180, 90 ] }, - links: [], - type: 'wfs3', - visibility: true, - url: undefined, - format: undefined, - tilingScheme: undefined, - tilingSchemes: undefined, - bbox: { crs: 'EPSG:4326', bounds: { minx: -180, miny: -90, maxx: 180, maxy: 90 } }, - capabilitiesUrl: '/geoserver/wfs3/collections' - }] - }); - done(); - }); - }); -}); - diff --git a/web/client/api/geoserver/Importer.js b/web/client/api/geoserver/Importer.js deleted file mode 100644 index 1ca8f36204..0000000000 --- a/web/client/api/geoserver/Importer.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import axios from '../../libs/ajax'; - - -var Api = { - getImports: function(geoserverBaseUrl, options) { - let url = geoserverBaseUrl + "imports"; - return axios.get(url, options); - }, - createImport: function(geoserverBaseUrl, body, options) { - let url = geoserverBaseUrl + "imports"; - return axios.post(url, body, options); - }, - uploadImportFiles: function(geoserverBaseUrl, importId, files = [], options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks"; - let data = new FormData(); - files.forEach((file) => {data.append(file.name, file); }); - return axios.post(url, data, options); - }, - loadImport: function(geoserverBaseUrl, importId, options) { - let url = geoserverBaseUrl + "imports/" + importId; - return axios.get(url, options); - }, - updateTask: function( geoserverBaseUrl, importId, taskId, element, body, options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks/" + taskId; - // element can be target, layer, transforms... - if (element && element !== "task") { - url += "/" + element; - } - return axios.put(url, body, options); - }, - loadTask: function( geoserverBaseUrl, importId, taskId, options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks/" + taskId; - return axios.get(url, options); - }, - getTaskProgress: function( geoserverBaseUrl, importId, taskId, options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks/" + taskId + "/progress"; - return axios.get(url, options); - }, - loadLayer: function( geoserverBaseUrl, importId, taskId, options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks/" + taskId + "/layer"; - return axios.get(url, options); - }, - updateLayer: function( geoserverBaseUrl, importId, taskId, layer, options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks/" + taskId + "/layer"; - return axios.put(url, layer, options); - }, - loadTarget: function( geoserverBaseUrl, importId, taskId, options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks/" + taskId + "/target"; - return axios.get(url, options); - }, - runImport: function( geoserverBaseUrl, importId, opts = {}) { - let url = geoserverBaseUrl + "imports/" + importId + "?async=true"; - const {headers = {}, ...options } = opts; - return axios.post(url, null, { - ...options, - headers: { - ...headers, - 'Content-Type': '' - } - }); - }, - deleteImport: function(geoserverBaseUrl, importId, options) { - let url = geoserverBaseUrl + "imports/" + importId; - return axios.delete(url, options); - }, - deleteTask: function(geoserverBaseUrl, importId, taskId, options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks/" + taskId; - return axios.delete(url, options); - }, - addTransform: function(geoserverBaseUrl, importId, taskId, transform, options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks/" + taskId + "/transforms"; - return axios.post(url, transform, options); - }, - loadTransform: function(geoserverBaseUrl, importId, taskId, transformId, options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks/" + taskId + "/transforms/" + transformId; - return axios.get(url, options); - }, - updateTransform: function(geoserverBaseUrl, importId, taskId, transformId, transform, options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks/" + taskId + "/transforms/" + transformId; - return axios.put(url, transform, options); - }, - deleteTransform: function(geoserverBaseUrl, importId, taskId, transformId, options) { - let url = geoserverBaseUrl + "imports/" + importId + "/tasks/" + taskId + "/transforms/" + transformId; - return axios.delete(url, options); - } -}; - -export default Api; diff --git a/web/client/api/geoserver/Workspaces.js b/web/client/api/geoserver/Workspaces.js deleted file mode 100644 index b80d6ef3fd..0000000000 --- a/web/client/api/geoserver/Workspaces.js +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import axios from '../../libs/ajax'; - - -var Api = { - getWorkspaces: function(geoserverRestURL) { - return axios.get(geoserverRestURL + 'workspaces.json', { - 'headers': { - 'Accept': 'application/json' - } - }).then(function(response) { - return response.data; - }); - }, - createWorkspace: function(geoserverRestURL, name) { - let body = { - workspace: { - name: name - } - }; - return axios.post(geoserverRestURL + 'workspaces', body, { - 'headers': { - 'Accept': 'application/json' - } - }); - }, - createDataStore: function(geoserverRestURL, workspace, datastore) { - return axios.post(geoserverRestURL + 'workspaces/' + workspace + "/datastores.json", datastore, { - 'headers': { - 'Accept': 'application/json' - } - }); - } -}; -export default Api; diff --git a/web/client/api/geoserver/__tests__/Importer-test.js b/web/client/api/geoserver/__tests__/Importer-test.js deleted file mode 100644 index bff34246ff..0000000000 --- a/web/client/api/geoserver/__tests__/Importer-test.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2019, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import expect from 'expect'; - -import axios from '../../../libs/ajax'; -import MockAdapter from 'axios-mock-adapter'; -import SAMPLE_IMPORTS_1 from '../../../test-resources/importer/imports.json'; -const BASE_URL = '/GEOSERVER_TEST/'; -import Importer from '../Importer'; -describe('GeoServer Importer API', () => { - let mock; - beforeEach(() => { - mock = new MockAdapter(axios); - }); - afterEach(() => { - mock.restore(); - }); - it('getImports', done => { - mock.onGet(`${BASE_URL}imports`).reply(200, SAMPLE_IMPORTS_1); - Importer.getImports(BASE_URL).then( ({data}) => { - expect(data.imports).toExist(); - expect((data.imports).length).toBe(2); - done(); - }).catch(e=> done(e)); - }); - it('uploadFiles', done => { - const ID = 1; - const FILE_NAME = 'foobar.txt'; - const FILE_CONTENT = ['foo', 'bar']; - - const FILE = new File(FILE_CONTENT, FILE_NAME); - mock.onPost(`${BASE_URL}imports/${ID}/tasks`).reply((config) => { - expect(config.data).toExist(); - expect(config.data.get(FILE_NAME)).toExist(); - expect(config.data.get(FILE_NAME).name).toBe(FILE_NAME); - return [200]; - }); - Importer.uploadImportFiles(BASE_URL, ID, [FILE]).then(() => { - done(); - }).catch(e => done(e)); - }); - it('runImport', done => { - const ID = 1; - mock.onPost(`${BASE_URL}imports/${ID}?async=true`).reply( (config) => { - // check that the post do not contains default axios Content-Type - // to prevent GeoServer to fail because of invalid Content-Type - // (POST request with empty body should not have Content-Type) - expect(config.headers['Content-Type']).toBe(''); - return [200, '']; - }); - Importer.runImport(BASE_URL, ID).then(({}) => { - done(); - }).catch(e => done(e)); - }); -}); diff --git a/web/client/api/geoserver/__tests__/Workspaces-test.js b/web/client/api/geoserver/__tests__/Workspaces-test.js deleted file mode 100644 index 7aa78a3aef..0000000000 --- a/web/client/api/geoserver/__tests__/Workspaces-test.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright 2015, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import expect from 'expect'; - -import API from '../Workspaces'; - -describe('Test Workspaces rest API', () => { - it('getWorkspaces', (done) => { - API.getWorkspaces("base/web/client/test-resources/geoserver/rest/").then((workspaces)=> { - expect(workspaces).toExist(); - done(); - }); - }); - it('saveDatastore', (done) => { - API.createDataStore("base/web/client/test-resources/geoserver/rest/", "test", "STYLE_BODY").then((response)=> { - expect(response).toExist(); - done(); - }); - }); -}); diff --git a/web/client/components/I18N/LangSelector.jsx b/web/client/components/I18N/LangSelector.jsx deleted file mode 100644 index 78db661521..0000000000 --- a/web/client/components/I18N/LangSelector.jsx +++ /dev/null @@ -1,54 +0,0 @@ - -/** - * Copyright 2015, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import React from 'react'; - -import { FormControl } from 'react-bootstrap'; -import PropTypes from 'prop-types'; -import { getSupportedLocales } from '../../utils/LocaleUtils'; - -class LangSelector extends React.Component { - static propTypes = { - id: PropTypes.string, - locales: PropTypes.object, - currentLocale: PropTypes.string, - onLanguageChange: PropTypes.func - }; - - static defaultProps = { - id: "mapstore-langselector", - locales: {}, - currentLocale: 'en-US', - onLanguageChange: function() {} - }; - - render() { - var val; - var label; - var list = []; - let locales = getSupportedLocales(); - for (let lang in locales) { - if (locales.hasOwnProperty(lang)) { - val = locales[lang].code; - label = locales[lang].description; - list.push(); - } - } - return ( - - {list} - - ); - } - - launchNewLangAction = (e) => { - this.props.onLanguageChange(e.target.value); - }; -} - -export default LangSelector; diff --git a/web/client/components/I18N/__tests__/LangSelector-test.jsx b/web/client/components/I18N/__tests__/LangSelector-test.jsx deleted file mode 100644 index 6c3f1622ac..0000000000 --- a/web/client/components/I18N/__tests__/LangSelector-test.jsx +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2015, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import expect from 'expect'; - -import React from 'react'; -import ReactDOM from 'react-dom'; -import LangSelector from '../LangSelector'; -import TestUtils from 'react-dom/test-utils'; - -describe('LangSelector', () => { - beforeEach((done) => { - document.body.innerHTML = '
'; - setTimeout(done); - }); - - afterEach((done) => { - ReactDOM.unmountComponentAtNode(document.getElementById("container")); - document.body.innerHTML = ''; - setTimeout(done); - }); - - it('checks default', () => { - var lbl; - var value; - - const cmp = ReactDOM.render(, document.getElementById("container")); - expect(cmp).toExist(); - const cmpDom = ReactDOM.findDOMNode(cmp); - expect(cmpDom).toExist(); - - const opts = cmpDom.childNodes; - const langs = { - 'Italiano': 'it-IT', - 'English': 'en-US', - 'Français': 'fr-FR', - 'Deutsch': 'de-DE', - 'Español': 'es-ES', - "中文": "zh-ZH", - 'Nederlands': 'nl-NL', - 'Hrvatski': 'hr-HR', - 'Português': 'pt-PT', - "tiếng Việt": "vi-VN", - 'Suomi': 'fi-FI', - 'Svenska': 'sv-SE', - 'Slovak': 'sk-SK', - 'Islensku': 'is-IS', - 'Dansk': 'da-DK' - }; - - for (let i = 0; i < opts.length; i++) { - lbl = opts[i].innerHTML; - value = opts[i].value; - expect(langs.hasOwnProperty(lbl)).toBe(true); - expect(langs[lbl]).toBe(value); - } - }); - - it('checks if a change of the combo fires the proper action', () => { - let newLang; - const cmp = ReactDOM.render( { - newLang = lang; - }}/>, document.getElementById("container")); - const cmpDom = ReactDOM.findDOMNode(cmp); - - cmpDom.value = "it-IT"; - TestUtils.Simulate.change(cmpDom, {target: {value: 'it-IT'}}); - // select.children[1].click(); - - expect(newLang).toBe('it-IT'); - }); -}); diff --git a/web/client/components/TOC/fragments/settings/Display.jsx b/web/client/components/TOC/fragments/settings/Display.jsx index 2f3ea6957d..2b7a97d497 100644 --- a/web/client/components/TOC/fragments/settings/Display.jsx +++ b/web/client/components/TOC/fragments/settings/Display.jsx @@ -210,6 +210,7 @@ export default class extends React.Component { projection={this.props.projection} resolutions={this.props.resolutions} zoom={this.props.zoom} + defaultLimitsType={this.props.element.visibilityLimitType} /> @@ -243,14 +244,6 @@ export default class extends React.Component { onChange={(e) => this.props.onChange("localizedLayerStyles", e.target.checked)}>  } /> ))} - {!this.props.isCesiumActive && ( this.props.onChange("forceProxy", e.target.checked)} - checked={this.props.element.forceProxy} > - - )} {(this.props.element?.serverType !== ServerTypes.NO_VENDOR && ( <>
diff --git a/web/client/components/TOC/fragments/settings/FeatureInfoFormat.jsx b/web/client/components/TOC/fragments/settings/FeatureInfoFormat.jsx deleted file mode 100644 index 1741c78fc5..0000000000 --- a/web/client/components/TOC/fragments/settings/FeatureInfoFormat.jsx +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2017, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; - -import PropTypes from 'prop-types'; -import { DropdownList } from 'react-widgets'; -import { getAvailableInfoFormat, getLabelFromValue } from '../../../../utils/MapInfoUtils'; -import { Grid } from 'react-bootstrap'; -import Message from '../../../I18N/Message'; - -/** - * FeatureInfoFormat shows the infoformat selected for that layer or the default one taken - * from the general settings. - * @class - * @memberof components.toc - * @prop {object} [element] the layer options - * @prop {object} [label] the label shown for the combobox - * @prop {object} [defaultInfoFormat] the object used to show options labels - * @prop {string} [generalInfoFormat] the infoFormat value set in the general settings - * @prop {function} [onInfoFormatChange] it updates the infoFormat and/or viewer for the given layer - */ -export default class extends React.Component { - static propTypes = { - element: PropTypes.object, - label: PropTypes.object, - defaultInfoFormat: PropTypes.object, - generalInfoFormat: PropTypes.string, - onChange: PropTypes.func - }; - - static defaultProps = { - defaultInfoFormat: getAvailableInfoFormat(), - generalInfoFormat: "text/plain", - onChange: () => {}, - label: - }; - - getInfoFormat = (infoFormats) => { - return Object.keys(infoFormats).map((infoFormat) => { - return infoFormat; - }); - } - render() { - // the selected value if missing on that layer should be set to the general info format value and not the first one. - const data = this.getInfoFormat(this.props.defaultInfoFormat); - const checkDisabled = !!(this.props.element.featureInfo && this.props.element.featureInfo.viewer); - return ( - - {this.props.element.type === "wms" ? - [(), - ( { - this.props.onChange("featureInfo", Object.assign({}, { - ['format']: value, - ['viewer']: this.props.element.featureInfo ? this.props.element.featureInfo.viewer : undefined - })); - }} /> - )] : null} - - ); - } -} diff --git a/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx b/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx index 5d1cc702e1..a69e50f8d3 100644 --- a/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx +++ b/web/client/components/TOC/fragments/settings/VisibilityLimitsForm.jsx @@ -392,6 +392,9 @@ function VisibilityLimitsForm({ disabled={disableResolutionLimits || loading} onChange={({ value }) => { setLimitsType(value); + onChange({ + visibilityLimitType: value + }); clearMessages(); }} /> diff --git a/web/client/components/TOC/fragments/settings/__tests__/Display-test.jsx b/web/client/components/TOC/fragments/settings/__tests__/Display-test.jsx index 631ae8713a..2ae82acbaf 100644 --- a/web/client/components/TOC/fragments/settings/__tests__/Display-test.jsx +++ b/web/client/components/TOC/fragments/settings/__tests__/Display-test.jsx @@ -77,7 +77,7 @@ describe('test Layer Properties Display module component', () => { expect(comp).toBeTruthy(); const inputs = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "input" ); expect(inputs).toBeTruthy(); - expect(inputs.length).toBe(14); + expect(inputs.length).toBe(13); ReactTestUtils.Simulate.focus(inputs[2]); expect(inputs[2].value).toBe('70'); inputs[8].click(); @@ -105,7 +105,7 @@ describe('test Layer Properties Display module component', () => { expect(comp).toBeTruthy(); const inputs = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "input" ); expect(inputs).toBeTruthy(); - expect(inputs.length).toBe(13); + expect(inputs.length).toBe(12); ReactTestUtils.Simulate.focus(inputs[2]); expect(inputs[2].value).toBe('70'); inputs[8].click(); @@ -199,64 +199,6 @@ describe('test Layer Properties Display module component', () => { expect(isLocalizedLayerStylesOption).toBeTruthy(); }); - it('tests Display component for wms with force proxy option displayed', () => { - const l = { - name: 'layer00', - title: 'Layer', - visibility: true, - storeIndex: 9, - type: 'wms', - url: 'fakeurl', - forceProxy: true - }; - const settings = { - options: {opacity: 0.7} - }; - ReactDOM.render(, document.getElementById("container")); - const isForceProxyOption = document.querySelector('[data-qa="display-forceProxy-option"]'); - expect(isForceProxyOption).toBeTruthy(); - }); - it('tests Display component for wms with force proxy option in cesium map', () => { - const l = { - name: 'layer00', - title: 'Layer', - visibility: true, - storeIndex: 9, - type: 'wms', - url: 'fakeurl', - forceProxy: true - }; - const settings = { - options: {opacity: 0.7} - }; - ReactDOM.render(, document.getElementById("container")); - const isForceProxyOption = document.querySelector('[data-qa="display-forceProxy-option"]'); - expect(isForceProxyOption).toBeFalsy(); - }); - it('tests Display component for wms with force proxy option onChange', () => { - const handlers = { - onChange() {} - }; - const spyOn = expect.spyOn(handlers, 'onChange'); - const l = { - name: 'layer00', - title: 'Layer', - visibility: true, - storeIndex: 9, - type: 'wms', - url: 'fakeurl', - forceProxy: false - }; - const settings = { - options: {opacity: 0.7} - }; - ReactDOM.render(, document.getElementById("container")); - const isForceProxyOption = document.querySelector('[data-qa="display-forceProxy-option"]'); - expect(isForceProxyOption).toBeTruthy(); - ReactTestUtils.Simulate.change(isForceProxyOption, { "target": { "checked": true }}); - expect(spyOn).toHaveBeenCalled(); - expect(spyOn.calls[0].arguments).toEqual([ 'forceProxy', true ]); - }); it('tests Layer Properties Legend component for map viewer only', () => { const l = { @@ -277,8 +219,8 @@ describe('test Layer Properties Display module component', () => { expect(comp).toBeTruthy(); const labels = ReactTestUtils.scryRenderedDOMComponentsWithClass( comp, "control-label" ); const inputs = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "input" ); - const legendWidth = inputs[12]; - const legendHeight = inputs[13]; + const legendWidth = inputs[11]; + const legendHeight = inputs[12]; // Default legend values expect(legendWidth.value).toBe('12'); expect(legendHeight.value).toBe('12'); @@ -307,8 +249,8 @@ describe('test Layer Properties Display module component', () => { expect(comp).toBeTruthy(); const labels = ReactTestUtils.scryRenderedDOMComponentsWithClass( comp, "control-label" ); const inputs = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "input" ); - const legendWidth = inputs[11]; - const legendHeight = inputs[12]; + const legendWidth = inputs[10]; + const legendHeight = inputs[11]; // Default legend values expect(legendWidth.value).toBe('12'); expect(legendHeight.value).toBe('12'); @@ -347,10 +289,10 @@ describe('test Layer Properties Display module component', () => { const legendPreview = ReactTestUtils.scryRenderedDOMComponentsWithClass( comp, "legend-preview" ); expect(legendPreview).toBeTruthy(); expect(inputs).toBeTruthy(); - expect(inputs.length).toBe(14); - let interactiveLegendConfig = inputs[11]; - let legendWidth = inputs[12]; - let legendHeight = inputs[13]; + expect(inputs.length).toBe(13); + let interactiveLegendConfig = inputs[10]; + let legendWidth = inputs[11]; + let legendHeight = inputs[12]; const img = ReactTestUtils.scryRenderedDOMComponentsWithTag(comp, 'img'); // Check value in img src @@ -423,8 +365,8 @@ describe('test Layer Properties Display module component', () => { expect(comp).toBeTruthy(); const inputs = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "input" ); expect(inputs).toBeTruthy(); - expect(inputs.length).toBe(14); - expect(inputs[12].value).toBe("20"); - expect(inputs[13].value).toBe("40"); + expect(inputs.length).toBe(13); + expect(inputs[11].value).toBe("20"); + expect(inputs[12].value).toBe("40"); }); }); diff --git a/web/client/components/TOC/fragments/settings/__tests__/FeatureInfoFormat-test.jsx b/web/client/components/TOC/fragments/settings/__tests__/FeatureInfoFormat-test.jsx deleted file mode 100644 index 3cf8c0326f..0000000000 --- a/web/client/components/TOC/fragments/settings/__tests__/FeatureInfoFormat-test.jsx +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright 2015, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import expect from 'expect'; -import React from 'react'; -import ReactDOM from 'react-dom'; -import ReactTestUtils from 'react-dom/test-utils'; - -import FeatureInfoFormat from '../FeatureInfoFormat'; - -describe('test Layer Properties FeatureInfoFormat module component', () => { - beforeEach((done) => { - document.body.innerHTML = '
'; - setTimeout(done); - }); - - afterEach((done) => { - ReactDOM.unmountComponentAtNode(document.getElementById("container")); - document.body.innerHTML = ''; - setTimeout(done); - }); - - it('tests FeatureInfoFormat component', () => { - const l = { - name: 'layer00', - title: 'Layer', - visibility: true, - storeIndex: 9, - type: 'shapefile', - url: 'fakeurl' - }; - const label = "label"; - // wrap in a stateful component, stateless components render return null - // see: https://facebook.github.io/react/docs/top-level-api.html#reactdom.render - const comp = ReactDOM.render(, document.getElementById("container")); - expect(comp).toExist(); - const span = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "span" ); - expect(span).toExist(); - expect(span.length).toBe(0); - }); - it('tests FeatureInfoFormat component for wms', () => { - const l = { - name: 'layer00', - title: 'Layer', - visibility: true, - storeIndex: 9, - type: 'wms', - url: 'fakeurl', - featureInfo: { - format: 'JSON' - } - }; - const label = "label"; - const handlers = { - onChange() {} - }; - let spy = expect.spyOn(handlers, "onChange"); - // wrap in a stateful component, stateless components render return null - // see: https://facebook.github.io/react/docs/top-level-api.html#reactdom.render - const comp = ReactDOM.render(, document.getElementById("container")); - expect(comp).toExist(); - const span = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "span" ); - expect(span).toExist(); - expect(span.length).toBe(2); - span[1].click(); - const li = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "li" ); - expect(li).toExist(); - li[0].click(); - expect(spy.calls.length).toBe(1); - }); - it('tests FeatureInfoFormat component for wms using generalInfoFormat', () => { - const l = { - name: 'layer00', - title: 'Layer', - visibility: true, - storeIndex: 9, - type: 'wms', - url: 'fakeurl' - }; - const generalInfoFormat = "text/html"; - const label = "label"; - const comp = ReactDOM.render( {}}/>, document.getElementById("container")); - expect(comp).toExist(); - const div = ReactTestUtils.scryRenderedDOMComponentsWithTag( comp, "div" ); - expect(div[2]).toExist(); - expect(div[2].textContent).toBe("HTML"); - - }); -}); diff --git a/web/client/components/TOC/fragments/settings/__tests__/VisibilityLimitsForm-test.jsx b/web/client/components/TOC/fragments/settings/__tests__/VisibilityLimitsForm-test.jsx index bd441c78bb..9f97a6aa64 100644 --- a/web/client/components/TOC/fragments/settings/__tests__/VisibilityLimitsForm-test.jsx +++ b/web/client/components/TOC/fragments/settings/__tests__/VisibilityLimitsForm-test.jsx @@ -9,7 +9,7 @@ import expect from 'expect'; import React from 'react'; import ReactDOM from 'react-dom'; -import { act } from 'react-dom/test-utils'; +import ReactTestUtils, { act } from 'react-dom/test-utils'; import VisibilityLimitsForm from '../VisibilityLimitsForm'; describe('VisibilityLimitsForm', () => { @@ -96,4 +96,32 @@ describe('VisibilityLimitsForm', () => { const buttons = document.querySelectorAll('.square-button-md'); expect(buttons.length).toBe(1); }); + + it("Should call onChange after visibilityLimitType change ", () => { + const layer = { + type: 'wms' + }; + const handlers = { + onChange: () => {} + }; + let spyUpdate = expect.spyOn(handlers, "onChange"); + + act(()=>ReactDOM.render(, document.getElementById('container'))); + + // Simpulate selection + const selectArrow = document.getElementById("container").querySelectorAll('.Select-arrow'); + const selectControl = document.getElementById("container").querySelectorAll('.Select-control'); + const inputs = document.getElementsByTagName("input"); + ReactTestUtils.Simulate.mouseDown(selectArrow[2], { button: 0 }); + ReactTestUtils.Simulate.keyDown(selectControl[2], { keyCode: 40, key: 'ArrowDown' }); + ReactTestUtils.Simulate.keyDown(inputs[3], { keyCode: 13, key: 'Enter' }); + expect(spyUpdate.calls.length).toBe(1); + expect(Object.keys(spyUpdate.calls[0].arguments[0])).toEqual(['visibilityLimitType']); + + }); + + }); diff --git a/web/client/components/catalog/editor/AdvancedSettings/CommonAdvancedSettings.jsx b/web/client/components/catalog/editor/AdvancedSettings/CommonAdvancedSettings.jsx index 5368fe49b9..147e4127eb 100644 --- a/web/client/components/catalog/editor/AdvancedSettings/CommonAdvancedSettings.jsx +++ b/web/client/components/catalog/editor/AdvancedSettings/CommonAdvancedSettings.jsx @@ -39,14 +39,7 @@ export default ({ - {!isNil(service.type) && service.type === "wfs" && - - onChangeServiceProperty("allowUnsecureLayers", e.target.checked)} - checked={!isNil(service.allowUnsecureLayers) ? service.allowUnsecureLayers : false}> -  } /> - - } + {!isNil(service.type) && service.type === "cog" &&  } /> } - {!isNil(service.type) && service.type === "wms" && - onChangeServiceProperty("allowUnsecureLayers", e.target.checked)} - checked={!isNil(service.allowUnsecureLayers) ? service.allowUnsecureLayers : false}> -  } /> - - } {(!isNil(service.type) ? (service.type === "csw" && !service.excludeShowTemplate) : false) && ( onToggleTemplate()} diff --git a/web/client/components/catalog/editor/AdvancedSettings/__tests__/CommonAdvancedSettings-test.js b/web/client/components/catalog/editor/AdvancedSettings/__tests__/CommonAdvancedSettings-test.js index 8cacf029fe..04a3eb57a2 100644 --- a/web/client/components/catalog/editor/AdvancedSettings/__tests__/CommonAdvancedSettings-test.js +++ b/web/client/components/catalog/editor/AdvancedSettings/__tests__/CommonAdvancedSettings-test.js @@ -38,7 +38,7 @@ describe('Test common advanced settings', () => { const advancedSettingPanel = document.getElementsByClassName("mapstore-switch-panel"); expect(advancedSettingPanel).toBeTruthy(); const fields = document.querySelectorAll(".form-group"); - expect(fields.length).toBe(3); + expect(fields.length).toBe(2); }); it('test wms advanced options onChangeServiceProperty autoreload', () => { const action = { @@ -52,7 +52,7 @@ describe('Test common advanced settings', () => { const advancedSettingPanel = document.getElementsByClassName("mapstore-switch-panel"); expect(advancedSettingPanel).toBeTruthy(); const fields = document.querySelectorAll(".form-group"); - expect(fields.length).toBe(3); + expect(fields.length).toBe(2); const autoload = document.querySelectorAll('input[type="checkbox"]')[0]; const formGroup = document.querySelectorAll('.form-group')[0]; expect(formGroup.textContent.trim()).toBe('catalog.autoload'); @@ -61,30 +61,6 @@ describe('Test common advanced settings', () => { expect(spyOn).toHaveBeenCalled(); expect(spyOn.calls[0].arguments).toEqual([ 'autoload', true ]); }); - it('test component onChangeServiceProperty allowUnsecureLayers', () => { - const action = { - onChangeServiceProperty: () => {} - }; - const spyOn = expect.spyOn(action, 'onChangeServiceProperty'); - ReactDOM.render(, document.getElementById("container")); - const advancedSettingsPanel = document.getElementsByClassName("mapstore-switch-panel"); - expect(advancedSettingsPanel).toBeTruthy(); - const allowUnsecureLayers = document.querySelectorAll('input[type="checkbox"]')[1]; - const formGroup = document.querySelectorAll('.form-group')[2]; - expect(formGroup.textContent.trim()).toBe('catalog.allowUnsecureLayers.label'); - expect(allowUnsecureLayers).toExist(); - TestUtils.Simulate.change(allowUnsecureLayers, { "target": { "checked": true }}); - expect(spyOn).toHaveBeenCalled(); - expect(spyOn.calls[0].arguments).toEqual([ 'allowUnsecureLayers', true ]); - - // Unset allowUnsecureLayers - TestUtils.Simulate.change(allowUnsecureLayers, { "target": { "checked": false }}); - expect(spyOn).toHaveBeenCalled(); - expect(spyOn.calls[1].arguments).toEqual([ 'allowUnsecureLayers', false ]); - }); it('test component onChangeServiceProperty fetchMetadata', () => { const action = { onChangeServiceProperty: () => {} diff --git a/web/client/components/catalog/editor/AdvancedSettings/__tests__/RasterAdvancedSettings-test.js b/web/client/components/catalog/editor/AdvancedSettings/__tests__/RasterAdvancedSettings-test.js index 3f8b726472..b32859c2e2 100644 --- a/web/client/components/catalog/editor/AdvancedSettings/__tests__/RasterAdvancedSettings-test.js +++ b/web/client/components/catalog/editor/AdvancedSettings/__tests__/RasterAdvancedSettings-test.js @@ -36,7 +36,7 @@ describe('Test Raster advanced settings', () => { const advancedSettingPanel = document.getElementsByClassName("mapstore-switch-panel"); expect(advancedSettingPanel).toBeTruthy(); const fields = document.querySelectorAll(".form-group"); - expect(fields.length).toBe(15); + expect(fields.length).toBe(14); // check disabled refresh button }); @@ -45,7 +45,7 @@ describe('Test Raster advanced settings', () => { const advancedSettingPanel = document.getElementsByClassName("mapstore-switch-panel"); expect(advancedSettingPanel).toBeTruthy(); const fields = document.querySelectorAll(".form-group"); - expect(fields.length).toBe(13); + expect(fields.length).toBe(12); const refreshButton = document.querySelectorAll('button')[0]; expect(refreshButton).toBeTruthy(); expect(refreshButton.disabled).toBe(false); @@ -214,30 +214,6 @@ describe('Test Raster advanced settings', () => { expect(spyOn).toHaveBeenCalled(); expect(spyOn.calls[0].arguments).toEqual([ 'layerOptions', { tileSize: 512 } ]); }); - it('test component onChangeServiceProperty allowUnsecureLayers', () => { - const action = { - onChangeServiceProperty: () => {} - }; - const spyOn = expect.spyOn(action, 'onChangeServiceProperty'); - ReactDOM.render(, document.getElementById("container")); - const advancedSettingsPanel = document.getElementsByClassName("mapstore-switch-panel"); - expect(advancedSettingsPanel).toBeTruthy(); - const allowUnsecureLayers = document.querySelectorAll('input[type="checkbox"]')[3]; - const formGroup = document.querySelectorAll('.form-group')[4]; - expect(formGroup.textContent.trim()).toBe('catalog.allowUnsecureLayers.label'); - expect(allowUnsecureLayers).toBeTruthy(); - TestUtils.Simulate.change(allowUnsecureLayers, { "target": { "checked": true }}); - expect(spyOn).toHaveBeenCalled(); - expect(spyOn.calls[0].arguments).toEqual([ 'allowUnsecureLayers', true ]); - - // Unset allowUnsecureLayers - TestUtils.Simulate.change(allowUnsecureLayers, { "target": { "checked": false }}); - expect(spyOn).toHaveBeenCalled(); - expect(spyOn.calls[1].arguments).toEqual([ 'allowUnsecureLayers', false ]); - }); it('test component onChangeServiceProperty useCacheOption for remote tile grids', () => { const action = { onChangeServiceProperty: () => {} @@ -249,7 +225,7 @@ describe('Test Raster advanced settings', () => { />, document.getElementById("container")); const advancedSettingsPanel = document.getElementsByClassName("mapstore-switch-panel"); expect(advancedSettingsPanel).toBeTruthy(); - const formGroup = document.querySelectorAll('.form-group')[7]; + const formGroup = document.querySelectorAll('.form-group')[6]; expect(formGroup.textContent.trim()).toBe('layerProperties.useCacheOptionInfo.label'); const useCacheOption = formGroup.querySelector('input[type="checkbox"]'); expect(useCacheOption).toBeTruthy(); diff --git a/web/client/components/catalog/editor/MainForm.jsx b/web/client/components/catalog/editor/MainForm.jsx index 3ec7ff0e83..42a22c31cd 100644 --- a/web/client/components/catalog/editor/MainForm.jsx +++ b/web/client/components/catalog/editor/MainForm.jsx @@ -5,8 +5,8 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ -import React, { useState, useEffect } from 'react'; -import {get, find, isEmpty} from 'lodash'; +import React, { useState } from 'react'; +import {get, find} from 'lodash'; import Message from '../../I18N/Message'; import HTML from '../../I18N/HTML'; @@ -154,17 +154,21 @@ export default ({ setValid = () => {} }) => { const [error, setError] = useState(null); + const [warning, setWarning] = useState(null); function handleProtocolValidity(url) { onChangeUrl(url); if (url) { const {valid, errorMsgId} = checkUrl(url, null, service?.allowUnsecureLayers); - setError(valid ? null : errorMsgId); - setValid(valid); + if (errorMsgId === "catalog.invalidUrlHttpProtocol") { + setError(null); + setWarning(errorMsgId); + } else { + setWarning(null); + setError(valid ? null : errorMsgId); + setValid(valid); + } } } - useEffect(() => { - !isEmpty(service.url) && handleProtocolValidity(service.url); - }, [service?.allowUnsecureLayers]); const URLEditor = service.type === "tms" ? TmsURLEditor : service.type === "cog" ? COGEditor : DefaultURLEditor; return (
@@ -195,6 +199,9 @@ export default ({ {error ? : null} + {warning ? + + : null}
); }; diff --git a/web/client/components/catalog/editor/MainFormUtils.js b/web/client/components/catalog/editor/MainFormUtils.js index a2d16a13d5..bd297ef9c0 100644 --- a/web/client/components/catalog/editor/MainFormUtils.js +++ b/web/client/components/catalog/editor/MainFormUtils.js @@ -27,12 +27,12 @@ export const defaultPlaceholder = (service) => { * @param {boolean} allowUnsecureLayers flag to allow unsecure url * @returns {object} {valid: boolean, errorMsgId: string} */ -export const checkUrl = (catalogUrl = '', currentLocation, allowUnsecureLayers) => { +export const checkUrl = (catalogUrl = '', currentLocation) => { try { const { protocol: mapStoreProtocol } = url.parse(currentLocation ?? window.location.href); const { protocol: catalogProtocol } = url.parse(catalogUrl); if (mapStoreProtocol === 'https:' && !!catalogProtocol) { - const isProtocolValid = (mapStoreProtocol === catalogProtocol || allowUnsecureLayers); + const isProtocolValid = (mapStoreProtocol === catalogProtocol); return isProtocolValid ? {valid: true} : {valid: false, errorMsgId: "catalog.invalidUrlHttpProtocol"}; } return {valid: true}; diff --git a/web/client/components/catalog/editor/__tests__/MainFormUtils-test.js b/web/client/components/catalog/editor/__tests__/MainFormUtils-test.js index dd67581e79..1b2e8d4b54 100644 --- a/web/client/components/catalog/editor/__tests__/MainFormUtils-test.js +++ b/web/client/components/catalog/editor/__tests__/MainFormUtils-test.js @@ -29,30 +29,4 @@ describe('Catalog Main Form Editor Utils', () => { expect(messageId).toEqual(errorMsgId); }); }); - it('checkUrl with allowUnsecureLayers', () => { - const URLS = [ - // http - ['http://myDomain.com/geoserver/wms', 'https://myMapStore.com/geoserver/wms', true, true], - ['http://myDomain.com/geoserver/wms', 'http://myMapStore.com/geoserver/wms', true, false], - // https - ['https://myDomain.com/geoserver/wms', 'http://myMapStore.com/geoserver/wms', true, true], - ['https://myDomain.com/geoserver/wms', 'https://myMapStore.com/geoserver/wms', true, false], - // protocol relative URL - ['//myDomain.com/geoserver/wms', 'http://myMapStore.com/geoserver/wms', true, false], - ['//myDomain.com/geoserver/wms', 'https://myMapStore.com/geoserver/wms', true, false], - // absolute path - ['/geoserver/wms', 'http://myMapStore.com/geoserver/wms', true, false], - ['/geoserver/wms', 'https://myMapStore.com/geoserver/wms', true, false], - // relative path - ["geoserver/wms", "http://myMapStore.com/geoserver/wms", true, false], - ["geoserver/wms", "https://myMapStore.com/geoserver/wms", true, true], - [["geoserver/wms", "geoserver/wms"], "https://myMapStore.com/geoserver/wms", false, false, "catalog.invalidArrayUsageForUrl"] // array - ]; - URLS.forEach(([catalogURL, locationURL, valid, allowUnsecureLayers, messageId]) => { - const {valid: isValid, errorMsgId} = checkUrl(catalogURL, locationURL, allowUnsecureLayers); - expect(!!isValid).toEqual(!!valid, `${catalogURL} - added when location is ${locationURL} should be ${valid}, but it is ${isValid}`); - expect(messageId).toEqual(errorMsgId); - expect(!!isValid).toEqual(!!valid, `${catalogURL} - added when location is ${locationURL} should be ${valid}, but it is ${isValid}`); - }); - }); }); diff --git a/web/client/components/data/featuregrid/renderers/cell.css b/web/client/components/data/featuregrid/renderers/cell.css deleted file mode 100644 index 09ded72ec5..0000000000 --- a/web/client/components/data/featuregrid/renderers/cell.css +++ /dev/null @@ -1,4 +0,0 @@ - -.ms2 .react-grid-Row.row-selected .react-grid-Cell.modified { - background-color: orange; -} diff --git a/web/client/components/data/identify/DefaultHeader.jsx b/web/client/components/data/identify/DefaultHeader.jsx deleted file mode 100644 index 776b313e25..0000000000 --- a/web/client/components/data/identify/DefaultHeader.jsx +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; - -import PropTypes from 'prop-types'; - -class DefaultHeader extends React.Component { - static propTypes = { - title: PropTypes.string - }; - - render() { - return {this.props.title} ; - } -} - -export default DefaultHeader; diff --git a/web/client/components/data/identify/DefaultViewer.jsx b/web/client/components/data/identify/DefaultViewer.jsx index 8a18d5330c..ff02bfbc6d 100644 --- a/web/client/components/data/identify/DefaultViewer.jsx +++ b/web/client/components/data/identify/DefaultViewer.jsx @@ -40,7 +40,8 @@ class DefaultViewer extends React.Component { renderValidOnly: PropTypes.bool, loaded: PropTypes.bool, isMobile: PropTypes.bool, - disableInfoAlert: PropTypes.bool + disableInfoAlert: PropTypes.bool, + hidePopupIfNoResults: PropTypes.bool }; static defaultProps = { @@ -64,7 +65,8 @@ class DefaultViewer extends React.Component { onPrevious: () => {}, setIndex: () => {}, isMobile: false, - disableInfoAlert: false + disableInfoAlert: false, + hidePopupIfNoResults: false }; shouldComponentUpdate(nextProps) { @@ -147,6 +149,9 @@ class DefaultViewer extends React.Component { renderEmptyPages = () => { const {emptyResponses} = this.getResponseProperties(); if (this.props.missingResponses === 0 && emptyResponses) { + if (this.props.hidePopupIfNoResults) { + return ; + } return (

diff --git a/web/client/components/data/identify/PopupViewer.jsx b/web/client/components/data/identify/PopupViewer.jsx index 399467a0e1..1f16a19afa 100644 --- a/web/client/components/data/identify/PopupViewer.jsx +++ b/web/client/components/data/identify/PopupViewer.jsx @@ -17,6 +17,7 @@ import Viewer from './DefaultViewer'; import {isArray, isUndefined} from 'lodash'; import SwipeHeader from './SwipeHeader'; import { identifyFloatingToolSelector } from '../../../selectors/map'; +import { hideEmptyPopupSelector } from '../../../selectors/mapPopups'; /** * Container that render only the selected result @@ -46,8 +47,8 @@ const selector = createSelector([ generalInfoFormatSelector, showEmptyMessageGFISelector, identifyFloatingToolSelector, - isLoadedResponseSelector], -(responses, validResponses, requests, format, showEmptyMessageGFI, renderValidOnly, loaded) => ({ + isLoadedResponseSelector, hideEmptyPopupSelector], +(responses, validResponses, requests, format, showEmptyMessageGFI, renderValidOnly, loaded, hidePopupIfNoResults ) => ({ responses, validResponses, requests, @@ -55,7 +56,8 @@ const selector = createSelector([ showEmptyMessageGFI, missingResponses: (requests || []).length - (responses || []).length, renderValidOnly, - loaded + loaded, + hidePopupIfNoResults })); diff --git a/web/client/components/data/identify/__tests__/DefaultHeader-test.jsx b/web/client/components/data/identify/__tests__/DefaultHeader-test.jsx deleted file mode 100644 index b180babf7e..0000000000 --- a/web/client/components/data/identify/__tests__/DefaultHeader-test.jsx +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import React from 'react'; - -import ReactDOM from 'react-dom'; -import DefaultHeader from '../DefaultHeader.jsx'; -import expect from 'expect'; - -describe('DefaultHeader', () => { - - beforeEach((done) => { - document.body.innerHTML = '
'; - setTimeout(done); - }); - - afterEach((done) => { - ReactDOM.unmountComponentAtNode(document.getElementById("container")); - document.body.innerHTML = ''; - setTimeout(done); - }); - - it('creates the DefaultHeader component with defaults', () => { - const header = ReactDOM.render( - , - document.getElementById("container") - ); - - expect(header).toExist(); - }); - - it('creates the DefaultHeader component with title', () => { - const header = ReactDOM.render( - , - document.getElementById("container") - ); - - expect(header).toExist(); - const dom = ReactDOM.findDOMNode(header); - expect(dom.innerHTML.indexOf('mytitle') !== -1).toBe(true); - }); -}); diff --git a/web/client/components/data/identify/__tests__/DefaultViewer-test.jsx b/web/client/components/data/identify/__tests__/DefaultViewer-test.jsx index 9fbe93fffd..3c8f19a47e 100644 --- a/web/client/components/data/identify/__tests__/DefaultViewer-test.jsx +++ b/web/client/components/data/identify/__tests__/DefaultViewer-test.jsx @@ -269,4 +269,18 @@ describe('DefaultViewer', () => { expect(gfiViewer.childNodes[1].childNodes.length).toBe(1); }); + it('test DefaultViewer component with hover identify if hidePopupIfNoResults = true', () => { + const responses = []; + ReactDOM.render( + , + document.getElementById("container") + ); + + const container = document.getElementById('container'); + let gfiViewer = container.querySelector('.mapstore-identify-viewer'); + expect(gfiViewer).toBeTruthy(); + expect(gfiViewer.childNodes.length).toBe(1); + expect(document.querySelector(".hidePopupIfNoResults")).toBeTruthy(); + expect(document.querySelector(".hidePopupIfNoResults").innerHTML).toBeFalsy(); + }); }); diff --git a/web/client/components/data/identify/css/identify.css b/web/client/components/data/identify/css/identify.css deleted file mode 100644 index 84f53dd747..0000000000 --- a/web/client/components/data/identify/css/identify.css +++ /dev/null @@ -1,62 +0,0 @@ - -#mapstore-identify-revgeocoder { - min-height: 40px; - margin-bottom: 10px; - border-bottom: solid 1px #BBB; -} - - -#mapstore-getfeatureinfo { - max-width: 100%; - position: absolute; - max-width: 500px; - z-index: 1023; - box-shadow: 2px 2px 4px #A7A7A7; -} - -@media (min-width: 500px) { - #mapstore-getfeatureinfo { - min-width: 500px; - } -} - -@media (min-width: 768px) { - #mapstore-getfeatureinfo { - min-width: 660px; - } -} - -#mapstore-getfeatureinfo .swipeable-view .panel-heading { - border: none; - height: 60px; - padding: 0; -} - -#mapstore-getfeatureinfo .swipeable-view .panel { - border: none; -} - -#mapstore-getfeatureinfo .modal-body .panel-body { - padding: 0; -} - -#mapstore-getfeatureinfo .swipeable-view .panel-heading .swipe-header-left-button { - right: 55px; - top: 0; -} - -#mapstore-getfeatureinfo .swipeable-view .panel-heading .swipe-header-right-button { - top: 0; -} - -#mapstore-getfeatureinfo .swipeable-view .panel-title { - font-weight: bold; -} - -.mapstore-identify-modal { - z-index: 2001; -} - -#mapstore-getfeatureinfo .m-fullscreen-btn { - cursor: pointer; -} diff --git a/web/client/components/data/identify/enhancers/__tests__/identify-test.jsx b/web/client/components/data/identify/enhancers/__tests__/identify-test.jsx index 1ba1708dc8..27369942cb 100644 --- a/web/client/components/data/identify/enhancers/__tests__/identify-test.jsx +++ b/web/client/components/data/identify/enhancers/__tests__/identify-test.jsx @@ -187,6 +187,18 @@ describe("test identify enhancers", () => { ); expect(spyIdentifyIsMounted.calls.length).toEqual(1); }); + it("test identifyLifecycle component for call enableHideEmptyPopupOption if hidePopupIfNoResults prop = true", () => { + const Component = identifyLifecycle(() =>
); + const testHandlers = { + enableHideEmptyPopupOption: () => {} + }; + const spyEnableHideEmptyPopupOption = expect.spyOn(testHandlers, 'enableHideEmptyPopupOption'); + ReactDOM.render( + , + document.getElementById("container") + ); + expect(spyEnableHideEmptyPopupOption.calls.length).toEqual(1); + }); it("Identify should run when enabled prop is true and showInMapPopup prop is false", () => { let run = sampleComponentDidMount({enabled: true, showInMapPopup: false}); expect(run.checkIdentifyIsMounted).toBe(true); diff --git a/web/client/components/data/identify/enhancers/identify.js b/web/client/components/data/identify/enhancers/identify.js index 03724ec45e..fd66c04056 100644 --- a/web/client/components/data/identify/enhancers/identify.js +++ b/web/client/components/data/identify/enhancers/identify.js @@ -79,7 +79,9 @@ export const identifyLifecycle = compose( setShowInMapPopup = () => {}, checkIdentifyIsMounted = () => {}, onInitPlugin = () => {}, - pluginCfg = {} + pluginCfg = {}, + enableHideEmptyPopupOption = () => {}, + hidePopupIfNoResults = false } = this.props; // Initialize plugin configuration @@ -91,6 +93,9 @@ export const identifyLifecycle = compose( showAllResponses, highlight: pluginCfg?.highlightEnabledFromTheStart || false }); + if (hidePopupIfNoResults) { + enableHideEmptyPopupOption(true); + } if (enabled || showInMapPopup) { changeMousePointer('pointer'); checkIdentifyIsMounted(true); diff --git a/web/client/components/data/query/AutocompleteField.jsx b/web/client/components/data/query/AutocompleteField.jsx deleted file mode 100644 index 6d6f47dac3..0000000000 --- a/web/client/components/data/query/AutocompleteField.jsx +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright 2017, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. -*/ - - -import PropTypes from 'prop-types'; - -import React from 'react'; -import { Combobox } from 'react-widgets'; -import { Glyphicon, Tooltip } from 'react-bootstrap'; -import AutocompleteListItem from './AutocompleteListItem'; -import { getMessageById } from '../../../utils/LocaleUtils'; -import { isLikeOrIlike } from '../../../utils/FilterUtils'; -import OverlayTrigger from '../../../components/misc/OverlayTrigger'; -import HTML from '../../../components/I18N/HTML'; -/** - * Combobox with remote autocomplete functionality. - * @memberof components.query - * @class - * @prop {bool} [disabled] if the combobox should be disabled - * @prop {object} [filterField] the filterField values - * @prop {string} [label] the label of the combobox - * @prop {number} [maxFeaturesWPS] the max number of features for any page - * @prop {string} [nextPage] the icon for the next page tool - * @prop {string} [prevPage] the icon for the prev page tool - * @prop {function} [onUpdateField] updated the filterfield values in the state - * @prop {bool} [paginated] if true it displays the pagination if there is more than one page - * @prop {string} [textField] the key used for the labes corresponding to filterField.options[x].label - * @prop {function} [toggleMenu] it toggles the combobox menu - * @prop {string} [valueField] the key used for the values corresponding to filterField.options[x].value - * - */ -class AutocompleteField extends React.Component { - static propTypes = { - dropUp: PropTypes.bool, - disabled: PropTypes.bool, - filterField: PropTypes.object, - label: PropTypes.string, - maxFeaturesWPS: PropTypes.number, - nextPage: PropTypes.string, - prevPage: PropTypes.string, - onUpdateField: PropTypes.func, - paginated: PropTypes.bool, - textField: PropTypes.string, - toggleMenu: PropTypes.func, - valueField: PropTypes.string - }; - - static contextTypes = { - messages: PropTypes.object - }; - - static defaultProps = { - label: null, - nextPage: "chevron-right", - prevPage: "chevron-left", - onUpdateField: () => {}, - paginated: true, - valueField: "value", - textField: "label", - toggleMenu: () => {}, - filterField: {} - }; - - getOptions = () => { - return this.props.filterField && - this.props.filterField.options && - this.props.filterField.options[this.props.filterField.attribute] && - this.props.filterField.options[this.props.filterField.attribute].map(o => { - return { value: o, label: o }; - }); - }; - - renderPagination = () => { - const numberOfPages = Math.ceil(this.props.filterField.fieldOptions.valuesCount / this.props.maxFeaturesWPS); - const firstPage = this.props.filterField.fieldOptions.currentPage === 1 || !this.props.filterField.fieldOptions.currentPage; - const lastPage = this.props.filterField.fieldOptions.currentPage === numberOfPages || !this.props.filterField.fieldOptions.currentPage; - return ( -
- { !firstPage && - this.props.onUpdateField(this.props.filterField.rowId, "value", this.props.filterField.value, "string", {currentPage: this.props.filterField.fieldOptions.currentPage - 1, delayDebounce: 0}) }/> - } - { !lastPage && - this.props.onUpdateField(this.props.filterField.rowId, "value", this.props.filterField.value, "string", {currentPage: this.props.filterField.fieldOptions.currentPage + 1, delayDebounce: 0})}/> - } -
- ); - }; - renderField = () => { - let selectedValue; - if (this.props.filterField && this.props.filterField.value && this.props.filterField.value !== "*") { - selectedValue = { - 'value': this.props.filterField.value, - 'label': this.props.filterField.value - }; - } - let options = this.getOptions() ? this.getOptions().slice(0) : []; - if (this.props.paginated && options.length > 0) { - options.push({ label: '', value: '', disabled: true, pagination: this.renderPagination() }); - } - const messages = { - emptyList: getMessageById(this.context.messages, "queryform.attributefilter.autocomplete.emptyList"), - open: getMessageById(this.context.messages, "queryform.attributefilter.autocomplete.open"), - emptyFilter: getMessageById(this.context.messages, "queryform.attributefilter.autocomplete.emptyFilter") - }; - const tooltip = ( - ); - const field = ( this.handleChange(value)} - onFocus={() => {this.handleFocus(options); }} - onSelect={this.handleSelect} - onToggle={() => {this.handleToggle(options); }} - textField={this.props.textField} - valueField={this.props.valueField} - value={selectedValue && selectedValue.value} - />); - return isLikeOrIlike(this.props.filterField.operator) ? ( - { field } - ) : field; - } - render() { - let label = this.props.label ? () : (); - return ( -
- {label} - {this.renderField()} -
); - } - - // called before onChange - handleSelect = () => { - this.selected = true; - }; - - handleChange = (input) => { - if (this.selected) { - this.selected = false; - if (input && input.value !== "") { - this.props.onUpdateField(this.props.filterField.rowId, "value", input.value, "string", {currentPage: 1, selected: "selected", delayDebounce: 0}); - } - } else { - this.props.onUpdateField(this.props.filterField.rowId, "value", input, "string", {currentPage: 1, delayDebounce: 1000}); - } - }; - - // called before onToggle - handleFocus = (options) => { - this.loadWithoutfilter(options); - }; - - handleToggle = () => { - this.props.toggleMenu(this.props.filterField.rowId, !this.props.filterField.openAutocompleteMenu); - }; - - loadWithoutfilter = (options) => { - if (options.length === 0 && !this.props.filterField.value) { - this.props.onUpdateField(this.props.filterField.rowId, "value", "", "string", {currentPage: 1, delayDebounce: 0}); - } - }; -} - -export default AutocompleteField; diff --git a/web/client/components/data/query/SimpleFilterField.jsx b/web/client/components/data/query/SimpleFilterField.jsx deleted file mode 100644 index 3ac4856131..0000000000 --- a/web/client/components/data/query/SimpleFilterField.jsx +++ /dev/null @@ -1,328 +0,0 @@ - -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import PropTypes from 'prop-types'; -import React from 'react'; -import { Panel, ButtonToolbar, Tooltip } from 'react-bootstrap'; -import Button from '../../misc/Button'; -import OverlayTrigger from '../../misc/OverlayTrigger'; -import ComboField from './ComboField'; -import NumberField from './NumberField'; -import TextField from './TextField'; -import { isEqual, head, findIndex } from 'lodash'; - -class SimpleFilterField extends React.Component { - static propTypes = { - dropUp: PropTypes.bool, - operator: PropTypes.string.isRequired, - maxLabelSize: PropTypes.number, - label: PropTypes.string, - attribute: PropTypes.string.isRequired, - optionsValues: PropTypes.array, - defaultOptions: PropTypes.array, - optionsLabels: PropTypes.object, - values: PropTypes.oneOfType([PropTypes.array, PropTypes.object, PropTypes.number, PropTypes.string]), - multivalue: PropTypes.bool, - combo: PropTypes.bool, - toolbar: PropTypes.bool, - type: PropTypes.oneOf(['list', 'number', 'string']).isRequired, - collapsible: PropTypes.bool, - defaultExpanded: PropTypes.bool, - eventKey: PropTypes.string, - fieldId: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - checkboxStyle: PropTypes.object, - radioStyle: PropTypes.object, - comboStyle: PropTypes.object, - sort: PropTypes.oneOf(['ASC', 'DESC', false]), - exception: PropTypes.oneOfType([ - PropTypes.object, - PropTypes.bool, - PropTypes.string - ]), - required: PropTypes.bool, - addAllOpt: PropTypes.bool, - updateFilter: PropTypes.func, - valueFormatter: PropTypes.func, - options: PropTypes.object - }; - - static contextTypes = { - messages: PropTypes.object - }; - - static defaultProps = { - options: null, - operator: null, - maxLabelSize: 20, - sort: false, - required: false, - exception: false, - checkboxStyle: {}, - radioStyle: {}, - comboStyle: {}, - optionsValues: [], - optionsLabels: {}, - defaultOptions: [], - values: [], - attribute: "", - multivalue: false, - combo: false, - toolbar: false, - type: null, - collapsible: false, - defaultExpanded: false, - updateFilter: () => {}, - valueFormatter: (value, labels) => { - return labels[value] ? labels[value] : value; - } - }; - - shouldComponentUpdate(nextProps) { - return !isEqual(this.props, nextProps); - } - - onRChange = (e) => { - this.props.updateFilter(this.props.fieldId, {values: [e.target.value]}); - }; - - onCheckChange = (e) => { - let values; - if (e.target.checked) { - values = [...this.props.values, e.target.value]; - } else { - values = this.props.values.filter((v) => { - return v !== e.target.value; - }); - } - let exception = this.props.required && values.length === 0 ? true : false; - this.props.updateFilter(this.props.fieldId, {values: values, exception: exception}); - }; - - onComboChange = (id, fieldName, value) => { - let values = Array.isArray(value) ? value : [value]; - let exception = this.props.required && values.length === 0 ? true : false; - this.props.updateFilter(id, {values: values, exception: exception}); - }; - - onNumberChange = (id, fieldName, value) => { - this.props.updateFilter(id, {values: value}); - }; - - onNumberException = (id, exception) => { - this.props.updateFilter(id, {exception: exception}); - }; - - onTextChange = (id, fieldName, value) => { - let exception = this.props.required && value.length === 0 ? true : false; - this.props.updateFilter(id, {values: value, exception: exception}); - }; - - getOptionsValue = () => { - let optionsValues = this.props.optionsValues.map((opt) => { - let val = opt; - if (val === null) { - val = "null"; - } else if (typeof val !== 'string') { - val = opt.toString(); - } - return val; - }, this); - if ( this.props.defaultOptions.length > 0) { - optionsValues = this.props.defaultOptions.reduce((opts, opt) => { - let nOpt = head(optionsValues.filter((v) => { - return v === opt; - })); - if (nOpt) { - opts.push(nOpt); - } - return opts; - }, []); - } else if (this.props.sort) { - optionsValues = this.props.sort === "ASC" ? optionsValues.sort() : optionsValues.sort().reverse(); - } - return optionsValues; - }; - - renderLabel = (opt) => { - let lab = this.props.valueFormatter(opt, this.props.optionsLabels, this.props.maxLabelSize); - return opt.length > this.props.maxLabelSize ? - {lab})}> - {lab.slice(0, this.props.maxLabelSize - 4).trim() + "...."} - - - : {lab}; - }; - - renderRadioElements = () => { - let optionsValues = this.getOptionsValue(); - return optionsValues.map((opt, idx) => { - return ( - ); - }, this); - }; - - renderRadioPanel = () => { - return this.renderRadioElements(); - }; - - renderCheckboxElements = () => { - let optionsValues = this.getOptionsValue(); - return optionsValues.map((opt, idx) => { - return ( - - ); - }, this); - }; - - renderToolbar = () => { - return ( - - - - - ); - }; - - renderCheckboxPanel = () => { - return ( -
- {this.renderCheckboxElements()} - {this.props.toolbar ? this.renderToolbar() : null} -
- ); - }; - - renderCombo = () => { - let optionsValues = this.getOptionsValue(); - let options = optionsValues.map((o) => { - return {val: o, text: this.props.valueFormatter(o, this.props.optionsLabels)}; - }, this); - let val = this.props.values && this.props.values.length > 0 ? {fieldValue: this.props.multivalue ? this.props.values : this.props.values[0] } : {}; - return ( -
- - {this.props.toolbar && this.props.multivalue ? this.renderToolbar() : null} -
- ); - }; - - renderCheckOrRadio = () => { - return this.props.multivalue ? this.renderCheckboxPanel() : this.renderRadioPanel(); - }; - - renderText = () => { - return ( - - ); - - }; - - renderNumber = () => { - return ( - <"} - lowLabel={this.props.optionsLabels.lowLabel} - upLabel={this.props.optionsLabels.upLabel} - fieldName={this.props.attribute} - fieldRowId={this.props.fieldId} - fieldValue={this.props.values} - attType={this.props.type} - onUpdateField={this.onNumberChange} - onUpdateExceptionField={this.onNumberException} - fieldException={this.props.exception} - options={this.props.options} - - />); - }; - - renderList = () => { - return this.props.combo ? this.renderCombo() : this.renderCheckOrRadio(); - }; - - render() { - let comp; - switch (this.props.type) { - case "list": { - comp = this.renderList(); - break; - } - case "number": { - comp = this.renderNumber(); - break; - } - case "string": { - comp = this.renderText(); - break; - } - default: { - comp = this.renderList(); - } - } - return ( - - {comp} - - ); - } - - selectAll = () => { - let values = this.getOptionsValue(); - this.props.updateFilter(this.props.fieldId, {values: values, exception: false}); - }; - - clearAll = () => { - this.props.updateFilter(this.props.fieldId, {values: [], exception: this.props.required}); - }; -} - -export default SimpleFilterField; diff --git a/web/client/components/data/query/__tests__/AutocompleteField-test.jsx b/web/client/components/data/query/__tests__/AutocompleteField-test.jsx deleted file mode 100644 index dd5b55b4b0..0000000000 --- a/web/client/components/data/query/__tests__/AutocompleteField-test.jsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import expect from 'expect'; - -import React from 'react'; -import ReactDOM from 'react-dom'; -import AutocompleteField from '../AutocompleteField'; - -describe('AutocompleteField', () => { - beforeEach((done) => { - document.body.innerHTML = '
'; - setTimeout(done); - }); - - afterEach((done) => { - ReactDOM.unmountComponentAtNode(document.getElementById("container")); - document.body.innerHTML = ''; - setTimeout(done); - }); - - it('create a AutocompleteField component without any props', () => { - const cmp = ReactDOM.render(, document.getElementById("container")); - expect(cmp).toExist(); - }); - it('create a AutocompleteField with 2 options', () => { - let conf = { - filterField: - {options: [{ - value: "val1", - label: "val1" - }, { - value: "val2", - label: "val2" - }] - }, - autocompleteEnabled: true - }; - const cmp = ReactDOM.render(, document.getElementById("container")); - expect(cmp).toExist(); - let node = ReactDOM.findDOMNode(cmp); - expect(node).toExist(); - let inputs = node.getElementsByTagName("input"); - expect(inputs).toExist(); - expect(inputs.length).toBe(1); - - }); -}); diff --git a/web/client/components/data/query/__tests__/SimpleFilterFiled-test.jsx b/web/client/components/data/query/__tests__/SimpleFilterFiled-test.jsx deleted file mode 100644 index d4f6383618..0000000000 --- a/web/client/components/data/query/__tests__/SimpleFilterFiled-test.jsx +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import expect from 'expect'; - -import React from 'react'; -import ReactDOM from 'react-dom'; -import SimpleFilterField from '../SimpleFilterField'; - -describe('SimpleFilterField', () => { - beforeEach((done) => { - document.body.innerHTML = '
'; - setTimeout(done); - }); - - afterEach((done) => { - ReactDOM.unmountComponentAtNode(document.getElementById("container")); - document.body.innerHTML = ''; - setTimeout(done); - }); - - it('create a SimpleFilterField component without any props', () => { - const cmp = ReactDOM.render(, document.getElementById("container")); - expect(cmp).toExist(); - }); - it('create a SimpleFilterField rendering radio', () => { - let conf = { - "fieldId": 1, - "label": "Highway System", - "attribute": "highway_system", - "multivalue": false, - "values": ["state"], - "optionsValues": ["local", "state"], - "optionsLabels": { - "local": "Local", - "state": "State" - }, - "required": true, - "sort": "ASC", - "defaultExpanded": true, - "collapsible": true - }; - const cmp = ReactDOM.render(, document.getElementById("container")); - expect(cmp).toExist(); - let node = ReactDOM.findDOMNode(cmp); - expect(node).toExist(); - let inputs = node.getElementsByTagName("input"); - expect(inputs).toExist(); - expect(inputs.length).toBe(2); - expect(inputs[0].getAttribute("type")).toBe("radio"); - inputs[0].checked = true; - expect(inputs[0].checked).toBe(true); - let e = {target: {value: "local"}}; - cmp.onRChange(e); - }); - it('create a SimpleFilterField rendering checkbox', () => { - let conf = { - "fieldId": 4, - "label": "Day(s) of Week", - "attribute": "day_of_week", - "checkboxStyle": {"width": 80}, - "multivalue": true, - "required": true, - "sort": "ASC", - "toolbar": true, - "type": "list", - "operator": "=", - "defaultExpanded": true, - "collapsible": true, - "optionsValues": ["Monday", "Tuesday", "Wednesday"], - "values": ["Monday", "Tuesday", "Wednesday"], - "defaultOptions": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] - }; - const cmp = ReactDOM.render(, document.getElementById("container")); - expect(cmp).toExist(); - let node = ReactDOM.findDOMNode(cmp); - expect(node).toExist(); - let inputs = node.getElementsByTagName("input"); - expect(inputs).toExist(); - expect(inputs.length).toBe(3); - expect(inputs[0].getAttribute("type")).toBe("checkbox"); - expect(inputs[0].checked).toBe(true); - expect(inputs[1].checked).toBe(true); - expect(inputs[2].checked).toBe(true); - inputs[0].checked = true; - let e = {target: {value: "Monday", checked: false}}; - cmp.onCheckChange(e); - e = {target: {value: "Monday", checked: true}}; - cmp.onCheckChange(e); - - }); - it('create a SimpleFilterField rendering combo single with bool', () => { - let conf = { - "fieldId": 4, - "label": "Day(s) of Week", - "attribute": "day_of_week", - "multivalue": false, - "sort": "ASC", - "toolbar": true, - "type": "list", - "operator": "=", - "defaultExpanded": true, - "collapsible": true, - "optionsValues": [false, true, null], - "values": [false], - "combo": true, - "optionsLabels": { - "true": "Y", - "false": "N", - "null": "Empty" - } - }; - const cmp = ReactDOM.render(, document.getElementById("container")); - expect(cmp).toExist(); - cmp.onComboChange("", "", "null"); - }); - it('create a SimpleFilterField rendering combo multi', () => { - let conf = { - "fieldId": 4, - "label": "Day(s) of Week", - "attribute": "day_of_week", - "checkboxStyle": {"width": 80}, - "multivalue": true, - "required": true, - "sort": "DESC", - "toolbar": true, - "combo": true, - "type": "list", - "operator": "=", - "defaultExpanded": true, - "collapsible": true, - "optionsValues": ["Monday", "Tuesday", "Wednesday"], - "values": ["Monday", "Tuesday", "Wednesday"] - }; - const cmp = ReactDOM.render(, document.getElementById("container")); - expect(cmp).toExist(); - cmp.selectAll(); - cmp.clearAll(); - cmp.shouldComponentUpdate(""); - }); - it('create a SimpleFilterField rendering number', () => { - let conf = { - "fieldId": 4, - "label": "Day(s) of Week", - "attribute": "day_of_week", - "sort": "DESC", - "type": "number", - "operator": "><", - "defaultExpanded": true, - "collapsible": true - }; - const cmp = ReactDOM.render(, document.getElementById("container")); - expect(cmp).toExist(); - cmp.selectAll(); - cmp.clearAll(); - cmp.onNumberChange(4, "day_of_week", 10); - cmp.onNumberException(4, "exception"); - }); - it('create a SimpleFilterField rendering text', () => { - let conf = { - "fieldId": 4, - "label": "Day(s) of Week", - "attribute": "day_of_week", - "sort": "DESC", - "type": "text", - "operator": "ilike", - "defaultExpanded": true, - "collapsible": true - }; - const cmp = ReactDOM.render(, document.getElementById("container")); - expect(cmp).toExist(); - cmp.selectAll(); - cmp.clearAll(); - cmp.onTextChange(4, "day_of_week", "10"); - }); -}); diff --git a/web/client/components/file/FileUploader.jsx b/web/client/components/file/FileUploader.jsx deleted file mode 100644 index 02315b5db9..0000000000 --- a/web/client/components/file/FileUploader.jsx +++ /dev/null @@ -1,151 +0,0 @@ -import PropTypes from 'prop-types'; - -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import React from 'react'; - -import { round } from 'lodash'; -import { Message, DateFormat } from '../I18N/I18N'; -import Spinner from 'react-spinkit'; -import { Glyphicon, ProgressBar, Table, Alert } from 'react-bootstrap'; -import Dropzone from 'react-dropzone'; - -class FileUploader extends React.Component { - static propTypes = { - dropMessage: PropTypes.node, - dropZoneStyle: PropTypes.object, - dropZoneActiveStyle: PropTypes.object, - beforeUploadMessage: PropTypes.node, - // can be a boolean or an object like this : { progress: 0.99 } - uploading: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), - onBeforeUpload: PropTypes.func, - onUpload: PropTypes.func, - uploadAdditionalParams: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), - // if exists do not run before upload and start directly the upload after drag - allowUpload: PropTypes.object, - error: PropTypes.object - }; - - static defaultProps = { - beforeUploadMessage: , - dropMessage: , - dropZoneStyle: { - borderStyle: "dashed", - borderWidth: "3px", - transition: "all 0.3s ease-in-out" - }, - dropZoneActiveStyle: { - backgroundColor: "#eee", - borderWidth: "5px", - boxShadow: "0px 0px 25px 14px #d9edf7" - - }, - onBeforeUpload: () => {}, - onUpload: () => {} - }; - - state = {}; - - componentDidUpdate() { - if (this.props.allowUpload && this.state && this.state.files) { - this.uploadFiles(this.state.files); - } - } - - renderProgress = (uploading) => { - if (uploading && uploading.progress) { - let percent = round(uploading.progress * 100, 2); - return ; - } - return null; - }; - - renderPreview = () => { - return ( - - - - - - - - - - {this.state.fileList && this.state.fileList.map((file, index) => - ( - - - - - ) ) - }
{file.name}{this.humanFileSize(file.size)}{file.type}
); - }; - - renderError = () => { - if (this.props.error) { - return There was an error during the upload: {this.props.error.statusText}
{this.props.error.data}
; - } - return null; - }; - - render() { - if (this.state && this.state.files) { - return
{this.props.beforeUploadMessage}{this.renderPreview()}
; - } else if ( this.props && this.props.uploading && !this.state.files ) { - return (
- - {this.renderProgress(this.props.uploading)} - {this.renderPreview()} -
); - } - - return (
-
- - - {this.props.dropMessage} - -
-
{this.renderError()}
); - - } - - humanFileSize = (size) => { - var i = Math.floor( Math.log(size) / Math.log(1024) ); - return ( size / Math.pow(1024, i) ).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]; - }; - - uploadFiles = (files) => { - if (!files) return; - if (!this.props.allowUpload) { - this.setState({files: files, fileList: files}); - this.props.onBeforeUpload(files); - } else { - this.setState({files: null, fileList: files}); - this.props.onUpload(files, this.props.uploadAdditionalParams); - } - }; -} - -export default FileUploader; diff --git a/web/client/components/help/HelpToggleBtn.jsx b/web/client/components/help/HelpToggleBtn.jsx deleted file mode 100644 index cdf0b53dd6..0000000000 --- a/web/client/components/help/HelpToggleBtn.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import PropTypes from 'prop-types'; - -/** - * Copyright 2015, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - - -import React from 'react'; - -import ToggleButton from '../buttons/ToggleButton'; -import './help.css'; - -/** - * A toggle button class for enabling / disabling help modus - */ -class HelpToggleBtn extends React.Component { - static propTypes = { - key: PropTypes.string, - isButton: PropTypes.bool, - glyphicon: PropTypes.string, - pressed: PropTypes.bool, - changeHelpState: PropTypes.func, - changeHelpwinVisibility: PropTypes.func - }; - - static defaultProps = { - key: 'helpButton', - isButton: true, - glyphicon: 'question-sign' - }; - - onClick = () => { - this.props.changeHelpState(!this.props.pressed); - this.props.changeHelpwinVisibility(false); - }; - - render() { - return ( - - ); - } -} - -export default HelpToggleBtn; diff --git a/web/client/components/help/__tests__/HelpToggleBtn-test.jsx b/web/client/components/help/__tests__/HelpToggleBtn-test.jsx deleted file mode 100644 index da721945f3..0000000000 --- a/web/client/components/help/__tests__/HelpToggleBtn-test.jsx +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2015, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import React from 'react'; - -import ReactDOM from 'react-dom'; -import HelpToggleBtn from '../HelpToggleBtn'; -import expect from 'expect'; - -describe('Test for HelpToggleBtn', () => { - beforeEach((done) => { - document.body.innerHTML = '
'; - setTimeout(done); - }); - - afterEach((done) => { - ReactDOM.unmountComponentAtNode(document.getElementById("container")); - document.body.innerHTML = ''; - setTimeout(done); - }); - - // test DEFAULTS - it('creates the component with defaults', () => { - const helpToggleBtn = ReactDOM.render(, document.getElementById("container")); - expect(helpToggleBtn).toExist(); - - const helpToggleBtnDom = ReactDOM.findDOMNode(helpToggleBtn); - expect(helpToggleBtnDom).toExist(); - - const icons = helpToggleBtnDom.getElementsByTagName('span'); - expect(icons.length).toBe(1); - - expect(helpToggleBtnDom.className.indexOf('default') >= 0).toBe(true); - expect(helpToggleBtnDom.innerHTML).toExist(); - }); - - it('test button state', () => { - const helpToggleBtn = ReactDOM.render(, document.getElementById("container")); - expect(helpToggleBtn).toExist(); - - const helpToggleBtnDom = ReactDOM.findDOMNode(helpToggleBtn); - - expect(helpToggleBtnDom.className.indexOf('primary') >= 0).toBe(true); - }); - - it('test click handler calls right functions', () => { - var triggered = 0; - const clickFn = { - changeHelpState: () => {triggered++; }, - changeHelpwinVisibility: () => {triggered++; } - }; - // const spy = expect.spyOn(testHandlers, 'onClick'); - const helpToggleBtn = ReactDOM.render(, document.getElementById("container")); - - const helpToggleBtnDom = ReactDOM.findDOMNode(helpToggleBtn); - helpToggleBtnDom.click(); - - expect(triggered).toEqual(2); - }); - -}); diff --git a/web/client/components/manager/importer/BreadCrumb.jsx b/web/client/components/manager/importer/BreadCrumb.jsx deleted file mode 100644 index 362bdbe1d8..0000000000 --- a/web/client/components/manager/importer/BreadCrumb.jsx +++ /dev/null @@ -1,68 +0,0 @@ -import PropTypes from 'prop-types'; - -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import React from 'react'; - -import Message from '../../I18N/Message'; - -class BreadCrumb extends React.Component { - static propTypes = { - loading: PropTypes.bool, - loadImports: PropTypes.func, - loadImport: PropTypes.func, - loadTask: PropTypes.func, - selectedImport: PropTypes.object, - selectedTask: PropTypes.object, - selectedTransform: PropTypes.object - }; - - static defaultProps = { - loadImports: () => {}, - loadImport: () => {}, - loadTask: () => {} - }; - - render() { - if ( this.props.selectedImport && this.props.selectedTask && this.props.selectedTransform) { - return (
    -
  1. {e.preventDefault(); this.props.loadImports(); }}>
  2. -
  3. {e.preventDefault(); this.props.loadImport(this.props.selectedImport.id); }}> - -
  4. -
  5. {e.preventDefault(); this.props.loadTask(this.props.selectedImport.id, this.props.selectedTask.id); }}> - Package {this.props.selectedTask.id} -
  6. -
  7. Transform {this.props.selectedTransform.id}
  8. -
); - } - if ( this.props.selectedImport && this.props.selectedTask) { - return ( -
    -
  1. {e.preventDefault(); this.props.loadImports(); }}>
  2. -
  3. {e.preventDefault(); this.props.loadImport(this.props.selectedImport.id); }}> - -
  4. -
  5. Package {this.props.selectedTask.id}
  6. -
); - } - if (this.props.selectedImport) { - return ( -
    -
  1. {e.preventDefault(); this.props.loadImports(); }}>
  2. -
  3. -
); - } - return ( -
    -
  1. -
); - } -} - -export default BreadCrumb; diff --git a/web/client/components/manager/importer/Import.jsx b/web/client/components/manager/importer/Import.jsx deleted file mode 100644 index 39749547c9..0000000000 --- a/web/client/components/manager/importer/Import.jsx +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import Spinner from 'react-spinkit'; -import OverlayTrigger from '../../misc/OverlayTrigger'; - -import Message from '../../I18N/Message'; -import TaskProgress from './TaskProgress'; -import { getbsStyleForState } from '../../../utils/ImporterUtils'; -import { Grid, Row, Panel, Label, Table, Glyphicon, Tooltip } from 'react-bootstrap'; -import './style/importer.css'; -import Button from '../../misc/Button'; - -class Task extends React.Component { - static propTypes = { - timeout: PropTypes.number, - "import": PropTypes.object, - loadImport: PropTypes.func, - loadTask: PropTypes.func, - loadStylerTool: PropTypes.func, - runImport: PropTypes.func, - updateProgress: PropTypes.func, - deleteImport: PropTypes.func, - deleteTask: PropTypes.func, - deleteAction: PropTypes.node, - placement: PropTypes.string - }; - - static contextTypes = { - router: PropTypes.object, - messages: PropTypes.object - }; - - static defaultProps = { - placement: "bottom", - deleteAction: , - timeout: 10000, - "import": {}, - loadTask: () => {}, - runImport: () => {}, - loadImport: () => {}, - loadStylerTool: () => {}, - updateProgress: () => {}, - deleteImport: () => {}, - deleteTask: () => {} - }; - - componentDidMount() { - if (this.props.import.state === "RUNNING") { - // Check if some task is running the update is not needed - this.interval = setInterval(this.props.loadImport.bind(null, this.props.import.id), this.props.timeout); - - } - } - - componentWillUnmount() { - if (this.interval) { - clearInterval(this.interval); - } - } - - renderGeneral = (importObj) => { - return (
-
-
-
-
{importObj.archive}
-
); - }; - - renderProgressTask = (task) => { - if ( task.state === "RUNNING") { - return ; - } - return null; - }; - - renderTask = (task) => { - let tooltipDelete = {this.props.deleteAction}; - return ( - {e.preventDefault(); this.props.loadTask(task.id); }} >{task.id} - {this.renderProgressTask(task)}{this.renderLoadingTask(task)} - - - - - - ); - }; - - renderLoading = () => { - if (this.props.import.loading) { - return
; - } - return null; - }; - - renderLoadingMessage = (task) => { - switch (task.message) { - case "applyPresets": - return ; - case "deleting": - return ; - case "analyzing": - return ; - default: - return null; - } - }; - - renderLoadingTask = (task) => { - if (task.loading) { - return (
- {this.renderLoadingMessage(task)} -
); - } - return null; - }; - - render() { - return ( - {this.renderLoading()}
} > - - - {this.renderGeneral(this.props.import)} - - -

- - - - - - - - - - {this.props.import.tasks.map(this.renderTask)} - -
-
- - { - this.props.import.tasks.reduce((prev, cur) => prev || cur.state === "READY", false) ? - - : null - } - - -
- - ); - } - - editDefaultStyle = (taskId) => { - this.context.router.history.push("/styler/openlayers"); - this.props.loadStylerTool(taskId); - }; -} - -export default Task; diff --git a/web/client/components/manager/importer/Importer.jsx b/web/client/components/manager/importer/Importer.jsx deleted file mode 100644 index db9ec69323..0000000000 --- a/web/client/components/manager/importer/Importer.jsx +++ /dev/null @@ -1,263 +0,0 @@ -import PropTypes from 'prop-types'; - -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ -import React from 'react'; - -import Spinner from 'react-spinkit'; -import Message from '../../I18N/Message'; -import ImportsGrid from './ImportsGrid'; -import Workspace from './Workspace'; -import FileUploader from '../../file/FileUploader'; -import Task from './Task'; -import Import from './Import'; -import Transform from './Transform'; -import { Grid, Col, Row, Alert } from 'react-bootstrap'; -import BreadCrumb from './BreadCrumb'; -import { head } from 'lodash'; - -class Importer extends React.Component { - static propTypes = { - loading: PropTypes.bool, - taskCreationError: PropTypes.object, - error: PropTypes.object, - defaultPresets: PropTypes.string, - /** - * Presets: {PRESET_ID: [changes: [{target: {}, layer: {}}], match: function() {...}, transforms: [{}, {}] }]} - */ - presets: PropTypes.object, - uploading: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), - createImport: PropTypes.func, - runImport: PropTypes.func, - loadWorkspaces: PropTypes.func, - workspaces: PropTypes.array, - selectedWorkSpace: PropTypes.object, - selectWorkSpace: PropTypes.func, - workspaceCreationStatus: PropTypes.object, - dismissWorkspaceCreationStatus: PropTypes.func, - createWorkspace: PropTypes.func, - datastoreTemplates: PropTypes.array, - deleteImport: PropTypes.func, - updateTask: PropTypes.func, - deleteTask: PropTypes.func, - loadImports: PropTypes.func, - loadImport: PropTypes.func, - loadTask: PropTypes.func, - updateProgress: PropTypes.func, - loadLayer: PropTypes.func, - updateLayer: PropTypes.func, - loadStylerTool: PropTypes.func, - loadTransform: PropTypes.func, - updateTransform: PropTypes.func, - editTransform: PropTypes.func, - deleteTransform: PropTypes.func, - uploadImportFiles: PropTypes.func, - selectedImport: PropTypes.object, - selectedTask: PropTypes.object, - selectedTransform: PropTypes.object, - imports: PropTypes.array, - onMount: PropTypes.func - }; - - static defaultProps = { - createImport: () => {}, - loadImport: () => {}, - loadTask: () => {}, - loadLayer: () => {}, - loadTransform: () => {}, - editTransform: () => {}, - updateTransform: () => {}, - loadImports: () => {}, - updateProgress: () => {}, - deleteTransform: () => {}, - uploadImportFiles: () => {}, - loadWorkspaces: () => {}, - dismissWorkspaceCreationStatus: () => {}, - createWorkspace: () => {}, - onMount: () => {}, - imports: [] - }; - - componentDidMount() { - this.props.onMount(); - } - - getPresets = () => { - return this.props.presets && this.props.presets[this.props.defaultPresets]; - }; - - getImportCreationDefaults = () => { - let presets = this.getPresets(); - if (!presets) { - return { - importCreationDefaults: { - "import": { - "targetWorkspace": { - "workspace": { - "name": "cite" - } - } - } - } - }; - } else if (this.props.selectedWorkSpace) { - return { - "import": { - "targetWorkspace": { - "workspace": { - "name": this.props.selectedWorkSpace.value - } - } - } - }; - } - return head(presets.filter((preset) => preset.import )); - }; - - getTargetWorkspace = (selectedImport) => { - let targetWorkspace = selectedImport && selectedImport.targetWorkspace; - if (targetWorkspace) { - return targetWorkspace && targetWorkspace.workspace && targetWorkspace.workspace.name; - } - let creationDefaults = this.getImportCreationDefaults(); - let importObj = creationDefaults && creationDefaults.import || creationDefaults && creationDefaults.importCreationDefaults && creationDefaults.importCreationDefaults.import; - return importObj && importObj.targetWorkspace && importObj.targetWorkspace.workspace && importObj.targetWorkspace.workspace.name; - - - }; - - renderError = () => { - if (this.props.error) { - return There was an error during the import list loading: {this.props.error.statusText}; - } - return null; - }; - - renderLoading = () => { - if (this.props.loading) { - return
; - } - return null; - }; - - renderDetails = () => { - let breadcrumb = (); - if ( this.props.selectedImport && this.props.selectedTask && this.props.selectedTransform) { - return (
- {breadcrumb} -

Transform {this.props.selectedTransform.id}

- -
); - } - if ( this.props.selectedImport && this.props.selectedTask) { - return (
- {breadcrumb} - -
); - } - if (this.props.selectedImport) { - return (
- {breadcrumb} - -
); - } - return (
- {breadcrumb} - -
); - }; - - render() { - let message = this.props.selectedImport ? "importer.dropfileImport" : "importer.dropfile"; - return ( - - - - } - dropMessage={} - uploading={this.props.uploading} - allowUpload={this.props.selectedImport} - onBeforeUpload={this.props.createImport.bind(null, this.getImportCreationDefaults())} - onUpload={this.props.uploadImportFiles.bind(null, this.props.selectedImport && this.props.selectedImport.id)} - uploadAdditionalParams={this.getPresets()} /> - - - - - - - {this.renderLoading()} - {this.renderDetails()} - {this.renderError()} - - ); - } - - createUpdateFunction = () => { - return; - }; -} - -export default Importer; diff --git a/web/client/components/manager/importer/ImportsGrid.jsx b/web/client/components/manager/importer/ImportsGrid.jsx deleted file mode 100644 index 9f9d26171b..0000000000 --- a/web/client/components/manager/importer/ImportsGrid.jsx +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import Spinner from 'react-spinkit'; -import PropTypes from 'prop-types'; -import { Table, Glyphicon, Label, Tooltip } from 'react-bootstrap'; -import { findIndex } from 'lodash'; - -import Button from '../../misc/Button'; -import Message from '../../I18N/Message'; -import { getbsStyleForState } from '../../../utils/ImporterUtils'; -import OverlayTrigger from '../../misc/OverlayTrigger'; - -class ImportsGrid extends React.Component { - static propTypes = { - loading: PropTypes.bool, - timeout: PropTypes.number, - loadImports: PropTypes.func, - loadImport: PropTypes.func, - deleteImport: PropTypes.func, - imports: PropTypes.array, - deleteAction: PropTypes.object, - placement: PropTypes.string - }; - - static contextTypes = { - messages: PropTypes.object - }; - - static defaultProps = { - timeout: 5000, - loadImports: () => {}, - placement: "bottom", - deleteAction: , - loadImport: () => {}, - deleteImport: () => {}, - imports: [] - }; - - componentDidMount() { - this.interval = setInterval(() => {this.update(); }, this.props.timeout); - } - - componentWillUnmount() { - clearInterval(this.interval); - } - - renderLoadingMessage = (importObj) => { - switch (importObj.message) { - case "deleting": - return ; - default: - return null; - } - }; - - renderLoadingImport = (importObj) => { - if (importObj.loading) { - return
{this.renderLoadingMessage(importObj)}
; - } - return null; - }; - - renderImportErrorMessage = (imp) => { - if (imp && imp.error) { - return ; - } - return null; - }; - - renderImport = (importObj) => { - let tooltip = {this.props.deleteAction}; - return ( - {e.preventDefault(); this.props.loadImport(importObj.id); }} >{importObj.id} - - {this.renderLoadingImport(importObj)} - {this.renderImportErrorMessage(importObj)} - - - - - - - ); - }; - - render() { - if (this.props.loading && this.props.imports.length === 0) { - return ; - } - return ( - - - - - - - - - - {this.props.imports.map(this.renderImport)} - -
- ); - } - - update = () => { - if (this.props.imports) { - let i = findIndex(this.props.imports, (importObj) => importObj.state === "RUNNING"); - if ( i >= 0 ) { - this.props.loadImports(); - } - } - }; -} - -export default ImportsGrid; diff --git a/web/client/components/manager/importer/Layer.jsx b/web/client/components/manager/importer/Layer.jsx deleted file mode 100644 index d00a211a71..0000000000 --- a/web/client/components/manager/importer/Layer.jsx +++ /dev/null @@ -1,99 +0,0 @@ - -/** - * Copyright 2016, GeoSolutions Sas. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; - -import Button from '../../misc/Button'; -import { Panel } from 'react-bootstrap'; -import Message from '../../I18N/Message'; - - -class Layer extends React.Component { - static propTypes = { - layer: PropTypes.object, - loading: PropTypes.bool, - edit: PropTypes.bool, - panProps: PropTypes.object, - updateLayer: PropTypes.func - }; - - static defaultProps = { - layer: {}, - edit: true, - loading: false, - updateLayer: () => {} - }; - - state = {}; - - onChange = (event) => { - let state = {}; - state[event.target.name] = event.target.value || ""; - this.setState(state); - }; - - renderInput = (name) => { - let input; - if (name !== "description") { - input = (); - } else { - input = (