mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Math
synced 2024-11-14 11:15:13 +00:00
Merge "Implement datatype 'Math' for Wikidata"
This commit is contained in:
commit
6c61ce19fa
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",
|
"MathInputCheckTexvc": "MathInputCheckTexvc.php",
|
||||||
"MathInputCheckRestbase": "MathInputCheckRestbase.php",
|
"MathInputCheckRestbase": "MathInputCheckRestbase.php",
|
||||||
"SpecialMathShowImage": "SpecialMathShowImage.php",
|
"SpecialMathShowImage": "SpecialMathShowImage.php",
|
||||||
"SpecialMathStatus": "SpecialMathStatus.php"
|
"SpecialMathStatus": "SpecialMathStatus.php",
|
||||||
|
"MathValidator": "MathValidator.php",
|
||||||
|
"MathFormatter": "MathFormatter.php",
|
||||||
|
"MathWikidataHook": "MathWikidataHook.php"
|
||||||
},
|
},
|
||||||
"DefaultUserOptions": {
|
"DefaultUserOptions": {
|
||||||
"math": "png"
|
"math": "png"
|
||||||
|
@ -57,6 +60,12 @@
|
||||||
],
|
],
|
||||||
"EditPageBeforeEditToolbar": [
|
"EditPageBeforeEditToolbar": [
|
||||||
"MathHooks::onEditPageBeforeEditToolbar"
|
"MathHooks::onEditPageBeforeEditToolbar"
|
||||||
|
],
|
||||||
|
"WikibaseClientDataTypes": [
|
||||||
|
"MathWikidataHook::onWikibaseClientDataTypes"
|
||||||
|
],
|
||||||
|
"WikibaseRepoDataTypes": [
|
||||||
|
"MathWikidataHook::onWikibaseRepoDataTypes"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"config": {
|
"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