From de38b29fad0787ad67889215a0f281f0d34a180c Mon Sep 17 00:00:00 2001 From: Daniel Kryska <31220811+danielkryska@users.noreply.github.com> Date: Fri, 4 Dec 2020 15:44:21 +0100 Subject: [PATCH] [Fix] Footprint, regular member visible elements, validate image WMS (#992) - [Fix] remove a footprint from search params on a polygon removal from the map - Remove a invitation edition - [Fix] Hide sub navigation in an institution profile if a user is just a member - [Fix] Image WMS validation while add a new overlay - Fetch capabilitites and set URL LAYERS AND STYLES if are undefined - BBOX URL param now can have a negative values - Server reponse should validate response content-type and should be image/* --- .../overlay-list/overlay-list.component.html | 30 +-- .../overlay-list/overlay-list.component.ts | 78 ++++-- .../components/overlay-list/wms-url.utils.ts | 43 +--- .../src/app/state/session/session.service.ts | 10 +- .../src/app/views/login/login.component.ts | 9 +- .../app/views/map-view/map/map.component.ts | 2 + .../map-view/state/overlay/image-wms.utils.ts | 26 +- .../map-view/state/overlay/overlay.model.ts | 3 - .../map-view/state/overlay/overlay.utils.ts | 2 +- .../map-view/state/product/product.service.ts | 4 - .../overlay-list-modal/wms-url.utils.ts | 226 ------------------ .../app/views/settings/dashboards.routes.ts | 1 - .../institution-profile.component.html | 2 +- .../person-list/person-list.component.html | 12 - .../person-list/person-list.component.spec.ts | 6 - 15 files changed, 102 insertions(+), 352 deletions(-) delete mode 100644 s4e-web/src/app/views/map-view/view-manager/overlay-list-modal/wms-url.utils.ts diff --git a/s4e-web/src/app/components/overlay-list/overlay-list.component.html b/s4e-web/src/app/components/overlay-list/overlay-list.component.html index b9eb173c9..e5553c50b 100644 --- a/s4e-web/src/app/components/overlay-list/overlay-list.component.html +++ b/s4e-web/src/app/components/overlay-list/overlay-list.component.html @@ -16,36 +16,10 @@
-

URL zdjęcia WMS może zawierać następujące parametry:

-
    -
  1. - LAYERS= - lista oddzielonych przecinkami nazw warstw (OPCJONALNE) -
  2. -
  3. - STYLES= - lista oddzielonych przecinkami typów renderingu dla każdej - z podanych warstw (OPCJONALNE) -
  4. -
    -
-

- Parametry akceptowalne i ich wartości, - usuwane podczas przetwarzania URL dla prawidłowego wyświetlania obrazu: + Jeśli podany URL nie posiada parametru LAYERS to zostanie on + uzupełniony nazwami wszystkich warstw zwróconych przez metodę GetCapabilities podanego serwera WMS.

-
    -
  1. VERSION= z wartościami numerycznymi w formacie x.y.z
  2. -
  3. REQUEST=GetMap
  4. -
  5. BBOX= z wartością w formacie minx,miny,maxx,maxy
  6. -
  7. SRS= lub CRS= w formacie nazwa przestrzeni:identyfikator
  8. -
  9. WIDTH= w formacie numerycznym
  10. -
  11. HEIGHT= w formacie numerycznym
  12. -
  13. FORMAT= w formacie image/rozszerzenie zdjęcia (vnd.jpeg-png|vnd.jpeg-png8|png|gif|tiff|jpg)
  14. -
  15. SERVICE=WMS
  16. -
  17. TRANSPARENT= z wartością false lub true
  18. -
-
diff --git a/s4e-web/src/app/components/overlay-list/overlay-list.component.ts b/s4e-web/src/app/components/overlay-list/overlay-list.component.ts index a0a8fc008..cff46a8de 100644 --- a/s4e-web/src/app/components/overlay-list/overlay-list.component.ts +++ b/s4e-web/src/app/components/overlay-list/overlay-list.component.ts @@ -8,12 +8,13 @@ import {OverlayQuery} from '../../views/map-view/state/overlay/overlay.query'; import {disableEnableForm, validateAllFormFields} from '../../utils/miscellaneous/miscellaneous'; import {OverlayService} from '../../views/map-view/state/overlay/overlay.service'; import {FormControl, FormGroup, Validators} from '@ng-stack/forms'; -import {removeAutoGeneratedParams, WMS_URL_VALIDATORS} from './wms-url.utils'; -import {Observable, Subject} from 'rxjs'; +import {WMS_URL_VALIDATORS} from './wms-url.utils'; +import {forkJoin, Observable, of, Subject} from 'rxjs'; import Projection from 'ol/proj/Projection'; import {filter, map, switchMap, takeUntil, tap} from 'rxjs/operators'; import {untilDestroyed} from 'ngx-take-until-destroy'; import {ActivatedRoute} from '@angular/router'; +import WMSCapabilities from 'ol/format/WMSCapabilities'; export interface OverlayForm { url: string; @@ -131,22 +132,20 @@ export class OverlayListComponent implements OnInit, OnDestroy { addNewOverlay() { validateAllFormFields(this.newOverlayForm); - - const sourceUrl = this.newOverlayForm.controls.url.value; - const url = removeAutoGeneratedParams(sourceUrl); if (this.newOverlayForm.invalid) { return; } + const url = this.newOverlayForm.controls.url.value; if (this.newOwner === 'INSTITUTIONAL') { this.hasLoadingError$(url) .pipe( untilDestroyed(this), - switchMap(() => this._activatedRoute.queryParamMap), - map(params => params.get('institution')) + switchMap(formattedUrl => forkJoin([of(formattedUrl), this._activatedRoute.queryParamMap])), + map(([url, params]) => [url, params.get('institution')]) ) .subscribe( - institutionSlug => this._overlayService + ([url, institutionSlug]) => this._overlayService .createInstitutionalOverlay({...this.newOverlayForm.value, url}, institutionSlug), (error: string) => this._notificationService.addGeneral({content: error, type: 'error'}) ); @@ -156,13 +155,13 @@ export class OverlayListComponent implements OnInit, OnDestroy { this.hasLoadingError$(url) .pipe(untilDestroyed(this)) .subscribe( - () => { + formattedUrl => { switch (this.newOwner) { case 'PERSONAL': - this._overlayService.createPersonalOverlay({...this.newOverlayForm.value, url}); + this._overlayService.createPersonalOverlay({...this.newOverlayForm.value, url: formattedUrl}); break; case 'GLOBAL': - this._overlayService.createGlobalOverlay({...this.newOverlayForm.value, url}); + this._overlayService.createGlobalOverlay({...this.newOverlayForm.value, url: formattedUrl}); break; } }, @@ -175,15 +174,37 @@ export class OverlayListComponent implements OnInit, OnDestroy { * Observable is used due to not working Open Layers Event Dispatcher * and force state changes */ + url = url.toLowerCase().includes('request=getcapabilities') ? url.split("?")[0] : url; return new Observable(observer$ => { - const source = getImageWmsFrom({url}); - source.setImageLoadFunction(getImageWmsLoader(observer$)); - - const BBOX = [49.497526,14.407200,54.573956,23.836237]; - const standardCrs = 'EPSG:3857'; - source - .getImage(BBOX, 1, 1, new Projection({code: standardCrs})) - .load(); + const capabilitiesUrl = `${url.split('?')[0]}?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities`; + fetch(capabilitiesUrl) + .then(response => response.text()) + .then(text => (new WMSCapabilities()).read(text)) + .then(parsedCapabilities => parsedCapabilities.Capability.Layer) + .then(layerMetadata => { + const {crs, extent, ...rest} = layerMetadata.BoundingBox[0]; + + if (!url.includes('LAYERS')) { + const layers = this._unpackLayers(layerMetadata) + .filter(layer => !!layer.Name) + .map(layer => layer.Name) + .join(','); + + const hasParams = url.includes("?"); + + url = hasParams ? `${url}&LAYERS=${layers}` : `${url.split('?')[0]}?LAYERS=${layers}`; + } + + if (!url.includes('STYLES=')) { + url = `${url}&STYLES=` + } + + const source = getImageWmsFrom({url}); + source.setImageLoadFunction(getImageWmsLoader(observer$)); + source + .getImage(extent, 1000,0.01, new Projection({code: crs})) + .load(); + }); }); } @@ -203,4 +224,23 @@ export class OverlayListComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this._overlayService.resetUI(); } + + private _unpackLayers(layer: any, depth = 0) { + const MAX_DEPTH = 10; + + if (!layer) { + return []; + } + + if (!layer.Layer || depth > MAX_DEPTH) { + return [layer]; + } + + return [ + layer, + ...layer.Layer + .map(layer => this._unpackLayers(layer, depth + 1)) + .reduce((finalLayers, layers) => finalLayers = [...finalLayers, ...layers]) + ]; + } } diff --git a/s4e-web/src/app/components/overlay-list/wms-url.utils.ts b/s4e-web/src/app/components/overlay-list/wms-url.utils.ts index ee2358b69..6c05a7f8d 100644 --- a/s4e-web/src/app/components/overlay-list/wms-url.utils.ts +++ b/s4e-web/src/app/components/overlay-list/wms-url.utils.ts @@ -4,9 +4,9 @@ import {Overlay} from '../../views/map-view/state/overlay/overlay.model'; const VERSION_EXPRESSION = /version=\d+\.\d+\.\d+(&?)/i; const REQUEST_EXPRESSION = /request=[a-zA-Z]*(&?)/i; -const LAYERS_EXPRESSION = /layers=[a-zA-Z._0-9,:]*(&?)/i; -const STYLES_EXPRESSION = /styles=[a-zA-Z._0-9,:]*(&?)/i; -const BBOX_EXPRESSION = /bbox=[.0-9]*,[.0-9]*,[.0-9]*,[.0-9]*(&?)/i; +const LAYERS_EXPRESSION = /layers=[a-zA-Z._0-9,:-]*(&?)/i; +const STYLES_EXPRESSION = /styles=[a-zA-Z._0-9,:-]*(&?)/i; +const BBOX_EXPRESSION = /bbox=(-?)[.0-9]*,(-?)[.0-9]*,(-?)[.0-9]*,(-?)[.0-9]*(&?)/i; const SRS_OR_CRS_EXPRESSION = /(srs|crs)=[a-zA-Z._0-9]*:[a-zA-Z._0-9]*(&?)/i; const WIDTH_EXPRESSION = /width=[0-9]*(&?)/i; const HEIGHT_EXPRESSION = /height=[0-9]*(&?)/i; @@ -15,10 +15,12 @@ const WMS_SERVICE_EXPRESSION = /service=wms(&?)/i; const TRANSPARENT_EXPRESSION = /transparent=(false|true)(&?)/i; /** - * List of params to be removed from url - * due to autogenerated duplicates by openlayers lib -*/ -const PARAMS_TO_BE_REMOVED = [ + * Params to be extracted due to correct usage + * of openlayers image wms + */ +const PARAMS_TO_BE_EXTRACTED = [ + LAYERS_EXPRESSION, + STYLES_EXPRESSION, VERSION_EXPRESSION, REQUEST_EXPRESSION, WMS_SERVICE_EXPRESSION, @@ -30,15 +32,6 @@ const PARAMS_TO_BE_REMOVED = [ FORMAT_EXPRESSION ]; -/** - * Params to be extracted due to correct usage - * of openlayers image wms - */ -const PARAMS_TO_BE_EXTRACTED = [ - LAYERS_EXPRESSION, - STYLES_EXPRESSION -]; - export const WMS_URL_VALIDATORS = [ /* OPTIONAL */ @@ -55,19 +48,6 @@ export const WMS_URL_VALIDATORS = [ formatValidator ]; -export function removeAutoGeneratedParams(url: string): string | null { - if (typeof url !== "string") { - return null; - } - - let preparedUrl = url; - PARAMS_TO_BE_REMOVED - .forEach(regex => preparedUrl = preparedUrl.replace(regex, '')); - preparedUrl = preparedUrl.replace(/(&?)$/, ''); - - return preparedUrl; -} - export function getBaseUrlAndParamsFrom(overlay: Partial): {url: string, [param: string]: any} { const urlBaseAndParams = overlay.url.split('?'); if (urlBaseAndParams.length === 1) { @@ -160,7 +140,10 @@ function _extractParamsMapFrom(partialUrl: string) { PARAMS_TO_BE_EXTRACTED .map(regex => partialUrl.match(regex)) .filter(match => !!match && match.length > 0) - .map(match => match[0].replace('&', '').split('=')) + .map(match => match[0] + .replace('&', '') + .split('=') + ) .forEach(([key, value]) => extractedParams[key] = value); return extractedParams; diff --git a/s4e-web/src/app/state/session/session.service.ts b/s4e-web/src/app/state/session/session.service.ts index af67d4c2a..f71e06c0d 100644 --- a/s4e-web/src/app/state/session/session.service.ts +++ b/s4e-web/src/app/state/session/session.service.ts @@ -73,8 +73,7 @@ export class SessionService { .pipe( handleHttpRequest$(this._store), switchMap(data => this._profileLoaderService.loadProfile$()), - tap(() => this._store.update({email: request.email})), - finalize(() => this._navigateToApplication()) + tap(() => this._store.update({email: request.email})) ); } @@ -83,10 +82,9 @@ export class SessionService { this._http.post(`${environment.apiPrefixV1}/logout`, {}) .pipe( tap(() => resetStores()), - tap(() => this._store.reset()), - finalize(() => this._router.navigate(['/login'])) + tap(() => this._store.reset()) ) - .subscribe(); + .subscribe(() => this._router.navigate(['/login'])); } removeAccount$(email: string, password: string) { @@ -107,7 +105,7 @@ export class SessionService { this._store.setError(null); } - private _navigateToApplication() { + goToLastUrl() { ( !!this._backLink && this._backLink !== '' ? this._router.navigateByUrl(this._backLink) diff --git a/s4e-web/src/app/views/login/login.component.ts b/s4e-web/src/app/views/login/login.component.ts index bb99a0021..e270dc035 100644 --- a/s4e-web/src/app/views/login/login.component.ts +++ b/s4e-web/src/app/views/login/login.component.ts @@ -54,11 +54,12 @@ export class LoginComponent extends GenericFormComponent this._activatedRoute.queryParamMap), - filter(params => params.has(TOKEN_QUERY_PARAMETER)), - map(params => params.get(TOKEN_QUERY_PARAMETER)), - tap(token => this._invitationService.confirm(token)) + tap(params => params.has(TOKEN_QUERY_PARAMETER) + ? this._invitationService.confirm(params.get(TOKEN_QUERY_PARAMETER)) + : null + ) ) - .subscribe(); + .subscribe(() => this._sessionService.goToLastUrl()); } protected _loadBackLink() { diff --git a/s4e-web/src/app/views/map-view/map/map.component.ts b/s4e-web/src/app/views/map-view/map/map.component.ts index d40108a84..e07aeffd4 100644 --- a/s4e-web/src/app/views/map-view/map/map.component.ts +++ b/s4e-web/src/app/views/map-view/map/map.component.ts @@ -348,6 +348,8 @@ export class MapComponent implements OnInit, OnDestroy { } private _clearPolygonDrawing() { + this._sentinelSearchService.setFootprint(null); + if (!!this._polygonDrawing.layer) { this.map.removeLayer(this._polygonDrawing.layer); this._polygonDrawing.layer.getSource() diff --git a/s4e-web/src/app/views/map-view/state/overlay/image-wms.utils.ts b/s4e-web/src/app/views/map-view/state/overlay/image-wms.utils.ts index 56c83634e..7d38657e8 100644 --- a/s4e-web/src/app/views/map-view/state/overlay/image-wms.utils.ts +++ b/s4e-web/src/app/views/map-view/state/overlay/image-wms.utils.ts @@ -3,21 +3,26 @@ import { getImageXhr } from 'src/app/views/settings/manage-institutions/institut import ImageWrapper from 'ol/Image'; -export function getImageWmsLoader( - error$: Subscriber, - urlParser: (src: string) => string = (src: string) => src -) { +export function getImageWmsLoader(error$: Subscriber) { return (image: ImageWrapper, src: string) => { handleBrowserEncodingError(image, error$); - return getHandledXhr(getImageXhr(urlParser(src)), error$); + + const xhr = getHandledXhr(getImageXhr(src), error$); + xhr.onloadend = () => { + error$.next(src); + error$.complete(); + }; + + return xhr; }; } function getHandledXhr(xhr: XMLHttpRequest, error$: Subscriber) { - xhr.onload = () => !handleHttpError(xhr, error$) - || handleHttpResponseImageTypeError(xhr, error$); - xhr.onloadend = () => error$.next(null); + xhr.onload = () => { + handleHttpError(xhr, error$); + handleHttpResponseImageTypeError(xhr, error$); + } xhr.onerror = () => handleHttpCorsAndOtherErrors(xhr, error$); xhr.send(); @@ -46,17 +51,16 @@ function handleBrowserEncodingError(image: ImageWrapper, error$: Subscriber): boolean { +function handleHttpError(xhr: XMLHttpRequest, error$: Subscriber) { const errorMessage = getErrorMessageBy(xhr.status); if (!!errorMessage) { error$.error(errorMessage + ', sprawdź poprawność URL'); } - - return !!errorMessage; } function handleHttpResponseImageTypeError(xhr: XMLHttpRequest, error$: Subscriber) { const isImage = xhr.getResponseHeader('content-type').indexOf('image') > -1; + const isInvalidImage = xhr.status === 200 && !isImage; if (isInvalidImage) { error$.error(`Odpowiedź serwera nie jest zdjęciem, sprawdź poprawność URL!`); diff --git a/s4e-web/src/app/views/map-view/state/overlay/overlay.model.ts b/s4e-web/src/app/views/map-view/state/overlay/overlay.model.ts index 035e0fd3f..40a27d2ca 100644 --- a/s4e-web/src/app/views/map-view/state/overlay/overlay.model.ts +++ b/s4e-web/src/app/views/map-view/state/overlay/overlay.model.ts @@ -1,9 +1,6 @@ import {Layer, Tile} from 'ol/layer'; import {IUILayer} from '../common.model'; import {EntityState} from '@datorama/akita'; -import { getBaseUrlAndParamsFrom } from '../../view-manager/overlay-list-modal/wms-url.utils'; -import { NgxUiLoaderService } from 'ngx-ui-loader'; -import {TileLoader} from '../utils/layers-loader.util'; export const GLOBAL_OWNER_TYPE = 'GLOBAL'; export const INSTITUTIONAL_OWNER_TYPE = 'INSTITUTIONAL'; diff --git a/s4e-web/src/app/views/map-view/state/overlay/overlay.utils.ts b/s4e-web/src/app/views/map-view/state/overlay/overlay.utils.ts index 42a6080e5..d40dc587a 100644 --- a/s4e-web/src/app/views/map-view/state/overlay/overlay.utils.ts +++ b/s4e-web/src/app/views/map-view/state/overlay/overlay.utils.ts @@ -1,10 +1,10 @@ import { Overlay, UIOverlay } from './overlay.model'; -import { getBaseUrlAndParamsFrom } from '../../view-manager/overlay-list-modal/wms-url.utils'; import {ImageWMS, TileWMS} from 'ol/source'; import {InjectorModule} from '../../../../common/injector.module'; import {TileLoader} from '../utils/layers-loader.util'; import {NgxUiLoaderService} from 'ngx-ui-loader'; import {Tile} from 'ol/layer'; +import {getBaseUrlAndParamsFrom} from '../../../../components/overlay-list/wms-url.utils'; export function convertToUIOverlay( overlay: Overlay, diff --git a/s4e-web/src/app/views/map-view/state/product/product.service.ts b/s4e-web/src/app/views/map-view/state/product/product.service.ts index b2a827242..ef41b2d83 100644 --- a/s4e-web/src/app/views/map-view/state/product/product.service.ts +++ b/s4e-web/src/app/views/map-view/state/product/product.service.ts @@ -79,10 +79,6 @@ export class ProductService { )), map(productsLicenses => productsLicenses .filter(productLicenses => !!productLicenses && productLicenses.length > 0) - .map(licenses => { - console.log(licenses) - return licenses; - }) .map(productLicenses => ({ ...productLicenses[0], institutionsSlugs: productLicenses.map(license => license.institutionSlug) diff --git a/s4e-web/src/app/views/map-view/view-manager/overlay-list-modal/wms-url.utils.ts b/s4e-web/src/app/views/map-view/view-manager/overlay-list-modal/wms-url.utils.ts deleted file mode 100644 index 1b44cba7c..000000000 --- a/s4e-web/src/app/views/map-view/view-manager/overlay-list-modal/wms-url.utils.ts +++ /dev/null @@ -1,226 +0,0 @@ -import { Overlay } from './../../state/overlay/overlay.model'; -import { AbstractControl } from '@angular/forms'; -import { over } from 'cypress/types/lodash'; - -/* BASE URL */ -const HTTP_PREFIX = '(http(s?):\\/\\/)?'; -const LOCALHOST = '(localhost)'; -const DOMAIN_NAME = '((www\\.)?(([a-zA-Z0-9\\-\\_]*\\.)+)([a-zA-Z0-9\\-\\_]{2,3}))'; -const LOCAL_SUB_PAGES = '((\\/[a-zA-Z0-9]*)+)'; -const IP = '(\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b)'; -const BASE_URL = HTTP_PREFIX + '(' + LOCALHOST + '|' + DOMAIN_NAME + '|' + IP + '|' + LOCAL_SUB_PAGES + ')'; - -/* ADVANCED URL PARTS (OPTIONAL) */ -const PORT = '(:[0-9]{1,5})?'; -const SUB_PAGES = '((\\/[a-zA-Z0-9\\.]*)+)?'; -const PARAMETERS = '((\\?)([a-zA-Z0-9\\-\\_\\.]*=[a-zA-Z\\-\\_\\.0-9]*(&?))+)?'; - -/* FULL URL ASSEMBLY */ -const URL_EXPRESSION = new RegExp(BASE_URL + PORT + SUB_PAGES + PARAMETERS, 'i'); - -const VERSION_EXPRESSION = /version=\d+\.\d+\.\d+(&?)/i; -const REQUEST_EXPRESSION = /request=[a-zA-Z]*(&?)/i; -const LAYERS_EXPRESSION = /layers=[a-zA-Z._0-9,:]*(&?)/i; -const STYLES_EXPRESSION = /styles=[a-zA-Z._0-9,:]*(&?)/i; -const BBOX_EXPRESSION = /bbox=[.0-9]*,[.0-9]*,[.0-9]*,[.0-9]*(&?)/i; -const SRS_OR_CRS_EXPRESSION = /(srs|crs)=[a-zA-Z._0-9]*:[a-zA-Z._0-9]*(&?)/i; -const WIDTH_EXPRESSION = /width=[0-9]*(&?)/i; -const HEIGHT_EXPRESSION = /height=[0-9]*(&?)/i; -const FORMAT_EXPRESSION = /format=image\/(vnd.jpeg-png|vnd.jpeg-png8|png|gif|tiff|jpg)(&?)/i; -const WMS_SERVICE_EXPRESSION = /service=wms(&?)/i; -const TRANSPARENT_EXPRESSION = /transparent=(false|true)(&?)/i; - -/** - * List of params to be removed from url - * due to autogenerated duplicates by openlayers lib -*/ -const PARAMS_TO_BE_REMOVED = [ - VERSION_EXPRESSION, - REQUEST_EXPRESSION, - WMS_SERVICE_EXPRESSION, - TRANSPARENT_EXPRESSION, - BBOX_EXPRESSION, - SRS_OR_CRS_EXPRESSION, - WIDTH_EXPRESSION, - HEIGHT_EXPRESSION, - FORMAT_EXPRESSION -]; - -/** - * Params to be extracted due to correct usage - * of openlayers image wms - */ -const PARAMS_TO_BE_EXTRACTED = [ - LAYERS_EXPRESSION, - STYLES_EXPRESSION -]; - -/** - * List of params documented in source - * https://mapserver.org/ogc/wms_server.html#test-with-a-getmap-request - */ -const RECOGNIZED_PARAMS_EXPRESSIONS = [ - ...PARAMS_TO_BE_REMOVED, - ...PARAMS_TO_BE_EXTRACTED -]; - - -export const WMS_URL_VALIDATORS = [ - urlValidator, - - /* OPTIONAL */ - wmsServiceValidator, - requestValidator, - layersValidator, - stylesValidator, - transparentValidator, - versionValidator, - srsOrCrsValidator, - bboxValidator, - widthValidator, - heightValidator, - formatValidator -]; - -export function removeAutoGeneratedParams(url: string): string | null { - if (typeof url !== "string") { - return null; - } - - let preparedUrl = url; - PARAMS_TO_BE_REMOVED - .forEach(regex => preparedUrl = preparedUrl.replace(regex, '')); - preparedUrl = preparedUrl.replace(/(&?)$/, ''); - - return preparedUrl; -} - -export function getBaseUrlAndParamsFrom(overlay: Partial): {url: string, [param: string]: any} { - const url = overlay.url; - if (!_hasMatch(url, URL_EXPRESSION)) { - return {url: overlay.url}; - } - - const urlBaseAndParams = url.split('?'); - if (urlBaseAndParams.length === 1) { - return {url: urlBaseAndParams[0]}; - } - - const urlBase = urlBaseAndParams[0]; - const urlParams = urlBaseAndParams[1]; - - return {url: urlBase, ..._extractParamsMapFrom(urlParams)}; -} - - -interface IValidatorOutput { - [key: string]: boolean; -} - - -function urlValidator(control: AbstractControl): IValidatorOutput | null { - return _hasMatch(control.value, URL_EXPRESSION) ? null : { url: true }; -} - -/* OPTIONAL */ -const VERSION_PARAM = 'version='; -function versionValidator(control: AbstractControl): IValidatorOutput | null { - return _hasOptionalParam(control.value, VERSION_EXPRESSION, VERSION_PARAM, { version: true }); -} - -/* OPTIONAL */ -const SRS_PARAM = 'srs='; -const CRS_PARAM = 'crs='; -function srsOrCrsValidator(control: AbstractControl): IValidatorOutput | null { - return _hasOptionalParam(control.value, SRS_OR_CRS_EXPRESSION, CRS_PARAM, { srsOrCrs: true }) - || _hasOptionalParam(control.value, SRS_OR_CRS_EXPRESSION, SRS_PARAM, { srsOrCrs: true }); -} - -/* OPTIONAL */ -const BBOX_PARAM = 'bbox='; -function bboxValidator(control: AbstractControl): IValidatorOutput | null { - return _hasOptionalParam(control.value, BBOX_EXPRESSION, BBOX_PARAM, { bbox: true }); -} - -/* OPTIONAL */ -const WIDTH_PARAM = 'width='; -function widthValidator(control: AbstractControl): IValidatorOutput | null { - return _hasOptionalParam(control.value, WIDTH_EXPRESSION, WIDTH_PARAM, { width: true }); -} - -/* OPTIONAL */ -const HEIGHT_PARAM = 'height='; -function heightValidator(control: AbstractControl): IValidatorOutput | null { - return _hasOptionalParam(control.value, HEIGHT_EXPRESSION, HEIGHT_PARAM, { height: true }); -} - -/* OPTIONAL */ -const FORMAT_PARAM = 'format='; -function formatValidator(control: AbstractControl): IValidatorOutput | null { - return _hasOptionalParam(control.value, FORMAT_EXPRESSION, FORMAT_PARAM, { format: true }); -} - -/* OPTIONAL */ -const SERVICE_PARAM = 'service='; -function wmsServiceValidator(control: AbstractControl): IValidatorOutput | null { - return _hasOptionalParam(control.value, WMS_SERVICE_EXPRESSION, SERVICE_PARAM, { wmsService: true }); -} - -/* OPTIONAL */ -const REQUEST_PARAM = 'request='; -function requestValidator(control: AbstractControl): IValidatorOutput | null { - return _hasOptionalParam(control.value, REQUEST_EXPRESSION, REQUEST_PARAM, { request: true }); -} - -/* OPTIONAL */ -const TRANSPARENT_PARAM = 'transparent='; -function transparentValidator(control: AbstractControl): IValidatorOutput | null { - return _hasOptionalParam(control.value, TRANSPARENT_EXPRESSION, TRANSPARENT_PARAM, { transparent: true }); -} - -/* OPTIONAL */ -const LAYERS_PARAM = 'layers='; -function layersValidator(control: AbstractControl): IValidatorOutput | null { - return _hasOptionalParam(control.value, LAYERS_EXPRESSION, LAYERS_PARAM, { layers: true }); -} - -/* OPTIONAL */ -const STYLES_PARAM = 'styles='; -function stylesValidator(control: AbstractControl): IValidatorOutput | null { - return _hasOptionalParam(control.value, STYLES_EXPRESSION, STYLES_PARAM, { styles: true }); -} - - -function _extractParamsMapFrom(partialUrl: string) { - const extractedParams: {[param: string]: any} = {}; - PARAMS_TO_BE_EXTRACTED - .map(regex => partialUrl.match(regex)) - .filter(match => !!match && match.length > 0) - .map(match => match[0].replace('&', '').split('=')) - .forEach(([key, value]) => extractedParams[key] = value); - - return extractedParams; -} - -function _hasOptionalParam( - url: string, - paramRegex: RegExp, - paramName: string, - output: IValidatorOutput -): IValidatorOutput | null { - const hasOptionalParam = _hasMatch(url, URL_EXPRESSION) - && ( - !_exist(url, paramName) - || _hasMatch(url, paramRegex) - ); - return hasOptionalParam ? null : output; -} - -function _hasMatch(url: string, regex: RegExp) { - return (typeof url === "string") && regex.test(url); -} - -function _exist(url: string, paramName: string) { - return (typeof url === "string") - && url.toLowerCase().indexOf(paramName.toLowerCase()) > -1; -} diff --git a/s4e-web/src/app/views/settings/dashboards.routes.ts b/s4e-web/src/app/views/settings/dashboards.routes.ts index 3283a1bc8..109eb36bf 100644 --- a/s4e-web/src/app/views/settings/dashboards.routes.ts +++ b/s4e-web/src/app/views/settings/dashboards.routes.ts @@ -1,6 +1,5 @@ import {UrlSegment} from '@angular/router'; import {InjectorModule} from 'src/app/common/injector.module'; -import {SessionQuery} from '../../state/session/session.query'; import {InstitutionQuery} from './state/institution/institution.query'; export function multipleInstitutionAdminDashboardMatcher(url: UrlSegment[]) { diff --git a/s4e-web/src/app/views/settings/intitution-profile/institution-profile.component.html b/s4e-web/src/app/views/settings/intitution-profile/institution-profile.component.html index d4b65565c..3b537aa85 100644 --- a/s4e-web/src/app/views/settings/intitution-profile/institution-profile.component.html +++ b/s4e-web/src/app/views/settings/intitution-profile/institution-profile.component.html @@ -1,4 +1,4 @@ -