Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Hooks
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 8
380
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 onParserFirstCallInit
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 onParserAfterParse
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 onParserTestGlobals
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 onRegistration
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
12
 onOutputTransformFirstStage
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 onOutputTransformLastStage
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 traverseJson
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2/**
3 * @license MIT
4 * @file
5 *
6 * @author Yuri Astrakhan
7 */
8
9namespace Kartographer;
10
11use Kartographer\Tag\LegacyMapFrame;
12use Kartographer\Tag\LegacyMapLink;
13use Kartographer\Tag\LegacyTagHandler;
14use MediaWiki\Config\Config;
15use MediaWiki\Hook\ParserTestGlobalsHook;
16use MediaWiki\Language\LanguageNameUtils;
17use MediaWiki\OutputTransform\Hook\OutputTransformFirstStageHook;
18use MediaWiki\OutputTransform\Hook\OutputTransformLastStageHook;
19use MediaWiki\Parser\Hook\ParserAfterParseHook;
20use MediaWiki\Parser\Hook\ParserFirstCallInitHook;
21use MediaWiki\Parser\Parser;
22use MediaWiki\Parser\ParserOptions;
23use MediaWiki\Parser\ParserOutput;
24use MediaWiki\Parser\StripState;
25use MediaWiki\Settings\SettingsBuilder;
26use MediaWiki\Title\TitleFormatter;
27
28/**
29 * @license MIT
30 */
31class Hooks implements
32    ParserFirstCallInitHook,
33    ParserAfterParseHook,
34    ParserTestGlobalsHook,
35    OutputTransformFirstStageHook,
36    OutputTransformLastStageHook
37{
38    private readonly LegacyMapLink $legacyMapLink;
39    private readonly LegacyMapFrame $legacyMapFrame;
40
41    public function __construct(
42        Config $config,
43        LanguageNameUtils $languageCodeValidator,
44        TitleFormatter $titleFormatter
45    ) {
46        $this->legacyMapLink = new LegacyMapLink(
47            $config,
48            $languageCodeValidator,
49            $titleFormatter
50        );
51        $this->legacyMapFrame = new LegacyMapFrame(
52            $config,
53            $languageCodeValidator,
54            $titleFormatter
55        );
56    }
57
58    /**
59     * ParserFirstCallInit hook handler
60     * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserFirstCallInit
61     * @param Parser $parser
62     */
63    public function onParserFirstCallInit( $parser ) {
64        $parser->setHook( LegacyMapLink::TAG, $this->legacyMapLink->handle( ... ) );
65        $parser->setHook( LegacyMapFrame::TAG, $this->legacyMapFrame->handle( ... ) );
66    }
67
68    /**
69     * ParserAfterParse hook handler
70     * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserAfterParse
71     * @param Parser $parser
72     * @param string &$text Text being parsed
73     * @param StripState $stripState StripState used
74     */
75    public function onParserAfterParse( $parser, &$text, $stripState ) {
76        $output = $parser->getOutput();
77        $state = State::getState( $output );
78
79        if ( $state ) {
80            $options = $parser->getOptions();
81            $isPreview = $options->getIsPreview() || $options->getIsSectionPreview();
82            $tracker = new ParserFunctionTracker( $parser );
83            LegacyTagHandler::finalParseStep( $state, $output, $isPreview, $tracker );
84        }
85    }
86
87    /**
88     * @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserTestGlobals
89     * @param array &$globals Array with all the globals which should be set for parser tests.
90     *   The arrays keys serve as the globals' names, its values are the globals' values.
91     * @return bool|void True or no return value to continue or false to abort
92     */
93    public function onParserTestGlobals( &$globals ) {
94        $globals['wgKartographerMapServer'] = 'https://maps.wikimedia.org';
95    }
96
97    /**
98     * Sets $wgKartographerMapServer in integration test/CI setup
99     * This is needed by parserTests that define articles containing Kartographer content - parsing them when
100     * inserting them in the test DB requires $wgKartographerMapServer to be defined early.
101     */
102    public static function onRegistration( array $extInfo, SettingsBuilder $settings ) {
103        if ( defined( 'MW_PHPUNIT_TEST' ) || defined( 'MW_QUIBBLE_CI' ) ) {
104            $settings->overrideConfigValue( 'KartographerMapServer', 'https://maps.wikimedia.org' );
105        }
106    }
107
108    public function onOutputTransformFirstStage( ParserOutput &$parserOutput, ParserOptions $parserOptions ): void {
109        $data = $parserOutput->getExtensionData( 'kartographer' )['data'] ?? [];
110        $path = 'kartographer:';
111        $traversal = static function ( ParserOutput $po, $json, string $prop, string $path ) {
112            $po->getContentHolder()->setAsHtmlString( $path, $json->properties->$prop );
113        };
114        $this->traverseJson( $parserOutput, $data, $path, $traversal );
115    }
116
117    public function onOutputTransformLastStage( ParserOutput &$parserOutput, ParserOptions $parserOptions ): void {
118        // Note: this works because $wgKartographerLiveData in jsConfigVars is referencing this same array
119        $data = $parserOutput->getExtensionData( 'kartographer' )['data'] ?? [];
120        $path = 'kartographer:';
121        $traversal = static function ( ParserOutput $po, $json, string $prop, string $path ) {
122            $json->properties->$prop = $po->getContentHolder()->getAsHtmlString( $path ) ?? '';
123            // clean up the contentholder to avoid extraneous serialization
124            $po->getContentHolder()->setAsHtmlString( $path, null );
125        };
126        $this->traverseJson( $parserOutput, $data, $path, $traversal );
127    }
128
129    private function traverseJson( ParserOutput $po, mixed &$json, string $path, callable $callable ): void {
130        if ( is_array( $json ) ) {
131            foreach ( $json as $k => &$element ) {
132                $this->traverseJson( $po, $element, $path . "$k/", $callable );
133            }
134        } elseif ( is_object( $json ) ) {
135            if ( isset( $json->properties ) && is_object( $json->properties ) ) {
136                foreach ( SimpleStyleParser::WIKITEXT_PROPERTIES as $prop ) {
137                    if ( !property_exists( $json->properties, $prop ) ) {
138                        continue;
139                    }
140                    $path .= $prop;
141                    $callable( $po, $json, $prop, $path );
142                }
143            }
144        }
145    }
146}