display_unique_or_multiple_answer()   F
last analyzed

Complexity

Conditions 35
Paths > 20000

Size

Total Lines 163
Code Lines 104

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 35
eloc 104
nc 155525
nop 13
dl 0
loc 163
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/* See license terms in /license.txt */
3
4
use Chamilo\CoreBundle\Entity\TrackEExercise;
5
use Chamilo\CoreBundle\Enums\StateIcon;
6
use Chamilo\CoreBundle\Framework\Container;
7
8
class ExerciseShowFunctions
9
{
10
    /**
11
     * Shows the answer to a fill-in-the-blanks question, as HTML.
12
     *
13
     * @param Exercise $exercise
14
     * @param int      $feedbackType
15
     * @param string   $answer
16
     * @param int      $id                           Exercise ID
17
     * @param int      $questionId                   Question ID
18
     * @param int      $resultsDisabled
19
     * @param bool     $showTotalScoreAndUserChoices
20
     * @param string   $originalStudentAnswer
21
     */
22
    public static function display_fill_in_blanks_answer(
23
        $exercise,
24
        $feedbackType,
25
        $answer,
26
        $id,
27
        $questionId,
28
        $resultsDisabled,
29
        $showTotalScoreAndUserChoices,
30
        $originalStudentAnswer = ''
31
    ) {
32
        $answerHTML = FillBlanks::getHtmlDisplayForAnswer(
33
            $answer,
34
            $feedbackType,
35
            $resultsDisabled,
36
            $showTotalScoreAndUserChoices
37
        );
38
39
        if (empty($id)) {
40
            echo '<tr><td>';
41
            echo Security::remove_XSS($answerHTML, COURSEMANAGERLOWSECURITY);
42
            echo '</td></tr>';
43
        } else {
44
            echo '<tr><td>';
45
            echo Security::remove_XSS($answerHTML, COURSEMANAGERLOWSECURITY);
46
            echo '</td>';
47
            echo '</tr>';
48
        }
49
    }
50
51
    /**
52
     * Shows the answer to a calculated question, as HTML.
53
     *
54
     *  @param Exercise $exercise
55
     * @param string    Answer text
56
     * @param int       Exercise ID
57
     * @param int       Question ID
58
     */
59
    public static function display_calculated_answer(
60
        $exercise,
61
        $feedback_type,
62
        $answer,
63
        $id,
64
        $questionId,
65
        $resultsDisabled,
66
        $showTotalScoreAndUserChoices,
67
        $expectedChoice = '',
68
        $choice = '',
69
        $status = ''
70
    ) {
71
        if ($exercise->showExpectedChoice()) {
72
            if (empty($id)) {
73
                echo '<tr><td>'.Security::remove_XSS($answer).'</td>';
74
                echo '<td>'.Security::remove_XSS($choice).'</td>';
75
                if ($exercise->showExpectedChoiceColumn()) {
76
                    echo '<td>'.Security::remove_XSS($expectedChoice).'</td>';
77
                }
78
79
                echo '<td>'.Security::remove_XSS($status).'</td>';
80
                echo '</tr>';
81
            } else {
82
                echo '<tr><td>';
83
                echo Security::remove_XSS($answer);
84
                echo '</td><td>';
85
                echo Security::remove_XSS($choice);
86
                echo '</td>';
87
                if ($exercise->showExpectedChoiceColumn()) {
88
                    echo '<td>';
89
                    echo Security::remove_XSS($expectedChoice);
90
                    echo '</td>';
91
                }
92
                echo '<td>';
93
                echo Security::remove_XSS($status);
94
                echo '</td>';
95
                echo '</tr>';
96
            }
97
        } else {
98
            if (empty($id)) {
99
                echo '<tr><td>'.Security::remove_XSS($answer).'</td></tr>';
100
            } else {
101
                echo '<tr><td>';
102
                echo Security::remove_XSS($answer);
103
                echo '</tr>';
104
            }
105
        }
106
    }
107
108
    /**
109
     * Shows the answer to an upload question.
110
     *
111
     * @param float|null $questionScore   Only used to check if > 0
112
     * @param int        $resultsDisabled Unused
113
     */
114
    public static function displayUploadAnswer(
115
        string $feedbackType,
116
        string $answer,
117
        int $exeId,
118
        int $questionId,
119
               $questionScore = null,
120
               $resultsDisabled = 0
121
    ) {
122
        $urls = [];
123
124
        $trackExercise = Container::getTrackEExerciseRepository()->find($exeId);
125
        if (null !== $trackExercise) {
126
            $questionAttempt = $trackExercise->getAttemptByQuestionId($questionId);
127
            if (null !== $questionAttempt) {
128
                $assetRepo = Container::getAssetRepository();
129
                $basePath  = rtrim(api_get_path(WEB_PATH), '/');
130
131
                foreach ($questionAttempt->getAttemptFiles() as $attemptFile) {
132
                    $asset = $attemptFile->getAsset();
133
                    if (null !== $asset) {
134
                        $urls[] = $basePath.$assetRepo->getAssetUrl($asset);
135
                    }
136
                }
137
            }
138
        }
139
140
        if (!empty($urls)) {
141
            echo '<tr><td>';
142
            echo '<ul class="max-w-3xl rounded-xl border border-gray-200 bg-white shadow-sm overflow-hidden divide-y divide-gray-200">';
143
144
            foreach ($urls as $url) {
145
                $path = parse_url($url, PHP_URL_PATH);
146
                $name = $path ? basename($path) : $url;
147
                $safeName = api_htmlentities($name);
148
                $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
149
150
                // Simple inline icon per file type (SVGs kept tiny)
151
                switch ($ext) {
152
                    case 'pdf':
153
                        $icon = '<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path d="M4 2a2 2 0 00-2 2v12a2 2 0 002 2h8l6-6V4a2 2 0 00-2-2H4z"/><path d="M12 14v4l4-4h-4z"/></svg>';
154
                        break;
155
                    case 'jpg':
156
                    case 'jpeg':
157
                    case 'png':
158
                    case 'gif':
159
                    case 'webp':
160
                        $icon = '<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path d="M4 3a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V8l-5-5H4z"/><path d="M8 13l2-2 3 3H5l2-3z"/></svg>';
161
                        break;
162
                    case 'csv':
163
                    case 'xlsx':
164
                    case 'xls':
165
                        $icon = '<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path d="M4 2a2 2 0 00-2 2v12a2 2 0 002 2h8l6-6V4a2 2 0 00-2-2H4z"/><text x="7" y="14" font-size="7" fill="white">X</text></svg>';
166
                        break;
167
                    default:
168
                        $icon = '<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"><path d="M4 2a2 2 0 00-2 2v12a2 2 0 002 2h8l6-6V4a2 2 0 00-2-2H4z"/></svg>';
169
                        break;
170
                }
171
172
                echo '<li class="flex items-center justify-between px-4 py-3 hover:bg-gray-20 transition">';
173
                echo '  <div class="flex items-center gap-3 min-w-0">';
174
                echo '      <span class="inline-flex h-9 w-9 items-center justify-center rounded-md bg-gray-100 text-gray-600">'.$icon.'</span>';
175
                echo '      <a class="truncate font-medium text-blue-600 hover:text-blue-700 hover:underline max-w-[36ch]" href="'.$url.'" target="_blank" rel="noopener noreferrer" title="'.$safeName.'">'.$safeName.'</a>';
176
                echo '  </div>';
177
                echo '  <div class="flex items-center gap-2">';
178
                echo '      <a class="text-sm text-blue-600 hover:text-blue-800" href="'.$url.'" download>Download</a>';
179
                echo '  </div>';
180
                echo '</li>';
181
            }
182
183
            echo '</ul>';
184
            echo '</td></tr>';
185
        }
186
187
        if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedbackType) {
188
            $comments = Event::get_comments($exeId, $questionId);
0 ignored issues
show
Bug introduced by
The method get_comments() does not exist on Event. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

188
            /** @scrutinizer ignore-call */ 
189
            $comments = Event::get_comments($exeId, $questionId);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
189
            if ($questionScore > 0 || !empty($comments)) {
190
                // Handled elsewhere
191
            } else {
192
                echo '<tr>';
193
                echo Display::tag('td', ExerciseLib::getNotCorrectedYetText());
194
                echo '</tr>';
195
            }
196
        }
197
    }
