ParserFirstCallInit hook. * * Registers the related parser function (see * {@see Hooks::onFuncRelated}). * * @param Parser $parser * @return boolean Always true */ public static function onParserFirstCallInit( Parser &$parser ) { $parser->setFunctionHook( 'related', 'RelatedArticles\\Hooks::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 * * @return string Always '' */ public static function onFuncRelated( Parser $parser ) { $parserOutput = $parser->getOutput(); $relatedPages = $parserOutput->getExtensionData( 'RelatedArticles' ); if ( !$relatedPages ) { $relatedPages = array(); } $args = func_get_args(); array_shift( $args ); // 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 ''; } /** * Handler for the ParserClearState hook. * * Empties the internal list so that related pages are not passed on to future * ParserOutput's - note that {{#related:Foo}} appends and can be used multiple times * in the page. * * @param Parser $parser * @return boolean Always true */ public static function onParserClearState( Parser &$parser ) { $parserOutput = $parser->getOutput(); $parserOutput->setExtensionData( 'RelatedArticles', array() ); // FIXME: Remove in 30 days (T115698) $parserOutput->unsetProperty( 'RelatedArticles' ); return true; } /** * 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 * @param ParserOutput $parserOutput * @return boolean Always true */ public static function onOutputPageParserOutput( OutputPage &$out, ParserOutput $parserOutput ) { $related = $parserOutput->getExtensionData( 'RelatedArticles' ); if ( $related ) { $out->setProperty( 'RelatedArticles', $related ); } return true; } /** * Generates anchor element attributes for each entry in list of pages. * * The attributes that are generated are: href, * text, and class, with the latter always * set to "interwiki-relart". * * If the the page is of the form "Foo && Bar", then * the text attribute will be set to "Bar", otherwise the * page's {@see Title::getPrefixedText prefixed text} will be used. * * @param array[string] $relatedPages * @return array An array of maps, each with href, * text, and class entries. */ private static function getRelatedPagesUrls( array $relatedPages ) { $relatedPagesUrls = array(); foreach ( $relatedPages as $page ) { // Tribute to Evan $page = urldecode( $page ); $altText = ''; if ( preg_match( '/\&\&/', $page ) ) { $parts = array_map( 'trim', explode( '&&', $page, 2 ) ); $page = $parts[0]; $altText = $parts[1]; } $title = Title::newFromText( $page ); if ( $title ) { $relatedPagesUrls[] = array( 'href' => $title->getLocalURL(), 'text' => $altText ?: $title->getPrefixedText(), 'class' => 'interwiki-relart' ); } }; return $relatedPagesUrls; } /** * Handler for the SkinBuildSidebar hook. * * Retrieves the list of related pages * and adds its HTML representation to the sidebar if the ReadMore feature * is disabled and the beta feature is enabled by the user. * * @param Skin $skin * @param array $bar * @return boolean Always true */ public static function onSkinBuildSidebar( Skin $skin, &$bar ) { $out = $skin->getOutput(); $relatedPages = $out->getProperty( 'RelatedArticles' ); if ( !Hooks::isAbleToShowRelatedPages( $relatedPages, $out->getUser() ) ) { return true; } $relatedPagesUrls = self::getRelatedPagesUrls( $relatedPages ); // build relatedarticles
  • 's $relatedPages = array(); foreach ( (array) $relatedPagesUrls as $url ) { $relatedPages[] = Html::rawElement( 'li', array( 'class' => htmlspecialchars( $url['class'] ) ), Html::element( 'a', array( 'href' => htmlspecialchars( $url['href'] ) ), $url['text'] ) ); } // build complete html $bar[$skin->msg( 'relatedarticles-title' )->text()] = Html::rawElement( 'ul', array(), implode( '', $relatedPages ) ); return true; } /** * Handler for the SkinTemplateToolboxEnd hook. * * Retrieves the list of related pages from the template and * echos its HTML representation to the sidebar if the * ReadMore feature is disabled and the beta feature is enabled by the user. * * @param SkinTemplate $skinTpl * @return boolean Always true */ public static function onSkinTemplateToolboxEnd( BaseTemplate &$skinTpl ) { $out = $skinTpl->getSkin()->getOutput(); $relatedPages = $out->getProperty( 'RelatedArticles' ); if ( !Hooks::isAbleToShowRelatedPages( $relatedPages, $out->getUser() ) ) { return true; } $relatedPagesUrls = self::getRelatedPagesUrls( $relatedPages ); // build relatedarticles
  • 's $relatedPages = array(); foreach ( (array) $relatedPagesUrls as $url ) { $relatedPages[] = Html::rawElement( 'li', array( 'class' => htmlspecialchars( $url['class'] ) ), Html::element( 'a', array( 'href' => htmlspecialchars( $url['href'] ) ), $url['text'] ) ); } // build complete html echo Html::closeElement( 'ul' ) . Html::closeElement( 'div' ) . Html::closeElement( 'div' ) . Html::openElement( 'div', array( 'class' => 'portal', 'role' => 'navigation', 'id' => 'p-relatedarticles', ) ) . Html::element( 'h3', array(), wfMessage( 'relatedarticles-title' )->text() ) . Html::openElement( 'div', array( 'class' => 'body' ) ) . Html::openElement( 'ul' ) . implode( '', $relatedPages ); return true; } /** * Handler for the UnitTestsList hook. * * Adds the path to this extension's PHPUnit test suite to the set of * paths. * * @param array $paths * @return boolean Always true */ public static function onUnitTestsList( array &$paths ) { $paths[] = __DIR__ . '/../tests/phpunit'; return true; } /** * GetBetaFeaturePreferences hook handler * @see https://www.mediawiki.org/wiki/Manual:Hooks/GetBetaFeaturePreferences * * @param User $user * @param array $preferences * * @return bool */ public static function onGetBetaFeaturePreferences( User $user, array &$preferences ) { $config = ConfigFactory::getDefaultInstance()->makeConfig( 'RelatedArticles' ); $wgExtensionAssetsPath = $config->get( 'ExtensionAssetsPath' ); $preferences['read-more'] = array( 'label-message' => 'relatedarticles-read-more-beta-feature-title', 'desc-message' => 'relatedarticles-read-more-beta-feature-description', 'screenshot' => array( 'ltr' => "$wgExtensionAssetsPath/RelatedArticles/images/BetaFeatures/wb-readmore-beta-ltr.svg", 'rtl' => "$wgExtensionAssetsPath/RelatedArticles/images/BetaFeatures/wb-readmore-beta-rtl.svg", ), 'info-link' => 'https://www.mediawiki.org/wiki/Reading/Web/Projects/Read_more', 'discussion-link' => 'https://www.mediawiki.org/wiki/Talk:Reading/Web/Projects/Read_more', ); return true; } /** * Check whether there are related articles that can be displayed, * the ReadMore feature is disabled, and the beta feature is * enabled by the user. Return true if BetaFeatures is not installed. * * @param mixed|null $relatedPages * @param User $user * @return bool * @throws \ConfigException */ private static function isAbleToShowRelatedPages( $relatedPages, User $user ) { $config = ConfigFactory::getDefaultInstance()->makeConfig( 'RelatedArticles' ); if ( !$relatedPages || $config->get( 'RelatedArticlesShowReadMore' ) ) { return false; } if ( class_exists( 'BetaFeatures' ) ) { return BetaFeatures::isFeatureEnabled( $user, 'read-more' ); } return true; } }