ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.assMultipleChoiceGUI.php
Go to the documentation of this file.
1<?php
2
34{
35 private bool $rebuild_thumbnails = false;
36
45 public function __construct($id = -1)
46 {
48 $this->object = new assMultipleChoice();
49 if ($id >= 0) {
50 $this->object->loadFromDb($id);
51 }
52 }
53
57 public function hasInlineFeedback(): bool
58 {
59 return $this->object->feedbackOBJ->isSpecificAnswerFeedbackAvailable($this->object->getId());
60 }
61
65 protected function writePostData(bool $always = false): int
66 {
67 $hasErrors = (!$always) ? $this->editQuestion(true) : false;
68 if (!$hasErrors) {
69 $is_singleline = $this->getEditAnswersSingleLine();
70 $form = $this->buildEditForm($is_singleline);
71 $form->setValuesByPost();
74 $this->writeAnswerSpecificPostData($form);
76 return 0;
77 }
78 return 1;
79 }
80
88 protected function getEditAnswersSingleLine(bool $checkonly = false): bool
89 {
90 if ($this->object->getSelfAssessmentEditingMode()) {
91 return $this->object->isSingleline();
92 }
93
94 if ($checkonly) {
95 return $this->request_data_collector->int('types') === 0;
96 }
97
98 if (empty($this->object->getLastChange())
99 && !$this->request_data_collector->isset('types')) {
100 // a new question is edited
101 return $this->object->getMultilineAnswerSetting() === 0;
102 }
103 // a saved question is edited
104 return $this->object->isSingleline();
105 }
106
107 public function editQuestion(
108 bool $checkonly = false,
109 ?bool $is_save_cmd = null
110 ): bool {
111 $save = $is_save_cmd ?? $this->isSaveCommand();
112
113 $is_singleline = $this->getEditAnswersSingleLine($checkonly);
114
115 $form = $this->buildEditForm($is_singleline);
116
117 if ($is_singleline) {
118 $form->setMultipart(true);
119 } else {
120 $form->setMultipart(false);
121 }
122
123 $errors = false;
124
125 if ($save) {
126 $form->getItemByPostVar('selection_limit')->setMaxValue(count($this->request_data_collector->raw('choice')['answer'] ?? []));
127
128 $form->setValuesByPost();
129 $errors = !$this->checkMaxPointsNotNegative($form) || !$form->checkInput();
130 if ($errors) {
131 $checkonly = false;
132 }
133 }
134
135 if (!$checkonly) {
136 $this->renderEditForm($form);
137 }
138 return $errors;
139 }
140
141 private function checkMaxPointsNotNegative(ilPropertyFormGUI $form): bool
142 {
143 $choice = $form->getItemByPostVar('choice');
144 if (!$choice instanceof ilMultipleChoiceWizardInputGUI) {
145 return true;
146 }
147
148 $answers = $choice->getValues();
149 $total_max_points = 0;
151 foreach ($answers as $answer) {
152 $total_max_points += max($answer->getPointsChecked(), $answer->getPointsUnchecked());
153 }
154
155 if ($total_max_points < 0) {
156 $choice->setAlert($this->lng->txt('total_max_points_cannot_be_negative'));
157 return false;
158 }
159
160 return true;
161 }
162
164 {
165 parent::addBasicQuestionFormProperties($form);
166 $form->getItemByPostVar('question')->setInitialRteWidth('100');
167 }
168
169 public function uploadchoice(): void
170 {
171 $this->setAdditionalContentEditingModeFromPost();
172 $this->writePostData(true);
173 $this->editQuestion();
174 }
175
176 public function removeimagechoice(): void
177 {
178 $this->setAdditionalContentEditingModeFromPost();
179 $this->writePostData(true);
180 $this->object->removeAnswerImage($this->request_data_collector->getCmdIndex('removeimagechoice'));
181 $this->editQuestion();
182 }
183
184 public function addchoice(): void
185 {
186 $this->writePostData(true);
187 $position = $this->request_data_collector->getCmdIndex('addchoice');
188 $this->object->addAnswer("", 0, $position + 1);
189 $this->editQuestion();
190 }
191
192 public function removechoice(): void
193 {
194 $this->writePostData(true);
195 $this->object->deleteAnswer($this->request_data_collector->getCmdIndex('removechoice'));
196 $this->editQuestion();
197 }
198
199 public function getSolutionOutput(
200 int $active_id,
201 ?int $pass = null,
202 bool $graphical_output = false,
203 bool $result_output = false,
204 bool $show_question_only = true,
205 bool $show_feedback = false,
206 bool $show_correct_solution = false,
207 bool $show_manual_scoring = false,
208 bool $show_question_text = true,
209 bool $show_inline_feedback = true
210 ): string {
211 if ($active_id > 0 && !$show_correct_solution) {
212 $user_solution = $this->object->getSolutionValues($active_id, $pass);
213 } else {
214 $user_solution = [];
215 foreach ($this->object->answers as $index => $answer) {
216 $points_checked = $answer->getPointsChecked();
217 $points_unchecked = $answer->getPointsUnchecked();
218 if ($points_checked > $points_unchecked && $points_checked > 0) {
219 $user_solution[] = ['value1' => $index];
220 }
221 }
222 }
223
224 return $this->renderSolutionOutput(
225 $user_solution,
226 $active_id,
227 $pass,
228 $graphical_output,
229 $result_output,
230 $show_question_only,
231 $show_feedback,
232 $show_correct_solution,
233 $show_manual_scoring,
234 $show_question_text,
235 false,
236 $show_inline_feedback,
237 );
238 }
239
240 public function renderSolutionOutput(
241 mixed $user_solutions,
242 int $active_id,
243 ?int $pass,
244 bool $graphical_output = false,
245 bool $result_output = false,
246 bool $show_question_only = true,
247 bool $show_feedback = false,
248 bool $show_correct_solution = false,
249 bool $show_manual_scoring = false,
250 bool $show_question_text = true,
251 bool $show_autosave_title = false,
252 bool $show_inline_feedback = false,
253 ): ?string {
254 $user_solution = [];
255
256 foreach ($user_solutions as $idx => $solution_value) {
257 array_push($user_solution, $solution_value["value1"]);
258 }
259
260 $template = new ilTemplate("tpl.il_as_qpl_mc_mr_output_solution.html", true, true, "components/ILIAS/TestQuestionPool");
261 $solutiontemplate = new ilTemplate("tpl.il_as_tst_solution_output.html", true, true, "components/ILIAS/TestQuestionPool");
262 $keys = $this->getChoiceKeys();
263 foreach ($keys as $answer_id) {
264 $answer = $this->object->answers[$answer_id];
265 if (($active_id > 0) && (!$show_correct_solution)) {
266 if ($graphical_output) {
267 // output of ok/not ok icons for user entered solutions
268 $ok = false;
269 $checked = false;
270 foreach ($user_solution as $mc_solution) {
271 if ((string) $mc_solution === (string) $answer_id) {
272 $checked = true;
273 }
274 }
275 if ($checked) {
276 if ($answer->getPointsChecked() > $answer->getPointsUnchecked()) {
277 $ok = self::CORRECTNESS_OK;
278 } else {
279 $ok = self::CORRECTNESS_NOT_OK;
280 }
281 } else {
282 if ($answer->getPointsChecked() > $answer->getPointsUnchecked()) {
283 $ok = self::CORRECTNESS_NOT_OK;
284 } else {
285 $ok = self::CORRECTNESS_OK;
286 }
287 }
288 $icon = $this->generateCorrectnessIconsForCorrectness($ok);
289 $template->setCurrentBlock("icon_ok");
290 $template->setVariable("ICON_OK", $icon);
291 $template->parseCurrentBlock();
292 }
293 }
294 if ($answer->hasImage()) {
295 $template->setCurrentBlock("answer_image");
296 if ($this->object->getThumbSize()) {
297 $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $answer->getImage());
298 } else {
299 $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $answer->getImage());
300 }
301 $alt = $answer->getImage();
302 if (strlen($answer->getAnswertext())) {
303 $alt = $answer->getAnswertext();
304 }
305 $alt = preg_replace("/<[^>]*?>/", "", $alt);
306 $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
307 $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
308 $template->parseCurrentBlock();
309 }
310
311
312 if (($show_feedback || !$this->isTestPresentationContext()) && $show_inline_feedback) {
313 if ($this->object->getSpecificFeedbackSetting() == 2) {
314 foreach ($user_solution as $mc_solution) {
315 if ((string) $mc_solution === (string) $answer_id) {
316 $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation(
317 $this->object->getId(),
318 0,
319 $answer_id
320 );
321 if (strlen($fb)) {
322 $template->setCurrentBlock("feedback");
323 $template->setVariable("FEEDBACK", $this->renderLatex(
325 ));
326 $template->parseCurrentBlock();
327 }
328 }
329 }
330 }
331
332 if ($this->object->getSpecificFeedbackSetting() == 1) {
333 $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation(
334 $this->object->getId(),
335 0,
336 $answer_id
337 );
338 if (strlen($fb)) {
339 $template->setCurrentBlock("feedback");
340 $template->setVariable("FEEDBACK", $this->renderLatex(
342 ));
343 $template->parseCurrentBlock();
344 }
345 }
346 }
347 if ($show_feedback) {
348 if ($this->object->getSpecificFeedbackSetting() == 3) {
349 $answer = $this->object->getAnswer($answer_id);
350
351 if ($answer->getPoints() > 0) {
352 $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation(
353 $this->object->getId(),
354 0,
355 $answer_id
356 );
357 if (strlen($fb)) {
358 $template->setCurrentBlock("feedback");
359 $template->setVariable("FEEDBACK", $this->renderLatex(
361 ));
362 $template->parseCurrentBlock();
363 }
364 }
365 }
366 }
367
368
369
370 $template->setCurrentBlock("answer_row");
371 $template->setVariable("ANSWER_TEXT", $this->renderLatex(
372 ilLegacyFormElementsUtil::prepareTextareaOutput($answer->getAnswertext(), true)
373 ));
374 $checked = false;
375 if ($result_output) {
376 $pointschecked = $this->object->answers[$answer_id]->getPointsChecked();
377 $pointsunchecked = $this->object->answers[$answer_id]->getPointsUnchecked();
378 $resulttextchecked = ($pointschecked == 1) || ($pointschecked == -1) ? "%s " . $this->lng->txt("point") : "%s " . $this->lng->txt("points");
379 $resulttextunchecked = ($pointsunchecked == 1) || ($pointsunchecked == -1) ? "%s " . $this->lng->txt("point") : "%s " . $this->lng->txt("points");
380 $template->setVariable("RESULT_OUTPUT", sprintf("(" . $this->lng->txt("checkbox_checked") . " = $resulttextchecked, " . $this->lng->txt("checkbox_unchecked") . " = $resulttextunchecked)", $pointschecked, $pointsunchecked));
381 }
382 foreach ($user_solution as $mc_solution) {
383 if ((string) $mc_solution === (string) $answer_id) {
384 if ($this->renderPurposeSupportsFormHtml() || $this->isRenderPurposePrintPdf()) {
385 $template->setVariable("SOLUTION_IMAGE", ilUtil::getHtmlPath(ilUtil::getImagePath("object/checkbox_checked.png")));
386 $template->setVariable("SOLUTION_ALT", $this->lng->txt("checked"));
387 } else {
388 $template->setVariable('QID', $this->object->getId());
389 $template->setVariable('SUFFIX', $show_correct_solution ? 'bestsolution' : 'usersolution');
390 $template->setVariable('SOLUTION_VALUE', $answer_id);
391 $template->setVariable('SOLUTION_CHECKED', 'checked');
392 }
393 $checked = true;
394 }
395 }
396 if (!$checked) {
397 if ($this->renderPurposeSupportsFormHtml() || $this->isRenderPurposePrintPdf()) {
398 $template->setVariable("SOLUTION_IMAGE", ilUtil::getHtmlPath(ilUtil::getImagePath("object/checkbox_unchecked.png")));
399 $template->setVariable("SOLUTION_ALT", $this->lng->txt("unchecked"));
400 } else {
401 $template->setVariable('QID', $this->object->getId());
402 $template->setVariable('SUFFIX', $show_correct_solution ? 'bestsolution' : 'usersolution');
403 $template->setVariable('SOLUTION_VALUE', $answer_id);
404 }
405 }
406 $template->parseCurrentBlock();
407 }
408 $questiontext = $this->object->getQuestionForHTMLOutput();
409 if ($show_feedback && $this->hasInlineFeedback()) {
410 $questiontext .= $this->buildFocusAnchorHtml();
411 }
412 if ($show_question_text == true) {
413 $template->setVariable("QUESTIONTEXT", $this->renderLatex(
415 ));
416 }
417 $questionoutput = $template->get();
418 $feedback = ($show_feedback && !$this->isTestPresentationContext()) ? $this->getGenericFeedbackOutput((int) $active_id, $pass) : "";
419
420 if (strlen($feedback)) {
421 $cssClass = (
422 $this->hasCorrectSolution($active_id, $pass) ?
424 );
425
426 $solutiontemplate->setVariable("ILC_FB_CSS_CLASS", $cssClass);
427 $solutiontemplate->setVariable("FEEDBACK", $this->renderLatex(
429 ));
430 }
431 $solutiontemplate->setVariable("SOLUTION_OUTPUT", $questionoutput);
432
433 $solutionoutput = $solutiontemplate->get();
434
435 if (!$show_question_only) {
436 // get page object output
437 $solutionoutput = $this->getILIASPage($solutionoutput);
438 }
439 return $solutionoutput;
440 }
441
442 public function getPreview(
443 bool $show_question_only = false,
444 bool $show_inline_feedback = false
445 ): string {
446 $user_solution = is_object($this->getPreviewSession()) ? (array) $this->getPreviewSession()->getParticipantsSolution() : [];
447 // shuffle output
448 $keys = $this->getChoiceKeys();
449
450 $this->tpl->addOnLoadCode('ilAssMultipleChoiceCharCounterInit();');
451 $template = new ilTemplate("tpl.il_as_qpl_mc_mr_output.html", true, true, "components/ILIAS/TestQuestionPool");
452 foreach ($keys as $answer_id) {
453 $answer = $this->object->answers[$answer_id];
454 if ($answer->hasImage()) {
455 if ($this->object->getThumbSize()) {
456 $template->setCurrentBlock("preview");
457 $template->setVariable("URL_PREVIEW", $this->object->getImagePathWeb() . $answer->getImage());
458 $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
459 $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('media/enlarge.svg'));
460 $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $answer->getImage());
461 list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
462 $alt = $answer->getImage();
463 if (strlen($answer->getAnswertext())) {
464 $alt = $answer->getAnswertext();
465 }
466 $alt = preg_replace("/<[^>]*?>/", "", $alt);
467 $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
468 $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
469 $template->parseCurrentBlock();
470 } else {
471 $template->setCurrentBlock("answer_image");
472 $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $answer->getImage());
473 list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
474 $alt = $answer->getImage();
475 if (strlen($answer->getAnswertext())) {
476 $alt = $answer->getAnswertext();
477 }
478 $alt = preg_replace("/<[^>]*?>/", "", $alt);
479 $template->setVariable("ATTR", $attr);
480 $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
481 $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
482 $template->parseCurrentBlock();
483 }
484 }
485
486 if ($show_inline_feedback) {
487 $this->populateSpecificFeedbackInline($user_solution, $answer_id, $template);
488 }
489
490 $template->setCurrentBlock("answer_row");
491 $template->setVariable("QID", $this->object->getId());
492 $template->setVariable("ANSWER_ID", $answer_id);
493 $template->setVariable("ANSWER_TEXT", $this->renderLatex(
494 ilLegacyFormElementsUtil::prepareTextareaOutput($answer->getAnswertext(), true)
495 ));
496 foreach ($user_solution as $mc_solution) {
497 if ((string) $mc_solution === (string) $answer_id) {
498 $template->setVariable("CHECKED_ANSWER", " checked=\"checked\"");
499 }
500 }
501 $template->parseCurrentBlock();
502 }
503 if ($this->object->getSelectionLimit()) {
504 $template->setVariable('SELECTION_LIMIT_HINT', sprintf(
505 $this->lng->txt('ass_mc_sel_lim_hint'),
506 $this->object->getSelectionLimit(),
507 $this->object->getAnswerCount()
508 ));
509
510 $template->setVariable('SELECTION_LIMIT_VALUE', $this->object->getSelectionLimit());
511 } else {
512 $template->setVariable('SELECTION_LIMIT_VALUE', 'null');
513 }
514 $template->setVariable("QUESTION_ID", $this->object->getId());
515 $questiontext = $this->object->getQuestionForHTMLOutput();
516 if ($show_inline_feedback && $this->hasInlineFeedback()) {
517 $questiontext .= $this->buildFocusAnchorHtml();
518 }
519 $template->setVariable("QUESTIONTEXT", $this->renderLatex(
521 ));
522 $questionoutput = $template->get();
523 if (!$show_question_only) {
524 // get page object output
525 $questionoutput = $this->getILIASPage($questionoutput);
526 }
527 return $questionoutput;
528 }
529
530 public function getTestOutput(
531 int $active_id,
532 int $pass,
533 bool $is_question_postponed = false,
534 array|bool $user_post_solutions = false,
535 bool $show_specific_inline_feedback = false
536 ): string {
537 // shuffle output
538 $keys = $this->getChoiceKeys();
539
540 // get the solution of the user for the active pass or from the last pass if allowed
541 $user_solution = [];
542 if ($active_id) {
543 $solutions = $this->object->getTestOutputSolutions($active_id, $pass);
544 // hey.
545 foreach ($solutions as $solution_value) {
546 // fau: testNav - don't add the dummy entry for 'none of the above' to the user options
547 if ($solution_value["value1"] == 'mc_none_above') {
548 $this->setUseEmptySolutionInputChecked(true);
549 continue;
550 }
551
552 $user_solution[] = $solution_value["value1"];
553 // fau.
554 }
555
556 if (empty($user_solution) && $this->object->getTestPresentationConfig()->isWorkedThrough()) {
557 $this->setUseEmptySolutionInputChecked(true);
558 }
559 }
560 // generate the question output
561 $this->tpl->addJavaScript('assets/js/ilAssMultipleChoice.js');
562 $this->tpl->addOnLoadCode('ilAssMultipleChoiceCharCounterInit();');
563
564 $template = new ilTemplate("tpl.il_as_qpl_mc_mr_output.html", true, true, "components/ILIAS/TestQuestionPool");
565 foreach ($keys as $answer_id) {
566 $answer = $this->object->answers[$answer_id];
567 if ($answer->hasImage()) {
568 if ($this->object->getThumbSize()) {
569 $template->setCurrentBlock("preview");
570 $template->setVariable("URL_PREVIEW", $this->object->getImagePathWeb() . $answer->getImage());
571 $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
572 $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('media/enlarge.svg'));
573 $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $answer->getImage());
574 list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
575 $alt = $answer->getImage();
576 if (strlen($answer->getAnswertext())) {
577 $alt = $answer->getAnswertext();
578 }
579 $alt = preg_replace("/<[^>]*?>/", "", $alt);
580 $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
581 $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
582 $template->parseCurrentBlock();
583 } else {
584 $template->setCurrentBlock("answer_image");
585 $template->setVariable("ANSWER_IMAGE_URL", $this->object->getImagePathWeb() . $answer->getImage());
586 list($width, $height, $type, $attr) = getimagesize($this->object->getImagePath() . $answer->getImage());
587 $alt = $answer->getImage();
588 if (strlen($answer->getAnswertext())) {
589 $alt = $answer->getAnswertext();
590 }
591 $alt = preg_replace("/<[^>]*?>/", "", $alt);
592 $template->setVariable("ATTR", $attr);
593 $template->setVariable("ANSWER_IMAGE_ALT", ilLegacyFormElementsUtil::prepareFormOutput($alt));
594 $template->setVariable("ANSWER_IMAGE_TITLE", ilLegacyFormElementsUtil::prepareFormOutput($alt));
595 $template->parseCurrentBlock();
596 }
597 }
598
599 if ($show_specific_inline_feedback) {
600 $this->populateSpecificFeedbackInline($user_solution, $answer_id, $template);
601 }
602
603 $template->setCurrentBlock("answer_row");
604 $template->setVariable("QID", $this->object->getId());
605 $template->setVariable("ANSWER_ID", $answer_id);
606 $template->setVariable("ANSWER_TEXT", $this->renderLatex(
607 ilLegacyFormElementsUtil::prepareTextareaOutput($answer->getAnswertext(), true)
608 ));
609 foreach ($user_solution as $mc_solution) {
610 if ((string) $mc_solution === (string) $answer_id) {
611 $template->setVariable("CHECKED_ANSWER", " checked=\"checked\"");
612 }
613 }
614 $template->parseCurrentBlock();
615 }
616
617 $template->setVariable("QUESTIONTEXT", $this->renderLatex($this->object->getQuestionForHTMLOutput()));
618 $template->setVariable("QUESTION_ID", $this->object->getId());
619 if ($this->object->getSelectionLimit()) {
620 $template->setVariable('SELECTION_LIMIT_HINT', sprintf(
621 $this->lng->txt('ass_mc_sel_lim_hint'),
622 $this->object->getSelectionLimit(),
623 $this->object->getAnswerCount()
624 ));
625
626 $template->setVariable('SELECTION_LIMIT_VALUE', $this->object->getSelectionLimit());
627 } else {
628 $template->setVariable('SELECTION_LIMIT_VALUE', 'null');
629 }
630 $questionoutput = $template->get();
631 $pageoutput = $this->outQuestionPage("", $is_question_postponed, $active_id, $questionoutput, $show_specific_inline_feedback);
632 return $pageoutput;
633 }
634
635 protected $useEmptySolutionInputChecked = false;
636
637 public function isUseEmptySolutionInputChecked(): bool
638 {
639 return $this->useEmptySolutionInputChecked;
640 }
641
642 public function setUseEmptySolutionInputChecked($useEmptySolutionInputChecked): void
643 {
644 $this->useEmptySolutionInputChecked = $useEmptySolutionInputChecked;
645 }
646
647 protected function getUseUnchangedAnswerCheckboxHtml(): string
648 {
649 // hey: prevPassSolutions - use abstracted template to share with other purposes of this kind
650 $this->tpl->addJavaScript('assets/js/ilAssMultipleChoice.js');
651
652 $tpl = new ilTemplate('tpl.tst_question_additional_behaviour_checkbox.html', true, true, 'components/ILIAS/TestQuestionPool');
653
654 // HEY: affects next if (!) /// noneAboveChecked repaired but disabled because the checked input ..
655 if (false) { // .. makes the qstEditController initialize the "edit" instead of the "answered" state
656 if ($this->isUseEmptySolutionInputChecked()) {
657 $tpl->setCurrentBlock('checked');
658 $tpl->touchBlock('checked');
659 $tpl->parseCurrentBlock();
660 }
661 }
662
663 $tpl->setCurrentBlock('checkbox');
664 $tpl->setVariable('TXT_FORCE_FORM_DIFF_LABEL', $this->object->getTestPresentationConfig()->getUseUnchangedAnswerLabel());
665 $tpl->parseCurrentBlock();
666 // hey.
667 return $tpl->get();
668 }
669
670 public function getPresentationJavascripts(): array
671 {
672 return ['assets/js/ilAssMultipleChoice.js'];
673 }
674
680 public function getChoiceKeys(): array
681 {
682 $choice_keys = array_keys($this->object->answers);
683
684 if ($this->object->getShuffle()) {
685 $choice_keys = $this->object->getShuffler()->transform($choice_keys);
686 }
687
688 return $choice_keys;
689 }
690
691 public function getSpecificFeedbackOutput(array $userSolution): string
692 {
693 // No return value, this question type supports inline specific feedback.
694 $output = "";
696 }
697
699 {
700 $this->object->setShuffle($this->request_data_collector->bool('shuffle') ?? false);
701
702 $selectionLimit = (int) $form->getItemByPostVar('selection_limit')?->getValue();
703 $this->object->setSelectionLimit($selectionLimit > 0 ? $selectionLimit : null);
704
705 $feedback_setting = $this->request_data_collector->int('feedback_setting');
706 if ($feedback_setting !== 0) {
707 $this->object->setSpecificFeedbackSetting($feedback_setting);
708 }
709
710 $types = $this->request_data_collector->int('types');
711 $this->object->setMultilineAnswerSetting($types);
712
713 $choice = $this->request_data_collector->raw('choice');
714 if (isset($choice['imagename']) && is_array($choice['imagename']) && $types === 1) {
715 $this->object->setIsSingleline(true);
716 $this->tpl->setOnScreenMessage('info', $this->lng->txt('info_answer_type_change'), true);
717 } else {
718 $this->object->setIsSingleline($types === 0);
719 }
720
721 $thumb_size = $this->request_data_collector->int('thumb_size') ?? $this->object->getThumbSize();
722 if ($thumb_size !== $this->object->getThumbSize()) {
723 $this->object->setThumbSize($thumb_size);
724 $this->rebuild_thumbnails = true;
725 }
726 }
727
729 {
730 // Delete all existing answers and create new answers from the form data
731 $this->object->flushAnswers();
732
733 $choice = $this->cleanupAnswerText(
734 $this->request_data_collector->raw('choice'),
735 !$this->object->isSingleline()
736 );
737
738 if (!$this->object->isSingleline()) {
739 foreach ($choice['answer'] as $index => $answer) {
740 $answertext = $answer;
741 $this->object->addAnswer(
742 $answertext,
743 (float) str_replace(',', '.', $choice['points'][$index]),
744 (float) str_replace(',', '.', $choice['points_unchecked'][$index]),
745 $index,
746 null,
747 (int) $choice['answer_id'][$index]
748 );
749 }
750 return;
751 }
752
753 foreach ($choice['answer'] as $index => $answertext) {
754 $answertext = htmlentities($answertext);
755 $picturefile = $choice['imagename'][$index] ?? null;
756 $file_org_name = $_FILES['choice']['name']['image'][$index] ?? '';
757 $file_temp_name = $_FILES['choice']['tmp_name']['image'][$index] ?? '';
758
759 if ($file_temp_name !== '') {
760 // check suffix
761 $parts = explode('.', $file_org_name);
762 $suffix = strtolower(array_pop($parts));
763 if (in_array($suffix, ['jpg', 'jpeg', 'png', 'gif'])) {
764 // upload image
765 $filename = $this->object->buildHashedImageFilename($file_org_name);
766 if ($this->object->setImageFile($filename, $file_temp_name) === 0) {
767 $picturefile = $filename;
768 }
769 }
770 }
771
772 $this->object->addAnswer(
773 $answertext,
774 (float) str_replace(',', '.', $choice['points'][$index]),
775 (float) str_replace(',', '.', $choice['points_unchecked'][$index]),
776 $index,
777 $picturefile,
778 $choice['answer_id'][$index]
779 );
780 }
781
782 if ($this->rebuild_thumbnails) {
783 $this->object->setAnswers(
784 $this->object->rebuildThumbnails(
785 $this->object->isSingleline(),
786 $this->object->getThumbSize(),
787 $this->object->getImagePath(),
788 $this->object->getAnswers()
789 )
790 );
791 }
792 }
793
794 public function populateQuestionSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline = false): ilPropertyFormGUI
795 {
796 // shuffle
797 $shuffle = new ilCheckboxInputGUI($this->lng->txt("shuffle_answers"), "shuffle");
798 $shuffle->setValue(1);
799 $shuffle->setChecked($this->object->getShuffle());
800 $shuffle->setRequired(false);
801 $form->addItem($shuffle);
802
803 $selLim = new ilNumberInputGUI($this->lng->txt('ass_mc_sel_lim_setting'), 'selection_limit');
804 $selLim->setInfo($this->lng->txt('ass_mc_sel_lim_setting_desc'));
805 $selLim->setSize(2);
806 $selLim->setRequired(false);
807 $selLim->allowDecimals(false);
808 $selLim->setMinvalueShouldBeGreater(false);
809 $selLim->setMaxvalueShouldBeLess(false);
810 $selLim->setMinValue(1);
811 $selLim->setMaxValue($this->object->getAnswerCount());
812 $selLim->setValue($this->object->getSelectionLimit());
813 $form->addItem($selLim);
814
815 if (!$this->object->getSelfAssessmentEditingMode()) {
816 // Answer types
817 $types = new ilSelectInputGUI($this->lng->txt("answer_types"), "types");
818 $types->setRequired(false);
819 $types->setOptions([
820 0 => $this->lng->txt('answers_singleline'),
821 1 => $this->lng->txt('answers_multiline'),
822 ]);
823 $types->setValue($is_singleline ? 0 : 1);
824 $form->addItem($types);
825 }
826
827 if ($is_singleline) {
828 // thumb size
829 $thumb_size = new ilNumberInputGUI($this->lng->txt('thumb_size'), "thumb_size");
830 $thumb_size->setSuffix($this->lng->txt('thumb_size_unit_pixel'));
831 $thumb_size->setMinValue($this->object->getMinimumThumbSize());
832 $thumb_size->setMaxValue($this->object->getMaximumThumbSize());
833 $thumb_size->setDecimals(0);
834 $thumb_size->setSize(6);
835 $thumb_size->setInfo($this->lng->txt('thumb_size_info'));
836 $thumb_size->setValue($this->object->getThumbSize());
837 $thumb_size->setRequired(true);
838 } else {
839 $thumb_size = new ilHiddenInputGUI('thumb_size');
840 $thumb_size->setValue($this->object->getThumbSize());
841 }
842 $form->addItem($thumb_size);
843
844 return $form;
845 }
846
847 public function populateAnswerSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline = false): ilPropertyFormGUI
848 {
849 $choices = new ilMultipleChoiceWizardInputGUI($this->lng->txt("answers"), "choice");
850 $choices->setRequired(true);
851 $choices->setQuestionObject($this->object);
852 $choices->setSingleline($is_singleline);
853 $choices->setAllowMove(false);
854 if ($this->object->getSelfAssessmentEditingMode()) {
855 $choices->setSize(40);
856 }
857 if ($this->object->getAnswerCount() == 0) {
858 $this->object->addAnswer("", 0, 0, 0);
859 }
860 $choices->setValues(array_map(
861 function (ASS_AnswerMultipleResponseImage $value) {
862 $value->setAnswerText(html_entity_decode($value->getAnswerText()));
863 return $value;
864 },
865 $this->object->getAnswers()
866 ));
867 $choices->setInfo($this->lng->txt('latex_edit_info'));
868 $form->addItem($choices);
869 return $form;
870 }
871
882 {
883 return [];
884 }
885
896 {
897 return [];
898 }
899
900 private function aggregateAnswers($relevant_answers_chosen, $answers_defined_on_question): array
901 {
902 $aggregate = [];
903 foreach ($answers_defined_on_question as $answer) {
904 $aggregated_info_for_answer = [];
905 $aggregated_info_for_answer['answertext'] = $answer->getAnswerText();
906 $aggregated_info_for_answer['count_checked'] = 0;
907
908 foreach ($relevant_answers_chosen as $relevant_answer) {
909 if ($relevant_answer['value1'] == $answer->getOrder()) {
910 $aggregated_info_for_answer['count_checked']++;
911 }
912 }
913 $aggregated_info_for_answer['count_unchecked'] =
914 ceil(count($relevant_answers_chosen) / count($answers_defined_on_question))
915 - $aggregated_info_for_answer['count_checked'];
916
917 $aggregate[] = $aggregated_info_for_answer;
918 }
919 return $aggregate;
920 }
921
922 private function populateSpecificFeedbackInline($user_solution, $answer_id, $template): void
923 {
924 if ($this->object->getSpecificFeedbackSetting() == 2) {
925 foreach ($user_solution as $mc_solution) {
926 if ((string) $mc_solution === (string) $answer_id) {
927 $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation($this->object->getId(), 0, $answer_id);
928 if (strlen($fb)) {
929 $template->setCurrentBlock("feedback");
930 $template->setVariable("FEEDBACK", $this->renderLatex(
932 ));
933 $template->parseCurrentBlock();
934 }
935 }
936 }
937 }
938
939 if ($this->object->getSpecificFeedbackSetting() == 1) {
940 $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation($this->object->getId(), 0, $answer_id);
941 if (strlen($fb)) {
942 $template->setCurrentBlock("feedback");
943 $template->setVariable("FEEDBACK", $this->renderLatex(
945 ));
946 $template->parseCurrentBlock();
947 }
948 }
949
950 if ($this->object->getSpecificFeedbackSetting() == 3) {
951 $answer = $this->object->getAnswer($answer_id);
952
953 if ($answer->getPoints() > 0) {
954 $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation($this->object->getId(), 0, $answer_id);
955 if (strlen($fb)) {
956 $template->setCurrentBlock("feedback");
957 $template->setVariable("FEEDBACK", $this->renderLatex(
959 ));
960 $template->parseCurrentBlock();
961 }
962 }
963 }
964 }
965
966 // fau: testNav - new functions setWithNoneAbove() and setIsAnswered()
967 // moved functionality to ilTestQuestionPresentationConfig
968 // fau.
969
973 protected function buildEditForm(bool $is_singleline = true): ilPropertyFormGUI
974 {
975 $form = new ilPropertyFormGUI();
976 $form->setFormAction($this->ctrl->getFormAction($this));
977 $form->setTitle($this->outQuestionType());
978 $form->setTableWidth("100%");
979 $form->setId("assmultiplechoice");
980
981 $this->addBasicQuestionFormProperties($form);
982 $this->populateQuestionSpecificFormPart($form, $is_singleline);
983 $this->populateAnswerSpecificFormPart($form, $is_singleline);
984 $this->populateTaxonomyFormSection($form);
985 $this->addQuestionFormCommandButtons($form);
986 return $form;
987 }
988
989 public function getAnswersFrequency($relevantAnswers, $questionIndex): array
990 {
991 $agg = $this->aggregateAnswers($relevantAnswers, $this->object->getAnswers());
992
993 $answers = [];
994
995 foreach ($agg as $ans) {
996 $answers[] = [
997 'answer' => $ans['answertext'],
998 'frequency' => $ans['count_checked']
999 ];
1000 }
1001
1002 return $answers;
1003 }
1004
1006 {
1007 $choices = new ilAssMultipleChoiceCorrectionsInputGUI($this->lng->txt("answers"), "choice");
1008 $choices->setRequired(true);
1009 $choices->setQuestionObject($this->object);
1010 $choices->setValues($this->object->getAnswers());
1011 $form->addItem($choices);
1012 }
1013
1018 {
1019 $input = $form->getItemByPostVar('choice');
1020 $answerElements = $input->getValues();
1021
1022 foreach ($this->object->getAnswers() as $index => $answer) {
1023 /* @var ASS_AnswerMultipleResponseImage $answer */
1024 $answer->setPointsChecked((float) str_replace(',', '.', $answerElements[$index]->getPointsChecked()));
1025 $answer->setPointsUnchecked((float) str_replace(',', '.', $answerElements[$index]->getPointsUnchecked()));
1026 }
1027 }
1028}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
$filename
Definition: buildRTE.php:78
ASS_AnswerBinaryStateImage is a class for answers with a binary state indicator (checked/unchecked,...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getSpecificFeedbackOutput(array $userSolution)
Returns the answer specific feedback for the question.
populateQuestionSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline=false)
writePostData(bool $always=false)
{Evaluates a posted edit form and writes the form data in the question object.integer A positive valu...
getSolutionOutput(int $active_id, ?int $pass=null, bool $graphical_output=false, bool $result_output=false, bool $show_question_only=true, bool $show_feedback=false, bool $show_correct_solution=false, bool $show_manual_scoring=false, bool $show_question_text=true, bool $show_inline_feedback=true)
getAfterParticipationSuppressionAnswerPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
setUseEmptySolutionInputChecked($useEmptySolutionInputChecked)
getChoiceKeys()
Create the key index numbers for the array of choices.
populateAnswerSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline=false)
getAfterParticipationSuppressionQuestionPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
renderSolutionOutput(mixed $user_solutions, int $active_id, ?int $pass, bool $graphical_output=false, bool $result_output=false, bool $show_question_only=true, bool $show_feedback=false, bool $show_correct_solution=false, bool $show_manual_scoring=false, bool $show_question_text=true, bool $show_autosave_title=false, bool $show_inline_feedback=false,)
aggregateAnswers($relevant_answers_chosen, $answers_defined_on_question)
getTestOutput(int $active_id, int $pass, bool $is_question_postponed=false, array|bool $user_post_solutions=false, bool $show_specific_inline_feedback=false)
populateSpecificFeedbackInline($user_solution, $answer_id, $template)
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
buildEditForm(bool $is_singleline=true)
writeAnswerSpecificPostData(ilPropertyFormGUI $form)
Extracts the answer specific values from the request and applies them to the data object.
getEditAnswersSingleLine(bool $checkonly=false)
Get the single/multiline editing of answers.
__construct($id=-1)
assMultipleChoiceGUI constructor
getAnswersFrequency($relevantAnswers, $questionIndex)
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
addBasicQuestionFormProperties(ilPropertyFormGUI $form)
getPreview(bool $show_question_only=false, bool $show_inline_feedback=false)
writeQuestionSpecificPostData(ilPropertyFormGUI $form)
Extracts the question specific values from the request and applies them to the data object.
editQuestion(bool $checkonly=false, ?bool $is_save_cmd=null)
Class for multiple choice tests.
renderEditForm(ilPropertyFormGUI $form)
This class represents a checkbox property in a property form.
setFormAction(string $a_formaction)
setId(string $a_id)
This class represents a hidden form property in a property form.
static prepareTextareaOutput(string $txt_output, bool $prepare_for_latex_output=false, bool $omitNl2BrWhenTextArea=false)
Prepares a string for a text area output where latex code may be in it If the text is HTML-free,...
static prepareFormOutput($a_str, bool $a_strip=false)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This class represents a number property in a property form.
This class represents a property form user interface.
setTableWidth(string $a_width)
getItemByPostVar(string $a_post_var)
This class represents a selection list property in a property form.
special template class to simplify handling of ITX/PEAR
static getHtmlPath(string $relative_path)
get url of path
static getImagePath(string $image_name, string $module_path="", string $mode="output", bool $offline=false)
get image path (for images located in a template directory)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:61
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
if(!file_exists('../ilias.ini.php'))