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    /** @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                'type' => '',
116            ],
117        ];
118    }
119
120    /**
121     * @inheritDoc
122     */
123    protected function fillParserOutput(
124        Content $content,
125        ContentParseParams $cpoParams,
126        ParserOutput &$parserOutput
127    ) {
128        '@phan-var GadgetDefinitionContent $content';
129        // Create a deep clone. FIXME: unserialize(serialize()) is hacky.
130        $data = unserialize( serialize( $content->getData()->getValue() ) );
131        if ( $data !== null ) {
132            if ( isset( $data->module->pages ) ) {
133                foreach ( $data->module->pages as &$page ) {
134                    $title = Title::makeTitleSafe( NS_MEDIAWIKI, "Gadget-$page" );
135                    $this->makeLink( $parserOutput, $page, $title );
136                }
137            }
138            if ( isset( $data->module->dependencies ) ) {
139                foreach ( $data->module->dependencies as &$dep ) {
140                    if ( str_starts_with( $dep, 'ext.gadget.' ) ) {
141                        $gadgetId = explode( 'ext.gadget.', $dep )[ 1 ];
142                        $title = $this->gadgetRepo->getGadgetDefinitionTitle( $gadgetId );
143                        if ( $title ) {
144                            $this->makeLink( $parserOutput, $dep, $title );
145                        }
146                    }
147                }
148            }
149            if ( isset( $data->module->peers ) ) {
150                foreach ( $data->module->peers as &$peer ) {
151                    $title = $this->gadgetRepo->getGadgetDefinitionTitle( $peer );
152                    if ( $title ) {
153                        $this->makeLink( $parserOutput, $peer, $title );
154                    }
155                }
156            }
157            if ( isset( $data->module->messages ) ) {
158                foreach ( $data->module->messages as &$msg ) {
159                    $title = Title::makeTitleSafe( NS_MEDIAWIKI, $msg );
160                    $this->makeLink( $parserOutput, $msg, $title );
161                }
162            }
163            if ( isset( $data->settings->section ) && $data->settings->section ) {
164                $this->makeLink(
165                    $parserOutput,
166                    $data->settings->section,
167                    Title::makeTitleSafe( NS_MEDIAWIKI, "gadget-section-" . $data->settings->section )
168                );
169            }
170        }
171
172        if ( !$cpoParams->getGenerateHtml() || !$content->isValid() ) {
173            $parserOutput->setText( '' );
174        } else {
175            $parserOutput->setText( $content->rootValueTable( $data ) );
176            $parserOutput->addModuleStyles( [ 'mediawiki.content.json' ] );
177        }
178    }
179
180    /**
181     * Create a link on the page
182     * @param ParserOutput $parserOutput
183     * @param string &$text The text to link
184     * @param Title|null $title Link target title
185     * @return void
186     */
187    private function makeLink( ParserOutput $parserOutput, string &$text, ?Title $title ) {
188        if ( $title ) {
189            $parserOutput->addLink( $title );
190            $text = new GadgetDefinitionContentArmor(
191                Linker::link( $title, htmlspecialchars( '"' . $text . '"' ) )
192            );
193        }
194    }
195}