@datagouv/components-next 1.0.2-dev.201 → 1.0.2-dev.203

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.
@@ -1,4 +1,4 @@
1
- import { g as Ke } from "./main-CVOZp_Nx.js";
1
+ import { g as Ke } from "./main-CICNrWF8.js";
2
2
  import We from "vue";
3
3
  function Fe(I, K) {
4
4
  for (var V = 0; V < K.length; V++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datagouv/components-next",
3
- "version": "1.0.2-dev.201",
3
+ "version": "1.0.2-dev.203",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -67,7 +67,7 @@ import styleVector from '../../../assets/json/vector.json'
67
67
  import PreviewUnavailable from './PreviewUnavailable.vue'
68
68
  import { useTranslation } from '../../composables/useTranslation'
69
69
  import franceSvg from './france.svg?raw'
70
- import { getOwnerName, getOwnerPage } from '../../functions/owned'
70
+ import { getMapAttribution } from '../../functions/owned'
71
71
 
72
72
  const props = defineProps<{ resource: Resource, dataset: Dataset | DatasetV2 }>()
73
73
 
@@ -94,11 +94,7 @@ const lastUpdate = computed(() => {
94
94
 
95
95
  const container = useTemplateRef('containerRef')
96
96
 
97
- const attributions = computed(() => {
98
- if (!props.dataset.organization && !props.dataset.owner)
99
- return ''
100
- return `© <a href="${getOwnerPage(props.dataset)}" target="_blank">${getOwnerName(props.dataset)}</a>`
101
- })
97
+ const attributions = computed(() => getMapAttribution(props.dataset))
102
98
 
103
99
  async function displayMap() {
104
100
  await import('maplibre-gl/dist/maplibre-gl.css')
package/src/config.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { inject, type Component, type InjectionKey } from 'vue'
2
2
  import type { UseFetchFunction } from './functions/api.types'
3
3
  import type { $Fetch, FetchOptions } from 'ofetch'
4
+ import type { Dataset, DatasetV2 } from './types/datasets'
5
+ import type { CommunityResource, Resource } from './types/resources'
4
6
 
5
7
  export type PluginConfig = {
6
8
  name: string // Name of the application (ex: data.gouv.fr)
@@ -45,6 +47,12 @@ export type PluginConfig = {
45
47
  clientOnly?: Component | null
46
48
  searchDebounce?: number
47
49
  forumUrl?: string
50
+ /**
51
+ * Overrides the "Copier le lien" target built by `getResourceExternalUrl`.
52
+ * Must return an absolute URL: it is copied to the clipboard and used as an
53
+ * external, `_blank` link target.
54
+ */
55
+ getResourceExternalUrl?: (dataset: Dataset | DatasetV2 | Omit<Dataset, 'resources' | 'community_resources'>, resource: Resource | CommunityResource) => string
48
56
  }
49
57
 
50
58
  export const configKey = Symbol() as InjectionKey<PluginConfig>
@@ -19,3 +19,24 @@ export function getOwnerPage(owned: Owned): string | null {
19
19
  }
20
20
  return null // Not supposed to exist but it does...
21
21
  }
22
+
23
+ function escapeHtml(value: string): string {
24
+ return value
25
+ .replace(/&/g, '&amp;')
26
+ .replace(/</g, '&lt;')
27
+ .replace(/>/g, '&gt;')
28
+ .replace(/"/g, '&quot;')
29
+ .replace(/'/g, '&#39;')
30
+ }
31
+
32
+ export function getMapAttribution(owned: Owned): string {
33
+ const name = getOwnerName(owned)
34
+ const page = getOwnerPage(owned)
35
+ if (!name && !page)
36
+ return ''
37
+ // The owner name is free text any user controls (organization name, or a
38
+ // profile's first/last name). maplibre-gl injects this attribution string
39
+ // into the DOM as HTML, and its own attribution sanitizer is bypassable via
40
+ // mutation XSS, so every interpolated value has to be escaped here.
41
+ return `© <a href="${escapeHtml(page ?? '')}" target="_blank">${escapeHtml(name)}</a>`
42
+ }
@@ -176,6 +176,9 @@ export function isCommunityResource(resource: Resource | CommunityResource): boo
176
176
  }
177
177
 
178
178
  export function getResourceExternalUrl(dataset: Dataset | DatasetV2 | Omit<Dataset, 'resources' | 'community_resources'>, resource: Resource | CommunityResource): string {
179
+ const config = useComponentsConfig()
180
+ if (config.getResourceExternalUrl) return config.getResourceExternalUrl(dataset, resource)
181
+
179
182
  return `${dataset.page}${isCommunityResource(resource) ? '/community-resources' : ''}?resource_id=${resource.id}`
180
183
  }
181
184