ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
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();
73  $this->writeQuestionSpecificPostData($form);
74  $this->writeAnswerSpecificPostData($form);
75  $this->saveTaxonomyAssignments();
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  {
172  $this->writePostData(true);
173  $this->editQuestion();
174  }
175 
176  public function removeimagechoice(): void
177  {
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  {
640  }
641 
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');
660  }
661  }
662 
663  $tpl->setCurrentBlock('checkbox');
664  $tpl->setVariable('TXT_FORCE_FORM_DIFF_LABEL', $this->object->getTestPresentationConfig()->getUseUnchangedAnswerLabel());
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 
728  public function writeAnswerSpecificPostData(ilPropertyFormGUI $form): void
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 }
hasCorrectSolution($activeId, $passIndex)
setSuffix(string $a_value)
generateCorrectnessIconsForCorrectness(int $correctness)
setId(string $a_id)
setCurrentBlock(string $blockname=self::DEFAULT_BLOCK)
Sets the template to the given block.
getAnswersFrequency($relevantAnswers, $questionIndex)
This class represents a selection list property in a property form.
getItemByPostVar(string $a_post_var)
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:61
setUseEmptySolutionInputChecked($useEmptySolutionInputChecked)
renderLatex($content)
Wrap content with latex in a LatexContent UI component and render it to be processed by MathJax in th...
getAfterParticipationSuppressionAnswerPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
getChoiceKeys()
Create the key index numbers for the array of choices.
ASS_AnswerBinaryStateImage is a class for answers with a binary state indicator (checked/unchecked, set/unset) and an image file.
populateQuestionSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline=false)
populateAnswerSpecificFormPart(\ilPropertyFormGUI $form, bool $is_singleline=false)
parseCurrentBlock(string $blockname=self::DEFAULT_BLOCK)
Parses the given block.
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
ilGlobalPageTemplate $tpl
static prepareFormOutput($a_str, bool $a_strip=false)
Class for multiple choice tests.
populateTaxonomyFormSection(ilPropertyFormGUI $form)
addQuestionFormCommandButtons(ilPropertyFormGUI $form)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
This class represents a hidden form property in a property form.
get(string $part=self::DEFAULT_BLOCK)
Renders the given block and returns the html string.
setFormAction(string $a_formaction)
This class represents a number property in a property form.
writeAnswerSpecificPostData(ilPropertyFormGUI $form)
Extracts the answer specific values from the request and applies them to the data object...
static getImagePath(string $image_name, string $module_path="", string $mode="output", bool $offline=false)
get image path (for images located in a template directory)
static getHtmlPath(string $relative_path)
get url of path
writeQuestionSpecificPostData(ilPropertyFormGUI $form)
Extracts the question specific values from the request and applies them to the data object...
setRequired(bool $a_required)
getEditAnswersSingleLine(bool $checkonly=false)
Get the single/multiline editing of answers.
aggregateAnswers($relevant_answers_chosen, $answers_defined_on_question)
setTableWidth(string $a_width)
editQuestion(bool $checkonly=false, ?bool $is_save_cmd=null)
$filename
Definition: buildRTE.php:78
getAfterParticipationSuppressionQuestionPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
touchBlock(string $blockname)
overwrites ITX::touchBlock.
__construct($id=-1)
assMultipleChoiceGUI constructor
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
writePostData(bool $always=false)
{}
getILIASPage(string $html="")
Returns the ILIAS Page around a question.
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="", $inlineFeedbackEnabled=false)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
__construct(Container $dic, ilPlugin $plugin)
cleanupAnswerText(array $answer_text, bool $is_rte)
sk - 12.05.2023: This is one more of those that we need, but don&#39;t want.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getPreview(bool $show_question_only=false, bool $show_inline_feedback=false)
buildEditForm(bool $is_singleline=true)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
getTestOutput(int $active_id, int $pass, bool $is_question_postponed=false, array|bool $user_post_solutions=false, bool $show_specific_inline_feedback=false)
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...
getSpecificFeedbackOutput(array $userSolution)
populateSpecificFeedbackInline($user_solution, $answer_id, $template)
setVariable(string $variable, $value='')
Sets the given variable to the given value.
addBasicQuestionFormProperties(ilPropertyFormGUI $form)
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,)
renderEditForm(ilPropertyFormGUI $form)
getGenericFeedbackOutput(int $active_id, ?int $pass)
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)