198
199
    /**
200
     * Shows the answer to a free-answer question, as HTML.
201
     *
202
     * @param string    Answer text
203
     * @param int       Exercise ID
204
     * @param int       Question ID
205
     */
206
    public static function display_free_answer(
207
        $feedback_type,
208
        $answer,
209
        $exe_id,
210
        $questionId,
211
        $questionScore = null,
212
        $resultsDisabled = 0
213
    ) {
214
        $comments = Event::get_comments($exe_id, $questionId);
215
216
        if (!empty($answer)) {
217
            echo '<tr><td>';
218
            echo Security::remove_XSS($answer);
219
            echo '</td></tr>';
220
        }
221
222
        if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedback_type) {
223
            if ($questionScore > 0 || !empty($comments)) {
224
            } else {
225
                echo '<tr>';
226
                echo Display::tag('td', ExerciseLib::getNotCorrectedYetText());
227
                echo '</tr>';
228
            }
229
        }
230
    }
231
232
    /**
233
     * @param $feedback_type
234
     * @param $answer
235
     * @param $trackExerciseId
236
     * @param $questionId
237
     * @param int $resultsDisabled
238
     * @param int $questionScore
239
     */
240
    public static function display_oral_expression_answer(
241
        $feedback_type,
242
        $answer,
243
        $trackExerciseId,
244
        $questionId,
245
        $resultsDisabled = 0,
246
        $questionScore = 0,
247
        $showAlertIfNotCorrected = false
248
    ) {
249
        /** @var TrackEExercise $trackExercise */
250
        $trackExercise = Container::getTrackEExerciseRepository()->find($trackExerciseId);
251
252
        if (null === $trackExerciseId) {
253
            return;
254
        }
255
256
        $questionAttempt = $trackExercise->getAttemptByQuestionId($questionId);
257
258
        if (null === $questionAttempt) {
259
            return;
260
        }
261
262
        $assetRepo = Container::getAssetRepository();
263
264
        foreach ($questionAttempt->getAttemptFiles() as $attemptFile) {
265
            echo Display::tag(
266
                'audio',
267
                '',
268
                [
269
                    'src' => $assetRepo->getAssetUrl($attemptFile->getAsset()),
270
                    'controls' => '',
271
                ]
272
            );
273
        }
274
275
        if (!empty($answer)) {
276
            echo Display::tag('p', Security::remove_XSS($answer));
277
        }
278
279
        $comment = Event::get_comments($trackExerciseId, $questionId);
280
        $teacherAudio = ExerciseLib::getOralFeedbackAudio(
281
                        $trackExerciseId,
282
                        $questionId
283
                    );
284
285
        if ($showAlertIfNotCorrected && !$questionScore && EXERCISE_FEEDBACK_TYPE_EXAM != $feedback_type && empty($comment) && empty($teacherAudio)) {
286
            echo Display::tag('p', ExerciseLib::getNotCorrectedYetText());
287
        }
288
    }
