Skip to content

Commit 362538f

Browse files
JeroenDeDauwclaude
andcommitted
Document the MediaWiki:Maps config page and preload an example
Adds documentation affordances to the `MediaWiki:Maps` on-wiki configuration page. Editing the page suppresses the default MediaWiki-namespace intro and frames the JSON editor with a one-line pointer to the on-page reference and the external documentation, and the generated reference below the editor. Viewing the page trims the rendered output to the JSON table and adds the same pointer and reference. Creating the page preloads a small valid example: two general settings, a coordinate setting and one custom Leaflet layer. A test pins that it passes the config validator, so a schema change cannot leave the preload invalid. The reference is generated from `ConfigSchema`, never hand-written, so it cannot drift from the settings actually exposed. Per group it renders a table of the page key, the accepted value (described by the value type itself) and the `LocalSettings.php` setting it overrides; the reference links the documentation once, from the pointer above it, so no per-setting prose is duplicated on the page. All three affordances apply only to the `MediaWiki:Maps` title and only when `$egMapsEnableInWikiConfig` is true; with the kill-switch off the page is a plain wikitext page, consistent with the existing config hooks. Considered, omitted: * A dedicated ResourceLoader styles module: the reference reuses the core `wikitable` class. * Per-setting prose in the reference: semantics live in the external documentation, found via the setting name in each row. * Translated group headings: the headings are the literal JSON keys an administrator types, shown verbatim. * An intro paragraph in the reference: the pointer above it already links the documentation. * A default-value column: deferred; defaults for the nested types are verbose, and the preloaded example already shows concrete starting values. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent df6d96f commit 362538f

36 files changed

Lines changed: 683 additions & 5 deletions

RELEASE-NOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ different releases and which versions of PHP and MediaWiki they support, see the
88
Released on TBD.
99

1010
* Added an on-wiki JSON configuration page at `MediaWiki:Maps` exposing most Maps settings, grouped by service and topic (general map, coordinate, geocoding, Semantic MediaWiki, Leaflet and Google Maps options), including the Leaflet custom layer definitions and available layers. It is editable only by administrators and interface administrators, is validated on save, and is combined with `LocalSettings.php` with the wiki page taking precedence. Secrets, script-injection primitives and setup-time settings stay exclusive to `LocalSettings.php`. Set `$egMapsEnableInWikiConfig` to `false` to disable it.
11+
* Editing or viewing the `MediaWiki:Maps` page now shows a configuration reference listing every key, its accepted value and the `LocalSettings.php` setting it overrides, next to a link to the full documentation. Creating the page preloads a small working example.
1112
* Hardened the custom Leaflet layer definitions used by `$egMapsLeafletLayerDefinitions` and the config page (breaking change): the `url` and `errorTileUrl` must be `http(s)` URLs, only an allowlist of Leaflet `options` is kept, and `attribution` is sanitized to plain text and `http(s)` links. Definitions relying on raw HTML attribution or non-allowlisted options need updating.
1213

1314
## Maps 13.1.0

i18n/en.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,5 +287,23 @@
287287
"maps-config-error-invalid-option-url": "The \"$2\" option of layer \"$1\" must be a URL that starts with http:// or https://.",
288288
"maps-config-error-invalid-attribution": "The \"attribution\" option of layer \"$1\" must be text.",
289289
"maps-config-error-invalid-default-list": "\"$1\" must be a list of layer names.",
290-
"maps-config-error-invalid-availability": "\"$1\" must be a map of layer names to true or false."
290+
"maps-config-error-invalid-availability": "\"$1\" must be a map of layer names to true or false.",
291+
"maps-config-type-boolean": "true or false",
292+
"maps-config-type-string": "Text",
293+
"maps-config-type-string-list": "A list of text values",
294+
"maps-config-type-number-map": "Names, each set to a positive number",
295+
"maps-config-type-integer": "A whole number",
296+
"maps-config-type-integer-min": "A whole number, $1 or greater",
297+
"maps-config-type-enum": "One of: $1",
298+
"maps-config-type-enum-list": "A list of any of: $1",
299+
"maps-config-type-dimension": "A size: a number, optionally with one of $1",
300+
"maps-config-type-dimension-auto": "A size: a number, optionally with one of $1, or \"auto\"",
301+
"maps-config-type-language": "A language code, such as \"en\" or \"en-GB\"",
302+
"maps-config-type-layer-definitions": "Named custom Leaflet layers; see the documentation",
303+
"maps-config-type-availability": "Each layer name set to true or false",
304+
"maps-config-docs-heading": "Configuration reference",
305+
"maps-config-docs-pointer": "See the [[#$1|configuration reference]] below and the [$2 full documentation].",
306+
"maps-config-docs-column-key": "Setting",
307+
"maps-config-docs-column-type": "Accepted value",
308+
"maps-config-docs-column-setting": "LocalSettings.php equivalent"
291309
}

