Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.47% covered (warning)
89.47%
51 / 57
66.67% covered (warning)
66.67%
4 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
HtmlPageLinkRendererEndHookHandler
89.47% covered (warning)
89.47%
51 / 57
66.67% covered (warning)
66.67%
4 / 6
15.26
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 factory
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 onHtmlPageLinkRendererEnd
88.89% covered (warning)
88.89%
8 / 9
0.00% covered (danger)
0.00%
0 / 1
3.01
 doHtmlPageLinkRendererEnd
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
5
 internalDoHtmlPageLinkRendererEnd
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
2
 shouldConvertNoBadTitle
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare( strict_types = 1 );
4
5namespace EntitySchema\MediaWiki\Hooks;
6
7use EntitySchema\DataAccess\LabelLookup;
8use HtmlArmor;
9use MediaWiki\Languages\LanguageFactory;
10use MediaWiki\Linker\Hook\HtmlPageLinkRendererEndHook;
11use MediaWiki\Linker\LinkRenderer;
12use MediaWiki\Title\Title;
13use RequestContext;
14
15/**
16 * @license GPL-2.0-or-later
17 */
18class HtmlPageLinkRendererEndHookHandler implements HtmlPageLinkRendererEndHook {
19
20    private LanguageFactory $languageFactory;
21    private LabelLookup $labelLookup;
22    private RequestContext $context;
23
24    public function __construct(
25        LanguageFactory $languageFactory,
26        LabelLookup $labelLookup,
27        RequestContext $context
28    ) {
29        $this->languageFactory = $languageFactory;
30        $this->labelLookup = $labelLookup;
31        $this->context = $context;
32    }
33
34    public static function factory(
35        LanguageFactory $languageFactory,
36        LabelLookup $labelLookup
37    ): self {
38        return new self(
39            $languageFactory,
40            $labelLookup,
41            RequestContext::getMain()
42        );
43    }
44
45    /**
46     * @inheritDoc
47     */
48    public function onHtmlPageLinkRendererEnd(
49        $linkRenderer,
50        $target,
51        $isKnown,
52        &$text,
53        &$extraAttribs,
54        &$ret
55    ): bool {
56        if ( !$this->context->hasTitle() ) {
57            // Short-circuit this hook if no title is
58            // set in the main context (T131176)
59            return true;
60        }
61
62        if ( !$target->inNamespace( NS_ENTITYSCHEMA_JSON ) ) {
63            return true;
64        }
65
66        return $this->doHtmlPageLinkRendererEnd(
67            $linkRenderer,
68            Title::newFromLinkTarget( $target ),
69            $text
70        );
71    }
72
73    /**
74     * @param LinkRenderer $linkRenderer
75     * @param Title $target
76     * @param HtmlArmor|string|null &$text
77     *
78     * @return bool always true
79     */
80    private function doHtmlPageLinkRendererEnd(
81        LinkRenderer $linkRenderer,
82        Title $target,
83        &$text
84    ): bool {
85        // if custom link text is given, there is no point in overwriting it
86        // but not if it is similar to the plain title
87        if ( $text !== null
88            && $target->getFullText() !== HtmlArmor::getHtml( $text )
89            && $target->getText() !== HtmlArmor::getHtml( $text )
90        ) {
91            return true;
92        }
93
94        $outputTitle = $this->context->getTitle();
95        '@phan-var Title $outputTitle';
96        if ( !$this->shouldConvertNoBadTitle( $outputTitle, $linkRenderer ) ) {
97            return true;
98        }
99
100        return $this->internalDoHtmlPageLinkRendererEnd(
101            $target,
102            $text,
103        );
104    }
105
106    /**
107     * Parts of the hook handling logic for the HtmlPageLinkRendererEnd hook that potentially
108     * interact with entity storage.
109     *
110     * @param Title $target
111     * @param HtmlArmor|string|null &$text
112     *
113     * @return bool always true
114     */
115    private function internalDoHtmlPageLinkRendererEnd(
116        Title $target,
117        &$text
118    ): bool {
119        $label = $this->labelLookup->getLabelForTitle( $target, $this->context->getLanguage()->getCode() );
120        if ( $label === null ) {
121            return true;
122        }
123
124        $labelLang = $this->languageFactory->getLanguage( $label->getLanguageCode() );
125
126        // $idHtml, $labelHtml and $text is closely based on Wikibase DefaultEntityLinkFormatter::getHtml()
127
128        $idHtml = '<span class="wb-itemlink-id">'
129            . $this->context->msg(
130                'wikibase-itemlink-id-wrapper',
131                $target->getText()
132            )->inContentLanguage()->escaped()
133            . '</span>';
134
135        $labelHtml = '<span class="wb-itemlink-label"'
136            . ' lang="' . htmlspecialchars( $labelLang->getHtmlCode() ) . '"'
137            . ' dir="' . htmlspecialchars( $labelLang->getDir() ) . '">'
138            . HtmlArmor::getHtml( $label->getText() )
139            . '</span>';
140
141        $text = new HtmlArmor( '<span class="wb-itemlink">'
142            . $this->context->msg( 'wikibase-itemlink' )->rawParams(
143                $labelHtml,
144                $idHtml
145            )->inContentLanguage()->escaped()
146            . '</span>' );
147
148        return true;
149    }
150
151    private function shouldConvertNoBadTitle( Title $currentTitle, LinkRenderer $linkRenderer ): bool {
152        return $linkRenderer->isForComment() ||
153            // Note: this may not work right with special page transclusion. If $out->getTitle()
154            // doesn't return the transcluded special page's title, the transcluded text will
155            // not have entity IDs resolved to labels.
156            // Also Note: Badtitle is excluded because it is used in rendering actual page content
157            // that is added to the ParserCache. See T327062#8796532 and https://www.mediawiki.org/wiki/API:Stashedit
158            ( $currentTitle->isSpecialPage() && !$currentTitle->isSpecial( 'Badtitle' ) );
159    }
160}