289
290
    /**
291
     * Displays the answer to a hotspot question.
292
     *
293
     * @param int    $feedback_type
294
     * @param int    $answerId
295
     * @param string $answer
296
     * @param string $studentChoice
297
     * @param string $answerComment
298
     * @param int    $resultsDisabled
299
     * @param int    $orderColor
300
     * @param bool   $showTotalScoreAndUserChoices
301
     */
302
    public static function display_hotspot_answer(
303
        $exercise,
304
        $feedback_type,
305
        $answerId,
306
        $answer,
307
        $studentChoice,
308
        $answerComment,
309
        $resultsDisabled,
310
        $orderColor,
311
        $showTotalScoreAndUserChoices
312
    ) {
313
        $hide_expected_answer = false;
314
        switch ($resultsDisabled) {
315
            case RESULT_DISABLE_SHOW_SCORE_ONLY:
316
                if (0 == $feedback_type) {
317
                    $hide_expected_answer = true;
318
                }
319
                break;
320
            case RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK:
321
            case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT:
322
                $hide_expected_answer = true;
323
                if ($showTotalScoreAndUserChoices) {
324
                    $hide_expected_answer = false;
325
                }
326
                break;
327
            case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
328
                $hide_expected_answer = true;
329
                if ($showTotalScoreAndUserChoices) {
330
                    $hide_expected_answer = false;
331
                }
332
                if (false === $showTotalScoreAndUserChoices && empty($studentChoice)) {
333
                    return '';
334
                }
335
                break;
336
        }
337
338
        if (!$hide_expected_answer
339
            && !$studentChoice
340
            && in_array($resultsDisabled, [RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER])
341
        ) {
342
            return;
343
        }
344
345
        $hotspotColors = [
346
            '', // $i starts from 1 on next loop (ugly fix)
347
            '#4271B5',
348
            '#FE8E16',
349
            '#45C7F0',
350
            '#BCD631',
351
            '#D63173',
352
            '#D7D7D7',
353
            '#90AFDD',
354
            '#AF8640',
355
            '#4F9242',
356
            '#F4EB24',
357
            '#ED2024',
358
            '#3B3B3B',
359
            '#F7BDE2',
360
        ];
361
362
        $content = '<tr>';
363
        $content .= '<td class="text-center" width="5%">';
364
        $content .= '<span class="fa fa-square fa-fw fa-2x" aria-hidden="true" style="color:'.
365
            $hotspotColors[$orderColor].'"></span>';
366
        $content .= '</td>';
367
        $content .= '<td class="text-left" width="25%">';
368
        $content .= "$answerId - $answer";
369
        $content .= '</td>';
370
        if (false === $exercise->hideComment) {
371
            $content .= '<td class="text-left" width="10%">';
372
            if (!$hide_expected_answer) {
373
                $status = Display::label(get_lang('Incorrect'), 'danger');
374
                if ($studentChoice) {
375
                    $status = Display::label(get_lang('Correct'), 'success');
376
                }
377
                $content .= $status;
378
            } else {
379
                $content .= '&nbsp;';
380
            }
381
            $content .= '</td>';
382
            if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedback_type) {
383
                $content .= '<td class="text-left" width="60%">';
384
                if ($studentChoice) {
385
                    $content .= '<span style="font-weight: bold; color: #008000;">'.nl2br($answerComment).'</span>';
386
                } else {
387
                    $content .= '&nbsp;';
388
                }
389
                $content .= '</td>';
390
            } else {
391
                $content .= '<td class="text-left" width="60%">&nbsp;</td>';
392
            }
393
        }
394
        $content .= '</tr>';
395
396
        echo $content;
397
    }
