Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
84.09% covered (warning)
84.09%
74 / 88
28.57% covered (danger)
28.57%
2 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialRestSandbox
84.09% covered (warning)
84.09%
74 / 88
28.57% covered (danger)
28.57%
2 / 7
18.16
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
 getApiSpecs
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isListed
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSpecUrl
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
5.58
 execute
81.82% covered (warning)
81.82%
36 / 44
0.00% covered (danger)
0.00%
0 / 1
4.10
 showForm
93.75% covered (success)
93.75%
30 / 32
0.00% covered (danger)
0.00%
0 / 1
4.00
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\Specials;
22
23use MediaWiki\Html\Html;
24use MediaWiki\HTMLForm\HTMLForm;
25use MediaWiki\MainConfigNames;
26use MediaWiki\SpecialPage\SpecialPage;
27use MediaWiki\Utils\UrlUtils;
28
29/**
30 * A special page showing a Swagger UI for exploring REST APIs.
31 *
32 * @ingroup SpecialPage
33 * @since 1.43
34 */
35class SpecialRestSandbox extends SpecialPage {
36
37    private UrlUtils $urlUtils;
38
39    public function __construct( UrlUtils $urlUtils ) {
40        parent::__construct( 'RestSandbox' );
41
42        $this->urlUtils = $urlUtils;
43    }
44
45    /**
46     * Returns the available choices for APIs to explore.
47     *
48     * @see MainConfigSchema::RestSandboxSpecs for the structure of the array
49     *
50     * @return array[]
51     */
52    private function getApiSpecs(): array {
53        return $this->getConfig()->get( MainConfigNames::RestSandboxSpecs );
54    }
55
56    public function isListed() {
57        // Hide the special pages if there are no APIs to explore.
58        return $this->getApiSpecs() !== [];
59    }
60
61    protected function getGroupName() {
62        return 'wiki';
63    }
64
65    private function getSpecUrl( ?string $apiId ): ?string {
66        $apiSpecs = $this->getApiSpecs();
67
68        if ( $apiId !== null && $apiId !== '' ) {
69            $spec = $apiSpecs[$apiId] ?? null;
70        } else {
71            $spec = reset( $apiSpecs ) ?: null;
72        }
73
74        if ( !$spec ) {
75            return null;
76        }
77
78        return $this->urlUtils->expand( $spec['url'] );
79    }
80
81    public function execute( $sub ) {
82        $this->setHeaders();
83        $out = $this->getOutput();
84        $this->addHelpLink( 'Help:RestSandbox' );
85
86        $apiId = $this->getRequest()->getText( 'api', $sub ?? '' );
87        $specUrl = $this->getSpecUrl( $apiId );
88
89        $apiSpecs = $this->getApiSpecs();
90
91        $out->addJsConfigVars( [
92            'specUrl' => $specUrl
93        ] );
94
95        $out->addModuleStyles( [
96            'mediawiki.special',
97            'mediawiki.hlist',
98            'mediawiki.special.restsandbox.styles'
99        ] );
100
101        if ( !$apiSpecs ) {
102            $out->addHTML( Html::errorBox(
103                $out->msg( 'restsandbox-no-specs-configured' )->parse()
104            ) );
105            return;
106        }
107
108        if ( $out->getLanguage()->getCode() !== 'en' ) {
109            $out->addHTML( Html::noticeBox( $out->msg( 'restsandbox-disclaimer' )->parse(), '' ) );
110        }
111
112        $this->showForm( $apiSpecs );
113
114        if ( !$specUrl ) {
115            $out->addHTML( Html::errorBox(
116                $out->msg( 'restsandbox-no-such-api' )->params( $apiId )->parse()
117            ) );
118            return;
119        }
120
121        $out->addModules( [
122            'mediawiki.codex.messagebox.styles',
123            'mediawiki.special.restsandbox'
124        ] );
125
126        $out->addHTML( Html::openElement( 'div', [ 'id' => 'mw-restsandbox' ] ) );
127
128        // Hidden when JS is available
129        $out->addHTML( Html::errorBox(
130            $out->msg( 'restsandbox-jsonly' )->parse(),
131            '',
132            'mw-restsandbox-client-nojs'
133        ) );
134
135        // To be replaced by Swagger UI.
136        $out->addElement( 'div', [
137            'id' => 'mw-restsandbox-swagger-ui',
138            // Force direction to "LTR" with swagger-ui.
139            // Since the swagger content is not internationalized, the information is always in English.
140            // We have to force the direction to "LTR" to avoid the content (specifically json strings)
141            // from being mangled.
142            'dir' => 'ltr',
143            'lang' => 'en',
144            // For dark mode compatibility
145            'class' => 'skin-invert'
146        ] );
147
148        $out->addHTML( Html::closeElement( 'div' ) ); // #mw-restsandbox
149    }
150
151    private function showForm( array $apiSpecs ) {
152        $apis = [];
153
154        foreach ( $apiSpecs as $key => $spec ) {
155            if ( isset( $spec['msg'] ) ) {
156                $text = $this->msg( $spec['msg'] )->plain();
157            } elseif ( isset( $spec['name'] ) ) {
158                $text = $spec['name'];
159            } else {
160                $text = $key;
161            }
162
163            $apis[$text] = $key;
164        }
165
166        $formDescriptor = [
167            'intro' => [
168                'type' => 'info',
169                'raw' => true,
170                'default' => $this->msg( 'restsandbox-text' )->parseAsBlock()
171            ],
172            'api' => [
173                'type' => 'select',
174                'name' => 'api',
175                'label-message' => 'restsandbox-select-api',
176                'options' => $apis
177            ],
178            'title' => [
179                'type' => 'hidden',
180                'name' => 'title',
181                'default' => $this->getPageTitle()->getPrefixedDBkey()
182            ],
183        ];
184
185        $action = $this->getPageTitle()->getLocalURL( [ 'action' => 'submit' ] );
186
187        $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() );
188        $htmlForm->setAction( $action );
189        $htmlForm->setMethod( 'GET' );
190        $htmlForm->setId( 'mw-restsandbox-form' );
191        $htmlForm->prepareForm()->displayForm( false );
192    }
193}