2015-10-02 19:46:48 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace RelatedArticles;
|
|
|
|
|
2015-11-19 03:49:46 +00:00
|
|
|
use BetaFeatures;
|
|
|
|
use ConfigFactory;
|
2015-10-02 19:46:48 +00:00
|
|
|
use Parser;
|
|
|
|
use Exception;
|
|
|
|
use Title;
|
|
|
|
use SkinTemplate;
|
|
|
|
use BaseTemplate;
|
|
|
|
use Skin;
|
|
|
|
use Html;
|
2015-10-08 00:21:13 +00:00
|
|
|
use OutputPage;
|
|
|
|
use ParserOutput;
|
2015-11-19 03:49:46 +00:00
|
|
|
use User;
|
2015-10-02 19:46:48 +00:00
|
|
|
|
|
|
|
class Hooks {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for the <code>ParserFirstCallInit</code> hook.
|
|
|
|
*
|
|
|
|
* Registers the <code>related</code> parser function (see
|
|
|
|
* {@see Hooks::onFuncRelated}).
|
|
|
|
*
|
|
|
|
* @param Parser $parser
|
|
|
|
* @return boolean Always <code>true</code>
|
|
|
|
*/
|
|
|
|
public static function onParserFirstCallInit( Parser &$parser ) {
|
|
|
|
$parser->setFunctionHook( 'related', 'RelatedArticles\\Hooks::onFuncRelated' );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The <code>related</code> parser function.
|
|
|
|
*
|
|
|
|
* Appends the arguments to the internal list so that it can be used
|
|
|
|
* more that once per page.
|
2015-10-08 00:21:13 +00:00
|
|
|
* 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.
|
2015-10-02 19:46:48 +00:00
|
|
|
*
|
|
|
|
* @todo Test for uniqueness
|
2015-10-08 00:21:13 +00:00
|
|
|
* @param Parser $parser
|
2015-10-02 19:46:48 +00:00
|
|
|
*
|
|
|
|
* @return string Always <code>''</code>
|
|
|
|
*/
|
2015-10-08 00:21:13 +00:00
|
|
|
public static function onFuncRelated( Parser $parser ) {
|
|
|
|
$parserOutput = $parser->getOutput();
|
2015-11-14 00:03:39 +00:00
|
|
|
$relatedPages = $parserOutput->getExtensionData( 'RelatedArticles' );
|
|
|
|
if ( !$relatedPages ) {
|
|
|
|
$relatedPages = array();
|
2015-10-08 00:21:13 +00:00
|
|
|
}
|
2015-10-02 19:46:48 +00:00
|
|
|
$args = func_get_args();
|
|
|
|
array_shift( $args );
|
|
|
|
|
2015-11-14 00:03:39 +00:00
|
|
|
// Add all the related pages passed by the parser function
|
2015-10-08 00:21:13 +00:00
|
|
|
// {{#related:Test with read more|Foo|Bar}}
|
2015-11-14 00:03:39 +00:00
|
|
|
foreach ( $args as $relatedPage ) {
|
|
|
|
$relatedPages[] = $relatedPage;
|
2015-10-02 19:46:48 +00:00
|
|
|
}
|
2015-11-14 00:03:39 +00:00
|
|
|
$parserOutput->setExtensionData( 'RelatedArticles', $relatedPages );
|
2015-10-02 19:46:48 +00:00
|
|
|
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for the <code>ParserClearState</code> hook.
|
|
|
|
*
|
2015-11-14 00:03:39 +00:00
|
|
|
* Empties the internal list so that related pages are not passed on to future
|
2015-10-08 00:21:13 +00:00
|
|
|
* ParserOutput's - note that {{#related:Foo}} appends and can be used multiple times
|
|
|
|
* in the page.
|
2015-10-02 19:46:48 +00:00
|
|
|
*
|
|
|
|
* @param Parser $parser
|
|
|
|
* @return boolean Always <code>true</code>
|
|
|
|
*/
|
|
|
|
public static function onParserClearState( Parser &$parser ) {
|
2015-10-16 14:05:39 +00:00
|
|
|
$parserOutput = $parser->getOutput();
|
|
|
|
|
|
|
|
$parserOutput->setExtensionData( 'RelatedArticles', array() );
|
|
|
|
|
|
|
|
// FIXME: Remove in 30 days (T115698)
|
|
|
|
$parserOutput->unsetProperty( 'RelatedArticles' );
|
2015-10-02 19:46:48 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-11-14 00:03:39 +00:00
|
|
|
* Passes the related pages list from the cached parser output
|
2015-10-14 10:29:36 +00:00
|
|
|
* object to the output page for rendering.
|
|
|
|
*
|
2015-11-14 00:03:39 +00:00
|
|
|
* The list of related pages will be retrieved using
|
2015-11-10 17:03:06 +00:00
|
|
|
* <code>ParserOutput#getExtensionData</code>.
|
2015-10-02 19:46:48 +00:00
|
|
|
*
|
2015-10-08 00:21:13 +00:00
|
|
|
* @param OutputPage $out
|
|
|
|
* @param ParserOutput $parserOutput
|
2015-10-02 19:46:48 +00:00
|
|
|
* @return boolean Always <code>true</code>
|
|
|
|
*/
|
2015-10-08 00:21:13 +00:00
|
|
|
public static function onOutputPageParserOutput( OutputPage &$out, ParserOutput $parserOutput ) {
|
|
|
|
$related = $parserOutput->getExtensionData( 'RelatedArticles' );
|
2015-10-02 19:46:48 +00:00
|
|
|
|
2015-10-08 00:21:13 +00:00
|
|
|
if ( $related ) {
|
|
|
|
$out->setProperty( 'RelatedArticles', $related );
|
|
|
|
}
|
2015-10-02 19:46:48 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-11-14 00:03:39 +00:00
|
|
|
* Generates anchor element attributes for each entry in list of pages.
|
2015-10-02 19:46:48 +00:00
|
|
|
*
|
|
|
|
* The attributes that are generated are: <code>href</code>,
|
|
|
|
* <code>text</code>, and <code>class</code>, with the latter always
|
|
|
|
* set to <code>"interwiki-relart"</code>.
|
|
|
|
*
|
2015-11-14 00:03:39 +00:00
|
|
|
* If the the page is of the form <code>"Foo && Bar"</code>, then
|
2015-10-02 19:46:48 +00:00
|
|
|
* the <code>text</code> attribute will be set to "Bar", otherwise the
|
2015-11-14 00:03:39 +00:00
|
|
|
* page's {@see Title::getPrefixedText prefixed text} will be used.
|
2015-10-02 19:46:48 +00:00
|
|
|
*
|
2015-11-14 00:03:39 +00:00
|
|
|
* @param array[string] $relatedPages
|
2015-10-02 19:46:48 +00:00
|
|
|
* @return array An array of maps, each with <code>href</code>,
|
|
|
|
* <code>text</code>, and <code>class</code> entries.
|
|
|
|
*/
|
2015-11-14 00:03:39 +00:00
|
|
|
private static function getRelatedPagesUrls( array $relatedPages ) {
|
|
|
|
$relatedPagesUrls = array();
|
2015-10-02 19:46:48 +00:00
|
|
|
|
2015-11-14 00:03:39 +00:00
|
|
|
foreach ( $relatedPages as $page ) {
|
2015-10-02 19:46:48 +00:00
|
|
|
// Tribute to Evan
|
2015-11-14 00:03:39 +00:00
|
|
|
$page = urldecode( $page );
|
2015-10-02 19:46:48 +00:00
|
|
|
|
|
|
|
$altText = '';
|
2015-11-14 00:03:39 +00:00
|
|
|
if ( preg_match( '/\&\&/', $page ) ) {
|
|
|
|
$parts = array_map( 'trim', explode( '&&', $page, 2 ) );
|
|
|
|
$page = $parts[0];
|
2015-10-02 19:46:48 +00:00
|
|
|
$altText = $parts[1];
|
|
|
|
}
|
|
|
|
|
2015-11-14 00:03:39 +00:00
|
|
|
$title = Title::newFromText( $page );
|
2015-10-02 19:46:48 +00:00
|
|
|
if ( $title ) {
|
2015-11-14 00:03:39 +00:00
|
|
|
$relatedPagesUrls[] = array(
|
2015-10-02 19:46:48 +00:00
|
|
|
'href' => $title->getLocalURL(),
|
|
|
|
'text' => $altText ?: $title->getPrefixedText(),
|
|
|
|
'class' => 'interwiki-relart'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-11-14 00:03:39 +00:00
|
|
|
return $relatedPagesUrls;
|
2015-10-02 19:46:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for the <code>SkinBuildSidebar</code> hook.
|
|
|
|
*
|
2015-11-14 00:03:39 +00:00
|
|
|
* Retrieves the list of related pages
|
2015-11-19 03:49:46 +00:00
|
|
|
* and adds its HTML representation to the sidebar if the ReadMore feature
|
|
|
|
* is disabled and the beta feature is enabled by the user.
|
2015-10-02 19:46:48 +00:00
|
|
|
*
|
|
|
|
* @param Skin $skin
|
|
|
|
* @param array $bar
|
|
|
|
* @return boolean Always <code>true</code>
|
|
|
|
*/
|
|
|
|
public static function onSkinBuildSidebar( Skin $skin, &$bar ) {
|
|
|
|
$out = $skin->getOutput();
|
2015-11-14 00:03:39 +00:00
|
|
|
$relatedPages = $out->getProperty( 'RelatedArticles' );
|
2015-10-02 19:46:48 +00:00
|
|
|
|
2015-11-19 03:49:46 +00:00
|
|
|
if ( !Hooks::isAbleToShowRelatedPages( $relatedPages, $out->getUser() ) ) {
|
2015-10-02 19:46:48 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-14 00:03:39 +00:00
|
|
|
$relatedPagesUrls = self::getRelatedPagesUrls( $relatedPages );
|
2015-10-02 19:46:48 +00:00
|
|
|
|
|
|
|
// build relatedarticles <li>'s
|
2015-11-14 00:03:39 +00:00
|
|
|
$relatedPages = array();
|
|
|
|
foreach ( (array) $relatedPagesUrls as $url ) {
|
|
|
|
$relatedPages[] =
|
2015-10-02 19:46:48 +00:00
|
|
|
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(),
|
2015-11-14 00:03:39 +00:00
|
|
|
implode( '', $relatedPages )
|
2015-10-02 19:46:48 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handler for the <code>SkinTemplateToolboxEnd</code> hook.
|
|
|
|
*
|
2015-11-14 00:03:39 +00:00
|
|
|
* Retrieves the list of related pages from the template and
|
2015-11-19 03:49:46 +00:00
|
|
|
* <code>echo</code>s its HTML representation to the sidebar if the
|
|
|
|
* ReadMore feature is disabled and the beta feature is enabled by the user.
|
2015-10-02 19:46:48 +00:00
|
|
|
*
|
|
|
|
* @param SkinTemplate $skinTpl
|
|
|
|
* @return boolean Always <code>true</code>
|
|
|
|
*/
|
|
|
|
public static function onSkinTemplateToolboxEnd( BaseTemplate &$skinTpl ) {
|
2015-11-19 03:49:46 +00:00
|
|
|
$out = $skinTpl->getSkin()->getOutput();
|
|
|
|
$relatedPages = $out->getProperty( 'RelatedArticles' );
|
2015-10-02 19:46:48 +00:00
|
|
|
|
2015-11-19 03:49:46 +00:00
|
|
|
if ( !Hooks::isAbleToShowRelatedPages( $relatedPages, $out->getUser() ) ) {
|
2015-10-02 19:46:48 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-11-14 00:03:39 +00:00
|
|
|
$relatedPagesUrls = self::getRelatedPagesUrls( $relatedPages );
|
2015-10-02 19:46:48 +00:00
|
|
|
|
|
|
|
// build relatedarticles <li>'s
|
2015-11-14 00:03:39 +00:00
|
|
|
$relatedPages = array();
|
|
|
|
foreach ( (array) $relatedPagesUrls as $url ) {
|
|
|
|
$relatedPages[] =
|
2015-10-02 19:46:48 +00:00
|
|
|
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' ) .
|
2015-11-14 00:03:39 +00:00
|
|
|
implode( '', $relatedPages );
|
2015-10-02 19:46:48 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-10-16 14:05:39 +00:00
|
|
|
/**
|
|
|
|
* Handler for the <code>UnitTestsList</code> hook.
|
|
|
|
*
|
|
|
|
* Adds the path to this extension's PHPUnit test suite to the set of
|
|
|
|
* paths.
|
|
|
|
*
|
|
|
|
* @param array $paths
|
|
|
|
* @return boolean Always <code>true</code>
|
|
|
|
*/
|
|
|
|
public static function onUnitTestsList( array &$paths ) {
|
|
|
|
$paths[] = __DIR__ . '/../tests/phpunit';
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2015-11-19 03:49:46 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
2015-10-02 19:46:48 +00:00
|
|
|
}
|