398
399
    /**
400
     * Display the answers to a multiple choice question.
401
     *
402
     * @param Exercise $exercise
403
     * @param int      $feedbackType                 Feedback type
404
     * @param int      $answerType                   Answer type
405
     * @param int      $studentChoice                Student choice
406
     * @param string   $answer                       Textual answer
407
     * @param string   $answerComment                Comment on answer
408
     * @param string   $answerCorrect                Correct answer comment
409
     * @param int      $id                           Exercise ID
410
     * @param int      $questionId                   Question ID
411
     * @param bool     $ans                          Whether to show the answer comment or not
412
     * @param bool     $resultsDisabled
413
     * @param bool     $showTotalScoreAndUserChoices
414
     * @param bool     $export
415
     */
416
    public static function display_unique_or_multiple_answer(
417
        $exercise,
418
        $feedbackType,
419
        $answerType,
420
        $studentChoice,
421
        $answer,
422
        $answerComment,
423
        $answerCorrect,
424
        $id,
425
        $questionId,
426
        $ans,
427
        $resultsDisabled,
428
        $showTotalScoreAndUserChoices,
429
        $export = false
430
    ) {
431
        if (true === $exercise->hideNoAnswer && empty($studentChoice)) {
432
            return '';
433
        }
434
        if ($export) {
435
            $answer = strip_tags_blacklist($answer, ['title', 'head']);
436
            // Fix answers that contains this tags
437
            $tags = [
438
                '<html>',
439
                '</html>',
440
                '<body>',
441
                '</body>',
442
            ];
443
            $answer = str_replace($tags, '', $answer);
444
        }
445
446
        $studentChoiceInt = (int) $studentChoice;
447
        $answerCorrectChoice = (int) $answerCorrect;
448
        $hide_expected_answer = false;
449
        $showComment = false;
450
        switch ($resultsDisabled) {
451
            case RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER:
452
                $hide_expected_answer = true;
453
                $showComment = true;
454
                if (!$answerCorrect && empty($studentChoice)) {
455
                    return '';
456
                }
457
                break;
458
            case RESULT_DISABLE_SHOW_SCORE_ONLY:
459
                if (0 == $feedbackType) {
460
                    $hide_expected_answer = true;
461
                }
462
                break;
463
            case RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK:
464
            case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT:
465
                $hide_expected_answer = true;
466
                if ($showTotalScoreAndUserChoices) {
467
                    $hide_expected_answer = false;
468
                }
469
                break;
470
            case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
471
                if (false === $showTotalScoreAndUserChoices && empty($studentChoiceInt)) {
472
                    return '';
473
                }
474
                break;
475
        }
476
477
        if (in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION])) {
478
            if ($studentChoice) {
479
                $icon = StateIcon::RADIOBOX_MARKED;
480
            } else {
481
                $icon = StateIcon::RADIOBOX_BLANK;
482
            }
483
        } else {
484
            if ($studentChoice) {
485
                $icon = StateIcon::CHECKBOX_MARKED;
486
            } else {
487
                $icon = StateIcon::CHECKBOX_BLANK;
488
            }
489
        }
490
        if (in_array($answerType, [UNIQUE_ANSWER, UNIQUE_ANSWER_NO_OPTION])) {
491
            if ($answerCorrect) {
492
                $iconAnswer = StateIcon::RADIOBOX_MARKED;
493
            } else {
494
                $iconAnswer = StateIcon::RADIOBOX_BLANK;
495
            }
496
        } else {
497
            if ($answerCorrect) {
498
                $iconAnswer = StateIcon::CHECKBOX_MARKED;
499
            } else {
500
                $iconAnswer = StateIcon::CHECKBOX_BLANK;
501
            }
502
503
        }
504
505
        $studentChoiceClass = '';
506
        if (in_array(
507
            $resultsDisabled,
508
            [
509
                RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER,
510
                RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING,
511
            ]
512
        )
513
        ) {
514
            if ($answerCorrect) {
515
                $studentChoiceClass = 'success';
516
            }
517
        }
518
519
        echo '<tr class="'.$studentChoiceClass.'">';
520
521
        echo '<td style="width:5%">';
522
        echo Display::getMdiIcon($icon, 'ch-tool-icon', null, ICON_SIZE_TINY);
523
        echo '</td>';
524
        if ($exercise->showExpectedChoiceColumn()) {
525
            if (false === $hide_expected_answer) {
526
                echo '<td style="width:5%">';
527
                echo Display::getMdiIcon($iconAnswer, 'ch-tool-icon', null, ICON_SIZE_TINY);
528
                echo '</td>';
529
            } else {
530
                echo '<td style="width:5%">';
531
                echo '-';
532
                echo '</td>';
533
            }
534
        }
535
536
        echo '<td style="width:40%">';
537
        echo $answer;
538
        echo '</td>';
