Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
50.70% covered (warning)
50.70%
181 / 357
25.00% covered (danger)
25.00%
3 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
Question
50.70% covered (warning)
50.70%
181 / 357
25.00% covered (danger)
25.00%
3 / 12
2488.49
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 setState
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
19
 getState
60.00% covered (warning)
60.00%
3 / 5
0.00% covered (danger)
0.00%
0 / 1
5.02
 parseHeader
64.71% covered (warning)
64.71%
11 / 17
0.00% covered (danger)
0.00%
0 / 1
3.40
 parseParameters
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
8
 checkRequestOrder
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
8.02
 singleChoiceParseObject
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 multipleChoiceParseObject
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 basicTypeParseObject
0.00% covered (danger)
0.00%
0 / 130
0.00% covered (danger)
0.00%
0 / 1
1980
 parseCategories
88.89% covered (warning)
88.89%
16 / 18
0.00% covered (danger)
0.00%
0 / 1
5.03
 textFieldParseObject
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
30
 parseTextField
92.17% covered (success)
92.17%
106 / 115
0.00% covered (danger)
0.00%
0 / 1
40.77
1<?php
2
3namespace MediaWiki\Extension\Quiz;
4
5use MediaWiki\Html\TemplateParser;
6use MediaWiki\Request\WebRequest;
7use Parser;
8use UnexpectedValueException;
9use Xml;
10
11class Question {
12
13    /** @var WebRequest */
14    private $mRequest;
15
16    /** @var int */
17    private $mQuestionId;
18
19    /** @var bool */
20    private $mBeingCorrected;
21
22    /** @var bool */
23    private $mCaseSensitive;
24
25    /** @var bool */
26    private $shuffleAnswers;
27
28    /** @var Parser */
29    private $mParser;
30
31    /** @var string */
32    private $mState;
33
34    /** @var string */
35    private $mProposalPattern = '`^([+-]) ?(.*)`';
36
37    /** @var string */
38    private $mCorrectionPattern = '`^\|\|(.*)`';
39
40    /** @var string */
41    private $mCategoryPattern = '`^\|(\n|[^\|].*\n)`';
42
43    /** @var string */
44    private $mTextFieldPattern = '`\{ ([^\}]*?)(_([\d]*) ?| )\}`';
45
46    /** @var string */
47    public $mType = 'multipleChoice';
48
49    /** @var int */
50    public $mCoef = 1;
51
52    /**
53     * @param bool $beingCorrected Identifier for quiz being corrected.
54     * @param bool $caseSensitive Identifier for case sensitive inputs.
55     * @param int $questionId the Identifier of the question used to generate input names.
56     * @param bool $shufAns Identifier if answers are supposed to be shuffled.
57     * @param Parser $parser Parser the wikitext parser.
58     */
59    public function __construct( $beingCorrected, $caseSensitive, $questionId, $shufAns, $parser ) {
60        global $wgRequest;
61        $this->mRequest = $wgRequest;
62        $this->mQuestionId = $questionId;
63        $this->mBeingCorrected = $beingCorrected;
64        $this->mCaseSensitive = $caseSensitive;
65        $this->shuffleAnswers = $shufAns;
66        $this->mParser = $parser;
67        $this->mState = ( $beingCorrected ) ? 'NA' : '';
68    }
69
70    /**
71     * Mutator of the question state
72     *
73     * @param string $pState
74     */
75    private function setState( $pState ) {
76        if ( $pState == 'error' || ( $pState == 'incorrect' && $this->mState != 'error' ) ||
77            ( $pState == 'correct' && ( $this->mState == 'NA' || $this->mState == 'na_correct' ) ) ||
78            ( $pState == 'na_incorrect' && ( $this->mState == 'NA' || $this->mState == 'na_correct' ) ) ||
79            ( $pState == 'na_correct' && ( $this->mState == 'NA' ) ) ||
80            ( $pState == 'new_NA' && ( $this->mState == 'NA' || $this->mState == 'correct' ) )
81        ) {
82            $this->mState = $pState;
83        }
84
85        // Special cases
86        if ( ( $pState == 'na_incorrect' && $this->mState == 'correct' ) ||
87            ( $pState == 'correct' && $this->mState == 'na_incorrect' )
88        ) {
89            $this->mState = 'incorrect';
90        }
91    }
92
93    /**
94     * Accessor of the question state.
95     *
96     * @return string
97     */
98    public function getState() {
99        if ( $this->mState == 'na_correct' ) {
100            return 'correct';
101        } elseif ( $this->mState == 'na_incorrect' || $this->mState == 'new_NA' ) {
102            return 'NA';
103        } else {
104            return $this->mState;
105        }
106    }
107
108    /**
109     * Convert the question's header into HTML.
110     *
111     * @param string $input the quiz header in quiz syntax.
112     * @return string
113     */
114    public function parseHeader( $input ) {
115        $parametersPattern = '`\n\|([^\|].*)\s*$`';
116        $input = preg_replace_callback(
117            $parametersPattern,
118            [ $this, 'parseParameters' ],
119            $input
120        );
121        $splitHeaderPattern = '`\n\|\|`';
122        $unparsedHeader = preg_split( $splitHeaderPattern, $input );
123        $output = $this->mParser->recursiveTagParse( trim( $unparsedHeader[0] ) );
124        if ( array_key_exists( 1, $unparsedHeader ) && $this->mBeingCorrected ) {
125            $output .= "\n";
126            $output .= '<table class="correction"><tr>';
127            $output .= '<td>&#x2192;</td><td>';
128            $output .= $this->mParser->recursiveTagParse( trim( $unparsedHeader[1] ) );
129            $output .= '</td>';
130            $output .= '</tr></table>';
131        }
132        return $output;
133    }
134
135    /**
136     * Determine the question's parameters.
137     *
138     * @param array $matches elements matching $parametersPattern
139     *                         $matches[0] are the potential question parameters.
140     */
141    private function parseParameters( $matches ) {
142        $typePattern = '`t[yi]p[eo]?="(.*?)"`';
143        if ( preg_match( $typePattern, $matches[1], $type ) ) {
144            // List of all object type code and the correspondant question type.
145            switch ( $type[1] ) {
146                case '{}':
147                    $this->mType = 'textField';
148                    break;
149                case '()':
150                    $this->mType = 'singleChoice';
151                    break;
152                case '[]':
153                    $this->mType = 'multipleChoice';
154                    break;
155            }
156        }
157        $coefPattern = '`[ck]oef="(.*?)"`';
158        if ( preg_match( $coefPattern, $matches[1], $coef ) &&
159            is_numeric( $coef[1] ) && $coef[1] > 0
160        ) {
161            $this->mCoef = (int)$coef[1];
162        }
163    }
164
165    /**
166     * Check order obtained from request
167     *
168     * @param string $order The order obtained from request
169     * @param int $proposalIndex Contains the index of last Proposal
170     *
171     * @return int
172     */
173    private function checkRequestOrder( $order, $proposalIndex ) {
174        $tempOrder = explode( ' ', $order );
175
176        // Check the number of order matches number of proposals
177        if ( count( $tempOrder ) !== $proposalIndex + 1 ) {
178            return 1;
179        }
180
181        // Check if each value is numeric and is within 0 to proposalIndex rannge
182        foreach ( $tempOrder as $orderVal ) {
183            if ( !is_numeric( $orderVal ) ) {
184                return 1;
185            }
186            if ( $orderVal < 0 || $orderVal > $proposalIndex ) {
187                return 1;
188            }
189        }
190        '@phan-var int[] $tempOrder';
191
192        // Check for repeated values
193        $orderChecker = array_fill( 0, $proposalIndex + 1, 0 );
194        foreach ( $tempOrder as $orderVal ) {
195            if ( $orderChecker[ $orderVal ] !== 1 ) {
196                $orderChecker[ $orderVal ] = 1;
197            } else {
198                return 1;
199            }
200        }
201        return 0;
202    }
203
204    /**
205     * Transmit a single choice object to the basic type parser.
206     *
207     * @param string $input A question object in quiz syntax.
208     *
209     * @return string A question object in HTML.
210     */
211    public function singleChoiceParseObject( $input ) {
212        return $this->basicTypeParseObject( $input, 'radio' );
213    }
214
215    /**
216     * Transmit a multiple choice object to the basic type parser.
217     *
218     * @param string $input A question object in quiz syntax.
219     *
220     * @return string A question object in HTML.
221     */
222    public function multipleChoiceParseObject( $input ) {
223        return $this->basicTypeParseObject( $input, 'checkbox' );
224    }
225
226    /**
227     * Convert a basic type object from quiz syntax to HTML.
228     *
229     * @param string $input A question object in quiz syntax
230     * @param string $inputType
231     *
232     * @return string A question object in HTML.
233     */
234    private function basicTypeParseObject( $input, $inputType ) {
235        $output = preg_match( $this->mCategoryPattern, $input, $matches )
236            ? $this->parseCategories( $matches[1] )
237            : '';
238        $raws = preg_split( '`\n`s', $input, -1, PREG_SPLIT_NO_EMPTY );
239        // Parameters used in some special cases.
240        $expectOn = 0;
241        $attemptChecker = 0;
242        $lines = [];
243        $proposalIndex = -1;
244        foreach ( $raws as $proposalId => $raw ) {
245            $text = null;
246            $colSpan = '';
247            $rawClass = '';
248            $signesOutput = '';
249            if ( preg_match( $this->mProposalPattern, $raw, $matches ) ) {
250                $proposalIndex++;
251                $rawClass = 'proposal';
252                // Insulate the proposal signes.
253                $text = array_pop( $matches );
254                array_shift( $matches );
255                // Determine a type ID, according to the questionType and the number of signes.
256                $typeId  = substr( $this->mType, 0, 1 );
257                $typeId .= array_key_exists( 1, $matches ) ? 'c' : 'n';
258                foreach ( $matches as $signId => $sign ) {
259                    $attribs = [];
260                    $attribs['type'] = $inputType;
261                    $attribs['class'] = 'check';
262                    // Determine the input's name and value.
263                    switch ( $typeId ) {
264                        case 'mn':
265                            $name = 'q' . $this->mQuestionId . 'p' . $proposalId;
266                            $value = 'p' . $proposalId;
267                            break;
268                        case 'sn':
269                            $name = 'q' . $this->mQuestionId;
270                            $value = 'p' . $proposalId;
271                            break;
272                        case 'mc':
273                            $name = 'q' . $this->mQuestionId . 'p' . $proposalId . 's' . $signId;
274                            $value = 's' . $signId;
275                            break;
276                        case 'sc':
277                            $name = 'q' . $this->mQuestionId . 'p' . $proposalId;
278                            $value = 's' . $signId;
279                            break;
280                        default:
281                            throw new UnexpectedValueException( 'unknown typeId' );
282                    }
283                    // Determine if the input had to be checked.
284                    if ( $this->mBeingCorrected && $this->mRequest->getVal( $name ) == $value ) {
285                        $attribs['checked'] = 'checked';
286                    }
287                    // Determine if the proposal has been attempted
288                    $attemptChecker = ( $this->mBeingCorrected && $this->mRequest->getVal( $name ) === $value )
289                        ? 1
290                        : 0;
291                    // Determine the color of the cell and modify the state of the question.
292                    switch ( $sign ) {
293                        case '+':
294                            $expectOn++;
295                            // A single choice object with many correct proposal is a syntax error.
296                            if ( $this->mType == 'singleChoice' && $expectOn > 1 ) {
297                                $this->setState( 'error' );
298                                $attribs['class'] .= ' error';
299                                $attribs['title'] = wfMessage( 'quiz_legend_error' )->text();
300                                $attribs['disabled'] = 'disabled';
301                            }
302                            if ( $this->mBeingCorrected ) {
303                                if ( array_key_exists( 'checked', $attribs ) ) {
304                                    $this->setState( 'correct' );
305                                    $attribs['class'] .= ' correct';
306                                    $attribs['title'] = wfMessage( 'quiz_legend_correct' )->text();
307                                } else {
308                                    $this->setState( 'na_incorrect' );
309                                    $attribs['class'] .= ' incorrect';
310                                    $attribs['title'] = wfMessage( 'quiz_legend_incorrect' )->text();
311                                }
312                            }
313                            break;
314                        case '-':
315                            if ( $this->mBeingCorrected ) {
316                                if ( array_key_exists( 'checked', $attribs ) ) {
317                                    $this->setState( 'incorrect' );
318                                    $attribs['class'] .= ' incorrect';
319                                    $attribs['title'] = wfMessage( 'quiz_legend_incorrect' )->text();
320                                } else {
321                                    $this->setState( 'na_correct' );
322                                }
323                            }
324                            break;
325                        default:
326                            $this->setState( 'error' );
327                            $attribs['class'] .= ' error';
328                            $attribs['title'] = wfMessage( 'quiz_legend_error' )->text();
329                            $attribs['disabled'] = 'disabled';
330                            break;
331                    }
332                    $signesOutput .= '<td class="sign">';
333                    $signesOutput .= Xml::input( $name, false, $value, $attribs );
334                    $signesOutput .= '</td>';
335                }
336                if ( $typeId == 'sc' ) {
337                    // A single choice object with no correct proposal is a syntax error.
338                    if ( $expectOn == 0 ) {
339                        $this->setState( 'error' );
340                    }
341                    $expectOn = 0;
342                }
343                // If the proposal text is empty, the question has a syntax error.
344                if ( trim( $text ) == '' ) {
345                    $text = '???';
346                    $this->setState( 'error' );
347                }
348            } elseif ( preg_match( $this->mCorrectionPattern, $raw, $matches ) &&
349                $this->mBeingCorrected
350            ) {
351                $rawClass = $attemptChecker ? 'correction selected' : 'correction unselected';
352                $text = array_pop( $matches );
353                $signesOutput = '<td>&#x2192;</td>';
354                // Hacks to avoid counting the number of signes.
355                $colSpan = ' colspan="13"';
356            }
357            if ( $text !== null ) {
358                $lineOutput = '<tr class="' . $rawClass . '">' . "\n";
359                $lineOutput .= $signesOutput;
360                $lineOutput .= '<td' . $colSpan . '>';
361                $lineOutput .= $this->mParser->recursiveTagParse( $text );
362                $lineOutput .= '</td>';
363                $lineOutput .= '</tr>' . "\n";
364                if ( $rawClass === 'correction selected' || $rawClass === 'correction unselected' ) {
365                    if ( $proposalIndex === -1 ) {
366                        // Add to output directly
367                        $output .= $lineOutput;
368                    } else {
369                        // Add feedback to previous proposal
370                        $lines[ $proposalIndex ] .= $lineOutput;
371                    }
372                } else {
373                    // Add lineOutput for proposal
374                    $lines[ $proposalIndex ] = $lineOutput;
375                }
376            }
377        }
378        // A single choice object with no correct proposal is a syntax error.
379        if ( isset( $typeId ) && $typeId == 'sn' && $expectOn == 0 ) {
380            $this->setState( 'error' );
381        }
382        // Finding order of shuffled proposals
383        $order = '';
384        if ( $this->shuffleAnswers ) {
385            if ( $this->mBeingCorrected ) {
386                $order = $this->mRequest->getVal( $this->mQuestionId . '|order', '' );
387
388                // Check order values
389                $orderInvalid = $this->checkRequestOrder( $order, $proposalIndex );
390
391                // If order is invalid then order is reset
392                if ( $orderInvalid ) {
393                    $order = '';
394                    for ( $i = 0; $i <= $proposalIndex; $i++ ) {
395                        $order .= ' ' . $i;
396                    }
397                }
398            } else {
399                $orderArray = range( 0, $proposalIndex );
400                shuffle( $orderArray );
401                $order = implode( ' ', $orderArray );
402            }
403        } else {
404            for ( $i = 0; $i <= $proposalIndex; $i++ ) {
405                $order .= ' ' . $i;
406            }
407        }
408        // @phan-suppress-next-line PhanTypeMismatchArgumentNullableInternal
409        $order = ltrim( $order );
410        $tempOrder = explode( ' ', $order );
411        for ( $i = 0; $i <= $proposalIndex; ++$i ) {
412            $output .= $lines[ $tempOrder[ $i ] ];
413        }
414        $orderName = $this->mQuestionId . '|order';
415        $orderValue = $order;
416        $attribs['hidden'] = 'hidden';
417        $attribs['checked'] = 'checked';
418
419        return $this->shuffleAnswers
420            ? Xml::input( $orderName, false, $orderValue, $attribs ) . $output
421            : $output;
422    }
423
424    /**
425     * Determine the object's parameters and convert a list of categories from
426     * quiz syntax to HTML.
427     *
428     * @param string $input pipe-separated list of the various categories.
429     * @return string
430     */
431    private function parseCategories( $input ) {
432        $linkPattern = '`(\[\[.*?\]\](*SKIP)(*FAIL)|\|)|({{.*?}}(*SKIP)(*FAIL)|\|)`';
433        $categories = preg_split( $linkPattern, $input );
434        // Less than two categories is a syntax error.
435        if ( !array_key_exists( 1, $categories ) ) {
436            $categories[1] = '???';
437            $this->setState( 'error' );
438        }
439        $output = '<tr class="categories">' . "\n";
440        $this->mProposalPattern = '`^';
441        foreach ( $categories as $key => $category ) {
442            // If a category name is empty, the question has a syntax error.
443            if ( trim( $category ) == '' ) {
444                $category = '???';
445                $this->setState( 'error' );
446            }
447            $output .= '<th>' . $this->mParser->recursiveTagParse( $category ) . '</th>';
448            if ( $key == 0 ) {
449                $this->mProposalPattern .= '([+-]) ?';
450            } else {
451                $this->mProposalPattern .= '([+-])? ?';
452            }
453        }
454        $output .= '</tr>' . "\n";
455        $this->mProposalPattern .= '(.*)`';
456        return $output;
457    }
458
459    /**
460     * Convert a "text field" object to HTML.
461     *
462     * @param string $input A question object in quiz syntax.
463     *
464     * @return string A question object in HTML.
465     */
466    public function textFieldParseObject( $input ) {
467        $raws = preg_split( '`\n`s', $input, -1, PREG_SPLIT_NO_EMPTY );
468        global $wqInputId;
469        $wqInputId = $this->mQuestionId * 100;
470        $output = '';
471        foreach ( $raws as $raw ) {
472            if ( preg_match( $this->mCorrectionPattern, $raw, $matches ) ) {
473                if ( $this->mBeingCorrected ) {
474                    $rawClass = 'correction';
475                    $text = '<td>&#x2192; ' . $this->mParser->recursiveTagParse( $matches[1] ) .
476                        '</td>';
477                } else {
478                    continue;
479                }
480            } elseif ( trim( $raw ) != '' ) {
481                $rawClass = 'proposal';
482                $text = $this->mParser->recursiveTagParse( $raw );
483                $text = preg_replace_callback(
484                    $this->mTextFieldPattern,
485                    [ $this, 'parseTextField' ],
486                    $text
487                );
488                $text = '<td class="input">' . $text . '</td>';
489            } else {
490                continue;
491            }
492            $output .= '<tr class="' . $rawClass . '">' . "\n";
493            $output .= $text . "\n";
494            $output .= '</tr>' . "\n";
495        }
496        return $output;
497    }
498
499    /**
500     * @param array $input
501     * @return string
502     */
503    private function parseTextField( $input ) {
504        global $wqInputId;
505        $wqInputId++;
506        $title = '';
507        $state = '';
508        $spanClass = '';
509        $size = '';
510        $maxlength = '';
511        $class = '';
512        $value = '';
513        $disabled = '';
514        $big = '';
515        $poss = '';
516        $bigDisplay = '';
517        // determine size and maxlength of the input.
518        if ( array_key_exists( 3, $input ) ) {
519            $size = $input[3];
520            if ( $size < 3 ) {
521                $size = 1;
522            } elseif ( $size < 12 ) {
523                $size -= 2;
524            } else {
525                $size--;
526            }
527            $maxlength = $input[3];
528        }
529        $templateParser = new TemplateParser( __DIR__ . '/../templates' );
530        // Syntax error if there is no input text.
531        if ( $input[1] === "" ) {
532            $value = 'value="???"';
533            $state = 'error';
534        } else {
535            // For hiding down arrow
536            $bigDisplay = 'display: none';
537            if ( $this->mBeingCorrected ) {
538                // @phan-suppress-next-line PhanTypeMismatchArgument
539                $value = trim( $this->mRequest->getVal( $wqInputId, '' ) );
540                $state = 'NA';
541                $title = wfMessage( 'quiz_legend_unanswered' )->escaped();
542            }
543            $class = 'numbers';
544            $poss = ' ';
545            foreach (
546                preg_split( '` *\| *`', trim( $input[1] ), -1, PREG_SPLIT_NO_EMPTY ) as $possibility
547            ) {
548                if ( $state == '' || $state == 'NA' || $state == 'incorrect' ) {
549                    if ( preg_match(
550                        '`^(-?\d+\.?\d*)(-(-?\d+\.?\d*)| (\d+\.?\d*)(%))?$`',
551                        str_replace( ',', '.', $possibility ),
552                        $matches )
553                    ) {
554                        if ( array_key_exists( 5, $matches ) ) {
555                            $strlen = $size = $maxlength = '';
556                        } elseif ( array_key_exists( 3, $matches ) ) {
557                            $strlen = strlen( $matches[1] ) > strlen( $matches[3] )
558                                ? strlen( $matches[1] )
559                                : strlen( $matches[3] );
560                        } else {
561                            $strlen = strlen( $matches[1] );
562                        }
563                        if ( $this->mBeingCorrected && $value !== "" ) {
564                            $value = str_replace( ',', '.', $value );
565                            if ( is_numeric( $value ) && (
566                                (
567                                    array_key_exists( 5, $matches )
568                                    && $value >=
569                                        ( (int)$matches[1] - ( (int)$matches[1] * (int)$matches[4] ) / 100 )
570                                    && $value <=
571                                        ( (int)$matches[1] + ( (int)$matches[1] * (int)$matches[4] ) / 100 )
572                                ) || (
573                                    array_key_exists( 3, $matches ) &&
574                                    $value >= $matches[1] && $value <= $matches[3]
575                                ) || $value == $possibility )
576                            ) {
577                                $state = 'correct';
578                                $title = wfMessage( 'quiz_legend_correct' )->escaped();
579                            } else {
580                                $state = 'incorrect';
581                                $title = wfMessage( 'quiz_legend_incorrect' )->escaped();
582                            }
583                        }
584                    } else {
585                        $strlen = preg_match( '` \(i\)$`', $possibility )
586                            ? mb_strlen( $possibility ) - 4
587                            : mb_strlen( $possibility );
588                        $class = 'words';
589                        if ( $this->mBeingCorrected && $value !== "" ) {
590                            if ( $value == $possibility ||
591                                ( preg_match(
592                                    '`^' . preg_quote( $value, '`' ) . ' \(i\)$`i', $possibility
593                                ) ) ||
594                                ( !$this->mCaseSensitive && preg_match(
595                                    '`^' . preg_quote( $value, '`' ) . '$`i', $possibility
596                                ) )
597                            ) {
598                                $state = 'correct';
599                                $title = wfMessage( 'quiz_legend_correct' )->escaped();
600                            } else {
601                                $state = 'incorrect';
602                                $title = wfMessage( 'quiz_legend_incorrect' )->escaped();
603                            }
604                        }
605                    }
606                    if ( array_key_exists( 3, $input ) && $strlen > $input[3] ) {
607                        // The textfield is too short for the answer
608                        $state = 'error';
609                        $value = '<_' . $possibility . '_>';
610                    }
611                }
612                if ( $this->mBeingCorrected ) {
613                    $strlen = preg_match( '` \(i\)$`', $possibility )
614                        ? mb_strlen( $possibility ) - 4
615                        : mb_strlen( $possibility );
616                    $possibility = mb_substr( $possibility, 0, $strlen );
617                    $poss .= $possibility . '<br />';
618                }
619            }
620            if ( $this->mBeingCorrected ) {
621                $big = '▼';
622                $bigDisplay = ' ';
623            }
624        }
625        if ( $state == 'error' || $this->mBeingCorrected ) {
626            $spanClass .= " border $state";
627            $this->setState( $value === "" ? 'new_NA' : $state );
628            if ( $state == 'error' ) {
629                $size = '';
630                $maxlength = '';
631                $disabled = 'disabled';
632                $title = wfMessage( 'quiz_legend_error' )->escaped();
633            }
634        }
635        $name = $wqInputId;
636        return $templateParser->processTemplate(
637            'Answer',
638            [
639                'title' => $title,
640                'class' => $class,
641                'spanClass' => trim( $spanClass ),
642                'value' => $value,
643                'correction' => $this->mBeingCorrected,
644                'possibility' => $poss,
645                'disabled' => $disabled,
646                'size' => $size,
647                'big' => $big,
648                'maxlength' => $maxlength,
649                'name' => $name,
650                'bigDisplay' => $bigDisplay,
651            ]
652        );
653    }
654}