forked from MapComplete/MapComplete
Add extra validation on custom downloaded themes
This commit is contained in:
parent
818cd62abc
commit
6f9199f1ad
3 changed files with 45 additions and 23 deletions
|
@ -1,23 +1,24 @@
|
||||||
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
|
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
|
||||||
import { QueryParameters } from "./Web/QueryParameters"
|
import {QueryParameters} from "./Web/QueryParameters"
|
||||||
import { AllKnownLayouts } from "../Customizations/AllKnownLayouts"
|
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"
|
||||||
import { FixedUiElement } from "../UI/Base/FixedUiElement"
|
import {FixedUiElement} from "../UI/Base/FixedUiElement"
|
||||||
import { Utils } from "../Utils"
|
import {Utils} from "../Utils"
|
||||||
import Combine from "../UI/Base/Combine"
|
import Combine from "../UI/Base/Combine"
|
||||||
import { SubtleButton } from "../UI/Base/SubtleButton"
|
import {SubtleButton} from "../UI/Base/SubtleButton"
|
||||||
import BaseUIElement from "../UI/BaseUIElement"
|
import BaseUIElement from "../UI/BaseUIElement"
|
||||||
import { UIEventSource } from "./UIEventSource"
|
import {UIEventSource} from "./UIEventSource"
|
||||||
import { LocalStorageSource } from "./Web/LocalStorageSource"
|
import {LocalStorageSource} from "./Web/LocalStorageSource"
|
||||||
import LZString from "lz-string"
|
import LZString from "lz-string"
|
||||||
import { FixLegacyTheme } from "../Models/ThemeConfig/Conversion/LegacyJsonConvert"
|
import {FixLegacyTheme} from "../Models/ThemeConfig/Conversion/LegacyJsonConvert"
|
||||||
import { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson"
|
import {LayerConfigJson} from "../Models/ThemeConfig/Json/LayerConfigJson"
|
||||||
import SharedTagRenderings from "../Customizations/SharedTagRenderings"
|
import SharedTagRenderings from "../Customizations/SharedTagRenderings"
|
||||||
import * as known_layers from "../assets/generated/known_layers.json"
|
import * as known_layers from "../assets/generated/known_layers.json"
|
||||||
import { PrepareTheme } from "../Models/ThemeConfig/Conversion/PrepareTheme"
|
import {PrepareTheme} from "../Models/ThemeConfig/Conversion/PrepareTheme"
|
||||||
import * as licenses from "../assets/generated/license_info.json"
|
import * as licenses from "../assets/generated/license_info.json"
|
||||||
import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"
|
import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig"
|
||||||
import { FixImages } from "../Models/ThemeConfig/Conversion/FixImages"
|
import {FixImages} from "../Models/ThemeConfig/Conversion/FixImages"
|
||||||
import Svg from "../Svg"
|
import Svg from "../Svg"
|
||||||
|
import {DoesImageExist, PrevalidateTheme, ValidateThemeAndLayers} from "../Models/ThemeConfig/Conversion/Validation";
|
||||||
|
|
||||||
export default class DetermineLayout {
|
export default class DetermineLayout {
|
||||||
private static readonly _knownImages = new Set(Array.from(licenses).map((l) => l.path))
|
private static readonly _knownImages = new Set(Array.from(licenses).map((l) => l.path))
|
||||||
|
@ -129,11 +130,11 @@ export default class DetermineLayout {
|
||||||
}),
|
}),
|
||||||
json !== undefined
|
json !== undefined
|
||||||
? new SubtleButton(Svg.download_svg(), "Download the JSON file").onClick(() => {
|
? new SubtleButton(Svg.download_svg(), "Download the JSON file").onClick(() => {
|
||||||
Utils.offerContentsAsDownloadableFile(
|
Utils.offerContentsAsDownloadableFile(
|
||||||
JSON.stringify(json, null, " "),
|
JSON.stringify(json, null, " "),
|
||||||
"theme_definition.json"
|
"theme_definition.json"
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
: undefined,
|
: undefined,
|
||||||
])
|
])
|
||||||
.SetClass("flex flex-col clickable")
|
.SetClass("flex flex-col clickable")
|
||||||
|
@ -179,6 +180,23 @@ export default class DetermineLayout {
|
||||||
|
|
||||||
json.id = forceId ?? json.id
|
json.id = forceId ?? json.id
|
||||||
|
|
||||||
|
{
|
||||||
|
let {errors} = new PrevalidateTheme().convert(json, "validation")
|
||||||
|
if (errors.length > 0) {
|
||||||
|
throw "Detected errors: " + errors.join("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let {errors} = new ValidateThemeAndLayers(
|
||||||
|
new DoesImageExist(new Set<string>(), _ => true),
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
SharedTagRenderings.SharedTagRendering
|
||||||
|
).convert(json, "validation")
|
||||||
|
if (errors.length > 0) {
|
||||||
|
throw "Detected errors: " + errors.join("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
return new LayoutConfig(json, false, {
|
return new LayoutConfig(json, false, {
|
||||||
definitionRaw: JSON.stringify(raw, null, " "),
|
definitionRaw: JSON.stringify(raw, null, " "),
|
||||||
definedAtUrl: sourceUrl,
|
definedAtUrl: sourceUrl,
|
||||||
|
|
|
@ -147,7 +147,7 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
|
||||||
const warnings = []
|
const warnings = []
|
||||||
const information = []
|
const information = []
|
||||||
|
|
||||||
const theme = new LayoutConfig(json, true)
|
const theme = new LayoutConfig(json, this._isBuiltin)
|
||||||
|
|
||||||
{
|
{
|
||||||
// Legacy format checks
|
// Legacy format checks
|
||||||
|
@ -168,7 +168,7 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
if(this._isBuiltin) {
|
||||||
// Check images: are they local, are the licenses there, is the theme icon square, ...
|
// Check images: are they local, are the licenses there, is the theme icon square, ...
|
||||||
const images = new ExtractImages(
|
const images = new ExtractImages(
|
||||||
this._isBuiltin,
|
this._isBuiltin,
|
||||||
|
@ -224,6 +224,8 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if(this._isBuiltin){
|
||||||
|
|
||||||
if (theme.id !== theme.id.toLowerCase()) {
|
if (theme.id !== theme.id.toLowerCase()) {
|
||||||
errors.push("Theme ids should be in lowercase, but it is " + theme.id)
|
errors.push("Theme ids should be in lowercase, but it is " + theme.id)
|
||||||
}
|
}
|
||||||
|
@ -250,6 +252,7 @@ class ValidateTheme extends DesugaringStep<LayoutConfigJson> {
|
||||||
warnings,
|
warnings,
|
||||||
information
|
information
|
||||||
)
|
)
|
||||||
|
}
|
||||||
const dups = Utils.Dupiclates(json.layers.map((layer) => layer["id"]))
|
const dups = Utils.Dupiclates(json.layers.map((layer) => layer["id"]))
|
||||||
if (dups.length > 0) {
|
if (dups.length > 0) {
|
||||||
errors.push(
|
errors.push(
|
||||||
|
@ -298,7 +301,7 @@ export class ValidateThemeAndLayers extends Fuse<LayoutConfigJson> {
|
||||||
super(
|
super(
|
||||||
"Validates a theme and the contained layers",
|
"Validates a theme and the contained layers",
|
||||||
new ValidateTheme(doesImageExist, path, isBuiltin, sharedTagRenderings),
|
new ValidateTheme(doesImageExist, path, isBuiltin, sharedTagRenderings),
|
||||||
new On("layers", new Each(new ValidateLayer(undefined, false, doesImageExist)))
|
new On("layers", new Each(new ValidateLayer(undefined, isBuiltin, doesImageExist)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -700,7 +703,7 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
{
|
if(this._isBuiltin) {
|
||||||
// Some checks for legacy elements
|
// Some checks for legacy elements
|
||||||
|
|
||||||
if (json["overpassTags"] !== undefined) {
|
if (json["overpassTags"] !== undefined) {
|
||||||
|
@ -747,7 +750,7 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
warnings.push(context + " has a tagRendering as `isShown`")
|
warnings.push(context + " has a tagRendering as `isShown`")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
if(this._isBuiltin) {
|
||||||
// Check location of layer file
|
// Check location of layer file
|
||||||
const expected: string = `assets/layers/${json.id}/${json.id}.json`
|
const expected: string = `assets/layers/${json.id}/${json.id}.json`
|
||||||
if (this._path != undefined && this._path.indexOf(expected) < 0) {
|
if (this._path != undefined && this._path.indexOf(expected) < 0) {
|
||||||
|
@ -795,6 +798,7 @@ export class ValidateLayer extends DesugaringStep<LayerConfigJson> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.tagRenderings !== undefined) {
|
if (json.tagRenderings !== undefined) {
|
||||||
const r = new On(
|
const r = new On(
|
||||||
"tagRenderings",
|
"tagRenderings",
|
||||||
|
|
|
@ -122,7 +122,7 @@ export default class ScriptUtils {
|
||||||
return root.svg
|
return root.svg
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async ReadSvgSync(path: string, callback: (svg: any) => void): Promise<any> {
|
public static ReadSvgSync(path: string, callback: (svg: any) => void): any {
|
||||||
xml2js.parseString(readFileSync(path, "UTF8"), { async: false }, (err, root) => {
|
xml2js.parseString(readFileSync(path, "UTF8"), { async: false }, (err, root) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err
|
throw err
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue