Passed
Pull Request — master (#7124)
by
unknown
09:36
created

ExerciseShowFunctions::display_calculated_answer()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 45
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 29
nc 6
nop 10
dl 0
loc 45
rs 8.8337
c 0
b 0
f 0

How to fix   Many Parameters   

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
     * Display the uploaded files for an exercise question, using Tailwind-based markup.
110
     *
111
     * This method now relies on getUploadAnswerFiles() which already resolves
112
     * ResourceNode → ResourceFile → public URL.
113
     *
114
     * @param string $feedbackType
115
     * @param string $answer
116
     * @param int    $exeId
117
     * @param int    $questionId
118
     * @param mixed  $questionScore
119
     * @param int    $resultsDisabled
120
     *
121
     * @return void
122
     */
123
    public static function displayUploadAnswer(
124
        string $feedbackType,
125
        string $answer,
126
        int $exeId,
127
        int $questionId,
128
               $questionScore = null,
129
               $resultsDisabled = 0
130
    ) {
131
        // URLs resolved from ResourceNode (resource-based storage)
132
        $urls = ExerciseLib::getUploadAnswerFiles($exeId, $questionId, true);
133
134
        if (!empty($urls)) {
135
            echo '<tr><td>';
136
            echo '<ul class="max-w-3xl rounded-xl border border-gray-200 bg-white shadow-sm overflow-hidden divide-y divide-gray-200">';
137
138
            foreach ($urls as $url) {
139
                $path = parse_url($url, PHP_URL_PATH);
140
                $name = $path ? basename($path) : $url;
141
                $safeName = api_htmlentities($name);
142
                $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
143
144
                // Simple inline icon per file type (SVG kept small and neutral)
145
                switch ($ext) {
146
                    case 'pdf':
147
                        $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>';
148
                        break;
149
                    case 'jpg':
150
                    case 'jpeg':
151
                    case 'png':
152
                    case 'gif':
153
                    case 'webp':
154
                        $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>';
155
                        break;
156
                    case 'csv':
157
                    case 'xlsx':
158
                    case 'xls':
159
                        $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>';
160
                        break;
161
                    default:
162
                        $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>';
163
                        break;
164
                }
165
166
                echo '<li class="flex items-center justify-between px-4 py-3 hover:bg-gray-20 transition">';
167
                echo '  <div class="flex items-center gap-3 min-w-0">';
168
                echo '      <span class="inline-flex h-9 w-9 items-center justify-center rounded-md bg-gray-100 text-gray-600">'.$icon.'</span>';
169
                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>';
170
                echo '  </div>';
171
                echo '  <div class="flex items-center gap-2">';
172
                echo '      <a class="text-sm text-blue-600 hover:text-blue-800" href="'.$url.'" download>Download</a>';
173
                echo '  </div>';
174
                echo '</li>';
175
            }
176
177
            echo '</ul>';
178
            echo '</td></tr>';
179
        }
180
181
        // Feedback / correction status (unchanged logic)
182
        if (EXERCISE_FEEDBACK_TYPE_EXAM != $feedbackType) {
183
            $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

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