MakeGlobalVariablesScript hook. * * Sets the value of the wgRelatedArticles global variable * to the list of related articles in the cached parser output. * * @param array &$vars variables to be added into the output of OutputPage::headElement. * @param OutputPage $out OutputPage instance calling the hook */ public static function onMakeGlobalVariablesScript( &$vars, OutputPage $out ) { $editorCuratedPages = $out->getProperty( 'RelatedArticles' ); if ( $editorCuratedPages ) { $vars['wgRelatedArticles'] = $editorCuratedPages; } } /** * Uses the Disambiguator extension to test whether the page is a disambiguation page. * * If the Disambiguator extension isn't installed, then the test always fails, i.e. the page is * never a disambiguation page. * * @param Title $title * @return bool */ private static function isDisambiguationPage( Title $title ) { return \ExtensionRegistry::getInstance()->isLoaded( 'Disambiguator' ) && DisambiguatorHooks::isDisambiguationPage( $title ); } /** * Check whether the output page is a diff page * * @param OutputPage $out * @return bool */ private static function isDiffPage( OutputPage $out ) { $request = $out->getRequest(); $type = $request->getText( 'type' ); $diff = $request->getText( 'diff' ); $oldId = $request->getText( 'oldid' ); $isSpecialMobileDiff = $out->getTitle()->isSpecial( 'MobileDiff' ); return $type === 'revision' || $diff || $oldId || $isSpecialMobileDiff; } /** * Is ReadMore allowed on skin? * * Some wikis may want to only enable the feature on some skins, so we'll only * show it if the allow list (`RelatedArticlesFooterAllowedSkins` * configuration variable) is empty or the skin is listed. * * @param User $user * @param Skin $skin * @return bool */ private static function isReadMoreAllowedOnSkin( User $user, Skin $skin ) { $config = MediaWikiServices::getInstance()->getConfigFactory() ->makeConfig( 'RelatedArticles' ); $skins = $config->get( 'RelatedArticlesFooterAllowedSkins' ); $skinName = $skin->getSkinName(); return empty( $skins ) || in_array( $skinName, $skins ); } /** * Can the page show related articles? * * @param Skin $skin * @return bool */ private static function hasRelatedArticles( Skin $skin ): bool { $out = $skin->getOutput(); $title = $out->getContext()->getTitle(); $action = $out->getRequest()->getText( 'action', 'view' ); return $title->inNamespace( NS_MAIN ) && // T120735 $action === 'view' && !$title->isMainPage() && $title->exists() && !self::isDisambiguationPage( $title ) && !self::isDiffPage( $out ) && self::isReadMoreAllowedOnSkin( $out->getUser(), $out->getSkin() ); } /** * Handler for the BeforePageDisplay hook. * * Adds the ext.relatedArticles.readMore.bootstrap module * to the output when: * *
    *
  1. On mobile, the output is being rendered with * SkinMinervaBeta
  2. *
  3. The page is in mainspace
  4. *
  5. The action is 'view'
  6. *
  7. The page is not the Main Page
  8. *
  9. The page is not a disambiguation page
  10. *
  11. The page is not a diff page
  12. *
  13. The feature is allowed on the skin (see isReadMoreAllowedOnSkin() above)
  14. *
* * @param OutputPage $out The OutputPage object * @param Skin $skin Skin object that will be used to generate the page * @return bool Always true */ public static function onBeforePageDisplay( OutputPage $out, Skin $skin ) { if ( self::hasRelatedArticles( $skin ) ) { $out->addModules( [ 'ext.relatedArticles.readMore.bootstrap' ] ); $out->addModuleStyles( [ 'ext.relatedArticles.styles' ] ); } return true; } /** * ResourceLoaderGetConfigVars hook handler for setting a config variable * @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderGetConfigVars * * @param array &$vars Array of variables to be added into the output of the startup module. * @return bool */ public static function onResourceLoaderGetConfigVars( &$vars ) { $config = MediaWikiServices::getInstance()->getConfigFactory() ->makeConfig( 'RelatedArticles' ); $limit = $config->get( 'RelatedArticlesCardLimit' ); $vars['wgRelatedArticlesCardLimit'] = $limit; if ( $limit < 1 || $limit > 20 ) { throw new \RuntimeException( 'The value of wgRelatedArticlesCardLimit is not valid. It should be between 1 and 20.' ); } return true; } /** * Handler for the ParserFirstCallInit hook. * * Registers the related parser function (see * {@see Hooks::onFuncRelated}). * * @param Parser $parser Parser object * @return bool Always true */ public static function onParserFirstCallInit( Parser $parser ) { $parser->setFunctionHook( 'related', [ self::class, 'onFuncRelated' ] ); return true; } /** * The related parser function. * * Appends the arguments to the internal list so that it can be used * more that once per page. * We don't use setProperty here is there is no need * to store it as a page prop in the database, only in the cache. * * @todo Test for uniqueness * @param Parser $parser Parser object * @param string ...$args * * @return string Always '' */ public static function onFuncRelated( Parser $parser, ...$args ) { $parserOutput = $parser->getOutput(); $relatedPages = $parserOutput->getExtensionData( 'RelatedArticles' ); if ( !$relatedPages ) { $relatedPages = []; } // Add all the related pages passed by the parser function // {{#related:Test with read more|Foo|Bar}} foreach ( $args as $relatedPage ) { $relatedPages[] = $relatedPage; } $parserOutput->setExtensionData( 'RelatedArticles', $relatedPages ); return ''; } /** * Passes the related pages list from the cached parser output * object to the output page for rendering. * * The list of related pages will be retrieved using * ParserOutput#getExtensionData. * * @param OutputPage $out the OutputPage object * @param ParserOutput $parserOutput ParserOutput object * @return bool Always true */ public static function onOutputPageParserOutput( OutputPage $out, ParserOutput $parserOutput ) { $related = $parserOutput->getExtensionData( 'RelatedArticles' ); if ( $related ) { $out->setProperty( 'RelatedArticles', $related ); } return true; } /** * Create container for ReadMore cards so that they're correctly placed in all skins. * * @param string &$data * @param Skin $skin */ public static function onSkinAfterContent( &$data, Skin $skin ) { if ( self::hasRelatedArticles( $skin ) ) { $data .= \Html::element( 'div', [ 'class' => 'read-more-container' ] ); } } }