Mwjames has uploaded a new change for review. https://gerrit.wikimedia.org/r/60393
Change subject: Refactor SMWParserExtensions, convert static into an instantiable class ...................................................................... Refactor SMWParserExtensions, convert static into an instantiable class This commit does not contain any processing or logic changes. + Rename SMWParserExtensions class to SMW\ParserTextProcessor (because the class is actually doing text processing) + Remove hook caller from class and move into the SMWHooks class + Eliminate static SMWParserData caller + Convert all static methods + Split parse into smaller piece for better modularity + Add test Change-Id: I66b812e0276820fa58dbbfbc632236ea6f8ba6d5 --- M SemanticMediaWiki.hooks.php M includes/ParserData.php A includes/ParserTextProcessor.php D includes/SMW_ParserExtensions.php M includes/Setup.php M tests/phpunit/includes/HooksTest.php A tests/phpunit/includes/ParserTextProcessorTest.php 7 files changed, 860 insertions(+), 230 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/SemanticMediaWiki refs/changes/93/60393/1 diff --git a/SemanticMediaWiki.hooks.php b/SemanticMediaWiki.hooks.php index 8dc4fd6..be0f613 100644 --- a/SemanticMediaWiki.hooks.php +++ b/SemanticMediaWiki.hooks.php @@ -240,6 +240,7 @@ 'Hooks', 'DataValueFactory', 'Settings', + 'ParserTextProcessor', 'api/ApiSMWInfo', @@ -610,4 +611,40 @@ $parserData->addSpecialProperties( $wikiPage, $revision, $user ); return true; } + + /** + * Hook: InternalParseBeforeLinks is used to process the expanded wiki + * code after <nowiki>, HTML-comments, and templates have been treated. + * + * This method will be called before an article is displayed or previewed. + * For display and preview we strip out the semantic properties and append them + * at the end of the article. + * + * @note MW 1.20+ see InternalParseBeforeSanitize + * + * @see Parser + * @see http://http://www.mediawiki.org/wiki/Manual:Hooks/InternalParseBeforeLinks + * + * @since 1.10 + * + * @param Parser $parser + * @param string $text + * + * @return true + */ + public static function onInternalParseBeforeLinks( Parser &$parser, &$text ) { + + $settings = SMW\Settings::newFromArray( array( + 'smwgNamespacesWithSemanticLinks' => $GLOBALS['smwgNamespacesWithSemanticLinks'], + 'smwgLinksInValues' => $GLOBALS['smwgLinksInValues'], + 'smwgInlineErrors' => $GLOBALS['smwgInlineErrors'] + ) ); + + $processor = new SMW\ParserTextProcessor( + new SMW\ParserData( $parser->getTitle(), $parser->getOutput() ), + $settings + ); + $processor->parse( $text ); + return true; + } } diff --git a/includes/ParserData.php b/includes/ParserData.php index 4f3d3f7..d35d307 100644 --- a/includes/ParserData.php +++ b/includes/ParserData.php @@ -166,18 +166,6 @@ protected $updateJobs = true; /** - * Allows explicitly to switch storage method, MW 1.21 comes with a new - * method setExtensionData/getExtensionData in how the ParserOutput - * stores arbitrary data - * - * MW 1.21 unit tests are passed but real page content did vanished - * therefore for now disable this feature for MW 1.21 as well - * - * @var $extensionData - */ - protected $useExtensionData = false; - - /** * Constructor * * @since 1.9 @@ -302,13 +290,15 @@ } /** - * Init semanticData container either from the ParserOutput object - * or if not available use the subject + * Initializes the semantic data container either from the ParserOutput or + * if not available a new container is being created + * + * @note MW 1.21+ use getExtensionData() * * @since 1.9 */ protected function setData() { - if ( method_exists( $this->parserOutput, 'getExtensionData' ) && $this->useExtensionData ) { + if ( method_exists( $this->parserOutput, 'getExtensionData' ) ) { $this->semanticData = $this->parserOutput->getExtensionData( 'smwdata' ); } elseif ( isset( $this->parserOutput->mSMWData ) ) { $this->semanticData = $this->parserOutput->mSMWData; @@ -323,6 +313,8 @@ /** * Update ParserOutput with processed semantic data * + * @note MW 1.21+ use setExtensionData() + * * @since 1.9 * * @throws MWException @@ -332,7 +324,7 @@ throw new MWException( 'The semantic data container is not available' ); } - if ( method_exists( $this->parserOutput, 'setExtensionData' ) && $this->useExtensionData ) { + if ( method_exists( $this->parserOutput, 'setExtensionData' ) ) { $this->parserOutput->setExtensionData( 'smwdata', $this->semanticData ); } else { $this->parserOutput->mSMWData = $this->semanticData; diff --git a/includes/ParserTextProcessor.php b/includes/ParserTextProcessor.php new file mode 100644 index 0000000..771dab3 --- /dev/null +++ b/includes/ParserTextProcessor.php @@ -0,0 +1,408 @@ +<?php + +namespace SMW; + +use MagicWord; +use Title; +use Html; +use SpecialPage; + +use SMWOutputs; +use SMWDIWikiPage; +use SMWDIProperty; + +/** + * Class collects all functions for wiki text parsing / processing that are + * relevant for SMW + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @since 1.9 + * + * @file + * @ingroup SMW + * @ingroup Parser + * + * @author Markus Krötzsch + * @author Denny Vrandecic + * @author mwjames + */ + +/** + * This class is contains all functions necessary for parsing wiki text before + * it is displayed or previewed while identifying SMW related + * annotations. + * + * @ingroup SMW + * @ingroup Parser + */ +class ParserTextProcessor { + + /** + * Represents a Settings object + * @var Settings + */ + protected $settings; + + /** + * Represents a IParserData object + * @var IParserData + */ + protected $parserData; + + /** + * Represents $smwgNamespacesWithSemanticLinks status + * @var boolean + */ + protected $isEnabled; + + /** + * Internal state for switching SMW link annotations off/on during parsing + * ([[SMW::on]] and [[SMW:off]]) + * @var boolean + */ + protected $isAnnotation = true; + + /** + * @par Example: + * @code + * $settings = Settings::newFromArray( array( + * 'smwgNamespacesWithSemanticLinks' => $GLOBALS['smwgNamespacesWithSemanticLinks'], + * 'smwgLinksInValues' => $GLOBALS['smwgLinksInValues'], + * 'smwgInlineErrors' => $GLOBALS['smwgInlineErrors'] + * ) ); + * + * $parserData = new ParserData( $title, $parserOutput ); + * + * new ParserTextProcessor( $parserData, $settings ); + * @endcode + * + * @see SMWHooks::onInternalParseBeforeLinks + * + * @since 1.9 + * + * @param IParserData $parserData + * @param Settings $settings + */ + public function __construct( IParserData $parserData, Settings $settings ) { + $this->parserData = $parserData; + $this->settings = $settings; + } + + /** + * Parsing text before an article is displayed or previewed, strip out + * semantic properties and add them to the ParserOutput object + * + * @since 1.9 + * + * @param string &$text + */ + public function parse( &$text ) { + $title = $this->parserData->getTitle(); + + // Strip magic words from text body + $this->stripMagicWords( $text ); + + // Attest if semantic data should be processed + $this->isSemanticEnabled( $title ); + + // Process redirects + $this->setRedirect( $title ); + + // Parse links to extract semantic properties + $linksInValues = $this->settings->get( 'smwgLinksInValues' ); + $text = preg_replace_callback( + $this->getRegexpPattern( $linksInValues ), + $linksInValues ? 'self::process' : 'self::preprocess', + $text + ); + + // Add RDF link to the HTML header. + $this->parserData->getOutput()->addHeadItem( "\t\t" . $this->getRDFUrl( $title ) . "\n" ); + + // Update ParserOutput + $this->parserData->getOutput()->addModules( $this->getModules() ); + $this->parserData->updateOutput(); + SMWOutputs::commitToParserOutput( $this->parserData->getOutput() ); + } + + /** + * Attest if semantic data should be processed and displayed + * for a page in the given namespace. (have to parse them in + * any case, in order to clean the wiki source for further processing) + * + * @since 1.9 + * + * @param Title $title + * + * @return boolean + */ + protected function isSemanticEnabled( Title $title ) { + $namespaces = $this->settings->get( 'smwgNamespacesWithSemanticLinks' ); + $this->isEnabled = !empty( $namespaces[$title->getNamespace()] ); + } + + /** + * Returns required resource modules + * + * @since 1.9 + * + * @return array + */ + protected function getModules() { + return array( + 'ext.smw.style', + 'ext.smw.tooltips' + ); + } + + /** + * Process and add '_REDI' property in case the current Title is a redirect + * + * @since 1.9 + * + * @param Title $title + */ + protected function setRedirect( Title $title ) { + if ( $this->isEnabled && $title->isRedirect() ) { + $this->parserData->getData()->addPropertyObjectValue( + new SMWDIProperty( '_REDI' ), + SMWDIWikiPage::newFromTitle( $title, '__red' ) + ); + } + } + + /** + * $smwgLinksInValues (default = false) determines which regexp pattern + * is returned, either a more complex (lib PCRE may cause segfaults if text + * is long) or a simpler (no segfaults found for those, but no links + * in values) pattern. + * + * If enabled (SMW accepts inputs like [[property::Some [[link]] in value]]), + * this may lead to PHP crashes (!) when very long texts are + * used as values. This is due to limitations in the library PCRE that + * PHP uses for pattern matching. + * + * @since 1.9 + * + * @param boolean $linksInValues + * + * @return string + */ + protected function getRegexpPattern( $linksInValues ) { + if ( $linksInValues ) { + return '/\[\[ # Beginning of the link + (?:([^:][^]]*):[=:])+ # Property name (or a list of those) + ( # After that: + (?:[^|\[\]] # either normal text (without |, [ or ]) + |\[\[[^]]*\]\] # or a [[link]] + |\[[^]]*\] # or an [external link] + )*) # all this zero or more times + (?:\|([^]]*))? # Display text (like "text" in [[link|text]]), optional + \]\] # End of link + /xu'; + } else { + return '/\[\[ # Beginning of the link + (?:([^:][^]]*):[=:])+ # Property name (or a list of those) + ([^\[\]]*) # content: anything but [, |, ] + \]\] # End of link + /xu'; + } + } + + /** + * Returns ExportRDF URL + * + * @since 1.9 + * + * @param Title $title + * + * @return string + */ + protected function getRDFUrl( Title $title ) { + return Html::element( 'link', array( + 'type' => 'application/rdf+xml', + 'title' => $title->getPrefixedText(), + 'href' => SpecialPage::getTitleFor( 'ExportRDF', $title->getPrefixedText() )->getLocalUrl( 'xmlmime=rdf' ) + ) + ); + } + + /** + * A method that precedes the process() callback, it takes care of separating + * value and caption (instead of leaving this to a more complex regexp). + * + * @since 1.9 + * + * @param array $semanticLink expects (linktext, properties, value|caption) + * + * @return string + */ + protected function preprocess( array $semanticLink ) { + $value = ''; + $caption = false; + + if ( array_key_exists( 2, $semanticLink ) ) { + $parts = explode( '|', $semanticLink[2] ); + if ( array_key_exists( 0, $parts ) ) { + $value = $parts[0]; + } + if ( array_key_exists( 1, $parts ) ) { + $caption = $parts[1]; + } + } + + if ( $caption !== false ) { + return $this->process( array( $semanticLink[0], $semanticLink[1], $value, $caption ) ); + } else { + return $this->process( array( $semanticLink[0], $semanticLink[1], $value ) ); + } + } + + /** + * This callback function strips out the semantic attributes from a wiki + * link. + * + * @since 1.9 + * + * @param array $semanticLink expects (linktext, properties, value|caption) + * + * @return string + */ + protected function process( array $semanticLink ) { + wfProfileIn( __METHOD__ ); + + if ( array_key_exists( 1, $semanticLink ) ) { + $property = $semanticLink[1]; + } else { + $property = ''; + } + + if ( array_key_exists( 2, $semanticLink ) ) { + $value = $semanticLink[2]; + } else { + $value = ''; + } + + if ( $value === '' ) { // silently ignore empty values + wfProfileOut( __METHOD__ ); + return ''; + } + + if ( $property == 'SMW' ) { + switch ( $value ) { + case 'on': + $this->isAnnotation = true; + break; + case 'off': + $this->isAnnotation = false; + break; + } + wfProfileOut( __METHOD__ ); + return ''; + } + + if ( array_key_exists( 3, $semanticLink ) ) { + $valueCaption = $semanticLink[3]; + } else { + $valueCaption = false; + } + + // Extract annotations and create tooltip. + $properties = preg_split( '/:[=:]/u', $property ); + + wfProfileOut( __METHOD__ ); + return $this->addPropertyValue( $properties, $value, $valueCaption ); + } + + /** + * Adds property values to the ParserOutput instance + * + * @since 1.9 + * + * @param array $properties + * + * @return string + */ + protected function addPropertyValue( array $properties, $value, $valueCaption ) { + wfProfileIn( __METHOD__ ); + + $subject = $this->parserData->getData()->getSubject(); + + // Add properties to the semantic container + foreach ( $properties as $property ) { + $dataValue = DataValueFactory::newPropertyValue( + $property, + $value, + $valueCaption, + $subject + ); + + if ( $this->isEnabled && $this->isAnnotation ) { + $this->parserData->addPropertyValue( $dataValue ); + } + } + + // Return the text representation + $result = $dataValue->getShortWikitext( true ); + + // If necessary add an error text + if ( ( $this->settings->get( 'smwgInlineErrors' ) && + $this->isEnabled && $this->isAnnotation ) && + ( !$dataValue->isValid() ) ) { + $result .= $dataValue->getErrorText(); + } + + wfProfileOut( __METHOD__ ); + return $result; + } + + /** + * Remove relevant SMW magic words from the given text and return + * an array of the names of all discovered magic words. Moreover, + * store this array in the current parser output, using the variable + * mSMWMagicWords and for MW 1.21+ 'smwmagicwords' + * + * @since 1.9 + * + * @param &$text + * + * @return array + */ + protected function stripMagicWords( &$text ) { + $words = array(); + $mw = MagicWord::get( 'SMW_NOFACTBOX' ); + + if ( $mw->matchAndRemove( $text ) ) { + $words[] = 'SMW_NOFACTBOX'; + } + + $mw = MagicWord::get( 'SMW_SHOWFACTBOX' ); + + if ( $mw->matchAndRemove( $text ) ) { + $words[] = 'SMW_SHOWFACTBOX'; + } + + // Store values into the mSMWMagicWords property + if ( method_exists( $this->parserData->getOutput(), 'setExtensionData' ) ) { + $this->parserData->getOutput()->setExtensionData( 'smwmagicwords', $words ); + } else { + $this->parserData->getOutput()->mSMWMagicWords = $words; + } + + return $words; + } +} diff --git a/includes/SMW_ParserExtensions.php b/includes/SMW_ParserExtensions.php deleted file mode 100644 index 989ce6b..0000000 --- a/includes/SMW_ParserExtensions.php +++ /dev/null @@ -1,212 +0,0 @@ -<?php -/** - * Static class to collect all functions related to parsing wiki text in SMW. - * It includes all parser function declarations and hooks. - * - * @file SMW_ParserExtensions.php - * @ingroup SMW - * - * @author Markus Krötzsch - * @author Denny Vrandecic - */ -class SMWParserExtensions { - - /// Temporarily store parser as it cannot be passed to call-back functions otherwise. - protected static $mTempParser; - /// Internal state for switching off/on SMW link annotations during parsing - protected static $mTempStoreAnnotations; - - /** - * This method will be called before an article is displayed or previewed. - * For display and preview we strip out the semantic properties and append them - * at the end of the article. - * - * @param Parser $parser - * @param string $text - * - * @return true - */ - static public function onInternalParseBeforeLinks( Parser &$parser, &$text ) { - global $smwgStoreAnnotations, $smwgLinksInValues; - - SMWParserExtensions::stripMagicWords( $text, $parser ); - - // Store the results if enabled (we have to parse them in any case, - // in order to clean the wiki source for further processing). - $smwgStoreAnnotations = smwfIsSemanticsProcessed( $parser->getTitle()->getNamespace() ); - SMWParserExtensions::$mTempStoreAnnotations = true; // used for [[SMW::on]] and [[SMW:off]] - - // Process redirects, if any (it seems that there is indeed no more direct way of getting this info from MW) - if ( $smwgStoreAnnotations ) { - $rt = Title::newFromRedirect( $text ); - - if ( !is_null( $rt ) ) { - $p = new SMWDIProperty( '_REDI' ); - $di = SMWDIWikiPage::newFromTitle( $rt, '__red' ); - SMWParseData::getSMWData( $parser )->addPropertyObjectValue( $p, $di ); - } - } - - // only used in subsequent callbacks, forgotten afterwards - SMWParserExtensions::$mTempParser = $parser; - - // In the regexp matches below, leading ':' escapes the markup, as known for Categories. - // Parse links to extract semantic properties. - if ( $smwgLinksInValues ) { // More complex regexp -- lib PCRE may cause segfaults if text is long :-( - $semanticLinkPattern = '/\[\[ # Beginning of the link - (?:([^:][^]]*):[=:])+ # Property name (or a list of those) - ( # After that: - (?:[^|\[\]] # either normal text (without |, [ or ]) - |\[\[[^]]*\]\] # or a [[link]] - |\[[^]]*\] # or an [external link] - )*) # all this zero or more times - (?:\|([^]]*))? # Display text (like "text" in [[link|text]]), optional - \]\] # End of link - /xu'; - $text = preg_replace_callback( $semanticLinkPattern, array( 'SMWParserExtensions', 'parsePropertiesCallback' ), $text ); - } else { // Simpler regexps -- no segfaults found for those, but no links in values. - $semanticLinkPattern = '/\[\[ # Beginning of the link - (?:([^:][^]]*):[=:])+ # Property name (or a list of those) - ([^\[\]]*) # content: anything but [, |, ] - \]\] # End of link - /xu'; - $text = preg_replace_callback( $semanticLinkPattern, array( 'SMWParserExtensions', 'simpleParsePropertiesCallback' ), $text ); - } - - // Add link to RDF to HTML header. - // TODO: do escaping via Html or Xml class. - SMWOutputs::requireHeadItem( - 'smw_rdf', '<link rel="alternate" type="application/rdf+xml" title="' . - htmlspecialchars( $parser->getTitle()->getPrefixedText() ) . '" href="' . - htmlspecialchars( - SpecialPage::getTitleFor( 'ExportRDF', $parser->getTitle()->getPrefixedText() )->getLocalUrl( 'xmlmime=rdf' ) - ) . "\" />" - ); - - SMWOutputs::commitToParser( $parser ); - return true; // always return true, in order not to stop MW's hook processing! - } - - /** - * This callback function strips out the semantic attributes from a wiki - * link. Expected parameter: array(linktext, properties, value|caption) - * This function is a preprocessing for smwfParsePropertiesCallback, and - * takes care of separating value and caption (instead of leaving this to - * a more complex regexp). - */ - static public function simpleParsePropertiesCallback( $semanticLink ) { - $value = ''; - $caption = false; - - if ( array_key_exists( 2, $semanticLink ) ) { - $parts = explode( '|', $semanticLink[2] ); - if ( array_key_exists( 0, $parts ) ) { - $value = $parts[0]; - } - if ( array_key_exists( 1, $parts ) ) { - $caption = $parts[1]; - } - } - - if ( $caption !== false ) { - return SMWParserExtensions::parsePropertiesCallback( array( $semanticLink[0], $semanticLink[1], $value, $caption ) ); - } else { - return SMWParserExtensions::parsePropertiesCallback( array( $semanticLink[0], $semanticLink[1], $value ) ); - } - } - - /** - * This callback function strips out the semantic attributes from a wiki - * link. Expected parameter: array(linktext, properties, value, caption) - */ - static public function parsePropertiesCallback( $semanticLink ) { - global $smwgInlineErrors, $smwgStoreAnnotations; - - wfProfileIn( 'smwfParsePropertiesCallback (SMW)' ); - - if ( array_key_exists( 1, $semanticLink ) ) { - $property = $semanticLink[1]; - } else { - $property = ''; - } - - if ( array_key_exists( 2, $semanticLink ) ) { - $value = $semanticLink[2]; - } else { - $value = ''; - } - - if ( $value === '' ) { // silently ignore empty values - wfProfileOut( 'smwfParsePropertiesCallback (SMW)' ); - return ''; - } - - if ( $property == 'SMW' ) { - switch ( $value ) { - case 'on': - SMWParserExtensions::$mTempStoreAnnotations = true; - break; - case 'off': - SMWParserExtensions::$mTempStoreAnnotations = false; - break; - } - wfProfileOut( 'smwfParsePropertiesCallback (SMW)' ); - return ''; - } - - if ( array_key_exists( 3, $semanticLink ) ) { - $valueCaption = $semanticLink[3]; - } else { - $valueCaption = false; - } - - // Extract annotations and create tooltip. - $properties = preg_split( '/:[=:]/u', $property ); - - foreach ( $properties as $singleprop ) { - $dv = SMWParseData::addProperty( $singleprop, $value, $valueCaption, SMWParserExtensions::$mTempParser, $smwgStoreAnnotations && SMWParserExtensions::$mTempStoreAnnotations ); - } - - $result = $dv->getShortWikitext( true ); - - if ( ( $smwgInlineErrors && $smwgStoreAnnotations && SMWParserExtensions::$mTempStoreAnnotations ) && ( !$dv->isValid() ) ) { - $result .= $dv->getErrorText(); - } - - wfProfileOut( 'smwfParsePropertiesCallback (SMW)' ); - - return $result; - } - - /** - * Remove relevant SMW magic words from the given text and return - * an array of the names of all discovered magic words. Moreover, - * store this array in the current parser output, using the variable - * mSMWMagicWords. - * - * @note moved from SMWParseData::stripMagicWords as it is only used - * in here - * - * @since 1.9 - */ - static public function stripMagicWords( &$text, Parser $parser ) { - $words = array(); - $mw = MagicWord::get( 'SMW_NOFACTBOX' ); - - if ( $mw->matchAndRemove( $text ) ) { - $words[] = 'SMW_NOFACTBOX'; - } - - $mw = MagicWord::get( 'SMW_SHOWFACTBOX' ); - - if ( $mw->matchAndRemove( $text ) ) { - $words[] = 'SMW_SHOWFACTBOX'; - } - - $output = $parser->getOutput(); - $output->mSMWMagicWords = $words; - - return $words; - } - -} diff --git a/includes/Setup.php b/includes/Setup.php index 3354267..693f0e8 100644 --- a/includes/Setup.php +++ b/includes/Setup.php @@ -72,7 +72,8 @@ // Additional special properties (modification date etc.) $wgHooks['NewRevisionFromEditComplete'][] = 'SMWHooks::onNewRevisionFromEditComplete'; - $wgHooks['InternalParseBeforeLinks'][] = 'SMWParserExtensions::onInternalParseBeforeLinks'; // parse annotations in [[link syntax]] + // Parsing [[link::syntax]] and resolves property annotations + $wgHooks['InternalParseBeforeLinks'][] = 'SMWHooks::onInternalParseBeforeLinks'; $wgHooks['OutputPageParserOutput'][] = 'SMWFactbox::onOutputPageParserOutput'; // copy some data for later Factbox display $wgHooks['ArticleFromTitle'][] = 'SMWHooks::onArticleFromTitle'; // special implementations for property/type articles @@ -122,7 +123,7 @@ $wgAutoloadClasses['SMWFactbox'] = $incDir . 'SMW_Factbox.php'; $wgAutoloadClasses['SMWInfolink'] = $incDir . 'SMW_Infolink.php'; $wgAutoloadClasses['SMWOutputs'] = $incDir . 'SMW_Outputs.php'; - $wgAutoloadClasses['SMWParserExtensions'] = $incDir . 'SMW_ParserExtensions.php'; + $wgAutoloadClasses['SMW\ParserTextProcessor'] = $incDir . 'ParserTextProcessor.php'; $wgAutoloadClasses['SMWQueryLanguage'] = $incDir . 'SMW_QueryLanguage.php'; $wgAutoloadClasses['SMWSemanticData'] = $incDir . 'SMW_SemanticData.php'; $wgAutoloadClasses['SMWPageLister'] = $incDir . 'SMW_PageLister.php'; diff --git a/tests/phpunit/includes/HooksTest.php b/tests/phpunit/includes/HooksTest.php index e0ad9df..66eed79 100644 --- a/tests/phpunit/includes/HooksTest.php +++ b/tests/phpunit/includes/HooksTest.php @@ -382,4 +382,19 @@ } } } + + /** + * @test SMWHooks::onInternalParseBeforeLinks + * @dataProvider getTextDataProvider + * + * @since 1.9 + * + * @param $text + */ + public function testOnInternalParseBeforeLinks( $text ) { + $parser = $this->getParser(); + $result = SMWHooks::onInternalParseBeforeLinks( $parser, $text ); + + $this->assertTrue( $result ); + } } diff --git a/tests/phpunit/includes/ParserTextProcessorTest.php b/tests/phpunit/includes/ParserTextProcessorTest.php new file mode 100644 index 0000000..2584bc5 --- /dev/null +++ b/tests/phpunit/includes/ParserTextProcessorTest.php @@ -0,0 +1,389 @@ +<?php + +namespace SMW\Test; + +use SMW\ParserTextProcessor; +use SMW\ParserData; +use SMW\Settings; + +use SMWDIProperty; +use SMWDataItem; +use SMWDataValueFactory; +use Title; +use MWException; +use ParserOutput; +use ReflectionClass; + +/** + * Tests for the SMW\ParserTextProcessor class + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * http://www.gnu.org/copyleft/gpl.html + * + * @since 1.9 + * + * @file + * @ingroup SMW + * @ingroup SMWParser + * @ingroup Test + * + * @group SMW + * @group SMWExtension + * + * @licence GNU GPL v2+ + * @author mwjames + */ + +/** + * Testing methods provided by the ParserTextProcessor class + * + * @ingroup SMW + */ +class ParserTextProcessorTest extends \MediaWikiTestCase { + + protected $className = 'SMW\ParserTextProcessor'; + + /** + * Provides text samples + * + * @return array + */ + public function getTextDataProvider() { + return array( + + // #0 NS_MAIN; [[FooBar...]] with a different caption + array( + NS_MAIN, + array( + 'smwgNamespacesWithSemanticLinks' => array( NS_MAIN => true ), + 'smwgLinksInValues' => false, + 'smwgInlineErrors' => true, + ), + 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' . + ' [[FooBar::dictumst|寒い]] cursus. Nisl sit condimentum Quisque facilisis' . + ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' . + ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[foo::9001]] et Donec.', + array( + 'resultText' => 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' . + ' [[:Dictumst|寒い]] cursus. Nisl sit condimentum Quisque facilisis' . + ' Suspendisse [[:Tincidunt semper|tincidunt semper]] facilisi dolor Aenean. Ut' . + ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[:9001|9001]] et Donec.', + 'propertyCount' => 3, + 'propertyLabel' => array( 'Foo', 'Bar', 'FooBar' ), + 'propertyValue' => array( 'Dictumst', 'Tincidunt semper', '9001' ) + ) + ), + + // #1 NS_MAIN; [[FooBar...]] with a different caption and smwgLinksInValues = true + array( + NS_MAIN, + array( + 'smwgNamespacesWithSemanticLinks' => array( NS_MAIN => true ), + 'smwgLinksInValues' => true, + 'smwgInlineErrors' => true, + ), + 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' . + ' [[FooBar::dictumst|寒い]] cursus. Nisl sit condimentum Quisque facilisis' . + ' Suspendisse [[Bar::[[tincidunt semper]]]] facilisi dolor Aenean. Ut' . + ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[foo::[http:://www/foo/9001] ]] et Donec.', + array( + 'resultText' => 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' . + ' [[:Dictumst|寒い]] cursus. Nisl sit condimentum Quisque facilisis' . + ' Suspendisse [[:Tincidunt semper|tincidunt semper]] facilisi dolor Aenean. Ut' . + ' Aliquam {{volutpat}} arcu ultrices eu Ut quis'. + ' [[:Http:://www/foo/9001|http:://www/foo/9001]] et Donec.', + 'propertyCount' => 3, + 'propertyLabel' => array( 'Foo', 'Bar', 'FooBar' ), + 'propertyValue' => array( 'Dictumst', 'Tincidunt semper', 'Http:://www/foo/9001' ) + ) + ), + + // #1 NS_MAIN, [[-FooBar...]] produces an error with inlineErrors = true + // (only check for an indication of an error in 'resultText' ) + array( + NS_MAIN, + array( + 'smwgNamespacesWithSemanticLinks' => array( NS_MAIN => true ), + 'smwgLinksInValues' => false, + 'smwgInlineErrors' => true, + ), + 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' . + ' [[-FooBar::dictumst|重い]] cursus. Nisl sit condimentum Quisque facilisis' . + ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' . + ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[foo::9001]] et Donec.', + array( + 'resultText' => 'class="smw-highlighter" data-type="4" data-state="inline"', + 'propertyCount' => 2, + 'propertyLabel' => array( 'Foo', 'Bar' ), + 'propertyValue' => array( 'Tincidunt semper', '9001' ) + ) + ), + + // #2 NS_MAIN, [[-FooBar...]] produces an error but inlineErrors = false + array( + NS_MAIN, + array( + 'smwgNamespacesWithSemanticLinks' => array( NS_MAIN => true ), + 'smwgLinksInValues' => false, + 'smwgInlineErrors' => false, + ), + 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' . + ' [[-FooBar::dictumst|軽い]] cursus. Nisl sit condimentum Quisque facilisis' . + ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' . + ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[foo::9001]] et Donec.', + array( + 'resultText' => 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' . + ' 軽い cursus. Nisl sit condimentum Quisque facilisis' . + ' Suspendisse [[:Tincidunt semper|tincidunt semper]] facilisi dolor Aenean. Ut' . + ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[:9001|9001]] et Donec.', + 'propertyCount' => 2, + 'propertyLabel' => array( 'Foo', 'Bar' ), + 'propertyValue' => array( 'Tincidunt semper', '9001' ) + ) + ), + + // #3 NS_HELP disabled + array( + NS_HELP, + array( + 'smwgNamespacesWithSemanticLinks' => array( NS_HELP => false ), + 'smwgLinksInValues' => false, + 'smwgInlineErrors' => true, + ), + 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' . + ' [[FooBar::dictumst|おもろい]] cursus. Nisl sit condimentum Quisque facilisis' . + ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' . + ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[foo::9001]] et Donec.', + array( + 'resultText' => 'Lorem ipsum dolor sit &$% consectetuer auctor at quis' . + ' [[:Dictumst|おもろい]] cursus. Nisl sit condimentum Quisque facilisis' . + ' Suspendisse [[:Tincidunt semper|tincidunt semper]] facilisi dolor Aenean. Ut' . + ' Aliquam {{volutpat}} arcu ultrices eu Ut quis [[:9001|9001]] et Donec.', + 'propertyCount' => 0, + 'propertyLabel' => array(), + 'propertyValue' => array() + ) + ), + ); + } + + /** + * Provides magic words sample text + * + * @return array + */ + public function getMagicWordDataProvider() { + return array( + // #0 __NOFACTBOX__ + array( + NS_MAIN, + 'Lorem ipsum dolor sit amet consectetuer auctor at quis' . + ' [[Foo::dictumst cursus]]. Nisl sit condimentum Quisque facilisis' . + ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' . + ' __NOFACTBOX__ ', + array( 'SMW_NOFACTBOX' ) + ), + + // #1 __SHOWFACTBOX__ + array( + NS_HELP, + 'Lorem ipsum dolor sit amet consectetuer auctor at quis' . + ' [[Foo::dictumst cursus]]. Nisl sit condimentum Quisque facilisis' . + ' Suspendisse [[Bar::tincidunt semper]] facilisi dolor Aenean. Ut' . + ' __SHOWFACTBOX__', + array( 'SMW_SHOWFACTBOX' ) + ), + ); + } + + /** + * Helper method that returns a random string + * + * @since 1.9 + * + * @param $length + * + * @return string + */ + private function getRandomString( $length = 10 ) { + return substr( str_shuffle( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ), 0, $length ); + } + + /** + * Helper method that returns a Title object + * + * @param $namespace + * + * @return Title + */ + private function getTitle( $namespace = NS_MAIN ){ + return Title::newFromText( $this->getRandomString(), $namespace ); + } + + /** + * Helper method that returns a ParserOutput object + * + * @return ParserOutput + */ + private function getParserOutput(){ + return new ParserOutput(); + } + + /** + * Helper method that returns a ParserData object + * + * @param $title + * @param $parserOutput + * + * @return ParserData + */ + private function getParserData( Title $title, ParserOutput $parserOutput ){ + return new ParserData( $title, $parserOutput ); + } + + /** + * Helper method that returns a ParserTextProcessor object + * + * @param $title + * @param $parserOutput + * @param $settings + * + * @return ParserTextProcessor + */ + private function getInstance( Title $title, ParserOutput $parserOutput, array $settings = array() ) { + return new ParserTextProcessor( + $this->getParserData( $title, $parserOutput ), + Settings::newFromArray( $settings ) + ); + } + + /** + * @test ParserTextProcessor::__construct + * @dataProvider getTextDataProvider + * + * @since 1.9 + * + * @param $namespace + */ + public function testConstructor( $namespace ) { + $instance = $this->getInstance( $this->getTitle( $namespace ), $this->getParserOutput() ); + $this->assertInstanceOf( $this->className, $instance ); + } + + /** + * @test ParserTextProcessor::getRDFUrl + * + * @since 1.9 + */ + public function testGetRDFUrl() { + $reflection = new ReflectionClass( $this->className ); + + $parserOutput = $this->getParserOutput(); + $title = $this->getTitle(); + $instance = $this->getInstance( $title, $parserOutput ); + + // Make proected method accessible + $method = $reflection->getMethod( 'getRDFUrl' ); + $method->setAccessible( true ); + + // Invoke the instance + $result = $method->invoke( $instance, $title ); + + // Doing a lazy check, the url has to contain the title text + $this->assertInternalType( 'string', $result ); + $this->assertContains( 'title="' . $title->getText() . '"', $result ); + } + + /** + * @test ParserTextProcessor::stripMagicWords + * @dataProvider getMagicWordDataProvider + * + * @since 1.9 + * + * @param $namespace + * @param $text + * @param $expected + */ + public function testStripMagicWords( $namespace, $text, array $expected ) { + $reflection = new ReflectionClass( $this->className ); + + $parserOutput = $this->getParserOutput(); + $title = $this->getTitle( $namespace ); + $instance = $this->getInstance( $title, $parserOutput ); + + // Make protected method accessible + $method = $reflection->getMethod( 'stripMagicWords' ); + $method->setAccessible( true ); + + $result = $method->invoke( $instance, array( &$text ) ); + + // Check return values + $this->assertInternalType( 'array', $result ); + $this->assertEquals( $expected, $result ); + + // Check values against ParserData/ParserOutput object + $parserData = $this->getParserData( $title, $parserOutput ); + + if ( method_exists( $parserOutput, 'getExtensionData' ) ) { + $this->assertEquals( $expected, $parserData->getOutput()->getExtensionData( 'smwmagicwords' ) ); + } else { + $this->assertEquals( $expected, $parserData->getOutput()->mSMWMagicWords ); + } + } + + /** + * @test ParserTextProcessor::parse + * @dataProvider getTextDataProvider + * + * @since 1.9 + * + * @param $namespace + * @param $settings + * @param $text + * @param $expected + */ + public function testParse( $namespace, array $settings, $text, array $expected ) { + $parserOutput = $this->getParserOutput(); + $title = $this->getTitle( $namespace ); + $instance = $this->getInstance( $title, $parserOutput, $settings ); + + // Text parsing + $instance->parse( $text ); + + // Check transformed text + $this->assertContains( $expected['resultText'], $text ); + + // Re-read data from stored parserOutput + $parserData = $this->getParserData( $title, $parserOutput ); + + // Check the returned instance + $this->assertInstanceOf( 'SMWSemanticData', $parserData->getData() ); + $this->assertCount( $expected['propertyCount'], $parserData->getData()->getProperties() ); + + // Check added properties + foreach ( $parserData->getData()->getProperties() as $key => $diproperty ){ + $this->assertInstanceOf( 'SMWDIProperty', $diproperty ); + $this->assertContains( $diproperty->getLabel(), $expected['propertyLabel'] ); + + // Check added property values + foreach ( $parserData->getData()->getPropertyValues( $diproperty ) as $dataItem ){ + $dataValue = SMWDataValueFactory::newDataItemValue( $dataItem, $diproperty ); + if ( $dataValue->getDataItem()->getDIType() === SMWDataItem::TYPE_WIKIPAGE ){ + $this->assertContains( $dataValue->getWikiValue(), $expected['propertyValue'] ); + } + } + } + } +} -- To view, visit https://gerrit.wikimedia.org/r/60393 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I66b812e0276820fa58dbbfbc632236ea6f8ba6d5 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/SemanticMediaWiki Gerrit-Branch: master Gerrit-Owner: Mwjames <jamesin.hongkon...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits