@eeacms/volto-arcgis-block 0.1.483 → 0.1.484

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. Dates are d
4
4
 
5
5
  Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
6
6
 
7
+ ### [0.1.484](https://github.com/eea/volto-arcgis-block/compare/0.1.483...0.1.484) - 15 September 2026
8
+
9
+ #### :hammer_and_wrench: Others
10
+
11
+ - (lint): lint and prettified [Unai Bolivar - [`13294af`](https://github.com/eea/volto-arcgis-block/commit/13294aff5e09463006cbc6421800c8f71bd4ac49)]
7
12
  ### [0.1.483](https://github.com/eea/volto-arcgis-block/compare/0.1.482...0.1.483) - 15 September 2026
8
13
 
9
14
  ### [0.1.482](https://github.com/eea/volto-arcgis-block/compare/0.1.481...0.1.482) - 15 September 2026
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eeacms/volto-arcgis-block",
3
- "version": "0.1.483",
3
+ "version": "0.1.484",
4
4
  "description": "volto-arcgis-block: Volto add-on",
5
5
  "main": "src/index.js",
6
6
  "author": "European Environment Agency: CodeSyntax",
@@ -7,6 +7,7 @@ import useCartState from '@eeacms/volto-clms-utils/cart/useCartState';
7
7
  import { Modal, Popup } from 'semantic-ui-react';
8
8
  import AreaWidget from './AreaWidget';
9
9
  import TimesliderWidget from './TimesliderWidget';
10
+ import { resolveServiceUrl } from './serviceUrlResolver';
10
11
 
11
12
  export const USER_SERVICES_KEY = 'user_services_session';
12
13
 
@@ -2661,28 +2662,6 @@ class MenuWidget extends React.Component {
2661
2662
  );
2662
2663
  }
2663
2664
 
2664
- stripProtocol(url) {
2665
- return (url || '').replace(/^https?:\/\//i, '');
2666
- }
2667
-
2668
- getProxyBase = () => {
2669
- const origin = window?.location?.origin || '';
2670
- return origin ? `${origin}/ogcproxy/` : '/ogcproxy/';
2671
- // return 'https://clmsdemo.devel6cph.eea.europa.eu/ogcproxy/';
2672
- // return 'https://land.copernicus.eu/ogcproxy/';
2673
- };
2674
-
2675
- buildProxiedUrl(url) {
2676
- if (!url) return url;
2677
- const hasProxy = /\/ogcproxy\//i.test(url);
2678
- if (hasProxy) {
2679
- const strippedUrl = this.stripProtocol(url);
2680
- const proxyPath = strippedUrl.split(/\/ogcproxy\//i)[1] || '';
2681
- return this.getProxyBase() + proxyPath.replace(/^\/+/, '');
2682
- }
2683
- return this.getProxyBase() + this.stripProtocol(url);
2684
- }
2685
-
2686
2665
  buildCdseLegendUrl(serviceUrl, layerTitle) {
2687
2666
  if (!serviceUrl || !layerTitle) return null;
2688
2667
  const collectionMatch =
@@ -3657,9 +3636,11 @@ class MenuWidget extends React.Component {
3657
3636
 
3658
3637
  async handleNewMapServiceLayer(viewService, serviceType, serviceSelection) {
3659
3638
  let resourceLayers = [];
3660
- const proxiedUrl = this.buildProxiedUrl(viewService);
3639
+ const resolvedServiceUrl = resolveServiceUrl(viewService, {
3640
+ fromUploadWidget: true,
3641
+ });
3661
3642
  try {
3662
- const rawUrl = (proxiedUrl || '').trim();
3643
+ const rawUrl = (resolvedServiceUrl || '').trim();
3663
3644
  const baseUrl = rawUrl.split('?')[0];
3664
3645
  const resolveCapabilitiesText = (xmlData) => {
3665
3646
  if (!xmlData) {
@@ -4034,7 +4015,7 @@ class MenuWidget extends React.Component {
4034
4015
  }
4035
4016
  }
4036
4017
  if (serviceType === 'WMS' || serviceType === 'WMTS') {
4037
- const forced = (proxiedUrl || '').trim();
4018
+ const forced = (resolvedServiceUrl || '').trim();
4038
4019
  if (forced) {
4039
4020
  if (typeof resourceLayer.url === 'string') {
4040
4021
  resourceLayer.url = forced;
@@ -6195,7 +6176,9 @@ class MenuWidget extends React.Component {
6195
6176
 
6196
6177
  getCapabilities = async (url, serviceType, options = {}) => {
6197
6178
  const useProxy = !!options?.useProxy;
6198
- const targetUrl = useProxy ? this.buildProxiedUrl(url) : url;
6179
+ const targetUrl = useProxy
6180
+ ? resolveServiceUrl(url, { fromUploadWidget: true })
6181
+ : url;
6199
6182
  const evaluateXmlResponse = (responseData) => {
6200
6183
  const parser = new DOMParser();
6201
6184
  const xmlDoc = parser.parseFromString(responseData, 'text/xml');
@@ -2,6 +2,7 @@ import React, { createRef } from 'react';
2
2
  import { loadModules } from 'esri-loader';
3
3
  // import { FontAwesomeIcon } from '@eeacms/volto-clms-utils/components';
4
4
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
5
+ import { resolveServiceUrl } from './serviceUrlResolver';
5
6
 
6
7
  var WMSLayer, WMTSLayer, WFSLayer, esriRequest;
7
8
  const SHAPEFILE_MAX_SIZE_BYTES = 2097152;
@@ -334,28 +335,6 @@ class UploadWidget extends React.Component {
334
335
  this.clearUploadForm(true);
335
336
  };
336
337
 
337
- stripProtocol = (url) => {
338
- return (url || '').replace(/^https?:\/\//i, '');
339
- };
340
-
341
- getProxyBase = () => {
342
- const origin = window?.location?.origin || '';
343
- return origin ? `${origin}/ogcproxy/` : '/ogcproxy/';
344
- // return 'https://clmsdemo.devel6cph.eea.europa.eu/ogcproxy/';
345
- // return 'https://land.copernicus.eu/ogcproxy/';
346
- };
347
-
348
- buildProxiedUrl = (url) => {
349
- if (!url) return url;
350
- const hasProxy = /\/ogcproxy\//i.test(url);
351
- if (hasProxy) {
352
- const strippedUrl = this.stripProtocol(url);
353
- const proxyPath = strippedUrl.split(/\/ogcproxy\//i)[1] || '';
354
- return this.getProxyBase() + proxyPath.replace(/^\/+/, '');
355
- }
356
- return this.getProxyBase() + this.stripProtocol(url);
357
- };
358
-
359
338
  resolveNodeValue = (nodeValue) => {
360
339
  if (nodeValue === null || nodeValue === undefined) {
361
340
  return null;
@@ -510,8 +489,10 @@ class UploadWidget extends React.Component {
510
489
 
511
490
  getCapabilities = (url, serviceType) => {
512
491
  // Get the coordinates of the click on the view
513
- const proxiedUrl = this.buildProxiedUrl(url);
514
- return esriRequest(proxiedUrl, {
492
+ const resolvedServiceUrl = resolveServiceUrl(url, {
493
+ fromUploadWidget: true,
494
+ });
495
+ return esriRequest(resolvedServiceUrl, {
515
496
  responseType: 'text',
516
497
  query: {
517
498
  request: 'GetCapabilities',
@@ -617,15 +598,21 @@ class UploadWidget extends React.Component {
617
598
  trimmedServiceUrl,
618
599
  selectedServiceType,
619
600
  );
620
- const proxiedUrl = this.buildProxiedUrl(normalizedUrl);
601
+ const resolvedServiceUrl = resolveServiceUrl(normalizedUrl, {
602
+ fromUploadWidget: true,
603
+ });
621
604
  if (selectedServiceType === 'WMS') {
622
- this.uploadUrlServiceHandler(proxiedUrl, 'WMS');
605
+ this.uploadUrlServiceHandler(resolvedServiceUrl, 'WMS');
623
606
  this.setState({ serviceUrl: '' });
624
607
  } else if (selectedServiceType === 'WMTS') {
625
- this.uploadUrlServiceHandler(proxiedUrl, 'WMTS');
608
+ this.uploadUrlServiceHandler(resolvedServiceUrl, 'WMTS');
626
609
  this.setState({ serviceUrl: '' });
627
610
  } else if (selectedServiceType === 'WFS') {
628
- this.uploadUrlServiceHandler(proxiedUrl, 'WFS', selectedFeatures);
611
+ this.uploadUrlServiceHandler(
612
+ resolvedServiceUrl,
613
+ 'WFS',
614
+ selectedFeatures,
615
+ );
629
616
  this.setState({ wfsFeatures: {}, serviceUrl: '' });
630
617
  this.setState({ selectedFeatures: {} });
631
618
  } else {
@@ -0,0 +1,131 @@
1
+ const PROXY_PATH_PATTERN = /\/ogcproxy\//i;
2
+ const INTERNAL_HOST_PATTERNS = [
3
+ /^localhost$/i,
4
+ /^127\.0\.0\.1$/i,
5
+ /^0\.0\.0\.0$/i,
6
+ /(^|\.)land\.copernicus\.eu$/i,
7
+ /(^|\.)clms-staging\.eea\.europa\.eu$/i,
8
+ ];
9
+
10
+ const stripProtocol = (url) => {
11
+ return (url || '').replace(/^https?:\/\//i, '');
12
+ };
13
+
14
+ const getProxyBase = () => {
15
+ const origin = window?.location?.origin || '';
16
+ return origin ? `${origin}/ogcproxy/` : '/ogcproxy/';
17
+ };
18
+
19
+ const resolveAbsoluteUrl = (url) => {
20
+ if (!url || typeof url !== 'string') {
21
+ return null;
22
+ }
23
+ try {
24
+ return new URL(url, window?.location?.origin || undefined);
25
+ } catch (e) {
26
+ return null;
27
+ }
28
+ };
29
+
30
+ const isRelativeUrl = (url) => {
31
+ if (!url || typeof url !== 'string') {
32
+ return false;
33
+ }
34
+ return !/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(url);
35
+ };
36
+
37
+ const isInternalHost = (hostname, currentHostname) => {
38
+ const hostValue = String(hostname || '').toLowerCase();
39
+ const currentHostValue = String(currentHostname || '').toLowerCase();
40
+ if (!hostValue) {
41
+ return false;
42
+ }
43
+ if (
44
+ INTERNAL_HOST_PATTERNS.some((hostPattern) => hostPattern.test(hostValue))
45
+ ) {
46
+ return true;
47
+ }
48
+ if (currentHostValue && hostValue === currentHostValue) {
49
+ return true;
50
+ }
51
+ if (currentHostValue && hostValue.endsWith(`.${currentHostValue}`)) {
52
+ return true;
53
+ }
54
+ if (currentHostValue && currentHostValue.endsWith(`.${hostValue}`)) {
55
+ return true;
56
+ }
57
+ return false;
58
+ };
59
+
60
+ const isInternalAbsoluteUrl = (absoluteUrl) => {
61
+ if (!absoluteUrl) {
62
+ return false;
63
+ }
64
+ const currentOrigin = window?.location?.origin || '';
65
+ const currentHostname = window?.location?.hostname || '';
66
+ if (currentOrigin && absoluteUrl.origin === currentOrigin) {
67
+ return true;
68
+ }
69
+ return isInternalHost(absoluteUrl.hostname, currentHostname);
70
+ };
71
+
72
+ const resolveUnproxiedUrl = (inputUrl) => {
73
+ if (!inputUrl || !PROXY_PATH_PATTERN.test(inputUrl)) {
74
+ return inputUrl;
75
+ }
76
+ const strippedUrl = stripProtocol(inputUrl);
77
+ const proxyPathData = strippedUrl.split(PROXY_PATH_PATTERN)[1] || '';
78
+ const normalizedPathData = proxyPathData.replace(/^\/+/, '');
79
+ if (!normalizedPathData) {
80
+ return inputUrl;
81
+ }
82
+ if (/^https?:\/\//i.test(normalizedPathData)) {
83
+ return normalizedPathData;
84
+ }
85
+ const secureProtocol =
86
+ window?.location?.protocol === 'http:' ? 'http://' : 'https://';
87
+ return secureProtocol + normalizedPathData;
88
+ };
89
+
90
+ export const resolveServiceUrl = (url, options = {}) => {
91
+ if (!url || typeof url !== 'string') {
92
+ return url;
93
+ }
94
+
95
+ if (!options.fromUploadWidget) {
96
+ return url;
97
+ }
98
+
99
+ const trimmedUrl = url.trim();
100
+ if (!trimmedUrl) {
101
+ return trimmedUrl;
102
+ }
103
+
104
+ if (/^(blob:|data:|about:)/i.test(trimmedUrl)) {
105
+ return trimmedUrl;
106
+ }
107
+
108
+ if (isRelativeUrl(trimmedUrl) && !PROXY_PATH_PATTERN.test(trimmedUrl)) {
109
+ return trimmedUrl;
110
+ }
111
+
112
+ const alreadyProxied = PROXY_PATH_PATTERN.test(trimmedUrl);
113
+ const unproxiedUrl = resolveUnproxiedUrl(trimmedUrl);
114
+ const absoluteUrl = resolveAbsoluteUrl(unproxiedUrl);
115
+
116
+ if (!absoluteUrl) {
117
+ return trimmedUrl;
118
+ }
119
+
120
+ if (isInternalAbsoluteUrl(absoluteUrl)) {
121
+ return absoluteUrl.toString();
122
+ }
123
+
124
+ const normalizedExternalUrl = stripProtocol(absoluteUrl.toString());
125
+ if (alreadyProxied) {
126
+ const proxyPathData = normalizedExternalUrl.replace(/^\/+/, '');
127
+ return getProxyBase() + proxyPathData;
128
+ }
129
+
130
+ return getProxyBase() + normalizedExternalUrl;
131
+ };