Skip to content

Add on-wiki JSON config page for Leaflet layer settings - #925

Merged
JeroenDeDauw merged 2 commits into
masterfrom
leaflet-wiki-config
Jul 19, 2026
Merged

Add on-wiki JSON config page for Leaflet layer settings#925
JeroenDeDauw merged 2 commits into
masterfrom
leaflet-wiki-config

Conversation

@JeroenDeDauw

@JeroenDeDauw JeroenDeDauw commented Jul 19, 2026

Copy link
Copy Markdown
Member

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.

AI-authored — Claude Code, Opus 4.8 (max); detailed brief from a Fable session (relaying @JeroenDeDauw), no human revisions; diff not yet human-reviewed but AI self-reviewed with findings addressed; full PHPUnit suite green locally (370 tests) with the new tests mutation-verified, and CI green.

Production notes

Design and the implementation brief by Fable 5 (max); implemented end to end by Opus 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.

JeroenDeDauw and others added 2 commits July 19, 2026 23:03
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
JeroenDeDauw marked this pull request as ready for review July 19, 2026 21:40
@JeroenDeDauw
JeroenDeDauw merged commit 81b0ff5 into master Jul 19, 2026
15 of 16 checks passed
@JeroenDeDauw
JeroenDeDauw deleted the leaflet-wiki-config branch July 19, 2026 21:40
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add on-wiki JSON configuration page for Leaflet layer settings

1 participant