diff options
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/LinksUpdateConstructed.php')
-rw-r--r-- | www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/LinksUpdateConstructed.php | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/LinksUpdateConstructed.php b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/LinksUpdateConstructed.php new file mode 100644 index 00000000..194c6916 --- /dev/null +++ b/www/wiki/extensions/SemanticMediaWiki/src/MediaWiki/Hooks/LinksUpdateConstructed.php @@ -0,0 +1,173 @@ +<?php + +namespace SMW\MediaWiki\Hooks; + +use Hooks; +use LinksUpdate; +use SMW\ApplicationFactory; +use SMW\SemanticData; +use SMW\NamespaceExaminer; +use Title; + +/** + * LinksUpdateConstructed hook is called at the end of LinksUpdate() + * + * @see http://www.mediawiki.org/wiki/Manual:Hooks/LinksUpdateConstructed + * + * @license GNU GPL v2+ + * @since 1.9 + * + * @author mwjames + */ +class LinksUpdateConstructed extends HookHandler { + + /** + * @var NamespaceExaminer + */ + private $namespaceExaminer; + + /** + * @var boolean + */ + private $enabledDeferredUpdate = true; + + /** + * @var boolean + */ + private $isReadOnly = false; + + /** + * @since 3.0 + * + * @param NamespaceExaminer $namespaceExaminer + */ + public function __construct( NamespaceExaminer $namespaceExaminer ) { + $this->namespaceExaminer = $namespaceExaminer; + } + + /** + * @since 3.0 + * + * @param boolean $isReadOnly + */ + public function isReadOnly( $isReadOnly ) { + $this->isReadOnly = (bool)$isReadOnly; + } + + /** + * @since 2.4 + */ + public function disableDeferredUpdate() { + $this->enabledDeferredUpdate = false; + } + + /** + * @since 1.9 + * + * @param LinksUpdate $linksUpdate + * + * @return true + */ + public function process( LinksUpdate $linksUpdate ) { + + if ( $this->isReadOnly ) { + return false; + } + + $title = $linksUpdate->getTitle(); + $latestRevID = $title->getLatestRevID( Title::GAID_FOR_UPDATE ); + + $opts = [ 'defer' => $this->enabledDeferredUpdate ]; + + // Allow any third-party extension to suppress the update process + if ( \Hooks::run( 'SMW::LinksUpdate::ApprovedUpdate', [ $title, $latestRevID ] ) === false ) { + return true; + } + + /** + * @var ParserData $parserData + */ + $parserData = ApplicationFactory::getInstance()->newParserData( + $title, + $linksUpdate->getParserOutput() + ); + + if ( $this->namespaceExaminer->isSemanticEnabled( $title->getNamespace() ) ) { + // #347 showed that an external process (e.g. RefreshLinksJob) can inject a + // ParserOutput without/cleared SemanticData which forces the Store updater + // to create an empty container that will clear all existing data. + if ( $parserData->getSemanticData()->isEmpty() ) { + $this->updateSemanticData( $parserData, $title, 'empty data' ); + } + } + + // Push updates on properties directly without delay + if ( $title->getNamespace() === SMW_NS_PROPERTY ) { + $opts['defer'] = false; + } + + // Scan the ParserOutput for a possible externally set option + if ( $linksUpdate->getParserOutput()->getExtensionData( $parserData::OPT_FORCED_UPDATE ) === true ) { + $parserData->setOption( $parserData::OPT_FORCED_UPDATE, true ); + } + + // Update incurred by a template change and is signaled through + // the following condition + if ( $linksUpdate->mTemplates !== [] && $linksUpdate->mRecursive === false ) { + $parserData->setOption( $parserData::OPT_FORCED_UPDATE, true ); + } + + $parserData->setOrigin( 'LinksUpdateConstructed' ); + $parserData->updateStore( $opts ); + + // Track the update on per revision because MW 1.29 made the LinksUpdate a + // EnqueueableDataUpdate which creates updates as JobSpecification + // (refreshLinksPrioritized) and posses a possibility of running an + // update more than once for the same RevID + $parserData->markUpdate( $latestRevID ); + + return true; + } + + /** + * To ensure that for a Title and its current revision a ParserOutput + * object is really meant to be "empty" (e.g. delete action initiated by a + * human) the content is re-parsed in order to fetch the newest available data + * + * @note Parsing is expensive but it is more expensive to loose data or to + * expect that an external process adheres the object contract + */ + private function updateSemanticData( &$parserData, $title, $reason = '' ) { + + $this->log( + [ + 'LinksUpdateConstructed', + "Required content re-parse due to $reason", + $title->getPrefixedDBKey() + ] + ); + + $semanticData = $this->reparseAndFetchSemanticData( $title ); + + if ( $semanticData instanceof SemanticData ) { + $parserData->setSemanticData( $semanticData ); + } + } + + private function reparseAndFetchSemanticData( $title ) { + + $contentParser = ApplicationFactory::getInstance()->newContentParser( $title ); + $parserOutput = $contentParser->parse()->getOutput(); + + if ( $parserOutput === null ) { + return null; + } + + if ( method_exists( $parserOutput, 'getExtensionData' ) ) { + return $parserOutput->getExtensionData( 'smwdata' ); + } + + return $parserOutput->mSMWData; + } + +} |