Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 125
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialMathWikibase
0.00% covered (danger)
0.00%
0 / 125
0.00% covered (danger)
0.00%
0 / 8
306
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
30
 showForm
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
2
 showError
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
6
 getPlainText
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 buildPageRepresentation
0.00% covered (danger)
0.00%
0 / 48
0.00% covered (danger)
0.00%
0 / 1
30
 createHTMLHeader
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 isWikibaseAvailable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace MediaWiki\Extension\Math;
4
5use Exception;
6use ExtensionRegistry;
7use InvalidArgumentException;
8use MediaWiki\Extension\Math\Widget\WikibaseEntitySelector;
9use MediaWiki\Html\Html;
10use MediaWiki\Logger\LoggerFactory;
11use MediaWiki\MediaWikiServices;
12use MediaWiki\Output\OutputPage;
13use MediaWiki\SpecialPage\SpecialPage;
14use Message;
15use OOUI\ButtonInputWidget;
16use OOUI\FormLayout;
17
18class SpecialMathWikibase extends SpecialPage {
19    /**
20     * The parameter for this special page
21     */
22    private const PARAMETER = "qid";
23
24    /**
25     * @var MathWikibaseConnector Wikibase connection
26     */
27    private $wikibase;
28
29    /**
30     * @var \Psr\Log\LoggerInterface
31     */
32    private $logger;
33
34    public function __construct() {
35        parent::__construct( 'MathWikibase' );
36
37        $this->logger = LoggerFactory::getInstance( 'Math' );
38    }
39
40    /**
41     * @inheritDoc
42     */
43    public function execute( $par ) {
44        global $wgLanguageCode;
45
46        if ( !self::isWikibaseAvailable() ) {
47            $out = $this->getOutput();
48
49            $out->setPageTitle(
50                $this->getPlainText( 'math-wikibase-special-error-header' )
51            );
52            $out->addHTML(
53                $this->msg( 'math-wikibase-special-error-no-wikibase' )->inContentLanguage()->parse()
54            );
55            return;
56        }
57
58        if ( !$this->wikibase ) {
59            $this->wikibase = MediaWikiServices::getInstance()->get( 'Math.WikibaseConnector' );
60        }
61
62        $request = $this->getRequest();
63        $output = $this->getOutput();
64        $output->enableOOUI();
65
66        $this->setHeaders();
67        $output->addModules( [ 'mw.widgets.MathWbEntitySelector' ] );
68
69        $output->setPageTitle(
70            $this->getPlainText( 'math-wikibase-header' )
71        );
72
73        // Get request
74        $requestId = $request->getText( self::PARAMETER, $par );
75
76        // if there is no id requested, show the request form
77        if ( !$requestId ) {
78            $this->showForm();
79        } else {
80            $this->logger->debug( "Request qID: " . $requestId );
81            try {
82                $info = $this->wikibase->fetchWikibaseFromId( $requestId, $wgLanguageCode );
83                $this->logger->debug( "Successfully fetched information for qID: " . $requestId );
84                $this->buildPageRepresentation( $info, $requestId, $output );
85            } catch ( Exception $e ) {
86                $this->showError( $e );
87            }
88        }
89    }
90
91    /**
92     * Shows the form to request information for a specific Wikibase id
93     */
94    private function showForm() {
95        $actionField = new \OOUI\ActionFieldLayout(
96            new WikibaseEntitySelector( [
97                'name' => self::PARAMETER,
98                'placeholder' => $this->getPlainText( 'math-wikibase-special-form-placeholder' ),
99                'required' => true,
100                'infusable' => true,
101                'id' => 'wbEntitySelector'
102            ] ),
103            new ButtonInputWidget( [
104                'name' => 'request-qid',
105                'label' => $this->getPlainText( 'math-wikibase-special-form-button' ),
106                'type' => 'submit',
107                'flags' => [ 'primary', 'progressive' ],
108                'icon' => 'check',
109            ] ),
110            [
111                'label' => $this->getPlainText( 'math-wikibase-special-form-header' ),
112                'align' => 'top'
113            ]
114        );
115
116        $formLayout = new FormLayout( [
117            'method' => 'POST',
118            'items' => [ $actionField ]
119        ] );
120
121        $this->getOutput()->addHTML( $formLayout );
122    }
123
124    /**
125     * Shows an error message for the user and writes information to $logger
126     * @param Exception $e can potentially be any exception.
127     */
128    private function showError( Exception $e ) {
129        $this->getOutput()->setPageTitle(
130            $this->getPlainText( 'math-wikibase-special-error-header' )
131        );
132
133        if ( $e instanceof InvalidArgumentException ) {
134            $this->logger->warning( "An invalid ID was specified. Reason: " . $e->getMessage() );
135            $this->getOutput()->addHTML(
136                $this->msg( 'math-wikibase-special-error-invalid-argument' )->inContentLanguage()->parse()
137            );
138        } else {
139            $this->logger->error( "An unknown error occurred while fetching data from Wikibase.", [
140                'exception' => $e
141            ] );
142            $this->getOutput()->addHTML(
143                $this->msg( 'math-wikibase-special-error-unknown' )->inContentLanguage()->parse()
144            );
145        }
146    }
147
148    /**
149     * Helper function to shorten i18n text processing
150     * @param string $key
151     * @return string the plain text in current content language
152     */
153    private function getPlainText( $key ) {
154        return $this->msg( $key )->inContentLanguage()->plain();
155    }
156
157    /**
158     * @param MathWikibaseInfo $info
159     * @param string $qid
160     * @param OutputPage $output
161     */
162    public function buildPageRepresentation(
163        MathWikibaseInfo $info,
164        $qid,
165        OutputPage $output
166    ) {
167        $output->setPageTitle( $info->getLabel() );
168
169        // if 'instance of' is specified, it can be found in the description before a colon
170        // FIXME: There are other reasons to have a colon in an Item's description, e.g.
171        // https://www.wikidata.org/wiki/Special:MathWikibase?qid=Q6203
172        if ( preg_match( '/(.*):\s*(.*)/', $info->getDescription(), $matches ) ) {
173            $output->setSubtitle( $matches[1] );
174        }
175
176        // add formula information
177        $header = $this->msg( 'math-wikibase-formula-information' )->inContentLanguage()
178            ->plain();
179        $output->addHTML( self::createHTMLHeader( $header ) );
180
181        if ( $info->getFormattedSymbol() ) {
182            $math = $info->getFormattedSymbol();
183            $formulaInfo = new Message( 'math-wikibase-formula-header-format' );
184            $formulaInfo->rawParams(
185                $this->msg( 'math-wikibase-formula' )->inContentLanguage(),
186                $math
187            );
188            $output->addHTML( Html::rawElement( "p", [], $formulaInfo->inContentLanguage()->parse() ) );
189        }
190
191        $labelName = $this->msg(
192            'math-wikibase-formula-header-format',
193            $this->msg( 'math-wikibase-formula-name' )->inContentLanguage(),
194            $info->getLabel()
195        )->inContentLanguage()->parse();
196        $output->addHTML( Html::rawElement( "p", [], $labelName ) );
197
198        if ( isset( $matches[2] ) ) {
199            $labelType = $this->msg(
200                'math-wikibase-formula-header-format',
201                $this->msg( 'math-wikibase-formula-type' )->inContentLanguage(),
202                $matches[1]
203            )->inContentLanguage()->parse();
204            $output->addHTML( Html::rawElement( "p", [], $labelType ) );
205
206            $description = $matches[2];
207        } else {
208            $description = $info->getDescription();
209        }
210        $labelDesc = $this->msg(
211            'math-wikibase-formula-header-format',
212            $this->msg( 'math-wikibase-formula-description' )->inContentLanguage(),
213            $description
214        )->inContentLanguage()->parse();
215        $output->addHTML( Html::rawElement( "p", [], $labelDesc ) );
216
217        // add parts of formula
218        if ( $info->hasParts() ) {
219            $elementsHeader = $this->msg( 'math-wikibase-formula-elements-header' )
220                ->inContentLanguage()->plain();
221            $output->addHTML( self::createHTMLHeader( $elementsHeader ) );
222            $output->addHTML( $info->generateTableOfParts() );
223        }
224
225        // add link information
226        $wikibaseHeader = $this->msg(
227            'math-wikibase-formula-link-header',
228            $info->getDescription()
229        )->inContentLanguage()->plain();
230
231        $output->addHTML( self::createHTMLHeader( $wikibaseHeader ) );
232
233        $url = $this->wikibase->buildURL( $qid );
234        $link = Html::linkButton( $url, [ "href" => $url ] );
235        $output->addHTML( Html::rawElement( "p", [], $link ) );
236    }
237
238    /**
239     * @param string $header Plain text
240     * @return string Raw HTML
241     */
242    private static function createHTMLHeader( string $header ): string {
243        return Html::element(
244            'h2',
245            [],
246            $header
247        );
248    }
249
250    /**
251     * Check whether Wikibase is available or not
252     * @return bool
253     */
254    public static function isWikibaseAvailable(): bool {
255        return ExtensionRegistry::getInstance()->isLoaded( 'WikibaseClient' );
256    }
257}