Skip to content

Commit

Permalink
Merge pull request #1008 from geoadmin/bug-PB--631-cors-kml-icons
Browse files Browse the repository at this point in the history
PB-803, PB-631, PB-175: Use proxy for all non geoadmin KML icon urls - #patch
  • Loading branch information
ltshb authored Jul 17, 2024
2 parents 7eb477c + 3e3222e commit 987f690
Show file tree
Hide file tree
Showing 15 changed files with 259 additions and 24 deletions.
4 changes: 2 additions & 2 deletions secrets.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# required in order to access the translation spreadsheet (while running `npm run update:translations`)
GOOGLE_API_KEY: !var infra-gopass-bgdi/web-mapviewer/GOOGLE_API_KEY
CYPRESS_RECORD_KEY: !var infra-gopass-bgdi/web-mapviewer/cypress-key key
GOOGLE_API_KEY: !var /google.com/web-mapviewer/api-key --profile swisstopo-bgdi-builder
CYPRESS_RECORD_KEY: !var /cypress.io/ppbgdi/web-mapviewer/record-key --profile swisstopo-bgdi-builder
17 changes: 16 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useI18n } from 'vue-i18n'
import { useStore } from 'vuex'
import ErrorWindow from '@/utils/components/ErrorWindow.vue'
import WarningWindow from '@/utils/components/WarningWindow.vue'
import debounce from '@/utils/debounce'
const withOutline = ref(false)
Expand All @@ -22,6 +23,12 @@ const dispatcher = { dispatcher: 'App.vue' }
let debouncedOnResize
const errorText = computed(() => store.state.ui.errorText)
const warning = computed(() => {
if (store.state.ui.warnings.size > 0) {
return store.state.ui.warnings.values().next().value
}
return null
})
onMounted(() => {
// reading size
Expand Down Expand Up @@ -58,8 +65,16 @@ function refreshPageTitle() {
v-if="errorText"
title="error"
@close="store.dispatch('setErrorText', { errorText: null, ...dispatcher })"
><div>{{ i18n.t(errorText) }}</div></ErrorWindow
>
<div>{{ i18n.t(errorText) }}</div>
</ErrorWindow>
<WarningWindow
v-if="warning"
title="warning"
@close="store.dispatch('removeWarning', { warning, ...dispatcher })"
>
<div>{{ i18n.t(warning.msg, warning.params) }}</div>
</WarningWindow>
</div>
</template>

Expand Down
14 changes: 14 additions & 0 deletions src/modules/i18n/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Internationalization (i18n) module

- [Quick guide](#quick-guide)
- [Formatting messages](#formatting-messages)
- [update translations](#update-translations)
- [State properties](#state-properties)
- [Noteworthy mutation](#noteworthy-mutation)

## Quick guide

Responsible for loading and serving `vue-i18n`. This utils can be accessed by linking the result of `useI18n()`
to a local ref (in Composition API) or in-place with the Option API. As we've deactivated the legacy support,
it's not possible to use `this.$i18n` anymore, we must now go through the `useI18n()` function to
Expand Down Expand Up @@ -39,6 +47,12 @@ useI18n().t('a_translation_key')

Or if you have multiple call to `t(...)`, you can store the reference given by `useI18n()` at some point (do not store it in `data()`)

## Formatting messages

Some message might require some formatting from the application to do so you can use the `{placeholder}` notation in the translation key and then use `i18n.t('my_formatted_key', {placeholder: 'my placeholder'})`

See [i18n guide](https://kazupon.github.io/vue-i18n/guide/formatting.html)

## update translations

See [the main README.md's section on that](../../../README.md#tooling-for-translation-update)
Expand Down
5 changes: 4 additions & 1 deletion src/modules/i18n/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -683,5 +683,8 @@
"webmapviewer_live_disclaimer": "Der Kartenviewer wird bald aktualisiert - <a target='_blank' href='https://www.geo.admin.ch/de/go-live-kartenviewer'>mach dich startklar</a>",
"drawing_too_large": "Ihre Zeichnung ist zu gross, entfernen Sie einige Details.",
"3d_unsupported_projection": "Dieser Datensatz (externe Quelle) kann wegen fehlender Unterstützung der Projektion EPSG:4326 nicht in 3D dargestellt werden",
"geoloc_out_of_bounds": "Ihre aktuelle Position liegt ausserhalb der Schweiz und kann deshalb nicht angezeigt werden"
"geoloc_out_of_bounds": "Ihre aktuelle Position liegt ausserhalb der Schweiz und kann deshalb nicht angezeigt werden",
"orient_map_north": "Karte einnorden",
"kml_icon_url_cors_issue": "Die KML-Datei „{layerName}“ enthält Symbol(e) mit der URL {url}, die keine CORS-Unterstützung bietet! Bitte wenden Sie sich an den Administrator dieser URL, um die Unterstützung für CORS hinzuzufügen.",
"warning": "Warnung"
}
5 changes: 4 additions & 1 deletion src/modules/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -683,5 +683,8 @@
"webmapviewer_live_disclaimer": "The new map viewer is coming soon - <a target='_blank' href='https://www.geo.admin.ch/en/go-live-map-viewer'>get ready</a>",
"drawing_too_large": "Your drawing is too large, remove some features",
"3d_unsupported_projection": "This map provided by external source can't be displayed in 3d because it doesn't support the projection EPSG:4326",
"geoloc_out_of_bounds": "Your current location is outside of Switzerland and cannot be shown"
"geoloc_out_of_bounds": "Your current location is outside of Switzerland and cannot be shown",
"orient_map_north": "Orient the map",
"kml_icon_url_cors_issue": "The KML \"{layerName}\" contains icon(s) with URL {url} that doesn't support CORS! Please contact the administrator of this URL to add support for CORS.",
"warning": "Warning"
}
5 changes: 4 additions & 1 deletion src/modules/i18n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -683,5 +683,8 @@
"webmapviewer_live_disclaimer": "Le visualiseur de cartes sera bientôt mis à jour - <a target='_blank' href='https://www.geo.admin.ch/fr/go-live-visualiseur-de-cartes'>comment se préparer</a>",
"drawing_too_large": "Ton dessin est trop grand, enlève quelques éléments",
"3d_unsupported_projection": "La carte ne peut pas être affichées en 3d parce qu'elle ne supporte pas la projection EPSG:4326",
"geoloc_out_of_bounds": "Votre emplacement actuel est en dehors de la Suisse et ne peut pas être affiché"
"geoloc_out_of_bounds": "Votre emplacement actuel est en dehors de la Suisse et ne peut pas être affiché",
"orient_map_north": "Orienter la carte",
"kml_icon_url_cors_issue": "Le KML « {layerName} » contient des icônes avec l'URL {url} qui ne supporte pas CORS ! Veuillez contacter l'administrateur de cette URL pour avoir du support de CORS.",
"warning": "Attention"
}
5 changes: 4 additions & 1 deletion src/modules/i18n/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -683,5 +683,8 @@
"webmapviewer_live_disclaimer": "Il visualizzatore di mappe sarà aggiornato in breve - <a target='_blank' href='https://www.geo.admin.ch/it/go-live-visualizzatore-di-mappe'>tenetevi pronti</a>",
"drawing_too_large": "Il suo disegno è troppo grande, rimuova alcuni elementi",
"3d_unsupported_projection": "La mappa non può essere visualizzata in 3D perché non supporta la proiezione EPSG:4326",
"geoloc_out_of_bounds": "La Sua posizione attuale è al di fuori della Svizzera e non può essere mostrata."
"geoloc_out_of_bounds": "La Sua posizione attuale è al di fuori della Svizzera e non può essere mostrata.",
"orient_map_north": "Orientare la mappa",
"kml_icon_url_cors_issue": "Il KML “{layerName}” contiene icone con l'URL {url} che non supporta CORS! Contattare l'amministratore di questo URL per aggiungere il supporto per CORS.",
"warning": "Avvertenza"
}
5 changes: 4 additions & 1 deletion src/modules/i18n/locales/rm.json
Original file line number Diff line number Diff line change
Expand Up @@ -681,5 +681,8 @@
"webmapviewer_live_disclaimer": "Il visualisader da chartas vegn actualisà proximamain - <a target='_blank' href='https://www.geo.admin.ch/de/go-live-kartenviewer'>stai pronts</a>",
"drawing_too_large": "Tes dissegn è memia grond, stizza intgins detagls",
"3d_unsupported_projection": "La carta na po betg vegnir mussada en 3D perquai ch'ella na sustegna betg la projecziun EPSG:4326",
"geoloc_out_of_bounds": "Voss lieu actual sa chatta ordaifer la Svizra e na po betg vegnir mussà"
"geoloc_out_of_bounds": "Voss lieu actual sa chatta ordaifer la Svizra e na po betg vegnir mussà",
"orient_map_north": "Orientar la carta",
"kml_icon_url_cors_issue": "Il KML \"{layerName}\" cuntegna icona(s) cun URL {url} che na po nagin sustegnair CORS! As drizzai p.pl. a l'administratura da quella URL per agiuntar il sustegn dal CORS.",
"warning": "Avertiment"
}
23 changes: 21 additions & 2 deletions src/modules/map/components/openlayers/OpenLayersKMLLayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { useStore } from 'vuex'
import KMLLayer from '@/api/layers/KMLLayer.class'
import { IS_TESTING_WITH_CYPRESS } from '@/config'
import useAddLayerToMap from '@/modules/map/components/openlayers/utils/useAddLayerToMap.composable'
import { parseKml } from '@/utils/kmlUtils'
import { iconUrlProxyFy, parseKml } from '@/utils/kmlUtils'
import log from '@/utils/logging'
import WarningMessage from '@/utils/WarningMessage.class'
const dispatcher = { dispatcher: 'OpenLayersKMLLayer.vue' }
Expand Down Expand Up @@ -39,6 +40,7 @@ const iconsArePresent = computed(() => availableIconSets.value.length > 0)
// extracting useful info from what we've linked so far
const layerId = computed(() => kmlLayerConfig.value.id)
const layerName = computed(() => kmlLayerConfig.value.name)
const opacity = computed(() => parentLayerOpacity.value ?? kmlLayerConfig.value.opacity)
const url = computed(() => kmlLayerConfig.value.baseUrl)
const kmlData = computed(() => kmlLayerConfig.value.kmlData)
Expand Down Expand Up @@ -81,6 +83,18 @@ onUnmounted(() => {
}
})
function iconUrlProxy(url) {
return iconUrlProxyFy(url, (url) => {
store.dispatch('addWarning', {
warning: new WarningMessage('kml_icon_url_cors_issue', {
layerName: layerName.value,
url: url,
}),
dispatcher: 'kmlUtils.js',
})
})
}
function createSourceForProjection() {
if (!kmlData.value) {
log.debug('no KML data loaded yet, could not create source')
Expand All @@ -94,7 +108,12 @@ function createSourceForProjection() {
new VectorSource({
wrapX: true,
projection: projection.value.epsg,
features: parseKml(kmlLayerConfig.value, projection.value, availableIconSets.value),
features: parseKml(
kmlLayerConfig.value,
projection.value,
availableIconSets.value,
iconUrlProxy
),
})
)
log.debug('Openlayer KML layer source created')
Expand Down
30 changes: 30 additions & 0 deletions src/store/modules/ui.store.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
WARNING_RIBBON_HOSTNAMES,
} from '@/config'
import log from '@/utils/logging'
import WarningMessage from '@/utils/WarningMessage.class'

const MAP_LOADING_BAR_REQUESTER = 'app-map-loading'

Expand Down Expand Up @@ -161,6 +162,13 @@ export default {
*/
errorText: null,

/**
* Set of warnings to display. Each warning must be an object WarningMessage
*
* @type Set(WarningMessage)
*/
warnings: new Set(),

/**
* Flag telling if the "Drop file here" overlay will be displayed on top of the map.
*
Expand Down Expand Up @@ -387,6 +395,26 @@ export default {
setErrorText({ commit }, { errorText, dispatcher }) {
commit('setErrorText', { errorText, dispatcher })
},
addWarning({ commit, state }, { warning, dispatcher }) {
if (!(warning instanceof WarningMessage)) {
throw new Error(
`Warning ${warning} dispatched by ${dispatcher} is not of type WarningMessage`
)
}
if (!state.warnings.has(warning)) {
commit('addWarning', { warning, dispatcher })
}
},
removeWarning({ commit, state }, { warning, dispatcher }) {
if (!(warning instanceof WarningMessage)) {
throw new Error(
`Warning ${warning} dispatched by ${dispatcher} is not of type WarningMessage`
)
}
if (state.warnings.has(warning)) {
commit('removeWarning', { warning, dispatcher })
}
},
setShowDragAndDropOverlay({ commit }, { showDragAndDropOverlay, dispatcher }) {
commit('setShowDragAndDropOverlay', { showDragAndDropOverlay, dispatcher })
},
Expand Down Expand Up @@ -450,6 +478,8 @@ export default {
},
setShowDisclaimer: (state, { showDisclaimer }) => (state.showDisclaimer = showDisclaimer),
setErrorText: (state, { errorText }) => (state.errorText = errorText),
addWarning: (state, { warning }) => state.warnings.add(warning),
removeWarning: (state, { warning }) => state.warnings.delete(warning),
setShowDragAndDropOverlay: (state, { showDragAndDropOverlay }) =>
(state.showDragAndDropOverlay = showDragAndDropOverlay),
},
Expand Down
11 changes: 11 additions & 0 deletions src/utils/WarningMessage.class.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/** Warning message to display to the user */
export default class WarningMessage {
/**
* @param {string} msg Translation key message
* @param {any} params Translation params to pass to i18n (used for message formatting)
*/
constructor(msg, params = null) {
this.msg = msg
this.params = params
}
}
8 changes: 3 additions & 5 deletions src/utils/components/ErrorWindow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ const props = defineProps({
type: Boolean,
default: false,
},
/** Add a minimize button in header that will hide/show the body */
hasMinimize: {
type: Boolean,
default: true,
},
})
const { title, hide } = toRefs(props)
Expand Down Expand Up @@ -70,6 +65,7 @@ const emit = defineEmits(['close'])
<style lang="scss" scoped>
@import '@/scss/variables.module';
@import '@/scss/media-query.mixin';
@import '@/scss/webmapviewer-bootstrap-theme';
.simple-window {
$top-margin: calc(2 * $header-height + 2rem);
Expand All @@ -96,6 +92,8 @@ const emit = defineEmits(['close'])
max-width: 100vw;
}
.card-body {
// Allow text selection
@extend .clear-no-ios-long-press;
overflow-y: auto;
&.hide {
Expand Down
5 changes: 0 additions & 5 deletions src/utils/components/SimpleWindow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ const props = defineProps({
type: Boolean,
default: false,
},
/** Add a minimize button in header that will hide/show the body */
hasMinimize: {
type: Boolean,
default: true,
},
})
const { title, hide } = toRefs(props)
Expand Down
103 changes: 103 additions & 0 deletions src/utils/components/WarningWindow.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<script setup>
import { computed, ref, toRefs } from 'vue'
import { useI18n } from 'vue-i18n'
import { useStore } from 'vuex'
const props = defineProps({
title: {
type: String,
default: '',
},
/**
* Hide the modal with backdrop, can be used to temporarily hide the modal without loosing its
* content
*/
hide: {
type: Boolean,
default: false,
},
})
const { title, hide } = toRefs(props)
const store = useStore()
const showBody = ref(true)
const hasDevSiteWarning = computed(() => store.getters.hasDevSiteWarning)
const i18n = useI18n()
const emit = defineEmits(['close'])
</script>

<template>
<div
v-show="!hide"
class="simple-window card bg-warning fw-bold"
:class="{ 'dev-disclaimer-present': hasDevSiteWarning }"
data-cy="warning-window"
>
<div
class="card-header d-flex align-items-center justify-content-sm-end"
data-cy="window-header"
>
<span v-if="title" class="me-auto text-truncate">{{ i18n.t(title) }}</span>
<span v-else class="me-auto" />
<button class="btn btn-sm btn-light me-2" @click.stop="showBody = !showBody">
<FontAwesomeIcon :icon="`caret-${showBody ? 'down' : 'right'}`" />
</button>
<button
class="btn btn-light btn-sm"
data-cy="warning-window-close"
@click.stop="emit('close')"
>
<FontAwesomeIcon icon="times" />
</button>
</div>
<div class="card-body" :class="{ hide: !showBody }" data-cy="warning-window-body">
<slot />
</div>
</div>
</template>

<style lang="scss" scoped>
@import '@/scss/variables.module';
@import '@/scss/media-query.mixin';
@import '@/scss/webmapviewer-bootstrap-theme';
.simple-window {
$top-margin: calc(2 * $header-height + 2rem);
z-index: calc($zindex-menu - 1);
position: fixed;
top: $top-margin;
right: 4rem;
width: max-content;
max-width: 400px;
max-height: calc(100vh - $top-margin);
@include respond-below(phone) {
$top-margin: $header-height;
&.dev-disclaimer-present {
$top-margin: calc($header-height + $dev-disclaimer-height);
}
top: $top-margin;
left: 50%;
right: unset;
transform: translate(-50%, 0%);
max-height: calc(100vh - $top-margin);
max-width: 100vw;
}
.card-body {
// Allow text selection
@extend .clear-no-ios-long-press;
overflow-y: auto;
&.hide {
visibility: hidden;
height: 0px;
padding: 0px;
}
}
}
</style>
Loading

0 comments on commit 987f690

Please sign in to comment.