Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
58.95% covered (warning)
58.95%
112 / 190
42.86% covered (danger)
42.86%
3 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
HTMLFeatureField
58.95% covered (warning)
58.95%
112 / 190
42.86% covered (danger)
42.86%
3 / 7
102.85
0.00% covered (danger)
0.00%
0 / 1
 getCheckboxHTML
88.00% covered (warning)
88.00%
22 / 25
0.00% covered (danger)
0.00%
0 / 1
4.03
 getHeaderHTML
95.74% covered (success)
95.74%
45 / 47
0.00% covered (danger)
0.00%
0 / 1
8
 getMainHTML
28.71% covered (danger)
28.71%
29 / 101
0.00% covered (danger)
0.00%
0 / 1
96.51
 getInputHTML
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
2.00
 getInputOOUI
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getFieldLayoutOOUI
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getOOUIModules
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * This file is part of the MediaWiki extension BetaFeatures.
4 *
5 * BetaFeatures is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * BetaFeatures is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with BetaFeatures.  If not, see <http://www.gnu.org/licenses/>.
17 *
18 * HTML feature field
19 *
20 * @file
21 * @ingroup Extensions
22 * @copyright 2013 Mark Holmquist and others; see AUTHORS
23 * @license GPL-2.0-or-later
24 */
25
26namespace MediaWiki\Extension\BetaFeatures;
27
28use MediaWiki\Html\Html;
29use MediaWiki\HTMLForm\Field\HTMLCheckField;
30use MediaWiki\HTMLForm\HTMLFormFieldLayout;
31use OOUI\CheckboxInputWidget;
32use OOUI\IconWidget;
33
34class HTMLFeatureField extends HTMLCheckField {
35    /**
36     * @param string $value
37     * @return string
38     */
39    protected function getCheckboxHTML( $value ) {
40        if ( !empty( $this->mParams['invert'] ) ) {
41            $value = !$value;
42        }
43        // TODO: Support $this->getTooltipAndAccessKey?
44
45        $extraParams = [];
46        // Only support disable here, it shouldn't be hide partially
47        if ( isset( $this->mParams['disable-if'] ) ) {
48            $extraParams['classes'] = [ 'mw-htmlform-disable-if' ];
49            $extraParams['condState']['disable'] = $this->parseCondState( $this->mParams['disable-if'] );
50        }
51        return Html::rawElement(
52            'div',
53            [ 'class' => 'mw-ui-feature-checkbox' ],
54            (string)new HTMLFormFieldLayout(
55                new CheckboxInputWidget( [
56                    'infusable' => true,
57                    'name' => $this->mName,
58                    'selected' => $value,
59                    'value' => 1,
60                    'classes' => $this->mClass ? [ $this->mClass ] : [],
61                    'disabled' => ( $this->mParams['disabled'] ?? false ) === true,
62                ] ),
63                [
64                    'infusable' => true,
65                    'align' => 'inline',
66                    'label' => $this->mLabel,
67                ] + $extraParams
68            )
69        );
70    }
71
72    /**
73     * @param string $value
74     * @return string HTML
75     */
76    private function getHeaderHTML( $value ) {
77        if ( isset( $this->mParams['info-message'] ) ) {
78            $infoLink = $this->mParent->msg( $this->mParams['info-message'] )->text();
79        } else {
80            $infoLink = $this->mParams['info-link'];
81        }
82
83        if ( isset( $this->mParams['discussion-message'] ) ) {
84            $discussionLink = $this->mParent->msg( $this->mParams['discussion-message'] )->text();
85        } else {
86            $discussionLink = $this->mParams['discussion-link'];
87        }
88
89        $infoLinkClasses = [ 'mw-ui-feature-info-links' ];
90
91        if ( $infoLink !== null || $discussionLink !== null ) {
92            $infoLinkClasses[] = 'filled';
93        }
94
95        $out = $this->mParent->getOutput();
96        $infoLinksHtml = '';
97        if ( $infoLink !== null ) {
98            $out->addModuleStyles( 'oojs-ui.styles.icons-content' );
99            $infoLinksHtml .= Html::rawElement( 'a', [
100                    'href' => $infoLink,
101                    'class' => 'mw-ui-feature-info-link',
102                ],
103                new IconWidget( [
104                    'icon' => 'article',
105                    'flags' => 'progressive'
106                ] ) .
107                $this->mParent->msg( 'mw-ui-feature-info' )->escaped()
108            );
109        }
110
111        if ( $discussionLink !== null ) {
112            $out->addModuleStyles( 'oojs-ui.styles.icons-alerts' );
113            if ( $infoLinksHtml !== '' ) {
114                $infoLinksHtml .= ' ';
115            }
116            $infoLinksHtml .= Html::rawElement( 'a', [
117                    'href' => $discussionLink,
118                    'class' => 'mw-ui-feature-discussion-link',
119                ],
120                new IconWidget( [
121                    'icon' => 'speechBubbles',
122                    'flags' => 'progressive'
123                ] ) .
124                $this->mParent->msg( 'mw-ui-feature-discuss' )->escaped()
125            );
126        }
127
128        return Html::rawElement( 'div', [ 'class' => 'mw-ui-feature-header' ],
129            Html::rawElement(
130                'div',
131                [ 'class' => 'mw-ui-feature-title-contain' ],
132                $this->getCheckboxHTML( $value )
133            ) .
134            Html::rawElement( 'div', [ 'class' => $infoLinkClasses ],
135                $infoLinksHtml
136            )
137        );
138    }
139
140    /**
141     * @return string HTML
142     */
143    private function getMainHTML() {
144        $parent = $this->mParent;
145        $metaHtml = '';
146
147        if ( isset( $this->mParams['user-count'] ) ) {
148            $userCountMsg = $this->mParams['user-count-msg'] ?? 'mw-ui-feature-user-count';
149            $metaHtml .= Html::rawElement(
150                'p',
151                [ 'class' => 'mw-ui-feature-user-count' ],
152                $parent->msg( $userCountMsg )->numParams( $this->mParams['user-count'] )->escaped()
153            );
154        }
155
156        if ( isset( $this->mParams['desc-message'] ) ) {
157            $metaHtml .= Html::rawElement(
158                'div',
159                [ 'class' => 'mw-ui-feature-description' ],
160                $parent->msg( $this->mParams['desc-message'] )->parse() );
161        }
162
163        $requirementsItemsHtml = '';
164
165        if ( isset( $this->mParams['requirements'] ) ) {
166            if ( $this->mParams['requirements']['javascript'] ?? false ) {
167                $requirementsItemsHtml .= Html::rawElement(
168                    'li',
169                    [ 'class' => 'mw-ui-feature-requirements-javascript' ],
170                    $parent->msg( 'mw-ui-feature-requirements-javascript' )->escaped()
171                );
172            }
173
174            $unsupportedList = $this->mParams['requirements']['unsupportedList'] ?? false;
175            if ( $unsupportedList ) {
176                $unsupportedItemsHtml = '';
177                $browserCount = count( $unsupportedList );
178                foreach ( $unsupportedList as $browser => $versions ) {
179                    $browserString = $browser;
180                    if ( $versions ) {
181                        foreach ( $versions as $version ) {
182                            $browserString .= ' ' . implode( ' ', $version );
183                        }
184                    }
185                    $unsupportedItemsHtml .= Html::element(
186                        'li',
187                        [],
188                        $browserString
189                    );
190                }
191
192                $requirementsItemsHtml .= Html::rawElement(
193                    'li',
194                    [ 'class' => 'mw-ui-feature-requirements-browser' ],
195                    $parent->msg( 'mw-ui-feature-requirements-browser', $browserCount )->escaped() .
196                    Html::rawElement( 'ul', [], $unsupportedItemsHtml )
197                );
198            }
199
200            if ( ( $this->mParams['requirements']['skin-not-supported'] ?? false ) === true ) {
201                $skinItemsHtml = '';
202                $skinCount = count( $this->mParams['requirements']['skins'] );
203                foreach ( $this->mParams['requirements']['skins'] as $skin ) {
204                    $skinItemsHtml .= Html::element(
205                        'li',
206                        [],
207                        $parent->msg( "skinname-$skin" )->text()
208                    );
209                }
210
211                $requirementsItemsHtml .= Html::rawElement(
212                    'li',
213                    [ 'class' => 'mw-ui-feature-requirements-skins' ],
214                    $parent->msg( 'mw-ui-feature-requirements-skins', $skinCount )->escaped() .
215                    Html::rawElement( 'ul', [], $skinItemsHtml )
216                );
217            }
218
219            if ( isset( $this->mParams['requirements']['betafeatures-messages'] ) ) {
220                $featureItemsHtml = '';
221                $featureCount = count( $this->mParams['requirements']['betafeatures-messages'] );
222                foreach ( $this->mParams['requirements']['betafeatures-messages'] as $message ) {
223                    $featureItemsHtml .= Html::rawElement(
224                        'li',
225                        [],
226                        $parent->msg( $message )->escaped()
227                    );
228                }
229
230                $requirementsItemsHtml .= Html::rawElement(
231                    'li',
232                    [ 'class' => 'mw-ui-feature-requirements-betafeatures' ],
233                    $parent->msg( 'mw-ui-feature-requirements-betafeatures', $featureCount )->escaped() .
234                    Html::rawElement( 'ul', [], $featureItemsHtml )
235                );
236            }
237        }
238
239        $metaHtml .= Html::rawElement(
240            'ul',
241            [ 'class' => 'mw-ui-feature-requirements-list' ],
242            $requirementsItemsHtml
243        );
244
245        $screenshotHtml = '';
246
247        if ( isset( $this->mParams['screenshot'] ) ) {
248            $screenshot = $this->mParams['screenshot'];
249
250            // The screenshot parameter is either a string with a filename
251            // or an array that specifies a screenshot for each language,
252            // and default screenshots for rtl and ltr languages
253            if ( is_array( $screenshot ) ) {
254                $language = $this->mParent->getLanguage();
255                $langCode = $language->getCode();
256                $screenshot = $screenshot[$langCode] ?? $screenshot[$language->getDir()];
257            }
258            $screenshotHtml .= Html::element( 'img', [
259                'src' => $screenshot,
260                'class' => 'mw-ui-feature-screenshot',
261                'alt' => '',
262            ] );
263        }
264
265        return Html::rawElement( 'div', [ 'class' => 'mw-ui-feature-main' ],
266            Html::rawElement(
267                'div',
268                [ 'class' => 'mw-ui-feature-meta' ],
269                $metaHtml
270            ) .
271            Html::rawElement(
272                'div',
273                [ 'class' => 'mw-ui-feature-screenshot-contain skin-invert' ],
274                $screenshotHtml
275            )
276        );
277    }
278
279    /** @inheritDoc */
280    public function getInputHTML( $value ) {
281        $divClasses = [ 'mw-ui-feature-field' ];
282
283        // Use 'cssclass' to populate this. Separate from 'class', of course.
284        if ( $this->mClass !== '' ) {
285            $divClasses[] = $this->mClass;
286        }
287
288        return Html::rawElement(
289            'div',
290            [ 'class' => $divClasses ],
291            Html::rawElement(
292                'div',
293                [ 'class' => 'mw-ui-feature-contain' ],
294                $this->getHeaderHTML( $value ) . $this->getMainHTML()
295            )
296        );
297    }
298
299    /** @inheritDoc */
300    public function getInputOOUI( $value ) {
301        $this->mParent->getOutput()->addModuleStyles( 'ext.betaFeatures.styles' );
302
303        // Use the same output as for the HTML version, otherwise OOUIHTMLForm would use
304        // a plain checkbox, inherited from HTMLCheckField. This isn't actually a widget
305        // (just a HTML string) but that's okay, HTMLFormField::getOOUI() will handle it.
306        // @phan-suppress-next-line PhanTypeMismatchReturn
307        return $this->getInputHTML( $value );
308    }
309
310    /** @inheritDoc */
311    protected function getFieldLayoutOOUI( $inputField, $config ) {
312        // Label is already included in the field's HTML, do not duplicate it
313        unset( $config['label'] );
314        return parent::getFieldLayoutOOUI( $inputField, $config );
315    }
316
317    /** @inheritDoc */
318    protected function getOOUIModules() {
319        return [ 'ext.betaFeatures' ];
320    }
321}