i18n/qqq.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,5 +265,23 @@
265265
"maps-config-error-invalid-option-url": "Error shown when a URL option of a custom Leaflet layer is not a valid URL. $1 is the layer name, $2 is the option.",
266266
"maps-config-error-invalid-attribution": "Error shown when the attribution option of a custom Leaflet layer is not text. $1 is the layer name.",
267267
"maps-config-error-invalid-default-list": "Error shown when a default layer list is not a list of strings. $1 is defaultLayers or defaultOverlays.",
268-
"maps-config-error-invalid-availability": "Error shown when a layer availability map is invalid. $1 is availableLayers or availableOverlays."
268+
"maps-config-error-invalid-availability": "Error shown when a layer availability map is invalid. $1 is availableLayers or availableOverlays.",
269+
"maps-config-type-boolean": "Value-type description in the config reference table for a boolean setting.",
270+
"maps-config-type-string": "Value-type description in the config reference table for a free-text setting.",
271+
"maps-config-type-string-list": "Value-type description in the config reference table for a list-of-text setting.",
272+
"maps-config-type-number-map": "Value-type description in the config reference table for the distance units setting.",
273+
"maps-config-type-integer": "Value-type description in the config reference table for a whole-number setting.",
274+
"maps-config-type-integer-min": "Value-type description in the config reference table for a whole-number setting with a minimum. $1 is the minimum.",
275+
"maps-config-type-enum": "Value-type description in the config reference table for a single-choice setting. $1 is the comma-separated list of allowed values.",
276+
"maps-config-type-enum-list": "Value-type description in the config reference table for a multi-choice list setting. $1 is the comma-separated list of allowed values.",
277+
"maps-config-type-dimension": "Value-type description in the config reference table for a map-size setting. $1 is the comma-separated list of allowed units.",
278+
"maps-config-type-dimension-auto": "Value-type description in the config reference table for a map-size setting that also accepts \"auto\". $1 is the comma-separated list of allowed units.",
279+
"maps-config-type-language": "Value-type description in the config reference table for the Google Maps language setting.",
280+
"maps-config-type-layer-definitions": "Value-type description in the config reference table for the custom Leaflet layer definitions setting.",
281+
"maps-config-type-availability": "Value-type description in the config reference table for a layer availability setting.",
282+
"maps-config-docs-heading": "Heading of the configuration reference shown on the MediaWiki:Maps config page. Also the link target of {{msg-mw|maps-config-docs-pointer}}.",
283+
"maps-config-docs-pointer": "One-line pointer shown above the config editor and view. $1 is the anchor of the on-page reference, $2 is the URL of the external documentation.",
284+
"maps-config-docs-column-key": "Column header in the config reference table for the setting key written on the page.",
285+
"maps-config-docs-column-type": "Column header in the config reference table for the accepted value.",
286+
"maps-config-docs-column-setting": "Column header in the config reference table for the LocalSettings.php setting that the page key overrides."
269287
}

src/Config/AvailabilityType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ public function validate( mixed $value, string $location ): array {
1515
return $this->isAvailabilityMap( $value ) ? [] : [ [ 'maps-config-error-invalid-availability', $location ] ];
1616
}
1717

