2013-02-22 18:50:54 +00:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* @file
|
|
|
|
* @ingroup Extensions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Represents the information about a template,
|
|
|
|
* coming from the JSON blob in the <templatedata> tags
|
|
|
|
* on wiki pages.
|
|
|
|
*
|
|
|
|
* @class
|
|
|
|
*/
|
|
|
|
class TemplateDataBlob {
|
2013-04-22 20:02:09 +00:00
|
|
|
|
2013-02-22 18:50:54 +00:00
|
|
|
/**
|
|
|
|
* @var stdClass
|
|
|
|
*/
|
|
|
|
private $data;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var Status: Cache of TemplateInfo::validate
|
|
|
|
*/
|
|
|
|
private $status;
|
|
|
|
|
|
|
|
/**
|
2013-04-22 19:56:28 +00:00
|
|
|
* @param string $json
|
2013-02-22 18:50:54 +00:00
|
|
|
* @return TemplateInfo
|
|
|
|
*/
|
|
|
|
public static function newFromJSON( $json ) {
|
2013-04-22 19:56:28 +00:00
|
|
|
$tdb = new self( json_decode( $json ) );
|
|
|
|
$status = $tdb->parse();
|
2013-02-22 18:50:54 +00:00
|
|
|
|
|
|
|
if ( !$status->isOK() ) {
|
|
|
|
// Don't save invalid data, clear it.
|
2013-04-22 19:56:28 +00:00
|
|
|
$tdb->data = new stdClass();
|
2013-02-22 18:50:54 +00:00
|
|
|
}
|
2013-04-22 19:56:28 +00:00
|
|
|
$tdb->status = $status;
|
|
|
|
return $tdb;
|
2013-02-22 18:50:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse the data, normalise it and validate it.
|
|
|
|
*
|
|
|
|
* See spec.templatedata.json for the expected format of the JSON object.
|
|
|
|
* @return Status
|
|
|
|
*/
|
|
|
|
private function parse() {
|
|
|
|
$data = $this->data;
|
|
|
|
|
|
|
|
if ( $data === null ) {
|
|
|
|
return Status::newFatal( 'templatedata-invalid-parse' );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !is_object( $data ) ) {
|
|
|
|
return Status::newFatal( 'templatedata-invalid-type', 'templatedata', 'object' );
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ( $data as $key => $value ) {
|
|
|
|
if ( !in_array( $key, array( 'params', 'description' ) ) ) {
|
|
|
|
return Status::newFatal( 'templatedata-invalid-unknown', $key );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-30 21:22:25 +00:00
|
|
|
// Root.description
|
2013-02-22 18:50:54 +00:00
|
|
|
if ( isset( $data->description ) ) {
|
2013-04-16 14:18:10 +00:00
|
|
|
if ( !is_object( $data->description ) && !is_string( $data->description ) ) {
|
|
|
|
return Status::newFatal( 'templatedata-invalid-type', 'description', 'string|object' );
|
2013-02-22 18:50:54 +00:00
|
|
|
}
|
2013-04-16 14:18:10 +00:00
|
|
|
$data->description = self::normaliseInterfaceText( $data->description );
|
2013-02-22 18:50:54 +00:00
|
|
|
} else {
|
2013-04-16 14:18:10 +00:00
|
|
|
$data->description = self::normaliseInterfaceText( '' );
|
2013-02-22 18:50:54 +00:00
|
|
|
}
|
|
|
|
|
2013-04-30 21:22:25 +00:00
|
|
|
// Root.params
|
|
|
|
if ( !isset( $data->params ) ) {
|
|
|
|
return Status::newFatal( 'templatedata-invalid-missing', 'params', 'object' );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !is_object( $data->params ) ) {
|
|
|
|
return Status::newFatal( 'templatedata-invalid-type', 'params', 'object' );
|
|
|
|
}
|
|
|
|
|
2013-02-22 18:50:54 +00:00
|
|
|
foreach ( $data->params as $paramName => $paramObj ) {
|
|
|
|
if ( !is_object( $paramObj ) ) {
|
|
|
|
return Status::newFatal( 'templatedata-invalid-type', 'params.' . $paramName, 'object' );
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ( $paramObj as $key => $value ) {
|
|
|
|
if ( !in_array( $key, array(
|
|
|
|
'required',
|
|
|
|
'description',
|
|
|
|
'deprecated',
|
|
|
|
'aliases',
|
|
|
|
'clones',
|
|
|
|
'default',
|
|
|
|
) ) ) {
|
|
|
|
return Status::newFatal( 'templatedata-invalid-unknown', $key );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-30 21:22:25 +00:00
|
|
|
// Param.inherits
|
|
|
|
// TODO: Implementation specifies we use inherit (target references origin), instead
|
|
|
|
// of clone (origin lists targets).
|
|
|
|
if ( isset( $paramObj->clones ) ) {
|
|
|
|
if ( !is_array( $paramObj->clones ) ) {
|
|
|
|
// TODO: Validate the array values.
|
|
|
|
return Status::newFatal( 'templatedata-invalid-type', 'params.' . $paramName . '.clones', 'array' );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$paramObj->clones = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Param.label
|
|
|
|
|
|
|
|
// Param.required
|
2013-02-22 18:50:54 +00:00
|
|
|
if ( isset( $paramObj->required ) ) {
|
|
|
|
if ( !is_bool( $paramObj->required ) ) {
|
|
|
|
return Status::newFatal( 'templatedata-invalid-type', 'params.' . $paramName . '.required', 'boolean' );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$paramObj->required = false;
|
|
|
|
}
|
|
|
|
|
2013-04-30 21:22:25 +00:00
|
|
|
// Param.description
|
2013-02-22 18:50:54 +00:00
|
|
|
if ( isset( $paramObj->description ) ) {
|
|
|
|
if ( !is_object( $paramObj->description ) && !is_string( $paramObj->description ) ) {
|
|
|
|
// TODO: Also validate that if it is an object, the keys are valid lang codes
|
|
|
|
// and the values strings.
|
|
|
|
return Status::newFatal( 'templatedata-invalid-type', 'params.' . $paramName . '.description', 'string|object' );
|
|
|
|
}
|
2013-04-16 14:18:10 +00:00
|
|
|
$paramObj->description = self::normaliseInterfaceText( $paramObj->description );
|
2013-02-22 18:50:54 +00:00
|
|
|
} else {
|
2013-04-16 14:18:10 +00:00
|
|
|
$paramObj->description = self::normaliseInterfaceText( '' );
|
2013-02-22 18:50:54 +00:00
|
|
|
}
|
|
|
|
|
2013-04-30 21:22:25 +00:00
|
|
|
// Param.deprecated
|
2013-02-22 18:50:54 +00:00
|
|
|
if ( isset( $paramObj->deprecated ) ) {
|
2013-04-22 19:56:28 +00:00
|
|
|
if ( $paramObj->deprecated !== false && !is_string( $paramObj->deprecated ) ) {
|
2013-02-22 18:50:54 +00:00
|
|
|
return Status::newFatal( 'templatedata-invalid-type', 'params.' . $paramName . '.deprecated', 'boolean|string' );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$paramObj->deprecated = false;
|
|
|
|
}
|
|
|
|
|
2013-04-30 21:22:25 +00:00
|
|
|
// Param.aliases
|
2013-02-22 18:50:54 +00:00
|
|
|
if ( isset( $paramObj->aliases ) ) {
|
|
|
|
if ( !is_array( $paramObj->aliases ) ) {
|
|
|
|
// TODO: Validate the array values.
|
|
|
|
return Status::newFatal( 'templatedata-invalid-type', 'params.' . $paramName . '.aliases', 'array' );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$paramObj->aliases = array();
|
|
|
|
}
|
|
|
|
|
2013-04-30 21:22:25 +00:00
|
|
|
// Param.default
|
2013-02-22 18:50:54 +00:00
|
|
|
if ( isset( $paramObj->default ) ) {
|
|
|
|
if ( !is_string( $paramObj->default ) ) {
|
|
|
|
return Status::newFatal( 'templatedata-invalid-type', 'params.' . $paramName . '.default', 'string' );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$paramObj->default = '';
|
|
|
|
}
|
2013-04-30 21:22:25 +00:00
|
|
|
|
|
|
|
// TODO: Param.type
|
2013-02-22 18:50:54 +00:00
|
|
|
}
|
|
|
|
|
2013-04-30 21:22:25 +00:00
|
|
|
// TODO: Root.sets
|
|
|
|
|
2013-02-22 18:50:54 +00:00
|
|
|
return Status::newGood();
|
|
|
|
}
|
|
|
|
|
2013-04-16 14:18:10 +00:00
|
|
|
/**
|
|
|
|
* Normalise a InterfaceText field in the TemplateData blob.
|
|
|
|
* @return stdClass|string $text
|
|
|
|
*/
|
|
|
|
protected static function normaliseInterfaceText( $text ) {
|
|
|
|
if ( is_string( $text ) ) {
|
|
|
|
global $wgContLang;
|
|
|
|
$ret = array();
|
|
|
|
$ret[ $wgContLang->getCode() ] = $text;
|
|
|
|
return (object) $ret;
|
|
|
|
}
|
|
|
|
return $text;
|
|
|
|
}
|
|
|
|
|
2013-02-22 18:50:54 +00:00
|
|
|
public function getStatus() {
|
|
|
|
return $this->status;
|
|
|
|
}
|
|
|
|
|
2013-04-22 19:56:28 +00:00
|
|
|
public function getData() {
|
|
|
|
// Returned by reference. Data is a private member. Use clone instead?
|
|
|
|
return $this->data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string JSON
|
|
|
|
*/
|
2013-02-22 18:50:54 +00:00
|
|
|
public function getJSON() {
|
|
|
|
return json_encode( $this->data );
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getHtml( IContextSource $context ) {
|
2013-04-22 20:02:09 +00:00
|
|
|
global $wgContLang;
|
|
|
|
$langCode = $wgContLang->getCode();
|
2013-02-22 18:50:54 +00:00
|
|
|
$data = $this->data;
|
|
|
|
$html =
|
|
|
|
Html::openElement( 'div', array( 'class' => 'mw-templatedata-doc-wrap' ) )
|
2013-04-22 20:02:09 +00:00
|
|
|
. Html::element( 'p', array( 'class' => 'mw-templatedata-doc-desc' ), $data->description->$langCode )
|
2013-02-22 18:50:54 +00:00
|
|
|
. '<table class="wikitable sortable mw-templatedata-doc-params">'
|
|
|
|
. Html::element( 'caption', array(), $context->msg( 'templatedata-doc-params' ) )
|
|
|
|
. '<thead><tr>'
|
|
|
|
. Html::element( 'th', array(), $context->msg( 'templatedata-doc-param-name' ) )
|
|
|
|
. Html::element( 'th', array(), $context->msg( 'templatedata-doc-param-desc' ) )
|
|
|
|
. Html::element( 'th', array(), $context->msg( 'templatedata-doc-param-default' ) )
|
|
|
|
. Html::element( 'th', array(), $context->msg( 'templatedata-doc-param-status' ) )
|
|
|
|
. '</tr></thead>'
|
|
|
|
. '<tbody>'
|
|
|
|
;
|
|
|
|
foreach ( $data->params as $paramName => $paramObj ) {
|
|
|
|
$description = '';
|
|
|
|
$default = '';
|
|
|
|
$html .= '<tr>'
|
|
|
|
. Html::element( 'th', array(), $paramName )
|
|
|
|
// Description
|
|
|
|
. Html::rawElement( 'td', array(
|
|
|
|
'class' => array(
|
2013-04-22 20:02:09 +00:00
|
|
|
'mw-templatedata-doc-param-empty' => !isset( $paramObj->description->$langCode ) && $paramObj->deprecated === false
|
2013-02-22 18:50:54 +00:00
|
|
|
)
|
2013-04-22 20:02:09 +00:00
|
|
|
), isset( $paramObj->description->$langCode ) ? $paramObj->description->$langCode : 'no description' )
|
2013-02-22 18:50:54 +00:00
|
|
|
// Default
|
|
|
|
. Html::element( 'td', array(
|
|
|
|
'class' => array(
|
|
|
|
'mw-templatedata-doc-param-empty' => $paramObj->default === ''
|
|
|
|
)
|
|
|
|
), $paramObj->default !== '' ? $paramObj->default : 'empty' )
|
|
|
|
// Status
|
|
|
|
. Html::element( 'td', array(),
|
|
|
|
$paramObj->deprecated ? 'deprecated' : (
|
|
|
|
$paramObj->required ? 'required' : 'optional'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
. '</tr>';
|
|
|
|
}
|
|
|
|
$html .= '</tbody></table>'
|
|
|
|
. Html::closeElement( 'div' );
|
|
|
|
|
|
|
|
return $html;
|
|
|
|
}
|
|
|
|
|
2013-04-16 14:18:10 +00:00
|
|
|
private function __construct( $data = null ) {
|
2013-02-22 18:50:54 +00:00
|
|
|
$this->data = $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|