Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hooks
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 14
812
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onGetDoubleUnderscoreIDs
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onWgQueryPages
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 excludeDisambiguationPages
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 onAncientPagesQuery
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onLonelyPagesQuery
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onShortPagesQuery
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onRandomPageQuery
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onGetLinkColours
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 onListDefinedTags
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onChangeTagsListActive
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onLinksUpdateComplete
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
42
 onEditPage__showEditForm_initial
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
30
 onRecentChange_save
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * Hooks for Disambiguator extension
4 *
5 * @file
6 * @ingroup Extensions
7 */
8
9namespace MediaWiki\Extension\Disambiguator;
10
11use MediaWiki\ChangeTags\Hook\ChangeTagsListActiveHook;
12use MediaWiki\ChangeTags\Hook\ListDefinedTagsHook;
13use MediaWiki\Config\Config;
14use MediaWiki\Deferred\LinksUpdate\LinksTable;
15use MediaWiki\Deferred\LinksUpdate\LinksUpdate;
16use MediaWiki\EditPage\EditPage;
17use MediaWiki\Extension\Disambiguator\Specials\SpecialDisambiguationPageLinks;
18use MediaWiki\Extension\Disambiguator\Specials\SpecialDisambiguationPages;
19use MediaWiki\Hook\EditPage__showEditForm_initialHook;
20use MediaWiki\Hook\GetDoubleUnderscoreIDsHook;
21use MediaWiki\MediaWikiServices;
22use MediaWiki\Output\OutputPage;
23use MediaWiki\Page\PageStore;
24use MediaWiki\Parser\Hook\GetLinkColoursHook;
25use MediaWiki\RecentChanges\Hook\RecentChange_saveHook;
26use MediaWiki\Registration\ExtensionRegistry;
27use MediaWiki\SpecialPage\Hook\WgQueryPagesHook;
28use MediaWiki\Specials\Hook\AncientPagesQueryHook;
29use MediaWiki\Specials\Hook\LonelyPagesQueryHook;
30use MediaWiki\Specials\Hook\RandomPageQueryHook;
31use MediaWiki\Specials\Hook\ShortPagesQueryHook;
32use MediaWiki\Title\Title;
33
34class Hooks implements
35    ListDefinedTagsHook,
36    ChangeTagsListActiveHook,
37    RecentChange_saveHook,
38    EditPage__showEditForm_initialHook,
39    GetDoubleUnderscoreIDsHook,
40    WgQueryPagesHook,
41    AncientPagesQueryHook,
42    LonelyPagesQueryHook,
43    ShortPagesQueryHook,
44    RandomPageQueryHook,
45    GetLinkColoursHook
46{
47
48    private const TAGS = [
49        'disambiguator-link-added'
50    ];
51
52    /** @var bool */
53    private $showNotifications;
54
55    /** @var bool[] Rev IDs are used as keys */
56    private static $revsToTag = [];
57
58    public function __construct(
59        private readonly Lookup $lookup,
60        Config $options,
61        private readonly PageStore $pageStore,
62    ) {
63        $this->showNotifications = $options->get( 'DisambiguatorNotifications' );
64    }
65
66    /**
67     * @param array &$doubleUnderscoreIDs
68     */
69    public function onGetDoubleUnderscoreIDs( &$doubleUnderscoreIDs ) {
70        $doubleUnderscoreIDs[] = 'disambiguation';
71    }
72
73    /**
74     * Add the Disambiguator special pages to the list of QueryPages. This
75     * allows direct access via the API.
76     * @param array &$queryPages
77     */
78    public function onWgQueryPages( &$queryPages ) {
79        $queryPages[] = [ SpecialDisambiguationPages::class, 'DisambiguationPages' ];
80        $queryPages[] = [ SpecialDisambiguationPageLinks::class, 'DisambiguationPageLinks' ];
81    }
82
83    /**
84     * Modify query parameters to ignore disambiguation pages
85     * @param array &$tables
86     * @param array &$conds
87     * @param array &$joinConds
88     */
89    private static function excludeDisambiguationPages( &$tables, &$conds, &$joinConds ) {
90        $tables['disambig_props'] = 'page_props';
91        $conds['disambig_props.pp_page'] = null;
92        $joinConds['disambig_props'] = [
93            'LEFT JOIN', [
94                'page_id = disambig_props.pp_page',
95                'disambig_props.pp_propname' => 'disambiguation'
96            ]
97        ];
98    }
99
100    /**
101     * Modify the Special:AncientPages query to ignore disambiguation pages
102     * @param array &$tables
103     * @param array &$conds
104     * @param array &$joinConds
105     */
106    public function onAncientPagesQuery( &$tables, &$conds, &$joinConds ) {
107        self::excludeDisambiguationPages( $tables, $conds, $joinConds );
108    }
109
110    /**
111     * Modify the Special:LonelyPages query to ignore disambiguation pages
112     * @param array &$tables
113     * @param array &$conds
114     * @param array &$joinConds
115     */
116    public function onLonelyPagesQuery( &$tables, &$conds, &$joinConds ) {
117        self::excludeDisambiguationPages( $tables, $conds, $joinConds );
118    }
119
120    /**
121     * Modify the Special:ShortPages query to ignore disambiguation pages
122     * @param array &$tables
123     * @param array &$conds
124     * @param array &$joinConds
125     * @param array &$options
126     */
127    public function onShortPagesQuery( &$tables, &$conds, &$joinConds, &$options ) {
128        self::excludeDisambiguationPages( $tables, $conds, $joinConds );
129    }
130
131    /**
132     * Modify the Special:Random query to ignore disambiguation pages
133     * @param array &$tables
134     * @param array &$conds
135     * @param array &$joinConds
136     */
137    public function onRandomPageQuery( &$tables, &$conds, &$joinConds ) {
138        self::excludeDisambiguationPages( $tables, $conds, $joinConds );
139    }
140
141    /**
142     * Add 'mw-disambig' CSS class to links to disambiguation pages.
143     * @param array $pageIdToDbKey Prefixed DB keys of the pages linked to, indexed by page_id
144     * @param array &$colours CSS classes, indexed by prefixed DB keys
145     * @param Title $title
146     */
147    public function onGetLinkColours( $pageIdToDbKey, &$colours, $title ) {
148        global $wgDisambiguatorIndicateLinks;
149        if ( !$wgDisambiguatorIndicateLinks ) {
150            return;
151        }
152
153        $pageIds = $this->lookup->filterDisambiguationPageIds(
154            array_keys( $pageIdToDbKey )
155        );
156
157        foreach ( $pageIds as $pageId ) {
158            if ( isset( $colours[ $pageIdToDbKey[$pageId] ] ) ) {
159                $colours[ $pageIdToDbKey[$pageId] ] .= ' mw-disambig';
160            } else {
161                $colours[ $pageIdToDbKey[$pageId] ] = 'mw-disambig';
162            }
163        }
164    }
165
166    /**
167     * Implements the ListDefinedTags hook, to populate core
168     * Special:Tags with the change tags used by Disambiguator.
169     *
170     * @param string[] &$tags List of all active tags. Append to this array.
171     */
172    public function onListDefinedTags( &$tags ) {
173        $tags = array_merge( $tags, static::TAGS );
174    }
175
176    /**
177     * Implements the ChangeTagsListActive hook, to mark the
178     * Disambiguator change tag as active.
179     *
180     * @param array &$tags Available change tags.
181     */
182    public function onChangeTagsListActive( &$tags ) {
183        $tags = array_merge( $tags, static::TAGS );
184    }
185
186    /**
187     * Add a change tag to edits that introduce links to disambiguation pages.
188     *
189     * @param LinksUpdate $linksUpdate
190     */
191    public function onLinksUpdateComplete( LinksUpdate $linksUpdate ) {
192        $addedLinks = $linksUpdate->getPageReferenceIterator( 'pagelinks', LinksTable::INSERTED );
193
194        $pageIds = [];
195        foreach ( $addedLinks as $pageReference ) {
196            $pageRecord = $this->pageStore->getPageByReference( $pageReference );
197            if ( $pageRecord ) {
198                $pageIds[] = $pageRecord->getId();
199            }
200        }
201
202        if ( !$pageIds ) {
203            return;
204        }
205
206        $disambigs = $this->lookup->filterDisambiguationPageIds( $pageIds );
207        $rev = $linksUpdate->getRevisionRecord();
208
209        if ( $disambigs && $rev ) {
210            self::$revsToTag[$rev->getId()] = true;
211        }
212    }
213
214    /**
215     * Add the modules to the edit form.
216     *
217     * @param EditPage $editor
218     * @param OutputPage $out
219     */
220    public function onEditPage__showEditForm_initial( $editor, $out ) {
221        if ( $editor->contentModel !== CONTENT_MODEL_WIKITEXT || !$this->showNotifications ) {
222            return;
223        }
224
225        // Add modules.
226        $services = MediaWikiServices::getInstance();
227        $isMobileView = ExtensionRegistry::getInstance()->isLoaded( 'MobileFrontend' ) &&
228            $services->getService( 'MobileFrontend.Context' )->shouldDisplayMobileView();
229        if ( !$isMobileView ) {
230            $out->addModules( 'ext.disambiguator' );
231        }
232    }
233
234    /** @inheritDoc */
235    public function onRecentChange_save( $recentChange ) {
236        $revId = $recentChange->getAttribute( 'rc_this_oldid' );
237        if ( $recentChange->getAttribute( 'rc_log_type' ) ) {
238            return;
239        }
240        if ( isset( self::$revsToTag[$revId] ) ) {
241            $recentChange->addTags( self::TAGS );
242            unset( self::$revsToTag[$revId] );
243        }
244    }
245}