18+
public function describe(): array {
19+
return [ 'maps-config-type-availability' ];
20+
}
21+
1822
private function isAvailabilityMap( mixed $value ): bool {
1923
if ( !is_array( $value ) || ( $value !== [] && array_is_list( $value ) ) ) {
2024
return false;

src/Config/BooleanType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,8 @@ public function validate( mixed $value, string $location ): array {
1010
return is_bool( $value ) ? [] : [ [ 'maps-config-error-invalid-boolean', $location ] ];
1111
}
1212

13+
public function describe(): array {
14+
return [ 'maps-config-type-boolean' ];
15+
}
16+
1317
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
3+
declare( strict_types = 1 );
4+
5+
namespace Maps\Config;
6+
7+
use MediaWiki\Html\Html;
8+
use MessageLocalizer;
9+
10+
/**
11+
* Renders the on-page configuration reference for the MediaWiki:Maps config page from the config
12+
* schema, so it can never drift from the settings that are actually exposed. Per group it renders a
13+
* table of page key, the value shape accepted there, and the LocalSettings.php setting it overrides.
14+
* The value shapes come from each type's own describe(); the setting name is the anchor into the
15+
* external documentation, so no per-setting prose is duplicated here.
16+
*/
17+
class ConfigDocumentationBuilder {
18+
19+
public const ANCHOR = 'maps-config-reference';
20+
21+
private const DOCUMENTATION_URL = 'https://maps.extension.wiki/wiki/Configuration';
22+
23+
public function __construct(
24+
private ConfigSchema $schema,
25+
private MessageLocalizer $messageLocalizer
26+
) {
27+
}
28+
29+
/**
30+
* A one-line pointer to the on-page reference and the external documentation. Rendered to HTML
31+
* so it can be placed directly in the edit form and the view output, neither of which parses
32+
* wikitext.
33+
*/
34+
public function buildPointer(): string {
35+
return Html::rawElement(
36+
'div',
37+
[ 'class' => 'maps-config-docs-pointer' ],
38+
$this->messageLocalizer->msg( 'maps-config-docs-pointer', self::ANCHOR, self::DOCUMENTATION_URL )->parse()
39+
);
40+
}
41+
42+
public function buildReference(): string {
43+
$sections = '';
44+
45+
foreach ( $this->groupedSettings() as $group => $settings ) {
46+
$sections .= $this->renderGroup( (string)$group, $settings );
47+
}
48+
49+
return Html::rawElement(
50+
'div',
51+
[ 'class' => 'maps-config-docs' ],
52+
Html::element(
53+
'h2',
54+
[ 'id' => self::ANCHOR ],
55+
$this->messageLocalizer->msg( 'maps-config-docs-heading' )->text()
56+
) . $sections
57+
);
58+
}
59+
60+
/**
61+
* @return array<string, ConfigSetting[]> Group name to its settings, in schema order.
62+
*/
63+
private function groupedSettings(): array {
64+
$groups = [];
65+
66+
foreach ( $this->schema->getSettings() as $setting ) {
67+
$groups[$setting->group][] = $setting;
68+
}
69+
70+
return $groups;
71+
}
72+
73+
/**
74+
* @param ConfigSetting[] $settings
75+
*/
76+
private function renderGroup( string $group, array $settings ): string {
77+
return Html::rawElement( 'h3', [], Html::element( 'code', [], $group ) )
78+
. $this->renderTable( $settings );
79+
}
80+
81+
/**
82+
* @param ConfigSetting[] $settings
83+
*/
84+
private function renderTable( array $settings ): string {
85+
$rows = $this->renderHeaderRow();
86+
87+
foreach ( $settings as $setting ) {
88+
$rows .= $this->renderRow( $setting );
89+
}
90+
91+
return Html::rawElement( 'table', [ 'class' => 'wikitable' ], $rows );
92+
}
93+
94+
private function renderHeaderRow(): string {
95+
return Html::rawElement(
96+
'tr',
97+
[],
98+
Html::element( 'th', [], $this->messageLocalizer->msg( 'maps-config-docs-column-key' )->text() )
99+
. Html::element( 'th', [], $this->messageLocalizer->msg( 'maps-config-docs-column-type' )->text() )
100+
. Html::element( 'th', [], $this->messageLocalizer->msg( 'maps-config-docs-column-setting' )->text() )
101+
);
102+
}
103+
104+
private function renderRow( ConfigSetting $setting ): string {
105+
return Html::rawElement(
106+
'tr',
107+
[],
108+
Html::rawElement( 'td', [], Html::element( 'code', [], $setting->key ) )
109+
. Html::element( 'td', [], $this->describeType( $setting->type ) )
110+
. Html::rawElement( 'td', [], Html::element( 'code', [], '$' . $setting->settingName ) )
111+
);
112+
}
113+
114+
private function describeType( ConfigType $type ): string {
115+
return $this->messageLocalizer->msg( ...$type->describe() )->text();
116+
}
117+
118+
}

src/Config/ConfigExample.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare( strict_types = 1 );
4+
5+
namespace Maps\Config;
6+
7+
/**
8+
* The worked example preloaded into the MediaWiki:Maps config page when it is first created. It is a
9+
* small, valid subset of the schema showing the nested shape: a couple of general settings, a
10+
* coordinate setting and one custom Leaflet layer definition. A test pins that it passes the
11+
* ConfigValidator, so a schema change can never leave the preload invalid.
12+
*/
13+
class ConfigExample {
14+
15+
public const JSON = <<<'JSON'
16+
{
17+
"general": {
18+
"mapWidth": "100%",
19+
"mapHeight": "500px"
20+
},
21+
"coordinates": {
22+
"notation": "float"
23+
},
24+
"leaflet": {
25+
"layerDefinitions": {
26+
"Historic 1904": {
27+
"url": "https://tiles.example.org/historic1904/{z}/{x}/{y}.png",
28+
"options": {
29+
"attribution": "Historic map tiles",
30+
"maxZoom": 18
31+
}
32+
}
33+
}
34+
}
35+
}
36+
JSON;
37+
38+
}

src/Config/ConfigSchema.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@ public static function newDefault(): self {
6262
$zoomControlStyles = [ 'default', 'small', 'large' ];
6363
// The D modifier keeps $ from matching before a trailing newline, so a value with one is
6464
// rejected rather than reaching the Google Maps API script URL.
65-
$language = new PatternType( '/^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})?$/D', 'maps-config-error-invalid-language' );
65+
$language = new PatternType(
66+
'/^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{2,8})?$/D',
67+
'maps-config-error-invalid-language',
68+
'maps-config-type-language'
69+
);
6670

6771
return new self( [
6872
self::replace( 'general', 'mapWidth', 'egMapsMapWidth', new DimensionType( [ 'px', 'ex', 'em', '%' ], true ) ),

src/Config/ConfigType.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,13 @@ interface ConfigType {
2020
*/
2121
public function validate( mixed $value, string $location ): array;
2222

23+
/**
24+
* Describes the accepted value shape for the on-page configuration reference. The description is
25+
* about the value type, not the individual setting: its semantics live in the external
26+
* documentation, reached via the setting name shown alongside it.
27+
*
28+
* @return array A single message spec, [ messageKey, ...params ].
29+
*/
30+
public function describe(): array;
31+
2332
}

src/Config/DimensionType.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ public function validate( mixed $value, string $location ): array {
2525
return $this->isValid( $value ) ? [] : [ [ 'maps-config-error-invalid-dimension', $location ] ];
2626
}
2727

28+
public function describe(): array {
29+
$units = implode( ', ', $this->units );
30+
31+
if ( $this->allowAuto ) {
32+
return [ 'maps-config-type-dimension-auto', $units ];
33+
}
34+
35+
return [ 'maps-config-type-dimension', $units ];
36+
}
37+
2838
private function isValid( mixed $value ): bool {
2939
if ( is_int( $value ) || is_float( $value ) ) {
3040
return $value >= 0;

0 commit comments

Comments
 (0)