539
540
        if ($exercise->showExpectedChoice()) {
541
            $status = Display::label(get_lang('Incorrect'), 'danger');
542
            if ($answerCorrect || ($answerCorrect && $studentChoiceInt === $answerCorrectChoice)) {
543
                $status = Display::label(get_lang('Correct'), 'success');
544
            }
545
            echo '<td class="text-center">';
546
            // Show only status for the selected student answer BT#16256
547
            if ($studentChoice) {
548
                echo $status;
549
            }
550
551
            echo '</td>';
552
        }
553
554
        if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedbackType) {
555
            $showComment = true;
556
        }
557
558
        if (false === $exercise->hideComment) {
559
            if ($showComment) {
560
                echo '<td style="width:20%">';
561
                $color = 'black';
562
                if ($answerCorrect) {
563
                    $color = 'green';
564
                }
565
                if ($hide_expected_answer) {
566
                    $color = '';
567
                }
568
                $comment = '<span style="font-weight: bold; color: '.$color.';">'.
569
                Security::remove_XSS($answerComment).
570
                '</span>';
571
                echo $comment;
572
                echo '</td>';
573
            } else {
574
                echo '<td>&nbsp;</td>';
575
            }
576
        }
577
578
        echo '</tr>';
579
    }
580
581
    /**
582
     * Display the answers to a multiple choice question.
583
     *
584
     * @param Exercise $exercise
585
     * @param int $feedbackType Feedback type
586
     * @param int $answerType Answer type
587
     * @param int $studentChoice Student choice
588
     * @param string  $answer Textual answer
589
     * @param string  $answerComment Comment on answer
590
     * @param string  $answerCorrect Correct answer comment
591
     * @param int $id Exercise ID
592
     * @param int $questionId Question ID
593
     * @param bool $ans Whether to show the answer comment or not
594
     * @param int $resultsDisabled
595
     * @param bool $showTotalScoreAndUserChoices
596
     */
597
    public static function display_multiple_answer_true_false(
598
        $exercise,
599
        $feedbackType,
600
        $answerType,
601
        $studentChoice,
602
        $answer,
603
        $answerComment,
604
        $answerCorrect,
605
        $id,
606
        $questionId,
607
        $ans,
608
        $resultsDisabled,
609
        $showTotalScoreAndUserChoices
610
    ) {
611
        $hide_expected_answer = false;
612
        $hideStudentChoice = false;
613
        switch ($resultsDisabled) {
614
            //case RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING:
615
            case RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER:
616
                $hideStudentChoice = false;
617
                $hide_expected_answer = true;
618
                break;
619
            case RESULT_DISABLE_SHOW_SCORE_ONLY:
620
                if (0 == $feedbackType) {
621
                    $hide_expected_answer = true;
622
                }
623
                break;
624
            case RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK:
625
            case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT:
626
                $hide_expected_answer = true;
627
                if ($showTotalScoreAndUserChoices) {
628
                    $hide_expected_answer = false;
629
                }
630
                break;
631
            case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
632
                if (false === $showTotalScoreAndUserChoices && empty($studentChoice)) {
633
                    return '';
634
                }
635
                break;
636
        }
637
638
        $content = '<tr>';
639
        if (false === $hideStudentChoice) {
640
            $content .= '<td width="5%">';
641
            $course_id = api_get_course_int_id();
642
            $new_options = [];
643
            $originOptions = Question::readQuestionOption($questionId);
644
645
            if (!empty($originOptions)) {
646
                foreach ($originOptions as $item) {
647
                    $new_options[$item['iid']] = $item;
648
                }
649
            }
650
651
            // Your choice
652
            if (isset($new_options[$studentChoice])) {
653
                $content .= get_lang($new_options[$studentChoice]['title']);
654
            } else {
655
                $content .= '-';
656
            }
657
            $content .= '</td>';
658
        }
659
660
        // Expected choice
661
        if ($exercise->showExpectedChoiceColumn()) {
662
            if (!$hide_expected_answer) {
663
                $content .= '<td width="5%">';
664
                if (isset($new_options[$answerCorrect])) {
665
                    $content .= get_lang($new_options[$answerCorrect]['title']);
666
                } else {
667
                    $content .= '-';
668
                }
669
                $content .= '</td>';
670
            }
671
        }
672
673
        $content .= '<td width="40%">';
674
        $content .= $answer;
675
        $content .= '</td>';
676
677
        if ($exercise->showExpectedChoice()) {
678
            $status = Display::label(get_lang('Incorrect'), 'danger');
679
            if (isset($new_options[$studentChoice])) {
680
                if ($studentChoice == $answerCorrect) {
681
                    $status = Display::label(get_lang('Correct'), 'success');
682
                }
683
            }
684
            $content .= '<td class="text-center">';
685
            $content .= $status;
686
            $content .= '</td>';
687
        }
688
689
        if (false === $exercise->hideComment) {
690
            if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedbackType) {
691
                $content .= '<td width="20%">';
692
                $color = 'black';
693
                if (isset($new_options[$studentChoice]) || in_array(
694
                    $exercise->results_disabled,
695
                    [
696
                        RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER,
697
                        RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING,
698
                    ]
699
                )
700
            ) {
701
                    if ($studentChoice == $answerCorrect) {
702
                        $color = 'green';
703
                    }
704
705
                    if ($hide_expected_answer) {
706
                        $color = '';
707
                    }
708
                    $content .= '<span style="font-weight: bold; color: '.$color.';">'.nl2br($answerComment).'</span>';
709
                }
710
                $content .= '</td>';
711
            }
712
        }
