fix(maps): default the Leaflet basemap to OpenFreeMap

CARTO started stamping "API KEY REQUIRED" across every keyless basemap tile
on 26.08.2026. #2060 added a key field as the immediate answer, but it leaves
a self-hosted instance needing an email exchange with CARTO before its maps
look right, and every default in the client still pointed at CARTO. Default
to OpenFreeMap Positron instead: no key, no account, no request limit,
commercial use allowed, attribution required.

OpenFreeMap serves vector tiles only, so the Leaflet basemap is a MapLibre
style document rather than an XYZ template. maplibre-gl-leaflet hangs a GL
canvas into Leaflet's own tile pane, which leaves everything above it alone -
markers, GeoJSON, clusters, plugin layers, the custom panes, and the click
handling, since Leaflet's CSS already puts pointer-events:none on that layer.
maplibre-gl still arrives only through a dynamic import, so no chunk that
draws a map grows by a megabyte.

Switched: the trip planner, the MapView fallback every preview goes through,
the journey map, collections, the atlas, the shared trip page and the offline
tile prefetch. OpenFreeMap Positron and Bright join the tile presets in
settings, the admin defaults and both mobile screens.

A CARTO template that is already saved keeps working when a key backs it -
that stays a supported choice. Without a key it is not drawn at all:
resolveTileUrl falls back to the default rather than to a watermark, so an
instance that upgrades with one in its settings sees the new basemap
immediately and needs no migration. The stored value is left as it is, so the
Map settings tab still shows what was chosen and the warning underneath it
explains why - one sentence appended, in all 23 locales.

OpenFreeMap asks for a credit of its own; OpenStreetMap alone under its tiles
is wrong and a licence problem. attributionForTile() derives it from the URL
rather than from a flag at each call site, and the shared trip map, which
carried no attribution at all, now gets one.

Offline maps keep working and get cheaper. A vector style is four things
instead of one, so sync/glPrefetcher.ts walks the style document, its sprite,
the Latin glyph ranges of each font stack it uses, the TileJSON - the planet
path is versioned, so guessing it answers 200 with an empty body - and then
the .pbf tiles. Over the Berlin bounding box TREK computes, raster z10-16 is
2237 requests and 34.6 MB and gives up above z16; vector z10-14 is 177
requests and 32.1 MB and stays sharp above that, because MapLibre scales the
z14 tile rather than asking for another. The Workbox rule for
tiles.openfreemap.org moves to CacheFirst and shares the prefetcher's cache,
so browsing tops a downloaded region up instead of filling a second copy
beside it - a versioned tile path can never be stale. Clearing offline maps
clears it too.

The atlas loses its second raster layer. It existed to warm the HTTP cache of
neighbouring zoom levels, which a vector basemap does not need; a second one
would only cost a second WebGL context. OpenFreeMap has no label-free style,
so its labels are switched off on the live map instead - they are all symbol
layers, while borders and coastlines are line and fill - re-armed on
style.load so a theme switch keeps them off.

