Manuel:Modèles de pages de contenu
Le ContentHandler introduit dans MediaWiki 1.21 vous permet d'ajouter de nouveaux modèles de contenu qui ne sont pas du wikicode. It makes it possible for wiki pages to be composed of data other than wikitext, and represented in any way — for example: Markdown, reStructuredText, icalendar, or a custom XML format. The display and editing of these content models can be handled in custom ways (e.g. different syntax highlighting, or whole new data-entry forms).
Cette page décrit les étapes à parcourir pour créer un nouveau modèle de contenu dans une extension. It assumes some familiarity with general extension development practices. For a brief summary of the requirements, see the Summary section at the bottom of this page.
A meaningless "Goat" content model will be used for the examples. Vous pouvez aussi examiner l'extension DataPages, qui est une partie de Extension:Examples .
Inscription
D'abord, ajoutez le nom du modèle de contenu de la classe du gestionnaire à votre extension.json :
"ContentHandlers": {
"goat": "MediaWiki\\Extension\\GoatExt\\GoatContentHandler"
}
- The left-hand value here is the name of the content type, it can be any unique string you want, and it lives alongside the five built-in content types: 'wikitext', 'JavaScript', 'CSS', 'plain text', 'JSON'. Cette valeur est exposée aux utilisateurs à des endroits tels que Special:ChangeContentModel et les informations de page.
- The right-hand value is the fully-qualified name of a class that extends ContentHandler.
Ceci nécessite la création de deux nouvelles classes comme \MediaWiki\Extension\GoatExt\GoatContent
et \MediaWiki\Extension\GoatExt\GoatContentHandler
(assurez-vous que leur espace de noms n'est pas dans AutoloadNamespaces).
D'autres informations concernant ces classes sont données ci-dessous.
Constantes optionnelles du modèle de contenu
The 'goat' string above is the content model's ID (generally called $modelId
in code), and is usually also defined as a constant.
These constants are defined for all built-in content models, and lots of documentation refers to the "CONTENT_MODEL_XXX" constants.
Si vous ne les avez pas définis, ceci peut être un peu gênant.
La définition doit être faite via la fonction de rappel (callback) dans extension.json. Par exemple :
Dans extension.json
:
"callback": "MediaWiki\\Extension\\GoatExt\\Hooks::registrationCallback"
Dans includes/Hooks.php
:
namespace MediaWiki\Extension\GoatExt;
class Hooks {
public static function registrationCallback() {
// Must match the name used in the 'ContentHandlers' section of extension.json
define( 'CONTENT_MODEL_GOAT', 'goat' );
}
}
Vous ne devez pas le faire de cette façon, mais simplement utiliser la chaîne de caractères.
Assigner des modèles de contenu aux pages
Pages can have their content type manually changed, but it's useful to have them default to the correct one. Two common ways of doing this are by namespace, and by file extension.
Par espace de noms: Si vous voulez que l'espace de noms entier d'un wiki ait un modèle de contenu par défaut, vous pouvez le définir comme tel dans $1 : If you want an entire wiki namespace to have a default content model, you can define it as such in extension.json:
"namespaces": [
{
"id": 550,
"constant": "NS_GOAT",
"name": "Goat",
"subpages": false,
"content": true,
"defaultcontentmodel": "goat"
},
{
"id": 551,
"constant": "NS_GOAT_TALK",
"name": "Goat_talk",
"subpages": true,
"content": false,
"defaultcontentmodel": "wikitext"
}
]
Note that published extensions should register the namespace IDs they use (550
and 551
above) on the Espaces de noms par défaut des extensions page.
By file extension: If you want to determine the content type by the addition of a quasi-file-type suffix on the wiki page name, you can use the ContentHandlerDefaultModelFor hook. For example:
namespace MediaWiki\Extension\GoatExt;
class Hooks {
public static function onContentHandlerDefaultModelFor( \Title $title, &$model ) {
// Any page title (in any namespace) ending in '.goat'.
$ext = '.goat';
if ( substr( $title->getText(), -strlen( $ext ) ) === $ext ) {
// This is the constant you defined earlier.
$model = CONTENT_MODEL_GOAT;
// If you change the content model, return false.
return false;
}
// If you don't change it, return true.
return true;
}
}
Gestionnaire de contenu
The next thing to define is the GoatContentHandler class, which is where we also specify what format this content type will be stored as (in this case, text). ContentHandlers don't know anything about any particular page content, but determine the general structure and storage of the content.
<?php
namespace MediaWiki\Extension\GoatExt;
class GoatContentHandler extends \ContentHandler {
public function __construct( $modelId = 'goat' ) {
parent::__construct( $modelId, [ CONTENT_FORMAT_TEXT ] );
}
public function serializeContent( \Content $content, $format = null ) {
}
public function unserializeContent( $blob, $format = null ) {
}
public function makeEmptyContent() {
return new GoatContent();
}
public function supportsDirectEditing() {
return true;
}
}
Contenu
The GoatContent
class is the representation of the content's data, and does not know anything about pages, revisions, or how it is stored in the database.
Beside the required seven inherited methods, you can add other public methods are domain-specific; in this case we want to be able to retrieve the goat's name.
<?php
namespace MediaWiki\Extension\GoatExt;
class GoatContent extends \AbstractContent {
public function __construct( $modelId = 'goat' ) {
parent::__construct( $modelId );
}
public function getTextForSearchIndex() {
}
public function getWikitextForTransclusion() {
}
public function getTextForSummary( $maxLength = 250 ) {
}
public function getNativeData() {
}
public function getSize() {
}
public function copy() {
}
public function isCountable( $hasLinks = null ) {
}
public function getName() {
return 'Garry';
}
}
Formulaire de modification
Maintenant que nous avons configuré le squelette, nous allons essayer de modifier une chèvre.
Pour faire ceci, nous créons GoatContentHandler::getActionOverrides()
en spécifiant quelles actions que nous voulons associer aux classes.
To start with, we'll just deal with 'edit' (which corresponds to ?action=edit
in the URL).
public function getActionOverrides() {
return [
'edit' => GoatEditAction::class,
];
}
And we'll create our new GoatEditAction
class, basically the same as the core EditAction
but using our own GoatEditPage
:
<?php
namespace MediaWiki\Extension\GoatExt;
class GoatEditAction extends \EditAction {
public function show() {
$this->useTransactionalTimeLimit();
$editPage = new GoatEditPage( $this->getArticle() );
$editPage->setContextTitle( $this->getTitle() );
$editPage->edit();
}
}
Our new GoatEditPage
class is where the action happens (excuse the pun):
<?php
namespace MediaWiki\Extension\GoatExt;
class GoatEditPage extends \MediaWiki\EditPage\EditPage {
protected function showContentForm() {
$out = $this->context->getOutput();
// Get the data.
$name = $this->getCurrentContent()->getGoatName();
// Create the form.
$nameField = new \OOUI\FieldLayout(
new \OOUI\TextInputWidget( [ 'name' => 'goat_name', 'value' => $name ] ),
[ 'label' => 'Name', 'align' => 'left' ]
);
$out->addHTML( $nameField );
}
}
You should now be able to edit a page and see your form. But when you put data into it, and hit 'preview', you'll see that things are not yet working fully and that you get no output, nor is your submitted text shown again in the form.
So we must override the 'submit' action as well, with a new GoatSubmitAction
class and the addition of 'submit' => GoatSubmitAction::class,
to our GoatContentHandler::getActionOverrides()
method.
Our GoatSubmitAction
class should be the same as that of core, but inheriting from our GoatEditAction
.
Affichage
A content model is responsible for producing any required output for display. This usually involves working with its data and producing HTML in some way, to add to the parser output.
<?php
namespace MediaWiki\Extension\GoatExt;
class GoatContentHandler extends \AbstractContentHandler {
protected function fillParserOutput(
Title $title, $revId, ParserOptions $options, $generateHtml, ParserOutput &$output
) {
// e.g. $output->setText( $html );
}
}
Afficher une description/documentation
Sometimes you may want to display some information or some documentation for a wiki page that has a custom content model such as JSON. There are no system messages to display text above such pages (except for MediaWiki:Clearyourcache displayed above only JavaScript and CSS pages). You may want to see phab:T206395 for further details.
Comparer les versions
The GoatDifferenceEngine
class is the representation of the difference between goat contents.
We override the default generateContentDiffBody
method to generate a diff.
<?php
namespace MediaWiki\Extension\GoatExt;
class GoatDifferenceEngine extends \DifferenceEngine {
public function generateContentDiffBody( Content $old, Content $new ) {
}
}
In order to tell MediaWiki to use our GoatDifferenceEngine
, we overwrite the getDiffEngineClass
in our GoatContentHandler
.
<?php
namespace MediaWiki\Extension\GoatExt;
class GoatContentHandler extends \ContentHandler {
public function getDiffEngineClass() {
return GoatDifferenceEngine::class;
}
}
Résumé
Pour implémenter un nouveau modèle de contenu avec un formulaire d'édition personnalisé, créez ceci :
<?php
namespace MediaWiki\Extension\GoatExt;
class GoatContent extends \AbstractContent {
public function __construct( $modelId = 'goat' ) {
parent::__construct($modelId);
}
public function getTextForSearchIndex() {}
public function getWikitextForTransclusion() {}
public function getTextForSummary( $maxLength = 250 ) {}
public function getNativeData() {}
public function getSize() {}
public function copy() {}
public function isCountable( $hasLinks = null ) {}
}
<?php
namespace MediaWiki\Extension\GoatExt;
class GoatContentHandler extends \ContentHandler {
public function __construct( $modelId = CONTENT_MODEL_GOAT, $formats = ['text/x-goat'] ) {
parent::__construct($modelId, $formats);
}
protected function getContentClass() {}
public function supportsDirectEditing() {}
public function serializeContent( \Content $content, $format = null ) {}
public function unserializeContent( $blob, $format = null ) {}
public function makeEmptyContent() {}
public function getActionOverrides() {}
protected function fillParserOutput( \Title $title, $revId, \ParserOptions $options, $generateHtml, \ParserOutput &$output) {}
}
Checklist
Action | Example | |
---|---|---|
Define a content model id (al. content type) and content model constant | goat CONTENT_MODEL_GOAT
| |
Choose your preferred mechanism of applying a content model: | ||
(a) By namespace. Define namespace constants, ids and names given any of them is not already taken |
NS_GOAT (id: 550, name 'Goat')NS_GOAT_TALK (id: 551, name: 'Goat talk')
| |
(b) By extension in pagename | .goat
| |
(c) Other | through 'Page information' interface (&action=info )
| |
extension.json
| ||
ContentHandlers - register a ContentHandler subclass for the new content model
|
"ContentHandlers": {
"goat": "GoatExt\\GoatContentHandler"
}
|
↑ |
callback - name your class method for callbacks, conventionally in your Hooks file
|
"callback": "GoatExt\\Hooks::registrationCallback"
|
↑ |
Hooks - provide a hook method for ContentHandlerDefaultModelFor
|
"Hooks": {
"ContentHandlerDefaultModelFor": [ "GoatExt\\Hooks::onContentHandlerDefaultModelFor" ]
}
| |
namespaces - define namespaces with the appropriate value for defaultcontentmodel .
For more options, use |
"namespaces": [
{
"id": 550,
"constant": "NS_GOAT",
"name": "Goat",
"subpages": false,
"content": false,
"defaultcontentmodel": "goat"
},
{
"id": 551,
"constant": "NS_GOAT_TALK",
"name": "Goat_talk",
"subpages": true,
"content": false,
"defaultcontentmodel": "wikitext"
}
]
|
↑ |
AutoloadNamespaces - load Content and ContentHandler subclasses in subdirectory
|
"AutoloadNamespaces": {
"GoatExt\\": "includes/"
}
| |
[[Special:MyLanguage/Manual:Extension.json/Schema#AutoloadClasses|]] - load Content and ContentHandler subclasses explicitly
|
"AutoloadClasses": {
"GoatExt\\Hooks": "includes/Hooks.php",
"GoatExt\\GoatContent": "includes/GoatContent.php",
"GoatExt\\GoatContentHandler": "includes/GoatContentHandler.php"
}
| |
Hooks file | GoatExt\\Hooks
| |
Add method for the callback | public static function registrationCallback() {
// Must match the name used in the 'ContentHandlers' section of extension.json
define( 'CONTENT_MODEL_GOAT', 'goat' );
}
| |
Add method for the ContentHandlerDefaultModelFor hook for wiki pages in the intended namespace
|
public static function onContentHandlerDefaultModelFor( \Title $title, &$model ) {
if ( $title->inNamespace( NS_GOAT ) {
$model = CONTENT_MODEL_GOAT;
return false;
}
return true;
}
|
|
Or add method for the ContentHandlerDefaultModelFor hook for wiki pages with the intended file extension
|
public static function onContentHandlerDefaultModelFor( \Title $title, &$model ) {
$ext = '.goat';
if ( substr( $title->getText(), -strlen( $ext ) ) === $ext ) {
$model = CONTENT_MODEL_GOAT;
return false;
}
return true;
}
|
↑ |
Content and ContentHandler subclasses | ||
Add a Content subclass by extending AbstractContent or one of the subclasses available (e.g. TextContent ) | namespace GoatExt;
class GoatContent extends \AbstractContent {
public function __construct( $modelId = 'goat' ) {
parent::__construct( $modelId );
}
public function getTextForSearchIndex() {
}
public function getWikitextForTransclusion() {
}
public function getTextForSummary( $maxLength = 250 ) {
}
public function getNativeData() {
}
public function getSize() {
}
public function copy() {
}
public function isCountable( $hasLinks = null ) {
}
// etc. See documentation for details.
}
|
↑ |
Add a ContentHandler subclass by extending either ContentHandler or one of its subclasses (e.g. TextContentHandler or CodeContentHandler ) | namespace GoatExt;
class GoatContentHandler extends \ContentHandler {
public function __construct( $modelId = 'goat' ) {
parent::__construct( $modelId, [ CONTENT_FORMAT_TEXT ] );
}
public function serializeContent( \Content $content, $format = null ) {
}
public function unserializeContent( $blob, $format = null ) {
}
public function makeEmptyContent() {
return new GoatContent();
}
public function supportsDirectEditing() {
return true;
}
protected function fillParserOutput(
Title $title, $revId, ParserOptions $options, $generateHtml, ParserOutput &$output
) {
// e.g. $output->setText( $html );
}
// etc. See documentation for details
}
|
↑ |
Compare revisions | Override ContentHandler::getDiffEngineClass() and DifferenceEngine::generateContentDiffBody() | ↑ |
Modify associated actions such as 'edit' | Override ContentHandler::getActionOverrides() and EditAction::show() | ↑ |
Voir aussi
- Manual:Hooks/ContentHandlerDefaultModelFor
- Aide:ChangeContentModel - pour la documentation de l'utilisateur final concernant la modification d'un modèle de contenu.
- Extension:Examples - pour un exemple de modèle de contenu personnalisé (mais pas encore n'importe quel formulaire de modification personnalisé).