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