Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialMathShowImage
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 5
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 setHeaders
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
 execute
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
240
 printSvgError
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getGroupName
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 InvalidArgumentException;
6use MediaWiki\Extension\Math\Render\RendererFactory;
7use MediaWiki\MainConfigNames;
8use MediaWiki\SpecialPage\SpecialPage;
9
10/**
11 * Description of SpecialMathShowSVG
12 *
13 * @author Moritz Schubotz (Physikerwelt)
14 */
15class SpecialMathShowImage extends SpecialPage {
16    /** @var bool */
17    private $noRender = false;
18    /** @var MathRenderer|null */
19    private $renderer = null;
20    /** @var string */
21    private $mode = MathConfig::MODE_MATHML;
22
23    /** @var MathConfig */
24    private $mathConfig;
25
26    /** @var RendererFactory */
27    private $rendererFactory;
28
29    /**
30     * @param MathConfig $mathConfig
31     * @param RendererFactory $rendererFactory
32     */
33    public function __construct(
34        MathConfig $mathConfig,
35        RendererFactory $rendererFactory
36    ) {
37        parent::__construct(
38            'MathShowImage',
39            '', // Don't restrict
40            false // Don't show on Special:SpecialPages - it's not useful interactively
41        );
42        $this->mathConfig = $mathConfig;
43        $this->rendererFactory = $rendererFactory;
44    }
45
46    /**
47     * Sets headers - this should be called from the execute() method of all derived classes!
48     * @param bool $success
49     */
50    public function setHeaders( $success = true ) {
51        $out = $this->getOutput();
52        $request = $this->getRequest();
53        $out->setArticleBodyOnly( true );
54        $out->setArticleRelated( false );
55        $out->setRobotPolicy( "noindex,nofollow" );
56        $out->disable();
57        $request->response()->header( "Content-type: image/svg+xml; charset=utf-8" );
58        if ( $success && !( $this->noRender ) ) {
59            $request->response()->header(
60                'Cache-Control: public, s-maxage=604800, max-age=3600'
61            ); // 1 week (server) 1 hour (client)
62            $request->response()->header( 'Vary: User-Agent' );
63        }
64    }
65
66    public function execute( $par ) {
67        $request = $this->getRequest();
68        $hash = $request->getText( 'hash', '' );
69        $tex = $request->getText( 'tex', '' );
70        if ( $this->getConfig()->get( 'MathEnableExperimentalInputFormats' ) ) {
71            $asciimath = $request->getText( 'asciimath', '' );
72        } else {
73            $asciimath = '';
74        }
75        $this->mode = MathConfig::normalizeRenderingMode( $request->getText( 'mode' ) );
76        if ( !$this->mathConfig->isValidRenderingMode( $this->mode ) ) {
77            // Fallback to the default if an invalid mode was specified
78            $this->mode = MathConfig::MODE_MATHML;
79        }
80        if ( $hash === '' && $tex === '' && $asciimath === '' ) {
81            $this->setHeaders( false );
82            echo $this->printSvgError( 'No Inputhash specified' );
83            return;
84        }
85
86        if ( $tex === '' && $asciimath === '' ) {
87            try {
88                $this->renderer = $this->rendererFactory->getFromHash( $hash );
89            } catch ( InvalidArgumentException $exception ) {
90                $this->setHeaders( false );
91                echo $this->printSvgError( $exception->getMessage() );
92                return;
93            }
94            $this->noRender = $request->getBool( 'noRender', false );
95            $isInDatabase = $this->renderer->readFromCache();
96            if ( $isInDatabase || $this->noRender ) {
97                $success = $isInDatabase;
98            } else {
99                $success = $this->renderer->render();
100            }
101        } elseif ( $asciimath === '' ) {
102            $this->renderer = $this->rendererFactory->getRenderer( $tex, [], $this->mode );
103            $success = $this->renderer->render();
104        } else {
105            $this->renderer = $this->rendererFactory->getRenderer(
106                $asciimath, [ 'type' => 'ascii' ], $this->mode
107            );
108            $success = $this->renderer->render();
109        }
110        if ( $success ) {
111            $output = $this->renderer->getSvg();
112        } else {
113            $output = $this->printSvgError( $this->renderer->getLastError() );
114        }
115        if ( $output == "" ) {
116            $output = $this->printSvgError( 'No Output produced' );
117            $success = false;
118        }
119        $this->setHeaders( $success );
120        echo $output;
121        if ( $success ) {
122            $this->renderer->writeCache();
123        }
124    }
125
126    /**
127     * Prints the specified error message as svg.
128     * @param string $msg error message, HTML escaped
129     * @return string xml svg image with the error message
130     */
131    private function printSvgError( $msg ) {
132        $result = <<<SVG
133<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 4" preserveAspectRatio="xMidYMid meet" >
134<text text-anchor="start" fill="red" y="2">
135$msg
136</text>
137</svg>
138SVG;
139        if ( $this->getConfig()->get( MainConfigNames::DebugComments ) ) {
140            $result .= '<!--' . var_export( $this->renderer, true ) . '-->';
141        }
142        return $result;
143    }
144
145    protected function getGroupName() {
146        return 'other';
147    }
148}