Extension:JsonConfig

The JsonConfig extension allows other extensions to store their configuration data as a JSON blob in a wiki page.

Installation
The JsonConfig extension allows other extensions to store their configuration data as a JSON blob in a wiki page.

Available Features and Usage Patterns
You may use JsonConfig to store data in a number of ways:
 * A single page configuration, e.g. a number of settings for your extension -- Config:MyExtSettings (Config is the default namespace associated with the JsonConfig extension)
 * A set of pages with similar structure residing in its own namespace, e.g. IP addresses of the known proxies or event log schemas -- Proxy:Opera, Schema:AccountCreation
 * To avoid polluting wiki with additional namespaces, you can use "sub-namespaces", e.g. Config:Proxy:Opera
 * You may provide a content class to do data validation and normalization
 * You may provide a view class to customize HTML generation
 * Data can be stored as "one-per-wiki", "one-per-cluster", or even one per some "family" (different structure of the caching key for shared memcached).
 * Data can be stored in a public or private wiki and accessed with credentials
 * Data can be stored in a separate cluster, and do remote notification when changed

Hello World
Simplest case - a single configuration page without any validation, stored locally on each wiki. We don't even need to have any real code - just add these settings to LocalSettings.php

Adding above settings enables namespace "Config" on the local wiki, but will only allow "Config:MySettings" page to be created, not any other page in that namespace. Any well-formed JSON data will be accepted. To get it in PHP, use TitleValue object:

Multiple configs shared in a cluster
Lets say we decide to store the trusted proxies' IPs as Config:Proxy: pages on the Meta wiki and share it with the cluster.

If instead you want to dedicate a separate namespace to proxies, the parameters would change to:

Lastly, our implementation of the Proxy storage could write a custom content class to handle validation and rendering, and set these settings in the ProxyExt.php file:

// Config server (meta?) should specify

For some cases, an extension may choose to have its own top namespace instead of using a sub-namespace. Here we create namespace called Zero:... and Zero praise:...:

Of course at a certain point you would want a custom content class with its own defaults, validation, and HTML rendering. To set it up, specify a model ID and a class that derives from the \JsonConfig\JCKeyValueContent or \JsonConfig\JCObjContent:

Content class
For instance, the description of the Opera Mini caching proxy page Proxy:Opera could be:

All JSON pages are handled by the content classes, responsible for parsing, validation and visualization. We may choose to have a free-form JSON, in which case we don't actually have to write any code, and let JsonConfig use the default JCContent class for storage. JCContent does not offer any validation except for JSON parsing, but extensions may choose to override validate($data) function to do custom validation, and getHtml for custom rendering. Alternatively, there is a JCObjContent and JCKeyValueContent</tt> classes that offers a number of useful validation primitives.

JCKeyValueContent</tt> treats JSON as a single level key-value storage, with each value being validated by a callback function. The class supports defaults, so the user will not need to check if certain value was given by the user. Page rendering will show JSON with all the defaults as grayed-out values, but will store only the values actually entered by the user. User values that equal defaults are also highlighted in a different color. When saving, the JSON is always reformatted to keep the order of key-values consistent, which makes version diffs easier to view. All unrecognized keys are placed at the end and highlighted.

Data Access
There are several data accessing scenarios: by the code that runs on the same wiki where the data is stored (local), by code that runs on another wiki project but shares all the settings with the storage wiki (cluster, e.g. Wikimedia), and by code that resides somewhere else (JavaScript, another site). The local and cluster scenarios have the benefit of sharing setting and code between the storage and accessor sites.

Local & Cluster Access
$content is an instance of our custom Content class, and could have specific functions to work with the data.

External Access
The stored configuration data may frequently be needed by some external agent such as JavaScript, bot, or some other program. JavaScript could use either JSONP to access needed data, or we could develop a forwarding service if CORS is unavailable. Extension authors may choose to add their own API modules to provide domain-specific information. Lastly, the rvprop=jcddata</tt> Query API parameter would return JSON data as part of the API result, not as a text blob that rvprop=content</tt> would return.

$wgJsonConfigs
This variable defines profiles for each type of configuration pages. $wgJsonConfigs is an associative array of arrays, with each sub-array having zero or more of the following parameters. By default, the string key is treated as the model ID that this profile represents, but in case you need more than one profile for the same model Id, you can override it with the 'model' parameter.

$wgJsonConfigModels
This variable defines which custom content class will handle which Model ID. More than one Model ID may be handled by the same content class. All content classes must derive from \JsonConfig\JCContent class. If the modelID is mapped to null</tt>, the default JCContent class will be used.

Example:

In case when a separate class should do HTML rendering, the value could be specified as an array:

Implementation details
JsonConfig is implemented as two parts - storage/parsing and editing/visualizing. The editor/visualizer is only available when JsonConfig runs on meta wiki, and allows complex presentation of the Config namespace wiki pages. The storage/parsing is available on all wikis, allowing quick access to cache as well as validation and parsing of the cached json blob.

Implemented Features
These features have already been implemented in Zero and/or logging, and might be useful for other extensions:
 * Visualization shows JSON as an easy to view table rather than code, with some extra highlighting. For example if the value is not provided and a default is used, it is shown in gray, or when the value is the same as default, it shows as purple. For example, see this and this.
 * Code Editor simplifies JSON editing
 * Custom Validation performs complex checks such as checking that the value is in the proper format or that a user ID exists.
 * MemCached caching stores json blobs in memcached under custom keys and expiration policies, and resets them on save.
 * Flagged Revisions support allows configurations to be marked as "reviewed" before going into production
 * Localization of most basic interface elements has been done in many languages, and it would reduce translation work if most common messages would be done just once in one place.

Unimplemented Nice-To-Haves
These features would be desirable to more than one type of configs:
 * Schema validator - Validate against JSON Schema, as most extensions might not need complex validation rules, or might want to combine schema plus extra validation.
 * Custom editor - Zero team has been thinking about implementing a more complex editor, possibly based on JSON Schema.
 * API query support - Allow config pages to be returned as regular API results in all formats - json/xml/... instead of text blobs:
 * api.php ? action=query & titles=Config:Proxy:Opera & prop=jsonconfig</tt>
 * Localization - it would be good to be able to show localized descriptions for each configuration key

Suggested Use Cases

 * Wikipedia Zero - this extension is basically a re-factoring of our internal code, so its a natural migration
 * Logging Schemas - again, most of the concepts were originally borrowed from there, hence its a natural user