713
        $content .= '</tr>';
714
715
        echo $content;
716
    }
717
718
    /**
719
     * Display the answers to a multiple choice question.
720
     *
721
     * @param Exercise $exercise
722
     * @param int      $feedbackType
723
     * @param int      $studentChoice
724
     * @param int      $studentChoiceDegree
725
     * @param string   $answer
726
     * @param string   $answerComment
727
     * @param int      $answerCorrect
728
     * @param int      $questionId
729
     * @param bool     $inResultsDisabled
730
     */
731
    public static function displayMultipleAnswerTrueFalseDegreeCertainty(
732
        $exercise,
733
        $feedbackType,
734
        $studentChoice,
735
        $studentChoiceDegree,
736
        $answer,
737
        $answerComment,
738
        $answerCorrect,
739
        $questionId,
740
        $inResultsDisabled
741
    ) {
742
        $hideExpectedAnswer = false;
743
        if (0 == $feedbackType && 2 == $inResultsDisabled) {
744
            $hideExpectedAnswer = true;
745
        }
746
747
        echo '<tr><td width="5%">';
748
        $question = new MultipleAnswerTrueFalseDegreeCertainty();
749
        $courseId = api_get_course_int_id();
750
        $newOptions = Question::readQuestionOption($questionId, $courseId);
751
752
        // Your choice
753
        if (isset($newOptions[$studentChoice])) {
754
            echo get_lang($newOptions[$studentChoice]['name']);
755
        } else {
756
            echo '-';
757
        }
758
        echo '</td>';
759
760
        // Expected choice
761
        if ($exercise->showExpectedChoiceColumn()) {
762
            echo '<td width="5%">';
763
            if (!$hideExpectedAnswer) {
764
                if (isset($newOptions[$answerCorrect])) {
765
                    echo get_lang($newOptions[$answerCorrect]['name']);
766
                } else {
767
                    echo '-';
768
                }
769
            } else {
770
                echo '-';
771
            }
772
            echo '</td>';
773
        }
774
775
        echo '<td width="20%">';
776
        echo $answer;
777
        echo '</td><td width="5%" style="text-align:center;">';
778
        if (isset($newOptions[$studentChoiceDegree])) {
779
            echo $newOptions[$studentChoiceDegree]['name'];
780
        }
781
        echo '</td>';
782
783
        $position = isset($newOptions[$studentChoiceDegree]) ? $newOptions[$studentChoiceDegree]['position'] : '';
784
        $degreeInfo = $question->getResponseDegreeInfo(
785
            $studentChoice,
786
            $answerCorrect,
787
            $position
788
        );
789
790
        $degreeInfo['color'] = isset($degreeInfo['color']) ? $degreeInfo['color'] : '';
791
        $degreeInfo['background-color'] = isset($degreeInfo['background-color']) ? $degreeInfo['background-color'] : '';
792
        $degreeInfo['description'] = isset($degreeInfo['description']) ? $degreeInfo['description'] : '';
793
        $degreeInfo['label'] = isset($degreeInfo['label']) ? $degreeInfo['label'] : '';
794
795
        echo '
796
            <td width="15%">
797
                <div style="text-align:center;color: '.$degreeInfo['color'].';
798
                    background-color: '.$degreeInfo['background-color'].';
799
                    line-height:30px;height:30px;width: 100%;margin:auto;"
800
                    title="'.$degreeInfo['description'].'">'.
801
                    nl2br($degreeInfo['label']).
802
                '</div>
803
            </td>';
804
805
        if (false === $exercise->hideComment) {
806
            if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedbackType) {
807
                echo '<td width="20%">';
808
                if (isset($newOptions[$studentChoice])) {
809
                    echo '<span style="font-weight: bold; color: black;">'.nl2br($answerComment).'</span>';
810
                }
811
                echo '</td>';
812
            } else {
813
                echo '<td>&nbsp;</td>';
814
            }
815
        }
816
        echo '</tr>';
817
    }
818
819
    /**
820
     * Display the answers to a multiple choice question.
821
     *
822
     * @param Exercise $exercise
823
     * @param int Answer type
824
     * @param int Student choice
825
     * @param string  Textual answer
826
     * @param string  Comment on answer
827
     * @param string  Correct answer comment
828
     * @param int Exercise ID
829
     * @param int Question ID
830
     * @param bool Whether to show the answer comment or not
831
     */
