From 7978ed0b961995308d7fa20930b1d71cdab130c9 Mon Sep 17 00:00:00 2001 From: Adam Robak Date: Fri, 29 May 2015 15:04:04 +0200 Subject: [PATCH 1/3] DAT-2845 add infobox parser api --- PortableInfobox.setup.php | 4 + controllers/ApiPortableInfobox.class.php | 79 +++++++++++++++++++ ...rtableInfoboxParserTagController.class.php | 36 ++++++--- services/Parser/XmlParser.php | 45 ++++++++--- ...PortableInfoboxParserTagControllerTest.php | 5 ++ 5 files changed, 146 insertions(+), 23 deletions(-) create mode 100644 controllers/ApiPortableInfobox.class.php diff --git a/PortableInfobox.setup.php b/PortableInfobox.setup.php index e37b187..8b2090d 100644 --- a/PortableInfobox.setup.php +++ b/PortableInfobox.setup.php @@ -45,6 +45,7 @@ $wgAutoloadClasses[ 'Wikia\PortableInfobox\Helpers\SimpleXmlUtil' ] = $dir . 'se // controller classes $wgAutoloadClasses[ 'PortableInfoboxParserTagController' ] = $dir . 'controllers/PortableInfoboxParserTagController.class.php'; +$wgAutoloadClasses[ 'ApiPortableInfobox' ] = $dir . 'controllers/ApiPortableInfobox.class.php'; $wgAutoloadClasses[ 'PortableInfoboxHooks' ] = $dir . 'PortableInfoboxHooks.class.php'; // hooks @@ -54,3 +55,6 @@ $wgHooks['ParserAfterTidy'][] = 'PortableInfoboxParserTagController::replaceInfo // i18n mapping $wgExtensionMessagesFiles[ 'PortableInfobox' ] = $dir . 'PortableInfobox.i18n.php'; + +//MW API +$wgAPIModules['infobox'] = 'ApiPortableInfobox'; diff --git a/controllers/ApiPortableInfobox.class.php b/controllers/ApiPortableInfobox.class.php new file mode 100644 index 0000000..614e94b --- /dev/null +++ b/controllers/ApiPortableInfobox.class.php @@ -0,0 +1,79 @@ +getParameter( "text" ); + $title = $this->getParameter( "title" ); + $arguments = $this->getFrameArguments(); + if ( $arguments === null ) { + $this->getResult()->setWarning( "Arguments json format incorect or empty" ); + } + $parser = new Parser(); + $parser->startExternalParse( Title::newFromText( $title ), new ParserOptions(), 'text', true ); + $frame = $parser->getPreprocessor()->newCustomFrame( $arguments ); + + try { + $output = PortableInfoboxParserTagController::getInstance()->render( $text, $parser, $frame ); + $this->getResult()->addValue( null, $this->getModuleName(), [ 'text' => [ '*' => $output ] ] ); + } catch ( \Wikia\PortableInfobox\Parser\Nodes\UnimplementedNodeException $e ) { + $this->dieUsage( wfMessage( 'unimplemented-infobox-tag', [ $e->getMessage() ] )->escaped(), "notimplemented" ); + } catch ( \Wikia\PortableInfobox\Parser\XmlMarkupParseErrorException $e ) { + $this->dieUsage( wfMessage( 'xml-parse-error' )->text(), "badxml" ); + } + } + + public function getAllowedParams() { + return array( + 'text' => array( + ApiBase :: PARAM_TYPE => 'string' + ), + 'title' => array( + ApiBase :: PARAM_TYPE => 'string' + ), + 'args' => array( + ApiBase :: PARAM_TYPE => 'string' + ) + ); + } + + public function getParamDescription() { + return array( + 'text' => 'Infobox to parse (xml string)', + 'title' => 'Title of page the text belongs to', + 'args' => 'Variable list to use during parse (json format)', + ); + } + + public function getDescription() { + return array( 'This module provides infobox parser' ); + } + + /** + * Examples + */ + public function getExamples() { + return array( + 'api.php?action=infobox', + 'api.php?action=infobox&text={{PAGENAME}}&title=Test', + 'api.php?action=infobox&text=&args={"test": "test value"}', + ); + } + + public function getVersion() { + return __CLASS__ . '$Id$'; + } + + /** + * @return mixed + */ + protected function getFrameArguments() { + $arguments = $this->getParameter( "args" ); + return isset( $arguments ) ? json_decode( $arguments, true ) : false; + } + +} \ No newline at end of file diff --git a/controllers/PortableInfoboxParserTagController.class.php b/controllers/PortableInfoboxParserTagController.class.php index b7f5d9b..fc29067 100644 --- a/controllers/PortableInfoboxParserTagController.class.php +++ b/controllers/PortableInfoboxParserTagController.class.php @@ -42,6 +42,31 @@ class PortableInfoboxParserTagController extends WikiaController { return true; } + /** + * @param $markup + * @param Parser $parser + * @param PPFrame $frame + * @return array|string + * @throws UnimplementedNodeException when node used in markup does not exists + * @throws XmlMarkupParseErrorException xml not well formatted + */ + public function render( $markup, Parser $parser, PPFrame $frame, $params = null ) { + $infoboxParser = new Wikia\PortableInfobox\Parser\XmlParser( + array_merge( $frame->getNamedArguments(), $frame->getArguments() ) ); + $infoboxParser->setExternalParser( new Wikia\PortableInfobox\Parser\MediaWikiParserService( $parser, $frame ) ); + + //get params if not overridden + if ( !isset( $params ) ) { + $params = $infoboxParser->getInfoboxParams( $markup ); + } + $data = $infoboxParser->getDataFromXmlString( $markup ); + //save for later api usage + $this->saveToParserOutput( $parser->getOutput(), $data ); + + $theme = $this->getThemeWithDefault( $params, $frame ); + return ( new PortableInfoboxRenderService() )->renderInfobox( $data, $theme ); + } + /** * @desc Renders Infobox * @@ -56,23 +81,14 @@ class PortableInfoboxParserTagController extends WikiaController { $this->markerNumber++; $markup = '<' . self::PARSER_TAG_NAME . '>' . $text . ''; - $infoboxParser = new Wikia\PortableInfobox\Parser\XmlParser( $frame->getNamedArguments() ); - $infoboxParser->setExternalParser( ( new Wikia\PortableInfobox\Parser\MediaWikiParserService( $parser, $frame ) ) ); - try { - $data = $infoboxParser->getDataFromXmlString( $markup ); + $renderedValue = $this->render( $markup, $parser, $frame, $params ); } catch ( \Wikia\PortableInfobox\Parser\Nodes\UnimplementedNodeException $e ) { return $this->handleError( wfMessage( 'unimplemented-infobox-tag', [ $e->getMessage() ] )->escaped() ); } catch ( \Wikia\PortableInfobox\Parser\XmlMarkupParseErrorException $e ) { return $this->handleError( wfMessage( 'xml-parse-error' ) ); } - //save for later api usage - $this->saveToParserOutput( $parser->getOutput(), $data ); - - $renderer = new PortableInfoboxRenderService(); - $theme = $this->getThemeWithDefault( $params, $frame ); - $renderedValue = $renderer->renderInfobox( $data, $theme ); if ( $wgArticleAsJson ) { // (wgArticleAsJson == true) it means that we need to encode output for use inside JSON $renderedValue = trim( json_encode( $renderedValue ), '"' ); diff --git a/services/Parser/XmlParser.php b/services/Parser/XmlParser.php index 0ea5416..a3b2151 100644 --- a/services/Parser/XmlParser.php +++ b/services/Parser/XmlParser.php @@ -55,25 +55,22 @@ class XmlParser { public function getDataFromXmlString( $xmlString ) { wfProfileIn( __METHOD__ ); - $global_libxml_setting = libxml_use_internal_errors(); - libxml_use_internal_errors( true ); - $xml = simplexml_load_string( $xmlString ); - $errors = libxml_get_errors(); - libxml_use_internal_errors( $global_libxml_setting ); - - if ( $xml === false ) { - foreach ( $errors as $xmlerror ) { - $this->logXmlParseError( $xmlerror->level, $xmlerror->code, trim( $xmlerror->message ) ); - } - libxml_clear_errors(); - throw new XmlMarkupParseErrorException(); - } + $xml = $this->parseXmlString( $xmlString ); $data = $this->getDataFromNodes( $xml ); wfProfileOut( __METHOD__ ); return $data; } + public function getInfoboxParams( $xmlString ) { + $xml = $this->parseXmlString( $xmlString ); + $result = []; + foreach ( $xml->attributes() as $k => $v ) { + $result[$k] = (string) $v; + } + return $result; + } + protected function logXmlParseError( $level, $code, $message ) { \Wikia\Logger\WikiaLogger::instance()->info( "PortableInfobox XML Parser problem", [ "level" => $level, @@ -106,6 +103,28 @@ class XmlParser { return new Nodes\NodeUnimplemented( $xmlNode, $this->infoboxData ); } + /** + * @param $xmlString + * @return \SimpleXMLElement + * @throws XmlMarkupParseErrorException + */ + protected function parseXmlString( $xmlString ) { + $global_libxml_setting = libxml_use_internal_errors(); + libxml_use_internal_errors( true ); + $xml = simplexml_load_string( $xmlString ); + $errors = libxml_get_errors(); + libxml_use_internal_errors( $global_libxml_setting ); + + if ( $xml === false ) { + foreach ( $errors as $xmlerror ) { + $this->logXmlParseError( $xmlerror->level, $xmlerror->code, trim( $xmlerror->message ) ); + } + libxml_clear_errors(); + throw new XmlMarkupParseErrorException(); + } + return $xml; + } + } class XmlMarkupParseErrorException extends \Exception { diff --git a/tests/PortableInfoboxParserTagControllerTest.php b/tests/PortableInfoboxParserTagControllerTest.php index 9fb8f96..dc69567 100644 --- a/tests/PortableInfoboxParserTagControllerTest.php +++ b/tests/PortableInfoboxParserTagControllerTest.php @@ -2,6 +2,11 @@ class PortableInfoboxParserTagControllerTest extends WikiaBaseTest { + /** @var PortableInfoboxParserTagController */ + protected $parser; + /** @var Parser */ + protected $controller; + protected function setUp() { $this->setupFile = dirname( __FILE__ ) . '/../PortableInfobox.setup.php'; parent::setUp(); From 369209b5f7653489a9c7c361593b9e07de31a6b2 Mon Sep 17 00:00:00 2001 From: Adam Robak Date: Mon, 1 Jun 2015 10:59:20 +0200 Subject: [PATCH 2/3] DAT-2845 ensure params are correctly acquired --- ...rtableInfoboxParserTagController.class.php | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/controllers/PortableInfoboxParserTagController.class.php b/controllers/PortableInfoboxParserTagController.class.php index fc29067..324e761 100644 --- a/controllers/PortableInfoboxParserTagController.class.php +++ b/controllers/PortableInfoboxParserTagController.class.php @@ -46,13 +46,12 @@ class PortableInfoboxParserTagController extends WikiaController { * @param $markup * @param Parser $parser * @param PPFrame $frame - * @return array|string + * @return string * @throws UnimplementedNodeException when node used in markup does not exists * @throws XmlMarkupParseErrorException xml not well formatted */ public function render( $markup, Parser $parser, PPFrame $frame, $params = null ) { - $infoboxParser = new Wikia\PortableInfobox\Parser\XmlParser( - array_merge( $frame->getNamedArguments(), $frame->getArguments() ) ); + $infoboxParser = new Wikia\PortableInfobox\Parser\XmlParser( $this->getFrameParams( $frame ) ); $infoboxParser->setExternalParser( new Wikia\PortableInfobox\Parser\MediaWikiParserService( $parser, $frame ) ); //get params if not overridden @@ -129,4 +128,19 @@ class PortableInfoboxParserTagController extends WikiaController { ( isset( $params[ 'theme' ] ) ? $params[ 'theme' ] : self::DEFAULT_THEME_NAME ); } + /** + * Function ensures that arrays are used for merging + * @param PPFrame $frame + * @return array + */ + protected function getFrameParams( PPFrame $frame ) { + //we use both getNamedArguments and getArguments to ensure we acquire variables no matter what frame is used + $namedArgs = $frame->getNamedArguments(); + $namedArgs = isset( $namedArgs ) ? ( is_array( $namedArgs ) ? $namedArgs : [ $namedArgs ] ) : [ ]; + $args = $frame->getArguments(); + $args = isset( $args ) ? ( is_array( $args ) ? $args : [ $args ] ) : [ ]; + + return array_merge( $namedArgs, $args ); + } + } From 3d3d682e6909b163f872bc54e64f9dc0788fec7d Mon Sep 17 00:00:00 2001 From: Adam Robak Date: Mon, 1 Jun 2015 11:40:06 +0200 Subject: [PATCH 3/3] DAT-2845 missing space in comment --- PortableInfobox.setup.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PortableInfobox.setup.php b/PortableInfobox.setup.php index 8b2090d..1c70812 100644 --- a/PortableInfobox.setup.php +++ b/PortableInfobox.setup.php @@ -56,5 +56,5 @@ $wgHooks['ParserAfterTidy'][] = 'PortableInfoboxParserTagController::replaceInfo // i18n mapping $wgExtensionMessagesFiles[ 'PortableInfobox' ] = $dir . 'PortableInfobox.i18n.php'; -//MW API +// MW API $wgAPIModules['infobox'] = 'ApiPortableInfobox';