Convert to new hook system (Workshop)

This converts the CategtoryTree extension to the new (MW1.35) hook system.
The patch was written live during a hackathon workshop.
A recording of the workshop is available at
<https://www.youtube.com/watch?v=ZOj44Rbh0tM&t>.

Bug: T282110
Bug: T271011
Change-Id: Ie52c393af378a980a2dac4ae7076fd6c016a8e0e
This commit is contained in:
daniel 2021-05-22 15:43:11 +02:00 committed by Daniel Kinzler
parent 31314b9332
commit 2ee5743cfa
2 changed files with 94 additions and 38 deletions

View file

@ -11,9 +11,6 @@
"ConfigRegistry": {
"categorytree": "GlobalVarConfig::newInstance"
},
"ExtensionFunctions": [
"MediaWiki\\Extension\\CategoryTree\\Hooks::initialize"
],
"SpecialPages": {
"CategoryTree": {
"class": "MediaWiki\\Extension\\CategoryTree\\CategoryTreePage",
@ -84,16 +81,23 @@
"localBasePath": "modules",
"remoteExtPath": "CategoryTree/modules"
},
"HookHandlers": {
"default": {
"class": "MediaWiki\\Extension\\CategoryTree\\Hooks",
"services": [ "DBLoadBalancer", "MainConfig" ]
}
},
"Hooks": {
"ArticleFromTitle": "MediaWiki\\Extension\\CategoryTree\\Hooks::articleFromTitle",
"SpecialTrackingCategories::preprocess": "MediaWiki\\Extension\\CategoryTree\\Hooks::onSpecialTrackingCategoriesPreprocess",
"SpecialTrackingCategories::generateCatLink": "MediaWiki\\Extension\\CategoryTree\\Hooks::onSpecialTrackingCategoriesGenerateCatLink",
"SkinBuildSidebar": "MediaWiki\\Extension\\CategoryTree\\Hooks::onSkinBuildSidebar",
"ParserFirstCallInit": "MediaWiki\\Extension\\CategoryTree\\Hooks::setHooks",
"OutputPageMakeCategoryLinks": "MediaWiki\\Extension\\CategoryTree\\Hooks::outputPageMakeCategoryLinks",
"BeforePageDisplay": "MediaWiki\\Extension\\CategoryTree\\Hooks::addHeaders",
"BeforePageDisplayMobile": "MediaWiki\\Extension\\CategoryTree\\Hooks::addHeaders",
"OutputPageParserOutput": "MediaWiki\\Extension\\CategoryTree\\Hooks::parserOutput"
"MediaWikiServices": "default",
"ArticleFromTitle": "default",
"SpecialTrackingCategories::preprocess": "default",
"SpecialTrackingCategories::generateCatLink": "default",
"SkinBuildSidebar": "default",
"ParserFirstCallInit": "default",
"OutputPageMakeCategoryLinks": "default",
"BeforePageDisplay": "default",
"OutputPageParserOutput": "default",
"BeforePageDisplayMobile": "MediaWiki\\Extension\\CategoryTree\\Hooks::addHeaders"
},
"config": {
"CategoryTreeMaxChildren": {

View file

@ -26,7 +26,19 @@ namespace MediaWiki\Extension\CategoryTree;
use Article;
use Category;
use Config;
use Html;
use IContextSource;
use MediaWiki\Hook\BeforePageDisplayHook;
use MediaWiki\Hook\MediaWikiServicesHook;
use MediaWiki\Hook\OutputPageMakeCategoryLinksHook;
use MediaWiki\Hook\OutputPageParserOutputHook;
use MediaWiki\Hook\ParserFirstCallInitHook;
use MediaWiki\Hook\SkinBuildSidebarHook;
use MediaWiki\Hook\SpecialTrackingCategories__generateCatLinkHook;
use MediaWiki\Hook\SpecialTrackingCategories__preprocessHook;
use MediaWiki\MediaWikiServices;
use MediaWiki\Page\Hook\ArticleFromTitleHook;
use OutputPage;
use Parser;
use ParserOutput;
@ -35,15 +47,45 @@ use Sanitizer;
use Skin;
use SpecialPage;
use Title;
use Wikimedia\Rdbms\ILoadBalancer;
/**
* Hooks for the CategoryTree extension, an AJAX based gadget
* to display the category structure of a wiki
*
* @phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName
*/
class Hooks {
class Hooks implements
ArticleFromTitleHook,
SpecialTrackingCategories__preprocessHook,
SpecialTrackingCategories__generateCatLinkHook,
BeforePageDisplayHook,
OutputPageParserOutputHook,
MediaWikiServicesHook,
SkinBuildSidebarHook,
ParserFirstCallInitHook,
OutputPageMakeCategoryLinksHook
{
private const EXTENSION_DATA_FLAG = 'CategoryTree';
/** @var ILoadBalancer */
private $loadBalancer;
/**
* @var Config
*/
private $config;
/**
* @param ILoadBalancer $loadBalancer
* @param Config $config
*/
public function __construct( ILoadBalancer $loadBalancer, Config $config ) {
$this->loadBalancer = $loadBalancer;
$this->config = $config;
}
/**
* @internal For use by CategoryTreeCategoryViewer and CategoryTreePage only!
* @return bool
@ -56,8 +98,9 @@ class Hooks {
/**
* Adjusts config once MediaWiki is fully initialised
* TODO: Don't do this, lazy initialize the config
* @param MediaWikiServices $services
*/
public static function initialize() {
public function onMediaWikiServices( $services ) {
global $wgRequest;
global $wgCategoryTreeDefaultOptions, $wgCategoryTreeDefaultMode;
global $wgCategoryTreeCategoryPageOptions, $wgCategoryTreeCategoryPageMode;
@ -87,13 +130,12 @@ class Hooks {
/**
* @param Parser $parser
*/
public static function setHooks( Parser $parser ) {
global $wgCategoryTreeAllowTag;
if ( !$wgCategoryTreeAllowTag ) {
public function onParserFirstCallInit( $parser ) {
if ( !$this->config->get( 'CategoryTreeAllowTag' ) ) {
return;
}
$parser->setHook( 'categorytree', [ self::class, 'parserHook' ] );
$parser->setFunctionHook( 'categorytree', [ self::class, 'parserFunction' ] );
$parser->setHook( 'categorytree', [ $this, 'parserHook' ] );
$parser->setFunctionHook( 'categorytree', [ $this, 'parserFunction' ] );
}
/**
@ -103,7 +145,7 @@ class Hooks {
* @param string ...$params
* @return array|string
*/
public static function parserFunction( Parser $parser, ...$params ) {
public function parserFunction( Parser $parser, ...$params ) {
// first user-supplied parameter must be category name
if ( !$params ) {
// no category specified, return nothing
@ -131,7 +173,7 @@ class Hooks {
$cat . Html::closeElement( 'categorytree' );
} else {
// now handle just like a <categorytree> tag
$html = self::parserHook( $cat, $argv, $parser );
$html = $this->parserHook( $cat, $argv, $parser );
return [ $html, 'noparse' => true, 'isHTML' => true ];
}
}
@ -142,7 +184,7 @@ class Hooks {
* @param Skin $skin
* @param array &$sidebar
*/
public static function onSkinBuildSidebar( Skin $skin, array &$sidebar ) {
public function onSkinBuildSidebar( $skin, &$sidebar ) {
global $wgCategoryTreeSidebarRoot, $wgCategoryTreeSidebarOptions;
if ( !$wgCategoryTreeSidebarRoot ) {
@ -166,7 +208,7 @@ class Hooks {
* @param bool $allowMissing
* @return bool|string
*/
public static function parserHook(
public function parserHook(
$cat,
array $argv,
Parser $parser = null,
@ -203,7 +245,7 @@ class Hooks {
* @param OutputPage $outputPage
* @param ParserOutput $parserOutput
*/
public static function parserOutput( OutputPage $outputPage, ParserOutput $parserOutput ) {
public function onOutputPageParserOutput( $outputPage, $parserOutput ) : void {
if ( self::shouldForceHeaders() ) {
// Skip, we've already set the headers unconditionally
return;
@ -213,6 +255,17 @@ class Hooks {
}
}
/**
* This hook is called prior to outputting a page.
*
* @param OutputPage $out
* @param Skin $skin
* @return void This hook must not abort, it must return no value
*/
public function onBeforePageDisplay( $out, $skin ) : void {
self::addHeaders( $out );
}
/**
* BeforePageDisplay and BeforePageDisplayMobile hooks.
* These hooks are used when $wgCategoryTreeForceHeaders is set.
@ -231,8 +284,10 @@ class Hooks {
*
* @param Title $title
* @param Article|null &$article Article (object) that will be returned
* @param IContextSource $context
* @return bool|void True or no return value to continue or false to abort
*/
public static function articleFromTitle( Title $title, Article &$article = null ) {
public function onArticleFromTitle( $title, &$article, $context ) {
if ( $title->getNamespace() == NS_CATEGORY ) {
$article = new CategoryTreeCategoryPage( $title );
}
@ -245,11 +300,7 @@ class Hooks {
* @param array &$links
* @return bool
*/
public static function outputPageMakeCategoryLinks(
OutputPage $out,
array $categories,
array &$links
) {
public function onOutputPageMakeCategoryLinks( $out, $categories, &$links ) {
global $wgCategoryTreePageCategoryOptions, $wgCategoryTreeHijackPageCategories;
if ( !$wgCategoryTreeHijackPageCategories ) {
@ -258,7 +309,7 @@ class Hooks {
}
foreach ( $categories as $category => $type ) {
$links[$type][] = self::parserHook( $category, $wgCategoryTreePageCategoryOptions, null, null, true );
$links[$type][] = $this->parserHook( $category, $wgCategoryTreePageCategoryOptions, null, null, true );
}
CategoryTree::setHeaders( $out );
@ -289,8 +340,9 @@ class Hooks {
* @param array $trackingCategories [ 'msg' => Title, 'cats' => Title[] ]
* @phan-param array<string,array{msg:Title,cats:Title[]}> $trackingCategories
*/
public static function onSpecialTrackingCategoriesPreprocess(
SpecialPage $specialPage, array $trackingCategories
public function onSpecialTrackingCategories__preprocess(
$specialPage,
$trackingCategories
) {
$categoryDbKeys = [];
foreach ( $trackingCategories as $catMsg => $data ) {
@ -300,7 +352,7 @@ class Hooks {
}
$categories = [];
if ( $categoryDbKeys ) {
$dbr = wfGetDB( DB_REPLICA );
$dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
$res = $dbr->select(
'category',
[ 'cat_id', 'cat_title', 'cat_pages', 'cat_subcats', 'cat_files' ],
@ -321,16 +373,16 @@ class Hooks {
* @param Title $catTitle Title object of the linked category
* @param string &$html Result html
*/
public static function onSpecialTrackingCategoriesGenerateCatLink(
SpecialPage $specialPage, Title $catTitle, &$html
public function onSpecialTrackingCategories__generateCatLink( $specialPage,
$catTitle, &$html
) {
if ( !isset( $specialPage->categoryTreeCategories ) ) {
return;
}
$cat = null;
if ( isset( $specialPage->categoryTreeCategories[$catTitle->getDbKey()] ) ) {
$cat = $specialPage->categoryTreeCategories[$catTitle->getDbKey()];
if ( isset( $specialPage->categoryTreeCategories[$catTitle->getDBkey()] ) ) {
$cat = $specialPage->categoryTreeCategories[$catTitle->getDBkey()];
}
$html .= CategoryTree::createCountString( $specialPage->getContext(), $cat, 0 );