@datagouv/components-next 1.0.2-dev.180 → 1.0.2-dev.182
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/dist/{Datafair.client-CaxMyNif.js → Datafair.client-BaBAcdjV.js} +1 -1
- package/dist/{ImagePreview.client-CPmCyisP.js → ImagePreview.client-DHcSrrvJ.js} +1 -1
- package/dist/{JsonPreview.client-CKOU-LBs.js → JsonPreview.client-Cp92vNCM.js} +2 -2
- package/dist/{MapContainer.client-IGYJWP5m.js → MapContainer.client-PPJBuRJc.js} +2 -2
- package/dist/{PdfPreview.client-csUebU3A.js → PdfPreview.client-CAcH3CtF.js} +2 -2
- package/dist/{Pmtiles.client-CLq7UHBg.js → Pmtiles.client-CcpkHIej.js} +1 -1
- package/dist/{PreviewWrapper.vue_vue_type_script_setup_true_lang-DeoHZXW1.js → PreviewWrapper.vue_vue_type_script_setup_true_lang-HZuzZUIF.js} +1 -1
- package/dist/{XmlPreview.client-C1toAbQ0.js → XmlPreview.client-Dtr7bH64.js} +3 -3
- package/dist/components-next.js +67 -66
- package/dist/{index-CaUlhl3k.js → index-nWgd09zR.js} +1 -1
- package/dist/{leaflet-src-ByYGrf8H.js → leaflet-src-BEZIcFC0.js} +1 -1
- package/dist/{main-DD4ieUAo.js → main-8mcMUsT7.js} +4807 -4793
- package/dist/{vue3-xml-viewer.common-B1KPK4L4.js → vue3-xml-viewer.common-B8-AwxIJ.js} +1 -1
- package/package.json +1 -1
- package/src/composables/useResourceCapabilities.ts +2 -20
- package/src/functions/helpers.ts +15 -0
- package/src/functions/resourceCapabilities.ts +34 -0
package/package.json
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { computed, type MaybeRefOrGetter, toValue } from 'vue'
|
|
2
2
|
import { useComponentsConfig } from '../config'
|
|
3
3
|
import { isOrganizationCertified } from '../functions/organizations'
|
|
4
|
-
import { getWfsExportFormats } from '../functions/resourceCapabilities'
|
|
4
|
+
import { getGeneratedFormats, getWfsExportFormats } from '../functions/resourceCapabilities'
|
|
5
5
|
import { detectOgcService, getParsingErrorMessage, getParsingErrorStep, isImagePreviewFormat } from '../functions/resources'
|
|
6
6
|
import type { Dataset, DatasetV2 } from '../types/datasets'
|
|
7
7
|
import type { Resource, WfsMetadata } from '../types/resources'
|
|
8
8
|
import { useHasTabularData } from './useHasTabularData'
|
|
9
9
|
import { useTranslation } from './useTranslation'
|
|
10
10
|
|
|
11
|
-
const GENERATED_FORMATS = ['parquet', 'pmtiles', 'geojson']
|
|
12
11
|
const URL_FORMATS = ['url', 'doi', 'www:link', 'www:link-1.0-http--link', 'www:link-1.0-http--partners', 'www:link-1.0-http--related', 'www:link-1.0-http--samples']
|
|
13
12
|
|
|
14
13
|
// Which preview the data tab shows. Single source of truth: the viewer renders it and
|
|
@@ -54,24 +53,7 @@ export function useResourceCapabilities(
|
|
|
54
53
|
const hasPmtilesError = computed(() => getParsingErrorStep(toValue(resource)) === 'pmtiles_export')
|
|
55
54
|
const pmtilesError = computed(() => hasPmtilesError.value ? getParsingErrorMessage(toValue(resource)) : null)
|
|
56
55
|
|
|
57
|
-
const generatedFormats = computed(() =>
|
|
58
|
-
const r = toValue(resource)
|
|
59
|
-
const formats = GENERATED_FORMATS
|
|
60
|
-
.filter(format => `analysis:parsing:${format}_url` in r.extras)
|
|
61
|
-
.map(format => ({
|
|
62
|
-
url: r.extras[`analysis:parsing:${format}_url`] as string,
|
|
63
|
-
size: r.extras[`analysis:parsing:${format}_size`] as number | undefined,
|
|
64
|
-
format,
|
|
65
|
-
}))
|
|
66
|
-
if ('analysis:parsing:parsing_table' in r.extras) {
|
|
67
|
-
formats.push({
|
|
68
|
-
url: `${config.tabularApiUrl}/api/resources/${r.id}/data/json/`,
|
|
69
|
-
size: undefined,
|
|
70
|
-
format: 'json',
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
return formats
|
|
74
|
-
})
|
|
56
|
+
const generatedFormats = computed(() => getGeneratedFormats(toValue(resource), config.tabularApiUrl))
|
|
75
57
|
|
|
76
58
|
const wfsFormats = computed(() => {
|
|
77
59
|
return getWfsExportFormats(toValue(resource))
|
package/src/functions/helpers.ts
CHANGED
|
@@ -38,6 +38,21 @@ export const summarize = (val: number, fractionDigits = 0) => {
|
|
|
38
38
|
return `${toFixedIfNotZero(val)}Y`
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// Guards any value that ends up in an `href`: only absolute http(s) URLs are allowed.
|
|
42
|
+
// Some of these URLs come from resource extras, which are freely writable by the
|
|
43
|
+
// resource owner, so an unguarded value could carry a `javascript:` or `data:` scheme
|
|
44
|
+
// and execute in the platform origin when a visitor clicks the link.
|
|
45
|
+
export const isSafeHttpUrl = (value: unknown): value is string => {
|
|
46
|
+
if (typeof value !== 'string') return false
|
|
47
|
+
try {
|
|
48
|
+
const { protocol } = new URL(value)
|
|
49
|
+
return protocol === 'http:' || protocol === 'https:'
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
41
56
|
export const escapeCsvValue = (value: string | number | null | undefined): string => {
|
|
42
57
|
if (value === null || value === undefined || value === '') {
|
|
43
58
|
return ''
|
|
@@ -1,4 +1,35 @@
|
|
|
1
1
|
import type { Resource, WfsMetadata, OgcLayerInfo } from '../types/resources'
|
|
2
|
+
import { isSafeHttpUrl } from './helpers'
|
|
3
|
+
|
|
4
|
+
const GENERATED_FORMATS = ['parquet', 'pmtiles', 'geojson']
|
|
5
|
+
|
|
6
|
+
export type GeneratedFormat = {
|
|
7
|
+
url: string
|
|
8
|
+
size: number | undefined
|
|
9
|
+
format: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function getGeneratedFormats(resource: Pick<Resource, 'extras' | 'id'>, tabularApiUrl: string | undefined): GeneratedFormat[] {
|
|
13
|
+
const formats: GeneratedFormat[] = GENERATED_FORMATS.flatMap((format) => {
|
|
14
|
+
const url = resource.extras[`analysis:parsing:${format}_url`]
|
|
15
|
+
// The URL is read verbatim from owner-writable extras: drop any entry that is not
|
|
16
|
+
// a plain http(s) URL so a `javascript:`/`data:` value never reaches an href.
|
|
17
|
+
if (!isSafeHttpUrl(url)) return []
|
|
18
|
+
return [{
|
|
19
|
+
url,
|
|
20
|
+
size: resource.extras[`analysis:parsing:${format}_size`] as number | undefined,
|
|
21
|
+
format,
|
|
22
|
+
}]
|
|
23
|
+
})
|
|
24
|
+
if ('analysis:parsing:parsing_table' in resource.extras) {
|
|
25
|
+
formats.push({
|
|
26
|
+
url: `${tabularApiUrl}/api/resources/${resource.id}/data/json/`,
|
|
27
|
+
size: undefined,
|
|
28
|
+
format: 'json',
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
return formats
|
|
32
|
+
}
|
|
2
33
|
|
|
3
34
|
const WFS_EXPORT_FORMATS = [
|
|
4
35
|
{
|
|
@@ -46,6 +77,9 @@ export function getWfsExportFormats(resource: Pick<Resource, 'extras' | 'url'>)
|
|
|
46
77
|
const outputFormats = wfsMetadata.output_formats.map((format: string) => format.toLowerCase())
|
|
47
78
|
const layer = wfsMetadata.detected_layer
|
|
48
79
|
if (!layer) return []
|
|
80
|
+
// Every download URL below is built on top of `resource.url`, so its scheme is the one
|
|
81
|
+
// of `resource.url`: guard it once here rather than on each of the built URLs.
|
|
82
|
+
if (!isSafeHttpUrl(resource.url)) return []
|
|
49
83
|
const formats = WFS_EXPORT_FORMATS.filter(format => outputFormats.includes(format.mimetype.toLowerCase()))
|
|
50
84
|
.map(format => ({
|
|
51
85
|
url: buildWfsDownloadUrl(resource.url, wfsMetadata, format, layer),
|