832
    public static function display_multiple_answer_combination_true_false(
833
        $exercise,
834
        $feedbackType,
835
        $answerType,
836
        $studentChoice,
837
        $answer,
838
        $answerComment,
839
        $answerCorrect,
840
        $id,
841
        $questionId,
842
        $ans,
843
        $resultsDisabled,
844
        $showTotalScoreAndUserChoices
845
    ) {
846
        $hide_expected_answer = false;
847
        $hideStudentChoice = false;
848
        switch ($resultsDisabled) {
849
            case RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING:
850
            case RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER:
851
                $hideStudentChoice = true;
852
                $hide_expected_answer = true;
853
                break;
854
            case RESULT_DISABLE_SHOW_SCORE_ONLY:
855
                if (0 == $feedbackType) {
856
                    $hide_expected_answer = true;
857
                }
858
                break;
859
            case RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK:
860
            case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT:
861
                $hide_expected_answer = true;
862
                if ($showTotalScoreAndUserChoices) {
863
                    $hide_expected_answer = false;
864
                }
865
                break;
866
            case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
867
                if (false === $showTotalScoreAndUserChoices && empty($studentChoice)) {
868
                    return '';
869
                }
870
                break;
871
        }
872
873
        echo '<tr>';
874
875
        if (false === $hideStudentChoice) {
876
            echo '<td width="5%">';
877
            // Your choice
878
            $question = new MultipleAnswerCombinationTrueFalse();
879
            if (isset($question->options[$studentChoice])) {
880
                echo $question->options[$studentChoice];
881
            } else {
882
                echo $question->options[2];
883
            }
884
            echo '</td>';
885
        }
886
887
        // Expected choice
888
        if ($exercise->showExpectedChoiceColumn()) {
889
            if (!$hide_expected_answer) {
890
                echo '<td width="5%">';
891
                if (isset($question->options[$answerCorrect])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $question does not seem to be defined for all execution paths leading up to this point.
Loading history...
892
                    echo $question->options[$answerCorrect];
893
                } else {
894
                    echo $question->options[2];
895
                }
896
                echo '</td>';
897
            }
898
        }
899
900
        echo '<td width="40%">';
901
        echo $answer;
902
        echo '</td>';
903
904
        if ($exercise->showExpectedChoice()) {
905
            $status = '';
906
            if (isset($studentChoice)) {
907
                $status = Display::label(get_lang('Incorrect'), 'danger');
908
                if ($studentChoice == $answerCorrect) {
909
                    $status = Display::label(get_lang('Correct'), 'success');
910
                }
911
            }
912
            echo '<td class="text-center">';
913
            echo $status;
914
            echo '</td>';
915
        }
916
917
        if (false === $exercise->hideComment) {
918
            if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedbackType) {
919
                echo '<td width="20%">';
920
                //@todo replace this harcoded value
921
                if ($studentChoice || in_array(
922
                        $resultsDisabled,
923
                        [
924
                RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER,
925
                RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING,
926
                        ]
927
                    )
928
            ) {
929
                    $color = 'black';
930
                    if ($studentChoice == $answerCorrect) {
931
                        $color = 'green';
932
                    }
933
                    if ($hide_expected_answer) {
934
                        $color = '';
935
                    }
936
                    echo '<span style="font-weight: bold; color: '.$color.';">'.nl2br($answerComment).'</span>';
937
                }
938
                echo '</td>';
939
            } else {
940
                echo '<td>&nbsp;</td>';
941
            }
942
        }
943
        echo '</tr>';
944
    }
945
946
    /**
947
     * @param int  $feedbackType
948
     * @param int  $exeId
949
     * @param int  $questionId
950
     * @param null $questionScore
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $questionScore is correct as it would always require null to be passed?
Loading history...
951
     * @param int  $resultsDisabled
952
     */
953
    public static function displayAnnotationAnswer(
954
        $feedbackType,
955
        $exeId,
956
        $questionId,
957
        $questionScore = null,
958
        $resultsDisabled = 0
959
    ) {
960
        $comments = Event::get_comments($exeId, $questionId);
961
        if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedbackType) {
962
            if ($questionScore <= 0 && empty($comments)) {
963
                echo '<br />'.ExerciseLib::getNotCorrectedYetText();
964
            }
965
        }
966
    }
967
968
    /**
969
     * Displays the answers for a Multiple Answer Dropdown question (result view).
970
     * Renders a row per choice, showing: student choice, expected choice (if allowed),
971
     * the textual answer, and status (Correct/Incorrect).
972
     *
973
     * Returned string contains <tr>...</tr> rows to be echoed in the answer table.
974
     */