Theme changes call setStyle in place rather than rebuilding the layer: a swap
per toggle would leak a WebGL context, and browsers cap those in the low
teens.
This commit is contained in:
Maurice
2026-08-29 00:02:12 +02:00
committed by Maurice
parent 1d12535b1b
commit b401da2264
53 changed files with 1342 additions and 168 deletions
+1 -1
View File
@@ -36,7 +36,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'تعرض خرائط CARTO الأساسية علامة مائية بدون مفتاح. مجاني وبدون حساب، من',
'settings.mapCartoKeyLink': 'مفتاح API لخرائط carto.com الأساسية',
'settings.mapCartoKeyMissing':
'هذا القالب خريطة أساسية من CARTO. بدون مفتاح تطبع CARTO عبارة "API KEY REQUIRED" على كل بلاطة.',
'هذا القالب خريطة أساسية من CARTO. بدون مفتاح تطبع CARTO عبارة "API KEY REQUIRED" على كل بلاطة. إلى أن تُدخل مفتاحًا، تعرض TREK الخريطة الأساسية الافتراضية بدلاً منها.',
'settings.mapStyle': 'نمط الخريطة',
'settings.mapStylePlaceholder': 'اختر نمط Mapbox',
'settings.mapStyleHint': 'إعداد مسبق أو عنوان URL mapbox://styles/USER/ID خاص بك',
+1 -1
View File
@@ -38,7 +38,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': "Os mapas base do CARTO exibem uma marca d'água sem chave. Gratuita e sem conta, em",
'settings.mapCartoKeyLink': 'chave de API de mapas base do carto.com',
'settings.mapCartoKeyMissing':
'Este modelo é um mapa base do CARTO. Sem uma chave, o CARTO estampa "API KEY REQUIRED" em cada bloco.',
'Este modelo é um mapa base do CARTO. Sem uma chave, o CARTO estampa "API KEY REQUIRED" em cada bloco. Até você inserir uma, o TREK mostra o mapa base padrão.',
'settings.mapStyle': 'Estilo do mapa',
'settings.mapStylePlaceholder': 'Selecionar um estilo Mapbox',
'settings.mapStyleHint': 'Preset ou sua própria URL mapbox://styles/USER/ID',
+1 -1
View File
@@ -28,7 +28,7 @@ const settings: TranslationStrings = {
"Els mapes base de CARTO mostren una marca d'aigua sense clau. Gratuïta i sense compte, des de",
'settings.mapCartoKeyLink': "clau d'API de mapes base de carto.com",
'settings.mapCartoKeyMissing':
'Aquesta plantilla és un mapa base de CARTO. Sense clau, CARTO estampa "API KEY REQUIRED" a cada tessel·la.',
'Aquesta plantilla és un mapa base de CARTO. Sense clau, CARTO estampa "API KEY REQUIRED" a cada tessel·la. Mentre no hi hagi clau, TREK mostra el mapa base per defecte.',
'settings.mapStyle': 'Estil de mapa',
'settings.mapStylePlaceholder': 'Selecciona un estil de Mapbox',
'settings.mapStyleHint': 'Predefinit o la teva pròpia URL mapbox://styles/USUARI/ID',
+1 -1
View File
@@ -37,7 +37,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'Podkladové mapy CARTO se bez klíče zobrazují s vodoznakem. Zdarma a bez účtu, na',
'settings.mapCartoKeyLink': 'carto.com API klíč pro podkladové mapy',
'settings.mapCartoKeyMissing':
'Tato šablona je podkladová mapa CARTO. Bez klíče CARTO vypálí do každé dlaždice nápis "API KEY REQUIRED".',
'Tato šablona je podkladová mapa CARTO. Bez klíče CARTO vypálí do každé dlaždice nápis "API KEY REQUIRED". Dokud klíč nezadáte, TREK zobrazuje výchozí podkladovou mapu.',
'settings.mapStyle': 'Styl mapy',
'settings.mapStylePlaceholder': 'Vyberte styl Mapbox',
'settings.mapStyleHint': 'Preset nebo vaše vlastní URL mapbox://styles/USER/ID',
+1 -1
View File
@@ -38,7 +38,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'CARTO-Basiskarten zeigen ohne Key ein Wasserzeichen. Kostenlos und ohne Konto, unter',
'settings.mapCartoKeyLink': 'carto.com Basemap-API-Key',
'settings.mapCartoKeyMissing':
'Diese Vorlage ist eine CARTO-Basiskarte. Ohne Key brennt CARTO "API KEY REQUIRED" in jede Kachel.',
'Diese Vorlage ist eine CARTO-Basiskarte. Ohne Key brennt CARTO "API KEY REQUIRED" in jede Kachel. Bis ein Key eingetragen ist, zeigt TREK die Standard-Basiskarte.',
'settings.mapStyle': 'Kartenstil',
'settings.mapStylePlaceholder': 'Mapbox-Stil wählen',
'settings.mapStyleHint': 'Preset oder eigene mapbox://styles/USER/ID URL',
+1 -1
View File
@@ -46,7 +46,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'CARTO basemaps show a watermark without a key. Free, no account needed, from',
'settings.mapCartoKeyLink': 'carto.com basemap API key',
'settings.mapCartoKeyMissing':
'This template is a CARTO basemap. Without a key CARTO stamps "API KEY REQUIRED" onto every tile.',
'This template is a CARTO basemap. Without a key CARTO stamps "API KEY REQUIRED" onto every tile. Until you enter one, TREK shows the default basemap instead.',
'settings.mapStyle': 'Map Style',
'settings.mapStylePlaceholder': 'Select a Mapbox style',
'settings.mapStyleHint': 'Preset or your own mapbox://styles/USER/ID URL',
+1 -1
View File
@@ -37,7 +37,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'Los mapas base de CARTO muestran una marca de agua sin clave. Gratuita y sin cuenta, en',
'settings.mapCartoKeyLink': 'clave de API de mapas base de carto.com',
'settings.mapCartoKeyMissing':
'Esta plantilla es un mapa base de CARTO. Sin clave, CARTO estampa "API KEY REQUIRED" en cada tesela.',
'Esta plantilla es un mapa base de CARTO. Sin clave, CARTO estampa "API KEY REQUIRED" en cada tesela. Hasta que introduzcas una, TREK muestra el mapa base predeterminado.',
'settings.mapStyle': 'Estilo de mapa',
'settings.mapStylePlaceholder': 'Seleccionar un estilo de Mapbox',
'settings.mapStyleHint': 'Preset o tu propia URL mapbox://styles/USER/ID',
+1 -1
View File
@@ -38,7 +38,7 @@ const settings: TranslationStrings = {
'Les fonds de carte CARTO affichent un filigrane sans clé. Gratuite et sans compte, depuis',
'settings.mapCartoKeyLink': "clé d'API de fonds de carte carto.com",
'settings.mapCartoKeyMissing':
'Ce modèle est un fond de carte CARTO. Sans clé, CARTO appose "API KEY REQUIRED" sur chaque tuile.',
'Ce modèle est un fond de carte CARTO. Sans clé, CARTO appose "API KEY REQUIRED" sur chaque tuile. Tant que la clé manque, TREK affiche le fond de carte par défaut.',
'settings.mapStyle': 'Style de carte',
'settings.mapStylePlaceholder': 'Sélectionner un style Mapbox',
'settings.mapStyleHint': 'Preset ou votre propre URL mapbox://styles/USER/ID',
+1 -1
View File
@@ -40,7 +40,7 @@ const settings: TranslationStrings = {
'Οι βασικοί χάρτες CARTO εμφανίζουν υδατογράφημα χωρίς κλειδί. Δωρεάν, χωρίς λογαριασμό, από',
'settings.mapCartoKeyLink': 'κλειδί API βασικών χαρτών carto.com',
'settings.mapCartoKeyMissing':
'Αυτό το πρότυπο είναι βασικός χάρτης CARTO. Χωρίς κλειδί, η CARTO τυπώνει "API KEY REQUIRED" σε κάθε πλακίδιο.',
'Αυτό το πρότυπο είναι βασικός χάρτης CARTO. Χωρίς κλειδί, η CARTO τυπώνει "API KEY REQUIRED" σε κάθε πλακίδιο. Μέχρι να εισαγάγετε κλειδί, το TREK εμφανίζει τον προεπιλεγμένο βασικό χάρτη.',
'settings.mapStyle': 'Στυλ Χάρτη',
'settings.mapStylePlaceholder': 'Επιλέξτε ένα στυλ Mapbox',
'settings.mapStyleHint': 'Προκαθορισμένο ή δικό σας mapbox://styles/USER/ID URL',
+1 -1
View File
@@ -38,7 +38,7 @@ const settings: TranslationStrings = {
'A CARTO alaptérképek kulcs nélkül vízjelet jelenítenek meg. Ingyenes, fiók nélkül, innen:',
'settings.mapCartoKeyLink': 'carto.com alaptérkép API-kulcs',
'settings.mapCartoKeyMissing':
'Ez a sablon CARTO alaptérkép. Kulcs nélkül a CARTO minden csempére ráírja: "API KEY REQUIRED".',
'Ez a sablon CARTO alaptérkép. Kulcs nélkül a CARTO minden csempére ráírja: "API KEY REQUIRED". Amíg nincs kulcs megadva, a TREK az alapértelmezett alaptérképet mutatja.',
'settings.mapStyle': 'Térkép stílus',
'settings.mapStylePlaceholder': 'Válassz Mapbox stílust',
'settings.mapStyleHint': 'Preset vagy saját mapbox://styles/USER/ID URL',
+1 -1
View File
@@ -37,7 +37,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'Peta dasar CARTO menampilkan tanda air tanpa kunci. Gratis, tanpa akun, dari',
'settings.mapCartoKeyLink': 'kunci API peta dasar carto.com',
'settings.mapCartoKeyMissing':
'Templat ini adalah peta dasar CARTO. Tanpa kunci, CARTO mencetak "API KEY REQUIRED" di setiap ubin.',
'Templat ini adalah peta dasar CARTO. Tanpa kunci, CARTO mencetak "API KEY REQUIRED" di setiap ubin. Sampai kunci dimasukkan, TREK menampilkan peta dasar bawaan.',
'settings.mapStyle': 'Gaya peta',
'settings.mapStylePlaceholder': 'Pilih gaya Mapbox',
'settings.mapStyleHint': 'Preset atau URL mapbox://styles/USER/ID milikmu',
+1 -1
View File
@@ -37,7 +37,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'Le mappe base CARTO mostrano una filigrana senza chiave. Gratuita e senza account, da',
'settings.mapCartoKeyLink': 'chiave API mappe base di carto.com',
'settings.mapCartoKeyMissing':
'Questo modello è una mappa base CARTO. Senza chiave, CARTO stampa "API KEY REQUIRED" su ogni tassello.',
'Questo modello è una mappa base CARTO. Senza chiave, CARTO stampa "API KEY REQUIRED" su ogni tassello. Finché non inserisci una chiave, TREK mostra la mappa base predefinita.',
'settings.mapStyle': 'Stile mappa',
'settings.mapStylePlaceholder': 'Seleziona uno stile Mapbox',
'settings.mapStyleHint': 'Preset o il tuo URL mapbox://styles/USER/ID',
+1 -1
View File
@@ -37,7 +37,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'キーがないと CARTO のベースマップに透かしが入ります。無料でアカウントも不要、取得先:',
'settings.mapCartoKeyLink': 'carto.com ベースマップ API キー',
'settings.mapCartoKeyMissing':
'このテンプレートは CARTO のベースマップです。キーがないと CARTO はすべてのタイルに "API KEY REQUIRED" を焼き込みます。',
'このテンプレートは CARTO のベースマップです。キーがないと CARTO はすべてのタイルに "API KEY REQUIRED" を焼き込みます。 キーを入力するまで、TREK は既定のベースマップを表示します。',
'settings.mapStyle': '地図スタイル',
'settings.mapStylePlaceholder': 'Mapboxスタイルを選択',
'settings.mapStyleHint': 'プリセットまたは mapbox://styles/USER/ID のURL',
+1 -1
View File
@@ -38,7 +38,7 @@ const settings: TranslationStrings = {
'CARTO 배경 지도는 키가 없으면 워터마크가 표시됩니다. 무료이며 계정도 필요 없습니다. 출처',
'settings.mapCartoKeyLink': 'carto.com 배경 지도 API 키',
'settings.mapCartoKeyMissing':
'이 템플릿은 CARTO 배경 지도입니다. 키가 없으면 CARTO가 모든 타일에 "API KEY REQUIRED"를 새깁니다.',
'이 템플릿은 CARTO 배경 지도입니다. 키가 없으면 CARTO가 모든 타일에 "API KEY REQUIRED"를 새깁니다. 키를 입력하기 전까지 TREK은 기본 배경 지도를 표시합니다.',
'settings.mapStyle': '지도 스타일',
'settings.mapStylePlaceholder': 'Mapbox 스타일 선택',
'settings.mapStyleHint': '프리셋 또는 mapbox://styles/USER/ID URL 직접 입력',
+1 -1
View File
@@ -38,7 +38,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'CARTO-basiskaarten tonen zonder sleutel een watermerk. Gratis en zonder account, via',
'settings.mapCartoKeyLink': 'carto.com basemap API-sleutel',
'settings.mapCartoKeyMissing':
'Deze sjabloon is een CARTO-basiskaart. Zonder sleutel drukt CARTO "API KEY REQUIRED" op elke tegel.',
'Deze sjabloon is een CARTO-basiskaart. Zonder sleutel drukt CARTO "API KEY REQUIRED" op elke tegel. Zolang er geen sleutel is, toont TREK de standaardbasiskaart.',
'settings.mapStyle': 'Kaartstijl',
'settings.mapStylePlaceholder': 'Kies een Mapbox-stijl',
'settings.mapStyleHint': 'Preset of eigen mapbox://styles/USER/ID URL',
+1 -1
View File
@@ -37,7 +37,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'Mapy podkładowe CARTO bez klucza pokazują znak wodny. Bezpłatny, bez konta, z',
'settings.mapCartoKeyLink': 'klucz API map podkładowych carto.com',
'settings.mapCartoKeyMissing':
'Ten szablon to mapa podkładowa CARTO. Bez klucza CARTO nanosi "API KEY REQUIRED" na każdy kafelek.',
'Ten szablon to mapa podkładowa CARTO. Bez klucza CARTO nanosi "API KEY REQUIRED" na każdy kafelek. Dopóki nie podasz klucza, TREK pokazuje domyślną mapę podkładową.',
'settings.mapStyle': 'Styl mapy',
'settings.mapStylePlaceholder': 'Wybierz styl Mapbox',
'settings.mapStyleHint': 'Preset lub własny URL mapbox://styles/USER/ID',
+1 -1
View File
@@ -38,7 +38,7 @@ const settings: TranslationStrings = {
'Без ключа на базовых картах CARTO появляется водяной знак. Бесплатно и без учётной записи, на',
'settings.mapCartoKeyLink': 'ключ API базовых карт carto.com',
'settings.mapCartoKeyMissing':
'Этот шаблон является базовой картой CARTO. Без ключа CARTO наносит "API KEY REQUIRED" на каждый тайл.',
'Этот шаблон является базовой картой CARTO. Без ключа CARTO наносит "API KEY REQUIRED" на каждый тайл. Пока ключ не указан, TREK показывает базовую карту по умолчанию.',
'settings.mapStyle': 'Стиль карты',
'settings.mapStylePlaceholder': 'Выберите стиль Mapbox',
'settings.mapStyleHint': 'Preset или собственный URL mapbox://styles/USER/ID',
+1 -1
View File
@@ -40,7 +40,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'CARTO-bakgrundskartor visar en vattenstämpel utan nyckel. Gratis och utan konto, från',
'settings.mapCartoKeyLink': 'API-nyckel för bakgrundskartor på carto.com',
'settings.mapCartoKeyMissing':
'Den här mallen är en CARTO-bakgrundskarta. Utan nyckel stämplar CARTO "API KEY REQUIRED" på varje ruta.',
'Den här mallen är en CARTO-bakgrundskarta. Utan nyckel stämplar CARTO "API KEY REQUIRED" på varje ruta. Tills en nyckel anges visar TREK standardbakgrundskartan.',
'settings.mapStyle': 'Kartstil',
'settings.mapStylePlaceholder': 'Välj en Mapbox-stil',
'settings.mapStyleHint': 'Förinställda eller egna mapbox://styles/USER/ID länk',
+1 -1
View File
@@ -38,7 +38,7 @@ const settings: TranslationStrings = {
'CARTO temel haritaları anahtar olmadan filigran gösterir. Ücretsiz, hesap gerekmez, kaynağı:',
'settings.mapCartoKeyLink': 'carto.com temel harita API anahtarı',
'settings.mapCartoKeyMissing':
'Bu şablon bir CARTO temel haritası. Anahtar olmadan CARTO her karoya "API KEY REQUIRED" damgası basar.',
'Bu şablon bir CARTO temel haritası. Anahtar olmadan CARTO her karoya "API KEY REQUIRED" damgası basar. Anahtar girilene kadar TREK varsayılan temel haritayı gösterir.',
'settings.mapStyle': 'Harita Stili',
'settings.mapStylePlaceholder': 'Bir Mapbox stili seçin',
'settings.mapStyleHint': 'Ön ayar veya kendi mapbox://styles/KULLANICI/ID adresiniz',
+1 -1
View File
@@ -39,7 +39,7 @@ const settings: TranslationStrings = {
'Без ключа базові карти CARTO показують водяний знак. Безкоштовно та без облікового запису, на',
'settings.mapCartoKeyLink': 'ключ API базових карт carto.com',
'settings.mapCartoKeyMissing':
'Цей шаблон є базовою картою CARTO. Без ключа CARTO наносить "API KEY REQUIRED" на кожен тайл.',
'Цей шаблон є базовою картою CARTO. Без ключа CARTO наносить "API KEY REQUIRED" на кожен тайл. Доки ключ не вказано, TREK показує базову карту за замовчуванням.',
'settings.mapStyle': 'Стиль карти',
'settings.mapStylePlaceholder': 'Виберіть стиль Mapbox',
'settings.mapStyleHint': 'Preset або власний URL mapbox://styles/USER/ID',
+1 -1
View File
@@ -38,7 +38,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKeyHint': 'Bản đồ nền CARTO hiển thị hình mờ nếu không có khóa. Miễn phí, không cần tài khoản, từ',
'settings.mapCartoKeyLink': 'khóa API bản đồ nền carto.com',
'settings.mapCartoKeyMissing':
'Mẫu này là bản đồ nền CARTO. Không có khóa, CARTO in "API KEY REQUIRED" lên mọi ô bản đồ.',
'Mẫu này là bản đồ nền CARTO. Không có khóa, CARTO in "API KEY REQUIRED" lên mọi ô bản đồ. Cho đến khi bạn nhập khóa, TREK hiển thị bản đồ nền mặc định.',
'settings.mapStyle': 'Kiểu bản đồ',
'settings.mapStylePlaceholder': 'Chọn kiểu Mapbox',
'settings.mapStyleHint': 'Đặt trước hoặc của riêng bạn mapbox://styles/USER/ID URL',
+1 -1
View File
@@ -36,7 +36,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKey': 'CARTO API 金鑰',
'settings.mapCartoKeyHint': '沒有金鑰時 CARTO 底圖會顯示浮水印。免費且無需帳戶,來自',
'settings.mapCartoKeyLink': 'carto.com 底圖 API 金鑰',
'settings.mapCartoKeyMissing': '此範本是 CARTO 底圖。沒有金鑰時,CARTO 會在每個圖磚上印上 "API KEY REQUIRED"。',
'settings.mapCartoKeyMissing': '此範本是 CARTO 底圖。沒有金鑰時,CARTO 會在每個圖磚上印上 "API KEY REQUIRED"。 在輸入金鑰之前,TREK 會顯示預設底圖。',
'settings.mapStyle': '地圖樣式',
'settings.mapStylePlaceholder': '選擇 Mapbox 樣式',
'settings.mapStyleHint': '預設或您自己的 mapbox://styles/USER/ID URL',
+1 -1
View File
@@ -36,7 +36,7 @@ const settings: TranslationStrings = {
'settings.mapCartoKey': 'CARTO API 密钥',
'settings.mapCartoKeyHint': '没有密钥时 CARTO 底图会显示水印。免费且无需账户,来自',
'settings.mapCartoKeyLink': 'carto.com 底图 API 密钥',
'settings.mapCartoKeyMissing': '此模板是 CARTO 底图。没有密钥时,CARTO 会在每个瓦片上打上 "API KEY REQUIRED"。',
'settings.mapCartoKeyMissing': '此模板是 CARTO 底图。没有密钥时,CARTO 会在每个瓦片上打上 "API KEY REQUIRED"。 在输入密钥之前,TREK 会显示默认底图。',
'settings.mapStyle': '地图样式',
'settings.mapStylePlaceholder': '选择 Mapbox 样式',
'settings.mapStyleHint': '预设或您自己的 mapbox://styles/USER/ID URL',