From 0018fcf1f0bd915fceabf3f0bf7a91aa4dc791fe Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Thu, 11 Nov 2021 17:14:03 +0100 Subject: [PATCH] Only do ID-check on tagrenderings for official themes --- Models/Constants.ts | 2 +- Models/ThemeConfig/LayerConfig.ts | 24 ++++++++----- Models/ThemeConfig/TagRenderingConfig.ts | 26 ++++++++------ Models/ThemeConfig/WithContextLoader.ts | 27 +++++++++++---- .../layers/nature_reserve/nature_reserve.json | 4 +-- .../surveillance_camera.json | 4 +-- assets/themes/buurtnatuur/buurtnatuur.json | 4 +-- assets/themes/climbing/climbing.json | 2 +- langs/layers/de.json | 30 ++++++++-------- langs/layers/en.json | 30 ++++++++-------- langs/layers/fr.json | 30 ++++++++-------- langs/layers/it.json | 30 ++++++++-------- langs/layers/nl.json | 34 +++++++++---------- langs/layers/ru.json | 8 ++--- langs/themes/en.json | 6 ++-- langs/themes/fr.json | 6 ++-- langs/themes/it.json | 6 ++-- 17 files changed, 150 insertions(+), 123 deletions(-) diff --git a/Models/Constants.ts b/Models/Constants.ts index 89e115476..79a55ca9b 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -2,7 +2,7 @@ import {Utils} from "../Utils"; export default class Constants { - public static vNumber = "0.12.6"; + public static vNumber = "0.12.7"; public static ImgurApiKey = '7070e7167f0a25a' public static readonly mapillary_client_token_v3 = 'TXhLaWthQ1d4RUg0czVxaTVoRjFJZzowNDczNjUzNmIyNTQyYzI2' public static readonly mapillary_client_token_v4 = "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" diff --git a/Models/ThemeConfig/LayerConfig.ts b/Models/ThemeConfig/LayerConfig.ts index 22fc990f1..34245bd7b 100644 --- a/Models/ThemeConfig/LayerConfig.ts +++ b/Models/ThemeConfig/LayerConfig.ts @@ -133,7 +133,6 @@ export default class LayerConfig extends WithContextLoader { const code = kv.substring(index + 1); try { - new Function("feat", "return " + code + ";"); } catch (e) { throw `Invalid function definition: code ${code} is invalid:${e} (at ${context})` @@ -225,9 +224,8 @@ export default class LayerConfig extends WithContextLoader { throw "Missing ids in tagrenderings" } - this.tagRenderings = this.ExtractLayerTagRenderings(json) - { - + this.tagRenderings = this.ExtractLayerTagRenderings(json, official) + if (official) { const emptyIds = this.tagRenderings.filter(tr => tr.id === ""); if (emptyIds.length > 0) { @@ -265,7 +263,9 @@ export default class LayerConfig extends WithContextLoader { } } - this.titleIcons = this.ParseTagRenderings(titleIcons, true); + this.titleIcons = this.ParseTagRenderings(titleIcons, { + readOnlyMode: true + }); this.title = this.tr("title", undefined); this.isShown = this.tr("isShown", "yes"); @@ -298,7 +298,7 @@ export default class LayerConfig extends WithContextLoader { } public defaultIcon(): BaseUIElement | undefined { - if(this.mapRendering === undefined || this.mapRendering === null){ + if (this.mapRendering === undefined || this.mapRendering === null) { return undefined; } const mapRendering = this.mapRendering.filter(r => r.location.has("point"))[0] @@ -309,7 +309,7 @@ export default class LayerConfig extends WithContextLoader { return mapRendering.GenerateLeafletStyle(defaultTags, false, {noSize: true}).html } - public ExtractLayerTagRenderings(json: LayerConfigJson): TagRenderingConfig[] { + public ExtractLayerTagRenderings(json: LayerConfigJson, official: boolean): TagRenderingConfig[] { if (json.tagRenderings === undefined) { return [] @@ -342,12 +342,16 @@ export default class LayerConfig extends WithContextLoader { throw `Error in ${this._context}.tagrenderings[${i}]: got a value which defines either \`rewrite\` or \`renderings\`, but not both. Either define both or move the \`renderings\` out of this scope` } - const allRenderings = this.ParseTagRenderings(normalTagRenderings, false); + const allRenderings = this.ParseTagRenderings(normalTagRenderings, + { + requiresId: official + }); if (renderingsToRewrite.length === 0) { return allRenderings } + /* Used for left|right group creation and replacement */ function prepConfig(keyToRewrite: string, target: string, tr: TagRenderingConfigJson) { function replaceRecursive(transl: string | any) { @@ -379,7 +383,9 @@ export default class LayerConfig extends WithContextLoader { const textToReplace = rewriteGroup.rewrite.sourceString const targets = rewriteGroup.rewrite.into for (const target of targets) { - const parsedRenderings = this.ParseTagRenderings(tagRenderings, false, tr => prepConfig(textToReplace, target, tr)) + const parsedRenderings = this.ParseTagRenderings(tagRenderings, { + prepConfig: tr => prepConfig(textToReplace, target, tr) + }) if (!rewriteGroups.has(target)) { rewriteGroups.set(target, []) diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index 43c8e0e4c..e9d74f377 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -71,6 +71,10 @@ export default class TagRenderingConfig { this.id = json.id ?? ""; + if(this.id.match(/^[a-zA-Z0-9 ()?\/=:;,_-]*$/) === null){ + throw "Invalid ID in "+context+": an id can only contain [a-zA-Z0-0_-] as characters. The offending id is: "+this.id + } + this.group = json.group ?? ""; this.render = Translations.T(json.render, context + ".render"); this.question = Translations.T(json.question, context + ".question"); @@ -173,6 +177,18 @@ export default class TagRenderingConfig { throw `${context}: A question is defined, but no mappings nor freeform (key) are. The question is ${this.question.txt} at ${context}` } + if (this.id === "questions" && this.render !== undefined) { + for (const ln in this.render.translations) { + const txt :string = this.render.translations[ln] + if(txt.indexOf("{questions}") >= 0){ + continue + } + throw `${context}: The rendering for language ${ln} does not contain {questions}. This is a bug, as this rendering should include exactly this to trigger those questions to be shown!` + + } + } + + if (this.freeform) { if(this.render === undefined){ throw `${context}: Detected a freeform key without rendering... Key: ${this.freeform.key} in ${context}` @@ -202,16 +218,6 @@ export default class TagRenderingConfig { } } - if (this.id === "questions" && this.render !== undefined) { - for (const ln in this.render.translations) { - const txt :string = this.render.translations[ln] - if(txt.indexOf("{questions}") >= 0){ - continue - } - throw `${context}: The rendering for language ${ln} does not contain {questions}. This is a bug, as this rendering should include exactly this to trigger those questions to be shown!` - - } - } if (this.render && this.question && this.freeform === undefined) { diff --git a/Models/ThemeConfig/WithContextLoader.ts b/Models/ThemeConfig/WithContextLoader.ts index 63c9b14d5..4290d73ef 100644 --- a/Models/ThemeConfig/WithContextLoader.ts +++ b/Models/ThemeConfig/WithContextLoader.ts @@ -48,9 +48,16 @@ export default class WithContextLoader { * A string is interpreted as a name to call */ public ParseTagRenderings( - tagRenderings?: (string | { builtin: string, override: any } | TagRenderingConfigJson)[], - readOnly = false, - prepConfig: ((config: TagRenderingConfigJson) => TagRenderingConfigJson) = undefined + tagRenderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[], + options?:{ + /** + * Throw an error if 'question' is defined + */ + readOnlyMode?: boolean, + requiresId?: boolean + prepConfig?: ((config: TagRenderingConfigJson) => TagRenderingConfigJson) + + } ): TagRenderingConfig[] { if (tagRenderings === undefined) { return []; @@ -58,8 +65,9 @@ export default class WithContextLoader { const context = this._context const renderings: TagRenderingConfig[] = [] - if (prepConfig === undefined) { - prepConfig = c => c + options = options ?? {} + if (options.prepConfig === undefined) { + options.prepConfig = c => c } for (let i = 0; i < tagRenderings.length; i++) { let renderingJson = tagRenderings[i] @@ -89,9 +97,16 @@ export default class WithContextLoader { } - const patchedConfig = prepConfig(renderingJson) + const patchedConfig = options.prepConfig(renderingJson) const tr = new TagRenderingConfig(patchedConfig, `${context}.tagrendering[${i}]`); + if(options.readOnlyMode && tr.question !== undefined){ + throw "A question is defined for "+`${context}.tagrendering[${i}], but this is not allowed at this position - probably because this rendering is an icon, badge or label` + } + if(options.requiresId && tr.id === ""){ + throw `${context}.tagrendering[${i}] has an invalid ID - make sure it is defined and not empty` + } + renderings.push(tr) } diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index 3c1214523..ca6baadd2 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -368,7 +368,7 @@ "freeform": { "key": "description" }, - "id": "Non-editable description {description}" + "id": "Non-editable description" }, { "question": "Is er extra info die je kwijt wil?", @@ -378,7 +378,7 @@ "freeform": { "key": "description:0" }, - "id": "Editable description {description:0}" + "id": "Editable description" }, { "render": { diff --git a/assets/layers/surveillance_camera/surveillance_camera.json b/assets/layers/surveillance_camera/surveillance_camera.json index de5883328..637192b72 100644 --- a/assets/layers/surveillance_camera/surveillance_camera.json +++ b/assets/layers/surveillance_camera/surveillance_camera.json @@ -140,7 +140,7 @@ "hideInAnswer": true } ], - "id": "direction. We don't ask this for a dome on a pole or ceiling as it has a 360° view" + "id": "camera_direction" }, { "freeform": { @@ -264,7 +264,7 @@ "hideInAnswer": true } ], - "id": "Indoor camera? This isn't clear for 'public'-cameras" + "id": "is_indoor" }, { "question": { diff --git a/assets/themes/buurtnatuur/buurtnatuur.json b/assets/themes/buurtnatuur/buurtnatuur.json index cb644deb3..e331bdf60 100644 --- a/assets/themes/buurtnatuur/buurtnatuur.json +++ b/assets/themes/buurtnatuur/buurtnatuur.json @@ -558,7 +558,7 @@ } }, { - "id": "Non-editable description {description}", + "id": "Non-editable description", "render": { "nl": "Extra info: {description}" }, @@ -567,7 +567,7 @@ } }, { - "id": "Editable description {description:0}", + "id": "Editable description", "question": "Is er extra info die je kwijt wil?
De naam van het gebied wordt in de volgende vraag gesteld", "render": { "nl": "Extra info via buurtnatuur.be: {description:0}" diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index 890b9e3ce..613f76d69 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -713,7 +713,7 @@ "it": "

Contiene {_contained_climbing_routes_count} vie

    {_contained_climbing_routes}
" }, "condition": "_contained_climbing_routes~*", - "id": "Containe {_contained_climbing_routes_count} routes" + "id": "Contained_climbing_routes" }, { "render": { diff --git a/langs/layers/de.json b/langs/layers/de.json index 3d3fe4f07..1359693b3 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -2397,20 +2397,6 @@ }, "question": "Um welche Kameratyp handelt se sich?" }, - "Indoor camera? This isn't clear for 'public'-cameras": { - "mappings": { - "0": { - "then": "Diese Kamera befindet sich im Innenraum" - }, - "1": { - "then": "Diese Kamera befindet sich im Freien" - }, - "2": { - "then": "Diese Kamera ist möglicherweise im Freien" - } - }, - "question": "Handelt es sich bei dem von dieser Kamera überwachten öffentlichen Raum um einen Innen- oder Außenbereich?" - }, "Level": { "question": "Auf welcher Ebene befindet sich diese Kamera?", "render": "Befindet sich auf Ebene {level}" @@ -2472,8 +2458,22 @@ "question": "Wie ist diese Kamera montiert?", "render": "Montageart: {camera:mount}" }, - "direction. We don't ask this for a dome on a pole or ceiling as it has a 360° view": { + "camera_direction": { "question": "In welche Himmelsrichtung ist diese Kamera ausgerichtet?" + }, + "is_indoor": { + "mappings": { + "0": { + "then": "Diese Kamera befindet sich im Innenraum" + }, + "1": { + "then": "Diese Kamera befindet sich im Freien" + }, + "2": { + "then": "Diese Kamera ist möglicherweise im Freien" + } + }, + "question": "Handelt es sich bei dem von dieser Kamera überwachten öffentlichen Raum um einen Innen- oder Außenbereich?" } }, "title": { diff --git a/langs/layers/en.json b/langs/layers/en.json index b410872c0..096e4a99e 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -3656,20 +3656,6 @@ }, "question": "What kind of camera is this?" }, - "Indoor camera? This isn't clear for 'public'-cameras": { - "mappings": { - "0": { - "then": "This camera is located indoors" - }, - "1": { - "then": "This camera is located outdoors" - }, - "2": { - "then": "This camera is probably located outdoors" - } - }, - "question": "Is the public space surveilled by this camera an indoor or outdoor space?" - }, "Level": { "question": "On which level is this camera located?", "render": "Located on level {level}" @@ -3731,7 +3717,7 @@ "question": "How is this camera placed?", "render": "Mounting method: {camera:mount}" }, - "direction. We don't ask this for a dome on a pole or ceiling as it has a 360° view": { + "camera_direction": { "mappings": { "0": { "then": "Films to a compass heading of {direction}" @@ -3739,6 +3725,20 @@ }, "question": "In which geographical direction does this camera film?", "render": "Films to a compass heading of {camera:direction}" + }, + "is_indoor": { + "mappings": { + "0": { + "then": "This camera is located indoors" + }, + "1": { + "then": "This camera is located outdoors" + }, + "2": { + "then": "This camera is probably located outdoors" + } + }, + "question": "Is the public space surveilled by this camera an indoor or outdoor space?" } }, "title": { diff --git a/langs/layers/fr.json b/langs/layers/fr.json index 94674cfc5..1f6ea34ce 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -1592,20 +1592,6 @@ }, "question": "Quel genre de caméra est-ce ?" }, - "Indoor camera? This isn't clear for 'public'-cameras": { - "mappings": { - "0": { - "then": "Cette caméra est située à l'intérieur" - }, - "1": { - "then": "Cette caméra est située à l'extérieur" - }, - "2": { - "then": "Cette caméra est probablement située à l'extérieur" - } - }, - "question": "L'espace public surveillé par cette caméra est-il un espace intérieur ou extérieur ?" - }, "Level": { "question": "À quel niveau se trouve cette caméra ?", "render": "Situé au niveau {level}" @@ -1667,7 +1653,7 @@ "question": "Comment cette caméra est-elle placée ?", "render": "Méthode de montage : {camera:mount}" }, - "direction. We don't ask this for a dome on a pole or ceiling as it has a 360° view": { + "camera_direction": { "mappings": { "0": { "then": "Filme dans une direction {direction}" @@ -1675,6 +1661,20 @@ }, "question": "Dans quelle direction géographique cette caméra filme-t-elle ?", "render": "Filme dans une direction {camera:direction}" + }, + "is_indoor": { + "mappings": { + "0": { + "then": "Cette caméra est située à l'intérieur" + }, + "1": { + "then": "Cette caméra est située à l'extérieur" + }, + "2": { + "then": "Cette caméra est probablement située à l'extérieur" + } + }, + "question": "L'espace public surveillé par cette caméra est-il un espace intérieur ou extérieur ?" } }, "title": { diff --git a/langs/layers/it.json b/langs/layers/it.json index 656edbad7..2f034ce89 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -1477,20 +1477,6 @@ }, "question": "Di che tipo di videocamera si tratta?" }, - "Indoor camera? This isn't clear for 'public'-cameras": { - "mappings": { - "0": { - "then": "Questa videocamera si trova al chiuso" - }, - "1": { - "then": "Questa videocamera si trova all'aperto" - }, - "2": { - "then": "Questa videocamera si trova probabilmente all'esterno" - } - }, - "question": "Lo spazio pubblico sorvegliato da questa videocamera è all'aperto o al chiuso?" - }, "Level": { "question": "A che piano si trova questa videocamera?", "render": "Si trova al piano {level}" @@ -1552,7 +1538,7 @@ "question": "Com'è posizionata questa telecamera?", "render": "Metodo di montaggio: {camera:mount}" }, - "direction. We don't ask this for a dome on a pole or ceiling as it has a 360° view": { + "camera_direction": { "mappings": { "0": { "then": "Punta in direzione {direction}" @@ -1560,6 +1546,20 @@ }, "question": "In quale direzione geografica punta questa videocamera?", "render": "Punta in direzione {camera:direction}" + }, + "is_indoor": { + "mappings": { + "0": { + "then": "Questa videocamera si trova al chiuso" + }, + "1": { + "then": "Questa videocamera si trova all'aperto" + }, + "2": { + "then": "Questa videocamera si trova probabilmente all'esterno" + } + }, + "question": "Lo spazio pubblico sorvegliato da questa videocamera è all'aperto o al chiuso?" } }, "title": { diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 4e9920bad..14f425ddf 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -3149,7 +3149,7 @@ }, "question": "Zijn honden toegelaten in dit gebied?" }, - "Editable description {description:0}": { + "Editable description": { "render": "Extra info: {description:0}" }, "Email": { @@ -3169,7 +3169,7 @@ "question": "Wat is de Nederlandstalige naam van dit gebied?", "render": "Dit gebied heet {name:nl}" }, - "Non-editable description {description}": { + "Non-editable description": { "render": "Extra info: {description}" }, "Operator tag": { @@ -3966,20 +3966,6 @@ }, "question": "Wat voor soort camera is dit?" }, - "Indoor camera? This isn't clear for 'public'-cameras": { - "mappings": { - "0": { - "then": "Deze camera bevindt zich binnen" - }, - "1": { - "then": "Deze camera bevindt zich buiten" - }, - "2": { - "then": "Deze camera bevindt zich waarschijnlijk buiten" - } - }, - "question": "Bevindt de bewaakte publieke ruimte camera zich binnen of buiten?" - }, "Level": { "question": "Op welke verdieping bevindt deze camera zich?", "render": "Bevindt zich op verdieping {level}" @@ -4041,7 +4027,7 @@ "question": "Hoe is deze camera geplaatst?", "render": "Montage: {camera:mount}" }, - "direction. We don't ask this for a dome on a pole or ceiling as it has a 360° view": { + "camera_direction": { "mappings": { "0": { "then": "Filmt in kompasrichting {direction}" @@ -4049,6 +4035,20 @@ }, "question": "In welke geografische richting filmt deze camera?", "render": "Filmt in kompasrichting {camera:direction}" + }, + "is_indoor": { + "mappings": { + "0": { + "then": "Deze camera bevindt zich binnen" + }, + "1": { + "then": "Deze camera bevindt zich buiten" + }, + "2": { + "then": "Deze camera bevindt zich waarschijnlijk buiten" + } + }, + "question": "Bevindt de bewaakte publieke ruimte camera zich binnen of buiten?" } }, "title": { diff --git a/langs/layers/ru.json b/langs/layers/ru.json index 7935e67ef..447b79dca 100644 --- a/langs/layers/ru.json +++ b/langs/layers/ru.json @@ -1219,7 +1219,10 @@ }, "question": "Какая это камера?" }, - "Indoor camera? This isn't clear for 'public'-cameras": { + "camera:mount": { + "question": "Как расположена эта камера?" + }, + "is_indoor": { "mappings": { "1": { "then": "Эта камера расположена снаружи" @@ -1228,9 +1231,6 @@ "then": "Возможно, эта камера расположена снаружи" } } - }, - "camera:mount": { - "question": "Как расположена эта камера?" } }, "title": { diff --git a/langs/themes/en.json b/langs/themes/en.json index 1972a62c9..01c86e54e 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -370,15 +370,15 @@ } }, "tagRenderings": { - "Containe {_contained_climbing_routes_count} routes": { - "render": "

Contains {_contained_climbing_routes_count} routes

    {_contained_climbing_routes}
" - }, "Contained routes hist": { "render": "

Difficulties overview

{histogram(_difficulty_hist)}" }, "Contained routes length hist": { "render": "

Length overview

{histogram(_length_hist)}" }, + "Contained_climbing_routes": { + "render": "

Contains {_contained_climbing_routes_count} routes

    {_contained_climbing_routes}
" + }, "Rock type (crag/rock/cliff only)": { "mappings": { "0": { diff --git a/langs/themes/fr.json b/langs/themes/fr.json index 4cf52e17c..98ac69f0a 100644 --- a/langs/themes/fr.json +++ b/langs/themes/fr.json @@ -357,15 +357,15 @@ } }, "tagRenderings": { - "Containe {_contained_climbing_routes_count} routes": { - "render": "

Contient {_contained_climbing_routes_count} voies

    {_contained_climbing_routes}
" - }, "Contained routes hist": { "render": "

Résumé des difficultés

{histogram(_difficulty_hist)}" }, "Contained routes length hist": { "render": "

Résumé de longueur

{histogram(_length_hist)}" }, + "Contained_climbing_routes": { + "render": "

Contient {_contained_climbing_routes_count} voies

    {_contained_climbing_routes}
" + }, "Rock type (crag/rock/cliff only)": { "mappings": { "0": { diff --git a/langs/themes/it.json b/langs/themes/it.json index 331e45984..ece099432 100644 --- a/langs/themes/it.json +++ b/langs/themes/it.json @@ -370,15 +370,15 @@ } }, "tagRenderings": { - "Containe {_contained_climbing_routes_count} routes": { - "render": "

Contiene {_contained_climbing_routes_count} vie

    {_contained_climbing_routes}
" - }, "Contained routes hist": { "render": "

Riassunto delle difficoltà

{histogram(_difficulty_hist)}" }, "Contained routes length hist": { "render": "

Riassunto della lunghezza

{histogram(_length_hist)}" }, + "Contained_climbing_routes": { + "render": "

Contiene {_contained_climbing_routes_count} vie

    {_contained_climbing_routes}
" + }, "Rock type (crag/rock/cliff only)": { "mappings": { "0": {