mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Math
synced 2024-11-30 18:35:12 +00:00
Implement datatype 'Math' for Wikidata
This change implements all components to use the datatype 'Math' in Wikidata. Because 'String 'is used as value type, only the Formatter and Validator are needed to be implemended. The components are: * hooks * Formatter class * Validator class * Test cases Bug: T67397 Change-Id: Ic64fd6c8560f48052e2db24ae1f013d48a82b5e9
This commit is contained in:
parent
2938d69e0b
commit
946a18d14c
83
MathFormatter.php
Normal file
83
MathFormatter.php
Normal file
|
@ -0,0 +1,83 @@
|
|||
<?php
|
||||
|
||||
use DataValues\StringValue;
|
||||
use ValueFormatters\Exceptions\MismatchingDataValueTypeException;
|
||||
use ValueFormatters\FormattingException;
|
||||
use ValueFormatters\ValueFormatter;
|
||||
use Wikibase\Lib\SnakFormatter;
|
||||
|
||||
/*
|
||||
* Formats the tex string based on the known formats
|
||||
* * text/plain: used in the value input field of Wikidata
|
||||
* * text/x-wiki: wikitext
|
||||
* * text/html: used in Wikidata to display the value of properties
|
||||
* Formats can look like this: "text/html; disposition=widget"
|
||||
* or just "text/plain"
|
||||
*/
|
||||
|
||||
class MathFormatter implements ValueFormatter {
|
||||
|
||||
private $format;
|
||||
|
||||
/*
|
||||
* Loads format to distinguish the type of formatting
|
||||
*
|
||||
* @param string $format
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct( $format ) {
|
||||
switch ( $format ) {
|
||||
case ( SnakFormatter::FORMAT_HTML ):
|
||||
case ( SnakFormatter::FORMAT_HTML_DIFF ):
|
||||
case ( SnakFormatter::FORMAT_HTML_WIDGET ):
|
||||
case ( SnakFormatter::FORMAT_WIKI ):
|
||||
case ( SnakFormatter::FORMAT_PLAIN ):
|
||||
$this->format = $format;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException( 'Unsupported output format: ' . $format );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* @param StringValue $value
|
||||
*
|
||||
* @return string
|
||||
* @throws \ValueFormatters\Exceptions\MismatchingDataValueTypeException
|
||||
*/
|
||||
public function format( $value ) {
|
||||
if ( !( $value instanceof StringValue ) ) {
|
||||
throw new MismatchingDataValueTypeException( 'StringValue', get_class( $value ) );
|
||||
}
|
||||
$tex = $value->getValue();
|
||||
|
||||
switch ( $this->format ) {
|
||||
case ( SnakFormatter::FORMAT_PLAIN ):
|
||||
return "$tex";
|
||||
case ( SnakFormatter::FORMAT_WIKI ):
|
||||
return "<math>$tex</math>";
|
||||
case ( SnakFormatter::FORMAT_HTML ):
|
||||
case ( SnakFormatter::FORMAT_HTML_WIDGET ):
|
||||
case ( SnakFormatter::FORMAT_HTML_DIFF ):
|
||||
$renderer = new MathMathML( $tex );
|
||||
if ( $renderer->checkTex() ) {
|
||||
if ( $renderer->render() ) {
|
||||
return $renderer->getHtmlOutput();
|
||||
}
|
||||
}
|
||||
// TeX string is not valid or rendering failed
|
||||
return $renderer->getLastError();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return format
|
||||
*/
|
||||
|
||||
public function getFormat() {
|
||||
return $this->format;
|
||||
}
|
||||
}
|
50
MathValidator.php
Normal file
50
MathValidator.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
use ValueFormatters\Exceptions\MismatchingDataValueTypeException;
|
||||
use ValueValidators\Result;
|
||||
use ValueValidators\ValueValidator;
|
||||
use ValueValidators\Error;
|
||||
use DataValues\StringValue;
|
||||
|
||||
// @author Duc Linh Tran, Julian Hilbig, Moritz Schubotz
|
||||
|
||||
class MathValidator implements ValueValidator {
|
||||
|
||||
/**
|
||||
* Validates a value with MathInputCheckRestbase
|
||||
*
|
||||
* @param mixed $value The value to validate
|
||||
*
|
||||
* @return \ValueValidators\Result
|
||||
* @throws ValueFormatters\Exceptions\MismatchingDataValueTypeException
|
||||
*/
|
||||
public function validate( $value ) {
|
||||
if ( !( $value instanceof StringValue ) ) {
|
||||
throw new MismatchingDataValueTypeException( 'StringValue', get_class( $value ) );
|
||||
}
|
||||
|
||||
// get input String from value
|
||||
$tex = $value->getValue();
|
||||
|
||||
$checker = new MathInputCheckRestbase( $tex );
|
||||
if ( $checker->isValid() ) {
|
||||
return Result::newSuccess();
|
||||
}
|
||||
|
||||
// TeX string is not valid
|
||||
return Result::newError(
|
||||
array(
|
||||
Error::newError( null, null, 'malformed-value', $checker->getError() )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ValueValidator::setOptions()
|
||||
*
|
||||
* @param array $options
|
||||
*/
|
||||
public function setOptions( array $options ) {
|
||||
// Do nothing. This method shouldn't even be in the interface.
|
||||
}
|
||||
}
|
56
MathWikidataHook.php
Normal file
56
MathWikidataHook.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
use ValueFormatters\FormatterOptions;
|
||||
use ValueParsers\StringParser;
|
||||
use Wikibase\Repo\Parsers\WikibaseStringValueNormalizer;
|
||||
use Wikibase\Repo\Validators\CompositeValidator;
|
||||
use Wikibase\Repo\WikibaseRepo;
|
||||
|
||||
class MathWikidataHook {
|
||||
/*
|
||||
* Add Datatype "Math" to the Wikibase Repository
|
||||
*/
|
||||
public static function onWikibaseRepoDataTypes( array &$dataTypeDefinitions ) {
|
||||
$dataTypeDefinitions['PT:math'] = array(
|
||||
'value-type' => 'string',
|
||||
'validator-factory-callback' => function() {
|
||||
// load validator builders
|
||||
$factory = WikibaseRepo::getDefaultValidatorBuilders();
|
||||
|
||||
// initialize an array with string validators
|
||||
// returns an array of validators
|
||||
// that add basic string validation such as preventing empty strings
|
||||
$validators = $factory->buildStringValidators();
|
||||
$validators[] = new MathValidator();
|
||||
return $validators;
|
||||
},
|
||||
'parser-factory-callback' => function( ParserOptions $options ) {
|
||||
$repo = WikibaseRepo::getDefaultInstance();
|
||||
$normalizer = new WikibaseStringValueNormalizer( $repo->getStringNormalizer() );
|
||||
return new StringParser( $normalizer );
|
||||
},
|
||||
'formatter-factory-callback' => function( $format, FormatterOptions $options ) {
|
||||
global $wgOut;
|
||||
$styles = array( 'ext.math.desktop.styles', 'ext.math.scripts', 'ext.math.styles' );
|
||||
$wgOut->addModuleStyles( $styles );
|
||||
return new MathFormatter( $format );
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add Datatype "Math" to the Wikibase Client
|
||||
*/
|
||||
public static function onWikibaseClientDataTypes( array &$dataTypeDefinitions ) {
|
||||
$dataTypeDefinitions['PT:math'] = array(
|
||||
'value-type' => 'string',
|
||||
'formatter-factory-callback' => function( $format, FormatterOptions $options ) {
|
||||
global $wgOut;
|
||||
$styles = array( 'ext.math.desktop.styles', 'ext.math.scripts', 'ext.math.styles' );
|
||||
$wgOut->addModuleStyles( $styles );
|
||||
return new MathFormatter( $format );
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,10 @@
|
|||
"MathInputCheckTexvc": "MathInputCheckTexvc.php",
|
||||
"MathInputCheckRestbase": "MathInputCheckRestbase.php",
|
||||
"SpecialMathShowImage": "SpecialMathShowImage.php",
|
||||
"SpecialMathStatus": "SpecialMathStatus.php"
|
||||
"SpecialMathStatus": "SpecialMathStatus.php",
|
||||
"MathValidator": "MathValidator.php",
|
||||
"MathFormatter": "MathFormatter.php",
|
||||
"MathWikidataHook": "MathWikidataHook.php"
|
||||
},
|
||||
"DefaultUserOptions": {
|
||||
"math": "png"
|
||||
|
@ -57,6 +60,12 @@
|
|||
],
|
||||
"EditPageBeforeEditToolbar": [
|
||||
"MathHooks::onEditPageBeforeEditToolbar"
|
||||
],
|
||||
"WikibaseClientDataTypes": [
|
||||
"MathWikidataHook::onWikibaseClientDataTypes"
|
||||
],
|
||||
"WikibaseRepoDataTypes": [
|
||||
"MathWikidataHook::onWikibaseRepoDataTypes"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
|
|
100
tests/MathFormatterTest.php
Normal file
100
tests/MathFormatterTest.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
/**
|
||||
* Test the results of MathFormatter
|
||||
*
|
||||
* @group Math
|
||||
*/
|
||||
|
||||
use DataValues\StringValue;
|
||||
use DataValues\NumberValue;
|
||||
|
||||
class MathFormatterTest extends MediaWikiTestCase {
|
||||
const SOME_TEX = "a^2+b^2=c^2";
|
||||
const FORMAT_PLAIN = 'text/plain';
|
||||
const FORMAT_HTML = 'text/html';
|
||||
const FORMAT_XWIKI = 'text/x-wiki';
|
||||
const FORMAT_UNKNOWN = 'unknown/unknown';
|
||||
const FORMAT_VALUE = "";
|
||||
|
||||
protected static $hasRestbase;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
$rbi = new MathRestbaseInterface();
|
||||
self::$hasRestbase = $rbi->checkBackend( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the fixture, for example, opens a network connection.
|
||||
* This method is called before a test is executed.
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
if ( !self::$hasRestbase ) {
|
||||
$this->markTestSkipped( "Can not connect to Restbase Math interface." );
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown() {
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the
|
||||
* @covers MathFormatter::__construct()
|
||||
*/
|
||||
public function testBasics() {
|
||||
$formatter = new MathFormatter( self::FORMAT_PLAIN );
|
||||
// check if the format input was corretly passed to the class
|
||||
$this->assertEquals( self::FORMAT_PLAIN, $formatter->getFormat(), 'test getFormat' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException ValueFormatters\Exceptions\MismatchingDataValueTypeException
|
||||
*/
|
||||
public function testNotStringValue() {
|
||||
$formatter = new MathFormatter( self::FORMAT_PLAIN );
|
||||
$formatter->format( new NumberValue( 0 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException ValueFormatters\Exceptions\MismatchingDataValueTypeException
|
||||
*/
|
||||
public function testNullValue() {
|
||||
$formatter = new MathFormatter( self::FORMAT_PLAIN );
|
||||
$formatter->format( null );
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testUnknownFormat() {
|
||||
$formatter = new MathFormatter( self::FORMAT_UNKNOWN );
|
||||
}
|
||||
|
||||
public function testFormatPlain() {
|
||||
$formatter = new MathFormatter( self::FORMAT_PLAIN );
|
||||
$value = new StringValue( self::SOME_TEX );
|
||||
$resultFormat = $formatter->format( $value );
|
||||
$this->assertEquals( self::SOME_TEX, $resultFormat,
|
||||
'Results should be equal' );
|
||||
|
||||
}
|
||||
|
||||
public function testFormatHtml() {
|
||||
$formatter = new MathFormatter( self::FORMAT_HTML );
|
||||
$value = new StringValue( self::SOME_TEX );
|
||||
$resultFormat = $formatter->format( $value );
|
||||
$this->assertContains( '</math>', $resultFormat,
|
||||
'Result must contain math-tag' );
|
||||
}
|
||||
|
||||
public function testFormatXWiki() {
|
||||
$tex = self::SOME_TEX;
|
||||
$formatter = new MathFormatter( self::FORMAT_XWIKI );
|
||||
$value = new StringValue( self::SOME_TEX );
|
||||
$resultFormat = $formatter->format( $value );
|
||||
$this->assertEquals( "<math>$tex</math>", $resultFormat,
|
||||
'Tex wasn\'t properly wrapped' );
|
||||
|
||||
}
|
||||
}
|
69
tests/MathValidatorTest.php
Normal file
69
tests/MathValidatorTest.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
/**
|
||||
* Test the results of MathFormatter
|
||||
*
|
||||
* @group Math
|
||||
*/
|
||||
|
||||
use DataValues\StringValue;
|
||||
use DataValues\NumberValue;
|
||||
|
||||
class MathValidatorTest extends MediaWikiTestCase {
|
||||
const VADLID_TEX = "a^2+b^2=c^2";
|
||||
const INVADLID_TEX = "\\notExists";
|
||||
|
||||
protected static $hasRestbase;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
$rbi = new MathRestbaseInterface();
|
||||
self::$hasRestbase = $rbi->checkBackend( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the fixture, for example, opens a network connection.
|
||||
* This method is called before a test is executed.
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
if ( !self::$hasRestbase ) {
|
||||
$this->markTestSkipped( "Can not connect to Restbase Math interface." );
|
||||
}
|
||||
}
|
||||
|
||||
protected function tearDown() {
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException ValueFormatters\Exceptions\MismatchingDataValueTypeException
|
||||
*/
|
||||
public function testNotStringValue() {
|
||||
$validator = new MathValidator();
|
||||
$validator->validate( new NumberValue( 0 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException ValueFormatters\Exceptions\MismatchingDataValueTypeException
|
||||
*/
|
||||
public function testNullValue() {
|
||||
$validator = new MathValidator();
|
||||
$validator->validate( null );
|
||||
}
|
||||
|
||||
public function testValidInput() {
|
||||
$validator = new MathValidator();
|
||||
$result = $validator->validate( new StringValue( self::VADLID_TEX ) );
|
||||
// not supported by jenkins php version
|
||||
// $this->assertType( \ValueValidators\Result::class, $result );
|
||||
$this->assertTrue( $result->isValid() );
|
||||
}
|
||||
|
||||
public function testInvalidInput() {
|
||||
$validator = new MathValidator();
|
||||
$result = $validator->validate( new StringValue( self::INVADLID_TEX ) );
|
||||
// not supported by jenkins php version
|
||||
// $this->assertType( \ValueValidators\Result::class, $result );
|
||||
$this->assertFalse( $result->isValid() );
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue