Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
37.97% covered (danger)
37.97%
30 / 79
55.56% covered (warning)
55.56%
5 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
GadgetDefinitionContentHandler
37.97% covered (danger)
37.97%
30 / 79
55.56% covered (warning)
55.56%
5 / 9
200.95
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
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 / 23
0.00% covered (danger)
0.00%
0 / 1
2
 fillParserOutput
37.50% covered (danger)
37.50%
12 / 32
0.00% covered (danger)
0.00%
0 / 1
87.56
 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    private GadgetRepo $gadgetRepo;
37
38    public function __construct( string $modelId, GadgetRepo $gadgetRepo ) {
39        parent::__construct( $modelId );
40        $this->gadgetRepo = $gadgetRepo;
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    public function makeEmptyContent() {
57        $class = $this->getContentClass();
58        return new $class( FormatJson::encode( $this->getEmptyDefinition(), "\t" ) );
59    }
60
61    /**
62     * @param Content $content
63     * @param ValidationParams $validationParams
64     * @return StatusValue
65     */
66    public function validateSave( Content $content, ValidationParams $validationParams ) {
67        $status = parent::validateSave( $content, $validationParams );
68        '@phan-var GadgetDefinitionContent $content';
69        if ( !$status->isOK() ) {
70            return $content->validate();
71        }
72        return $status;
73    }
74
75    /**
76     * @return array<string,array>
77     */
78    public function getEmptyDefinition() {
79        return [
80            'settings' => [
81                'category' => '',
82            ],
83            'module' => [
84                'pages' => [],
85                'dependencies' => [],
86            ]
87        ];
88    }
89
90    /**
91     * @return array<string,array>
92     */
93    public function getDefaultMetadata() {
94        return [
95            'settings' => [
96                'rights' => [],
97                'default' => false,
98                'package' => false,
99                'requiresES6' => false,
100                'hidden' => false,
101                'skins' => [],
102                'actions' => [],
103                'namespaces' => [],
104                'categories' => [],
105                'contentModels' => [],
106                'category' => '',
107                'supportsUrlLoad' => false,
108            ],
109            'module' => [
110                'pages' => [],
111                'peers' => [],
112                'dependencies' => [],
113                'messages' => [],
114                'type' => '',
115            ],
116        ];
117    }
118
119    /**
120     * @inheritDoc
121     */
122    protected function fillParserOutput(
123        Content $content,
124        ContentParseParams $cpoParams,
125        ParserOutput &$parserOutput
126    ) {
127        '@phan-var GadgetDefinitionContent $content';
128        // Create a deep clone. FIXME: unserialize(serialize()) is hacky.
129        $data = unserialize( serialize( $content->getData()->getValue() ) );
130        if ( $data !== null ) {
131            if ( isset( $data->module->pages ) ) {
132                foreach ( $data->module->pages as &$page ) {
133                    $title = Title::makeTitleSafe( NS_MEDIAWIKI, "Gadget-$page" );
134                    $this->makeLink( $parserOutput, $page, $title );
135                }
136            }
137            if ( isset( $data->module->dependencies ) ) {
138                foreach ( $data->module->dependencies as &$dep ) {
139                    if ( str_starts_with( $dep, 'ext.gadget.' ) ) {
140                        $gadgetId = explode( 'ext.gadget.', $dep )[ 1 ];
141                        $title = $this->gadgetRepo->getGadgetDefinitionTitle( $gadgetId );
142                        if ( $title ) {
143                            $this->makeLink( $parserOutput, $dep, $title );
144                        }
145                    }
146                }
147            }
148            if ( isset( $data->module->peers ) ) {
149                foreach ( $data->module->peers as &$peer ) {
150                    $title = $this->gadgetRepo->getGadgetDefinitionTitle( $peer );
151                    if ( $title ) {
152                        $this->makeLink( $parserOutput, $peer, $title );
153                    }
154                }
155            }
156            if ( isset( $data->module->messages ) ) {
157                foreach ( $data->module->messages as &$msg ) {
158                    $title = Title::makeTitleSafe( NS_MEDIAWIKI, $msg );
159                    $this->makeLink( $parserOutput, $msg, $title );
160                }
161            }
162            if ( isset( $data->settings->category ) && $data->settings->category ) {
163                $this->makeLink(
164                    $parserOutput,
165                    $data->settings->category,
166                    Title::makeTitleSafe( NS_MEDIAWIKI, "gadget-section-" . $data->settings->category )
167                );
168            }
169        }
170
171        if ( !$cpoParams->getGenerateHtml() || !$content->isValid() ) {
172            $parserOutput->setText( '' );
173        } else {
174            $parserOutput->setText( $content->rootValueTable( $data ) );
175            $parserOutput->addModuleStyles( [ 'mediawiki.content.json' ] );
176        }
177    }
178
179    /**
180     * Create a link on the page
181     * @param ParserOutput $parserOutput
182     * @param string &$text The text to link
183     * @param Title|null $title Link target title
184     * @return void
185     */
186    private function makeLink( ParserOutput $parserOutput, string &$text, ?Title $title ) {
187        if ( $title ) {
188            $parserOutput->addLink( $title );
189            $text = new GadgetDefinitionContentArmor(
190                Linker::link( $title, htmlspecialchars( '"' . $text . '"' ) )
191            );
192        }
193    }
194}