Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
37.04% covered (danger)
37.04%
30 / 81
55.56% covered (warning)
55.56%
5 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
GadgetDefinitionContentHandler
37.04% covered (danger)
37.04%
30 / 81
55.56% covered (warning)
55.56%
5 / 9
208.96
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 canBeUsedOn
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getContentClass
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 makeEmptyContent
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 validateSave
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 getEmptyDefinition
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 getDefaultMetadata
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
2
 fillParserOutput
38.24% covered (danger)
38.24%
13 / 34
0.00% covered (danger)
0.00%
0 / 1
85.10
 makeLink
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\Extension\Gadgets\Content;
22
23use MediaWiki\Content\Content;
24use MediaWiki\Content\JsonContentHandler;
25use MediaWiki\Content\Renderer\ContentParseParams;
26use MediaWiki\Content\ValidationParams;
27use MediaWiki\Extension\Gadgets\GadgetRepo;
28use MediaWiki\Extension\Gadgets\MediaWikiGadgetsJsonRepo;
29use MediaWiki\Json\FormatJson;
30use MediaWiki\Linker\Linker;
31use MediaWiki\Parser\ParserOutput;
32use MediaWiki\Title\Title;
33use StatusValue;
34
35class GadgetDefinitionContentHandler extends JsonContentHandler {
36    public function __construct(
37        string $modelId,
38        private readonly GadgetRepo $gadgetRepo,
39    ) {
40        parent::__construct( $modelId );
41    }
42
43    /**
44     * @param Title $title
45     * @return bool
46     */
47    public function canBeUsedOn( Title $title ) {
48        return MediaWikiGadgetsJsonRepo::isGadgetDefinitionTitle( $title );
49    }
50
51    /** @inheritDoc */
52    protected function getContentClass() {
53        return GadgetDefinitionContent::class;
54    }
55
56    /** @inheritDoc */
57    public function makeEmptyContent() {
58        $class = $this->getContentClass();
59        return new $class( FormatJson::encode( $this->getEmptyDefinition(), "\t" ) );
60    }
61
62    /**
63     * @param Content $content
64     * @param ValidationParams $validationParams
65     * @return StatusValue
66     */
67    public function validateSave( Content $content, ValidationParams $validationParams ) {
68        $status = parent::validateSave( $content, $validationParams );
69        '@phan-var GadgetDefinitionContent $content';
70        if ( !$status->isOK() ) {
71            return $content->validate();
72        }
73        return $status;
74    }
75
76    /**
77     * @return array<string,array>
78     */
79    public function getEmptyDefinition() {
80        return [
81            'settings' => [
82                'section' => '',
83            ],
84            'module' => [
85                'pages' => [],
86                'dependencies' => [],
87            ]
88        ];
89    }
90
91    /**
92     * @return array<string,array>
93     */
94    public function getDefaultMetadata() {
95        return [
96            'settings' => [
97                'rights' => [],
98                'default' => false,
99                'package' => false,
100                'requiresES6' => false,
101                'hidden' => false,
102                'skins' => [],
103                'actions' => [],
104                'namespaces' => [],
105                'categories' => [],
106                'contentModels' => [],
107                'section' => '',
108                'supportsUrlLoad' => false,
109            ],
110            'module' => [
111                'pages' => [],
112                'peers' => [],
113                'dependencies' => [],
114                'messages' => [],
115                'codexIcons' => [],
116                'type' => '',
117            ],
118        ];
119    }
120
121    /**
122     * @inheritDoc
123     */
124    protected function fillParserOutput(
125        Content $content,
126        ContentParseParams $cpoParams,
127        ParserOutput &$parserOutput
128    ) {
129        '@phan-var GadgetDefinitionContent $content';
130        // Create a deep clone. FIXME: unserialize(serialize()) is hacky.
131        $data = unserialize( serialize( $content->getData()->getValue() ) );
132        if ( $data !== null ) {
133            if ( isset( $data->module->pages ) ) {
134                foreach ( $data->module->pages as &$page ) {
135                    $title = Title::makeTitleSafe( NS_MEDIAWIKI, "Gadget-$page" );
136                    $this->makeLink( $parserOutput, $page, $title );
137                }
138            }
139            if ( isset( $data->module->dependencies ) ) {
140                foreach ( $data->module->dependencies as &$dep ) {
141                    if ( str_starts_with( $dep, 'ext.gadget.' ) ) {
142                        $gadgetId = explode( 'ext.gadget.', $dep )[ 1 ];
143                        $title = $this->gadgetRepo->getGadgetDefinitionTitle( $gadgetId );
144                        $this->makeLink( $parserOutput, $dep, $title );
145                    }
146                }
147            }
148            if ( isset( $data->module->peers ) ) {
149                foreach ( $data->module->peers as &$peer ) {
150                    $title = $this->gadgetRepo->getGadgetDefinitionTitle( $peer );
151                    $this->makeLink( $parserOutput, $peer, $title );
152                }
153            }
154            if ( isset( $data->module->messages ) ) {
155                foreach ( $data->module->messages as &$msg ) {
156                    $title = Title::makeTitleSafe( NS_MEDIAWIKI, $msg );
157                    $this->makeLink( $parserOutput, $msg, $title );
158                }
159            }
160            if ( isset( $data->settings->categories ) ) {
161                foreach ( $data->settings->categories as &$category ) {
162                    $title = Title::makeTitleSafe( NS_CATEGORY, $category );
163                    $this->makeLink( $parserOutput, $category, $title );
164                }
165            }
166            if ( isset( $data->settings->section ) && $data->settings->section ) {
167                $this->makeLink(
168                    $parserOutput,
169                    $data->settings->section,
170                    Title::makeTitleSafe( NS_MEDIAWIKI, "gadget-section-" . $data->settings->section )
171                );
172            }
173        }
174
175        if ( !$cpoParams->getGenerateHtml() || !$content->isValid() ) {
176            $parserOutput->setContentHolderText( '' );
177        } else {
178            $parserOutput->setContentHolderText( $content->rootValueTable( $data ) );
179            $parserOutput->addModuleStyles( [ 'mediawiki.content.json' ] );
180        }
181    }
182
183    /**
184     * Create a link on the page
185     * @param ParserOutput $parserOutput
186     * @param string &$text The text to link
187     * @param Title|null $title Link target title
188     * @return void
189     */
190    private function makeLink( ParserOutput $parserOutput, string &$text, ?Title $title ) {
191        if ( $title ) {
192            $parserOutput->addLink( $title );
193            $text = new GadgetDefinitionContentArmor(
194                Linker::link( $title, htmlspecialchars( '"' . $text . '"' ) )
195            );
196        }
197    }
198}