Extension:CommunityConfiguration/Technical documentation
Getting started
editRegistering a new provider
editCommunity configuration providers are a formal description of how a set of configuration options should be handled by the extension. They consist of three main component declarations: store, validator and an optional form. The following example shows how to add a provider using extension.json attributes:
"attributes": {
"CommunityConfiguration": {
"Providers": {
"MyProvider": {
"store": {
"type": "wikipage",
"args": [
"MediaWiki:ExampleConfig.json"
]
},
"validator": {
"type": "jsonschema",
"args": [
"CommunityConfigurationExample\\Schemas\\ExampleSchema"
]
},
"type": "mw-config"
}
}
}
},
There are other mechanisms for registering a new configuration provider, through MW Configuration setting $wgCommunityConfigurationProviders
or using the CommunityConfigurationProvider_initListHook
hook, CommunityConfigurationProvider_initListHook.
Creating a JSON schema with PHP
editThe extensions provides a JsonSchema interface to facilitate creating JSON schema document representations with a PHP class. This also restricts the JSON schema features available to those Community Configuration can handle. Below a minimal example of an schema class:
<?php
namespace CommunityConfigurationExample\Schemas;
use MediaWiki\Extension\CommunityConfiguration\Schema\JsonSchema;
class ExampleSchema implements JsonSchema {
public const ExampleConfigOption = [
self::TYPE => self::TYPE_STRING
];
}
Public class properties are collected as top level keys of an object. At this stage, Community Configuration can only work with type object for the top level data structure. Hence there's no support for top level type array. The ExampleSchema class would be expanded to the following JSON schema in json format:
{
"$schema": "https://json-schema.org/draft-04/schema#"
"$id": "CommunityConfigurationExample/Schemas/ExampleSchema/1.0.0",
"type": "object",
"properties": {
"ExampleConfigOption": {
"type": "string"
}
},
"additionalProperties": false
}
Creating a new config page
editThe configuration data for a given set of options under a provider is stored as wiki json pages in the MediaWiki namespace. Whenever you create a new provider using the "type": "wikipage" for its store, you'll need to create the page on your target wiki and add a valid configuration for the first time. This action requires some privileged rights, currently, any of 'administrator', 'interface administrator' (see more in docs/CC-user-rights-tbd).
This process is tedious and not ideal, further improvements are being discussed in task T351517
Configuration providers
editCommunity configuration providers are a formal description of how a set of configuration options should be handled by the extension. Each provider forms a group of configuration options that are treated equally: loaded from the same place, presented on the same configuration form and similar. For each provider, several aspects need to be declared:
- type, which defines what access pattern should be used to read the configuration from the provider,
- store, which defines where the configuration data is stored and which MediaWiki users should have the permissions to modify it
- validator, which defines the requirements configuration data needs to meet to be considered valid
- options (optional), to modify certain aspects of what providers normally does (for example, the
excludeFromUI
option causes the provider to be not included in the dashboard atSpecial:CommunityConfiguration
)
This is the full description of what a provider can have configured:
"attributes": {
"CommunityConfiguration": {
"Providers": {
"providerId": {
"type": "...",
"store": {
"type": "...",
"args": [
// ...
]
},
"validator": {
"type": "...",
"args": [
// ...
]
},
"options": {
// ...
}
}
}
}
}
Registering a provider
editProviders can be registered in three ways:
- via the
attributes
section inextension.json
, - via the
$wgCommunityConfigurationProviders
MediaWiki configuration variable, - via the CommunityConfigurationProvider_initListHook hook
We recommend using the first solution if possible. If a provider needs to be registered conditionally, it can be unset from the CommunityConfigurationProvider_initListHook hook.
Provider types
editProvider types define the class used for communication among the store, the validator and the caller. Most importantly, the exact pattern to use for reading/storing configuration data from the provider depends on the type of the provider.
The following provider types are supported by CommunityConfiguration:
data
: configuration data are treated as a blob; with this type, CommunityConfiguration only supports fetch/store operations at the blob level (ie. fetching/storing the configuration data in full)mw-config
: configuration data are interpreted as representing MediaWiki configuration options (with each option having its own top-level key in the JSON); with this type, CommunityConfiguration allows individual configuration options to be fetched individually
Extensions can define additional provider types via the attributes/CommunityConfiguration/ProviderClasses
key in extension.json
, see the declaration below. Any class used for a provider type needs to implement the IConfigurationProvider
interface.
"attributes": {
"CommunityConfiguration": {
"ProviderClasses": {
"GrowthSuggestedEdits": {
// ObjectFactory specification
}
}
}
}
Stores
editStore defines where the configuration data is stored. The following stores are supported:
wikipage
: MediaWiki wikipages are used as the storage backend, with the standard MediaWiki permission system used to guard access; configuration is stored to the page specified as the first argumentstatic
: read-only provider that always returns its first argument as the configuration (this is mostly useful for testing purposes)
Validators
editValidators are used to ensure configuration meets defined criteria before being returned. CommunityConfiguration guarantees the validator passes on all reads and writes that happen via the CommunityConfiguration-defined interfaces. If the store allows modifications to happen outside of CommunityConfiguration[1] the validator may or may not run and its result may or may not block the write. CommunityConfiguration attempts to enforce rules imposed by the validator for known external writes[2], but this is done on best-efforts basis.
The following validators are supported:
jsonschema
: configuration is validated against a defined PHP schema; for more details, see #PHP schemas[3]noop
: special validator to disable all validation on this provider
New validators can be defined via the $wgCommunityConfigurationValidators
configuration variable, or added directly to CommunityConfiguration. All validators need to implement the IValidator
interface.
Options
editThe extension supports the following options for every provider:
excludeFromUI
(defaults tofalse
): if set totrue
, the provider is not displayed in the dashboard at Special:CommunityConfiguration,editorCapability
(defaults togeneric-editor
): value defines the editor capability that is responsible for rendering the editor users should see when going toSpecial:CommunityConfiguration/PROVIDER_ID
; the default editor capability renders an editing form based on the PHP schema associated with the provider
PHP schemas
editCommunityConfiguration is designed to work with PHP files as the source for defining json-schema compliant documents. This approach is also used in other MediaWiki software components such as MainConfigSchema. The support for PHP schemas is built ad-hoc in the extension and has limitations compared to the full json-schema specification.
Limitations
edit- Limited support for json-schema version draft-04
- Root schema must be of
"type": "object"
- No support for multiple types in any schema definition
additionalProperties
is set tofalse
for root properties
JSON-schema vocabulary
editThe following json-schema keywords are supported:
- additionalProperties
- default
- properties
- type
- items
- enum
- $ref: details are described in #References.
Extended vocabulary
edit- control
Building the schema
editInternally, the CommunityConfiguration extension compiles each of the PHP files into an actual JSON schema (represented as PHP arrays). By default, the compiled schema is used in several other places:
- the JSON validation library[4] to validate the configuration against the schema
- the frontend, to generate the default editing form
This is the default behaviour, which can be adjusted as-needed. Validation library can be changed by setting a different validator. Editing form can be changed by setting a different editor capability.
MediaWiki Core is ultimately responsible for building the schema. CommunityConfiguration triggers the build via JsonSchemaBuilder::getRootSchema
, which internally calls ReflectionSchemaSource::loadAsSchema
from Core.
References
editA schema might choose to reference a different schema using the $ref
keyword. References are identified via a PHP associative array of the following format: [ 'class' => CLASSNAME, 'field' => 'Constant' ]
. CLASSNAME
is a fully-qualified name of a PHP class, which must meet the requirements for a PHP schema. Constant
is a name of a PHP constant within that class, which is the referenced subschema. It is currently not possible to reference a full class.
During the schema building process, ReflectionSchemaSource
inlines all references. The result is a flat JSON schema (with all references replaced with their actual content). Using the built schema, it is impossible to determine whether references were used or not.
Note the behaviour of CommunityConfiguration is different than the standard JSON schema references. Normally, JSON Schema's $ref
keyword is based on JSON Pointer URIs, and users access the subschema via the pointer URI. CommunityConfiguration inlines all references before making use of the schema, which is done to make processing the schema a bit easier.
Example
editWith the following declarations:
class ExampleSchema extends JsonSchema {
public const ExamplePageTitle = [
self::REF => [
'class' => MediaWikiDefinitions::class, 'field' => 'PageTitle'
]
];
}
class MediaWikiDefinitions extends JsonSchema {
public const PageTitle = [
self::TYPE => self::TYPE_STRING,
self::DEFAULT => '',
];
}
parsing ExampleSchema
results in the following result:
[
"$schema" => "https://json-schema.org/draft-04/schema#",
"$id" => "CommunityConfigurationExample/Schemas/ExampleSchema",
"additionalProperties" => false,
"type" => "object",
"properties" => [
"ExamplePageTitle" => [
"type" => "string",
"default" => "",
],
],
]
If needed, the parsing can be triggered via a shell.php session using \MediaWiki\MediaWikiServices::getInstance()->get('CommunityConfiguration.ProviderFactory')->newProvider('ProviderName')->getValidator()->getSchemaBuilder()->getRootSchema()
(replacing ProviderName
with the name of the respective provider).
Versioning
editSchema definitions need to change occasionally due to new product or technical requirements. CommunityConfiguration provides a way for developers to migrate between schema versions. All schemas extending the JsonSchema
class have a public const
VERSION = '1.0.0'
that can be override for bumping the schema version, eg: public const
VERSION = '1.0.1'
. Learn more about migrations in the section below.
Migrations
editIn order for CommunityConfiguration to ease with a schema version migration a convenience maintenance script CommunityConfiguration/maintenance/migrateConfig.php
is provided. Find the steps to prepare for a migration below:
Backup the current schema
editIn order to access older schema versions, create a copy of the current schema version in some directory in your extension. The schema backup file name should contain its version number suffixed with underscore separators and the class
name should also be updated to avoid duplicating the already declared. For example:
Schemas ├── Migrations │ └── MySchema_1_0_0.php └── MySchema.php
Link the older schema to the new one using: public const SCHEMA_NEXT_VERSION = '2.0.0';
Bump the schema version
editOnce the new schema version definitions have been added, removed or changed, set a higher version number in the schema file, MySchema.php
. Link the new schema version to the older one using SCHEMA_PREVIOUS_VERSION
. For example:
class MySchema extends JsonSchema {
public const VERSION = '2.0.0';
public const SCHEMA_PREVIOUS_VERSION = '1.0.0';
public const SCHEMA_CONVERTER = MySchemaConverter_2_0_0::class;
// Add, remove or change definitions
public const NewDefinition = [
self::TYPE => self::TYPE_STRING,
self::DEFAULT => '',
]
}
Create a converter
editIn order to be able to transform some configuration data format from one schema version to another, the extension provides conventional methods in the ISchemaConverter
interface. Create a PHP class implementing such interface and suffixing the new version number on its file name, for example:
Schemas ├── Converters │ └── MySchemaConverter_2_0_0.php ├── Migrations │ └── MySchema_1_0_0.php └── MySchema.php
Implement interface methods upgradeFromOlder
and downgradeFromNewer
, for example:
class MySchemaConverter_2_0_0 implements ISchemaConverter {
public function upgradeFromOlder( stdClass $data ): stdClass {
$data->NewDefinition = '';
return $data;
}
public function downgradeFromNewer( stdClass $data ): stdClass {
unset( $data->NewDefinition );
return $data;
}
}
Run the migration
editIn order to apply the migration to the existing config use migrateConfig
maintenance script in the extension pointing to the provider using the target schema.
> php extensions/CommunityConfiguration/maintenance/migrateConfig.php MyProvider
Glossary
edit- schema: polysemic, it can refer to a full configuration specification in a PHP file, eg: MySchema.php or to the specification of a property at any level in the schema, eg:
{ "type": "string" }
- subschema: used to disambiguate schema, refers to the specification of a property at any level in the schema as opposed to the full schema.
- root schema: used to disambiguate schema, refers to the top level specification of a full schema. In CommunityConfiguration this is restricted to be
"type": "object"
, see [Extension:CommunityConfiguration/Technical_documentation#tbd].
- property: refers to the schema defined under the key
"properties"
in any schema.
- root property: refers to the schema defined under the key
"properties"
in the root schema.
References
edit- ↑ For example, this affects the
wikipage
store, where admins can go to the underlying wikipage and edit it directly. - ↑ For the
wikipage
backend, CommunityConfiguration implements the JsonValidateSave hook to prevent invalid saves. - ↑ In case you are wondering about the "JSON schema" vs "PHP schema" inconsistency, CommunityConfiguration internally compiles PHP schemas into a JSON schema, which is then used for validation.
- ↑ As of July 2024, jsonrainbow/json-schema is used.