975
    public static function displayMultipleAnswerDropdown(
976
        Exercise $exercise,
977
        Answer $answer,
978
        array $correctAnswers,
979
        array $studentChoices,
980
        bool $showTotalScoreAndUserChoices = true
981
    ): string {
982
        // Hide if teacher wants to hide empty answers and user gave no answer
983
        if (true === $exercise->hideNoAnswer && empty($studentChoices)) {
984
            return '';
985
        }
986
987
        // Normalize inputs
988
        $correctAnswers = array_map('intval', (array) $correctAnswers);
989
        $studentChoices = array_map(
990
            'intval',
991
            array_filter((array) $studentChoices, static fn ($v) => $v !== '' && $v !== null && (int)$v !== -1)
992
        );
993
994
        // Build id => text map from Answer::getAnswers()
995
        // getAnswers() typically returns rows with keys: iid, answer, correct, comment, weighting, position
996
        $idToText = [];
997
        if (method_exists($answer, 'getAnswers')) {
998
            $rows = $answer->getAnswers();
999
            if (is_array($rows)) {
1000
                foreach ($rows as $row) {
1001
                    if (isset($row['iid'])) {
1002
                        $id = (int) $row['iid'];
1003
                        $idToText[$id] = $row['answer'] ?? '';
1004
                    }
1005
                }
1006
            }
1007
        }
1008
1009
        // Union of expected + student choices to render a single row per unique option
1010
        $allChoices = array_values(array_unique(array_merge($correctAnswers, $studentChoices)));
1011
        sort($allChoices);
1012
1013
        // Icons/labels
1014
        $checkboxOn  = Display::getMdiIcon(StateIcon::CHECKBOX_MARKED, 'ch-tool-icon', null, ICON_SIZE_TINY);
1015
        $checkboxOff = Display::getMdiIcon(StateIcon::CHECKBOX_BLANK,  'ch-tool-icon', null, ICON_SIZE_TINY);
1016
        $labelOk     = Display::label(get_lang('Correct'), 'success');
1017
        $labelKo     = Display::label(get_lang('Incorrect'), 'danger');
1018
1019
        $html = '';
1020
1021
        foreach ($allChoices as $choiceId) {
1022
            $isStudentAnswer  = in_array($choiceId, $studentChoices, true);
1023
            $isExpectedAnswer = in_array($choiceId, $correctAnswers, true);
1024
            $isCorrectAnswer  = $isStudentAnswer && $isExpectedAnswer;
1025
1026
            // Resolve displayed text safely; fall back to "None" if not found
1027
            $answerText = $idToText[$choiceId] ?? get_lang('None');
1028
1029
            if ($exercise->export) {
1030
                // Strip potentially problematic wrappers on export
1031
                $answerText = strip_tags_blacklist($answerText, ['title', 'head']);
1032
                $answerText = str_replace(['<html>', '</html>', '<body>', '</body>'], '', $answerText);
1033
            }
1034
1035
            // Respect result-visibility policy
1036
            $hideExpected = false;
1037
            switch ($exercise->selectResultsDisabled()) {
1038
                case RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER:
1039
                    $hideExpected = true;
1040
                    if (!$isCorrectAnswer && empty($studentChoices)) {
1041
                        continue 2;
1042
                    }
1043
                    break;
1044
                case RESULT_DISABLE_SHOW_SCORE_ONLY:
1045
                    if (0 == $exercise->getFeedbackType()) {
1046
                        $hideExpected = true;
1047
                    }
1048
                    break;
1049
                case RESULT_DISABLE_DONT_SHOW_SCORE_ONLY_IF_USER_FINISHES_ATTEMPTS_SHOW_ALWAYS_FEEDBACK:
1050
                case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT:
1051
                    $hideExpected = true;
1052
                    if ($showTotalScoreAndUserChoices) {
1053
                        $hideExpected = false;
1054
                    }
1055
                    break;
1056
                case RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT_NO_FEEDBACK:
1057
                    if (false === $showTotalScoreAndUserChoices && empty($studentChoices)) {
1058
                        continue 2;
1059
                    }
1060
                    break;
1061
            }
1062
1063
            // Highlight only when policy requires and the student/expected match
1064
            $rowClass = '';
1065
            if ($isCorrectAnswer
1066
                && in_array(
1067
                    $exercise->selectResultsDisabled(),
1068
                    [RESULT_DISABLE_SHOW_ONLY_IN_CORRECT_ANSWER, RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS_AND_RANKING],
1069
                    true
1070
                )
1071
            ) {
1072
                $rowClass = 'success';
1073
            }
1074
1075
            $html .= '<tr class="'.$rowClass.'">';
1076
1077
            // Student choice icon
1078
            $html .= '<td class="text-center">'.($isStudentAnswer ? $checkboxOn : $checkboxOff).'</td>';
1079
1080
            // Expected choice icon (optional)
1081
            if ($exercise->showExpectedChoiceColumn()) {
1082
                $html .= '<td class="text-center">';
1083
                $html .= $hideExpected ? '<span class="text-muted">&mdash;</span>' : ($isExpectedAnswer ? $checkboxOn : $checkboxOff);
1084
                $html .= '</td>';
1085
            }
1086
1087
            // Answer text
1088
            $html .= '<td>'.Security::remove_XSS($answerText).'</td>';
1089
1090
            // Status (optional)
1091
            if ($exercise->showExpectedChoice()) {
1092
                $html .= '<td class="text-center">'.($isCorrectAnswer ? $labelOk : $labelKo).'</td>';
1093
            }
1094
1095
            $html .= '</tr>';
1096
        }
1097
1098
        return $html;
1099
    }
1100
}
1101