Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
VueContentHandler
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 5
90
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getContentClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 makeEmptyContent
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 fillParserOutput
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
12
 validateSave
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Content;
8
9use InvalidArgumentException;
10use MediaWiki\Config\Config;
11use MediaWiki\Content\Renderer\ContentParseParams;
12use MediaWiki\MainConfigNames;
13use MediaWiki\Page\WikiPage;
14use MediaWiki\Parser\ParserFactory;
15use MediaWiki\Parser\ParserOutput;
16use MediaWiki\Parser\ParserOutputFlags;
17use MediaWiki\ResourceLoader\VueComponentParser;
18use StatusValue;
19
20/**
21 * Content handler for Vue pages.
22 *
23 * @since 1.45
24 * @ingroup Content
25 */
26class VueContentHandler extends CodeContentHandler {
27
28    private readonly array $textModelsToParse;
29
30    private ?VueComponentParser $vueComponentParser = null;
31
32    public function __construct(
33        string $modelId,
34        Config $config,
35        private readonly ParserFactory $parserFactory,
36        private readonly CodeHighlighter $codeHighlighter,
37    ) {
38        parent::__construct( $modelId, [ CONTENT_FORMAT_VUE ] );
39        $this->textModelsToParse = $config->get( MainConfigNames::TextModelsToParse ) ?? [];
40    }
41
42    /**
43     * @return class-string<VueContent>
44     */
45    protected function getContentClass() {
46        return VueContent::class;
47    }
48
49    /** @inheritDoc */
50    public function makeEmptyContent() {
51        $class = $this->getContentClass();
52        return new $class( "<template>\n</template>\n\n<script>\n</script>\n\n<style>\n</style>\n" );
53    }
54
55    /**
56     * Fills the provided ParserOutput object with information derived from the content.
57     * Unless $cpo->getGenerateHtml was false, this includes an HTML representation of the content.
58     *
59     * For content models listed in $wgTextModelsToParse, this method will call the MediaWiki
60     * wikitext parser on the text to extract any (wikitext) links, magic words, etc.
61     *
62     * Subclasses may override this to provide custom content processing..
63     *
64     * @stable to override
65     *
66     * @param Content $content
67     * @param ContentParseParams $cpoParams
68     * @param ParserOutput &$output The output object to fill (reference).
69     */
70    protected function fillParserOutput(
71        Content $content,
72        ContentParseParams $cpoParams,
73        ParserOutput &$output
74    ) {
75        '@phan-var VueContent $content';
76        if ( in_array( $content->getModel(), $this->textModelsToParse ) ) {
77            // parse just to get links etc into the database, HTML is replaced below.
78            $output = $this->parserFactory->getInstance()->parse(
79                $content->getText(),
80                $cpoParams->getPage(),
81                WikiPage::makeParserOptionsFromTitleAndModel(
82                    $cpoParams->getPage(),
83                    $content->getModel(),
84                    'canonical'
85                ),
86                true,
87                true,
88                $cpoParams->getRevId()
89            );
90        }
91
92        if ( $cpoParams->getGenerateHtml() ) {
93            $highlightOutput = $this->codeHighlighter->highlight( $content->getText(), new CodeHighlighterOptions(
94                language: 'vue',
95                classes: [ 'mw-vue' ],
96                includeLineNumbers: true,
97                includeLineLinks: true,
98            ) );
99            $html = $highlightOutput->getHtml();
100            $highlightOutput->getMetadata()->addToParserOutput( $output );
101        } else {
102            $html = null;
103        }
104
105        $output->clearWrapperDivClass();
106        $output->setContentHolderText( $html );
107        // Suppress the TOC (T307691)
108        $output->setOutputFlag( ParserOutputFlags::NO_TOC );
109        $output->setSections( [] );
110    }
111
112    /**
113     * @param Content $content
114     * @param ValidationParams $validationParams
115     * @return StatusValue
116     */
117    public function validateSave( Content $content, ValidationParams $validationParams ): StatusValue {
118        $this->vueComponentParser ??= new VueComponentParser;
119        try {
120            $parsedComponent = $this->vueComponentParser->parse( $content->serialize() );
121        } catch ( InvalidArgumentException $e ) {
122            // TODO: i18n for error messages
123            return StatusValue::newFatal( 'vue-invalid-content', $e->getMessage() );
124        }
125        if ( $parsedComponent['styleLang'] === 'less' ) {
126            return StatusValue::newFatal( 'vue-less-notsupported' );
127        }
128        return StatusValue::newGood();
129    }
130}