Add on-wiki JSON config page for Leaflet layer settings - #925
Merged
Conversation
Fixes #924 Adds a MediaWiki:Maps JSON config page for the Leaflet layer settings, combined with LocalSettings.php with the wiki page winning. Core enforces the editinterface and editsitejson rights via a forced JSON content model (ContentHandlerDefaultModelFor), and edits are validated on save (EditFilter) with precise, i18n'd errors. The page is read lazily at parse time and falls back to the PHP config when it is missing, invalid or the database is unavailable. A new $egMapsEnableInWikiConfig (default true) disables it. Exposed settings: custom layerDefinitions plus the default and available base layers and overlays. The singular $egMapsLeafletLayer is intentionally omitted as it is unused. The layer-definition contract is hardened for both the PHP and wiki sources through one pipeline (breaking change, hence 14.0.0): url and errorTileUrl must be http(s), only allowlisted Leaflet options are kept, and attribution is sanitized server-side to plain text and http(s) links. Save-time validation is hand-rolled to give precise per-field errors without adding a dependency. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addresses self-review findings: stub the wiki config source in MapsTestFactory so the parser integration tests no longer trigger a real MediaWiki:Maps revision lookup, import Throwable instead of referencing it with a leading backslash, and add a test for clearing the default layers with an empty wiki list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
JeroenDeDauw
marked this pull request as ready for review
July 19, 2026 21:40
This was referenced Jul 19, 2026
JeroenDeDauw
added a commit
that referenced
this pull request
Jul 20, 2026
* Expand the MediaWiki:Maps config page beyond Leaflet layers Fixes #926 Follows-up to #925 Generalizes the MediaWiki:Maps config page (added in #925 for Leaflet layers) to expose most Maps settings, driven by a single declarative schema so that exposing a setting is one schema entry rather than bespoke code. ## Design Maps\Config\ConfigSchema lists every exposed setting as a ConfigSetting: page group + key, the egMaps*/smgQP* setting it overrides, its value type, and its merge strategy. It drives both save-time validation and read-time application: * ConfigValidator walks the schema on save (EditFilter hook), rejecting unknown groups/keys and per-value type errors. * EffectiveSettings is the one effective-settings lookup wrapping the PHP settings with the schema-translated wiki overlay. It reads the page lazily at parse time, memoizes the result, falls back to the PHP settings on any Throwable or when disabled, and ignores wiki values that fail their type validation. All exposed-setting consumption sites read through it instead of $GLOBALS. Value types are small reusable objects: boolean, integer, string, enum, enum list, string list, dimension, positive-number map, pattern, plus the layer-definitions and availability specials. Wiki values replace the PHP value, except layerDefinitions, availableLayers and availableOverlays, which merge per name via a MergeStrategy attribute of those entries. The #925 Leaflet-specific classes were reshaped into this general concept: LeafletConfigValidator -> ConfigValidator, WikiLeafletConfigSource -> WikiPageConfigSource (now reads the whole page), CombiningLeafletConfigLookup -> EffectiveSettings; LeafletConfig/LeafletConfigLookup dissolved into LeafletService reading the effective settings directly. LeafletLayerContract and AttributionSanitizer are unchanged. ## Exposed and excluded Groups: general, coordinates, geocoding, semanticMediaWiki, leaflet, googleMaps (see the README for the full key list). Secrets/API keys, script-injection primitives, the caches and setup-time switches stay PHP-only, as decided in the issue. Two settings from the issue's list are deviated from: * defaultService is excluded. egMapsDefaultService is consumed when MappingServices is constructed and when SMW result-format aliases are registered, both during extension setup before the config page can be read. Routing it would force a wiki read at setup, defeating the lazy design, so it stays PHP-only. * internationalDirectionLabels is excluded. egMapsInternatDirectionLabels is not read anywhere in the code, so exposing it would have no effect. distanceUnit is validated as a plain string rather than against the effective units: an unknown default unit self-heals to the first available unit at runtime and cannot hard-fail. ## Security mapWidth/mapHeight use a strict dimension pattern (they reach inline styles), googleMaps.language a strict pattern (it reaches the Google API URL), and distance unit names are restricted to alphanumerics (they reach a regex and rendered output). Layer definitions keep the #925 hardening unchanged. ## Tests Table-driven unit tests per value type, schema and validator tests, EffectiveSettings merge, fallback and memoization tests, and integration spot checks that a wiki-set value lands end to end. All #925 tests are preserved through the renames. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Reject a trailing newline in the config value patterns The dimension, distance-unit-name and Google Maps language patterns anchored with $, which matches just before a trailing newline. Add the D modifier so a value with a trailing newline is rejected, keeping these strict patterns fully strict before their style, URL and regex sinks. Harmless in practice since the sinks escape or encode, found in review. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Correct README wording about config page key naming Several page keys are deliberately renamed from their PHP settings (e.g. pagesWithMapsCategory for egMapsEnableCategory), so the reference list should not claim the names are the same. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #924
Adds a MediaWiki:Maps JSON config page for the Leaflet layer settings, combined with
LocalSettings.php with the wiki page winning. Core enforces the editinterface and
editsitejson rights via a forced JSON content model (ContentHandlerDefaultModelFor), and
edits are validated on save (EditFilter) with precise, i18n'd errors. The page is read
lazily at parse time and falls back to the PHP config when it is missing, invalid or the
database is unavailable. A new $egMapsEnableInWikiConfig (default true) disables it.
Exposed settings: custom layerDefinitions plus the default and available base layers and
overlays. The singular $egMapsLeafletLayer is intentionally omitted as it is unused.
The layer-definition contract is hardened for both the PHP and wiki sources through one
pipeline (breaking change, hence 14.0.0): url and errorTileUrl must be http(s), only
allowlisted Leaflet options are kept, and attribution is sanitized server-side to plain
text and http(s) links. Save-time validation is hand-rolled to give precise per-field
errors without adding a dependency.
Production notes
Design and the implementation brief by
Fable 5 (max); implemented end to end byOpus 4.8 (max)in a separate session. Full scope delivered (config-page infrastructure plus the default/availability settings). Save-time validation is hand-rolled rather than opis/json-schema to yield precise per-field messages without adding a runtime dependency. A read-only AI review empirically fuzzed the attribution sanitizer against ~65 XSS vectors (no bypass found) and confirmed no config read happens during extension setup.