Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 379 |
|
0.00% |
0 / 40 |
CRAP | |
0.00% |
0 / 1 |
SpecialMlpEval | |
0.00% |
0 / 379 |
|
0.00% |
0 / 40 |
17030 | |
0.00% |
0 / 1 |
isTexInputChanged | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getIdentifiers | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setStep | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
loadData | |
0.00% |
0 / 51 |
|
0.00% |
0 / 1 |
420 | |||
execute | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
20 | |||
getStep | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRandomPageText | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
42 | |||
setPage | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
20 | |||
setRevision | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
30 | |||
log | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
enableMathStyles | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
getSubStep | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getNextStep | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
56 | |||
getPreviousStep | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
72 | |||
getRenderingFields | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setFId | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
getRandomFId | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
12 | |||
getFId | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getOldId | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRevisionTitle | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
printIntorduction | |
0.00% |
0 / 69 |
|
0.00% |
0 / 1 |
420 | |||
writeLog | |
0.00% |
0 / 41 |
|
0.00% |
0 / 1 |
56 | |||
resetPage | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
resetFormula | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
printSource | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getMathMLRenderingAsHtmlFragment | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
printFormula | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
printTitle | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
printIntro | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
getWikiTextLink | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
printFormulaRef | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
printPrefix | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
printMathObjectInContext | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
20 | |||
getRelations | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
getSpeechRuleText | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getMathMlRenderer | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
updateTex | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
removeSVGs | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | use MediaWiki\Extension\Math\MathRenderer; |
4 | use MediaWiki\Logger\LoggerFactory; |
5 | use MediaWiki\MediaWikiServices; |
6 | use MediaWiki\Message\Message; |
7 | use MediaWiki\Revision\RevisionRecord; |
8 | use MediaWiki\SpecialPage\SpecialPage; |
9 | use MediaWiki\Title\Title; |
10 | |
11 | /** |
12 | * MediaWiki MathSearch extension |
13 | * |
14 | * (c) 2015 Moritz Schubotz |
15 | * GPLv2 license; info in main package. |
16 | * |
17 | * @file |
18 | * @ingroup extensions |
19 | */ |
20 | class SpecialMlpEval extends SpecialPage { |
21 | |
22 | public const STEP_PAGE = 1; |
23 | public const STEP_FORMULA = 2; |
24 | public const STEP_TEX = 3; |
25 | public const STEP_RENDERING = 4; |
26 | public const STEP_IDENTIFIERS = 5; |
27 | public const STEP_DEFINITIONS = 6; |
28 | public const STEP_FINISHED = 7; |
29 | private const MAX_ATTEMPTS = 10; |
30 | |
31 | /** @var string[] */ |
32 | private $subStepNames = [ |
33 | '' => '', |
34 | '4' => '', |
35 | '4a' => 'Mathoid-', |
36 | '4b' => 'Native-', |
37 | '4c' => 'LaTeXML-' |
38 | ]; |
39 | /** @var MathObject */ |
40 | private $selectedMathTag; |
41 | /** @var int */ |
42 | private $step; |
43 | /** @var MathIdGenerator */ |
44 | private $mathIdGen; |
45 | /** @var int */ |
46 | private $oldId; |
47 | /** @var bool|string */ |
48 | private $lastError = false; |
49 | /** @var string */ |
50 | private $fId; |
51 | /** @var RevisionRecord */ |
52 | private $revisionRecord; |
53 | /** @var bool */ |
54 | private $texInputChanged = false; |
55 | /** @var string[] */ |
56 | private $identifiers = []; |
57 | /** @var array<string,string[]>|null */ |
58 | private $relations; |
59 | /** @var string */ |
60 | private $speechRuleText; |
61 | /** @var string */ |
62 | private $subStep = ''; |
63 | /** @var string[] */ |
64 | private $renderingFields = [ 'absolute', 'best', 'size', 'spacing', 'integration', 'font' ]; |
65 | |
66 | /** |
67 | * @return bool |
68 | */ |
69 | public function isTexInputChanged() { |
70 | return $this->texInputChanged; |
71 | } |
72 | |
73 | /** |
74 | * @return string[] |
75 | */ |
76 | public function getIdentifiers() { |
77 | return $this->identifiers; |
78 | } |
79 | |
80 | function __construct() { |
81 | parent::__construct( 'MlpEval' ); |
82 | } |
83 | |
84 | private function setStep( $step ) { |
85 | $this->step = $step; |
86 | return $step; |
87 | } |
88 | |
89 | private function loadData() { |
90 | $req = $this->getRequest(); |
91 | $revId = $req->getInt( 'oldId' ); |
92 | if ( $req->getText( 'wp1-page' ) ) { |
93 | $t = Title::newFromText( $req->getText( 'wp1-page' ) ); |
94 | if ( $this->setPage( $t ) ) { |
95 | if ( $revId && $revId != $this->getOldId() ) { |
96 | $this->writeLog( "$revId was not selected", -1, $revId ); |
97 | } |
98 | } |
99 | $revId = $this->getOldId(); |
100 | } |
101 | if ( $revId === 0 ) { |
102 | return $this->setStep( 1 ); |
103 | } |
104 | if ( $this->setRevision( $revId ) === false ) { |
105 | return $this->setStep( 1 ); |
106 | } |
107 | if ( $req->getText( 'pgRst' ) ) { |
108 | return $this->resetPage(); |
109 | } elseif ( $req->getInt( 'oldStep' ) === 1 ) { |
110 | $this->writeLog( "pgSelect: User selects page" . $revId, 1 ); |
111 | } |
112 | $fId = $req->getText( 'fId' ); |
113 | $oldStep = $req->getInt( 'oldStep' ); |
114 | $restButton = $req->getBool( 'fRst' ); |
115 | if ( $oldStep >= self::STEP_FINISHED || $restButton ) { |
116 | $this->resetFormula(); |
117 | $fId = ''; |
118 | } |
119 | if ( $fId === '' ) { |
120 | $this->setFId( $this->getRandomFId() ); |
121 | return $this->setStep( 2 ); |
122 | } |
123 | if ( $this->setFId( $fId ) === false ) { |
124 | return $this->setStep( 2 ); |
125 | } |
126 | if ( $req->getInt( 'oldStep' ) == 3 || $req->getInt( 'oldStep' ) == 4 ) { |
127 | $this->setStep( 4 ); |
128 | $this->subStep = $req->getText( 'oldSubStep' ); |
129 | foreach ( $this->renderingFields as $key ) { |
130 | $val = $req->getVal( "wp4-$key" ); |
131 | $substep = $this->subStep; |
132 | if ( $val ) { |
133 | $req->setVal( "4-$key-$substep", $val ); |
134 | $req->unsetVal( "wp4-$key" ); |
135 | } |
136 | } |
137 | $nextStep = $this->getNextStep(); |
138 | if ( $nextStep !== 5 ) { |
139 | $this->subStep = $nextStep; |
140 | $this->writeLog( "User updates step 4.", 4 ); |
141 | return 4; |
142 | } else { |
143 | $this->writeLog( "User completes step 4.", 4 ); |
144 | return $this->setStep( 5 ); |
145 | } |
146 | } |
147 | if ( $req->getArray( 'wp5-identifiers' ) ) { |
148 | $this->identifiers = $req->getArray( 'wp5-identifiers' ); |
149 | $missing = $req->getText( 'wp5-missing' ); |
150 | if ( $missing ) { |
151 | // TODO: Check for invalid TeX |
152 | $this->identifiers = |
153 | array_merge( $this->identifiers, preg_split( '/[\n\r]/', $missing ) ); |
154 | } |
155 | } |
156 | $this->writeLog( "User completes step " . $req->getInt( 'oldStep' ), $req->getInt( 'oldStep' ) ); |
157 | return $this->setStep( $req->getInt( 'oldStep' ) + 1 ); |
158 | } |
159 | |
160 | /** |
161 | * The main function |
162 | * @param string|null $par |
163 | */ |
164 | public function execute( $par ) { |
165 | $this->loadData(); |
166 | $this->setHeaders(); |
167 | if ( !defined( 'MW_PHPUNIT_TEST' ) ) { |
168 | $this->printIntorduction(); |
169 | } |
170 | $form = new MlpEvalForm( $this ); |
171 | $form->show(); |
172 | if ( $this->step == 5 || $this->step == 6 ) { |
173 | $this->getOutput()->addWikiMsg( 'math-lp-5-footer' ); |
174 | } |
175 | } |
176 | |
177 | public function getStep() { |
178 | return $this->step; |
179 | } |
180 | |
181 | public function getRandomPageText() { |
182 | try { |
183 | $uid = $this->getUser()->getId(); |
184 | $dbr = MediaWikiServices::getInstance() |
185 | ->getConnectionProvider() |
186 | ->getReplicaDatabase(); |
187 | $results = $dbr->selectFieldValues( 'math_review_list', 'revision_id', |
188 | "revision_id not in (SELECT revision_id from math_mlp where user_id = $uid )", |
189 | __METHOD__, [ |
190 | 'LIMIT' => 1, |
191 | 'ORDER BY' => 'priority desc' |
192 | ] ); |
193 | if ( $results ) { |
194 | $this->setRevision( $results[0] ); |
195 | return $this->revisionRecord->getPageAsLinkTarget()->getText(); |
196 | } |
197 | } catch ( Exception $e ) { |
198 | // empty |
199 | } |
200 | $rp = MediaWikiServices::getInstance()->getSpecialPageFactory()->getPage( 'Randompage' ); |
201 | $rp->setContext( $this->getContext() ); |
202 | for ( $i = 0; $i < self::MAX_ATTEMPTS; $i++ ) { |
203 | $title = $rp->getRandomTitle(); |
204 | if ( $title !== null && $this->setPage( $title ) ) { |
205 | $this->lastError = ""; |
206 | return $title->getText(); |
207 | } |
208 | } |
209 | $this->log()->warning( "Could not find suitable page with math:" . $this->lastError ); |
210 | $this->lastError = ""; |
211 | return ""; |
212 | } |
213 | |
214 | private function setPage( Title $title ) { |
215 | if ( $title === null ) { |
216 | $this->lastError = "Title was null."; |
217 | return false; |
218 | } |
219 | if ( $title->isRedirect() ) { |
220 | $this->lastError = "Redirects are not supported"; |
221 | return false; |
222 | } |
223 | $revision = $title->getLatestRevID(); |
224 | if ( $revision ) { |
225 | return $this->setRevision( $revision ); |
226 | } else { |
227 | $this->lastError = "invalid revision"; |
228 | return false; |
229 | } |
230 | } |
231 | |
232 | /** |
233 | * @param int $revId |
234 | * @return bool |
235 | */ |
236 | private function setRevision( $revId = 0 ) { |
237 | if ( $revId == 0 && $this->oldId > 0 ) { |
238 | $revId = $this->oldId; |
239 | } else { |
240 | $this->oldId = $revId; |
241 | } |
242 | if ( $revId == 0 ) { |
243 | $this->lastError = "no revision id given"; |
244 | return false; |
245 | } |
246 | $this->revisionRecord = MediaWikiServices::getInstance() |
247 | ->getRevisionLookup() |
248 | ->getRevisionById( $revId ); |
249 | $this->mathIdGen = MathIdGenerator::newFromRevisionRecord( |
250 | $this->revisionRecord |
251 | ); |
252 | $tagCount = count( $this->mathIdGen->getMathTags() ); |
253 | if ( $tagCount == 0 ) { |
254 | $this->lastError = "has no math tags"; |
255 | return false; |
256 | } |
257 | return true; |
258 | } |
259 | |
260 | /** |
261 | * @return \Psr\Log\LoggerInterface |
262 | */ |
263 | private function log() { |
264 | return LoggerFactory::getInstance( 'MathSearch' ); |
265 | } |
266 | |
267 | private function enableMathStyles() { |
268 | $out = $this->getOutput(); |
269 | $styles = [ 'ext.math.desktop.styles', 'ext.math.scripts' ]; |
270 | if ( $this->subStep == '4b' ) { |
271 | $styles[] = 'ext.math-svg.styles'; |
272 | } elseif ( $this->subStep ) { |
273 | $styles[] = 'ext.math-mathml.styles'; |
274 | } else { |
275 | $styles[] = 'ext.math.styles'; |
276 | } |
277 | $out->addModuleStyles( $styles ); |
278 | } |
279 | |
280 | public function getSubStep() { |
281 | return $this->subStep; |
282 | } |
283 | |
284 | public function getNextStep() { |
285 | if ( $this->step == 4 ) { |
286 | switch ( $this->subStep ) { |
287 | case '': |
288 | return '4'; |
289 | case '4': |
290 | return '4a'; |
291 | case '4a': |
292 | return '4b'; |
293 | case '4b': |
294 | return '4c'; |
295 | default: |
296 | return 5; |
297 | } |
298 | } else { |
299 | return $this->step + 1; |
300 | } |
301 | } |
302 | |
303 | public function getPreviousStep( $step = false, $substep = '' ) { |
304 | if ( $step === false ) { |
305 | $step = $this->step; |
306 | $substep = $this->subStep; |
307 | } |
308 | if ( $step == 5 ) { |
309 | return '4c'; |
310 | } |
311 | if ( $step == 4 ) { |
312 | switch ( $substep ) { |
313 | case '4a': |
314 | return '4'; |
315 | case '4b': |
316 | return '4a'; |
317 | case '4c': |
318 | return '4b'; |
319 | // case '4': |
320 | default: |
321 | return '3'; |
322 | } |
323 | } else { |
324 | return $step - 1; |
325 | } |
326 | } |
327 | |
328 | /** |
329 | * @return string[] |
330 | */ |
331 | public function getRenderingFields() { |
332 | return $this->renderingFields; |
333 | } |
334 | |
335 | protected function getGroupName() { |
336 | return 'mathsearch'; |
337 | } |
338 | |
339 | /** |
340 | * @param string $fId |
341 | * |
342 | * @return true |
343 | */ |
344 | private function setFId( $fId = '' ) { |
345 | if ( $fId == '' && $this->fId != '' ) { |
346 | $fId = $this->fId; |
347 | } else { |
348 | $this->fId = $fId; |
349 | } |
350 | $this->selectedMathTag = MathObject::newFromRevisionText( $this->oldId, $fId ); |
351 | return true; |
352 | } |
353 | |
354 | /** |
355 | * @return string |
356 | */ |
357 | private function getRandomFId() { |
358 | try { |
359 | $uid = $this->getUser()->getId(); |
360 | $rid = $this->revisionRecord->getId(); |
361 | $dbr = MediaWikiServices::getInstance() |
362 | ->getConnectionProvider() |
363 | ->getReplicaDatabase(); |
364 | // Note that the math anchor is globally unique |
365 | $results = $dbr->selectFieldValues( 'math_review_list', 'anchor', |
366 | "anchor not in (SELECT anchor from math_mlp where user_id = $uid " . |
367 | "and anchor is not NULL) and revision_id = $rid", |
368 | __METHOD__, [ |
369 | 'LIMIT' => 1, |
370 | 'ORDER BY' => 'priority desc' |
371 | ] ); |
372 | if ( $results ) { |
373 | return $results[0]; |
374 | } |
375 | } catch ( Exception $e ) { |
376 | // empty |
377 | } |
378 | $unique = array_rand( $this->mathIdGen->getMathTags() ); |
379 | return $this->mathIdGen->parserKey2fId( $unique ); |
380 | } |
381 | |
382 | /** |
383 | * @return string |
384 | */ |
385 | public function getFId() { |
386 | return $this->fId; |
387 | } |
388 | |
389 | /** |
390 | * @return int |
391 | */ |
392 | public function getOldId() { |
393 | return $this->oldId; |
394 | } |
395 | |
396 | /** |
397 | * @return Title |
398 | */ |
399 | public function getRevisionTitle() { |
400 | return Title::newFromLinkTarget( $this->revisionRecord->getPageAsLinkTarget() ); |
401 | } |
402 | |
403 | private function printIntorduction() { |
404 | $this->enableMathStyles(); |
405 | $out = $this->getOutput(); |
406 | // $out->addWikiTextAsInterface( "Welcome to the MLP evaluation. Your data will be recorded." ); |
407 | // $out->addWikiTextAsInterface( "You are in step {$this->step} of possible evaluation 5 steps" ); |
408 | $this->printPrefix(); |
409 | switch ( $this->step ) { |
410 | case self::STEP_PAGE: |
411 | break; |
412 | case self::STEP_FORMULA: |
413 | $this->printMathObjectInContext(); |
414 | |
415 | break; |
416 | case self::STEP_TEX: |
417 | $mo = $this->selectedMathTag; |
418 | $this->printSource( $mo->getUserInputTex(), |
419 | $this->msg( 'math-lp-3-pretty-option-1' )->text(), 'latex' ); |
420 | $texInfo = $mo->getTexInfo(); |
421 | if ( $texInfo ) { |
422 | if ( $texInfo->getChecked() !== $mo->getUserInputTex() ) { |
423 | $this->printSource( $texInfo->getChecked(), |
424 | $this->msg( 'math-lp-3-pretty-option-2' )->text(), 'latex' ); |
425 | $this->texInputChanged = true; |
426 | } |
427 | } |
428 | break; |
429 | case self::STEP_RENDERING: |
430 | $services = MediaWikiServices::getInstance(); |
431 | switch ( $this->subStep ) { |
432 | case '4a': |
433 | $services->getUserOptionsManager()->setOption( $this->getUser(), 'math', 'mathml' ); |
434 | $this->printMathObjectInContext( false, false, |
435 | $this->getMathMLRenderingAsHtmlFragment( 'mathml' ) ); |
436 | break; |
437 | case '4b': |
438 | $services->getUserOptionsManager()->setOption( $this->getUser(), 'math', 'native' ); |
439 | $this->printMathObjectInContext( false, false, |
440 | $this->getMathMLRenderingAsHtmlFragment( 'native' ) ); |
441 | break; |
442 | case '4c': |
443 | $services->getUserOptionsManager()->setOption( $this->getUser(), 'math', 'latexml' ); |
444 | $this->printMathObjectInContext( false, false, |
445 | $this->getMathMLRenderingAsHtmlFragment( 'latexml' ), |
446 | [ __CLASS__, 'removeSVGs' ] ); |
447 | } |
448 | break; |
449 | case self::STEP_IDENTIFIERS: |
450 | $mo = MathObject::newFromRevisionText( $this->oldId, $this->fId ); |
451 | $md = $mo->getTexInfo(); |
452 | if ( $md ) { |
453 | $this->identifiers = array_unique( $md->getIdentifiers() ); |
454 | if ( $this->getRequest()->getArray( 'wp5-identifiers' ) == null ) { |
455 | $this->getRequest()->setVal( 'wp5-identifiers', $this->identifiers ); |
456 | } |
457 | } |
458 | break; |
459 | case self::STEP_DEFINITIONS: |
460 | $this->printMathObjectInContext(); |
461 | $this->enableMathStyles(); |
462 | $mo = MathObject::newFromRevisionText( $this->oldId, $this->fId ); |
463 | $this->relations = []; |
464 | $rels = $mo->getRelations(); |
465 | $srt = $mo->getMathMlAltText(); |
466 | if ( !$srt ) { |
467 | // retry to get alltext might be a caching problem |
468 | $r = new MathoidDriver( $mo->getUserInputTex() ); |
469 | $srt = $r->getSpeech(); |
470 | } |
471 | $this->speechRuleText = $srt; |
472 | $wd = new WikidataDriver(); |
473 | foreach ( $this->identifiers as $i ) { |
474 | $this->relations[$i] = []; |
475 | if ( isset( $rels[$i] ) ) { |
476 | foreach ( $rels[$i] as $rel ) { |
477 | if ( preg_match( '/\[\[(.*)\]\]/', $rel->definition, $m ) ) { |
478 | if ( $wd->search( $m[1] ) ) { |
479 | $res = $wd->getResults(); |
480 | $this->relations[$i][] = $res[1]; |
481 | } |
482 | } else { |
483 | $this->relations[$i][] = $rel->definition; |
484 | } |
485 | } |
486 | } |
487 | } |
488 | break; |
489 | } |
490 | } |
491 | |
492 | private function writeLog( $message, $step = false, $revId = false ) { |
493 | $userId = $this->getUser()->getId(); |
494 | $logData = [ |
495 | 'data' => $this->getRequest()->getValues(), |
496 | 'header' => $this->getRequest()->getAllHeaders() |
497 | ]; |
498 | $json = json_encode( $logData ); |
499 | if ( $step == false ) { |
500 | $step = $this->step; |
501 | } |
502 | if ( !$revId ) { |
503 | $revId = $this->oldId; |
504 | } |
505 | $row = [ |
506 | 'user_id' => $userId, |
507 | 'step' => $step, |
508 | 'json_data' => $json, |
509 | 'revision_id' => $revId, |
510 | 'anchor' => $this->fId ?? 'undefined', // NULL is no longer allowed |
511 | 'comment' => $message |
512 | ]; |
513 | if ( $userId == 0 ) { |
514 | $this->printSource( var_export( $message, true ), 'Error: No user found to store results.' ); |
515 | return; |
516 | } |
517 | $dbw = MediaWikiServices::getInstance() |
518 | ->getConnectionProvider() |
519 | ->getPrimaryDatabase(); |
520 | |
521 | $dbw->upsert( 'math_mlp', $row, [ [ 'user_id', 'revision_id', 'anchor', 'step' ] ], $row ); |
522 | if ( $this->fId ) { |
523 | $dbw->startAtomic( __METHOD__ ); |
524 | $cnt = $dbw->selectField( 'math_mlp', 'count( distinct user_id)', [ |
525 | 'revision_id' => $revId, |
526 | 'anchor' => $this->fId |
527 | ] ); |
528 | if ( $cnt == 1 ) { |
529 | $row = [ |
530 | 'revision_id' => $revId, |
531 | 'anchor' => $this->fId, |
532 | 'priority' => 2 |
533 | ]; |
534 | $dbw->upsert( 'math_review_list', $row, [ [ 'revision_id', 'anchor' ] ], $row ); |
535 | } elseif ( $cnt > 1 ) { |
536 | $dbw->delete( 'math_review_list', [ 'revision_id', 'anchor' ] ); |
537 | } |
538 | $dbw->endAtomic( __METHOD__ ); |
539 | } |
540 | } |
541 | |
542 | private function resetPage() { |
543 | $req = $this->getRequest(); |
544 | $rndRev = $req->getInt( 'oldId' ); |
545 | $oldStep = $req->getInt( 'oldStep' ); |
546 | if ( $rndRev && $oldStep == 1 ) { |
547 | $this->writeLog( "$rndRev was not selected, by button", -1, $rndRev ); |
548 | } |
549 | $req->unsetVal( 'wp1-page' ); |
550 | $req->unsetVal( 'oldId' ); |
551 | $req->unsetVal( 'fId' ); |
552 | $req->unsetVal( 'oldStep' ); |
553 | $this->fId = ''; |
554 | return $this->setStep( 1 ); |
555 | } |
556 | |
557 | private function resetFormula() { |
558 | $req = $this->getRequest(); |
559 | $this->writeLog( "pgRst: User selects another formula", $req->getInt( 'oldStep' ) ); |
560 | $valueNames = $req->getValueNames( [ 'oldId', 'wpEditToken' ] ); |
561 | foreach ( $valueNames as $name ) { |
562 | $req->unsetVal( $name ); |
563 | } |
564 | $this->fId = ''; |
565 | } |
566 | |
567 | private function printSource( $source, $description = "", $language = "text", $linestart = true ) { |
568 | if ( $description ) { |
569 | $description .= ": "; |
570 | } |
571 | $this->getOutput()->addWikiTextAsInterface( "$description<syntaxhighlight lang=\"$language\">" . |
572 | $source . '</syntaxhighlight>', $linestart ); |
573 | } |
574 | |
575 | /** |
576 | * @param string $mode |
577 | * @param float $factor |
578 | * @param string|false $tex |
579 | * @param array $options |
580 | * @return string |
581 | */ |
582 | public function getMathMLRenderingAsHtmlFragment( |
583 | $mode, $factor = 2.5, $tex = false, $options = [] |
584 | ) { |
585 | $renderer = $this->getMathMlRenderer( $mode, $tex, $options ); |
586 | $largeMathML = $renderer->getMathml(); |
587 | $factor = round( $factor * 100 ); |
588 | return preg_replace( |
589 | "/<math/", |
590 | "<math style=\"font-size: {$factor}% !important;\"", |
591 | $largeMathML |
592 | ); |
593 | } |
594 | |
595 | private function printFormula() { |
596 | $mo = MathObject::newFromRevisionText( $this->oldId, $this->fId ); |
597 | /** @var MathRenderer $renderer */ |
598 | $renderer = MediaWikiServices::getInstance() |
599 | ->get( 'Math.RendererFactory' ) |
600 | ->getRenderer( $mo->getUserInputTex(), [] ); |
601 | if ( $renderer->render() ) { |
602 | $output = $renderer->getHtmlOutput(); |
603 | } else { |
604 | $output = $renderer->getLastError(); |
605 | } |
606 | $this->getOutput()->addHTML( $output ); |
607 | } |
608 | |
609 | private function printTitle() { |
610 | $sectionTitle = $this->msg( "math-lp-{$this->step}-head" ) |
611 | ->params( $this->subStepNames[$this->subStep], $this->subStep ); |
612 | $this->getOutput()->addHTML( "<h2>$sectionTitle</h2>" ); |
613 | } |
614 | |
615 | private function printIntro() { |
616 | $msg = |
617 | new Message( "math-lp-{$this->step}-intro", [ $this->subStepNames[$this->subStep] ] ); |
618 | $this->getOutput()->addWikiTextAsInterface( $msg->text() ); |
619 | } |
620 | |
621 | private function getWikiTextLink() { |
622 | $description = "{$this->getRevisionTitle()}#{$this->fId}"; |
623 | return "[[Special:Permalink/{$this->oldId}#{$this->fId}|$description]]"; |
624 | } |
625 | |
626 | private function printFormulaRef() { |
627 | $this->getOutput()->addWikiMsg( 'math-lp-formula-ref', $this->getWikiTextLink(), |
628 | $this->selectedMathTag->getWikiText(), $this->revisionRecord->getTimestamp() ); |
629 | } |
630 | |
631 | private function printPrefix() { |
632 | if ( $this->step > 1 ) { |
633 | $this->printFormulaRef(); |
634 | } |
635 | if ( $this->step === 1 ) { |
636 | $this->printIntro(); |
637 | $this->printTitle(); |
638 | } else { |
639 | $this->printTitle(); |
640 | $this->printIntro(); |
641 | } |
642 | } |
643 | |
644 | /** |
645 | * @param bool $highlight |
646 | * @param bool $collapsed |
647 | * @param string|false $formula |
648 | * @param callback|false $filter |
649 | */ |
650 | public function printMathObjectInContext( |
651 | $highlight = true, $collapsed = true, $formula = false, $filter = false |
652 | ) { |
653 | if ( $collapsed ) { |
654 | $collapsed = "mw-collapsed"; |
655 | } |
656 | $out = $this->getOutput(); |
657 | $hl = new MathHighlighter( $this->fId, $this->oldId, $highlight ); |
658 | $out->addHTML( |
659 | "<div class=\"toccolours mw-collapsible $collapsed\" style=\"text-align: left\">" |
660 | ); |
661 | if ( !$formula ) { |
662 | $this->printFormula(); |
663 | } else { |
664 | $out->addHTML( $formula ); |
665 | } |
666 | $out->addHTML( '<div class="mw-collapsible-content">' ); |
667 | |
668 | $popts = $out->parserOptions(); |
669 | $popts->setInterfaceMessage( false ); |
670 | |
671 | $parser = MediaWikiServices::getInstance()->getParserFactory()->getInstance(); |
672 | $parserOutput = $parser->parse( |
673 | $hl->getWikiText(), $this->getRevisionTitle(), $popts )->getText(); |
674 | if ( $filter ) { |
675 | call_user_func_array( $filter, [ &$parserOutput ] ); |
676 | } |
677 | $out->addHTML( $parserOutput ); |
678 | $out->addHTML( '</div></div>' ); |
679 | } |
680 | |
681 | /** |
682 | * @param string|null $key |
683 | * @return string[]|array |
684 | */ |
685 | public function getRelations( $key = null ) { |
686 | if ( $key ) { |
687 | return $this->relations[$key]; |
688 | } |
689 | return $this->relations; |
690 | } |
691 | |
692 | /** |
693 | * @return string |
694 | */ |
695 | public function getSpeechRuleText() { |
696 | return $this->speechRuleText; |
697 | } |
698 | |
699 | /** |
700 | * @param string $mode |
701 | * @param string|false $tex |
702 | * @param array $options |
703 | * @return MathRenderer |
704 | */ |
705 | private function getMathMlRenderer( $mode, $tex, $options ) { |
706 | $this->updateTex( $tex, $options ); |
707 | $renderer = MathRenderer::getRenderer( $tex, $options, $mode ); |
708 | $renderer->checkTeX(); |
709 | $renderer->render(); |
710 | $renderer->writeCache(); |
711 | return $renderer; |
712 | } |
713 | |
714 | /** |
715 | * @param string|false &$tex |
716 | * @param array &$options |
717 | */ |
718 | private function updateTex( &$tex, &$options ) { |
719 | if ( !$tex ) { |
720 | $tex = $this->selectedMathTag->getUserInputTex(); |
721 | $options = $this->selectedMathTag->getParams(); |
722 | } |
723 | } |
724 | |
725 | public static function removeSVGs( &$html ) { |
726 | // $html = str_replace( 'style="display: none;"><math', '><math', $html ); |
727 | // $html = preg_replace( '/<meta(.*?)mwe-math-fallback-image(.*?)>/', '', $html ); |
728 | } |
729 | |
730 | } |